Add parameter archive func & Add ctrl object archive function #42

Merged
MrKBear merged 5 commits from dev-mrkbear into master 2022-04-21 19:49:20 +08:00
14 changed files with 391 additions and 138 deletions

92
package-lock.json generated
View File

@ -15,13 +15,15 @@
"express": "^4.17.3", "express": "^4.17.3",
"gl-matrix": "^3.4.3", "gl-matrix": "^3.4.3",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2" "react-dom": "^17.0.2",
"uuid": "^8.3.2"
}, },
"devDependencies": { "devDependencies": {
"@atao60/fse-cli": "^0.1.7", "@atao60/fse-cli": "^0.1.7",
"@types/detect-port": "^1.3.2", "@types/detect-port": "^1.3.2",
"@types/react": "^17.0.38", "@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11", "@types/react-dom": "^17.0.11",
"@types/uuid": "^8.3.4",
"autoprefixer": "^10.4.2", "autoprefixer": "^10.4.2",
"css-loader": "^6.5.1", "css-loader": "^6.5.1",
"electron": "^18.0.4", "electron": "^18.0.4",
@ -1273,6 +1275,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
"dev": true
},
"node_modules/@types/ws": { "node_modules/@types/ws": {
"version": "8.5.0", "version": "8.5.0",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.0.tgz", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.0.tgz",
@ -1737,9 +1745,9 @@
} }
}, },
"node_modules/async": { "node_modules/async": {
"version": "2.6.3", "version": "2.6.4",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"lodash": "^4.17.14" "lodash": "^4.17.14"
@ -5214,9 +5222,9 @@
} }
}, },
"node_modules/node-forge": { "node_modules/node-forge": {
"version": "1.2.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
"integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">= 6.13.0" "node": ">= 6.13.0"
@ -6354,6 +6362,16 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/request/node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/require-directory": { "node_modules/require-directory": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@ -6937,15 +6955,6 @@
"websocket-driver": "^0.7.4" "websocket-driver": "^0.7.4"
} }
}, },
"node_modules/sockjs/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/socks": { "node_modules/socks": {
"version": "2.6.2", "version": "2.6.2",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz",
@ -7628,13 +7637,11 @@
} }
}, },
"node_modules/uuid": { "node_modules/uuid": {
"version": "3.4.0", "version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
"bin": { "bin": {
"uuid": "bin/uuid" "uuid": "dist/bin/uuid"
} }
}, },
"node_modules/validate-npm-package-license": { "node_modules/validate-npm-package-license": {
@ -9238,6 +9245,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
"dev": true
},
"@types/ws": { "@types/ws": {
"version": "8.5.0", "version": "8.5.0",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.0.tgz", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.0.tgz",
@ -9620,9 +9633,9 @@
"dev": true "dev": true
}, },
"async": { "async": {
"version": "2.6.3", "version": "2.6.4",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"dev": true, "dev": true,
"requires": { "requires": {
"lodash": "^4.17.14" "lodash": "^4.17.14"
@ -12326,9 +12339,9 @@
} }
}, },
"node-forge": { "node-forge": {
"version": "1.2.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
"integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
"dev": true "dev": true
}, },
"node-gyp": { "node-gyp": {
@ -13181,6 +13194,14 @@
"tough-cookie": "~2.5.0", "tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0", "tunnel-agent": "^0.6.0",
"uuid": "^3.3.2" "uuid": "^3.3.2"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
}
} }
}, },
"require-directory": { "require-directory": {
@ -13619,14 +13640,6 @@
"faye-websocket": "^0.11.3", "faye-websocket": "^0.11.3",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"websocket-driver": "^0.7.4" "websocket-driver": "^0.7.4"
},
"dependencies": {
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true
}
} }
}, },
"socks": { "socks": {
@ -14161,10 +14174,9 @@
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
}, },
"uuid": { "uuid": {
"version": "3.4.0", "version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
"dev": true
}, },
"validate-npm-package-license": { "validate-npm-package-license": {
"version": "3.0.4", "version": "3.0.4",

View File

@ -49,6 +49,7 @@
"@types/detect-port": "^1.3.2", "@types/detect-port": "^1.3.2",
"@types/react": "^17.0.38", "@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11", "@types/react-dom": "^17.0.11",
"@types/uuid": "^8.3.4",
"autoprefixer": "^10.4.2", "autoprefixer": "^10.4.2",
"css-loader": "^6.5.1", "css-loader": "^6.5.1",
"electron": "^18.0.4", "electron": "^18.0.4",
@ -72,6 +73,7 @@
"express": "^4.17.3", "express": "^4.17.3",
"gl-matrix": "^3.4.3", "gl-matrix": "^3.4.3",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2" "react-dom": "^17.0.2",
"uuid": "^8.3.2"
} }
} }

