diff --git a/source/Behavior/Behavior.ts b/source/Behavior/Behavior.ts index addd747..33e6a52 100644 --- a/source/Behavior/Behavior.ts +++ b/source/Behavior/Behavior.ts @@ -3,12 +3,14 @@ import { Template } from "@Behavior/Template"; import { Dynamics } from "@Behavior/Dynamics"; import { Brownian } from "@Behavior/Brownian"; import { BoundaryConstraint } from "@Behavior/BoundaryConstraint"; +import { Tracking } from "@Behavior/Tracking"; const AllBehaviors: IAnyBehaviorRecorder[] = [ new BehaviorRecorder(Template), new BehaviorRecorder(Dynamics), new BehaviorRecorder(Brownian), new BehaviorRecorder(BoundaryConstraint), + new BehaviorRecorder(Tracking), ] /** @@ -54,4 +56,13 @@ function categoryBehaviors(behaviors: IAnyBehaviorRecorder[]): ICategory[] { return res; } -export { AllBehaviors, AllBehaviorsWithCategory, ICategory as ICategoryBehavior }; \ No newline at end of file +function getBehaviorById(id: string): IAnyBehaviorRecorder { + for (let i = 0; i < AllBehaviors.length; i++) { + if (AllBehaviors[i].behaviorId === id) { + return AllBehaviors[i]; + } + } + return getBehaviorById("Template"); +} + +export { AllBehaviors, AllBehaviorsWithCategory, getBehaviorById, ICategory as ICategoryBehavior }; \ No newline at end of file diff --git a/source/Behavior/BoundaryConstraint.ts b/source/Behavior/BoundaryConstraint.ts index befd8c5..0d4b9a7 100644 --- a/source/Behavior/BoundaryConstraint.ts +++ b/source/Behavior/BoundaryConstraint.ts @@ -28,7 +28,7 @@ class BoundaryConstraint extends Behavior { let rangeList: Range[] = this.parameter.range.objects; let fx = 0; diff --git a/source/Behavior/Brownian.ts b/source/Behavior/Brownian.ts index 62e04aa..b060929 100644 --- a/source/Behavior/Brownian.ts +++ b/source/Behavior/Brownian.ts @@ -31,7 +31,7 @@ class Brownian extends Behavior { const {maxFrequency, minFrequency, maxStrength, minStrength} = this.parameter; diff --git a/source/Behavior/Dynamics.ts b/source/Behavior/Dynamics.ts index 34c122a..444af6f 100644 --- a/source/Behavior/Dynamics.ts +++ b/source/Behavior/Dynamics.ts @@ -31,7 +31,7 @@ class Dynamics extends Behavior { // 计算当前速度 const currentV = individual.vectorLength(individual.velocity); diff --git a/source/Behavior/Template.ts b/source/Behavior/Template.ts index 48d75be..bd84347 100644 --- a/source/Behavior/Template.ts +++ b/source/Behavior/Template.ts @@ -14,6 +14,8 @@ type ITemplateBehaviorParameter = { testLR: "LR"; testLG: "LG"; testVec: "vec"; + testCG: "CG", + testCLG: "CLG", } type ITemplateBehaviorEvent = {} @@ -42,10 +44,12 @@ class Template extends Behavior { } diff --git a/source/Behavior/Tracking.ts b/source/Behavior/Tracking.ts new file mode 100644 index 0000000..260edab --- /dev/null +++ b/source/Behavior/Tracking.ts @@ -0,0 +1,84 @@ +import { Behavior } from "@Model/Behavior"; +import { Group } from "@Model/Group"; +import { Individual } from "@Model/Individual"; +import { Model } from "@Model/Model"; + +type ITrackingBehaviorParameter = { + target: "LG", + strength: "number" +} + +type ITrackingBehaviorEvent = {} + +class Tracking extends Behavior { + + public override behaviorId: string = "Tracking"; + + public override behaviorName: string = "$Title"; + + public override iconName: string = "BullseyeTarget"; + + public override describe: string = "$Intro"; + + public override category: string = "$Interactive"; + + public override parameterOption = { + target: { type: "CLG", name: "$Target" }, + strength: { type: "number", name: "$Strength", defaultValue: 10, numberMin: 0, numberStep: .1 } + }; + + public effect = (individual: Individual, group: Group, model: Model, t: number): void => { + let target: Individual | undefined; + let currentDistant: number = Infinity; + + for (let i = 0; i < this.parameter.target.objects.length; i++) { + const targetGroup = this.parameter.target.objects[i]; + + targetGroup.individuals.forEach((targetIndividual) => { + + // 排除自己 + if (targetIndividual === individual) return; + let dis = targetIndividual.distanceTo(individual); + + if (dis < currentDistant) { + target = targetIndividual; + currentDistant = dis; + } + + }); + } + + if (target) { + individual.applyForce( + (target.position[0] - individual.position[0]) * this.parameter.strength, + (target.position[1] - individual.position[1]) * this.parameter.strength, + (target.position[2] - individual.position[2]) * this.parameter.strength + ); + } + } + + public override terms: Record> = { + "$Title": { + "ZH_CN": "追踪", + "EN_US": "Tracking" + }, + "$Target": { + "ZH_CN": "追踪目标", + "EN_US": "Tracking target" + }, + "$Strength": { + "ZH_CN": "追踪强度系数", + "EN_US": "Tracking intensity coefficient" + }, + "$Intro": { + "ZH_CN": "个体将主动向最近的目标群个体发起追踪", + "EN_US": "The individual will actively initiate tracking to the nearest target group individual" + }, + "$Interactive": { + "ZH_CN": "交互", + "EN_US": "Interactive" + } + }; +} + +export { Tracking }; \ No newline at end of file diff --git a/source/Input/ObjectPicker/ObjectPicker.tsx b/source/Input/ObjectPicker/ObjectPicker.tsx index 0fd0ff6..8bbc26f 100644 --- a/source/Input/ObjectPicker/ObjectPicker.tsx +++ b/source/Input/ObjectPicker/ObjectPicker.tsx @@ -59,6 +59,10 @@ class ObjectPicker extends Component; + /** + * 指定当前群的 Key + */ + public currentGroupKey: Array = []; + /** * 对象参数列表 */ @@ -222,7 +227,7 @@ class Behavior< * @param model 模型 * @param t 经过时间 */ - public effect(individual: Individual, group: Group, model: Model, t: number): void {}; + public effect?: (individual: Individual, group: Group, model: Model, t: number) => void; /** * 作用影响于个体 @@ -231,7 +236,7 @@ class Behavior< * @param model 模型 * @param t 经过时间 */ - public afterEffect(individual: Individual, group: Group, model: Model, t: number): void {}; + public afterEffect?: (individual: Individual, group: Group, model: Model, t: number) => void; /** * 全部影响作用后 @@ -240,7 +245,7 @@ class Behavior< * @param model 模型 * @param t 经过时间 */ - public finalEffect(individual: Individual, group: Group, model: Model, t: number): void {}; + public finalEffect?: (individual: Individual, group: Group, model: Model, t: number) => void; } diff --git a/source/Model/Group.ts b/source/Model/Group.ts index 79b0bd9..d28b7f1 100644 --- a/source/Model/Group.ts +++ b/source/Model/Group.ts @@ -1,6 +1,6 @@ import { Individual } from "@Model/Individual"; import { CtrlObject } from "@Model/CtrlObject"; -import type { Behavior } from "@Model/Behavior"; +import type { Behavior, IAnyBehavior } from "@Model/Behavior"; import { Label } from "@Model/Label"; import { Range } from "@Model/Range"; import { Model, ObjectID } from "@Model/Model"; @@ -308,7 +308,7 @@ class Group extends CtrlObject { /** * 行为列表 */ - public behaviors: Behavior[] = []; + public behaviors: IAnyBehavior[] = []; /** * 添加行为 @@ -358,15 +358,37 @@ class Group extends CtrlObject { * @param */ public runner(t: number, effectType: "finalEffect" | "effect" | "afterEffect" ): void { - this.individuals.forEach((individual) => { - for(let j = 0; j < this.behaviors.length; j++) { - if (this.behaviors[j].isDeleted()) { - continue; + + for(let j = 0; j < this.behaviors.length; j++) { + + const behavior = this.behaviors[j]; + if (behavior.isDeleted()) { + continue; + } + + const runnerFunction = behavior[effectType]; + if (!runnerFunction) { + continue; + } + + for (let k = 0; k < behavior.currentGroupKey.length; k++) { + + let parameterCache = behavior.parameter[ + behavior.currentGroupKey[k] as string + ]; + + if (Array.isArray(parameterCache?.objects)) { + parameterCache.objects = [this]; + } else { - this.behaviors[j][effectType](individual, this, this.model, t); + parameterCache.objects = this; } - } - }); + } + + this.individuals.forEach((individual) => { + runnerFunction(individual, this, this.model, t); + }); + } } /** diff --git a/source/Model/Model.ts b/source/Model/Model.ts index 2ea6f02..c73f707 100644 --- a/source/Model/Model.ts +++ b/source/Model/Model.ts @@ -66,6 +66,11 @@ class Model extends Emitter { */ public allGroupLabel = new Label(this, "AllGroup").setBuildInLabel(); + /** + * 内置标签-全部群标签 + */ + public currentGroupLabel = new Label(this, "CurrentGroupLabel").setBuildInLabel(); + /** * 添加标签 */ @@ -223,6 +228,7 @@ class Model extends Emitter { public updateBehaviorParameter() { for (let i = 0; i < this.behaviorPool.length; i++) { const behavior = this.behaviorPool[i]; + behavior.currentGroupKey = []; for (let key in behavior.parameterOption) { switch (behavior.parameterOption[key].type) { @@ -236,11 +242,17 @@ class Model extends Emitter { } break; + case "CG": case "G": - const dataG: IParamValue<"G"> = behavior.parameter[key]; + const dataG: IParamValue<"CG"> = behavior.parameter[key]; dataG.objects = undefined; + + if (dataG.picker instanceof Label && dataG.picker.id === this.currentGroupLabel.id) { + behavior.currentGroupKey.push(key); + dataG.objects = undefined; + } - if (dataG.picker instanceof Group && !dataG.picker.isDeleted()) { + else if (dataG.picker instanceof Group && !dataG.picker.isDeleted()) { dataG.objects = dataG.picker; } break; @@ -260,8 +272,9 @@ class Model extends Emitter { } break; + case "CLG": case "LG": - const dataLG: IParamValue<"LG"> = behavior.parameter[key]; + const dataLG: IParamValue<"CLG"> = behavior.parameter[key]; dataLG.objects = []; if (dataLG.picker instanceof Group && !dataLG.picker.isDeleted()) { @@ -269,9 +282,16 @@ class Model extends Emitter { } if (dataLG.picker instanceof Label && !dataLG.picker.isDeleted()) { - dataLG.objects = this.getObjectByLabel(dataLG.picker).filter((obj) => { - return obj instanceof Group; - }) as any; + + if (dataLG.picker.id === this.currentGroupLabel.id) { + behavior.currentGroupKey.push(key); + dataLG.objects = []; + + } else { + dataLG.objects = this.getObjectByLabel(dataLG.picker).filter((obj) => { + return obj instanceof Group; + }) as any; + } } break; } diff --git a/source/Model/Parameter.ts b/source/Model/Parameter.ts index 54bf988..20705cd 100644 --- a/source/Model/Parameter.ts +++ b/source/Model/Parameter.ts @@ -22,6 +22,8 @@ type IMapObjectParamTypeKeyToType = { "G": IObjectParamCacheType; "LR": IObjectParamCacheType