Compare commits
	
		
			No commits in common. "feb277c3a295fb1d51478d6586e5ceb495d07b4e" and "021e2e7821c12e2fe13715e7c6ba8b2386dd9a28" have entirely different histories.
		
	
	
		
			feb277c3a2
			...
			021e2e7821
		
	
		
| @ -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,328 +3,40 @@ 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"; | ||||
| 
 | ||||
| /** | ||||
|  * 行为构造函数类型 | ||||
|  * 群体的某种行为 | ||||
|  */ | ||||
| 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 = "" | ||||
| abstract class Behavior< | ||||
|     P extends IAnyObject = {}, | ||||
|     E extends Record<EventType, any> = {} | ||||
| > extends Emitter<E> { | ||||
| 
 | ||||
|     /** | ||||
|      * 行为 ID | ||||
|      */ | ||||
|     public behaviorId: string = ""; | ||||
|     abstract id: string; | ||||
|      | ||||
|     /** | ||||
|      * 行为名称 | ||||
|      */ | ||||
|     public behaviorName: string = ""; | ||||
|     abstract name: 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; | ||||
| 
 | ||||
|     /** | ||||
|      * 行为参数 | ||||
|      */ | ||||
|     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 {} | ||||
|     abstract parameter?: P; | ||||
| 
 | ||||
|     /** | ||||
|      * 全部影响作用前 | ||||
| @ -355,5 +67,5 @@ class Behavior< | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export { Behavior, BehaviorRecorder }; | ||||
| export { Behavior }; | ||||
| export default { Behavior }; | ||||
| @ -60,36 +60,22 @@ class CtrlObject extends LabelObject { | ||||
|         return this === obj || this.id === obj.id; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 标记对象被删除 | ||||
|      */ | ||||
|     public markDelete() { | ||||
|         this.deleteFlag = true; | ||||
|     }; | ||||
| 
 | ||||
|     /** | ||||
|      * 删除标记 | ||||
|      */ | ||||
|     private deleteFlag: boolean = false; | ||||
| 
 | ||||
|     /** | ||||
|      * 检测是否被删除 | ||||
|      */ | ||||
|     public testDelete() { | ||||
|         for (let i = 0; i < this.model.objectPool.length; i++) { | ||||
|             if (this.model.objectPool[i].equal(this)) { | ||||
|                 this.deleteFlag = false; | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         this.deleteFlag = true; | ||||
|     } | ||||
| 
 | ||||
|      /** | ||||
|       * 是否被删除 | ||||
|       */ | ||||
|     public isDeleted(): boolean { | ||||
|         return this.deleteFlag; | ||||
|         if (this.deleteFlag) return true; | ||||
|         for (let i = 0; i < this.model.objectPool.length; i++) { | ||||
|             if (this.model.objectPool[i].equal(this)) return false; | ||||
|         } | ||||
|         this.deleteFlag = true; | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -22,7 +22,7 @@ class Group extends CtrlObject { | ||||
|     /** | ||||
|      * 个体生成方式 | ||||
|      */ | ||||
|     public genMethod: GenMod = GenMod.Range; | ||||
|     public genMethod: GenMod = GenMod.Point; | ||||
| 
 | ||||
|     /** | ||||
|      * 生成位置坐标 | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| 
 | ||||
| import { Individual } from "./Individual"; | ||||
| import { Group } from "./Group"; | ||||
| import { Range } from "./Range"; | ||||
| @ -5,14 +6,18 @@ 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; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
| @ -63,6 +68,7 @@ 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; | ||||
|     } | ||||
| @ -91,6 +97,7 @@ 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); | ||||
|         } | ||||
|     } | ||||
| @ -128,6 +135,8 @@ 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; | ||||
|     } | ||||
| @ -139,6 +148,8 @@ 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; | ||||
|     } | ||||
| @ -165,7 +176,6 @@ class Model extends Emitter<ModelEvent> { | ||||
| 
 | ||||
|             if (needDeleted) { | ||||
|                 deletedObject.push(currentObject); | ||||
|                 currentObject.markDelete(); | ||||
|                 return false; | ||||
|             } else { | ||||
|                 return true; | ||||
| @ -174,68 +184,12 @@ 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