From d11fd2d3280c1bec7006e74d65a849b75c816c8b Mon Sep 17 00:00:00 2001 From: MrKBear Date: Thu, 21 Apr 2022 23:22:38 +0800 Subject: [PATCH 1/3] Add group archive function --- source/Behavior/PhysicsDynamics.ts | 4 ++-- source/Model/CtrlObject.ts | 2 +- source/Model/Group.ts | 17 ++++++++++++++--- source/Model/Individual.ts | 4 +++- source/Model/Label.ts | 3 ++- source/Model/Model.ts | 17 ++++++++++++++--- source/Model/Parameter.ts | 11 +++++++++-- 7 files changed, 45 insertions(+), 13 deletions(-) diff --git a/source/Behavior/PhysicsDynamics.ts b/source/Behavior/PhysicsDynamics.ts index 994b731..e29ee85 100644 --- a/source/Behavior/PhysicsDynamics.ts +++ b/source/Behavior/PhysicsDynamics.ts @@ -1,6 +1,6 @@ import { Behavior } from "@Model/Behavior"; -import Group from "@Model/Group"; -import Individual from "@Model/Individual"; +import { Group } from "@Model/Group"; +import { Individual } from "@Model/Individual"; import { Model } from "@Model/Model"; type IPhysicsDynamicsBehaviorParameter = { diff --git a/source/Model/CtrlObject.ts b/source/Model/CtrlObject.ts index 8769222..53afb9c 100644 --- a/source/Model/CtrlObject.ts +++ b/source/Model/CtrlObject.ts @@ -47,7 +47,7 @@ class CtrlObject extends LabelObject { /** * 控制模型 */ - protected model: Model; + public model: Model; /** * 渲染数据 diff --git a/source/Model/Group.ts b/source/Model/Group.ts index d16f175..2fe5c5f 100644 --- a/source/Model/Group.ts +++ b/source/Model/Group.ts @@ -4,17 +4,29 @@ import type { Behavior, IAnyBehavior } from "@Model/Behavior"; import { Label } from "@Model/Label"; import { Range } from "@Model/Range"; import { Model, ObjectID } from "@Model/Model"; -import { getDefaultValue } from "@Model/Parameter"; +import { getDefaultValue, IObjectParamArchiveType } from "@Model/Parameter"; enum GenMod { Point = "p", Range = "R" } +interface IArchiveGroup { + individuals: IObjectParamArchiveType[]; + genMethod: Group["genMethod"]; + genPoint: Group["genPoint"]; + genRange: IObjectParamArchiveType | undefined; + genCount: Group["genCount"]; + genErrorMessage: Group["genErrorMessage"]; + genErrorMessageShowCount: Group["genErrorMessageShowCount"]; + killCount: Group["killCount"]; + behaviors: IObjectParamArchiveType[]; +} + /** * 群体类型 */ -class Group extends CtrlObject { +class Group extends CtrlObject { /** * 所有个体 @@ -417,5 +429,4 @@ class Group extends CtrlObject { } } -export default Group; export { Group, GenMod }; \ No newline at end of file diff --git a/source/Model/Individual.ts b/source/Model/Individual.ts index ba41bb9..74e926e 100644 --- a/source/Model/Individual.ts +++ b/source/Model/Individual.ts @@ -47,6 +47,8 @@ class Individual { } } + public id: string; + /** * 坐标 */ @@ -95,6 +97,7 @@ class Individual { */ public constructor(group: Group) { this.group = group; + this.id = this.group.model.getNextIndividualId(); } public isDie(): boolean { @@ -169,5 +172,4 @@ class Individual { } } -export default Individual; export { Individual }; \ No newline at end of file diff --git a/source/Model/Label.ts b/source/Model/Label.ts index 96e10f5..f7ffb39 100644 --- a/source/Model/Label.ts +++ b/source/Model/Label.ts @@ -78,7 +78,8 @@ class Label { /** * 设置为内置标签 */ - public setBuildInLabel(): this { + public setBuildInLabel(id: string): this { + this.id = id; this.isBuildIn = true; return this; } diff --git a/source/Model/Model.ts b/source/Model/Model.ts index c7a0086..1ac2e2a 100644 --- a/source/Model/Model.ts +++ b/source/Model/Model.ts @@ -29,6 +29,17 @@ type ModelEvent = { */ class Model extends Emitter { + /** + * 个体 ID 生成 + */ + public nextIndividualId: number = 0; + + public getNextIndividualId() { + this.nextIndividualId ++; + const random = Math.random().toString(36).slice(-8); + return `${this.nextIndividualId}-${random}`; + } + /** * 对象列表 */ @@ -50,17 +61,17 @@ class Model extends Emitter { /** * 内置标签-全部范围标签 */ - public allRangeLabel = new Label(this, "AllRange").setBuildInLabel(); + public allRangeLabel = new Label(this).setBuildInLabel("AllRange"); /** * 内置标签-全部群标签 */ - public allGroupLabel = new Label(this, "AllGroup").setBuildInLabel(); + public allGroupLabel = new Label(this).setBuildInLabel("AllGroup"); /** * 内置标签-全部群标签 */ - public currentGroupLabel = new Label(this, "CurrentGroupLabel").setBuildInLabel(); + public currentGroupLabel = new Label(this).setBuildInLabel("CurrentGroupLabel"); /** * 添加标签 diff --git a/source/Model/Parameter.ts b/source/Model/Parameter.ts index b77702b..b0353ae 100644 --- a/source/Model/Parameter.ts +++ b/source/Model/Parameter.ts @@ -2,6 +2,7 @@ import { Group } from "@Model/Group"; import { Range } from "@Model/Range"; import { Label } from "@Model/Label"; import { Behavior } from "@Model/Behavior"; +import { Individual } from "@Model/Individual"; type IObjectParamArchiveType = { __LIVING_TOGETHER_OBJECT_ID: string; @@ -229,7 +230,13 @@ type IArchiveParseFn = (archive: IObjectParamArchiveType) => IRealObjectType | u function object2ArchiveObject(object: IRealObjectType | IRealObjectType[] | any, testArray: boolean = true): IObjectParamArchiveType | IObjectParamArchiveType[] | undefined { - if (object instanceof Range) { + if (object instanceof Individual) { + return { + __LIVING_TOGETHER_OBJECT_ID: "Individual", + __LIVING_TOGETHER_OBJECT_TYPE: object.id + } + } + else if (object instanceof Range) { return { __LIVING_TOGETHER_OBJECT_ID: "Range", __LIVING_TOGETHER_OBJECT_TYPE: object.id @@ -351,5 +358,5 @@ export { IParamType, IParamValue, isObjectType, isVectorType, getDefaultValue, IParameterOptionItem, IParameter, IParameterOption, IParameterValue, object2ArchiveObject, parameter2ArchiveObject, archiveObject2Parameter, - IArchiveParseFn + IArchiveParseFn, IObjectParamArchiveType } \ No newline at end of file From 276c8c63a136f73256653bef8f00b02739ec590a Mon Sep 17 00:00:00 2001 From: MrKBear Date: Fri, 22 Apr 2022 20:48:53 +0800 Subject: [PATCH 2/3] Add group & individual archive function --- source/Model/Group.ts | 33 +++++++++++++++-- source/Model/Individual.ts | 73 +++++++++++++++++++++++++++++++++++++- source/Model/Parameter.ts | 6 ++-- 3 files changed, 105 insertions(+), 7 deletions(-) diff --git a/source/Model/Group.ts b/source/Model/Group.ts index 2fe5c5f..8048bf3 100644 --- a/source/Model/Group.ts +++ b/source/Model/Group.ts @@ -1,10 +1,10 @@ import { Individual } from "@Model/Individual"; -import { CtrlObject } from "@Model/CtrlObject"; +import { CtrlObject, IArchiveCtrlObject } from "@Model/CtrlObject"; import type { Behavior, IAnyBehavior } from "@Model/Behavior"; import { Label } from "@Model/Label"; import { Range } from "@Model/Range"; import { Model, ObjectID } from "@Model/Model"; -import { getDefaultValue, IObjectParamArchiveType } from "@Model/Parameter"; +import { getDefaultValue, IArchiveParseFn, IObjectParamArchiveType, object2ArchiveObject } from "@Model/Parameter"; enum GenMod { Point = "p", @@ -12,7 +12,6 @@ enum GenMod { } interface IArchiveGroup { - individuals: IObjectParamArchiveType[]; genMethod: Group["genMethod"]; genPoint: Group["genPoint"]; genRange: IObjectParamArchiveType | undefined; @@ -418,6 +417,34 @@ class Group extends CtrlObject { }); return dataBuffer; } + + public override toArchive(): IArchiveCtrlObject & IArchiveGroup { + return { + ...super.toArchive(), + genMethod: this.genMethod, + genPoint: this.genPoint.concat([]), + genRange: object2ArchiveObject(this.genRange) as IObjectParamArchiveType, + genCount: this.genCount, + genErrorMessage: this.genErrorMessage, + genErrorMessageShowCount: this.genErrorMessageShowCount, + killCount: this.killCount, + behaviors: object2ArchiveObject(this.behaviors) as IObjectParamArchiveType[] + }; + } + + public override fromArchive(archive: IArchiveCtrlObject & IArchiveGroup, paster: IArchiveParseFn): void { + super.fromArchive(archive, paster); + this.genMethod = archive.genMethod, + this.genPoint = archive.genPoint.concat([]), + this.genRange = archive.genRange ? paster(archive.genRange) as any : undefined, + this.genCount = archive.genCount, + this.genErrorMessage = archive.genErrorMessage, + this.genErrorMessageShowCount = archive.genErrorMessageShowCount, + this.killCount = archive.killCount, + this.behaviors = archive.behaviors.map((item) => { + return item ? paster(item) as any : undefined; + }); + } public constructor(model: Model) { diff --git a/source/Model/Individual.ts b/source/Model/Individual.ts index 74e926e..96637fd 100644 --- a/source/Model/Individual.ts +++ b/source/Model/Individual.ts @@ -1,5 +1,15 @@ import type { Group } from "@Model/Group"; -import { ObjectID } from "@Model/Model"; +import { IAnyObject, ObjectID } from "@Model/Model"; +import { IArchiveParseFn, object2ArchiveObject, isArchiveObjectType } from "@Model/Parameter"; + +interface IArchiveIndividual { + id: string; + position: number[]; + velocity: number[]; + acceleration: number[]; + force: number[]; + metaData: IAnyObject; +} /** * 群中的个体类型 @@ -170,6 +180,67 @@ class Individual { this.metaData.set(key, value); return value; } + + public toArchive(): IArchiveIndividual { + + const metaDataArchive = {} as IAnyObject; + this.metaData.forEach((value, key) => { + + // 处理内置对象 + let ltObject = object2ArchiveObject(value); + if (ltObject) { + metaDataArchive[key] = ltObject; + } + + // 处理数组 + else if (Array.isArray(value)) { + metaDataArchive[key] = value.concat([]); + } + + // 处理值 + else { + metaDataArchive[key] = value; + } + }); + + return { + id: this.id, + position: this.position.concat([]), + velocity: this.velocity.concat([]), + acceleration: this.acceleration.concat([]), + force: this.force.concat([]), + metaData: metaDataArchive + }; + } + + public fromArchive(archive: IArchiveIndividual, paster: IArchiveParseFn): void { + + const metaData = new Map() as Map; + for (const key in archive.metaData) { + + const value = archive.metaData[key]; + + // 处理内置对象 + if (value instanceof Object && isArchiveObjectType(value)) { + metaData.set(key, paster(value)); + } + + else if (Array.isArray(value)) { + metaData.set(key, value.concat([])); + } + + else { + metaData.set(key, value); + } + } + + this.id = this.id, + this.position = this.position.concat([]), + this.velocity = this.velocity.concat([]), + this.acceleration = this.acceleration.concat([]), + this.force = this.force.concat([]), + this.metaData = metaData; + } } export { Individual }; \ No newline at end of file diff --git a/source/Model/Parameter.ts b/source/Model/Parameter.ts index b0353ae..82aba18 100644 --- a/source/Model/Parameter.ts +++ b/source/Model/Parameter.ts @@ -1,7 +1,7 @@ import { Group } from "@Model/Group"; import { Range } from "@Model/Range"; import { Label } from "@Model/Label"; -import { Behavior } from "@Model/Behavior"; +import { Behavior, IAnyBehavior } from "@Model/Behavior"; import { Individual } from "@Model/Individual"; type IObjectParamArchiveType = { @@ -225,7 +225,7 @@ function getDefaultValue

(option: IParameterOption

): IP return defaultObj; } -type IRealObjectType = Range | Group | Label | Behavior; +type IRealObjectType = Range | Group | Label | IAnyBehavior; type IArchiveParseFn = (archive: IObjectParamArchiveType) => IRealObjectType | undefined; function object2ArchiveObject(object: IRealObjectType | IRealObjectType[] | any, testArray: boolean = true): @@ -358,5 +358,5 @@ export { IParamType, IParamValue, isObjectType, isVectorType, getDefaultValue, IParameterOptionItem, IParameter, IParameterOption, IParameterValue, object2ArchiveObject, parameter2ArchiveObject, archiveObject2Parameter, - IArchiveParseFn, IObjectParamArchiveType + IArchiveParseFn, IObjectParamArchiveType, isArchiveObjectType } \ No newline at end of file From 068acb19a87bf4010feeedf09d6ef70737935759 Mon Sep 17 00:00:00 2001 From: MrKBear Date: Fri, 22 Apr 2022 23:48:10 +0800 Subject: [PATCH 3/3] Add label archive function --- source/Component/CommandBar/CommandBar.tsx | 8 ++++- source/Model/Archive.ts | 41 +++++++++++++++++----- source/Model/CtrlObject.ts | 22 ++++++++---- source/Model/Group.ts | 2 +- source/Model/Label.ts | 30 ++++++++++++++-- source/Model/Range.ts | 2 +- 6 files changed, 85 insertions(+), 20 deletions(-) diff --git a/source/Component/CommandBar/CommandBar.tsx b/source/Component/CommandBar/CommandBar.tsx index 151892e..b6faf75 100644 --- a/source/Component/CommandBar/CommandBar.tsx +++ b/source/Component/CommandBar/CommandBar.tsx @@ -33,7 +33,13 @@ class CommandBar extends Component

- {this.getRenderButton({ iconName: "Save", i18NKey: "Command.Bar.Save.Info" })} + {this.getRenderButton({ + iconName: "Save", + i18NKey: "Command.Bar.Save.Info", + click: () => { + this.props.status?.archive.save(this.props.status.model); + } + })} {this.getRenderButton({ iconName: this.props.status?.actuator.start() ? "Pause" : "Play", i18NKey: "Command.Bar.Play.Info", diff --git a/source/Model/Archive.ts b/source/Model/Archive.ts index b229182..8f9e2d6 100644 --- a/source/Model/Archive.ts +++ b/source/Model/Archive.ts @@ -1,10 +1,18 @@ import { Emitter, EventType } from "@Model/Emitter"; -import { Model } from "./Model"; +import { IArchiveCtrlObject } from "@Model/CtrlObject"; +import { Model } from "@Model/Model"; +import { IArchiveLabel } from "@Model/Label"; interface IArchiveEvent { fileChange: Archive; } +interface IArchiveObject { + nextIndividualId: number; + objectPool: IArchiveCtrlObject[]; + labelPool: IArchiveLabel[]; +} + class Archive< M extends any = any, E extends Record = {} @@ -35,17 +43,32 @@ class Archive< * 模型转换为文件 */ public save(model: Model): string { - let fileData: Record = {}; - - // 保存对象 - fileData.objects = []; - - // 记录 - model.objectPool.map((object) => { + // 存贮 CtrlObject + const objectPool: IArchiveCtrlObject[] = []; + model.objectPool.forEach(obj => { + objectPool.push(obj.toArchive()); }) - return JSON.stringify(model); + // 存储 Label + const labelPool: IArchiveLabel[] = []; + model.labelPool.forEach(obj => { + labelPool.push(obj.toArchive()); + }) + + const fileData: IArchiveObject = { + nextIndividualId: model.nextIndividualId, + objectPool: objectPool, + labelPool: labelPool + }; + + console.log(fileData); + console.log({value: JSON.stringify(fileData)}); + + this.isSaved = true; + this.emit( ...["fileChange", this] as any ); + + return ""; } /** diff --git a/source/Model/CtrlObject.ts b/source/Model/CtrlObject.ts index 53afb9c..21a6f31 100644 --- a/source/Model/CtrlObject.ts +++ b/source/Model/CtrlObject.ts @@ -1,8 +1,11 @@ import { LabelObject } from "@Model/Label" import { v4 as uuid } from "uuid"; -import { parameter2ArchiveObject, archiveObject2Parameter, IArchiveParseFn } from "@Model/Parameter"; import type { IAnyObject, Model } from "@Model/Model"; import type { ObjectID } from "@Model/Model"; +import { + parameter2ArchiveObject, archiveObject2Parameter, + IArchiveParseFn, IObjectParamArchiveType, object2ArchiveObject +} from "@Model/Parameter"; interface IArchiveCtrlObject { displayName: CtrlObject["displayName"]; @@ -12,6 +15,7 @@ interface IArchiveCtrlObject { id: string; renderParameter: any; deleteFlag: CtrlObject["deleteFlag"]; + labels: IObjectParamArchiveType[]; } /** @@ -117,20 +121,26 @@ class CtrlObject extends LabelObject { update: !!this.update, id: this.id, renderParameter: parameter2ArchiveObject(this.renderParameter), - deleteFlag: !!this.deleteFlag + deleteFlag: !!this.deleteFlag, + labels: this.labels.map((label) => { + return object2ArchiveObject(label); + }) } as any; } - public fromArchive(archive: IArchiveCtrlObject & A, paster?: IArchiveParseFn): void { + public fromArchive(archive: IArchiveCtrlObject & A, paster: IArchiveParseFn): void { this.displayName = archive.displayName; this.color = archive.color.concat([]); this.display = !!archive.display; this.update = !!archive.update; this.id = archive.id; - this.renderParameter = archiveObject2Parameter( - archive.renderParameter, paster ?? (() => undefined) - ); this.deleteFlag = !!archive.deleteFlag; + this.renderParameter = archiveObject2Parameter( + archive.renderParameter, paster + ); + this.labels = archive.labels.map((obj) => { + return paster(obj) as any; + }).filter((c) => !!c); } } diff --git a/source/Model/Group.ts b/source/Model/Group.ts index 8048bf3..bb3a970 100644 --- a/source/Model/Group.ts +++ b/source/Model/Group.ts @@ -443,7 +443,7 @@ class Group extends CtrlObject { this.killCount = archive.killCount, this.behaviors = archive.behaviors.map((item) => { return item ? paster(item) as any : undefined; - }); + }).filter(c => !!c); } public constructor(model: Model) { diff --git a/source/Model/Label.ts b/source/Model/Label.ts index f7ffb39..48502a5 100644 --- a/source/Model/Label.ts +++ b/source/Model/Label.ts @@ -1,6 +1,14 @@ import type { Model, ObjectID } from "@Model/Model"; import { v4 as uuid } from "uuid"; +interface IArchiveLabel { + isBuildIn: boolean; + id: string; + name: string; + color: number[]; + deleteFlag: boolean; +} + /** * 数据标签 */ @@ -83,6 +91,24 @@ class Label { this.isBuildIn = true; return this; } + + public toArchive(): IArchiveLabel { + return { + isBuildIn: this.isBuildIn, + id: this.id, + name: this.name, + color: this.color.concat([]), + deleteFlag: this.deleteFlag + } as any; + } + + public fromArchive(archive: IArchiveLabel): void { + this.isBuildIn = archive.isBuildIn, + this.id = archive.id, + this.name = archive.name, + this.color = archive.color.concat([]), + this.deleteFlag = archive.deleteFlag + } } /** @@ -93,7 +119,7 @@ class LabelObject { /** * 标签集合 */ - private labels: Label[] = []; + public labels: Label[] = []; /** * 获取全部 Label @@ -133,4 +159,4 @@ class LabelObject { } } -export { Label, LabelObject }; \ No newline at end of file +export { Label, LabelObject, IArchiveLabel }; \ No newline at end of file diff --git a/source/Model/Range.ts b/source/Model/Range.ts index 7b4a507..7c0b39b 100644 --- a/source/Model/Range.ts +++ b/source/Model/Range.ts @@ -39,7 +39,7 @@ class Range extends CtrlObject { }; } - public override fromArchive(archive: IArchiveCtrlObject & IArchiveRange, paster?: IArchiveParseFn): void { + public override fromArchive(archive: IArchiveCtrlObject & IArchiveRange, paster: IArchiveParseFn): void { super.fromArchive(archive, paster); this.position = archive.position.concat([]), this.radius = archive.radius.concat([])