diff --git a/sjplayer/src/sjplayer/AbstractScheduledController.d b/sjplayer/src/sjplayer/AbstractScheduledController.d new file mode 100644 index 0000000..7a3b300 --- /dev/null +++ b/sjplayer/src/sjplayer/AbstractScheduledController.d @@ -0,0 +1,105 @@ +/// License: MIT +module sjplayer.AbstractScheduledController; + +import std.algorithm, + std.exception, + std.typecons; + +import sjscript; + +import sjplayer.ScheduledControllerInterface, + sjplayer.VarStoreInterface, + sjplayer.util.Parameter, + sjplayer.util.Period; + +/// +abstract class AbstractScheduledController : ScheduledControllerInterface { + public: + /// + this(in VarStoreInterface varstore, in ParametersBlock[] operations) { + varstore_ = varstore; + operations_ = operations; + } + + override void Operate(float time) { + FinalizeCurrentOperationIfEnded(time); + PrepareNextOperationIfStarted(time); + ProcessCurrentOperationIfAvailable(time); + } + + protected: + static struct VarStore { + public: + float opIndex(string name) const { + if (!time_.isNull && name == "time") return time_.get; + return this_.GetVariable(name); + } + private: + AbstractScheduledController this_; + Nullable!float time_; + } + + void PrepareOperation(ref in ParametersBlock params) { + user_vars_.clear(); + + auto vars = VarStore(this); + params.parameters. + filter!(x => x.type == ParameterType.OnceAssign). + each !(x => SetParameter(x, vars)); + } + void ProcessOperation(float time, ref in ParametersBlock params) { + auto vars = VarStore(this, time.nullable); + params.parameters. + filter!(x => x.type != ParameterType.OnceAssign). + each !(x => SetParameter(x, vars)); + } + void FinalizeOperation(ref in ParametersBlock params) { + } + + float GetVariable(string name) const { + if (name in user_vars_) return user_vars_[name]; + return varstore_[name]; + } + void SetParameter(ref in Parameter param, ref in VarStore vars) { + (param.name.length >= 2 && param.name[0..2] == "__"). + enforce("user defined variables must be prefixed '__'"); + user_vars_[param.name] = 0; + param.CalculateParameter(user_vars_[param.name], vars); + } + + private: + void FinalizeCurrentOperationIfEnded(float time) { + if (next_operation_index_ < 1) return; + + const current = &operations_[next_operation_index_-1]; + if (current.period.end > time) return; + + FinalizeOperation(*current); + } + void PrepareNextOperationIfStarted(float time) { + if (next_operation_index_ >= operations_.length) return; + + const next = &operations_[next_operation_index_]; + if (next.period.start > time) return; + + ++next_operation_index_; + PrepareOperation(*next); + } + void ProcessCurrentOperationIfAvailable(float time) { + if (next_operation_index_ < 1) return; + + const current = &operations_[next_operation_index_-1]; + if (current.period.end <= time) return; + + ProcessOperation( + current.period.ConvertToRelativeTime(time), *current); + } + + const VarStoreInterface varstore_; + + const ParametersBlock[] operations_; + + size_t next_operation_index_; + + float[string] user_vars_; +} diff --git a/sjplayer/src/sjplayer/CircleElement.d b/sjplayer/src/sjplayer/CircleElement.d index b5ce776..6032ea5 100644 --- a/sjplayer/src/sjplayer/CircleElement.d +++ b/sjplayer/src/sjplayer/CircleElement.d @@ -7,7 +7,8 @@ import std.algorithm, import gl4d; -import sjplayer.ElementInterface; +import sjplayer.ElementDrawerInterface, + sjplayer.ElementInterface; /// class CircleElement : ElementInterface { @@ -24,7 +25,8 @@ class CircleElement : ElementInterface { align(1) vec4 color = vec4(0, 0, 0, 0); } - override void Initialize() { + /// + void Initialize() { alive = false; damage = 0; nearness_coe = 0; diff --git a/sjplayer/src/sjplayer/CircleElementScheduledController.d b/sjplayer/src/sjplayer/CircleElementScheduledController.d index bef3459..977bb6f 100644 --- a/sjplayer/src/sjplayer/CircleElementScheduledController.d +++ b/sjplayer/src/sjplayer/CircleElementScheduledController.d @@ -4,6 +4,7 @@ module sjplayer.CircleElementScheduledController; import std.typecons; import sjplayer.CircleElement, + sjplayer.ElementScheduledControllerFactory, sjplayer.ScheduledController; /// diff --git a/sjplayer/src/sjplayer/Context.d b/sjplayer/src/sjplayer/Context.d index f2e1217..bc19403 100644 --- a/sjplayer/src/sjplayer/Context.d +++ b/sjplayer/src/sjplayer/Context.d @@ -9,6 +9,7 @@ import sjscript; import sjplayer.Background, sjplayer.ContextBuilderInterface, + sjplayer.ElementDrawerInterface, sjplayer.ElementInterface, sjplayer.ProgramSet, sjplayer.ScheduledControllerInterface, diff --git a/sjplayer/src/sjplayer/ContextBuilderInterface.d b/sjplayer/src/sjplayer/ContextBuilderInterface.d index ec15b46..0092e77 100644 --- a/sjplayer/src/sjplayer/ContextBuilderInterface.d +++ b/sjplayer/src/sjplayer/ContextBuilderInterface.d @@ -1,7 +1,8 @@ /// License: MIT module sjplayer.ContextBuilderInterface; -import sjplayer.ElementInterface, +import sjplayer.ElementDrawerInterface, + sjplayer.ElementInterface, sjplayer.ScheduledControllerInterface; /// diff --git a/sjplayer/src/sjplayer/ElementDrawerInterface.d b/sjplayer/src/sjplayer/ElementDrawerInterface.d new file mode 100644 index 0000000..9efe992 --- /dev/null +++ b/sjplayer/src/sjplayer/ElementDrawerInterface.d @@ -0,0 +1,9 @@ +/// License: MIT +module sjplayer.ElementDrawerInterface; + +/// +interface ElementDrawerInterface { + public: + /// + void Draw(); +} diff --git a/sjplayer/src/sjplayer/ElementInterface.d b/sjplayer/src/sjplayer/ElementInterface.d index b52c6e0..a1a2fe6 100644 --- a/sjplayer/src/sjplayer/ElementInterface.d +++ b/sjplayer/src/sjplayer/ElementInterface.d @@ -14,16 +14,6 @@ interface ElementInterface { float nearness; } - /// - void Initialize(); - /// DamageCalculationResult CalculateDamage(vec2 p1, vec2 p2) const; } - -/// -interface ElementDrawerInterface { - public: - /// - void Draw(); -} diff --git a/sjplayer/src/sjplayer/ElementScheduledControllerFactory.d b/sjplayer/src/sjplayer/ElementScheduledControllerFactory.d new file mode 100644 index 0000000..1b386d1 --- /dev/null +++ b/sjplayer/src/sjplayer/ElementScheduledControllerFactory.d @@ -0,0 +1,98 @@ +/// License: MIT +module sjplayer.ElementScheduledControllerFactory; + +import sjscript; + +import std.algorithm, + std.array, + std.conv, + std.meta, + std.range.primitives, + std.traits; + +import sjplayer.ContextBuilderInterface, + sjplayer.ElementDrawerInterface, + sjplayer.ElementInterface, + sjplayer.ProgramSet, + sjplayer.ScheduledControllerInterface, + sjplayer.VarStoreInterface, + sjplayer.util.Period; + +/// +struct ElementScheduledControllerFactory(ScheduledController, ElementDrawer) + if (is(ScheduledController : ScheduledControllerInterface) && + is(ElementDrawer : ElementDrawerInterface)) { + public: + /// ScheduledController's first constructor's first argument type. + alias Element = + Parameters!(__traits(getOverloads, ScheduledController, "__ctor")[0])[0]; + + static assert(is(Element : ElementInterface)); + + /// ElementDrawer's first constructor's first argument type. + alias ElementProgram = + Parameters!(__traits(getOverloads, ElementDrawer, "__ctor")[0])[0]; + + static assert(staticIndexOf!( + ElementProgram, ProgramSet.Programs.Types) >= 0); + static assert(is(Element[] : + Parameters!(__traits(getOverloads, ElementDrawer, "__ctor")[0])[1])); + + /// + this(ProgramSet programs, VarStoreInterface varstore) { + program_ = programs.Get!ElementProgram; + varstore_ = varstore; + } + + /// + void Create(R)(R params, ContextBuilderInterface builder) + if (isInputRange!R && is(ElementType!R == ParametersBlock)) { + auto parallelized = ParallelizeParams(params); + auto elements = appender!(Element[]); + + foreach (ref serial; parallelized) { + auto element = new Element; + elements ~= element; + builder.AddElement(element); + builder.AddScheduledController( + new ScheduledController(element, varstore_, serial)); + } + if (elements[].length > 0) { + builder.AddElementDrawer(new ElementDrawer(program_, elements[])); + } + } + + private: + static ParametersBlock[][] ParallelizeParams(R)(R params) + if (isInputRange!R && is(ElementType!R == ParametersBlock)) { + ParametersBlock[][] parallelized; + foreach (ref param; params) { + auto inserted = false; + foreach (ref serial; parallelized) { + const found_index = serial. + countUntil!(x => x.period.start > param.period.start); + const insert_index = + found_index >= 0? found_index.to!size_t: serial.length; + + const intersect_prev = insert_index >= 1 && + IsPeriodIntersectedToPeriod(serial[insert_index-1].period, param.period); + const intersect_next = insert_index < serial.length && + IsPeriodIntersectedToPeriod(serial[insert_index].period, param.period); + + if (!intersect_prev && !intersect_next) { + serial = serial[0..insert_index]~ param ~serial[insert_index..$]; + inserted = true; + break; + } + } + if (!inserted) { + parallelized ~= [param]; + } + } + return parallelized; + } + + ElementProgram program_; + + VarStoreInterface varstore_; +} diff --git a/sjplayer/src/sjplayer/ScheduledController.d b/sjplayer/src/sjplayer/ScheduledController.d index c6b5acc..9b9cd49 100644 --- a/sjplayer/src/sjplayer/ScheduledController.d +++ b/sjplayer/src/sjplayer/ScheduledController.d @@ -1,60 +1,51 @@ /// License: MIT module sjplayer.ScheduledController; -import std.algorithm, - std.array, - std.conv, - std.meta, - std.range.primitives, - std.traits, +import std.traits, std.typecons; import gl4d; import sjscript; -import sjplayer.ContextBuilderInterface, - sjplayer.ElementInterface, - sjplayer.ProgramSet, +import sjplayer.AbstractScheduledController, sjplayer.ScheduledControllerInterface, sjplayer.VarStoreInterface, sjplayer.util.MatrixFactory, - sjplayer.util.Parameter, - sjplayer.util.Period; + sjplayer.util.Parameter; /// class ScheduledController( - Element, string[string] ParameterNameMap) : - AbstractScheduledControllerWithOperationImpl { + Target, string[string] ParameterNameMap) : AbstractScheduledController { public: /// enum AliveManagementAvailable = - is(typeof((Element e) => e.alive)) && - is(ReturnType!((Element e) => e.alive) == bool); + is(typeof((Target x) => x.alive)) && + is(ReturnType!((Target x) => x.alive) == bool); /// enum MatrixModificationAvailable = - is(typeof((Element e) => e.matrix)) && - is(ReturnType!((Element e) => e.matrix) == mat3); + is(typeof((Target x) => x.matrix)) && + is(ReturnType!((Target x) => x.matrix) == mat3); /// enum AutoInitializationAvailable = - is(typeof((Element e) => e.Initialize())); + is(typeof((Target x) => x.Initialize())); /// this( - Element element, + Target target, in VarStoreInterface varstore, in ParametersBlock[] operations) { super(varstore, operations); - element_ = element; + target_ = target; } protected: override void PrepareOperation(ref in ParametersBlock params) { static if (AutoInitializationAvailable) { - element_.Initialize(); + target_.Initialize(); } static if (AliveManagementAvailable) { - element_.alive = true; + target_.alive = true; } static if (MatrixModificationAvailable) { matrix_factory_ = matrix_factory_.init; @@ -64,12 +55,12 @@ class ScheduledController( override void ProcessOperation(float time, ref in ParametersBlock params) { super.ProcessOperation(time, params); static if (MatrixModificationAvailable) { - element_.matrix = matrix_factory_.Create().transposed; + target_.matrix = matrix_factory_.Create().transposed; } } override void FinalizeOperation(ref in ParametersBlock params) { static if (AliveManagementAvailable) { - element_.alive = false; + target_.alive = false; } } @@ -77,17 +68,16 @@ class ScheduledController( switch (name) { static foreach (map_name, code; ParameterNameMap) { case map_name: - return mixin("element_."~code); + return mixin("target_."~code); } default: return super.GetVariable(name); } } - override void SetParameter(Nullable!float time, ref in Parameter param) { - auto vars = VarStore(this, time); + override void SetParameter(ref in Parameter param, ref in VarStore vars) { switch (param.name) { static foreach (map_name, code; ParameterNameMap) { case map_name: - param.CalculateParameter(mixin("element_."~code), vars); + param.CalculateParameter(mixin("target_."~code), vars); return; } default: @@ -95,90 +85,12 @@ class ScheduledController( static if (MatrixModificationAvailable) { if (param.CalculateMatrixParameter(matrix_factory_, vars)) return; } - super.SetParameter(time, param); + super.SetParameter(param, vars); } - Element element_; + Target target_; static if (MatrixModificationAvailable) { MatrixFactory matrix_factory_; } } - -/// -struct ElementScheduledControllerFactory(ScheduledController, ElementDrawer) - if (is(ScheduledController : ScheduledControllerInterface) && - is(ElementDrawer : ElementDrawerInterface)) { - public: - /// ScheduledController's first constructor's first argument type. - alias Element = - Parameters!(__traits(getOverloads, ScheduledController, "__ctor")[0])[0]; - - static assert(is(Element : ElementInterface)); - - /// ElementDrawer's first constructor's first argument type. - alias ElementProgram = - Parameters!(__traits(getOverloads, ElementDrawer, "__ctor")[0])[0]; - - static assert(staticIndexOf!( - ElementProgram, ProgramSet.Programs.Types) >= 0); - static assert(is(Element[] : - Parameters!(__traits(getOverloads, ElementDrawer, "__ctor")[0])[1])); - - /// - this(ProgramSet programs, VarStoreInterface varstore) { - program_ = programs.Get!ElementProgram; - varstore_ = varstore; - } - - /// - void Create(R)(R params, ContextBuilderInterface builder) - if (isInputRange!R && is(ElementType!R == ParametersBlock)) { - auto parallelized = params.ParallelizeParams(); - auto elements = appender!(Element[]); - - foreach (ref serial; parallelized) { - auto element = new Element; - elements ~= element; - builder.AddElement(element); - builder.AddScheduledController( - new ScheduledController(element, varstore_, serial)); - } - if (elements[].length > 0) { - builder.AddElementDrawer(new ElementDrawer(program_, elements[])); - } - } - - private: - ElementProgram program_; - VarStoreInterface varstore_; -} - -private ParametersBlock[][] ParallelizeParams(R)(R params) - if (isInputRange!R && is(ElementType!R == ParametersBlock)) { - ParametersBlock[][] parallelized; - foreach (ref param; params) { - auto inserted = false; - foreach (ref serial; parallelized) { - const found_index = serial. - countUntil!(x => x.period.start > param.period.start); - const insert_index = - found_index >= 0? found_index.to!size_t: serial.length; - - const intersect_prev = insert_index >= 1 && - IsPeriodIntersectedToPeriod(serial[insert_index-1].period, param.period); - const intersect_next = insert_index < serial.length && - IsPeriodIntersectedToPeriod(serial[insert_index].period, param.period); - - if (!intersect_prev && !intersect_next) { - serial = serial[0..insert_index]~ param ~serial[insert_index..$]; - inserted = true; - break; - } - } - if (!inserted) { - parallelized ~= [param]; - } - } - return parallelized; -} diff --git a/sjplayer/src/sjplayer/ScheduledControllerInterface.d b/sjplayer/src/sjplayer/ScheduledControllerInterface.d index eb4c40e..9014887 100644 --- a/sjplayer/src/sjplayer/ScheduledControllerInterface.d +++ b/sjplayer/src/sjplayer/ScheduledControllerInterface.d @@ -1,121 +1,9 @@ /// License: MIT module sjplayer.ScheduledControllerInterface; -import std.algorithm, - std.array, - std.exception, - std.format, - std.typecons; - -import sjscript; - -import sjplayer.VarStoreInterface, - sjplayer.util.Parameter, - sjplayer.util.Period; - /// interface ScheduledControllerInterface { public: /// void Operate(float time); } - -/// -abstract class AbstractScheduledController : ScheduledControllerInterface { - public: - /// The operations must be sorted. - this(in ParametersBlock[] operations) { - operations_ = operations; - } - - override void Operate(float time) { - scope(exit) last_operation_time_ = time; - - if (next_operation_index_ >= 1) { - assert(next_operation_index_ <= operations_.length); - - const last_operation = &operations_[next_operation_index_-1]; - const period = last_operation.period; - if (IsTimeInPeriod(time, period)) { - ProcessOperation(period.ConvertToRelativeTime(time), *last_operation); - return; - } - FinalizeOperation(*last_operation); - } - - if (next_operation_index_ >= operations_.length) return; - - const next_operation = &operations_[next_operation_index_]; - if (IsTimeInPeriod(time, next_operation.period)) { - PrepareOperation(*next_operation); - ProcessOperation( - next_operation.period.ConvertToRelativeTime(time), *next_operation); - ++next_operation_index_; - } - } - - protected: - abstract void PrepareOperation(ref in ParametersBlock params); - - abstract void ProcessOperation(float time, ref in ParametersBlock params); - - abstract void FinalizeOperation(ref in ParametersBlock params); - - private: - const ParametersBlock[] operations_; - - float last_operation_time_ = -1; - - size_t next_operation_index_; -} - -/// -abstract class AbstractScheduledControllerWithOperationImpl : - AbstractScheduledController { - public: - /// - this(in VarStoreInterface varstore, in ParametersBlock[] operations) { - super(operations); - varstore_ = varstore; - } - - protected: - static struct VarStore { - public: - float opIndex(string name) { - if (!time_.isNull && name == "time") return time_.get; - return this_.GetVariable(name); - } - private: - AbstractScheduledControllerWithOperationImpl this_; - Nullable!float time_; - } - - override void PrepareOperation(ref in ParametersBlock params) { - user_vars_.clear(); - params.parameters. - filter!(x => x.type == ParameterType.OnceAssign). - each !(x => SetParameter(Nullable!float.init, x)); - } - override void ProcessOperation(float time, ref in ParametersBlock params) { - params.parameters. - filter!(x => x.type != ParameterType.OnceAssign). - each !(x => SetParameter(time.nullable, x)); - } - - float GetVariable(string name) const { - if (name in user_vars_) return user_vars_[name]; - return varstore_[name]; - } - void SetParameter(Nullable!float time, ref in Parameter param) { - (param.name.length >= 2 && param.name[0..2] == "__"). - enforce("user defined variables must be prefixed '__'"); - user_vars_[param.name] = 0; - param.CalculateParameter(user_vars_[param.name], VarStore(this, time)); - } - - private: - const VarStoreInterface varstore_; - - float[string] user_vars_; -} diff --git a/sjplayer/src/sjplayer/util/Period.d b/sjplayer/src/sjplayer/util/Period.d index 72660dc..628f94b 100644 --- a/sjplayer/src/sjplayer/util/Period.d +++ b/sjplayer/src/sjplayer/util/Period.d @@ -4,7 +4,7 @@ module sjplayer.util.Period; import sjscript; /// -bool IsTimeInPeriod(float time, in Period period) { +bool IsTimeInPeriod(in Period period, float time) { return period.start <= time && time < period.end; }