Merge pull request 'Add group individual label archive function' (#43) from dev-mrkbear into master

Reviewed-on: http://git.mrkbear.com/MrKBear/living-together/pulls/43
This commit is contained in:
MrKBear 2022-04-23 16:56:07 +08:00
commit 0d7cbe135d
10 changed files with 231 additions and 36 deletions

View File

@ -1,6 +1,6 @@
import { Behavior } from "@Model/Behavior"; import { Behavior } from "@Model/Behavior";
import Group from "@Model/Group"; import { Group } from "@Model/Group";
import Individual from "@Model/Individual"; import { Individual } from "@Model/Individual";
import { Model } from "@Model/Model"; import { Model } from "@Model/Model";
type IPhysicsDynamicsBehaviorParameter = { type IPhysicsDynamicsBehaviorParameter = {

View File

@ -33,7 +33,13 @@ class CommandBar extends Component<ICommandBarProps & IMixinSettingProps & IMixi
}} }}
> >
<div> <div>
{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({ {this.getRenderButton({
iconName: this.props.status?.actuator.start() ? "Pause" : "Play", iconName: this.props.status?.actuator.start() ? "Pause" : "Play",
i18NKey: "Command.Bar.Play.Info", i18NKey: "Command.Bar.Play.Info",

View File

@ -1,10 +1,18 @@
import { Emitter, EventType } from "@Model/Emitter"; 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 { interface IArchiveEvent {
fileChange: Archive; fileChange: Archive;
} }
interface IArchiveObject {
nextIndividualId: number;
objectPool: IArchiveCtrlObject[];
labelPool: IArchiveLabel[];
}
class Archive< class Archive<
M extends any = any, M extends any = any,
E extends Record<EventType, any> = {} E extends Record<EventType, any> = {}
@ -35,17 +43,32 @@ class Archive<
* *
*/ */
public save(model: Model): string { public save(model: Model): string {
let fileData: Record<string, any> = {};
// 保存对象
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 "";
} }
/** /**

View File

@ -1,8 +1,11 @@
import { LabelObject } from "@Model/Label" import { LabelObject } from "@Model/Label"
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
import { parameter2ArchiveObject, archiveObject2Parameter, IArchiveParseFn } from "@Model/Parameter";
import type { IAnyObject, Model } from "@Model/Model"; import type { IAnyObject, Model } from "@Model/Model";
import type { ObjectID } from "@Model/Model"; import type { ObjectID } from "@Model/Model";
import {
parameter2ArchiveObject, archiveObject2Parameter,
IArchiveParseFn, IObjectParamArchiveType, object2ArchiveObject
} from "@Model/Parameter";
interface IArchiveCtrlObject { interface IArchiveCtrlObject {
displayName: CtrlObject["displayName"]; displayName: CtrlObject["displayName"];
@ -12,6 +15,7 @@ interface IArchiveCtrlObject {
id: string; id: string;
renderParameter: any; renderParameter: any;
deleteFlag: CtrlObject["deleteFlag"]; deleteFlag: CtrlObject["deleteFlag"];
labels: IObjectParamArchiveType[];
} }
/** /**
@ -47,7 +51,7 @@ class CtrlObject<A extends IAnyObject = IAnyObject> extends LabelObject {
/** /**
* *
*/ */
protected model: Model; public model: Model;
/** /**
* *
@ -117,20 +121,26 @@ class CtrlObject<A extends IAnyObject = IAnyObject> extends LabelObject {
update: !!this.update, update: !!this.update,
id: this.id, id: this.id,
renderParameter: parameter2ArchiveObject(this.renderParameter), renderParameter: parameter2ArchiveObject(this.renderParameter),
deleteFlag: !!this.deleteFlag deleteFlag: !!this.deleteFlag,
labels: this.labels.map((label) => {
return object2ArchiveObject(label);
})
} as any; } as any;
} }
public fromArchive(archive: IArchiveCtrlObject & A, paster?: IArchiveParseFn): void { public fromArchive(archive: IArchiveCtrlObject & A, paster: IArchiveParseFn): void {
this.displayName = archive.displayName; this.displayName = archive.displayName;
this.color = archive.color.concat([]); this.color = archive.color.concat([]);
this.display = !!archive.display; this.display = !!archive.display;
this.update = !!archive.update; this.update = !!archive.update;
this.id = archive.id; this.id = archive.id;
this.renderParameter = archiveObject2Parameter(
archive.renderParameter, paster ?? (() => undefined)
);
this.deleteFlag = !!archive.deleteFlag; this.deleteFlag = !!archive.deleteFlag;
this.renderParameter = archiveObject2Parameter(
archive.renderParameter, paster
);
this.labels = archive.labels.map((obj) => {
return paster(obj) as any;
}).filter((c) => !!c);
} }
} }

View File

@ -1,20 +1,31 @@
import { Individual } from "@Model/Individual"; import { Individual } from "@Model/Individual";
import { CtrlObject } from "@Model/CtrlObject"; import { CtrlObject, IArchiveCtrlObject } from "@Model/CtrlObject";
import type { Behavior, IAnyBehavior } from "@Model/Behavior"; import type { Behavior, IAnyBehavior } from "@Model/Behavior";
import { Label } from "@Model/Label"; import { Label } from "@Model/Label";
import { Range } from "@Model/Range"; import { Range } from "@Model/Range";
import { Model, ObjectID } from "@Model/Model"; import { Model, ObjectID } from "@Model/Model";
import { getDefaultValue } from "@Model/Parameter"; import { getDefaultValue, IArchiveParseFn, IObjectParamArchiveType, object2ArchiveObject } from "@Model/Parameter";
enum GenMod { enum GenMod {
Point = "p", Point = "p",
Range = "R" Range = "R"
} }
interface IArchiveGroup {
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<IArchiveGroup> {
/** /**
* *
@ -407,6 +418,34 @@ class Group extends CtrlObject {
return dataBuffer; 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;
}).filter(c => !!c);
}
public constructor(model: Model) { public constructor(model: Model) {
super(model); super(model);
@ -417,5 +456,4 @@ class Group extends CtrlObject {
} }
} }
export default Group;
export { Group, GenMod }; export { Group, GenMod };

View File

@ -1,5 +1,15 @@
import type { Group } from "@Model/Group"; 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;
}
/** /**
* *
@ -47,6 +57,8 @@ class Individual {
} }
} }
public id: string;
/** /**
* *
*/ */
@ -95,6 +107,7 @@ class Individual {
*/ */
public constructor(group: Group) { public constructor(group: Group) {
this.group = group; this.group = group;
this.id = this.group.model.getNextIndividualId();
} }
public isDie(): boolean { public isDie(): boolean {
@ -167,7 +180,67 @@ class Individual {
this.metaData.set(key, value); this.metaData.set(key, value);
return 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<ObjectID, any>;
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 default Individual;
export { Individual }; export { Individual };

View File

@ -1,6 +1,14 @@
import type { Model, ObjectID } from "@Model/Model"; import type { Model, ObjectID } from "@Model/Model";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
interface IArchiveLabel {
isBuildIn: boolean;
id: string;
name: string;
color: number[];
deleteFlag: boolean;
}
/** /**
* *
*/ */
@ -78,10 +86,29 @@ class Label {
/** /**
* *
*/ */
public setBuildInLabel(): this { public setBuildInLabel(id: string): this {
this.id = id;
this.isBuildIn = true; this.isBuildIn = true;
return this; 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
}
} }
/** /**
@ -92,7 +119,7 @@ class LabelObject {
/** /**
* *
*/ */
private labels: Label[] = []; public labels: Label[] = [];
/** /**
* Label * Label
@ -132,4 +159,4 @@ class LabelObject {
} }
} }
export { Label, LabelObject }; export { Label, LabelObject, IArchiveLabel };

View File

@ -29,6 +29,17 @@ type ModelEvent = {
*/ */
class Model extends Emitter<ModelEvent> { class Model extends Emitter<ModelEvent> {
/**
* 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<ModelEvent> {
/** /**
* - * -
*/ */
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");
/** /**
* *

View File

@ -1,7 +1,8 @@
import { Group } from "@Model/Group"; import { Group } from "@Model/Group";
import { Range } from "@Model/Range"; import { Range } from "@Model/Range";
import { Label } from "@Model/Label"; import { Label } from "@Model/Label";
import { Behavior } from "@Model/Behavior"; import { Behavior, IAnyBehavior } from "@Model/Behavior";
import { Individual } from "@Model/Individual";
type IObjectParamArchiveType = { type IObjectParamArchiveType = {
__LIVING_TOGETHER_OBJECT_ID: string; __LIVING_TOGETHER_OBJECT_ID: string;
@ -224,12 +225,18 @@ function getDefaultValue<P extends IParameter> (option: IParameterOption<P>): IP
return defaultObj; return defaultObj;
} }
type IRealObjectType = Range | Group | Label | Behavior; type IRealObjectType = Range | Group | Label | IAnyBehavior;
type IArchiveParseFn = (archive: IObjectParamArchiveType) => IRealObjectType | undefined; type IArchiveParseFn = (archive: IObjectParamArchiveType) => IRealObjectType | undefined;
function object2ArchiveObject(object: IRealObjectType | IRealObjectType[] | any, testArray: boolean = true): function object2ArchiveObject(object: IRealObjectType | IRealObjectType[] | any, testArray: boolean = true):
IObjectParamArchiveType | IObjectParamArchiveType[] | undefined { 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 { return {
__LIVING_TOGETHER_OBJECT_ID: "Range", __LIVING_TOGETHER_OBJECT_ID: "Range",
__LIVING_TOGETHER_OBJECT_TYPE: object.id __LIVING_TOGETHER_OBJECT_TYPE: object.id
@ -351,5 +358,5 @@ export {
IParamType, IParamValue, isObjectType, isVectorType, getDefaultValue, IParamType, IParamValue, isObjectType, isVectorType, getDefaultValue,
IParameterOptionItem, IParameter, IParameterOption, IParameterValue, IParameterOptionItem, IParameter, IParameterOption, IParameterValue,
object2ArchiveObject, parameter2ArchiveObject, archiveObject2Parameter, object2ArchiveObject, parameter2ArchiveObject, archiveObject2Parameter,
IArchiveParseFn IArchiveParseFn, IObjectParamArchiveType, isArchiveObjectType
} }

View File

@ -39,7 +39,7 @@ class Range extends CtrlObject<IArchiveRange> {
}; };
} }
public override fromArchive(archive: IArchiveCtrlObject & IArchiveRange, paster?: IArchiveParseFn): void { public override fromArchive(archive: IArchiveCtrlObject & IArchiveRange, paster: IArchiveParseFn): void {
super.fromArchive(archive, paster); super.fromArchive(archive, paster);
this.position = archive.position.concat([]), this.position = archive.position.concat([]),
this.radius = archive.radius.concat([]) this.radius = archive.radius.concat([])