diff --git a/src/DatabaseServices/AutoRecord.ts b/src/DatabaseServices/AutoRecord.ts new file mode 100644 index 000000000..7d1f0e574 --- /dev/null +++ b/src/DatabaseServices/AutoRecord.ts @@ -0,0 +1,65 @@ + + +export const ISPROXYKEY = "_isProxy"; + +/** + * 自动对CADObject的属性添加属性记录器,自动调用 `WriteAllObjectRecord` + * 如果属性是数组,那么自动添加`Proxy`. + * 可以使用`ISPROXYKEY`覆盖这个函数的代理行为. + * + * @param target + * @param property + * @param [descriptor] + */ +export function AutoRecord( + target: { WriteAllObjectRecord: () => void }, + property: string, + descriptor?: PropertyDecorator) +{ + let privateKey = '__' + property; + Object.defineProperty(target, property, + { + set: function (value) + { + if (value instanceof Array) + { + if (!this[privateKey]) + { + if (value[ISPROXYKEY]) + this[privateKey] = value; + else + this[privateKey] = new Proxy(value, { + set: (target, key, value, receiver) => + { + if (Reflect.get(target, key, receiver) !== value) + this.WriteAllObjectRecord(); + return Reflect.set(target, key, value, receiver); + } + }); + } + else + { + let arr = this[privateKey] as Array; + arr.length = 0; + arr.push(...value); + } + } + else + { + let oldv = this[privateKey]; + if (oldv !== value) + { + this.WriteAllObjectRecord(); + this[privateKey] = value; + } + } + }, + get: function () + { + return this[privateKey]; + }, + enumerable: true, + configurable: true + } + ); +} diff --git a/src/DatabaseServices/CADFiler.ts b/src/DatabaseServices/CADFiler.ts index 0ce13fe65..87e55353f 100644 --- a/src/DatabaseServices/CADFiler.ts +++ b/src/DatabaseServices/CADFiler.ts @@ -62,7 +62,7 @@ export class CADFiler obj.WriteFile(this); } - ReadObject(obj?: CADObject): CADObject + ReadObject(obj?: T): T { let className = this.ReadString(); if (className) diff --git a/src/DatabaseServices/CADObject.ts b/src/DatabaseServices/CADObject.ts index 371749184..693832b5d 100644 --- a/src/DatabaseServices/CADObject.ts +++ b/src/DatabaseServices/CADObject.ts @@ -151,7 +151,7 @@ export abstract class CADObject return this._db.hm.UndoData; } //写入所有的对象数据 以便还原对象 - protected WriteAllObjectRecord() + WriteAllObjectRecord() { let undoData = this.UndoRecord(); if (undoData) diff --git a/src/DatabaseServices/Database.ts b/src/DatabaseServices/Database.ts index a6db6b723..f9a3db978 100644 --- a/src/DatabaseServices/Database.ts +++ b/src/DatabaseServices/Database.ts @@ -15,6 +15,7 @@ import { ObjectId } from './ObjectId'; import { PhysicalMaterialRecord } from './PhysicalMaterialRecord'; import { SymbolTable } from './SymbolTable'; import { SymbolTableRecord } from './SymbolTableRecord'; +import { TemplateTable } from './TemplateTable'; import { TextureTableRecord } from './Texture'; import { TextureTable } from './TextureTable'; import { WblockCloneFiler } from './WblockCloneFiler'; @@ -29,6 +30,7 @@ export class Database BlockTable: BlockTable; MaterialTable: MaterialTable; TextureTable: TextureTable; + TemplateTable: TemplateTable; ModelSpace: BlockTableRecord; DefaultMaterial: PhysicalMaterialRecord; @@ -40,6 +42,7 @@ export class Database this.ModelSpace = new BlockTableRecord().SetOwnerDatabase(this); this.MaterialTable = new MaterialTable().SetOwnerDatabase(this); this.TextureTable = new TextureTable().SetOwnerDatabase(this); + this.TemplateTable = new TemplateTable().SetOwnerDatabase(this); this.hm = new HistoricManage().SetDefaultDb(this); @@ -85,6 +88,7 @@ export class Database this.ModelSpace.Destroy(); this.MaterialTable.Destroy(); this.TextureTable.Destroy(); + this.TemplateTable.Destroy(); this.hm.Destroy(); this.hm.historyRecord.length = 0; @@ -92,6 +96,7 @@ export class Database this.ModelSpace.SetOwnerDatabase(this); this.MaterialTable.SetOwnerDatabase(this); this.TextureTable.SetOwnerDatabase(this); + this.TemplateTable.SetOwnerDatabase(this); this.hm.SetOwnerDatabase(this); this.idIndex = 100; } @@ -101,12 +106,13 @@ export class Database FileWrite(): CADFiler { let file = new CADFiler(); - file.Write(1);//ver; + file.Write(2);//ver; file.Write(this.idIndex); this.ModelSpace.WriteFile(file); this.TextureTable.WriteFile(file); this.MaterialTable.WriteFile(file); this.hm.WriteFile(file); + this.TemplateTable.WriteFile(file); return file; } @@ -120,6 +126,8 @@ export class Database this.TextureTable.ReadFile(file); this.MaterialTable.ReadFile(file); this.hm.ReadFile(file); + if (ver > 1) + this.TemplateTable.ReadFile(file); this.SettingDefaultMaterial(); diff --git a/src/DatabaseServices/TemplateRecord.ts b/src/DatabaseServices/TemplateRecord.ts index 81a8c3591..f2a584b34 100644 --- a/src/DatabaseServices/TemplateRecord.ts +++ b/src/DatabaseServices/TemplateRecord.ts @@ -1,13 +1,17 @@ import { Box3, Matrix4, Vector3 } from "three"; import { app } from "../ApplicationServices/Application"; +import { arrayClone } from "../Common/ArrayExt"; import { StretchParse } from "../Common/StretchParse"; import { FixIndex } from "../Common/Utils"; import { commandMachine } from "../Editor/CommandMachine"; import { PromptSsgetResult, PromptStatus } from "../Editor/PromptResult"; import { SelectBox, SelectType } from "../Editor/SelectBox"; -import { ZeroVec, equaln, equalv3, MoveMatrix, AsVector3 } from "../Geometry/GeUtils"; +import { AsVector3, equaln, equalv3, MoveMatrix, ZeroVec } from "../Geometry/GeUtils"; import { HotCMD } from "../Hot/HotCommand"; +import { AutoRecord, ISPROXYKEY } from "./AutoRecord"; import { Board } from "./Board"; +import { Factory } from "./CADFactory"; +import { CADFiler } from "./CADFiler"; import { Entity } from "./Entity"; import { ObjectId } from "./ObjectId"; import { SymbolTableRecord } from "./SymbolTableRecord"; @@ -41,22 +45,64 @@ export enum TemplateType /** * 模版参数 */ +@Factory export class TemplateParam { - name: string; - expr: string; - value: string | number; - default: string | number; + @AutoRecord name: string; + @AutoRecord expr: string; - type: TemplateParamType; - min: number; - max: number; + /** + * 如果需要更新参数的动作,那么应该调用 UpdateParam + */ + @AutoRecord value: string | number; + @AutoRecord default: string | number; + + @AutoRecord description: string; + + @AutoRecord type: TemplateParamType; + @AutoRecord min: number; + @AutoRecord max: number; //可选值 - option: any[]; + @AutoRecord option: any[]; + @AutoRecord actions: TemplateAction[]; + @AutoRecord parent: TemplateRecord; - actions: TemplateAction[] = []; + constructor() + { + //监听 + this.actions = new Proxy([], { + set: (target, key, value, receiver) => + { + if (Reflect.get(target, key, receiver) !== value) + { + this.WriteAllObjectRecord(); + if (value instanceof TemplateAction) + value.parent = this; + } + return Reflect.set(target, key, value, receiver); + }, + get: (target, key, receiver) => + { + if (key === ISPROXYKEY) + return true; + else + return Reflect.get(target, key, receiver); + } + }); + } + + WriteAllObjectRecord() + { + if (this.parent) + this.parent.WriteAllObjectRecord(); + } + + /** + * 更新参数值,并且触发动作. + * 如果你想要只更新参数的值,那么你应该直接`this.value = xxx;` + */ UpdateParam(value: string | number) { switch (this.type) @@ -80,43 +126,143 @@ export class TemplateParam break; } } -} + //#region -------------------------File------------------------- + //对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化 + + //对象从文件中读取数据,初始化自身 + ReadFile(file: CADFiler) + { + let ver = file.Read(); + + this.name = file.Read(); + this.expr = file.Read(); + this.value = file.Read(); + this.default = file.Read(); + this.description = file.Read(); + this.type = file.Read(); + this.min = file.Read(); + this.max = file.Read(); + this.option = file.Read(); + + let count = file.Read(); + this.actions.length = 0; + for (let i = 0; i < count; i++) + this.actions.push(file.ReadObject()); + } + //对象将自身数据写入到文件. + WriteFile(file: CADFiler) + { + file.Write(1); + file.Write(this.name); + file.Write(this.expr); + file.Write(this.value); + file.Write(this.default); + file.Write(this.description); + file.Write(this.type); + file.Write(this.min); + file.Write(this.max); + file.Write(this.option); + + file.Write(this.actions.length); + for (let action of this.actions) + { + file.WriteObject(action); + } + } + //#endregion +} /** * 模版动作 */ +@Factory class TemplateAction { + parent: TemplateParam; + WriteAllObjectRecord() + { + if (this.parent) + this.parent.WriteAllObjectRecord(); + } Update(paramDiff: number | string) { } + + //#region -------------------------File------------------------- + //对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化 + + //对象从文件中读取数据,初始化自身 + ReadFile(file: CADFiler) + { + let ver = file.Read(); + } + //对象将自身数据写入到文件. + WriteFile(file: CADFiler) + { + file.Write(1); + } + //#endregion } -class TemplateMoveAction +class TemplateMoveAction extends TemplateAction { - StretchDirection: Vector3; - Entitys: Entity[]; + StretchDirection: Vector3 = new Vector3(); + Entitys: ObjectId[] = []; Update(paramDiff: number) { let moveMatrix = MoveMatrix(this.StretchDirection.clone().multiplyScalar(paramDiff)); - for (let ent of this.Entitys) + for (let id of this.Entitys) + { + let ent = id.Object as Entity; ent.ApplyMatrix(moveMatrix); + } + } + + //#region -------------------------File------------------------- + //对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化 + + //对象从文件中读取数据,初始化自身 + ReadFile(file: CADFiler) + { + let ver = file.Read(); + this.StretchDirection.fromArray(file.Read()); + this.Entitys.length = 0; + let count = file.Read(); + for (let i = 0; i < count; i++) + { + this.Entitys.push(file.ReadObjectId()); + } + } + //对象将自身数据写入到文件. + WriteFile(file: CADFiler) + { + file.Write(1); + file.Write(this.StretchDirection.toArray()); + file.Write(this.Entitys.length); + for (let ent of this.Entitys) + file.WriteObjectId(ent); } + //#endregion } /** * Stretch夹点动作 */ +@Factory class TemplateStretchGripAction extends TemplateAction { - StretchDirection: Vector3; + /** + * 正常不会直接修改下面的2个属性,如果真的需要修改,请调用 `this.WriteAllObjectRecord`; + * 避免历史记录没有记录更新. + */ + StretchDirection: Vector3 = new Vector3(); - EntityStretchPointMap: { entity: Entity, indexs: number[] }[] = []; + EntityStretchPointMap: { entity: ObjectId, indexs: number[] }[] = []; Update(dist: number) { @@ -124,9 +270,45 @@ class TemplateStretchGripAction extends TemplateAction for (let { entity, indexs } of this.EntityStretchPointMap) { - entity.MoveStretchPoints(indexs, v); + let ent = entity.Object as Entity; + ent.MoveStretchPoints(indexs, v); + } + } + + //#region -------------------------File------------------------- + //对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化 + + //对象从文件中读取数据,初始化自身 + ReadFile(file: CADFiler) + { + let ver = file.Read(); + super.ReadFile(file); + + this.StretchDirection.fromArray(file.Read()); + + this.EntityStretchPointMap.length = 0; + let count = file.Read() as number; + for (let i = 0; i < count; i++) + { + let entity = file.ReadObjectId(); + let indexs = file.Read(); + this.EntityStretchPointMap.push({ entity, indexs }); + } + } + //对象将自身数据写入到文件. + WriteFile(file: CADFiler) + { + file.Write(1); + super.WriteFile(file); + file.Write(this.StretchDirection.toArray()); + file.Write(this.EntityStretchPointMap.length); + for (let d of this.EntityStretchPointMap) + { + file.WriteObjectId(d.entity); + file.Write(arrayClone(d.indexs)); } } + //#endregion } @@ -135,9 +317,13 @@ class TemplateStretchGripAction extends TemplateAction */ class TemplateStretchScaleBoxAction extends TemplateAction { - StretchDirection: Vector3; + /** + * 正常不会直接修改下面的2个属性,如果真的需要修改,请调用 `this.WriteAllObjectRecord`; + * 避免历史记录没有记录更新. + */ + StretchDirection: Vector3 = new Vector3(); - EntityStretchData: { entity: Entity, scaleBox: Box3 }[] = []; + EntityStretchData: { entity: ObjectId, scaleBox: Box3 }[] = []; Update(dist: number) { @@ -145,9 +331,10 @@ class TemplateStretchScaleBoxAction extends TemplateAction for (let { entity, scaleBox } of this.EntityStretchData) { - let pts = entity.GetStretchPoints(); - let ocsInv = entity.OCSInv; - let entityBox = entity.BoundingBoxInOCS; + let ent = entity.Object as Entity; + let pts = ent.GetStretchPoints(); + let ocsInv = ent.OCSInv; + let entityBox = ent.BoundingBoxInOCS; let size = entityBox.getSize(new Vector3()); scaleBox = scaleBox.clone(); @@ -165,9 +352,46 @@ class TemplateStretchScaleBoxAction extends TemplateAction stretchIndexs.push(i); } - entity.MoveStretchPoints(stretchIndexs, v); + ent.MoveStretchPoints(stretchIndexs, v); + } + } + + //#region -------------------------File------------------------- + //对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化 + + //对象从文件中读取数据,初始化自身 + ReadFile(file: CADFiler) + { + let ver = file.Read(); + super.ReadFile(file); + + this.StretchDirection.fromArray(file.Read()); + + this.EntityStretchData.length = 0; + let count = file.Read() as number; + for (let i = 0; i < count; i++) + { + let entity = file.ReadObjectId(); + let min = new Vector3().fromArray(file.Read()); + let max = new Vector3().fromArray(file.Read()); + this.EntityStretchData.push({ entity, scaleBox: new Box3(min, max) }); } } + //对象将自身数据写入到文件. + WriteFile(file: CADFiler) + { + file.Write(1); + super.WriteFile(file); + file.Write(this.StretchDirection.toArray()); + file.Write(this.EntityStretchData.length); + for (let d of this.EntityStretchData) + { + file.WriteObjectId(d.entity); + file.Write(d.scaleBox.min.toArray()); + file.Write(d.scaleBox.max.toArray()); + } + } + //#endregion } @@ -183,15 +407,55 @@ class TemplateStretchSizeBoxAction extends TemplateAction } */ +@Factory export class TemplateRecord extends SymbolTableRecord { - type: TemplateType; - parent: ObjectId; - children: ObjectId[]; - params: TemplateParam[] = []; + @AutoRecord type: TemplateType; + @AutoRecord parent: ObjectId; + @AutoRecord children: ObjectId[] = []; + @AutoRecord params: TemplateParam[]; + @AutoRecord objects: ObjectId[] = []; + + constructor() + { + super(); + + //监听 + this.params = new Proxy([], { + set: (target, key, value, receiver) => + { + if (Reflect.get(target, key, receiver) !== value) + { + this.WriteAllObjectRecord(); + + if (value instanceof TemplateParam) + value.parent = this; + } + return Reflect.set(target, key, value, receiver); + }, + get: (target, key, receiver) => + { + if (key === ISPROXYKEY) + return true; + return Reflect.get(target, key, receiver); + } + }); + } + + InitSizeParams() + { + for (let paramName of ["L", "W", "H"]) + { + let param = new TemplateParam(); + param.name = paramName; + param.type = TemplateParamType.Float; + param.value = 0; + this.params.push(param); + } + return this; + } - objects: ObjectId[] = []; UpdateAllTemplate(newParams: []) { for (let i = 0; i < this.params.length; i++) @@ -200,11 +464,19 @@ export class TemplateRecord extends SymbolTableRecord oldParam.UpdateParam(newParams[i]); } } + + get Params() + { + return this.params; + } + UpdateParam(name: string, value: any, expr?: string) { let param = this.GetParam(name); if (param) + { param.UpdateParam(value); + } } GetParam(paramName: string) @@ -215,6 +487,55 @@ export class TemplateRecord extends SymbolTableRecord return param; } } + + //#region -------------------------File------------------------- + //对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化 + //对象从文件中读取数据,初始化自身 + ReadFile(file: CADFiler) + { + let ver = file.Read(); + super.ReadFile(file); + + this.type = file.Read(); + this.parent = file.ReadObjectId(); + + let count = file.Read() as number; + this.children.length = 0; + for (let i = 0; i < count; i++) + this.children.push(file.ReadObjectId()); + + count = file.Read(); + this.params.length = 0; + for (let i = 0; i < count; i++) + this.params.push(file.ReadObject()); + + count = file.Read(); + this.objects.length = 0; + for (let i = 0; i < count; i++) + this.objects.push(file.ReadObjectId()); + } + //对象将自身数据写入到文件. + WriteFile(file: CADFiler) + { + file.Write(1); + super.WriteFile(file); + + file.Write(this.type); + file.WriteObjectId(this.parent); + + file.Write(this.children.length); + for (let id of this.children) + file.WriteObjectId(id); + + file.Write(this.params.length); + for (let param of this.params) + file.WriteObject(param); + + file.Write(this.objects.length); + for (let id of this.objects) + file.WriteObjectId(id); + } + //#endregion } @@ -260,7 +581,7 @@ export class TestTemplateAction let params = ["L", "W", "H"]; let paramIndex = 0; - let record = globalThis.record as TemplateRecord + let record = GetDefaultTemplate() as TemplateRecord let count = 1; this.time = setInterval(() => @@ -282,28 +603,18 @@ export class TestTemplateAction } } -globalThis.record = new TemplateRecord(); - -let lParam = new TemplateParam(); -lParam.name = "L"; -lParam.type = TemplateParamType.Float; -lParam.value = 0; - -globalThis.record.params.push(lParam); - -let wParam = new TemplateParam(); -wParam.name = "W"; -wParam.type = TemplateParamType.Float; -wParam.value = 0; - -globalThis.record.params.push(wParam); - -let hParam = new TemplateParam(); -hParam.name = "H"; -hParam.type = TemplateParamType.Float; -hParam.value = 0; - -globalThis.record.params.push(hParam); +function GetDefaultTemplate() +{ + let templates = app.m_Database.TemplateTable.Objects; + if (templates.length === 0) + { + let template = new TemplateRecord().InitSizeParams(); + app.m_Database.TemplateTable.Append(template); + return template; + } + else + return templates[0]; +} const SCALEMIN = new Vector3(-0.1, -0.1, -0.1); const SCALEMAX = new Vector3(1.1, 1.1, 1.1); @@ -351,19 +662,19 @@ export class AddTemplateAction for (let { ent, indexs } of stretchData.stretchEntityMap) { stretchAction.EntityStretchPointMap.push({ - entity: ent, + entity: ent.Id, indexs }); } if (stretchData.stretchEntityMap.length > 0) { - globalThis.record.GetParam(this.name).actions.push(stretchAction); + GetDefaultTemplate().GetParam(this.name).actions.push(stretchAction); } - moveAction.Entitys = stretchData.moveEntityList; + moveAction.Entitys = stretchData.moveEntityList.map(ent => ent.Id); if (moveAction.Entitys.length > 0) { - globalThis.record.GetParam(this.name).actions.push(moveAction); + GetDefaultTemplate().GetParam(this.name).actions.push(moveAction); } } @@ -394,7 +705,7 @@ export class AddTemplateAction scaleBox.min.clamp(SCALEMIN, SCALEMAX); scaleBox.max.clamp(SCALEMIN, SCALEMAX); action.EntityStretchData.push({ - entity: br, + entity: br.Id, scaleBox }); } @@ -402,7 +713,7 @@ export class AddTemplateAction } if (action.EntityStretchData.length > 0) { - globalThis.record.GetParam(this.name).actions.push(action); + GetDefaultTemplate().GetParam(this.name).actions.push(action); } } } @@ -493,9 +804,47 @@ export class TestAction { async exec() { - globalThis.record.UpdateParam("L", globalThis.record.GetParam("L").value + 500); - globalThis.record.UpdateParam("W", globalThis.record.GetParam("W").value + 500); - globalThis.record.UpdateParam("H", globalThis.record.GetParam("H").value + 500); + GetDefaultTemplate().UpdateParam("L", GetDefaultTemplate().GetParam("L").value + 500); + GetDefaultTemplate().UpdateParam("W", GetDefaultTemplate().GetParam("W").value + 500); + GetDefaultTemplate().UpdateParam("H", GetDefaultTemplate().GetParam("H").value + 500); + } +} + +export class UpdateAction +{ + async exec() + { + let templates = app.m_Database.TemplateTable.Objects; + if (templates.length === 0) + { + app.m_Editor.Prompt("当前不存在模版") + return; + } + + let template = globalThis.template = templates[0]; + + let keyRes = await app.m_Editor.GetKeyWords({ + KeyWordList: template.Params.map(p => + { + return { + key: p.name, + msg: `当前值${p.value}` + } + }) + }); + + if (keyRes.Status === PromptStatus.Keyword) + { + let paramname = keyRes.StringResult; + let param = template.GetParam(paramname); + + let newV = await app.m_Editor.GetDistance({ Msg: "新值!", Default: param.value as number }); + + if (newV.Status === PromptStatus.OK) + { + param.UpdateParam(newV.Distance); + } + } } } @@ -503,14 +852,16 @@ export class Clear { async exec() { - globalThis.record.GetParam("L").actions.length = 0; - globalThis.record.GetParam("L").value = 0; + GetDefaultTemplate().GetParam("L").actions.length = 0; + GetDefaultTemplate().GetParam("L").actions.push(new TemplateAction()) + GetDefaultTemplate().GetParam("L").value = 0; } } commandMachine.RegisterCommand("al", new AddTemplateAction("L")); commandMachine.RegisterCommand("aw", new AddTemplateAction("W")); commandMachine.RegisterCommand("ah", new AddTemplateAction("H")); +commandMachine.RegisterCommand("uu", new UpdateAction()); commandMachine.RegisterCommand("test", new TestAction); commandMachine.RegisterCommand("clear", new Clear); diff --git a/src/DatabaseServices/TemplateTable.ts b/src/DatabaseServices/TemplateTable.ts index 88cbd8340..22d9810c4 100644 --- a/src/DatabaseServices/TemplateTable.ts +++ b/src/DatabaseServices/TemplateTable.ts @@ -1,7 +1,8 @@ -import { ObjectId } from "./ObjectId"; +import { Factory } from "./CADFactory"; +import { ObjectCollection } from "./ObjectCollection"; +import { TemplateRecord } from "./TemplateRecord"; - -export class TemplateTable +@Factory +export class TemplateTable extends ObjectCollection { - templates: ObjectId[]; }