Add avoidance behavior & direction cluster behavior & central cluster behavior #53
86
source/Behavior/Avoidance.ts
Normal file
86
source/Behavior/Avoidance.ts
Normal file
@ -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<IAvoidanceBehaviorParameter, IAvoidanceBehaviorEvent> {
|
||||||
|
|
||||||
|
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<string, Record<string, string>> = {
|
||||||
|
"$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 };
|
@ -7,6 +7,9 @@ import { Tracking } from "@Behavior/Tracking";
|
|||||||
import { ContactAttacking } from "@Behavior/ContactAttacking";
|
import { ContactAttacking } from "@Behavior/ContactAttacking";
|
||||||
import { ContactAssimilate } from "@Behavior/ContactAssimilate";
|
import { ContactAssimilate } from "@Behavior/ContactAssimilate";
|
||||||
import { DelayAssimilate } from "@Behavior/DelayAssimilate";
|
import { DelayAssimilate } from "@Behavior/DelayAssimilate";
|
||||||
|
import { Avoidance } from "@Behavior/Avoidance";
|
||||||
|
import { DirectionCluster } from "@Behavior/DirectionCluster";
|
||||||
|
import { CentralCluster } from "@Behavior/CentralCluster";
|
||||||
|
|
||||||
const AllBehaviors: IAnyBehaviorRecorder[] = [
|
const AllBehaviors: IAnyBehaviorRecorder[] = [
|
||||||
new BehaviorRecorder(Template),
|
new BehaviorRecorder(Template),
|
||||||
@ -17,6 +20,9 @@ const AllBehaviors: IAnyBehaviorRecorder[] = [
|
|||||||
new BehaviorRecorder(ContactAttacking),
|
new BehaviorRecorder(ContactAttacking),
|
||||||
new BehaviorRecorder(ContactAssimilate),
|
new BehaviorRecorder(ContactAssimilate),
|
||||||
new BehaviorRecorder(DelayAssimilate),
|
new BehaviorRecorder(DelayAssimilate),
|
||||||
|
new BehaviorRecorder(Avoidance),
|
||||||
|
new BehaviorRecorder(DirectionCluster),
|
||||||
|
new BehaviorRecorder(CentralCluster),
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
96
source/Behavior/CentralCluster.ts
Normal file
96
source/Behavior/CentralCluster.ts
Normal file
@ -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<ICentralClusterBehaviorParameter, ICentralClusterBehaviorEvent> {
|
||||||
|
|
||||||
|
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<string, Record<string, string>> = {
|
||||||
|
"$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 };
|
93
source/Behavior/DirectionCluster.ts
Normal file
93
source/Behavior/DirectionCluster.ts
Normal file
@ -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<IDirectionClusterBehaviorParameter, IDirectionClusterBehaviorEvent> {
|
||||||
|
|
||||||
|
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<string, Record<string, string>> = {
|
||||||
|
"$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 };
|
@ -31,11 +31,11 @@ class PhysicsDynamics extends Behavior<IPhysicsDynamicsBehaviorParameter, IPhysi
|
|||||||
limit: { name: "$Limit", type: "boolean", defaultValue: true },
|
limit: { name: "$Limit", type: "boolean", defaultValue: true },
|
||||||
maxAcceleration: {
|
maxAcceleration: {
|
||||||
name: "$Max.Acceleration", type: "number", defaultValue: 6.25,
|
name: "$Max.Acceleration", type: "number", defaultValue: 6.25,
|
||||||
numberStep: .1, numberMin: 0, condition: { key: "limit", value: true }
|
numberStep: .1, numberMin: 0.0001, condition: { key: "limit", value: true }
|
||||||
},
|
},
|
||||||
maxVelocity: {
|
maxVelocity: {
|
||||||
name: "$Max.Velocity", type: "number", defaultValue: 12.5,
|
name: "$Max.Velocity", type: "number", defaultValue: 12.5,
|
||||||
numberStep: .1, numberMin: 0, condition: { key: "limit", value: true }
|
numberStep: .1, numberMin: 0.0001, condition: { key: "limit", value: true }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ class PhysicsDynamics extends Behavior<IPhysicsDynamicsBehaviorParameter, IPhysi
|
|||||||
// 加速度约束
|
// 加速度约束
|
||||||
if (this.parameter.limit) {
|
if (this.parameter.limit) {
|
||||||
const lengthA = individual.vectorLength(individual.acceleration);
|
const lengthA = individual.vectorLength(individual.acceleration);
|
||||||
if (lengthA > this.parameter.maxAcceleration) {
|
if (lengthA > this.parameter.maxAcceleration && lengthA) {
|
||||||
individual.acceleration[0] = individual.acceleration[0] * 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[1] = individual.acceleration[1] * this.parameter.maxAcceleration / lengthA;
|
||||||
individual.acceleration[2] = individual.acceleration[2] * this.parameter.maxAcceleration / lengthA;
|
individual.acceleration[2] = individual.acceleration[2] * this.parameter.maxAcceleration / lengthA;
|
||||||
@ -79,7 +79,7 @@ class PhysicsDynamics extends Behavior<IPhysicsDynamicsBehaviorParameter, IPhysi
|
|||||||
// 速度约束
|
// 速度约束
|
||||||
if (this.parameter.limit) {
|
if (this.parameter.limit) {
|
||||||
const lengthV = individual.vectorLength(individual.velocity);
|
const lengthV = individual.vectorLength(individual.velocity);
|
||||||
if (lengthV > this.parameter.maxVelocity) {
|
if (lengthV > this.parameter.maxVelocity && lengthV) {
|
||||||
individual.velocity[0] = individual.velocity[0] * 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[1] = individual.velocity[1] * this.parameter.maxVelocity / lengthV;
|
||||||
individual.velocity[2] = individual.velocity[2] * this.parameter.maxVelocity / lengthV;
|
individual.velocity[2] = individual.velocity[2] * this.parameter.maxVelocity / lengthV;
|
||||||
|
Loading…
Reference in New Issue
Block a user