Add behavior model
This commit is contained in:
parent
021e2e7821
commit
156b4651f5
@ -83,13 +83,13 @@ class Status extends Emitter<IStatusEvent> {
|
||||
*/
|
||||
public focusLabel?: Label;
|
||||
|
||||
private drawtimer?: NodeJS.Timeout;
|
||||
private drawTimer?: NodeJS.Timeout;
|
||||
|
||||
private delayDraw = () => {
|
||||
this.drawtimer ? clearTimeout(this.drawtimer) : null;
|
||||
this.drawtimer = setTimeout(() => {
|
||||
this.drawTimer ? clearTimeout(this.drawTimer) : null;
|
||||
this.drawTimer = setTimeout(() => {
|
||||
this.model.draw();
|
||||
this.drawtimer = undefined;
|
||||
this.drawTimer = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3,40 +3,328 @@ import { Emitter, EventType } from "./Emitter";
|
||||
import type { Individual } from "./Individual";
|
||||
import type { Group } from "./Group";
|
||||
import type { Model } from "./Model";
|
||||
import type { Range } from "./Range";
|
||||
import type { Label } from "./Label";
|
||||
|
||||
/**
|
||||
* 群体的某种行为
|
||||
* 行为构造函数类型
|
||||
*/
|
||||
abstract class Behavior<
|
||||
P extends IAnyObject = {},
|
||||
E extends Record<EventType, any> = {}
|
||||
> extends Emitter<E> {
|
||||
type IBehaviorConstructor<B extends Behavior<any, any>> =
|
||||
new (id: string, parameter: IBehaviorParameterValue<B["parameterOption"]>) => B;
|
||||
|
||||
/**
|
||||
* 参数类型
|
||||
*/
|
||||
type IMapBasicParamTypeKeyToType = {
|
||||
"number": number;
|
||||
"string": string;
|
||||
"boolean": boolean;
|
||||
}
|
||||
|
||||
type IMapObjectParamTypeKeyToType = {
|
||||
"R"?: Range;
|
||||
"G"?: Group;
|
||||
"GR"?: Group | Range;
|
||||
"LR"?: Label | Range;
|
||||
"LG"?: Label | Group;
|
||||
"LGR"?: Label | Group | Range;
|
||||
}
|
||||
|
||||
type IMapVectorParamTypeKeyToType = {
|
||||
"vec": number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数类型映射
|
||||
*/
|
||||
type AllMapType = IMapBasicParamTypeKeyToType & IMapObjectParamTypeKeyToType & IMapVectorParamTypeKeyToType;
|
||||
type IParamType = keyof AllMapType;
|
||||
type IObjectType = keyof IMapObjectParamTypeKeyToType;
|
||||
type IVectorType = keyof IMapVectorParamTypeKeyToType;
|
||||
type IParamValue<K extends IParamType> = AllMapType[K];
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 特殊对象类型判定
|
||||
*/
|
||||
const objectTypeListEnumSet = new Set<IParamType>(["R", "G", "GR", "LR", "LG", "LGR"]);
|
||||
|
||||
/**
|
||||
* 对象断言表达式
|
||||
*/
|
||||
function isObjectType(key: IParamType): key is IVectorType {
|
||||
return objectTypeListEnumSet.has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向量断言表达式
|
||||
*/
|
||||
function isVectorType(key: IParamType): key is IObjectType {
|
||||
return key === "vec";
|
||||
}
|
||||
|
||||
/**
|
||||
* 模型参数类型
|
||||
*/
|
||||
interface IBehaviorParameterOptionItem<T extends IParamType = IParamType> {
|
||||
|
||||
/**
|
||||
* 参数类型
|
||||
*/
|
||||
type: T;
|
||||
|
||||
/**
|
||||
* 参数默认值
|
||||
*/
|
||||
defaultValue?: IParamValue<T>;
|
||||
|
||||
/**
|
||||
* 数值变化回调
|
||||
*/
|
||||
onChange?: (value: IParamValue<T>) => any;
|
||||
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* 字符长度
|
||||
*/
|
||||
stringLength?: number;
|
||||
|
||||
/**
|
||||
* 数字步长
|
||||
*/
|
||||
numberStep?: number;
|
||||
|
||||
/**
|
||||
* 最大值最小值
|
||||
*/
|
||||
numberMax?: number;
|
||||
numberMin?: number;
|
||||
|
||||
/**
|
||||
* 图标名字
|
||||
*/
|
||||
iconName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数键值类型
|
||||
*/
|
||||
type IBehaviorParameterValueItem<P extends IBehaviorParameterOptionItem> = IParamValue<P["type"]>;
|
||||
|
||||
/**
|
||||
* 参数类型列表
|
||||
*/
|
||||
interface IBehaviorParameterOption {
|
||||
[x: string]: IBehaviorParameterOptionItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数类型列表映射到参数对象
|
||||
*/
|
||||
type IBehaviorParameterValue<P extends IBehaviorParameterOption> = {
|
||||
[x in keyof P]: IBehaviorParameterValueItem<P[x]>
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 行为的基础信息
|
||||
*/
|
||||
class BehaviorInfo<E extends Record<EventType, any> = {}> extends Emitter<E> {
|
||||
|
||||
/**
|
||||
* 图标名字
|
||||
*/
|
||||
public iconName: string = ""
|
||||
|
||||
/**
|
||||
* 行为 ID
|
||||
*/
|
||||
abstract id: string;
|
||||
|
||||
public behaviorId: string = "";
|
||||
|
||||
/**
|
||||
* 行为名称
|
||||
*/
|
||||
abstract name: string;
|
||||
public behaviorName: string = "";
|
||||
|
||||
/**
|
||||
* 行为描述
|
||||
*/
|
||||
public describe?: string = "";
|
||||
}
|
||||
|
||||
class BehaviorRecorder<
|
||||
B extends Behavior<any, any>
|
||||
> extends BehaviorInfo {
|
||||
|
||||
/**
|
||||
* 命名序号
|
||||
*/
|
||||
public nameIndex: number = 0;
|
||||
|
||||
/**
|
||||
* 获取下一个 ID
|
||||
*/
|
||||
public getNextId() {
|
||||
return `B-${this.behaviorName}-${this.nameIndex ++}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 行为类型
|
||||
*/
|
||||
public behavior: IBehaviorConstructor<B>;
|
||||
|
||||
/**
|
||||
* 行为实例
|
||||
*/
|
||||
public behaviorInstance: B;
|
||||
|
||||
/**
|
||||
* 对象参数列表
|
||||
*/
|
||||
public parameterOption: B["parameterOption"];
|
||||
|
||||
/**
|
||||
* 获取参数列表的默认值
|
||||
*/
|
||||
public getDefaultValue(): IBehaviorParameterValue<B["parameterOption"]> {
|
||||
let defaultObj = {} as IBehaviorParameterValue<B["parameterOption"]>;
|
||||
for (let key in this.parameterOption) {
|
||||
let defaultVal = this.parameterOption[key].defaultValue;
|
||||
|
||||
defaultObj[key] = defaultVal as any;
|
||||
if (defaultObj[key] === undefined) {
|
||||
|
||||
switch (this.parameterOption[key].type) {
|
||||
case "string":
|
||||
defaultObj[key] = "" as any;
|
||||
break;
|
||||
|
||||
case "number":
|
||||
defaultObj[key] = 0 as any;
|
||||
break;
|
||||
|
||||
case "boolean":
|
||||
defaultObj[key] = false as any;
|
||||
break;
|
||||
|
||||
case "vec":
|
||||
defaultObj[key] = [0, 0, 0] as any;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的行为实例
|
||||
*/
|
||||
public new(): B {
|
||||
return new this.behavior(this.getNextId(), this.getDefaultValue());
|
||||
}
|
||||
|
||||
public constructor(behavior: IBehaviorConstructor<B>) {
|
||||
super();
|
||||
this.behavior = behavior;
|
||||
this.behaviorInstance = new this.behavior(this.getNextId(), {} as any);
|
||||
this.parameterOption = this.behaviorInstance.parameterOption;
|
||||
this.iconName = this.behaviorInstance.iconName;
|
||||
this.behaviorId = this.behaviorInstance.behaviorId;
|
||||
this.behaviorName = this.behaviorInstance.behaviorName;
|
||||
this.describe = this.behaviorInstance.describe;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 群体的某种行为
|
||||
*/
|
||||
class Behavior<
|
||||
P extends IBehaviorParameterOption = {},
|
||||
E extends Record<EventType, any> = {}
|
||||
> extends BehaviorInfo<E> {
|
||||
|
||||
/**
|
||||
* 用户自定义名字
|
||||
*/
|
||||
public name: string = "";
|
||||
|
||||
/**
|
||||
* 实例 ID
|
||||
*/
|
||||
public id: string = "";
|
||||
|
||||
/**
|
||||
* 优先级
|
||||
* 值越大执行顺序越靠后
|
||||
*/
|
||||
public priority?: number = 0;
|
||||
public priority: number = 0;
|
||||
|
||||
/**
|
||||
* 行为参数
|
||||
*/
|
||||
abstract parameter?: P;
|
||||
public parameter: IBehaviorParameterValue<P>;
|
||||
|
||||
/**
|
||||
* 对象参数列表
|
||||
*/
|
||||
public parameterOption: P = {} as any;
|
||||
|
||||
public constructor(id: string, parameter: IBehaviorParameterValue<P>) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.parameter = parameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 相等校验
|
||||
*/
|
||||
public equal(behavior: Behavior<any, any>): boolean {
|
||||
return this === behavior || this.id === behavior.id;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
private deleteFlag: boolean = false;
|
||||
|
||||
/**
|
||||
* 标记对象被删除
|
||||
*/
|
||||
public markDelete() {
|
||||
this.deleteFlag = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否被删除
|
||||
*/
|
||||
public isDeleted(): boolean {
|
||||
return this.deleteFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载时调用
|
||||
*/
|
||||
public load(model: Model): void {}
|
||||
|
||||
/**
|
||||
* 卸载时调用
|
||||
*/
|
||||
public unload(model: Model): void {}
|
||||
|
||||
/**
|
||||
* 挂载时调用
|
||||
*/
|
||||
public mount(group: Group, model: Model): void {}
|
||||
|
||||
/**
|
||||
* 挂载时调用
|
||||
*/
|
||||
public unmount(group: Group, model: Model): void {}
|
||||
|
||||
/**
|
||||
* 全部影响作用前
|
||||
@ -67,5 +355,5 @@ abstract class Behavior<
|
||||
|
||||
}
|
||||
|
||||
export { Behavior };
|
||||
export { Behavior, BehaviorRecorder };
|
||||
export default { Behavior };
|
@ -60,22 +60,36 @@ class CtrlObject extends LabelObject {
|
||||
return this === obj || this.id === obj.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记对象被删除
|
||||
*/
|
||||
public markDelete() {
|
||||
this.deleteFlag = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
private deleteFlag: boolean = false;
|
||||
|
||||
/**
|
||||
* 是否被删除
|
||||
*/
|
||||
public isDeleted(): boolean {
|
||||
if (this.deleteFlag) return true;
|
||||
/**
|
||||
* 检测是否被删除
|
||||
*/
|
||||
public testDelete() {
|
||||
for (let i = 0; i < this.model.objectPool.length; i++) {
|
||||
if (this.model.objectPool[i].equal(this)) return false;
|
||||
if (this.model.objectPool[i].equal(this)) {
|
||||
this.deleteFlag = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.deleteFlag = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否被删除
|
||||
*/
|
||||
public isDeleted(): boolean {
|
||||
return this.deleteFlag;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import { Individual } from "./Individual";
|
||||
import { Group } from "./Group";
|
||||
import { Range } from "./Range";
|
||||
@ -6,18 +5,14 @@ import { Emitter, EventType, EventMixin } from "./Emitter";
|
||||
import { CtrlObject } from "./CtrlObject";
|
||||
import { ObjectID, AbstractRenderer } from "./Renderer";
|
||||
import { Label } from "./Label";
|
||||
import { Behavior, BehaviorRecorder } from "./Behavior";
|
||||
|
||||
type ModelEvent = {
|
||||
loop: number;
|
||||
groupAdd: Group;
|
||||
rangeAdd: Range;
|
||||
labelAdd: Label;
|
||||
labelDelete: Label;
|
||||
labelChange: Label[];
|
||||
objectAdd: CtrlObject;
|
||||
objectDelete: CtrlObject[];
|
||||
objectChange: CtrlObject[];
|
||||
individualChange: Group;
|
||||
behaviorChange: Behavior;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -68,7 +63,6 @@ class Model extends Emitter<ModelEvent> {
|
||||
console.log(`Model: Creat label with id ${this.idIndex}`);
|
||||
let label = new Label(this, this.nextId("L"), name);
|
||||
this.labelPool.push(label);
|
||||
this.emit("labelAdd", label);
|
||||
this.emit("labelChange", this.labelPool);
|
||||
return label;
|
||||
}
|
||||
@ -97,7 +91,6 @@ class Model extends Emitter<ModelEvent> {
|
||||
this.labelPool.splice(index, 1);
|
||||
deletedLabel.testDelete();
|
||||
console.log(`Model: Delete label ${deletedLabel.name ?? deletedLabel.id}`);
|
||||
this.emit("labelDelete", deletedLabel);
|
||||
this.emit("labelChange", this.labelPool);
|
||||
}
|
||||
}
|
||||
@ -135,8 +128,6 @@ class Model extends Emitter<ModelEvent> {
|
||||
console.log(`Model: Creat group with id ${this.idIndex}`);
|
||||
let group = new Group(this, this.nextId("G"));
|
||||
this.objectPool.push(group);
|
||||
this.emit("groupAdd", group);
|
||||
this.emit("objectAdd", group);
|
||||
this.emit("objectChange", this.objectPool);
|
||||
return group;
|
||||
}
|
||||
@ -148,8 +139,6 @@ class Model extends Emitter<ModelEvent> {
|
||||
console.log(`Model: Creat range with id ${this.idIndex}`);
|
||||
let range = new Range(this, this.nextId("R"));
|
||||
this.objectPool.push(range);
|
||||
this.emit("rangeAdd", range);
|
||||
this.emit("objectAdd", range);
|
||||
this.emit("objectChange", this.objectPool);
|
||||
return range;
|
||||
}
|
||||
@ -176,6 +165,7 @@ class Model extends Emitter<ModelEvent> {
|
||||
|
||||
if (needDeleted) {
|
||||
deletedObject.push(currentObject);
|
||||
currentObject.markDelete();
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
@ -184,12 +174,68 @@ class Model extends Emitter<ModelEvent> {
|
||||
|
||||
if (deletedObject.length) {
|
||||
console.log(`Model: Delete object ${deletedObject.map((object) => object.id).join(", ")}`);
|
||||
this.emit("objectDelete", deletedObject);
|
||||
this.emit("objectChange", this.objectPool);
|
||||
}
|
||||
return deletedObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* 行为池
|
||||
*/
|
||||
public behaviorPool: Behavior<any, any>[] = [];
|
||||
|
||||
/**
|
||||
* 添加一个行为
|
||||
*/
|
||||
public addBehavior<B extends Behavior<any, any>>(recorder: BehaviorRecorder<B>): B {
|
||||
let behavior = recorder.new();
|
||||
behavior.load(this);
|
||||
this.behaviorPool.push(behavior);
|
||||
console.log(`Model: Add ${behavior.behaviorName} behavior ${behavior.id}`);
|
||||
this.emit("behaviorChange", behavior);
|
||||
return behavior;
|
||||
};
|
||||
|
||||
/**
|
||||
* 通过 ID 获取行为
|
||||
*/
|
||||
public getBehaviorById(id: ObjectID): Behavior<any, any> | undefined {
|
||||
for (let i = 0; i < this.behaviorPool.length; i++) {
|
||||
if (this.behaviorPool[i].id.toString() === id.toString()) {
|
||||
return this.behaviorPool[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索并删除一个 Behavior
|
||||
* @param name 搜索值
|
||||
*/
|
||||
public deleteBehavior(name: Behavior<any, any> | ObjectID) {
|
||||
let deletedBehavior: Behavior<any, any> | undefined;
|
||||
let index = 0;
|
||||
|
||||
for (let i = 0; i < this.behaviorPool.length; i++) {
|
||||
if (name instanceof Behavior) {
|
||||
if (this.behaviorPool[i].equal(name)) {
|
||||
deletedBehavior = this.behaviorPool[i];
|
||||
index = i;
|
||||
}
|
||||
} else if (name === this.behaviorPool[i].id) {
|
||||
deletedBehavior = this.behaviorPool[i];
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (deletedBehavior) {
|
||||
this.behaviorPool.splice(index, 1);
|
||||
deletedBehavior.unload(this);
|
||||
deletedBehavior.markDelete();
|
||||
console.log(`Model: Delete behavior ${deletedBehavior.name ?? deletedBehavior.id}`);
|
||||
this.emit("behaviorChange", deletedBehavior);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染器
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user