From f660aefa4c7942c9fa139963dbc14148d667ff15 Mon Sep 17 00:00:00 2001 From: MrKBear Date: Fri, 6 May 2022 18:55:31 +0800 Subject: [PATCH 1/3] Add avoidance behavior --- source/Behavior/Avoidance.ts | 86 ++++++++++++++++++++++++++++++++++++ source/Behavior/Behavior.ts | 2 + 2 files changed, 88 insertions(+) create mode 100644 source/Behavior/Avoidance.ts diff --git a/source/Behavior/Avoidance.ts b/source/Behavior/Avoidance.ts new file mode 100644 index 0000000..8982e88 --- /dev/null +++ b/source/Behavior/Avoidance.ts @@ -0,0 +1,86 @@ +import { Behavior } from "@Model/Behavior"; +import { Group } from "@Model/Group"; +import { Individual } from "@Model/Individual"; +import { Model } from "@Model/Model"; + +type IAvoidanceBehaviorParameter = { + avoid: "CLG", + strength: "number", + range: "number" +} + +type IAvoidanceBehaviorEvent = {} + +class Avoidance extends Behavior { + + public override behaviorId: string = "Avoidance"; + + public override behaviorName: string = "$Title"; + + public override iconName: string = "FastMode"; + + public override describe: string = "$Intro"; + + public override category: string = "$Interactive"; + + public override parameterOption = { + avoid: { name: "$Avoid", type: "CLG" }, + strength: { type: "number", name: "$Strength", defaultValue: 1, numberMin: 0, numberStep: .1 }, + range: { type: "number", name: "$Range", defaultValue: 4, numberMin: 0, numberStep: .1 } + }; + + public effect = (individual: Individual, group: Group, model: Model, t: number): void => { + + let currentDistant: number = Infinity; + let avoidTarget = undefined as Individual | undefined; + + for (let i = 0; i < this.parameter.avoid.objects.length; i++) { + const targetGroup = this.parameter.avoid.objects[i]; + + targetGroup.individuals.forEach((targetIndividual) => { + + // 排除自己 + if (targetIndividual === individual) return; + let dis = targetIndividual.distanceTo(individual); + + if (dis < currentDistant && dis <= this.parameter.range) { + avoidTarget = targetIndividual; + currentDistant = dis; + } + }); + } + + if (avoidTarget && currentDistant !== Infinity) { + individual.applyForce( + (individual.position[0] - avoidTarget.position[0]) * this.parameter.strength / currentDistant, + (individual.position[1] - avoidTarget.position[1]) * this.parameter.strength / currentDistant, + (individual.position[2] - avoidTarget.position[2]) * this.parameter.strength / currentDistant + ); + } + } + + public override terms: Record> = { + "$Title": { + "ZH_CN": "躲避", + "EN_US": "Avoidance" + }, + "$Intro": { + "ZH_CN": "远离视野范围内最近的躲避目标", + "EN_US": "Stay away from the nearest evasive target in the field of vision" + }, + "$Avoid": { + "ZH_CN": "躲避对象", + "EN_US": "Avoid object" + }, + "$Strength": { + "ZH_CN": "躲避强度", + "EN_US": "Avoidance intensity" + }, + "$Range": { + "ZH_CN": "视野范围", + "EN_US": "Field of vision" + } + }; +} + +export { Avoidance }; \ No newline at end of file diff --git a/source/Behavior/Behavior.ts b/source/Behavior/Behavior.ts index 40b72b5..2ce5ccc 100644 --- a/source/Behavior/Behavior.ts +++ b/source/Behavior/Behavior.ts @@ -7,6 +7,7 @@ import { Tracking } from "@Behavior/Tracking"; import { ContactAttacking } from "@Behavior/ContactAttacking"; import { ContactAssimilate } from "@Behavior/ContactAssimilate"; import { DelayAssimilate } from "@Behavior/DelayAssimilate"; +import { Avoidance } from "@Behavior/Avoidance"; const AllBehaviors: IAnyBehaviorRecorder[] = [ new BehaviorRecorder(Template), @@ -17,6 +18,7 @@ const AllBehaviors: IAnyBehaviorRecorder[] = [ new BehaviorRecorder(ContactAttacking), new BehaviorRecorder(ContactAssimilate), new BehaviorRecorder(DelayAssimilate), + new BehaviorRecorder(Avoidance), ] /** -- 2.45.2 From 974cd2951d5267dfb17504ef327c27679eb6c400 Mon Sep 17 00:00:00 2001 From: MrKBear Date: Fri, 6 May 2022 23:12:00 +0800 Subject: [PATCH 2/3] Add direction cluster behavior --- source/Behavior/Behavior.ts | 2 + source/Behavior/DirectionCluster.ts | 93 +++++++++++++++++++++++++++++ source/Behavior/PhysicsDynamics.ts | 8 +-- 3 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 source/Behavior/DirectionCluster.ts diff --git a/source/Behavior/Behavior.ts b/source/Behavior/Behavior.ts index 2ce5ccc..091199b 100644 --- a/source/Behavior/Behavior.ts +++ b/source/Behavior/Behavior.ts @@ -8,6 +8,7 @@ import { ContactAttacking } from "@Behavior/ContactAttacking"; import { ContactAssimilate } from "@Behavior/ContactAssimilate"; import { DelayAssimilate } from "@Behavior/DelayAssimilate"; import { Avoidance } from "@Behavior/Avoidance"; +import { DirectionCluster } from "@Behavior/DirectionCluster"; const AllBehaviors: IAnyBehaviorRecorder[] = [ new BehaviorRecorder(Template), @@ -19,6 +20,7 @@ const AllBehaviors: IAnyBehaviorRecorder[] = [ new BehaviorRecorder(ContactAssimilate), new BehaviorRecorder(DelayAssimilate), new BehaviorRecorder(Avoidance), + new BehaviorRecorder(DirectionCluster), ] /** diff --git a/source/Behavior/DirectionCluster.ts b/source/Behavior/DirectionCluster.ts new file mode 100644 index 0000000..7ac90d3 --- /dev/null +++ b/source/Behavior/DirectionCluster.ts @@ -0,0 +1,93 @@ +import { Behavior } from "@Model/Behavior"; +import { Group } from "@Model/Group"; +import { Individual } from "@Model/Individual"; +import { Model } from "@Model/Model"; + +type IDirectionClusterBehaviorParameter = { + cluster: "CLG", + strength: "number", + range: "number" +} + +type IDirectionClusterBehaviorEvent = {} + +class DirectionCluster extends Behavior { + + public override behaviorId: string = "DirectionCluster"; + + public override behaviorName: string = "$Title"; + + public override iconName: string = "RawSource"; + + public override describe: string = "$Intro"; + + public override category: string = "$Interactive"; + + public override parameterOption = { + cluster: { name: "$Cluster", type: "CLG" }, + strength: { type: "number", name: "$Strength", defaultValue: 1, numberMin: 0, numberStep: .1 }, + range: { type: "number", name: "$Range", defaultValue: 4, numberMin: 0, numberStep: .1 } + }; + + public effect = (individual: Individual, group: Group, model: Model, t: number): void => { + + let findCount = 0; + let centerDir: number[] = [0, 0, 0]; + + for (let i = 0; i < this.parameter.cluster.objects.length; i++) { + const targetGroup = this.parameter.cluster.objects[i]; + + targetGroup.individuals.forEach((targetIndividual) => { + + // 排除自己 + if (targetIndividual === individual) return; + let dis = targetIndividual.distanceTo(individual); + + if (dis <= this.parameter.range) { + centerDir[0] += targetIndividual.velocity[0]; + centerDir[1] += targetIndividual.velocity[1]; + centerDir[2] += targetIndividual.velocity[2]; + findCount ++; + } + }); + } + + if (findCount > 0) { + + let length = individual.vectorLength(centerDir); + + if (length) { + individual.applyForce( + centerDir[0] * this.parameter.strength / length, + centerDir[1] * this.parameter.strength / length, + centerDir[2] * this.parameter.strength / length + ); + } + } + } + + public override terms: Record> = { + "$Title": { + "ZH_CN": "方向结群", + "EN_US": "Directional clustering" + }, + "$Intro": { + "ZH_CN": "个体将按照视野范围内目标方向结群对象个体的平均移动方向移动", + "EN_US": "Individuals will move according to the average moving direction of the grouped object individuals in the target direction within the field of vision" + }, + "$Cluster": { + "ZH_CN": "方向结群对象", + "EN_US": "Directional clustering object" + }, + "$Strength": { + "ZH_CN": "结群强度", + "EN_US": "Clustering strength" + }, + "$Range": { + "ZH_CN": "视野范围", + "EN_US": "Field of vision" + } + }; +} + +export { DirectionCluster }; \ No newline at end of file diff --git a/source/Behavior/PhysicsDynamics.ts b/source/Behavior/PhysicsDynamics.ts index e29ee85..b1c4846 100644 --- a/source/Behavior/PhysicsDynamics.ts +++ b/source/Behavior/PhysicsDynamics.ts @@ -31,11 +31,11 @@ class PhysicsDynamics extends Behavior this.parameter.maxAcceleration) { + if (lengthA > this.parameter.maxAcceleration && lengthA) { individual.acceleration[0] = individual.acceleration[0] * this.parameter.maxAcceleration / lengthA; individual.acceleration[1] = individual.acceleration[1] * this.parameter.maxAcceleration / lengthA; individual.acceleration[2] = individual.acceleration[2] * this.parameter.maxAcceleration / lengthA; @@ -79,7 +79,7 @@ class PhysicsDynamics extends Behavior this.parameter.maxVelocity) { + if (lengthV > this.parameter.maxVelocity && lengthV) { individual.velocity[0] = individual.velocity[0] * this.parameter.maxVelocity / lengthV; individual.velocity[1] = individual.velocity[1] * this.parameter.maxVelocity / lengthV; individual.velocity[2] = individual.velocity[2] * this.parameter.maxVelocity / lengthV; -- 2.45.2 From dfb3905c9ba3e6b7a0ed93949c9a5c3f01e96e25 Mon Sep 17 00:00:00 2001 From: MrKBear Date: Sat, 7 May 2022 12:09:05 +0800 Subject: [PATCH 3/3] Add central cluster behavior --- source/Behavior/Behavior.ts | 2 + source/Behavior/CentralCluster.ts | 96 +++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 source/Behavior/CentralCluster.ts diff --git a/source/Behavior/Behavior.ts b/source/Behavior/Behavior.ts index 091199b..b603a2f 100644 --- a/source/Behavior/Behavior.ts +++ b/source/Behavior/Behavior.ts @@ -9,6 +9,7 @@ import { ContactAssimilate } from "@Behavior/ContactAssimilate"; import { DelayAssimilate } from "@Behavior/DelayAssimilate"; import { Avoidance } from "@Behavior/Avoidance"; import { DirectionCluster } from "@Behavior/DirectionCluster"; +import { CentralCluster } from "@Behavior/CentralCluster"; const AllBehaviors: IAnyBehaviorRecorder[] = [ new BehaviorRecorder(Template), @@ -21,6 +22,7 @@ const AllBehaviors: IAnyBehaviorRecorder[] = [ new BehaviorRecorder(DelayAssimilate), new BehaviorRecorder(Avoidance), new BehaviorRecorder(DirectionCluster), + new BehaviorRecorder(CentralCluster), ] /** diff --git a/source/Behavior/CentralCluster.ts b/source/Behavior/CentralCluster.ts new file mode 100644 index 0000000..e924e56 --- /dev/null +++ b/source/Behavior/CentralCluster.ts @@ -0,0 +1,96 @@ +import { Behavior } from "@Model/Behavior"; +import { Group } from "@Model/Group"; +import { Individual } from "@Model/Individual"; +import { Model } from "@Model/Model"; + +type ICentralClusterBehaviorParameter = { + cluster: "CLG", + strength: "number", + range: "number" +} + +type ICentralClusterBehaviorEvent = {} + +class CentralCluster extends Behavior { + + public override behaviorId: string = "CentralCluster"; + + public override behaviorName: string = "$Title"; + + public override iconName: string = "ZoomToFit"; + + public override describe: string = "$Intro"; + + public override category: string = "$Interactive"; + + public override parameterOption = { + cluster: { name: "$Cluster", type: "CLG" }, + strength: { type: "number", name: "$Strength", defaultValue: 1, numberMin: 0, numberStep: .1 }, + range: { type: "number", name: "$Range", defaultValue: 4, numberMin: 0, numberStep: .1 } + }; + + public effect = (individual: Individual, group: Group, model: Model, t: number): void => { + + let findCount = 0; + let centerPos: number[] = [0, 0, 0]; + + for (let i = 0; i < this.parameter.cluster.objects.length; i++) { + const targetGroup = this.parameter.cluster.objects[i]; + + targetGroup.individuals.forEach((targetIndividual) => { + + // 排除自己 + if (targetIndividual === individual) return; + let dis = targetIndividual.distanceTo(individual); + + if (dis <= this.parameter.range) { + centerPos[0] += targetIndividual.position[0]; + centerPos[1] += targetIndividual.position[1]; + centerPos[2] += targetIndividual.position[2]; + findCount ++; + } + }); + } + + if (findCount > 0) { + + let dirX = centerPos[0] / findCount - individual.position[0]; + let dirY = centerPos[1] / findCount - individual.position[1]; + let dirZ = centerPos[2] / findCount - individual.position[2]; + let length = individual.vectorLength(dirX, dirY, dirZ); + + if (length > 0) { + individual.applyForce( + dirX * this.parameter.strength / length, + dirY * this.parameter.strength / length, + dirZ * this.parameter.strength / length + ); + } + } + } + + public override terms: Record> = { + "$Title": { + "ZH_CN": "中心结群", + "EN_US": "Central cluster" + }, + "$Intro": { + "ZH_CN": "个体将按照视野范围内目标方向结群对象个体的几何中心移动", + "EN_US": "The individual will move according to the geometric center of the grouped object individual in the target direction within the field of view" + }, + "$Cluster": { + "ZH_CN": "中心结群对象", + "EN_US": "Central clustering object" + }, + "$Strength": { + "ZH_CN": "结群强度", + "EN_US": "Clustering strength" + }, + "$Range": { + "ZH_CN": "视野范围", + "EN_US": "Field of vision" + } + }; +} + +export { CentralCluster }; \ No newline at end of file -- 2.45.2