View File

@ -117,10 +117,26 @@ class BehaviorPopupComponent extends Component<
if (this.props.status && recorder instanceof BehaviorRecorder) { if (this.props.status && recorder instanceof BehaviorRecorder) {
let newBehavior = this.props.status.model.addBehavior(recorder); let newBehavior = this.props.status.model.addBehavior(recorder);
// 初始化名字 // 根据用户的命名搜索下一个名字
newBehavior.name = recorder.getTerms( let searchKey = recorder.getTerms(
recorder.behaviorName, this.props.setting?.language recorder.behaviorName, this.props.setting?.language
) + " " + (recorder.nameIndex - 1).toString(); );
let nextIndex = 1;
this.props.status.model.behaviorPool.forEach((obj) => {
if (obj.behaviorId === recorder.behaviorId && obj.name.indexOf(searchKey) >= 0) {
let searchRes = obj.name.match(/(\d+)$/);
if (searchRes) {
let nameNumber = parseInt(searchRes[1]);
if (!isNaN(nameNumber)) {
nextIndex = Math.max(nextIndex, nameNumber + 1);
}
}
}
});
// 初始化名字
newBehavior.name = `${searchKey} ${nextIndex}`;
// 赋予一个随机颜色 // 赋予一个随机颜色
newBehavior.color = randomColor(true); newBehavior.color = randomColor(true);

View File

@ -56,12 +56,6 @@ class Status extends Emitter<IStatusEvent> {
public setting: Setting = undefined as any; public setting: Setting = undefined as any;
/**
*
*/
public objectNameIndex = 1;
public labelNameIndex = 1;
/** /**
* *
*/ */
@ -300,34 +294,72 @@ class Status extends Emitter<IStatusEvent> {
*/ */
public mouseMod: MouseMod = MouseMod.Drag; public mouseMod: MouseMod = MouseMod.Drag;
private readonly SEARCH_NAME_KEY_REG = /(\d+)$/;
private getNextNumber(name: string, searchKey: string): number {
if (name.indexOf(searchKey) < 0) return 1;
let searchRes = name.match(this.SEARCH_NAME_KEY_REG);
if (searchRes) {
let nameNumber = parseInt(searchRes[1]);
if (isNaN(nameNumber)) {
return 1;
} else {
return nameNumber + 1;
}
} else {
return 1;
}
}
public newGroup() { public newGroup() {
const group = this.model.addGroup(); const group = this.model.addGroup();
group.color = randomColor(); group.color = randomColor();
group.displayName = I18N(this.setting.language, "Object.List.New.Group", { let searchKey = I18N(this.setting.language, "Object.List.New.Group", { id: "" });
id: this.objectNameIndex.toString() let nextIndex = 1;
this.model.objectPool.forEach((obj) => {
if (obj instanceof Group) {
nextIndex = Math.max(nextIndex, this.getNextNumber(
obj.displayName, searchKey
));
}
});
group.displayName = I18N(this.setting.language, "Object.List.New.Group", {
id: nextIndex.toString()
}); });
this.objectNameIndex ++;
return group; return group;
} }
public newRange() { public newRange() {
const range = this.model.addRange(); const range = this.model.addRange();
range.color = randomColor(); range.color = randomColor();
range.displayName = I18N(this.setting.language, "Object.List.New.Range", { let searchKey = I18N(this.setting.language, "Object.List.New.Range", { id: "" });
id: this.objectNameIndex.toString() let nextIndex = 1;
this.model.objectPool.forEach((obj) => {
if (obj instanceof Range) {
nextIndex = Math.max(nextIndex, this.getNextNumber(
obj.displayName, searchKey
));
}
});
range.displayName = I18N(this.setting.language, "Object.List.New.Range", {
id: nextIndex.toString()
}); });
this.objectNameIndex ++;
return range; return range;
} }
public newLabel() { public newLabel() {
let searchKey = I18N(this.setting.language, "Object.List.New.Label", { id: "" });
let nextIndex = 1;
this.model.labelPool.forEach((obj) => {
nextIndex = Math.max(nextIndex, this.getNextNumber(
obj.name, searchKey
));
});
const label = this.model.addLabel( const label = this.model.addLabel(
I18N(this.setting.language, "Object.List.New.Label", { I18N(this.setting.language, "Object.List.New.Label", {
id: this.labelNameIndex.toString() id: nextIndex.toString()
}) })
); );
label.color = randomColor(true); label.color = randomColor(true);
this.labelNameIndex ++;
return label; return label;
} }

View File

@ -36,9 +36,6 @@ class Archive<
*/ */
public save(model: Model): string { public save(model: Model): string {
let fileData: Record<string, any> = {}; let fileData: Record<string, any> = {};
// 保存 Next ID
fileData.idIndex = model.idIndex;
// 保存对象 // 保存对象
fileData.objects = []; fileData.objects = [];

View File

@ -1,4 +1,5 @@
import { Emitter, EventType } from "@Model/Emitter"; import { Emitter, EventType } from "@Model/Emitter";
import { v4 as uuid } from "uuid";
import type { Individual } from "@Model/Individual"; import type { Individual } from "@Model/Individual";
import type { Group } from "@Model/Group"; import type { Group } from "@Model/Group";
import type { Model } from "@Model/Model"; import type { Model } from "@Model/Model";
@ -10,7 +11,7 @@ import { getDefaultValue, IParameter, IParameterOption, IParameterValue } from "
type IBehaviorConstructor< type IBehaviorConstructor<
P extends IParameter = {}, P extends IParameter = {},
E extends Record<EventType, any> = {} E extends Record<EventType, any> = {}
> = new (id: string, parameter: IParameterValue<P>) => Behavior<P, E>; > = new (parameter: IParameterValue<P>) => Behavior<P, E>;
type IAnyBehavior = Behavior<any, any>; type IAnyBehavior = Behavior<any, any>;
type IAnyBehaviorRecorder = BehaviorRecorder<any, any>; type IAnyBehaviorRecorder = BehaviorRecorder<any, any>;
@ -76,18 +77,6 @@ class BehaviorRecorder<
E extends Record<EventType, any> = {} E extends Record<EventType, any> = {}
> extends BehaviorInfo<{}> { > extends BehaviorInfo<{}> {
/**
*
*/
public nameIndex: number = 0;
/**
* ID
*/
public getNextId() {
return `B-${this.behaviorId}-${this.nameIndex ++}`;
}
/** /**
* *
*/ */
@ -107,13 +96,13 @@ class BehaviorRecorder<
* *
*/ */
public new(): Behavior<P, E> { public new(): Behavior<P, E> {
return new this.behavior(this.getNextId(), getDefaultValue(this.parameterOption)); return new this.behavior(getDefaultValue(this.parameterOption));
} }
public constructor(behavior: IBehaviorConstructor<P, E>) { public constructor(behavior: IBehaviorConstructor<P, E>) {
super(); super();
this.behavior = behavior; this.behavior = behavior;
this.behaviorInstance = new this.behavior(this.getNextId(), {} as any); this.behaviorInstance = new this.behavior({} as any);
this.parameterOption = this.behaviorInstance.parameterOption; this.parameterOption = this.behaviorInstance.parameterOption;
this.iconName = this.behaviorInstance.iconName; this.iconName = this.behaviorInstance.iconName;
this.behaviorId = this.behaviorInstance.behaviorId; this.behaviorId = this.behaviorInstance.behaviorId;
@ -168,9 +157,9 @@ class Behavior<
*/ */
public parameterOption: IParameterOption<P> = {} as any; public parameterOption: IParameterOption<P> = {} as any;
public constructor(id: string, parameter: IParameterValue<P>) { public constructor(parameter: IParameterValue<P>) {
super(); super();
this.id = id; this.id = uuid();
this.parameter = parameter; this.parameter = parameter;
} }

View File

@ -1,11 +1,23 @@
import { LabelObject } from "@Model/Label" 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 { IAnyObject, Model } from "@Model/Model";
import type { ObjectID } from "@Model/Model"; import type { ObjectID } from "@Model/Model";
interface IArchiveCtrlObject {
displayName: CtrlObject["displayName"];
color: CtrlObject["color"];
display: CtrlObject["display"];
update: CtrlObject["update"];
id: string;
renderParameter: any;
deleteFlag: CtrlObject["deleteFlag"];
}
/** /**
* *
*/ */
class CtrlObject extends LabelObject { class CtrlObject<A extends IAnyObject = IAnyObject> extends LabelObject {
/** /**
* *
@ -45,10 +57,10 @@ class CtrlObject extends LabelObject {
/** /**
* *
*/ */
public constructor(model: Model, id: ObjectID) { public constructor(model: Model) {
super(); super();
this.model = model; this.model = model;
this.id = id; this.id = uuid();
} }
/** /**
@ -96,7 +108,30 @@ class CtrlObject extends LabelObject {
public isDeleted(): boolean { public isDeleted(): boolean {
return this.deleteFlag; return this.deleteFlag;
} }
public toArchive(): IArchiveCtrlObject & A {
return {
displayName: this.displayName,
color: this.color.concat([]),
display: !!this.display,
update: !!this.update,
id: this.id,
renderParameter: parameter2ArchiveObject(this.renderParameter),
deleteFlag: !!this.deleteFlag
} as any;
}
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;
}
} }
export default CtrlObject; export { CtrlObject, IArchiveCtrlObject };
export { CtrlObject };

View File

@ -407,9 +407,9 @@ class Group extends CtrlObject {
return dataBuffer; return dataBuffer;
} }
public constructor(model: Model, id: ObjectID) { public constructor(model: Model) {
super(model, id); super(model);
if (model.renderer) { if (model.renderer) {
this.renderParameter = getDefaultValue(model.renderer.pointsParameterOption); this.renderParameter = getDefaultValue(model.renderer.pointsParameterOption);

View File

@ -1,4 +1,5 @@
import type { Model, ObjectID } from "@Model/Model"; import type { Model, ObjectID } from "@Model/Model";
import { v4 as uuid } from "uuid";
/** /**
* *
@ -35,9 +36,9 @@ class Label {
* @param id ID * @param id ID
* @param name * @param name
*/ */
public constructor(model: Model, id: ObjectID, name?: string) { public constructor(model: Model, name?: string) {
this.model = model; this.model = model;
this.id = id; this.id = uuid();
this.name = name ?? this.name; this.name = name ?? this.name;
} }

View File

@ -2,9 +2,8 @@ import { Label } from "@Model/Label";
import { Group } from "@Model/Group"; import { Group } from "@Model/Group";
import { Range } from "@Model/Range"; import { Range } from "@Model/Range";
import { IParamValue } from "@Model/Parameter"; import { IParamValue } from "@Model/Parameter";
import { Individual } from "@Model/Individual";
import { CtrlObject } from "@Model/CtrlObject"; import { CtrlObject } from "@Model/CtrlObject";
import { Emitter, EventType, EventMixin } from "@Model/Emitter"; import { Emitter } from "@Model/Emitter";
import { AbstractRenderer } from "@Model/Renderer"; import { AbstractRenderer } from "@Model/Renderer";
import { Behavior, IAnyBehavior, IAnyBehaviorRecorder } from "@Model/Behavior"; import { Behavior, IAnyBehavior, IAnyBehaviorRecorder } from "@Model/Behavior";
@ -30,14 +29,6 @@ type ModelEvent = {
*/ */
class Model extends Emitter<ModelEvent> { class Model extends Emitter<ModelEvent> {
/**
* ID
*/
public idIndex: number = 1;
public nextId(label: string = "U"): string {
return `${label}-${this.idIndex ++}`;
}
/** /**
* *
*/ */
@ -75,10 +66,10 @@ class Model extends Emitter<ModelEvent> {
* *
*/ */
public addLabel(name: string): Label { public addLabel(name: string): Label {
console.log(`Model: Creat label with id ${this.idIndex}`); let label = new Label(this, name);
let label = new Label(this, this.nextId("L"), name);
this.labelPool.push(label); this.labelPool.push(label);
this.emit("labelChange", this.labelPool); this.emit("labelChange", this.labelPool);
console.log(`Model: Creat label with id ${label.id}`);
return label; return label;
} }
@ -140,10 +131,10 @@ class Model extends Emitter<ModelEvent> {
* *
*/ */
public addGroup(): Group { public addGroup(): Group {
console.log(`Model: Creat group with id ${this.idIndex}`); let group = new Group(this);
let group = new Group(this, this.nextId("G"));
this.objectPool.push(group); this.objectPool.push(group);
this.emit("objectChange", this.objectPool); this.emit("objectChange", this.objectPool);
console.log(`Model: Creat group with id ${group.id}`);
return group; return group;
} }
@ -151,10 +142,10 @@ class Model extends Emitter<ModelEvent> {
* *
*/ */
public addRange(): Range { public addRange(): Range {
console.log(`Model: Creat range with id ${this.idIndex}`); let range = new Range(this);
let range = new Range(this, this.nextId("R"));
this.objectPool.push(range); this.objectPool.push(range);
this.emit("objectChange", this.objectPool); this.emit("objectChange", this.objectPool);
console.log(`Model: Creat range with id ${range.id}`);
return range; return range;
} }
@ -393,14 +384,4 @@ class Model extends Emitter<ModelEvent> {
} }
} }
export { export { Model, ObjectID, IAnyObject }
Individual,
Group,
Emitter,
EventType,
EventMixin,
Model,
CtrlObject,
ObjectID,
IAnyObject
}

View File

@ -1,6 +1,12 @@
import type { Group } from "@Model/Group"; import { Group } from "@Model/Group";
import type { Range } from "@Model/Range"; import { Range } from "@Model/Range";
import type { Label } from "@Model/Label"; import { Label } from "@Model/Label";
import { Behavior } from "@Model/Behavior";
type IObjectParamArchiveType = {
__LIVING_TOGETHER_OBJECT_ID: string;
__LIVING_TOGETHER_OBJECT_TYPE: string;
}
type IObjectParamCacheType<P, Q = P> = { type IObjectParamCacheType<P, Q = P> = {
picker: P; picker: P;
@ -26,6 +32,15 @@ type IMapObjectParamTypeKeyToType = {
"CLG": IObjectParamCacheType<Label | Group | undefined, Group[]>; "CLG": IObjectParamCacheType<Label | Group | undefined, Group[]>;
} }
type IMapArchiveObjectParamTypeKeyToType = {
"R": IObjectParamCacheType<IObjectParamArchiveType | undefined>;
"G": IObjectParamCacheType<IObjectParamArchiveType | undefined>;
"LR": IObjectParamCacheType<IObjectParamArchiveType | undefined, IObjectParamArchiveType[]>;
"LG": IObjectParamCacheType<IObjectParamArchiveType | undefined, IObjectParamArchiveType[]>;
"CG": IObjectParamCacheType<IObjectParamArchiveType | undefined>;
"CLG": IObjectParamCacheType<IObjectParamArchiveType | undefined, IObjectParamArchiveType[]>;
}
type IMapVectorParamTypeKeyToType = { type IMapVectorParamTypeKeyToType = {
"vec": number[]; "vec": number[];
"color": number[]; "color": number[];
@ -35,30 +50,42 @@ type IMapVectorParamTypeKeyToType = {
* *
*/ */
type AllMapType = IMapBasicParamTypeKeyToType & IMapObjectParamTypeKeyToType & IMapVectorParamTypeKeyToType; type AllMapType = IMapBasicParamTypeKeyToType & IMapObjectParamTypeKeyToType & IMapVectorParamTypeKeyToType;
type AllArchiveMapType = IMapBasicParamTypeKeyToType & IMapArchiveObjectParamTypeKeyToType & IMapVectorParamTypeKeyToType;
type IParamType = keyof AllMapType; type IParamType = keyof AllMapType;
type IObjectType = keyof IMapObjectParamTypeKeyToType; type IObjectType = keyof IMapObjectParamTypeKeyToType;
type IVectorType = keyof IMapVectorParamTypeKeyToType; type IVectorType = keyof IMapVectorParamTypeKeyToType;
type IParamValue<K extends IParamType> = AllMapType[K]; type IParamValue<K extends IParamType> = AllMapType[K];
type IArchiveParamValue<K extends IParamType> = AllArchiveMapType[K];
/** /**
* *
*/ */
const objectTypeListEnumSet = new Set<string>(["R", "G", "LR", "LG", "CG", "CLG"]); const objectTypeListEnumSet = new Set<string>(["R", "G", "LR", "LG", "CG", "CLG"]);
/** /**
* *
*/ */
function isObjectType(key: string): key is IVectorType { function isObjectType(key: string): key is IVectorType {
return objectTypeListEnumSet.has(key); return objectTypeListEnumSet.has(key);
} }
/** /**
* *
*/ */
function isVectorType(key: string): key is IObjectType { function isVectorType(key: string): key is IObjectType {
return key === "vec"; return key === "vec";
} }
/**
*
*/
function isArchiveObjectType(key: Object): key is IObjectParamArchiveType {
return !!(
(key as IObjectParamArchiveType).__LIVING_TOGETHER_OBJECT_ID &&
(key as IObjectParamArchiveType).__LIVING_TOGETHER_OBJECT_TYPE
);
}
/** /**
* *
*/ */
@ -144,6 +171,10 @@ type IParameterValue<P extends IParameter> = {
[X in keyof P]: IParamValue<P[X]> [X in keyof P]: IParamValue<P[X]>
} }
type IArchiveParameterValue<P extends IParameter> = {
[X in keyof P]: IArchiveParamValue<P[X]>
}
function getDefaultValue<P extends IParameter> (option: IParameterOption<P>): IParameterValue<P> { function getDefaultValue<P extends IParameter> (option: IParameterOption<P>): IParameterValue<P> {
let defaultObj = {} as IParameterValue<P>; let defaultObj = {} as IParameterValue<P>;
for (let key in option) { for (let key in option) {
@ -193,7 +224,132 @@ function getDefaultValue<P extends IParameter> (option: IParameterOption<P>): IP
return defaultObj; return defaultObj;
} }
type IRealObjectType = Range | Group | Label | Behavior;
type IArchiveParseFn = (archive: IObjectParamArchiveType) => IRealObjectType | undefined;
function object2ArchiveObject(object: IRealObjectType | IRealObjectType[] | any, testArray: boolean = true):
IObjectParamArchiveType | IObjectParamArchiveType[] | undefined {
if (object instanceof Range) {
return {
__LIVING_TOGETHER_OBJECT_ID: "Range",
__LIVING_TOGETHER_OBJECT_TYPE: object.id
}
} else if (object instanceof Group) {
return {
__LIVING_TOGETHER_OBJECT_ID: "Group",
__LIVING_TOGETHER_OBJECT_TYPE: object.id
}
} else if (object instanceof Label) {
return {
__LIVING_TOGETHER_OBJECT_ID: "Label",
__LIVING_TOGETHER_OBJECT_TYPE: object.id
}
} else if (object instanceof Behavior) {
return {
__LIVING_TOGETHER_OBJECT_ID: "Behavior",
__LIVING_TOGETHER_OBJECT_TYPE: object.id
}
} else if (Array.isArray(object) && testArray) {
const hasValue = (item: any): item is IObjectParamArchiveType => !!item;
return object.map(item => object2ArchiveObject(item, false)).filter(hasValue);
} else {
return undefined;
}
}
function parameter2ArchiveObject<P extends IParameter>
(value: IParameterValue<P>, option?: IParameterOption<P>): IArchiveParameterValue<P> {
let archive = {} as IArchiveParameterValue<P>;
const handelColl = (key: string, cValue: IParamValue<IParamType>) => {
// 处理对象类型
if (cValue instanceof Object && !Array.isArray(cValue)) {
const picker = (cValue as IObjectParamCacheType<any>).picker;
const objects = (cValue as IObjectParamCacheType<any>).objects;
(archive[key] as any) = {
picker: object2ArchiveObject(picker),
objects: object2ArchiveObject(objects)
}
}
// 处理数组类型
else if (Array.isArray(cValue)) {
(archive[key] as any) = cValue.concat([]);
}
// 处理数值
else {
(archive[key] as any) = cValue;
}
}
// 存在参考设置对象
if (option) {
for (const key in option) {
handelColl(key, value[key]);
}
}
// 不存在设置对象
else {
for (const key in value) {
handelColl(key, value[key]);
}
}
return archive;
}
function archiveObject2Parameter<P extends IParameter>
(value: IArchiveParameterValue<P>, parse: IArchiveParseFn, option?: IParameterOption<P>): IParameterValue<P> {
let parameter = {} as IParameterValue<P>;
const handelColl = (key: string, cValue: IArchiveParamValue<IParamType>) => {
// 处理对象类型
if (cValue instanceof Object && !Array.isArray(cValue)) {
const picker = (cValue as IObjectParamCacheType<IObjectParamArchiveType>).picker;
const objects = (cValue as IObjectParamCacheType<IObjectParamArchiveType>).objects;
(parameter[key] as any) = {
picker: picker ? parse(picker) : picker,
objects: objects ? parse(objects) : objects
}
}
// 处理数组类型
else if (Array.isArray(cValue)) {
(parameter[key] as any) = cValue.concat([]);
}
// 处理数值
else {
(parameter[key] as any) = cValue;
}
}
// 存在参考设置对象
if (option) {
for (const key in option) {
handelColl(key, value[key]);
}
}
// 不存在设置对象
else {
for (const key in value) {
handelColl(key, value[key]);
}
}
return parameter;
}
export { export {
IParamType, IParamValue, isObjectType, isVectorType, getDefaultValue, IParamType, IParamValue, isObjectType, isVectorType, getDefaultValue,
IParameterOptionItem, IParameter, IParameterOption, IParameterValue IParameterOptionItem, IParameter, IParameterOption, IParameterValue,
object2ArchiveObject, parameter2ArchiveObject, archiveObject2Parameter,
IArchiveParseFn
} }

View File

@ -1,11 +1,16 @@
import { CtrlObject } from "@Model/CtrlObject"; import { CtrlObject, IArchiveCtrlObject } from "@Model/CtrlObject";
import { Model, ObjectID } from "@Model/Model"; import { Model } from "@Model/Model";
import { getDefaultValue } from "@Model/Parameter"; import { getDefaultValue, IArchiveParseFn, parameter2ArchiveObject } from "@Model/Parameter";
interface IArchiveRange {
position: Range["position"];
radius: Range["radius"];
}
/** /**
* *
*/ */
class Range extends CtrlObject { class Range extends CtrlObject<IArchiveRange> {
/** /**
* *
@ -17,16 +22,29 @@ class Range extends CtrlObject {
*/ */
public radius: number[] = [1, 1, 1]; public radius: number[] = [1, 1, 1];
public constructor(model: Model, id: ObjectID) { public constructor(model: Model) {
super(model, id); super(model);
if (model.renderer) { if (model.renderer) {
this.renderParameter = getDefaultValue(model.renderer.cubeParameterOption); this.renderParameter = getDefaultValue(model.renderer.cubeParameterOption);
} }
} }
public override toArchive(): IArchiveCtrlObject & IArchiveRange {
return {
...super.toArchive(),
position: this.position.concat([]),
radius: this.radius.concat([])
};
}
public override fromArchive(archive: IArchiveCtrlObject & IArchiveRange, paster?: IArchiveParseFn): void {
super.fromArchive(archive, paster);
this.position = archive.position.concat([]),
this.radius = archive.radius.concat([])
}
} }
export default Range; export { Range, IArchiveRange };
export { Range };

View File

@ -153,7 +153,10 @@ class SimulatorWeb extends Component {
}, 200) }, 200)
} }
(window as any).s = this; (window as any).LT = {
status: this.status,
setting: this.setting
};
} }
public componentDidMount() { public componentDidMount() {

View File

@ -4,6 +4,8 @@ import { useSetting, IMixinSettingProps } from "@Context/Setting";
import { Localization } from "@Component/Localization/Localization"; import { Localization } from "@Component/Localization/Localization";
import { DetailsList } from "@Component/DetailsList/DetailsList"; import { DetailsList } from "@Component/DetailsList/DetailsList";
import { ObjectID } from "@Model/Model"; import { ObjectID } from "@Model/Model";
import { Group } from "@Model/Group";
import { Range } from "@Model/Range";
import { Icon } from "@fluentui/react"; import { Icon } from "@fluentui/react";
import "./ObjectList.scss"; import "./ObjectList.scss";
@ -24,12 +26,21 @@ class ObjectList extends Component<IMixinStatusProps & IMixinSettingProps> {
return <DetailsList return <DetailsList
className="object-list" className="object-list"
items={objList.concat([]).map((object => { items={objList.concat([]).map((object => {
let objectType = "";
if (object instanceof Group) {
objectType = "G";
} else if (object instanceof Range) {
objectType = "R";
}
return { return {
key: object.id.toString(), key: object.id.toString(),
name: object.displayName, name: object.displayName,
color: object.color, color: object.color,
display: object.display, display: object.display,
update: object.update, update: object.update,
type: objectType,
select: this.props.status ? select: this.props.status ?
this.props.status.focusObject.has(object.id.toString()) || this.props.status.focusObject.has(object.id.toString()) ||
this.props.status.focusObject.has(object.id) : this.props.status.focusObject.has(object.id) :
@ -41,10 +52,10 @@ class ObjectList extends Component<IMixinStatusProps & IMixinSettingProps> {
this.props.status.setFocusObject(new Set<ObjectID>().add(item.key)); this.props.status.setFocusObject(new Set<ObjectID>().add(item.key));
} }
if (this.props.setting) { if (this.props.setting) {
if (item.key.slice(0, 1) === "R") { if (item.type === "R") {
this.props.setting.layout.focus("RangeDetails"); this.props.setting.layout.focus("RangeDetails");
} }
if (item.key.slice(0, 1) === "G") { else if (item.type === "G") {
this.props.setting.layout.focus("GroupDetails"); this.props.setting.layout.focus("GroupDetails");
} }
this.props.setting.layout.focus("ObjectList"); this.props.setting.layout.focus("ObjectList");
@ -71,13 +82,13 @@ class ObjectList extends Component<IMixinStatusProps & IMixinSettingProps> {
}} }}
renderCheckbox={(item, click) => { renderCheckbox={(item, click) => {
let icon = "CheckMark"; let icon = "CheckMark";
if (item.key.slice(0, 1) === "R") { if (item.type === "R") {
icon = "CubeShape"; icon = "CubeShape";
} }
if (item.key.slice(0, 1) === "G") { else if (item.type === "G") {
icon = "WebAppBuilderFragment"; icon = "WebAppBuilderFragment";
} }
return <div return <div
className="object-list-checkbox details-list-checkbox" className="object-list-checkbox details-list-checkbox"
onClick={click} onClick={click}
> >