From 156b4651f590e4386ac549f4ab548b6190b4cd60 Mon Sep 17 00:00:00 2001 From: MrKBear Date: Fri, 25 Mar 2022 15:40:15 +0800 Subject: [PATCH] Add behavior model --- source/Context/Status.tsx | 8 +- source/Model/Behavior.ts | 310 +++++++++++++++++++++++++++++++++++-- source/Model/CtrlObject.ts | 28 +++- source/Model/Model.ts | 74 +++++++-- 4 files changed, 384 insertions(+), 36 deletions(-) diff --git a/source/Context/Status.tsx b/source/Context/Status.tsx index b74e557..16f111f 100644 --- a/source/Context/Status.tsx +++ b/source/Context/Status.tsx @@ -83,13 +83,13 @@ class Status extends Emitter { */ 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; }); } diff --git a/source/Model/Behavior.ts b/source/Model/Behavior.ts index d86de8c..2258936 100644 --- a/source/Model/Behavior.ts +++ b/source/Model/Behavior.ts @@ -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 = {} -> extends Emitter { +type IBehaviorConstructor> = + new (id: string, parameter: IBehaviorParameterValue) => 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 = AllMapType[K]; + + + +/** + * 特殊对象类型判定 + */ +const objectTypeListEnumSet = new Set(["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 { + + /** + * 参数类型 + */ + type: T; + + /** + * 参数默认值 + */ + defaultValue?: IParamValue; + + /** + * 数值变化回调 + */ + onChange?: (value: IParamValue) => any; + + /** + * 名字 + */ + name: string; + + /** + * 字符长度 + */ + stringLength?: number; + + /** + * 数字步长 + */ + numberStep?: number; + + /** + * 最大值最小值 + */ + numberMax?: number; + numberMin?: number; + + /** + * 图标名字 + */ + iconName?: string; +} + +/** + * 参数键值类型 + */ +type IBehaviorParameterValueItem

= IParamValue; + +/** + * 参数类型列表 + */ +interface IBehaviorParameterOption { + [x: string]: IBehaviorParameterOptionItem; +} + +/** + * 参数类型列表映射到参数对象 + */ +type IBehaviorParameterValue

= { + [x in keyof P]: IBehaviorParameterValueItem +} + + +/** + * 行为的基础信息 + */ +class BehaviorInfo = {}> extends Emitter { + + /** + * 图标名字 + */ + public iconName: string = "" /** * 行为 ID */ - abstract id: string; - + public behaviorId: string = ""; + /** * 行为名称 */ - abstract name: string; + public behaviorName: string = ""; /** * 行为描述 */ public describe?: string = ""; +} + +class BehaviorRecorder< + B extends Behavior +> extends BehaviorInfo { + + /** + * 命名序号 + */ + public nameIndex: number = 0; + + /** + * 获取下一个 ID + */ + public getNextId() { + return `B-${this.behaviorName}-${this.nameIndex ++}`; + } + + /** + * 行为类型 + */ + public behavior: IBehaviorConstructor; + + /** + * 行为实例 + */ + public behaviorInstance: B; + + /** + * 对象参数列表 + */ + public parameterOption: B["parameterOption"]; + + /** + * 获取参数列表的默认值 + */ + public getDefaultValue(): IBehaviorParameterValue { + let defaultObj = {} as IBehaviorParameterValue; + 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) { + 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 = {} +> extends BehaviorInfo { + + /** + * 用户自定义名字 + */ + public name: string = ""; + + /** + * 实例 ID + */ + public id: string = ""; /** * 优先级 * 值越大执行顺序越靠后 */ - public priority?: number = 0; + public priority: number = 0; /** * 行为参数 */ - abstract parameter?: P; + public parameter: IBehaviorParameterValue

; + + /** + * 对象参数列表 + */ + public parameterOption: P = {} as any; + + public constructor(id: string, parameter: IBehaviorParameterValue

) { + super(); + this.id = id; + this.parameter = parameter; + } + + /** + * 相等校验 + */ + public equal(behavior: Behavior): 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 }; \ No newline at end of file diff --git a/source/Model/CtrlObject.ts b/source/Model/CtrlObject.ts index 982d20d..a6e810e 100644 --- a/source/Model/CtrlObject.ts +++ b/source/Model/CtrlObject.ts @@ -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; } } diff --git a/source/Model/Model.ts b/source/Model/Model.ts index 9e537db..6f445af 100644 --- a/source/Model/Model.ts +++ b/source/Model/Model.ts @@ -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 { 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 { 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 { 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 { 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 { if (needDeleted) { deletedObject.push(currentObject); + currentObject.markDelete(); return false; } else { return true; @@ -184,12 +174,68 @@ class Model extends Emitter { 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[] = []; + + /** + * 添加一个行为 + */ + public addBehavior>(recorder: BehaviorRecorder): 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 | 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 | ObjectID) { + let deletedBehavior: Behavior | 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); + } + } + /** * 渲染器 */