Compare commits
No commits in common. "ac0fef2901a396910cb653adc0e3a0118e912d53" and "920958b1a2ba6c2ea5ac83009013a8a72a7508ce" have entirely different histories.
ac0fef2901
...
920958b1a2
@ -10,9 +10,6 @@ import { DelayAssimilate } from "@Behavior/DelayAssimilate";
|
|||||||
import { Avoidance } from "@Behavior/Avoidance";
|
import { Avoidance } from "@Behavior/Avoidance";
|
||||||
import { DirectionCluster } from "@Behavior/DirectionCluster";
|
import { DirectionCluster } from "@Behavior/DirectionCluster";
|
||||||
import { CentralCluster } from "@Behavior/CentralCluster";
|
import { CentralCluster } from "@Behavior/CentralCluster";
|
||||||
import { Manufacture } from "@Behavior/Manufacture";
|
|
||||||
import { Wastage } from "@Behavior/Wastage";
|
|
||||||
import { SampleTracking } from "@Behavior/SampleTracking";
|
|
||||||
|
|
||||||
const AllBehaviors: IAnyBehaviorRecorder[] = [
|
const AllBehaviors: IAnyBehaviorRecorder[] = [
|
||||||
new BehaviorRecorder(Template),
|
new BehaviorRecorder(Template),
|
||||||
@ -26,9 +23,6 @@ const AllBehaviors: IAnyBehaviorRecorder[] = [
|
|||||||
new BehaviorRecorder(Avoidance),
|
new BehaviorRecorder(Avoidance),
|
||||||
new BehaviorRecorder(DirectionCluster),
|
new BehaviorRecorder(DirectionCluster),
|
||||||
new BehaviorRecorder(CentralCluster),
|
new BehaviorRecorder(CentralCluster),
|
||||||
new BehaviorRecorder(Manufacture),
|
|
||||||
new BehaviorRecorder(Wastage),
|
|
||||||
new BehaviorRecorder(SampleTracking),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,17 +48,11 @@ class BoundaryConstraint extends Behavior<IBoundaryConstraintBehaviorParameter,
|
|||||||
|
|
||||||
if (ox || oy || oz) {
|
if (ox || oy || oz) {
|
||||||
|
|
||||||
const backFocus: number[] = [0, 0, 0];
|
let currentFLen = individual.vectorLength(rx, ry, rz);
|
||||||
|
|
||||||
if (ox) backFocus[0] = rx - rx * rangeList[i].radius[0] / Math.abs(rx);
|
|
||||||
if (oy) backFocus[1] = ry - ry * rangeList[i].radius[1] / Math.abs(ry);
|
|
||||||
if (oz) backFocus[2] = rz - rz * rangeList[i].radius[2] / Math.abs(rz);
|
|
||||||
|
|
||||||
let currentFLen = individual.vectorLength(backFocus);
|
|
||||||
if (currentFLen < fLen) {
|
if (currentFLen < fLen) {
|
||||||
fx = backFocus[0];
|
fx = rx;
|
||||||
fy = backFocus[1];
|
fy = ry;
|
||||||
fz = backFocus[2];
|
fz = rz;
|
||||||
fLen = currentFLen;
|
fLen = currentFLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,9 +7,7 @@ type IBrownianBehaviorParameter = {
|
|||||||
maxFrequency: "number",
|
maxFrequency: "number",
|
||||||
minFrequency: "number",
|
minFrequency: "number",
|
||||||
maxStrength: "number",
|
maxStrength: "number",
|
||||||
minStrength: "number",
|
minStrength: "number"
|
||||||
dirLimit: "boolean",
|
|
||||||
angle: "number"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type IBrownianBehaviorEvent = {}
|
type IBrownianBehaviorEvent = {}
|
||||||
@ -30,88 +28,9 @@ class Brownian extends Behavior<IBrownianBehaviorParameter, IBrownianBehaviorEve
|
|||||||
maxFrequency: { type: "number", name: "$Max.Frequency", defaultValue: 5, numberStep: .1, numberMin: 0 },
|
maxFrequency: { type: "number", name: "$Max.Frequency", defaultValue: 5, numberStep: .1, numberMin: 0 },
|
||||||
minFrequency: { type: "number", name: "$Min.Frequency", defaultValue: 0, numberStep: .1, numberMin: 0 },
|
minFrequency: { type: "number", name: "$Min.Frequency", defaultValue: 0, numberStep: .1, numberMin: 0 },
|
||||||
maxStrength: { type: "number", name: "$Max.Strength", defaultValue: 10, numberStep: .01, numberMin: 0 },
|
maxStrength: { type: "number", name: "$Max.Strength", defaultValue: 10, numberStep: .01, numberMin: 0 },
|
||||||
minStrength: { type: "number", name: "$Min.Strength", defaultValue: 0, numberStep: .01, numberMin: 0 },
|
minStrength: { type: "number", name: "$Min.Strength", defaultValue: 0, numberStep: .01, numberMin: 0 }
|
||||||
dirLimit: { type: "boolean", name: "$Direction.Limit", defaultValue: false },
|
|
||||||
angle: {
|
|
||||||
type: "number", name: "$Angle", defaultValue: 180, numberStep: 5,
|
|
||||||
numberMin: 0, numberMax: 360, condition: { key: "dirLimit", value: true }
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private randomFocus360(): number[] {
|
|
||||||
let randomVec = [
|
|
||||||
Math.random() * 2 - 1,
|
|
||||||
Math.random() * 2 - 1,
|
|
||||||
Math.random() * 2 - 1
|
|
||||||
];
|
|
||||||
|
|
||||||
let randomVecLen = (randomVec[0] ** 2 + randomVec[1] ** 2 + randomVec[2] ** 2) ** 0.5;
|
|
||||||
return [randomVec[0] / randomVecLen, randomVec[1] / randomVecLen, randomVec[2] / randomVecLen];
|
|
||||||
}
|
|
||||||
|
|
||||||
private rotateWithVec(vec: number[], r: number[], ang: number) {
|
|
||||||
|
|
||||||
const cos = Math.cos(ang); const sin = Math.sin(ang);
|
|
||||||
const a1 = r[0] ?? 0; const a2 = r[1] ?? 0; const a3 = r[2] ?? 0;
|
|
||||||
|
|
||||||
return [
|
|
||||||
(cos + (1 - cos) * a1 * a1) * (vec[0] ?? 0) +
|
|
||||||
((1 - cos) * a1 * a2 - sin * a3) * (vec[1] ?? 0) +
|
|
||||||
((1 - cos) * a1 * a3 + sin * a2) * (vec[2] ?? 0),
|
|
||||||
|
|
||||||
((1 - cos) * a1 * a2 + sin * a3) * (vec[0] ?? 0) +
|
|
||||||
(cos + (1 - cos) * a2 * a2) * (vec[1] ?? 0) +
|
|
||||||
((1 - cos) * a2 * a3 - sin * a1) * (vec[2] ?? 0),
|
|
||||||
|
|
||||||
((1 - cos) * a1 * a3 - sin * a2) * (vec[0] ?? 0) +
|
|
||||||
((1 - cos) * a2 * a3 + sin * a1) * (vec[1] ?? 0) +
|
|
||||||
(cos + (1 - cos) * a3 * a3) * (vec[2] ?? 0)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
private angle2Vector(v1: number[], v2: number[]): number {
|
|
||||||
return Math.acos(
|
|
||||||
(v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) /
|
|
||||||
(
|
|
||||||
(v1[0] ** 2 + v1[1] ** 2 + v1[2] ** 2) ** 0.5 *
|
|
||||||
(v2[0] ** 2 + v2[1] ** 2 + v2[2] ** 2) ** 0.5
|
|
||||||
)
|
|
||||||
) * 180 / Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
private randomFocusRange(dir: number[], angle: number): number[] {
|
|
||||||
|
|
||||||
// 计算 X-Z 投影
|
|
||||||
let pxz = [dir[0] ?? 0, 0, dir[2] ?? 0];
|
|
||||||
|
|
||||||
// 通过叉乘计算垂直向量
|
|
||||||
let dxz: number[];
|
|
||||||
|
|
||||||
// 如果投影向量没有长度,使用单位向量
|
|
||||||
if (pxz[0] ** 2 + pxz[2] ** 2 === 0) {
|
|
||||||
dxz = [0, 0, 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 通过叉乘计算垂直轴线
|
|
||||||
else {
|
|
||||||
dxz = [
|
|
||||||
dir[1] * pxz[2] - pxz[1] * dir[2],
|
|
||||||
dir[2] * pxz[0] - pxz[2] * dir[0],
|
|
||||||
dir[0] * pxz[1] - pxz[0] * dir[1]
|
|
||||||
];
|
|
||||||
|
|
||||||
let lenDxz = (dxz[0] ** 2 + dxz[1] ** 2 + dxz[2] ** 2) ** 0.5;
|
|
||||||
dxz = [dxz[0] / lenDxz, dxz[1] / lenDxz, dxz[2] / lenDxz];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 航偏角 360 随机旋转
|
|
||||||
let randomH = this.rotateWithVec(dxz, dir, Math.random() * Math.PI * 2);
|
|
||||||
|
|
||||||
// 俯仰角 180 * R 随机旋转
|
|
||||||
let randomP = this.rotateWithVec(dir, randomH, (Math.random() - 0.5) * 2 * angle * Math.PI / 180);
|
|
||||||
|
|
||||||
return randomP;
|
|
||||||
}
|
|
||||||
|
|
||||||
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
||||||
|
|
||||||
const {maxFrequency, minFrequency, maxStrength, minStrength} = this.parameter;
|
const {maxFrequency, minFrequency, maxStrength, minStrength} = this.parameter;
|
||||||
@ -122,49 +41,11 @@ class Brownian extends Behavior<IBrownianBehaviorParameter, IBrownianBehaviorEve
|
|||||||
|
|
||||||
currentTime += t;
|
currentTime += t;
|
||||||
if (currentTime > nextTime) {
|
if (currentTime > nextTime) {
|
||||||
|
|
||||||
let randomDir: number[];
|
|
||||||
|
|
||||||
// 开启角度限制
|
|
||||||
if (this.parameter.dirLimit) {
|
|
||||||
|
|
||||||
// 计算当前速度大小
|
|
||||||
const vLen = individual.vectorLength(individual.velocity);
|
|
||||||
|
|
||||||
// 随机旋转算法
|
|
||||||
if (vLen > 0) {
|
|
||||||
randomDir = this.randomFocusRange(
|
|
||||||
[
|
|
||||||
individual.velocity[0] / vLen,
|
|
||||||
individual.velocity[1] / vLen,
|
|
||||||
individual.velocity[2] / vLen
|
|
||||||
],
|
|
||||||
this.parameter.angle / 2
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isNaN(randomDir[0]) || isNaN(randomDir[1]) || isNaN(randomDir[2])) {
|
|
||||||
randomDir = this.randomFocus360()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
randomDir = this.randomFocus360()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 随机生成算法
|
|
||||||
else {
|
|
||||||
randomDir = this.randomFocus360()
|
|
||||||
}
|
|
||||||
|
|
||||||
const randomLength = minStrength + Math.random() * (maxStrength - minStrength);
|
|
||||||
|
|
||||||
individual.applyForce(
|
individual.applyForce(
|
||||||
randomDir[0] * randomLength,
|
minStrength + (Math.random() * 2 - 1) * (maxStrength - minStrength),
|
||||||
randomDir[1] * randomLength,
|
minStrength + (Math.random() * 2 - 1) * (maxStrength - minStrength),
|
||||||
randomDir[2] * randomLength
|
minStrength + (Math.random() * 2 - 1) * (maxStrength - minStrength)
|
||||||
);
|
);
|
||||||
|
|
||||||
nextTime = minFrequency + Math.random() * (maxFrequency - minFrequency);
|
nextTime = minFrequency + Math.random() * (maxFrequency - minFrequency);
|
||||||
currentTime = 0;
|
currentTime = 0;
|
||||||
}
|
}
|
||||||
@ -197,14 +78,6 @@ class Brownian extends Behavior<IBrownianBehaviorParameter, IBrownianBehaviorEve
|
|||||||
"$Min.Strength": {
|
"$Min.Strength": {
|
||||||
"ZH_CN": "最小强度",
|
"ZH_CN": "最小强度",
|
||||||
"EN_US": "Minimum strength"
|
"EN_US": "Minimum strength"
|
||||||
},
|
|
||||||
"$Direction.Limit": {
|
|
||||||
"ZH_CN": "开启角度限制",
|
|
||||||
"EN_US": "Enable limit angle"
|
|
||||||
},
|
|
||||||
"$Angle": {
|
|
||||||
"ZH_CN": "限制立体角 (deg)",
|
|
||||||
"EN_US": "Restricted solid angle (deg)"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import { Model } from "@Model/Model";
|
|||||||
type IContactAssimilateBehaviorParameter = {
|
type IContactAssimilateBehaviorParameter = {
|
||||||
target: "CLG",
|
target: "CLG",
|
||||||
assimilate: "CG",
|
assimilate: "CG",
|
||||||
self: "CG",
|
|
||||||
success: "number",
|
success: "number",
|
||||||
range: "number"
|
range: "number"
|
||||||
}
|
}
|
||||||
@ -28,13 +27,15 @@ class ContactAssimilate extends Behavior<IContactAssimilateBehaviorParameter, IC
|
|||||||
public override parameterOption = {
|
public override parameterOption = {
|
||||||
target: { type: "CLG", name: "$Target" },
|
target: { type: "CLG", name: "$Target" },
|
||||||
assimilate: { type: "CG", name: "$Assimilate" },
|
assimilate: { type: "CG", name: "$Assimilate" },
|
||||||
self: { type: "CG", name: "$Self" },
|
|
||||||
range: { type: "number", name: "$Range", defaultValue: .05, numberMin: 0, numberStep: .01 },
|
range: { type: "number", name: "$Range", defaultValue: .05, numberMin: 0, numberStep: .01 },
|
||||||
success: { type: "number", name: "$Success", defaultValue: 90, numberMin: 0, numberMax: 100, numberStep: 5 }
|
success: { type: "number", name: "$Success", defaultValue: 90, numberMin: 0, numberMax: 100, numberStep: 5 }
|
||||||
};
|
};
|
||||||
|
|
||||||
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
||||||
|
|
||||||
|
let assimilateGroup = this.parameter.assimilate.objects;
|
||||||
|
if (!assimilateGroup) return;
|
||||||
|
|
||||||
for (let i = 0; i < this.parameter.target.objects.length; i++) {
|
for (let i = 0; i < this.parameter.target.objects.length; i++) {
|
||||||
const targetGroup = this.parameter.target.objects[i];
|
const targetGroup = this.parameter.target.objects[i];
|
||||||
|
|
||||||
@ -49,18 +50,7 @@ class ContactAssimilate extends Behavior<IContactAssimilateBehaviorParameter, IC
|
|||||||
|
|
||||||
// 成功判定
|
// 成功判定
|
||||||
if (Math.random() * 100 < this.parameter.success) {
|
if (Math.random() * 100 < this.parameter.success) {
|
||||||
|
targetIndividual.transfer(assimilateGroup!);
|
||||||
// 同化目标
|
|
||||||
let assimilateGroup = this.parameter.assimilate.objects;
|
|
||||||
if (assimilateGroup) {
|
|
||||||
targetIndividual.transfer(assimilateGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 同化目标
|
|
||||||
let selfGroup = this.parameter.self.objects;
|
|
||||||
if (selfGroup) {
|
|
||||||
individual.transfer(selfGroup);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -80,10 +70,6 @@ class ContactAssimilate extends Behavior<IContactAssimilateBehaviorParameter, IC
|
|||||||
"ZH_CN": "到哪个群...",
|
"ZH_CN": "到哪个群...",
|
||||||
"EN_US": "To group..."
|
"EN_US": "To group..."
|
||||||
},
|
},
|
||||||
"$Self": {
|
|
||||||
"ZH_CN": "自身同化到...",
|
|
||||||
"EN_US": "Self assimilate to..."
|
|
||||||
},
|
|
||||||
"$Range": {
|
"$Range": {
|
||||||
"ZH_CN": "同化范围 (m)",
|
"ZH_CN": "同化范围 (m)",
|
||||||
"EN_US": "Assimilate range (m)"
|
"EN_US": "Assimilate range (m)"
|
||||||
|
@ -6,8 +6,7 @@ import { Model } from "@Model/Model";
|
|||||||
type IContactAttackingBehaviorParameter = {
|
type IContactAttackingBehaviorParameter = {
|
||||||
target: "CLG",
|
target: "CLG",
|
||||||
success: "number",
|
success: "number",
|
||||||
range: "number",
|
range: "number"
|
||||||
assimilate: "CG",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type IContactAttackingBehaviorEvent = {}
|
type IContactAttackingBehaviorEvent = {}
|
||||||
@ -27,8 +26,7 @@ class ContactAttacking extends Behavior<IContactAttackingBehaviorParameter, ICon
|
|||||||
public override parameterOption = {
|
public override parameterOption = {
|
||||||
target: { type: "CLG", name: "$Target" },
|
target: { type: "CLG", name: "$Target" },
|
||||||
range: { type: "number", name: "$Range", defaultValue: .05, numberMin: 0, numberStep: .01 },
|
range: { type: "number", name: "$Range", defaultValue: .05, numberMin: 0, numberStep: .01 },
|
||||||
success: { type: "number", name: "$Success", defaultValue: 90, numberMin: 0, numberMax: 100, numberStep: 5 },
|
success: { type: "number", name: "$Success", defaultValue: 90, numberMin: 0, numberMax: 100, numberStep: 5 }
|
||||||
assimilate: { type: "CG", name: "$Assimilate"}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
||||||
@ -48,10 +46,6 @@ class ContactAttacking extends Behavior<IContactAttackingBehaviorParameter, ICon
|
|||||||
// 成功判定
|
// 成功判定
|
||||||
if (Math.random() * 100 < this.parameter.success) {
|
if (Math.random() * 100 < this.parameter.success) {
|
||||||
targetIndividual.die();
|
targetIndividual.die();
|
||||||
|
|
||||||
if (this.parameter.assimilate?.objects) {
|
|
||||||
individual.transfer(this.parameter.assimilate.objects);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -78,10 +72,6 @@ class ContactAttacking extends Behavior<IContactAttackingBehaviorParameter, ICon
|
|||||||
"$Intro": {
|
"$Intro": {
|
||||||
"ZH_CN": "攻击进入共进范围的目标群个体",
|
"ZH_CN": "攻击进入共进范围的目标群个体",
|
||||||
"EN_US": "Attack the target group and individual entering the range"
|
"EN_US": "Attack the target group and individual entering the range"
|
||||||
},
|
|
||||||
"$Assimilate": {
|
|
||||||
"ZH_CN": "同化",
|
|
||||||
"EN_US": "Assimilate"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
import { Behavior } from "@Model/Behavior";
|
|
||||||
import { Group } from "@Model/Group";
|
|
||||||
import { Individual } from "@Model/Individual";
|
|
||||||
import { Model } from "@Model/Model";
|
|
||||||
|
|
||||||
type IManufactureBehaviorParameter = {
|
|
||||||
maxFrequency: "number",
|
|
||||||
minFrequency: "number",
|
|
||||||
genTarget: "CG",
|
|
||||||
}
|
|
||||||
|
|
||||||
type IManufactureBehaviorEvent = {}
|
|
||||||
|
|
||||||
class Manufacture extends Behavior<IManufactureBehaviorParameter, IManufactureBehaviorEvent> {
|
|
||||||
|
|
||||||
public override behaviorId: string = "Manufacture";
|
|
||||||
|
|
||||||
public override behaviorName: string = "$Title";
|
|
||||||
|
|
||||||
public override iconName: string = "ProductionFloorManagement";
|
|
||||||
|
|
||||||
public override describe: string = "$Intro";
|
|
||||||
|
|
||||||
public override category: string = "$Initiative";
|
|
||||||
|
|
||||||
public override parameterOption = {
|
|
||||||
genTarget: { type: "CG", name: "$Gen.Target" },
|
|
||||||
maxFrequency: { type: "number", name: "$Max.Frequency", defaultValue: 5, numberStep: .1, numberMin: 0 },
|
|
||||||
minFrequency: { type: "number", name: "$Min.Frequency", defaultValue: 0, numberStep: .1, numberMin: 0 }
|
|
||||||
};
|
|
||||||
|
|
||||||
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
|
||||||
|
|
||||||
const {genTarget, maxFrequency, minFrequency} = this.parameter;
|
|
||||||
|
|
||||||
if (genTarget.objects) {
|
|
||||||
|
|
||||||
let nextTime = individual.getData("Manufacture.nextTime") ??
|
|
||||||
minFrequency + Math.random() * (maxFrequency - minFrequency);
|
|
||||||
let currentTime = individual.getData("Manufacture.currentTime") ?? 0;
|
|
||||||
|
|
||||||
if (currentTime > nextTime) {
|
|
||||||
|
|
||||||
// 生成个体
|
|
||||||
let newIndividual = genTarget.objects.new(1);
|
|
||||||
newIndividual.position = individual.position.concat([]);
|
|
||||||
|
|
||||||
nextTime = minFrequency + Math.random() * (maxFrequency - minFrequency);
|
|
||||||
currentTime = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentTime += t;
|
|
||||||
|
|
||||||
individual.setData("Manufacture.nextTime", nextTime);
|
|
||||||
individual.setData("Manufacture.currentTime", currentTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override terms: Record<string, Record<string, string>> = {
|
|
||||||
"$Title": {
|
|
||||||
"ZH_CN": "生产",
|
|
||||||
"EN_US": "Manufacture"
|
|
||||||
},
|
|
||||||
"$Intro": {
|
|
||||||
"ZH_CN": "在指定的群创造新的个体",
|
|
||||||
"EN_US": "Create new individuals in a given group"
|
|
||||||
},
|
|
||||||
"$Gen.Target": {
|
|
||||||
"ZH_CN": "目标群",
|
|
||||||
"EN_US": "Target group"
|
|
||||||
},
|
|
||||||
"$Max.Frequency": {
|
|
||||||
"ZH_CN": "最大频率",
|
|
||||||
"EN_US": "Maximum frequency"
|
|
||||||
},
|
|
||||||
"$Min.Frequency": {
|
|
||||||
"ZH_CN": "最小频率",
|
|
||||||
"EN_US": "Minimum frequency"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Manufacture };
|
|
@ -1,160 +0,0 @@
|
|||||||
import { Behavior } from "@Model/Behavior";
|
|
||||||
import { Group } from "@Model/Group";
|
|
||||||
import { Individual } from "@Model/Individual";
|
|
||||||
import { Model } from "@Model/Model";
|
|
||||||
|
|
||||||
type ISampleTrackingBehaviorParameter = {
|
|
||||||
target: "CLG",
|
|
||||||
key: "string",
|
|
||||||
strength: "number",
|
|
||||||
range: "number",
|
|
||||||
angle: "number",
|
|
||||||
accuracy: "number"
|
|
||||||
}
|
|
||||||
|
|
||||||
type ISampleTrackingBehaviorEvent = {}
|
|
||||||
|
|
||||||
class SampleTracking extends Behavior<ISampleTrackingBehaviorParameter, ISampleTrackingBehaviorEvent> {
|
|
||||||
|
|
||||||
public override behaviorId: string = "SampleTracking";
|
|
||||||
|
|
||||||
public override behaviorName: string = "$Title";
|
|
||||||
|
|
||||||
public override iconName: string = "Video360Generic";
|
|
||||||
|
|
||||||
public override describe: string = "$Intro";
|
|
||||||
|
|
||||||
public override category: string = "$Initiative";
|
|
||||||
|
|
||||||
public override parameterOption = {
|
|
||||||
target: { type: "CLG", name: "$Target" },
|
|
||||||
key: { type: "string", name: "$Key"},
|
|
||||||
range: { type: "number", name: "$Range", defaultValue: 4, numberMin: 0, numberStep: .1 },
|
|
||||||
angle: { type: "number", name: "$Angle", defaultValue: 180, numberMin: 0, numberMax: 360, numberStep: 5 },
|
|
||||||
strength: { type: "number", name: "$Strength", defaultValue: 1, numberMin: 0, numberStep: .1 },
|
|
||||||
accuracy: { type: "number", name: "$Accuracy", defaultValue: 5, numberMin: 0, numberMax: 180, numberStep: 1 }
|
|
||||||
};
|
|
||||||
|
|
||||||
private angle2Vector(v1: number[], v2: number[]): number {
|
|
||||||
return Math.acos(
|
|
||||||
(v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) /
|
|
||||||
(
|
|
||||||
(v1[0] ** 2 + v1[1] ** 2 + v1[2] ** 2) ** 0.5 *
|
|
||||||
(v2[0] ** 2 + v2[1] ** 2 + v2[2] ** 2) ** 0.5
|
|
||||||
)
|
|
||||||
) * 180 / Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
|
||||||
|
|
||||||
const dirArr: number[][] = []; const valArr: number[] = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < this.parameter.target.objects.length; i++) {
|
|
||||||
const targetGroup = this.parameter.target.objects[i];
|
|
||||||
|
|
||||||
targetGroup.individuals.forEach((targetIndividual) => {
|
|
||||||
|
|
||||||
// 计算距离
|
|
||||||
let dis = targetIndividual.distanceTo(individual);
|
|
||||||
if (dis > this.parameter.range) return;
|
|
||||||
|
|
||||||
// 计算方向
|
|
||||||
let targetDir = [
|
|
||||||
targetIndividual.position[0] - individual.position[0],
|
|
||||||
targetIndividual.position[1] - individual.position[1],
|
|
||||||
targetIndividual.position[2] - individual.position[2]
|
|
||||||
];
|
|
||||||
|
|
||||||
// 计算视线角度
|
|
||||||
let angle = this.angle2Vector(individual.velocity, targetDir);
|
|
||||||
|
|
||||||
// 在可视角度内
|
|
||||||
if (angle < (this.parameter.angle ?? 360) / 2) {
|
|
||||||
|
|
||||||
// 采样
|
|
||||||
let isFindNest = false;
|
|
||||||
for (let i = 0; i < valArr.length; i++) {
|
|
||||||
|
|
||||||
// 计算采样角度
|
|
||||||
let sampleAngle = this.angle2Vector(dirArr[i], targetDir);
|
|
||||||
|
|
||||||
// 小于采样精度,合并
|
|
||||||
if (sampleAngle < this.parameter.accuracy ?? 5) {
|
|
||||||
dirArr[i][0] += targetDir[0];
|
|
||||||
dirArr[i][1] += targetDir[1];
|
|
||||||
dirArr[i][2] += targetDir[2];
|
|
||||||
valArr[i] += targetIndividual.getData(this.parameter.key) ?? 0;
|
|
||||||
isFindNest = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isFindNest) {
|
|
||||||
|
|
||||||
// 保存
|
|
||||||
dirArr.push(targetDir);
|
|
||||||
valArr.push(targetIndividual.getData(this.parameter.key) ?? 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算最大方向
|
|
||||||
let maxVal = -1; let maxDir: number[] | undefined;
|
|
||||||
for (let i = 0; i < valArr.length; i++) {
|
|
||||||
if (valArr[i] > maxVal) {
|
|
||||||
maxVal = valArr[i];
|
|
||||||
maxDir = dirArr[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxDir) {
|
|
||||||
const dir = individual.vectorNormalize(maxDir);
|
|
||||||
individual.applyForce(
|
|
||||||
dir[0] * this.parameter.strength,
|
|
||||||
dir[1] * this.parameter.strength,
|
|
||||||
dir[2] * this.parameter.strength
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override terms: Record<string, Record<string, string>> = {
|
|
||||||
"$Title": {
|
|
||||||
"ZH_CN": "采样追踪",
|
|
||||||
"EN_US": "Sample tracking"
|
|
||||||
},
|
|
||||||
"$Target": {
|
|
||||||
"ZH_CN": "追踪目标",
|
|
||||||
"EN_US": "Tracking target"
|
|
||||||
},
|
|
||||||
"$Key": {
|
|
||||||
"ZH_CN": "计算键值",
|
|
||||||
"EN_US": "Calculate key value"
|
|
||||||
},
|
|
||||||
"$Accuracy": {
|
|
||||||
"ZH_CN": "采样精度",
|
|
||||||
"EN_US": "Sampling accuracy"
|
|
||||||
},
|
|
||||||
"$Range": {
|
|
||||||
"ZH_CN": "追踪范围 (m)",
|
|
||||||
"EN_US": "Tracking range (m)"
|
|
||||||
},
|
|
||||||
"$Strength": {
|
|
||||||
"ZH_CN": "追踪强度系数",
|
|
||||||
"EN_US": "Tracking intensity coefficient"
|
|
||||||
},
|
|
||||||
"$Intro": {
|
|
||||||
"ZH_CN": "个体将主动向目标个体较多的方向发起追踪",
|
|
||||||
"EN_US": "Individuals will actively initiate tracking in the direction of more target individuals"
|
|
||||||
},
|
|
||||||
"$Interactive": {
|
|
||||||
"ZH_CN": "交互",
|
|
||||||
"EN_US": "Interactive"
|
|
||||||
},
|
|
||||||
"$Angle": {
|
|
||||||
"ZH_CN": "可视角度",
|
|
||||||
"EN_US": "Viewing angle"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export { SampleTracking };
|
|
@ -7,7 +7,6 @@ type ITrackingBehaviorParameter = {
|
|||||||
target: "CLG",
|
target: "CLG",
|
||||||
strength: "number",
|
strength: "number",
|
||||||
range: "number",
|
range: "number",
|
||||||
angle: "number",
|
|
||||||
lock: "boolean"
|
lock: "boolean"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,23 +28,12 @@ class Tracking extends Behavior<ITrackingBehaviorParameter, ITrackingBehaviorEve
|
|||||||
target: { type: "CLG", name: "$Target" },
|
target: { type: "CLG", name: "$Target" },
|
||||||
lock: { type: "boolean", name: "$Lock", defaultValue: false },
|
lock: { type: "boolean", name: "$Lock", defaultValue: false },
|
||||||
range: { type: "number", name: "$Range", defaultValue: 4, numberMin: 0, numberStep: .1 },
|
range: { type: "number", name: "$Range", defaultValue: 4, numberMin: 0, numberStep: .1 },
|
||||||
angle: { type: "number", name: "$Angle", defaultValue: 180, numberMin: 0, numberMax: 360, numberStep: 5 },
|
|
||||||
strength: { type: "number", name: "$Strength", defaultValue: 1, numberMin: 0, numberStep: .1 }
|
strength: { type: "number", name: "$Strength", defaultValue: 1, numberMin: 0, numberStep: .1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
private target: Individual | undefined = undefined;
|
private target: Individual | undefined = undefined;
|
||||||
private currentDistant: number = Infinity;
|
private currentDistant: number = Infinity;
|
||||||
|
|
||||||
private angle2Vector(v1: number[], v2: number[]): number {
|
|
||||||
return Math.acos(
|
|
||||||
(v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) /
|
|
||||||
(
|
|
||||||
(v1[0] ** 2 + v1[1] ** 2 + v1[2] ** 2) ** 0.5 *
|
|
||||||
(v2[0] ** 2 + v2[1] ** 2 + v2[2] ** 2) ** 0.5
|
|
||||||
)
|
|
||||||
) * 180 / Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
private searchTarget(individual: Individual) {
|
private searchTarget(individual: Individual) {
|
||||||
|
|
||||||
for (let i = 0; i < this.parameter.target.objects.length; i++) {
|
for (let i = 0; i < this.parameter.target.objects.length; i++) {
|
||||||
@ -58,21 +46,8 @@ class Tracking extends Behavior<ITrackingBehaviorParameter, ITrackingBehaviorEve
|
|||||||
let dis = targetIndividual.distanceTo(individual);
|
let dis = targetIndividual.distanceTo(individual);
|
||||||
|
|
||||||
if (dis < this.currentDistant && dis <= this.parameter.range) {
|
if (dis < this.currentDistant && dis <= this.parameter.range) {
|
||||||
|
|
||||||
// 计算目标方位
|
|
||||||
const targetDir = [
|
|
||||||
targetIndividual.position[0] - individual.position[0],
|
|
||||||
targetIndividual.position[1] - individual.position[1],
|
|
||||||
targetIndividual.position[2] - individual.position[2]
|
|
||||||
];
|
|
||||||
|
|
||||||
// 计算角度
|
|
||||||
const angle = this.angle2Vector(individual.velocity, targetDir);
|
|
||||||
|
|
||||||
if (angle < (this.parameter.angle ?? 360) / 2) {
|
|
||||||
this.target = targetIndividual;
|
this.target = targetIndividual;
|
||||||
this.currentDistant = dis;
|
this.currentDistant = dis;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -175,10 +150,6 @@ class Tracking extends Behavior<ITrackingBehaviorParameter, ITrackingBehaviorEve
|
|||||||
"$Interactive": {
|
"$Interactive": {
|
||||||
"ZH_CN": "交互",
|
"ZH_CN": "交互",
|
||||||
"EN_US": "Interactive"
|
"EN_US": "Interactive"
|
||||||
},
|
|
||||||
"$Angle": {
|
|
||||||
"ZH_CN": "可视角度",
|
|
||||||
"EN_US": "Viewing angle"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
import { Behavior } from "@Model/Behavior";
|
|
||||||
import { Group } from "@Model/Group";
|
|
||||||
import { Individual } from "@Model/Individual";
|
|
||||||
import { Model } from "@Model/Model";
|
|
||||||
|
|
||||||
type IWastageBehaviorParameter = {
|
|
||||||
key: "string",
|
|
||||||
value: "number",
|
|
||||||
speed: "number",
|
|
||||||
threshold: "number",
|
|
||||||
kill: "boolean",
|
|
||||||
assimilate: "CG"
|
|
||||||
}
|
|
||||||
|
|
||||||
type IWastageBehaviorEvent = {}
|
|
||||||
|
|
||||||
class Wastage extends Behavior<IWastageBehaviorParameter, IWastageBehaviorEvent> {
|
|
||||||
|
|
||||||
public override behaviorId: string = "Wastage";
|
|
||||||
|
|
||||||
public override behaviorName: string = "$Title";
|
|
||||||
|
|
||||||
public override iconName: string = "BackgroundColor";
|
|
||||||
|
|
||||||
public override describe: string = "$Intro";
|
|
||||||
|
|
||||||
public override category: string = "$Initiative";
|
|
||||||
|
|
||||||
public override parameterOption = {
|
|
||||||
key: { type: "string", name: "$Key" },
|
|
||||||
value: { type: "number", name: "$Value", defaultValue: 100, numberStep: 1 },
|
|
||||||
speed: { type: "number", name: "$Speed", defaultValue: 1, numberStep: .1 },
|
|
||||||
threshold: { type: "number", name: "$Threshold", defaultValue: 0, numberStep: 1 },
|
|
||||||
kill: { type: "boolean", name: "$Kill", defaultValue: true },
|
|
||||||
assimilate: { type: "CG", name: "$Assimilate", condition: { key: "kill", value: false } }
|
|
||||||
};
|
|
||||||
|
|
||||||
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
|
||||||
const key = this.parameter.key;
|
|
||||||
if (!key) return;
|
|
||||||
|
|
||||||
let currentValue = individual.getData(`Wastage.${key}`) ?? this.parameter.value;
|
|
||||||
currentValue -= this.parameter.speed * t;
|
|
||||||
|
|
||||||
// 超过阈值
|
|
||||||
if (currentValue < this.parameter.threshold) {
|
|
||||||
|
|
||||||
currentValue = undefined;
|
|
||||||
|
|
||||||
// 杀死个体
|
|
||||||
if (this.parameter.kill) {
|
|
||||||
individual.die();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 开启同化
|
|
||||||
else if (this.parameter.assimilate.objects) {
|
|
||||||
individual.transfer(this.parameter.assimilate.objects);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
individual.setData(`Wastage.${key}`, currentValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override terms: Record<string, Record<string, string>> = {
|
|
||||||
"$Title": {
|
|
||||||
"ZH_CN": "流逝",
|
|
||||||
"EN_US": "Wastage"
|
|
||||||
},
|
|
||||||
"$Intro": {
|
|
||||||
"ZH_CN": "随着时间流逝",
|
|
||||||
"EN_US": "As time goes by"
|
|
||||||
},
|
|
||||||
"$Key": {
|
|
||||||
"ZH_CN": "元数据",
|
|
||||||
"EN_US": "Metadata"
|
|
||||||
},
|
|
||||||
"$Value": {
|
|
||||||
"ZH_CN": "默认数值",
|
|
||||||
"EN_US": "Default value"
|
|
||||||
},
|
|
||||||
"$Speed": {
|
|
||||||
"ZH_CN": "流逝速度 (c/s)",
|
|
||||||
"EN_US": "Passing speed (c/s)"
|
|
||||||
},
|
|
||||||
"$Threshold": {
|
|
||||||
"ZH_CN": "阈值",
|
|
||||||
"EN_US": "Threshold"
|
|
||||||
},
|
|
||||||
"$Kill": {
|
|
||||||
"ZH_CN": "死亡",
|
|
||||||
"EN_US": "Death"
|
|
||||||
},
|
|
||||||
"$Assimilate": {
|
|
||||||
"ZH_CN": "同化",
|
|
||||||
"EN_US": "Assimilate"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Wastage };
|
|
Loading…
Reference in New Issue
Block a user