Add tracking assimilate attacking behaviot #39
@ -1,14 +1,22 @@
|
|||||||
import { BehaviorRecorder, IAnyBehaviorRecorder } from "@Model/Behavior";
|
import { BehaviorRecorder, IAnyBehaviorRecorder } from "@Model/Behavior";
|
||||||
import { Template } from "@Behavior/Template";
|
import { Template } from "@Behavior/Template";
|
||||||
import { Dynamics } from "@Behavior/Dynamics";
|
import { PhysicsDynamics } from "@Behavior/PhysicsDynamics";
|
||||||
import { Brownian } from "@Behavior/Brownian";
|
import { Brownian } from "@Behavior/Brownian";
|
||||||
import { BoundaryConstraint } from "@Behavior/BoundaryConstraint";
|
import { BoundaryConstraint } from "@Behavior/BoundaryConstraint";
|
||||||
|
import { Tracking } from "@Behavior/Tracking";
|
||||||
|
import { ContactAttacking } from "@Behavior/ContactAttacking";
|
||||||
|
import { ContactAssimilate } from "@Behavior/ContactAssimilate";
|
||||||
|
import { DelayAssimilate } from "@Behavior/DelayAssimilate";
|
||||||
|
|
||||||
const AllBehaviors: IAnyBehaviorRecorder[] = [
|
const AllBehaviors: IAnyBehaviorRecorder[] = [
|
||||||
new BehaviorRecorder(Template),
|
new BehaviorRecorder(Template),
|
||||||
new BehaviorRecorder(Dynamics),
|
new BehaviorRecorder(PhysicsDynamics),
|
||||||
new BehaviorRecorder(Brownian),
|
new BehaviorRecorder(Brownian),
|
||||||
new BehaviorRecorder(BoundaryConstraint),
|
new BehaviorRecorder(BoundaryConstraint),
|
||||||
|
new BehaviorRecorder(Tracking),
|
||||||
|
new BehaviorRecorder(ContactAttacking),
|
||||||
|
new BehaviorRecorder(ContactAssimilate),
|
||||||
|
new BehaviorRecorder(DelayAssimilate),
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,4 +62,13 @@ function categoryBehaviors(behaviors: IAnyBehaviorRecorder[]): ICategory[] {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { AllBehaviors, AllBehaviorsWithCategory, ICategory as ICategoryBehavior };
|
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 };
|
@ -17,7 +17,7 @@ class BoundaryConstraint extends Behavior<IBoundaryConstraintBehaviorParameter,
|
|||||||
|
|
||||||
public override behaviorName: string = "$Title";
|
public override behaviorName: string = "$Title";
|
||||||
|
|
||||||
public override iconName: string = "Running";
|
public override iconName: string = "Quantity";
|
||||||
|
|
||||||
public override describe: string = "$Intro";
|
public override describe: string = "$Intro";
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ class BoundaryConstraint extends Behavior<IBoundaryConstraintBehaviorParameter,
|
|||||||
strength: { type: "number", name: "$Strength", defaultValue: 1, numberMin: 0, numberStep: .1 }
|
strength: { type: "number", name: "$Strength", defaultValue: 1, numberMin: 0, numberStep: .1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
public effect(individual: Individual, group: Group, model: Model, t: number): void {
|
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
||||||
let rangeList: Range[] = this.parameter.range.objects;
|
let rangeList: Range[] = this.parameter.range.objects;
|
||||||
|
|
||||||
let fx = 0;
|
let fx = 0;
|
||||||
@ -57,7 +57,6 @@ class BoundaryConstraint extends Behavior<IBoundaryConstraintBehaviorParameter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
fx = 0;
|
fx = 0;
|
||||||
fy = 0;
|
fy = 0;
|
||||||
fz = 0;
|
fz = 0;
|
||||||
@ -65,11 +64,13 @@ class BoundaryConstraint extends Behavior<IBoundaryConstraintBehaviorParameter,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
individual.applyForce(
|
if (fLen && fLen !== Infinity) {
|
||||||
fx * this.parameter.strength,
|
individual.applyForce(
|
||||||
fy * this.parameter.strength,
|
fx * this.parameter.strength / fLen,
|
||||||
fz * this.parameter.strength
|
fy * this.parameter.strength / fLen,
|
||||||
);
|
fz * this.parameter.strength / fLen
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override terms: Record<string, Record<string, string>> = {
|
public override terms: Record<string, Record<string, string>> = {
|
||||||
|
@ -18,7 +18,7 @@ class Brownian extends Behavior<IBrownianBehaviorParameter, IBrownianBehaviorEve
|
|||||||
|
|
||||||
public override behaviorName: string = "$Title";
|
public override behaviorName: string = "$Title";
|
||||||
|
|
||||||
public override iconName: string = "Running";
|
public override iconName: string = "ScatterChart";
|
||||||
|
|
||||||
public override describe: string = "$Intro";
|
public override describe: string = "$Intro";
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ class Brownian extends Behavior<IBrownianBehaviorParameter, IBrownianBehaviorEve
|
|||||||
minStrength: { type: "number", name: "$Min.Strength", defaultValue: 0, numberStep: .01, numberMin: 0 }
|
minStrength: { type: "number", name: "$Min.Strength", defaultValue: 0, numberStep: .01, numberMin: 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
88
source/Behavior/ContactAssimilate.ts
Normal file
88
source/Behavior/ContactAssimilate.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { Behavior } from "@Model/Behavior";
|
||||||
|
import { Group } from "@Model/Group";
|
||||||
|
import { Individual } from "@Model/Individual";
|
||||||
|
import { Model } from "@Model/Model";
|
||||||
|
|
||||||
|
type IContactAssimilateBehaviorParameter = {
|
||||||
|
target: "CLG",
|
||||||
|
assimilate: "CG",
|
||||||
|
success: "number",
|
||||||
|
range: "number"
|
||||||
|
}
|
||||||
|
|
||||||
|
type IContactAssimilateBehaviorEvent = {}
|
||||||
|
|
||||||
|
class ContactAssimilate extends Behavior<IContactAssimilateBehaviorParameter, IContactAssimilateBehaviorEvent> {
|
||||||
|
|
||||||
|
public override behaviorId: string = "ContactAssimilate";
|
||||||
|
|
||||||
|
public override behaviorName: string = "$Title";
|
||||||
|
|
||||||
|
public override iconName: string = "SyncStatus";
|
||||||
|
|
||||||
|
public override describe: string = "$Intro";
|
||||||
|
|
||||||
|
public override category: string = "$Interactive";
|
||||||
|
|
||||||
|
public override parameterOption = {
|
||||||
|
target: { type: "CLG", name: "$Target" },
|
||||||
|
assimilate: { type: "CG", name: "$Assimilate" },
|
||||||
|
range: { type: "number", name: "$Range", defaultValue: .05, numberMin: 0, numberStep: .01 },
|
||||||
|
success: { type: "number", name: "$Success", defaultValue: 90, numberMin: 0, numberMax: 100, numberStep: 5 }
|
||||||
|
};
|
||||||
|
|
||||||
|
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++) {
|
||||||
|
const targetGroup = this.parameter.target.objects[i];
|
||||||
|
|
||||||
|
targetGroup.individuals.forEach((targetIndividual) => {
|
||||||
|
|
||||||
|
// 排除自己
|
||||||
|
if (targetIndividual === individual) return;
|
||||||
|
let dis = targetIndividual.distanceTo(individual);
|
||||||
|
|
||||||
|
// 进入同化范围
|
||||||
|
if (dis <= this.parameter.range) {
|
||||||
|
|
||||||
|
// 成功判定
|
||||||
|
if (Math.random() * 100 < this.parameter.success) {
|
||||||
|
targetIndividual.transfer(assimilateGroup!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override terms: Record<string, Record<string, string>> = {
|
||||||
|
"$Title": {
|
||||||
|
"ZH_CN": "接触同化",
|
||||||
|
"EN_US": "Contact Assimilate"
|
||||||
|
},
|
||||||
|
"$Target": {
|
||||||
|
"ZH_CN": "从哪个群...",
|
||||||
|
"EN_US": "From group..."
|
||||||
|
},
|
||||||
|
"$Assimilate": {
|
||||||
|
"ZH_CN": "到哪个群...",
|
||||||
|
"EN_US": "To group..."
|
||||||
|
},
|
||||||
|
"$Range": {
|
||||||
|
"ZH_CN": "同化范围 (m)",
|
||||||
|
"EN_US": "Assimilate range (m)"
|
||||||
|
},
|
||||||
|
"$Success": {
|
||||||
|
"ZH_CN": "成功率 (%)",
|
||||||
|
"EN_US": "Success rate (%)"
|
||||||
|
},
|
||||||
|
"$Intro": {
|
||||||
|
"ZH_CN": "将进入同化范围内的个体同化至另一个群",
|
||||||
|
"EN_US": "Assimilate individuals who enter the assimilation range to another group"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ContactAssimilate };
|
79
source/Behavior/ContactAttacking.ts
Normal file
79
source/Behavior/ContactAttacking.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { Behavior } from "@Model/Behavior";
|
||||||
|
import { Group } from "@Model/Group";
|
||||||
|
import { Individual } from "@Model/Individual";
|
||||||
|
import { Model } from "@Model/Model";
|
||||||
|
|
||||||
|
type IContactAttackingBehaviorParameter = {
|
||||||
|
target: "CLG",
|
||||||
|
success: "number",
|
||||||
|
range: "number"
|
||||||
|
}
|
||||||
|
|
||||||
|
type IContactAttackingBehaviorEvent = {}
|
||||||
|
|
||||||
|
class ContactAttacking extends Behavior<IContactAttackingBehaviorParameter, IContactAttackingBehaviorEvent> {
|
||||||
|
|
||||||
|
public override behaviorId: string = "ContactAttacking";
|
||||||
|
|
||||||
|
public override behaviorName: string = "$Title";
|
||||||
|
|
||||||
|
public override iconName: string = "DefenderTVM";
|
||||||
|
|
||||||
|
public override describe: string = "$Intro";
|
||||||
|
|
||||||
|
public override category: string = "$Interactive";
|
||||||
|
|
||||||
|
public override parameterOption = {
|
||||||
|
target: { type: "CLG", name: "$Target" },
|
||||||
|
range: { type: "number", name: "$Range", defaultValue: .05, numberMin: 0, numberStep: .01 },
|
||||||
|
success: { type: "number", name: "$Success", defaultValue: 90, numberMin: 0, numberMax: 100, numberStep: 5 }
|
||||||
|
};
|
||||||
|
|
||||||
|
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
||||||
|
|
||||||
|
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 <= this.parameter.range) {
|
||||||
|
|
||||||
|
// 成功判定
|
||||||
|
if (Math.random() * 100 < this.parameter.success) {
|
||||||
|
targetIndividual.die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override terms: Record<string, Record<string, string>> = {
|
||||||
|
"$Title": {
|
||||||
|
"ZH_CN": "接触攻击",
|
||||||
|
"EN_US": "Contact Attacking"
|
||||||
|
},
|
||||||
|
"$Target": {
|
||||||
|
"ZH_CN": "攻击目标",
|
||||||
|
"EN_US": "Attacking target"
|
||||||
|
},
|
||||||
|
"$Range": {
|
||||||
|
"ZH_CN": "攻击范围 (m)",
|
||||||
|
"EN_US": "Attacking range (m)"
|
||||||
|
},
|
||||||
|
"$Success": {
|
||||||
|
"ZH_CN": "成功率 (%)",
|
||||||
|
"EN_US": "Success rate (%)"
|
||||||
|
},
|
||||||
|
"$Intro": {
|
||||||
|
"ZH_CN": "攻击进入共进范围的目标群个体",
|
||||||
|
"EN_US": "Attack the target group and individual entering the range"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ContactAttacking };
|
96
source/Behavior/DelayAssimilate.ts
Normal file
96
source/Behavior/DelayAssimilate.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 IDelayAssimilateBehaviorParameter = {
|
||||||
|
target: "CG",
|
||||||
|
maxDelay: "number",
|
||||||
|
minDelay: "number",
|
||||||
|
success: "number"
|
||||||
|
}
|
||||||
|
|
||||||
|
type IDelayAssimilateBehaviorEvent = {}
|
||||||
|
|
||||||
|
class DelayAssimilate extends Behavior<IDelayAssimilateBehaviorParameter, IDelayAssimilateBehaviorEvent> {
|
||||||
|
|
||||||
|
public override behaviorId: string = "DelayAssimilate";
|
||||||
|
|
||||||
|
public override behaviorName: string = "$Title";
|
||||||
|
|
||||||
|
public override iconName: string = "FunctionalManagerDashboard";
|
||||||
|
|
||||||
|
public override describe: string = "$Intro";
|
||||||
|
|
||||||
|
public override category: string = "$Initiative";
|
||||||
|
|
||||||
|
public override parameterOption = {
|
||||||
|
target: { type: "CG", name: "$Target" },
|
||||||
|
maxDelay: { type: "number", name: "$Max.Delay", defaultValue: 20, numberStep: 1, numberMin: 0 },
|
||||||
|
minDelay: { type: "number", name: "$Min.Delay", defaultValue: 5, numberStep: 1, numberMin: 0 },
|
||||||
|
success: { type: "number", name: "$Success", defaultValue: 90, numberStep: 5, numberMin: 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
||||||
|
|
||||||
|
let assimilateGroup = this.parameter.target.objects;
|
||||||
|
if (!assimilateGroup) return;
|
||||||
|
|
||||||
|
const {maxDelay, minDelay, success} = this.parameter;
|
||||||
|
|
||||||
|
let nextTime = individual.getData("DelayAssimilate.nextTime") ??
|
||||||
|
minDelay + Math.random() * (maxDelay - minDelay);
|
||||||
|
let currentTime = individual.getData("DelayAssimilate.currentTime") ?? 0;
|
||||||
|
|
||||||
|
currentTime += t;
|
||||||
|
if (currentTime > nextTime) {
|
||||||
|
|
||||||
|
// 成功判定
|
||||||
|
if (Math.random() * 100 < success) {
|
||||||
|
individual.transfer(assimilateGroup);
|
||||||
|
nextTime = undefined;
|
||||||
|
currentTime = undefined;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
nextTime = minDelay + Math.random() * (maxDelay - minDelay);
|
||||||
|
currentTime = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
individual.setData("DelayAssimilate.nextTime", nextTime);
|
||||||
|
individual.setData("DelayAssimilate.currentTime", currentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override terms: Record<string, Record<string, string>> = {
|
||||||
|
"$Title": {
|
||||||
|
"ZH_CN": "延迟同化",
|
||||||
|
"EN_US": "Delayed assimilation"
|
||||||
|
},
|
||||||
|
"$Intro": {
|
||||||
|
"ZH_CN": "随着时间的推移,个体逐渐向另一个群同化。",
|
||||||
|
"EN_US": "Over time, individuals gradually assimilate to another group."
|
||||||
|
},
|
||||||
|
"$Target": {
|
||||||
|
"ZH_CN": "同化目标",
|
||||||
|
"EN_US": "Assimilation target"
|
||||||
|
},
|
||||||
|
"$Max.Delay": {
|
||||||
|
"ZH_CN": "最长时间",
|
||||||
|
"EN_US": "Longest time"
|
||||||
|
},
|
||||||
|
"$Min.Delay": {
|
||||||
|
"ZH_CN": "最短时间",
|
||||||
|
"EN_US": "Shortest time"
|
||||||
|
},
|
||||||
|
"$Success": {
|
||||||
|
"ZH_CN": "成功率",
|
||||||
|
"EN_US": "Minimum strength"
|
||||||
|
},
|
||||||
|
"$Initiative": {
|
||||||
|
"ZH_CN": "主动",
|
||||||
|
"EN_US": "Initiative"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { DelayAssimilate };
|
@ -3,22 +3,23 @@ import Group from "@Model/Group";
|
|||||||
import Individual from "@Model/Individual";
|
import Individual from "@Model/Individual";
|
||||||
import { Model } from "@Model/Model";
|
import { Model } from "@Model/Model";
|
||||||
|
|
||||||
type IDynamicsBehaviorParameter = {
|
type IPhysicsDynamicsBehaviorParameter = {
|
||||||
mass: "number",
|
mass: "number",
|
||||||
maxAcceleration: "number",
|
maxAcceleration: "number",
|
||||||
maxVelocity: "number",
|
maxVelocity: "number",
|
||||||
resistance: "number"
|
resistance: "number",
|
||||||
|
limit: "boolean"
|
||||||
}
|
}
|
||||||
|
|
||||||
type IDynamicsBehaviorEvent = {}
|
type IPhysicsDynamicsBehaviorEvent = {}
|
||||||
|
|
||||||
class Dynamics extends Behavior<IDynamicsBehaviorParameter, IDynamicsBehaviorEvent> {
|
class PhysicsDynamics extends Behavior<IPhysicsDynamicsBehaviorParameter, IPhysicsDynamicsBehaviorEvent> {
|
||||||
|
|
||||||
public override behaviorId: string = "Dynamics";
|
public override behaviorId: string = "PhysicsDynamics";
|
||||||
|
|
||||||
public override behaviorName: string = "$Title";
|
public override behaviorName: string = "$Title";
|
||||||
|
|
||||||
public override iconName: string = "Running";
|
public override iconName: string = "SliderHandleSize";
|
||||||
|
|
||||||
public override describe: string = "$Intro";
|
public override describe: string = "$Intro";
|
||||||
|
|
||||||
@ -26,12 +27,19 @@ class Dynamics extends Behavior<IDynamicsBehaviorParameter, IDynamicsBehaviorEve
|
|||||||
|
|
||||||
public override parameterOption = {
|
public override parameterOption = {
|
||||||
mass: { name: "$Mass", type: "number", defaultValue: 1, numberStep: .01, numberMin: .001 },
|
mass: { name: "$Mass", type: "number", defaultValue: 1, numberStep: .01, numberMin: .001 },
|
||||||
maxAcceleration: { name: "$Max.Acceleration", type: "number", defaultValue: 5, numberStep: .1, numberMin: 0 },
|
resistance: { name: "$Resistance", type: "number", defaultValue: 2.8, numberStep: .1, numberMin: 0 },
|
||||||
maxVelocity: { name: "$Max.Velocity", type: "number", defaultValue: 10, numberStep: .1, numberMin: 0 },
|
limit: { name: "$Limit", type: "boolean", defaultValue: true },
|
||||||
resistance: { name: "$Resistance", type: "number", defaultValue: 0.5, numberStep: .1, numberMin: 0 }
|
maxAcceleration: {
|
||||||
|
name: "$Max.Acceleration", type: "number", defaultValue: 6.25,
|
||||||
|
numberStep: .1, numberMin: 0, condition: { key: "limit", value: true }
|
||||||
|
},
|
||||||
|
maxVelocity: {
|
||||||
|
name: "$Max.Velocity", type: "number", defaultValue: 12.5,
|
||||||
|
numberStep: .1, numberMin: 0, condition: { key: "limit", value: true }
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
public override finalEffect(individual: Individual, group: Group, model: Model, t: number): void {
|
public override finalEffect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
||||||
|
|
||||||
// 计算当前速度
|
// 计算当前速度
|
||||||
const currentV = individual.vectorLength(individual.velocity);
|
const currentV = individual.vectorLength(individual.velocity);
|
||||||
@ -54,24 +62,28 @@ class Dynamics extends Behavior<IDynamicsBehaviorParameter, IDynamicsBehaviorEve
|
|||||||
individual.acceleration[2] = individual.force[2] / this.parameter.mass;
|
individual.acceleration[2] = individual.force[2] / this.parameter.mass;
|
||||||
|
|
||||||
// 加速度约束
|
// 加速度约束
|
||||||
const lengthA = individual.vectorLength(individual.acceleration);
|
if (this.parameter.limit) {
|
||||||
if (lengthA > this.parameter.maxAcceleration) {
|
const lengthA = individual.vectorLength(individual.acceleration);
|
||||||
individual.acceleration[0] = individual.acceleration[0] * this.parameter.maxAcceleration / lengthA;
|
if (lengthA > this.parameter.maxAcceleration) {
|
||||||
individual.acceleration[1] = individual.acceleration[1] * this.parameter.maxAcceleration / lengthA;
|
individual.acceleration[0] = individual.acceleration[0] * this.parameter.maxAcceleration / lengthA;
|
||||||
individual.acceleration[2] = individual.acceleration[2] * 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.velocity[0] = individual.velocity[0] + individual.acceleration[0] * t;
|
individual.velocity[0] = individual.velocity[0] + individual.acceleration[0] * t;
|
||||||
individual.velocity[1] = individual.velocity[1] + individual.acceleration[1] * t;
|
individual.velocity[1] = individual.velocity[1] + individual.acceleration[1] * t;
|
||||||
individual.velocity[2] = individual.velocity[2] + individual.acceleration[2] * t;
|
individual.velocity[2] = individual.velocity[2] + individual.acceleration[2] * t;
|
||||||
|
|
||||||
// 速度约束
|
// 速度约束
|
||||||
const lengthV = individual.vectorLength(individual.velocity);
|
if (this.parameter.limit) {
|
||||||
if (lengthV > this.parameter.maxVelocity) {
|
const lengthV = individual.vectorLength(individual.velocity);
|
||||||
individual.velocity[0] = individual.velocity[0] * this.parameter.maxVelocity / lengthV;
|
if (lengthV > this.parameter.maxVelocity) {
|
||||||
individual.velocity[1] = individual.velocity[1] * this.parameter.maxVelocity / lengthV;
|
individual.velocity[0] = individual.velocity[0] * this.parameter.maxVelocity / lengthV;
|
||||||
individual.velocity[2] = individual.velocity[2] * this.parameter.maxVelocity / lengthV;
|
individual.velocity[1] = individual.velocity[1] * this.parameter.maxVelocity / lengthV;
|
||||||
|
individual.velocity[2] = individual.velocity[2] * this.parameter.maxVelocity / lengthV;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 应用速度
|
// 应用速度
|
||||||
@ -87,13 +99,17 @@ class Dynamics extends Behavior<IDynamicsBehaviorParameter, IDynamicsBehaviorEve
|
|||||||
|
|
||||||
public override terms: Record<string, Record<string, string>> = {
|
public override terms: Record<string, Record<string, string>> = {
|
||||||
"$Title": {
|
"$Title": {
|
||||||
"ZH_CN": "动力学",
|
"ZH_CN": "物理动力学",
|
||||||
"EN_US": "Dynamics"
|
"EN_US": "Physics dynamics"
|
||||||
},
|
},
|
||||||
"$Intro": {
|
"$Intro": {
|
||||||
"ZH_CN": "一切可以运动物体的必要行为,执行物理法则。",
|
"ZH_CN": "一切按照物理规则运动物体的行为, 按照牛顿经典物理运动公式执行。",
|
||||||
"EN_US": "All necessary behaviors that can move objects and implement the laws of physics."
|
"EN_US": "The behavior of all moving objects according to physical rules is carried out according to Newton's classical physical motion formula."
|
||||||
},
|
},
|
||||||
|
"$Limit": {
|
||||||
|
"ZH_CN": "开启限制",
|
||||||
|
"EN_US": "Enable limit"
|
||||||
|
},
|
||||||
"$Mass": {
|
"$Mass": {
|
||||||
"ZH_CN": "质量 (Kg)",
|
"ZH_CN": "质量 (Kg)",
|
||||||
"EN_US": "Mass (Kg)"
|
"EN_US": "Mass (Kg)"
|
||||||
@ -117,4 +133,4 @@ class Dynamics extends Behavior<IDynamicsBehaviorParameter, IDynamicsBehaviorEve
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Dynamics };
|
export { PhysicsDynamics };
|
@ -14,6 +14,8 @@ type ITemplateBehaviorParameter = {
|
|||||||
testLR: "LR";
|
testLR: "LR";
|
||||||
testLG: "LG";
|
testLG: "LG";
|
||||||
testVec: "vec";
|
testVec: "vec";
|
||||||
|
testCG: "CG",
|
||||||
|
testCLG: "CLG",
|
||||||
}
|
}
|
||||||
|
|
||||||
type ITemplateBehaviorEvent = {}
|
type ITemplateBehaviorEvent = {}
|
||||||
@ -42,10 +44,12 @@ class Template extends Behavior<ITemplateBehaviorParameter, ITemplateBehaviorEve
|
|||||||
testG: { name: "$Test", type: "G" },
|
testG: { name: "$Test", type: "G" },
|
||||||
testLR: { name: "$Test", type: "LR" },
|
testLR: { name: "$Test", type: "LR" },
|
||||||
testLG: { name: "$Test", type: "LG" },
|
testLG: { name: "$Test", type: "LG" },
|
||||||
|
testCG: { name: "$Test", type: "CG" },
|
||||||
|
testCLG: { name: "$Test", type: "CLG" },
|
||||||
testVec: { name: "$Test", type: "vec", defaultValue: [1, 2, 3], numberMax: 10, numberMin: 0, numberStep: 1 }
|
testVec: { name: "$Test", type: "vec", defaultValue: [1, 2, 3], numberMax: 10, numberMin: 0, numberStep: 1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
public effect(individual: Individual, group: Group, model: Model, t: number): void {
|
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
157
source/Behavior/Tracking.ts
Normal file
157
source/Behavior/Tracking.ts
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import { Behavior } from "@Model/Behavior";
|
||||||
|
import { Group } from "@Model/Group";
|
||||||
|
import { Individual } from "@Model/Individual";
|
||||||
|
import { Model } from "@Model/Model";
|
||||||
|
|
||||||
|
type ITrackingBehaviorParameter = {
|
||||||
|
target: "CLG",
|
||||||
|
strength: "number",
|
||||||
|
range: "number",
|
||||||
|
lock: "boolean"
|
||||||
|
}
|
||||||
|
|
||||||
|
type ITrackingBehaviorEvent = {}
|
||||||
|
|
||||||
|
class Tracking extends Behavior<ITrackingBehaviorParameter, ITrackingBehaviorEvent> {
|
||||||
|
|
||||||
|
public override behaviorId: string = "Tracking";
|
||||||
|
|
||||||
|
public override behaviorName: string = "$Title";
|
||||||
|
|
||||||
|
public override iconName: string = "Bullseye";
|
||||||
|
|
||||||
|
public override describe: string = "$Intro";
|
||||||
|
|
||||||
|
public override category: string = "$Interactive";
|
||||||
|
|
||||||
|
public override parameterOption = {
|
||||||
|
target: { type: "CLG", name: "$Target" },
|
||||||
|
lock: { type: "boolean", name: "$Lock", defaultValue: false },
|
||||||
|
range: { type: "number", name: "$Range", defaultValue: 4, numberMin: 0, numberStep: .1 },
|
||||||
|
strength: { type: "number", name: "$Strength", defaultValue: 1, numberMin: 0, numberStep: .1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
private target: Individual | undefined = undefined;
|
||||||
|
private currentDistant: number = Infinity;
|
||||||
|
|
||||||
|
private searchTarget(individual: Individual) {
|
||||||
|
|
||||||
|
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 < this.currentDistant && dis <= this.parameter.range) {
|
||||||
|
this.target = targetIndividual;
|
||||||
|
this.currentDistant = dis;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearTarget() {
|
||||||
|
this.target = undefined as Individual | undefined;
|
||||||
|
this.currentDistant = Infinity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
||||||
|
|
||||||
|
this.clearTarget();
|
||||||
|
|
||||||
|
if (this.parameter.lock) {
|
||||||
|
|
||||||
|
let isValidTarget = false;
|
||||||
|
this.target = individual.getData("Tracking.lock.target");
|
||||||
|
|
||||||
|
if (this.target) {
|
||||||
|
|
||||||
|
// 校验目标所在的群是否仍是目标
|
||||||
|
let isInTarget = false;
|
||||||
|
for (let i = 0; i < this.parameter.target.objects.length; i++) {
|
||||||
|
if (this.parameter.target.objects[i].equal(this.target.group)) {
|
||||||
|
isInTarget = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果还在目标范围内,校验距离
|
||||||
|
if (isInTarget) {
|
||||||
|
let dis = individual.distanceTo(this.target);
|
||||||
|
|
||||||
|
// 校验成功
|
||||||
|
if (dis <= this.parameter.range) {
|
||||||
|
this.currentDistant = dis;
|
||||||
|
isValidTarget = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果目标无效,尝试搜索新的目标
|
||||||
|
if (!isValidTarget) {
|
||||||
|
|
||||||
|
this.clearTarget();
|
||||||
|
this.searchTarget(individual);
|
||||||
|
|
||||||
|
// 如果成功搜索,缓存目标
|
||||||
|
if (this.target && this.currentDistant && this.currentDistant !== Infinity) {
|
||||||
|
individual.setData("Tracking.lock.target", this.target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索失败,清除目标
|
||||||
|
else {
|
||||||
|
individual.setData("Tracking.lock.target", undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
this.searchTarget(individual);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.target && this.currentDistant && this.currentDistant !== Infinity) {
|
||||||
|
individual.applyForce(
|
||||||
|
(this.target.position[0] - individual.position[0]) * this.parameter.strength / this.currentDistant,
|
||||||
|
(this.target.position[1] - individual.position[1]) * this.parameter.strength / this.currentDistant,
|
||||||
|
(this.target.position[2] - individual.position[2]) * this.parameter.strength / this.currentDistant
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override terms: Record<string, Record<string, string>> = {
|
||||||
|
"$Title": {
|
||||||
|
"ZH_CN": "追踪",
|
||||||
|
"EN_US": "Tracking"
|
||||||
|
},
|
||||||
|
"$Target": {
|
||||||
|
"ZH_CN": "追踪目标",
|
||||||
|
"EN_US": "Tracking target"
|
||||||
|
},
|
||||||
|
"$Lock": {
|
||||||
|
"ZH_CN": "追踪锁定",
|
||||||
|
"EN_US": "Tracking lock"
|
||||||
|
},
|
||||||
|
"$Range": {
|
||||||
|
"ZH_CN": "追踪范围 (m)",
|
||||||
|
"EN_US": "Tracking range (m)"
|
||||||
|
},
|
||||||
|
"$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 };
|
@ -94,8 +94,8 @@ div.behavior-list {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.behavior-content-view {
|
div.behavior-content-view {
|
||||||
width: calc( 100% - 50px );
|
width: calc( 100% - 55px );
|
||||||
padding-right: 5px;
|
padding-right: 10px;
|
||||||
max-width: 125px;
|
max-width: 125px;
|
||||||
height: $behavior-item-height;
|
height: $behavior-item-height;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -119,6 +119,10 @@ div.app-container {
|
|||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.app-panel.hide-scrollbar {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
div.app-panel.hide-scrollbar::-webkit-scrollbar {
|
div.app-panel.hide-scrollbar::-webkit-scrollbar {
|
||||||
width : 0; /*高宽分别对应横竖滚动条的尺寸*/
|
width : 0; /*高宽分别对应横竖滚动条的尺寸*/
|
||||||
height: 0;
|
height: 0;
|
||||||
|
@ -59,6 +59,10 @@ class ObjectPicker extends Component<IObjectPickerProps & IMixinStatusProps, IOb
|
|||||||
option.push(this.props.status.model.objectPool[j]);
|
option.push(this.props.status.model.objectPool[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.props.type.includes("C")) {
|
||||||
|
option.push(this.props.status.model.currentGroupLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return option;
|
return option;
|
||||||
|
@ -32,9 +32,15 @@ class Parameter<P extends IParameter> extends Component<IParameterProps<P> & IMi
|
|||||||
private renderParameter<K extends keyof P>
|
private renderParameter<K extends keyof P>
|
||||||
(key: K, option: IParameterOptionItem<P[K]>, value: IParamValue<P[K]>): ReactNode {
|
(key: K, option: IParameterOptionItem<P[K]>, value: IParamValue<P[K]>): ReactNode {
|
||||||
|
|
||||||
const language = this.props.setting?.language ?? "EN_US";
|
|
||||||
const indexKey = `${this.props.key}-${key}`;
|
const indexKey = `${this.props.key}-${key}`;
|
||||||
|
|
||||||
|
// 条件检测
|
||||||
|
if (option.condition && this.props.value[option.condition.key] !== option.condition.value) {
|
||||||
|
return <Fragment key={indexKey}/>;
|
||||||
|
}
|
||||||
|
|
||||||
const type = option.type;
|
const type = option.type;
|
||||||
|
const language = this.props.setting?.language ?? "EN_US";
|
||||||
let keyI18n: string, keyI18nOption: Record<string, string> | undefined;
|
let keyI18n: string, keyI18nOption: Record<string, string> | undefined;
|
||||||
|
|
||||||
// Custom I18N
|
// Custom I18N
|
||||||
|
@ -51,16 +51,25 @@ function getObjectDisplayInfo(item?: IPickerListItem): IDisplayInfo {
|
|||||||
if (item instanceof Label) {
|
if (item instanceof Label) {
|
||||||
|
|
||||||
if (item.isBuildIn) {
|
if (item.isBuildIn) {
|
||||||
|
|
||||||
internal = true;
|
internal = true;
|
||||||
allLabel = true;
|
allLabel = true;
|
||||||
color = "transparent";
|
color = "transparent";
|
||||||
|
|
||||||
if (item.id === "AllRange") {
|
if (item.id === "AllRange") {
|
||||||
icon = "ProductList";
|
icon = "ProductList";
|
||||||
name = "Build.In.Label.Name.All.Range";
|
name = "Build.In.Label.Name.All.Range";
|
||||||
} else if (item.id === "AllGroup") {
|
}
|
||||||
|
|
||||||
|
else if (item.id === "AllGroup") {
|
||||||
icon = "SizeLegacy";
|
icon = "SizeLegacy";
|
||||||
name = "Build.In.Label.Name.All.Group";
|
name = "Build.In.Label.Name.All.Group";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (item.id === "CurrentGroupLabel") {
|
||||||
|
icon = "TriangleShape";
|
||||||
|
name = "Build.In.Label.Name.Current.Group";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
@ -57,8 +57,11 @@ const EN_US = {
|
|||||||
"Popup.Action.No": "Cancel",
|
"Popup.Action.No": "Cancel",
|
||||||
"Popup.Action.Objects.Confirm.Title": "Confirm Delete",
|
"Popup.Action.Objects.Confirm.Title": "Confirm Delete",
|
||||||
"Popup.Action.Objects.Confirm.Delete": "Delete",
|
"Popup.Action.Objects.Confirm.Delete": "Delete",
|
||||||
|
"Popup.Action.Objects.Confirm.Restore.Title": "Confirm Restore",
|
||||||
|
"Popup.Action.Objects.Confirm.Restore": "Restore",
|
||||||
"Popup.Delete.Objects.Confirm": "Are you sure you want to delete this object(s)? The object is deleted and cannot be recalled.",
|
"Popup.Delete.Objects.Confirm": "Are you sure you want to delete this object(s)? The object is deleted and cannot be recalled.",
|
||||||
"Popup.Delete.Behavior.Confirm": "Are you sure you want to delete this behavior? The behavior is deleted and cannot be recalled.",
|
"Popup.Delete.Behavior.Confirm": "Are you sure you want to delete this behavior? The behavior is deleted and cannot be recalled.",
|
||||||
|
"Popup.Restore.Behavior.Confirm": "Are you sure you want to reset all parameters of this behavior? This operation cannot be recalled.",
|
||||||
"Popup.Setting.Title": "Preferences setting",
|
"Popup.Setting.Title": "Preferences setting",
|
||||||
"Popup.Add.Behavior.Title": "Add behavior",
|
"Popup.Add.Behavior.Title": "Add behavior",
|
||||||
"Popup.Add.Behavior.Action.Add": "Add all select behavior",
|
"Popup.Add.Behavior.Action.Add": "Add all select behavior",
|
||||||
@ -68,6 +71,7 @@ const EN_US = {
|
|||||||
"Popup.Behavior.Info.Confirm": "OK, I know it",
|
"Popup.Behavior.Info.Confirm": "OK, I know it",
|
||||||
"Build.In.Label.Name.All.Group": "All group",
|
"Build.In.Label.Name.All.Group": "All group",
|
||||||
"Build.In.Label.Name.All.Range": "All range",
|
"Build.In.Label.Name.All.Range": "All range",
|
||||||
|
"Build.In.Label.Name.Current.Group": "Current group",
|
||||||
"Common.Search.Placeholder": "Search in here...",
|
"Common.Search.Placeholder": "Search in here...",
|
||||||
"Common.No.Data": "No Data",
|
"Common.No.Data": "No Data",
|
||||||
"Common.No.Unknown.Error": "Unknown error",
|
"Common.No.Unknown.Error": "Unknown error",
|
||||||
@ -107,6 +111,7 @@ const EN_US = {
|
|||||||
"Common.Attr.Key.Generation.Error.Invalid.Label": "The specified label has expired",
|
"Common.Attr.Key.Generation.Error.Invalid.Label": "The specified label has expired",
|
||||||
"Common.Attr.Key.Kill.Random": "Random kill",
|
"Common.Attr.Key.Kill.Random": "Random kill",
|
||||||
"Common.Attr.Key.Kill.Count": "Kill count",
|
"Common.Attr.Key.Kill.Count": "Kill count",
|
||||||
|
"Common.Attr.Key.Behavior.Restore": "Restore default parameters",
|
||||||
"Common.Render.Attr.Key.Display.Shape": "Display Shape",
|
"Common.Render.Attr.Key.Display.Shape": "Display Shape",
|
||||||
"Common.Render.Attr.Key.Display.Shape.Square": "Square",
|
"Common.Render.Attr.Key.Display.Shape.Square": "Square",
|
||||||
"Common.Render.Attr.Key.Display.Shape.Hollow.Square": "Hollow square",
|
"Common.Render.Attr.Key.Display.Shape.Hollow.Square": "Hollow square",
|
||||||
|
@ -57,17 +57,21 @@ const ZH_CN = {
|
|||||||
"Popup.Action.No": "取消",
|
"Popup.Action.No": "取消",
|
||||||
"Popup.Action.Objects.Confirm.Title": "删除确认",
|
"Popup.Action.Objects.Confirm.Title": "删除确认",
|
||||||
"Popup.Action.Objects.Confirm.Delete": "删除",
|
"Popup.Action.Objects.Confirm.Delete": "删除",
|
||||||
|
"Popup.Action.Objects.Confirm.Restore.Title": "重置确认",
|
||||||
|
"Popup.Action.Objects.Confirm.Restore": "重置",
|
||||||
"Popup.Delete.Objects.Confirm": "你确定要删除这个(些)对象吗?对象被删除将无法撤回。",
|
"Popup.Delete.Objects.Confirm": "你确定要删除这个(些)对象吗?对象被删除将无法撤回。",
|
||||||
"Popup.Delete.Behavior.Confirm": "你确定要删除这个行为吗?行为被删除将无法撤回。",
|
"Popup.Delete.Behavior.Confirm": "你确定要删除这个行为吗?行为被删除将无法撤回。",
|
||||||
|
"Popup.Restore.Behavior.Confirm": "你确定要重置此行为的全部参数吗?此操作无法撤回。",
|
||||||
"Popup.Setting.Title": "首选项设置",
|
"Popup.Setting.Title": "首选项设置",
|
||||||
"Popup.Add.Behavior.Title": "添加行为",
|
"Popup.Add.Behavior.Title": "添加行为",
|
||||||
"Popup.Add.Behavior.Action.Add": "添加全部选中行为",
|
"Popup.Add.Behavior.Action.Add": "添加全部选中行为",
|
||||||
"Popup.Add.Behavior.Select.Counter": "找不到名为 \"{name}\" 的行为",
|
"Popup.Add.Behavior.Select.Counter": "已选择 {count} 个行为",
|
||||||
"Popup.Add.Behavior.Select.Nodata": "Could not find behavior named \"{name}\"",
|
"Popup.Add.Behavior.Select.Nodata": "找不到名为 \"{name}\" 的行为",
|
||||||
"Popup.Behavior.Info.Title": "行为详情: {behavior}",
|
"Popup.Behavior.Info.Title": "行为详情: {behavior}",
|
||||||
"Popup.Behavior.Info.Confirm": "好的, 我知道了",
|
"Popup.Behavior.Info.Confirm": "好的, 我知道了",
|
||||||
"Build.In.Label.Name.All.Group": "全部群",
|
"Build.In.Label.Name.All.Group": "全部群",
|
||||||
"Build.In.Label.Name.All.Range": "全部范围",
|
"Build.In.Label.Name.All.Range": "全部范围",
|
||||||
|
"Build.In.Label.Name.Current.Group": "当前群",
|
||||||
"Common.Search.Placeholder": "在此处搜索...",
|
"Common.Search.Placeholder": "在此处搜索...",
|
||||||
"Common.No.Data": "暂无数据",
|
"Common.No.Data": "暂无数据",
|
||||||
"Common.No.Unknown.Error": "未知错误",
|
"Common.No.Unknown.Error": "未知错误",
|
||||||
@ -107,6 +111,7 @@ const ZH_CN = {
|
|||||||
"Common.Attr.Key.Generation.Error.Invalid.Label": "指定的标签已失效",
|
"Common.Attr.Key.Generation.Error.Invalid.Label": "指定的标签已失效",
|
||||||
"Common.Attr.Key.Kill.Random": "随机消除",
|
"Common.Attr.Key.Kill.Random": "随机消除",
|
||||||
"Common.Attr.Key.Kill.Count": "消除数量",
|
"Common.Attr.Key.Kill.Count": "消除数量",
|
||||||
|
"Common.Attr.Key.Behavior.Restore": "还原默认参数",
|
||||||
"Common.Render.Attr.Key.Display.Shape": "显示形状",
|
"Common.Render.Attr.Key.Display.Shape": "显示形状",
|
||||||
"Common.Render.Attr.Key.Display.Shape.Square": "方形",
|
"Common.Render.Attr.Key.Display.Shape.Square": "方形",
|
||||||
"Common.Render.Attr.Key.Display.Shape.Hollow.Square": "空心方形",
|
"Common.Render.Attr.Key.Display.Shape.Hollow.Square": "空心方形",
|
||||||
|
@ -158,6 +158,11 @@ class Behavior<
|
|||||||
*/
|
*/
|
||||||
public parameter: IParameterValue<P>;
|
public parameter: IParameterValue<P>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定当前群的 Key
|
||||||
|
*/
|
||||||
|
public currentGroupKey: Array<keyof P> = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对象参数列表
|
* 对象参数列表
|
||||||
*/
|
*/
|
||||||
@ -222,7 +227,7 @@ class Behavior<
|
|||||||
* @param model 模型
|
* @param model 模型
|
||||||
* @param t 经过时间
|
* @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 model 模型
|
||||||
* @param t 经过时间
|
* @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 model 模型
|
||||||
* @param t 经过时间
|
* @param t 经过时间
|
||||||
*/
|
*/
|
||||||
public finalEffect(individual: Individual, group: Group, model: Model, t: number): void {};
|
public finalEffect?: (individual: Individual, group: Group, model: Model, t: number) => void;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,8 +61,8 @@ class CtrlObject extends LabelObject {
|
|||||||
/**
|
/**
|
||||||
* 判断是否为相同对象
|
* 判断是否为相同对象
|
||||||
*/
|
*/
|
||||||
public equal(obj: CtrlObject): boolean {
|
public equal(obj?: CtrlObject): boolean {
|
||||||
return this === obj || this.id === obj.id;
|
return this === obj || this.id === obj?.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Individual } from "@Model/Individual";
|
import { Individual } from "@Model/Individual";
|
||||||
import { CtrlObject } from "@Model/CtrlObject";
|
import { CtrlObject } from "@Model/CtrlObject";
|
||||||
import type { Behavior } from "@Model/Behavior";
|
import type { Behavior, IAnyBehavior } from "@Model/Behavior";
|
||||||
import { Label } from "@Model/Label";
|
import { Label } from "@Model/Label";
|
||||||
import { Range } from "@Model/Range";
|
import { Range } from "@Model/Range";
|
||||||
import { Model, ObjectID } from "@Model/Model";
|
import { Model, ObjectID } from "@Model/Model";
|
||||||
@ -39,7 +39,7 @@ class Group extends CtrlObject {
|
|||||||
/**
|
/**
|
||||||
* 生成个数
|
* 生成个数
|
||||||
*/
|
*/
|
||||||
public genCount: number = 1;
|
public genCount: number = 100;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成错误信息
|
* 生成错误信息
|
||||||
@ -54,7 +54,7 @@ class Group extends CtrlObject {
|
|||||||
/**
|
/**
|
||||||
* 删除个数
|
* 删除个数
|
||||||
*/
|
*/
|
||||||
public killCount: number = 1;
|
public killCount: number = 100;
|
||||||
|
|
||||||
private genInSingleRange(count: number, range: Range) {
|
private genInSingleRange(count: number, range: Range) {
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
@ -272,9 +272,11 @@ class Group extends CtrlObject {
|
|||||||
public remove(individual: Individual[] | Individual): this {
|
public remove(individual: Individual[] | Individual): this {
|
||||||
if (Array.isArray(individual)) {
|
if (Array.isArray(individual)) {
|
||||||
for (let i = 0; i < individual.length; i++) {
|
for (let i = 0; i < individual.length; i++) {
|
||||||
|
individual[i].group = undefined;
|
||||||
this.individuals.delete(individual[i]);
|
this.individuals.delete(individual[i]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
individual.group = undefined;
|
||||||
this.individuals.delete(individual);
|
this.individuals.delete(individual);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
@ -308,7 +310,7 @@ class Group extends CtrlObject {
|
|||||||
/**
|
/**
|
||||||
* 行为列表
|
* 行为列表
|
||||||
*/
|
*/
|
||||||
public behaviors: Behavior[] = [];
|
public behaviors: IAnyBehavior[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加行为
|
* 添加行为
|
||||||
@ -358,15 +360,37 @@ class Group extends CtrlObject {
|
|||||||
* @param
|
* @param
|
||||||
*/
|
*/
|
||||||
public runner(t: number, effectType: "finalEffect" | "effect" | "afterEffect" ): void {
|
public runner(t: number, effectType: "finalEffect" | "effect" | "afterEffect" ): void {
|
||||||
this.individuals.forEach((individual) => {
|
|
||||||
for(let j = 0; j < this.behaviors.length; j++) {
|
for(let j = 0; j < this.behaviors.length; j++) {
|
||||||
if (this.behaviors[j].isDeleted()) {
|
|
||||||
continue;
|
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 {
|
} else {
|
||||||
this.behaviors[j][effectType](individual, this, this.model, t);
|
parameterCache.objects = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
this.individuals.forEach((individual) => {
|
||||||
|
runnerFunction(individual, this, this.model, t);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,7 +88,7 @@ class Individual {
|
|||||||
/**
|
/**
|
||||||
* 所属群组
|
* 所属群组
|
||||||
*/
|
*/
|
||||||
public group: Group;
|
public group: Group | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化
|
* 初始化
|
||||||
@ -97,11 +97,16 @@ class Individual {
|
|||||||
this.group = group;
|
this.group = group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isDie(): boolean {
|
||||||
|
return !!this.group;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 死亡
|
* 死亡
|
||||||
*/
|
*/
|
||||||
public die(): this {
|
public die(): this {
|
||||||
this.group.remove(this);
|
this.group?.remove(this);
|
||||||
|
this.group = undefined;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +115,7 @@ class Individual {
|
|||||||
* @param newGroup 新群体
|
* @param newGroup 新群体
|
||||||
*/
|
*/
|
||||||
public transfer(newGroup: Group): this {
|
public transfer(newGroup: Group): this {
|
||||||
this.group.remove(this);
|
this.group?.remove(this);
|
||||||
newGroup.add(this);
|
newGroup.add(this);
|
||||||
this.group = newGroup;
|
this.group = newGroup;
|
||||||
return this;
|
return this;
|
||||||
|
@ -66,6 +66,11 @@ class Model extends Emitter<ModelEvent> {
|
|||||||
*/
|
*/
|
||||||
public allGroupLabel = new Label(this, "AllGroup").setBuildInLabel();
|
public allGroupLabel = new Label(this, "AllGroup").setBuildInLabel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内置标签-全部群标签
|
||||||
|
*/
|
||||||
|
public currentGroupLabel = new Label(this, "CurrentGroupLabel").setBuildInLabel();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加标签
|
* 添加标签
|
||||||
*/
|
*/
|
||||||
@ -223,6 +228,7 @@ class Model extends Emitter<ModelEvent> {
|
|||||||
public updateBehaviorParameter() {
|
public updateBehaviorParameter() {
|
||||||
for (let i = 0; i < this.behaviorPool.length; i++) {
|
for (let i = 0; i < this.behaviorPool.length; i++) {
|
||||||
const behavior = this.behaviorPool[i];
|
const behavior = this.behaviorPool[i];
|
||||||
|
behavior.currentGroupKey = [];
|
||||||
|
|
||||||
for (let key in behavior.parameterOption) {
|
for (let key in behavior.parameterOption) {
|
||||||
switch (behavior.parameterOption[key].type) {
|
switch (behavior.parameterOption[key].type) {
|
||||||
@ -236,11 +242,17 @@ class Model extends Emitter<ModelEvent> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "CG":
|
||||||
case "G":
|
case "G":
|
||||||
const dataG: IParamValue<"G"> = behavior.parameter[key];
|
const dataG: IParamValue<"CG"> = behavior.parameter[key];
|
||||||
dataG.objects = undefined;
|
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;
|
dataG.objects = dataG.picker;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -260,8 +272,9 @@ class Model extends Emitter<ModelEvent> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "CLG":
|
||||||
case "LG":
|
case "LG":
|
||||||
const dataLG: IParamValue<"LG"> = behavior.parameter[key];
|
const dataLG: IParamValue<"CLG"> = behavior.parameter[key];
|
||||||
dataLG.objects = [];
|
dataLG.objects = [];
|
||||||
|
|
||||||
if (dataLG.picker instanceof Group && !dataLG.picker.isDeleted()) {
|
if (dataLG.picker instanceof Group && !dataLG.picker.isDeleted()) {
|
||||||
@ -269,9 +282,16 @@ class Model extends Emitter<ModelEvent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dataLG.picker instanceof Label && !dataLG.picker.isDeleted()) {
|
if (dataLG.picker instanceof Label && !dataLG.picker.isDeleted()) {
|
||||||
dataLG.objects = this.getObjectByLabel(dataLG.picker).filter((obj) => {
|
|
||||||
return obj instanceof Group;
|
if (dataLG.picker.id === this.currentGroupLabel.id) {
|
||||||
}) as any;
|
behavior.currentGroupKey.push(key);
|
||||||
|
dataLG.objects = [];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
dataLG.objects = this.getObjectByLabel(dataLG.picker).filter((obj) => {
|
||||||
|
return obj instanceof Group;
|
||||||
|
}) as any;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ type IMapObjectParamTypeKeyToType = {
|
|||||||
"G": IObjectParamCacheType<Group | undefined>;
|
"G": IObjectParamCacheType<Group | undefined>;
|
||||||
"LR": IObjectParamCacheType<Label | Range | undefined, Range[]>;
|
"LR": IObjectParamCacheType<Label | Range | undefined, Range[]>;
|
||||||
"LG": IObjectParamCacheType<Label | Group | undefined, Group[]>;
|
"LG": IObjectParamCacheType<Label | Group | undefined, Group[]>;
|
||||||
|
"CG": IObjectParamCacheType<Label | Group | undefined, Group | undefined>;
|
||||||
|
"CLG": IObjectParamCacheType<Label | Group | undefined, Group[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type IMapVectorParamTypeKeyToType = {
|
type IMapVectorParamTypeKeyToType = {
|
||||||
@ -41,7 +43,7 @@ type IParamValue<K extends IParamType> = AllMapType[K];
|
|||||||
/**
|
/**
|
||||||
* 特殊对象类型判定
|
* 特殊对象类型判定
|
||||||
*/
|
*/
|
||||||
const objectTypeListEnumSet = new Set<string>(["R", "G", "LR", "LG"]);
|
const objectTypeListEnumSet = new Set<string>(["R", "G", "LR", "LG", "CG", "CLG"]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对象断言表达式
|
* 对象断言表达式
|
||||||
@ -113,6 +115,11 @@ interface IParameterOptionItem<T extends IParamType = IParamType> {
|
|||||||
*/
|
*/
|
||||||
colorNormal?: boolean;
|
colorNormal?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示条件
|
||||||
|
*/
|
||||||
|
condition?: {key: string, value: any};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全部选项
|
* 全部选项
|
||||||
*/
|
*/
|
||||||
@ -163,6 +170,7 @@ function getDefaultValue<P extends IParameter> (option: IParameterOption<P>): IP
|
|||||||
defaultObj[key] = [0, 0, 0] as any;
|
defaultObj[key] = [0, 0, 0] as any;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "CG":
|
||||||
case "G":
|
case "G":
|
||||||
case "R":
|
case "R":
|
||||||
defaultObj[key] = {
|
defaultObj[key] = {
|
||||||
@ -171,6 +179,7 @@ function getDefaultValue<P extends IParameter> (option: IParameterOption<P>): IP
|
|||||||
} as any;
|
} as any;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "CLG":
|
||||||
case "LR":
|
case "LR":
|
||||||
case "LG":
|
case "LG":
|
||||||
defaultObj[key] = {
|
defaultObj[key] = {
|
||||||
|
@ -6,11 +6,12 @@ import { ClassicRenderer } from "@GLRender/ClassicRenderer";
|
|||||||
import { initializeIcons } from '@fluentui/font-icons-mdl2';
|
import { initializeIcons } from '@fluentui/font-icons-mdl2';
|
||||||
import { RootContainer } from "@Component/Container/RootContainer";
|
import { RootContainer } from "@Component/Container/RootContainer";
|
||||||
import { LayoutDirection } from "@Context/Layout";
|
import { LayoutDirection } from "@Context/Layout";
|
||||||
import { AllBehaviors } from "@Behavior/Behavior";
|
import { AllBehaviors, getBehaviorById } from "@Behavior/Behavior";
|
||||||
import { CommandBar } from "@Component/CommandBar/CommandBar";
|
import { CommandBar } from "@Component/CommandBar/CommandBar";
|
||||||
import { HeaderBar } from "@Component/HeaderBar/HeaderBar";
|
import { HeaderBar } from "@Component/HeaderBar/HeaderBar";
|
||||||
import { Popup } from "@Component/Popup/Popup";
|
import { Popup } from "@Component/Popup/Popup";
|
||||||
import { Entry } from "../Entry/Entry";
|
import { Entry } from "../Entry/Entry";
|
||||||
|
import { Group } from "@Model/Group";
|
||||||
import "./SimulatorWeb.scss";
|
import "./SimulatorWeb.scss";
|
||||||
|
|
||||||
initializeIcons("https://img.mrkbear.com/fabric-cdn-prod_20210407.001/");
|
initializeIcons("https://img.mrkbear.com/fabric-cdn-prod_20210407.001/");
|
||||||
@ -40,18 +41,22 @@ class SimulatorWeb extends Component {
|
|||||||
this.status.bindRenderer(classicRender);
|
this.status.bindRenderer(classicRender);
|
||||||
this.status.setting = this.setting;
|
this.status.setting = this.setting;
|
||||||
|
|
||||||
// 测试代码
|
const randomPosition = (group: Group) => {
|
||||||
if (true) {
|
|
||||||
let group = this.status.newGroup();
|
|
||||||
let range = this.status.newRange();
|
|
||||||
range.color = [.1, .5, .9];
|
|
||||||
group.new(100);
|
|
||||||
group.color = [.8, .1, .6];
|
|
||||||
group.individuals.forEach((individual) => {
|
group.individuals.forEach((individual) => {
|
||||||
individual.position[0] = (Math.random() - .5) * 2;
|
individual.position[0] = (Math.random() - .5) * 2;
|
||||||
individual.position[1] = (Math.random() - .5) * 2;
|
individual.position[1] = (Math.random() - .5) * 2;
|
||||||
individual.position[2] = (Math.random() - .5) * 2;
|
individual.position[2] = (Math.random() - .5) * 2;
|
||||||
})
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// 测试代码
|
||||||
|
if (false) {
|
||||||
|
let group = this.status.newGroup();
|
||||||
|
let range = this.status.newRange();
|
||||||
|
range.color = [.1, .5, .9];
|
||||||
|
group.new(100);
|
||||||
|
group.color = [.8, .1, .6];
|
||||||
|
randomPosition(group);
|
||||||
this.status.model.update(0);
|
this.status.model.update(0);
|
||||||
this.status.newLabel().name = "New Label";
|
this.status.newLabel().name = "New Label";
|
||||||
this.status.newLabel().name = "Test Label 01";
|
this.status.newLabel().name = "Test Label 01";
|
||||||
@ -70,6 +75,84 @@ class SimulatorWeb extends Component {
|
|||||||
group.addBehavior(boundary);
|
group.addBehavior(boundary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 鱼群模型测试
|
||||||
|
if (true) {
|
||||||
|
let fish1 = this.status.newGroup();
|
||||||
|
let fish2 = this.status.newGroup();
|
||||||
|
let shark = this.status.newGroup();
|
||||||
|
let range = this.status.newRange();
|
||||||
|
|
||||||
|
range.displayName = "Experimental site";
|
||||||
|
range.color = [.8, .1, .6];
|
||||||
|
|
||||||
|
fish1.new(100);
|
||||||
|
fish1.displayName = "Fish A";
|
||||||
|
fish1.color = [.1, .5, .9];
|
||||||
|
randomPosition(fish1);
|
||||||
|
|
||||||
|
fish2.new(50);
|
||||||
|
fish2.displayName = "Fish B";
|
||||||
|
fish2.color = [.3, .2, .9];
|
||||||
|
randomPosition(fish2);
|
||||||
|
|
||||||
|
shark.new(3);
|
||||||
|
shark.displayName = "Shark";
|
||||||
|
shark.color = [.8, .2, .3];
|
||||||
|
shark.renderParameter.size = 100;
|
||||||
|
shark.renderParameter.shape = "5";
|
||||||
|
randomPosition(shark);
|
||||||
|
|
||||||
|
this.status.model.update(0);
|
||||||
|
let fishLabel = this.status.newLabel();
|
||||||
|
fishLabel.name = "Fish";
|
||||||
|
fish1.addLabel(fishLabel);
|
||||||
|
fish2.addLabel(fishLabel);
|
||||||
|
|
||||||
|
let template = this.status.model.addBehavior(getBehaviorById("Template"));
|
||||||
|
template.name = "Template"; template.color = [150, 20, 220];
|
||||||
|
|
||||||
|
let dynamicFish = this.status.model.addBehavior(getBehaviorById("PhysicsDynamics"));
|
||||||
|
dynamicFish.name = "Dynamic Fish"; dynamicFish.color = [250, 200, 80];
|
||||||
|
|
||||||
|
let dynamicShark = this.status.model.addBehavior(getBehaviorById("PhysicsDynamics"));
|
||||||
|
dynamicShark.name = "Dynamic Shark"; dynamicShark.color = [250, 200, 80];
|
||||||
|
dynamicShark.parameter.maxAcceleration = 8.5;
|
||||||
|
dynamicShark.parameter.maxVelocity = 15.8;
|
||||||
|
dynamicShark.parameter.resistance = 3.6;
|
||||||
|
|
||||||
|
let brownian = this.status.model.addBehavior(getBehaviorById("Brownian"));
|
||||||
|
brownian.name = "Brownian"; brownian.color = [200, 80, 250];
|
||||||
|
|
||||||
|
let boundary = this.status.model.addBehavior(getBehaviorById("BoundaryConstraint"));
|
||||||
|
boundary.name = "Boundary"; boundary.color = [80, 200, 250];
|
||||||
|
boundary.parameter.range.picker = this.status.model.allRangeLabel;
|
||||||
|
|
||||||
|
let tracking = this.status.model.addBehavior(getBehaviorById("Tracking"));
|
||||||
|
tracking.name = "Tracking"; tracking.color = [80, 200, 250];
|
||||||
|
tracking.parameter.target.picker = fishLabel;
|
||||||
|
|
||||||
|
let attacking = this.status.model.addBehavior(getBehaviorById("ContactAttacking"));
|
||||||
|
attacking.name = "Contact Attacking"; attacking.color = [120, 100, 250];
|
||||||
|
attacking.parameter.target.picker = fishLabel;
|
||||||
|
|
||||||
|
fish1.addBehavior(dynamicFish);
|
||||||
|
fish1.addBehavior(brownian);
|
||||||
|
fish1.addBehavior(boundary);
|
||||||
|
|
||||||
|
fish2.addBehavior(dynamicFish);
|
||||||
|
fish2.addBehavior(brownian);
|
||||||
|
fish2.addBehavior(boundary);
|
||||||
|
|
||||||
|
shark.addBehavior(dynamicShark);
|
||||||
|
shark.addBehavior(boundary);
|
||||||
|
shark.addBehavior(tracking);
|
||||||
|
shark.addBehavior(attacking);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.status.model.updateBehaviorParameter();
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
|
||||||
(window as any).s = this;
|
(window as any).s = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Component, ReactNode} from "react";
|
import { Component, ReactNode} from "react";
|
||||||
import { useSettingWithEvent, IMixinSettingProps } from "@Context/Setting";
|
import { useSettingWithEvent, IMixinSettingProps } from "@Context/Setting";
|
||||||
import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
|
import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
|
||||||
|
import { getDefaultValue } from "@Model/Parameter";
|
||||||
import { IAnyBehavior } from "@Model/Behavior";
|
import { IAnyBehavior } from "@Model/Behavior";
|
||||||
import { Message } from "@Input/Message/Message";
|
import { Message } from "@Input/Message/Message";
|
||||||
import { AttrInput } from "@Input/AttrInput/AttrInput";
|
import { AttrInput } from "@Input/AttrInput/AttrInput";
|
||||||
@ -12,9 +13,17 @@ import "./BehaviorDetails.scss";
|
|||||||
|
|
||||||
interface IBehaviorDetailsProps {}
|
interface IBehaviorDetailsProps {}
|
||||||
|
|
||||||
|
interface IBehaviorDetailsState {
|
||||||
|
updateId: number;
|
||||||
|
}
|
||||||
|
|
||||||
@useSettingWithEvent("language")
|
@useSettingWithEvent("language")
|
||||||
@useStatusWithEvent("focusBehaviorChange", "behaviorAttrChange")
|
@useStatusWithEvent("focusBehaviorChange", "behaviorAttrChange")
|
||||||
class BehaviorDetails extends Component<IBehaviorDetailsProps & IMixinStatusProps & IMixinSettingProps> {
|
class BehaviorDetails extends Component<IBehaviorDetailsProps & IMixinStatusProps & IMixinSettingProps, IBehaviorDetailsState> {
|
||||||
|
|
||||||
|
public state: Readonly<IBehaviorDetailsState> = {
|
||||||
|
updateId: 1
|
||||||
|
};
|
||||||
|
|
||||||
private handelDeleteBehavior = (behavior: IAnyBehavior) => {
|
private handelDeleteBehavior = (behavior: IAnyBehavior) => {
|
||||||
if (this.props.status) {
|
if (this.props.status) {
|
||||||
@ -32,6 +41,28 @@ class BehaviorDetails extends Component<IBehaviorDetailsProps & IMixinStatusProp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handelRestoreBehavior = (behavior: IAnyBehavior) => {
|
||||||
|
if (this.props.status) {
|
||||||
|
const status = this.props.status;
|
||||||
|
status.popup.showPopup(ConfirmPopup, {
|
||||||
|
infoI18n: "Popup.Restore.Behavior.Confirm",
|
||||||
|
titleI18N: "Popup.Action.Objects.Confirm.Restore.Title",
|
||||||
|
yesI18n: "Popup.Action.Objects.Confirm.Restore",
|
||||||
|
red: "yes",
|
||||||
|
yes: () => {
|
||||||
|
status.changeBehaviorAttrib(
|
||||||
|
behavior.id, "parameter",
|
||||||
|
getDefaultValue(behavior.parameterOption) as any,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
this.setState({
|
||||||
|
updateId: this.state.updateId + 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private renderFrom(behavior: IAnyBehavior): ReactNode {
|
private renderFrom(behavior: IAnyBehavior): ReactNode {
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
@ -60,9 +91,17 @@ class BehaviorDetails extends Component<IBehaviorDetailsProps & IMixinStatusProp
|
|||||||
this.handelDeleteBehavior(behavior)
|
this.handelDeleteBehavior(behavior)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<TogglesInput
|
||||||
|
keyI18n="Common.Attr.Key.Behavior.Restore" red
|
||||||
|
onIconName="ReplyAll" offIconName="ReplyAll"
|
||||||
|
valueChange={() => {
|
||||||
|
this.handelRestoreBehavior(behavior)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<Parameter
|
<Parameter
|
||||||
key={behavior.id}
|
key={`${behavior.id}-${this.state.updateId}`}
|
||||||
option={behavior.parameterOption}
|
option={behavior.parameterOption}
|
||||||
value={behavior.parameter}
|
value={behavior.parameter}
|
||||||
i18n={(name, language) => behavior.getTerms(name, language)}
|
i18n={(name, language) => behavior.getTerms(name, language)}
|
||||||
|
Loading…
Reference in New Issue
Block a user