Add tracking behaviot & current label & Optmi running func & test fish scene
This commit is contained in:
parent
f8fbb70608
commit
0e1a070390
@ -3,12 +3,14 @@ import { Template } from "@Behavior/Template";
|
||||
import { Dynamics } from "@Behavior/Dynamics";
|
||||
import { Brownian } from "@Behavior/Brownian";
|
||||
import { BoundaryConstraint } from "@Behavior/BoundaryConstraint";
|
||||
import { Tracking } from "@Behavior/Tracking";
|
||||
|
||||
const AllBehaviors: IAnyBehaviorRecorder[] = [
|
||||
new BehaviorRecorder(Template),
|
||||
new BehaviorRecorder(Dynamics),
|
||||
new BehaviorRecorder(Brownian),
|
||||
new BehaviorRecorder(BoundaryConstraint),
|
||||
new BehaviorRecorder(Tracking),
|
||||
]
|
||||
|
||||
/**
|
||||
@ -54,4 +56,13 @@ function categoryBehaviors(behaviors: IAnyBehaviorRecorder[]): ICategory[] {
|
||||
return res;
|
||||
}
|
||||
|
||||
export { AllBehaviors, AllBehaviorsWithCategory, ICategory as ICategoryBehavior };
|
||||
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 };
|
@ -28,7 +28,7 @@ class BoundaryConstraint extends Behavior<IBoundaryConstraintBehaviorParameter,
|
||||
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 fx = 0;
|
||||
|
@ -31,7 +31,7 @@ class Brownian extends Behavior<IBrownianBehaviorParameter, IBrownianBehaviorEve
|
||||
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;
|
||||
|
||||
|
@ -31,7 +31,7 @@ class Dynamics extends Behavior<IDynamicsBehaviorParameter, IDynamicsBehaviorEve
|
||||
resistance: { name: "$Resistance", type: "number", defaultValue: 0.5, numberStep: .1, numberMin: 0 }
|
||||
};
|
||||
|
||||
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);
|
||||
|
@ -14,6 +14,8 @@ type ITemplateBehaviorParameter = {
|
||||
testLR: "LR";
|
||||
testLG: "LG";
|
||||
testVec: "vec";
|
||||
testCG: "CG",
|
||||
testCLG: "CLG",
|
||||
}
|
||||
|
||||
type ITemplateBehaviorEvent = {}
|
||||
@ -42,10 +44,12 @@ class Template extends Behavior<ITemplateBehaviorParameter, ITemplateBehaviorEve
|
||||
testG: { name: "$Test", type: "G" },
|
||||
testLR: { name: "$Test", type: "LR" },
|
||||
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 }
|
||||
};
|
||||
|
||||
public effect(individual: Individual, group: Group, model: Model, t: number): void {
|
||||
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
||||
|
||||
}
|
||||
|
||||
|
84
source/Behavior/Tracking.ts
Normal file
84
source/Behavior/Tracking.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import { Behavior } from "@Model/Behavior";
|
||||
import { Group } from "@Model/Group";
|
||||
import { Individual } from "@Model/Individual";
|
||||
import { Model } from "@Model/Model";
|
||||
|
||||
type ITrackingBehaviorParameter = {
|
||||
target: "LG",
|
||||
strength: "number"
|
||||
}
|
||||
|
||||
type ITrackingBehaviorEvent = {}
|
||||
|
||||
class Tracking extends Behavior<ITrackingBehaviorParameter, ITrackingBehaviorEvent> {
|
||||
|
||||
public override behaviorId: string = "Tracking";
|
||||
|
||||
public override behaviorName: string = "$Title";
|
||||
|
||||
public override iconName: string = "BullseyeTarget";
|
||||
|
||||
public override describe: string = "$Intro";
|
||||
|
||||
public override category: string = "$Interactive";
|
||||
|
||||
public override parameterOption = {
|
||||
target: { type: "CLG", name: "$Target" },
|
||||
strength: { type: "number", name: "$Strength", defaultValue: 10, numberMin: 0, numberStep: .1 }
|
||||
};
|
||||
|
||||
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
|
||||
let target: Individual | undefined;
|
||||
let currentDistant: number = Infinity;
|
||||
|
||||
for (let i = 0; i < this.parameter.target.objects.length; i++) {
|
||||
const targetGroup = this.parameter.target.objects[i];
|
||||
|
||||
targetGroup.individuals.forEach((targetIndividual) => {
|
||||
|
||||
// 排除自己
|
||||
if (targetIndividual === individual) return;
|
||||
let dis = targetIndividual.distanceTo(individual);
|
||||
|
||||
if (dis < currentDistant) {
|
||||
target = targetIndividual;
|
||||
currentDistant = dis;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
if (target) {
|
||||
individual.applyForce(
|
||||
(target.position[0] - individual.position[0]) * this.parameter.strength,
|
||||
(target.position[1] - individual.position[1]) * this.parameter.strength,
|
||||
(target.position[2] - individual.position[2]) * this.parameter.strength
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public override terms: Record<string, Record<string, string>> = {
|
||||
"$Title": {
|
||||
"ZH_CN": "追踪",
|
||||
"EN_US": "Tracking"
|
||||
},
|
||||
"$Target": {
|
||||
"ZH_CN": "追踪目标",
|
||||
"EN_US": "Tracking target"
|
||||
},
|
||||
"$Strength": {
|
||||
"ZH_CN": "追踪强度系数",
|
||||
"EN_US": "Tracking intensity coefficient"
|
||||
},
|
||||
"$Intro": {
|
||||
"ZH_CN": "个体将主动向最近的目标群个体发起追踪",
|
||||
"EN_US": "The individual will actively initiate tracking to the nearest target group individual"
|
||||
},
|
||||
"$Interactive": {
|
||||
"ZH_CN": "交互",
|
||||
"EN_US": "Interactive"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { Tracking };
|
@ -59,6 +59,10 @@ class ObjectPicker extends Component<IObjectPickerProps & IMixinStatusProps, IOb
|
||||
option.push(this.props.status.model.objectPool[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.type.includes("C")) {
|
||||
option.push(this.props.status.model.currentGroupLabel);
|
||||
}
|
||||
}
|
||||
|
||||
return option;
|
||||
|
@ -51,16 +51,25 @@ function getObjectDisplayInfo(item?: IPickerListItem): IDisplayInfo {
|
||||
if (item instanceof Label) {
|
||||
|
||||
if (item.isBuildIn) {
|
||||
|
||||
internal = true;
|
||||
allLabel = true;
|
||||
color = "transparent";
|
||||
|
||||
if (item.id === "AllRange") {
|
||||
icon = "ProductList";
|
||||
name = "Build.In.Label.Name.All.Range";
|
||||
} else if (item.id === "AllGroup") {
|
||||
}
|
||||
|
||||
else if (item.id === "AllGroup") {
|
||||
icon = "SizeLegacy";
|
||||
name = "Build.In.Label.Name.All.Group";
|
||||
}
|
||||
|
||||
else if (item.id === "CurrentGroupLabel") {
|
||||
icon = "TriangleShape";
|
||||
name = "Build.In.Label.Name.Current.Group";
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
|
@ -68,6 +68,7 @@ const EN_US = {
|
||||
"Popup.Behavior.Info.Confirm": "OK, I know it",
|
||||
"Build.In.Label.Name.All.Group": "All group",
|
||||
"Build.In.Label.Name.All.Range": "All range",
|
||||
"Build.In.Label.Name.Current.Group": "Current group",
|
||||
"Common.Search.Placeholder": "Search in here...",
|
||||
"Common.No.Data": "No Data",
|
||||
"Common.No.Unknown.Error": "Unknown error",
|
||||
|
@ -68,6 +68,7 @@ const ZH_CN = {
|
||||
"Popup.Behavior.Info.Confirm": "好的, 我知道了",
|
||||
"Build.In.Label.Name.All.Group": "全部群",
|
||||
"Build.In.Label.Name.All.Range": "全部范围",
|
||||
"Build.In.Label.Name.Current.Group": "当前群",
|
||||
"Common.Search.Placeholder": "在此处搜索...",
|
||||
"Common.No.Data": "暂无数据",
|
||||
"Common.No.Unknown.Error": "未知错误",
|
||||
|
@ -158,6 +158,11 @@ class Behavior<
|
||||
*/
|
||||
public parameter: IParameterValue<P>;
|
||||
|
||||
/**
|
||||
* 指定当前群的 Key
|
||||
*/
|
||||
public currentGroupKey: Array<keyof P> = [];
|
||||
|
||||
/**
|
||||
* 对象参数列表
|
||||
*/
|
||||
@ -222,7 +227,7 @@ class Behavior<
|
||||
* @param model 模型
|
||||
* @param t 经过时间
|
||||
*/
|
||||
public effect(individual: Individual, group: Group, model: Model, t: number): void {};
|
||||
public effect?: (individual: Individual, group: Group, model: Model, t: number) => void;
|
||||
|
||||
/**
|
||||
* 作用影响于个体
|
||||
@ -231,7 +236,7 @@ class Behavior<
|
||||
* @param model 模型
|
||||
* @param t 经过时间
|
||||
*/
|
||||
public afterEffect(individual: Individual, group: Group, model: Model, t: number): void {};
|
||||
public afterEffect?: (individual: Individual, group: Group, model: Model, t: number) => void;
|
||||
|
||||
/**
|
||||
* 全部影响作用后
|
||||
@ -240,7 +245,7 @@ class Behavior<
|
||||
* @param model 模型
|
||||
* @param t 经过时间
|
||||
*/
|
||||
public finalEffect(individual: Individual, group: Group, model: Model, t: number): void {};
|
||||
public finalEffect?: (individual: Individual, group: Group, model: Model, t: number) => void;
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Individual } from "@Model/Individual";
|
||||
import { CtrlObject } from "@Model/CtrlObject";
|
||||
import type { Behavior } from "@Model/Behavior";
|
||||
import type { Behavior, IAnyBehavior } from "@Model/Behavior";
|
||||
import { Label } from "@Model/Label";
|
||||
import { Range } from "@Model/Range";
|
||||
import { Model, ObjectID } from "@Model/Model";
|
||||
@ -308,7 +308,7 @@ class Group extends CtrlObject {
|
||||
/**
|
||||
* 行为列表
|
||||
*/
|
||||
public behaviors: Behavior[] = [];
|
||||
public behaviors: IAnyBehavior[] = [];
|
||||
|
||||
/**
|
||||
* 添加行为
|
||||
@ -358,15 +358,37 @@ class Group extends CtrlObject {
|
||||
* @param
|
||||
*/
|
||||
public runner(t: number, effectType: "finalEffect" | "effect" | "afterEffect" ): void {
|
||||
this.individuals.forEach((individual) => {
|
||||
for(let j = 0; j < this.behaviors.length; j++) {
|
||||
if (this.behaviors[j].isDeleted()) {
|
||||
continue;
|
||||
|
||||
for(let j = 0; j < this.behaviors.length; j++) {
|
||||
|
||||
const behavior = this.behaviors[j];
|
||||
if (behavior.isDeleted()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const runnerFunction = behavior[effectType];
|
||||
if (!runnerFunction) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let k = 0; k < behavior.currentGroupKey.length; k++) {
|
||||
|
||||
let parameterCache = behavior.parameter[
|
||||
behavior.currentGroupKey[k] as string
|
||||
];
|
||||
|
||||
if (Array.isArray(parameterCache?.objects)) {
|
||||
parameterCache.objects = [this];
|
||||
|
||||
} else {
|
||||
this.behaviors[j][effectType](individual, this, this.model, t);
|
||||
parameterCache.objects = this;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.individuals.forEach((individual) => {
|
||||
runnerFunction(individual, this, this.model, t);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -66,6 +66,11 @@ class Model extends Emitter<ModelEvent> {
|
||||
*/
|
||||
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() {
|
||||
for (let i = 0; i < this.behaviorPool.length; i++) {
|
||||
const behavior = this.behaviorPool[i];
|
||||
behavior.currentGroupKey = [];
|
||||
|
||||
for (let key in behavior.parameterOption) {
|
||||
switch (behavior.parameterOption[key].type) {
|
||||
@ -236,11 +242,17 @@ class Model extends Emitter<ModelEvent> {
|
||||
}
|
||||
break;
|
||||
|
||||
case "CG":
|
||||
case "G":
|
||||
const dataG: IParamValue<"G"> = behavior.parameter[key];
|
||||
const dataG: IParamValue<"CG"> = behavior.parameter[key];
|
||||
dataG.objects = undefined;
|
||||
|
||||
if (dataG.picker instanceof Label && dataG.picker.id === this.currentGroupLabel.id) {
|
||||
behavior.currentGroupKey.push(key);
|
||||
dataG.objects = undefined;
|
||||
}
|
||||
|
||||
if (dataG.picker instanceof Group && !dataG.picker.isDeleted()) {
|
||||
else if (dataG.picker instanceof Group && !dataG.picker.isDeleted()) {
|
||||
dataG.objects = dataG.picker;
|
||||
}
|
||||
break;
|
||||
@ -260,8 +272,9 @@ class Model extends Emitter<ModelEvent> {
|
||||
}
|
||||
break;
|
||||
|
||||
case "CLG":
|
||||
case "LG":
|
||||
const dataLG: IParamValue<"LG"> = behavior.parameter[key];
|
||||
const dataLG: IParamValue<"CLG"> = behavior.parameter[key];
|
||||
dataLG.objects = [];
|
||||
|
||||
if (dataLG.picker instanceof Group && !dataLG.picker.isDeleted()) {
|
||||
@ -269,9 +282,16 @@ class Model extends Emitter<ModelEvent> {
|
||||
}
|
||||
|
||||
if (dataLG.picker instanceof Label && !dataLG.picker.isDeleted()) {
|
||||
dataLG.objects = this.getObjectByLabel(dataLG.picker).filter((obj) => {
|
||||
return obj instanceof Group;
|
||||
}) as any;
|
||||
|
||||
if (dataLG.picker.id === this.currentGroupLabel.id) {
|
||||
behavior.currentGroupKey.push(key);
|
||||
dataLG.objects = [];
|
||||
|
||||
} else {
|
||||
dataLG.objects = this.getObjectByLabel(dataLG.picker).filter((obj) => {
|
||||
return obj instanceof Group;
|
||||
}) as any;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ type IMapObjectParamTypeKeyToType = {
|
||||
"G": IObjectParamCacheType<Group | undefined>;
|
||||
"LR": IObjectParamCacheType<Label | Range | undefined, Range[]>;
|
||||
"LG": IObjectParamCacheType<Label | Group | undefined, Group[]>;
|
||||
"CG": IObjectParamCacheType<Label | Group | undefined, Group | undefined>;
|
||||
"CLG": IObjectParamCacheType<Label | Group | undefined, Group[]>;
|
||||
}
|
||||
|
||||
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"]);
|
||||
|
||||
/**
|
||||
* 对象断言表达式
|
||||
@ -163,6 +165,7 @@ function getDefaultValue<P extends IParameter> (option: IParameterOption<P>): IP
|
||||
defaultObj[key] = [0, 0, 0] as any;
|
||||
break;
|
||||
|
||||
case "CG":
|
||||
case "G":
|
||||
case "R":
|
||||
defaultObj[key] = {
|
||||
@ -171,6 +174,7 @@ function getDefaultValue<P extends IParameter> (option: IParameterOption<P>): IP
|
||||
} as any;
|
||||
break;
|
||||
|
||||
case "CLG":
|
||||
case "LR":
|
||||
case "LG":
|
||||
defaultObj[key] = {
|
||||
|
@ -6,11 +6,12 @@ import { ClassicRenderer } from "@GLRender/ClassicRenderer";
|
||||
import { initializeIcons } from '@fluentui/font-icons-mdl2';
|
||||
import { RootContainer } from "@Component/Container/RootContainer";
|
||||
import { LayoutDirection } from "@Context/Layout";
|
||||
import { AllBehaviors } from "@Behavior/Behavior";
|
||||
import { AllBehaviors, getBehaviorById } from "@Behavior/Behavior";
|
||||
import { CommandBar } from "@Component/CommandBar/CommandBar";
|
||||
import { HeaderBar } from "@Component/HeaderBar/HeaderBar";
|
||||
import { Popup } from "@Component/Popup/Popup";
|
||||
import { Entry } from "../Entry/Entry";
|
||||
import { Group } from "@Model/Group";
|
||||
import "./SimulatorWeb.scss";
|
||||
|
||||
initializeIcons("https://img.mrkbear.com/fabric-cdn-prod_20210407.001/");
|
||||
@ -40,18 +41,22 @@ class SimulatorWeb extends Component {
|
||||
this.status.bindRenderer(classicRender);
|
||||
this.status.setting = this.setting;
|
||||
|
||||
// 测试代码
|
||||
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];
|
||||
const randomPosition = (group: Group) => {
|
||||
group.individuals.forEach((individual) => {
|
||||
individual.position[0] = (Math.random() - .5) * 2;
|
||||
individual.position[1] = (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.newLabel().name = "New Label";
|
||||
this.status.newLabel().name = "Test Label 01";
|
||||
@ -70,6 +75,76 @@ class SimulatorWeb extends Component {
|
||||
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("Dynamics"));
|
||||
dynamicFish.name = "Dynamic Fish"; dynamicFish.color = [250, 200, 80];
|
||||
|
||||
let dynamicShark = this.status.model.addBehavior(getBehaviorById("Dynamics"));
|
||||
dynamicShark.name = "Dynamic Shark"; dynamicShark.color = [250, 200, 80];
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
setTimeout(() => {
|
||||
this.status.model.updateBehaviorParameter();
|
||||
}, 200)
|
||||
}
|
||||
|
||||
(window as any).s = this;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user