diff --git a/src/Add-on/FilletUtils.ts b/src/Add-on/FilletUtils.ts index 8fc9452f4..532c4d3a6 100644 --- a/src/Add-on/FilletUtils.ts +++ b/src/Add-on/FilletUtils.ts @@ -425,7 +425,7 @@ export class FilletUtils }; } - findNearestPt(pts: Vector3[], target: Vector3): Vector3 + FindNearestPt(pts: Vector3[], target: Vector3): Vector3 { let res = pts[0]; let dis = Infinity; @@ -455,7 +455,7 @@ export class FilletUtils if (ipts.length > 2)//超过2个则有可能有多余交点 //找最近点 - ipts = [this.findNearestPt(ipts, brResPt), this.findNearestPt(ipts, ptResPt)] + ipts = [this.FindNearestPt(ipts, brResPt), this.FindNearestPt(ipts, ptResPt)] if (ipts.length !== 2) return "倒角失败!交点个数异常."; diff --git a/src/DatabaseServices/Template/Action/TemplateAction.ts b/src/DatabaseServices/Template/Action/TemplateAction.ts index e93dff139..63cb10ef6 100644 --- a/src/DatabaseServices/Template/Action/TemplateAction.ts +++ b/src/DatabaseServices/Template/Action/TemplateAction.ts @@ -16,7 +16,7 @@ export class TemplateAction if (this.parent) this.parent.WriteAllObjectRecord(); } - Update(paramDiff: number | string) + Update(paramDiff: number | string, newValue: number | string) { } //#region -------------------------File------------------------- diff --git a/src/DatabaseServices/Template/Action/TemplateFilletAction.ts b/src/DatabaseServices/Template/Action/TemplateFilletAction.ts new file mode 100644 index 000000000..f7706cef5 --- /dev/null +++ b/src/DatabaseServices/Template/Action/TemplateFilletAction.ts @@ -0,0 +1,64 @@ +import { Factory } from "../../CADFactory"; +import { TemplateAction } from "./TemplateAction"; +import { CADFiler } from "../../CADFiler"; +import { ObjectId } from "../../ObjectId"; +import { Board } from "../../Entity/Board"; +import { FilletUtils } from "../../../Add-on/FilletUtils"; +import { PromptEntityResult } from "../../../Editor/PromptResult"; +import { Polyline } from "../../Entity/Polyline"; +import { Circle } from "../../Entity/Circle"; + +@Factory +export class TemplateFilletAction extends TemplateAction +{ + + constructor(protected _FilletEntity?: ObjectId, + protected _CurveParam1?: number, + protected _CurveParam2?: number + ) + { + super(); + } + + Update(paramDiff: number | string, newValue: number | string) + { + if (this._FilletEntity.IsErase) return; + + let br = this._FilletEntity.Object as Board; + let cu = br.ContourCurve; + if (cu instanceof Circle) + return; + + let fillet = new FilletUtils(); + fillet.FilletRadius = newValue as number; + + let p1 = cu.GetPointAtParam(this._CurveParam1); + let p2 = cu.GetPointAtParam(this._CurveParam2); + + let res1 = new PromptEntityResult(cu, p1); + let res2 = new PromptEntityResult(cu, p2); + + let fres = fillet.FilletPolyLineSelf(res1, res2); + if (fres) + br.ContourCurve = fres.cu1 as Polyline; + } + + //#region -------------------------File------------------------- + //对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化 + //对象从文件中读取数据,初始化自身 + ReadFile(file: CADFiler) + { + let ver = file.Read(); + this._FilletEntity = file.ReadObjectId(); + this._CurveParam1 = file.Read(); + this._CurveParam2 = file.Read(); + } + //对象将自身数据写入到文件. + WriteFile(file: CADFiler) + { + file.Write(1); + file.WriteObjectId(this._FilletEntity); + file.Write(this._CurveParam1); + file.Write(this._CurveParam2); + } +} diff --git a/src/DatabaseServices/Template/Action/TestTemplateAction.ts b/src/DatabaseServices/Template/Action/TestTemplateAction.ts deleted file mode 100644 index d40202628..000000000 --- a/src/DatabaseServices/Template/Action/TestTemplateAction.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { app } from "../../../ApplicationServices/Application"; -import { FixIndex } from "../../../Common/Utils"; -import { TemplateRecord } from "../TemplateRecord"; -import { GetDefaultTemplate } from "../TemplateTest"; -export class TestTemplateAction -{ - time; - exec() - { - if (this.time) - { - clearInterval(this.time); - this.time = undefined; - return; - } - let params = ["L", "W", "H"]; - let paramIndex = 0; - let record = GetDefaultTemplate() as TemplateRecord; - let count = 1; - this.time = setInterval(() => - { - count++; - if (count === 100) - { - count = 0; - paramIndex = FixIndex(paramIndex + 1, 3); - } - let param = record.GetParam(params[paramIndex]); - param.UpdateParam(Math.abs(count * 10 - 500)); - app.Editor.UpdateScreen(); - }, 16); - } -} diff --git a/src/DatabaseServices/Template/Param/TemplateParam.ts b/src/DatabaseServices/Template/Param/TemplateParam.ts index 7a4e2c7ec..1bb18d50a 100644 --- a/src/DatabaseServices/Template/Param/TemplateParam.ts +++ b/src/DatabaseServices/Template/Param/TemplateParam.ts @@ -5,6 +5,7 @@ import { CADFiler } from "../../CADFiler"; import { TemplateRecord } from "../TemplateRecord"; import { TemplateAction } from "../Action/TemplateAction"; import { TemplateParamType } from "./TemplateParamType"; +import { eval2 } from "../../../Common/eval"; /** * 模版参数 @@ -12,9 +13,15 @@ import { TemplateParamType } from "./TemplateParamType"; @Factory export class TemplateParam { + /** + * 变量名称 + * 命名规范(同js) 并且 禁止前缀`_`,`$`. + */ @AutoRecord name: string; - @AutoRecord expr: string; + /** 表达式 使用js引起求值(暂时) */ + @AutoRecord expr: string | number = ""; /** + * 设置参数的值,通常在模版设计中可以这么干 * 如果需要更新参数的动作,那么应该调用 UpdateParam */ @AutoRecord value: string | number; @@ -55,6 +62,7 @@ export class TemplateParam if (this.parent) this.parent.WriteAllObjectRecord(); } + /** * 更新参数值,并且触发动作. * 如果你想要只更新参数的值,那么你应该直接`this.value = xxx;` @@ -72,7 +80,7 @@ export class TemplateParam { let diff = newV - oldV; for (let a of this.actions) - a.Update(diff); + a.Update(diff, newV); this.value = newV; } break; @@ -82,6 +90,43 @@ export class TemplateParam break; } } + + /** + * 计算表达式的值并更新 + * @param vardefines 变量定义列表 + * @param paramMap 所有的参数列表.(可能我们需要依赖更新它) + */ + EvalUpdate(vardefines: Object, paramMap: Map, evaled: Set, update = true): number + { + if (this.expr === "") return this.value as number; + if (evaled.has(this)) return this.value as number; + + let value = parseFloat(this.expr as string); + if (isNaN(value)) + { + //依赖收集 提前更新 + let keywords = (this.expr).split(/[\s(){}=+-/*/,%;]/).filter(s => s.length > 0); + for (let key of keywords) + { + if (key !== this.name && paramMap.has(key)) + paramMap.get(key).EvalUpdate(vardefines, paramMap, evaled, update); + } + + value = eval2(this.expr as string, vardefines); + } + else + this.expr = ""; + + vardefines[this.name] = value; + + if (update) + this.UpdateParam(value); + + evaled.add(this); + + return value; + } + //#region -------------------------File------------------------- //对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化 //对象从文件中读取数据,初始化自身 diff --git a/src/DatabaseServices/Template/Positioning/Positioning.ts b/src/DatabaseServices/Template/Positioning/Positioning.ts index 52ceb01a6..60dba2d8e 100644 --- a/src/DatabaseServices/Template/Positioning/Positioning.ts +++ b/src/DatabaseServices/Template/Positioning/Positioning.ts @@ -12,15 +12,18 @@ export interface PositioningParam count?: number; } + +/** + * 模版定位信息(基类) + */ @Factory export class Positioning implements ISerialize { SpaceCS: Matrix4;//空间坐标系 - SpaceBox: Box3;//有可能不存在 SpaceSize: Vector3;//有可能不存在 /** - * 定位 + * 定位 (更新 SpaceCS SpaceBox SpaceSize) */ async Positioning(param?: PositioningParam) { diff --git a/src/DatabaseServices/Template/Positioning/PositioningClampSpace.ts b/src/DatabaseServices/Template/Positioning/PositioningClampSpace.ts index c2f628d40..6d3343b08 100644 --- a/src/DatabaseServices/Template/Positioning/PositioningClampSpace.ts +++ b/src/DatabaseServices/Template/Positioning/PositioningClampSpace.ts @@ -41,7 +41,8 @@ export class PositioningClampSpace extends Positioning if (parse.m_ParseOK) { this.SpaceCS = parse.m_SpaceOCS; - this.SpaceBox = parse.m_SpaceBox; + let p = parse.m_SpaceBox.min.clone().applyMatrix4(this.SpaceCS); + this.SpaceCS.setPosition(p); this.SpaceSize = parse.Size; } else diff --git a/src/DatabaseServices/Template/TemplateRecord.ts b/src/DatabaseServices/Template/TemplateRecord.ts index 57fb833ea..f32c648e5 100644 --- a/src/DatabaseServices/Template/TemplateRecord.ts +++ b/src/DatabaseServices/Template/TemplateRecord.ts @@ -1,3 +1,4 @@ +import { Matrix4, Vector3, Math, Box3 } from "three"; import { AutoRecord, ISPROXYKEY } from "../AutoRecord"; import { Factory } from "../CADFactory"; import { CADFiler } from "../CADFiler"; @@ -8,16 +9,29 @@ import { SymbolTableRecord } from "../SymbolTableRecord"; import { TemplateParam } from "./Param/TemplateParam"; import { TemplateParamType } from "./Param/TemplateParamType"; import { Positioning } from "./Positioning/Positioning"; -import { TemplateType } from "./TemplateType"; +import { TemplateSplitType, TemplateType } from "./TemplateType"; /** - * 模板记录 - * 模版与实体总是互相关联的,所以添加实体进入模版的时候,应该保证这个记录已经加入到数据库 + * ### 模板记录 + * 模版与实体总是互相关联的,所以添加实体进入模版的时候,应该保证这个记录已经加入到数据库 + * + * 保留参数名称列表: L W H RX RY RZ X Y Z + * 保留参数前缀: _ $ + * + * #### 批量修改参数值. + * 程序应该只传入expr的值,(禁止直接编辑value).传入后,交由模版进行计算更新. + * 模版内部消化,决定是否保留expr.(当纯数字将被直接计算成value而不保存expr). + * + * 由于`DIV`变量的计算机制,`LWH`变量将不能依赖同层变量,(这可能会导致一些错误) + * + * 当模块节点处于切割空间内时,如果想重新绑定空间,那么空间树的位置将发生变更(实际上不管是不是在切割空间内,空间位置都可能发生变更) + * */ @Factory export class TemplateRecord extends SymbolTableRecord { @AutoRecord Type: TemplateType; + @AutoRecord SplitType = TemplateSplitType.None; @AutoRecord Parent: ObjectId; @AutoRecord Children: ObjectId[]; @AutoRecord Params: TemplateParam[]; @@ -27,63 +41,48 @@ export class TemplateRecord extends SymbolTableRecord { super(); //监听 - this.Params = new Proxy([], { - set: (target, key, value, receiver) => + this.Params = this.CreateProxyArray( + value => { - if (Reflect.get(target, key, receiver) !== value) + if (value instanceof TemplateParam) + value.parent = this; + } + ); + + this.Objects = this.CreateProxyArray( + value => + { + if (value instanceof ObjectId && value.Object instanceof Entity) { - this.WriteAllObjectRecord(); - if (value instanceof TemplateParam) - value.parent = this; + if (!this.Id) + console.warn("请先加模版添加到Database后在进行操作!"); + value.Object.Template = this.Id; } - return Reflect.set(target, key, value, receiver); - }, - get: (target, key, receiver) => - { - if (key === ISPROXYKEY) - return true; - return Reflect.get(target, key, receiver); } - }); - this.Objects = new Proxy([], { - set: (target, key, value, receiver) => + ); + + this.Children = this.CreateProxyArray( + value => { - if (Reflect.get(target, key, receiver) !== value) + if (value instanceof ObjectId && value.Object instanceof TemplateRecord) { - if (this.WriteAllObjectRecord()) - { - if (value instanceof ObjectId && value.Object instanceof Entity) - { - if (!this.Id) - console.warn("请先加模版添加到Database后在进行操作!"); - value.Object.Template = this.Id; - } - } + if (!this.Id) + console.warn("请先加模版添加到Database后在进行操作!"); + value.Object.Parent = this.Id; } - return Reflect.set(target, key, value, receiver); - }, - get: (target, key, receiver) => - { - if (key === ISPROXYKEY) - return true; - return Reflect.get(target, key, receiver); } - }); + ); + } - this.Children = new Proxy([], { + private CreateProxyArray(setCallback: (v: any) => void) + { + return new Proxy([], { set: (target, key, value, receiver) => { if (Reflect.get(target, key, receiver) !== value) { - if (this.WriteAllObjectRecord()) - { - if (value instanceof ObjectId && value.Object instanceof TemplateRecord) - { - if (!this.Id) - console.warn("请先加模版添加到Database后在进行操作!"); - value.Object.Parent = this.Id; - } - } + this.WriteAllObjectRecord(); + setCallback(value); } return Reflect.set(target, key, value, receiver); }, @@ -96,26 +95,40 @@ export class TemplateRecord extends SymbolTableRecord }); } - /** - * 节点深度 - */ - get NodeDepth() + Traverse(callback: (arg0: this) => void) { - let parent = this.Parent; - let depth = 0; - while (parent) + callback(this); + for (let c of this.Children) { - depth++; - let template = parent.Object as TemplateRecord; - parent = template.Parent; + let template = c.Object as TemplateRecord; + template.Traverse(callback); } - return depth; } + private _NodeDepthCache: number; + /** 节点深度,根节点=0 */ + get NodeDepth() + { + if (this._NodeDepthCache !== undefined) + return this._NodeDepthCache; + + if (!this.Parent) return 0; + + let parentTemplate = this.Parent.Object as TemplateRecord; + this._NodeDepthCache = parentTemplate.NodeDepth + 1; + + return this._NodeDepthCache; + } + + /** 模版定位 */ get Positioning(): Positioning { return this.positioning; } + + /** + * 当存在夹层空间定位时,辅助定位表达式将使用夹层空间作为相对空间. + */ set Positioning(p: Positioning) { this.WriteAllObjectRecord(); @@ -123,14 +136,18 @@ export class TemplateRecord extends SymbolTableRecord this.positioning = p; } - static SizeParamName = ["L", "W", "H"]; - InitSizeParams(length = 0, width = 0, height = 0) + //#region param + + static BaseParamName = ["L", "W", "H", + "PX", "PY", "PZ", + "RX", "RY", "RZ"]; + + /** 初始化基础参数 */ + InitBaseParams() { - let size = [length, width, height]; - for (let i = 0; i < 3; i++) + for (let paramName of TemplateRecord.BaseParamName) { - let paramName = TemplateRecord.SizeParamName[i]; - let value = size[i]; + let value = 0; let param = new TemplateParam(); param.name = paramName; param.type = TemplateParamType.Float; @@ -140,148 +157,337 @@ export class TemplateRecord extends SymbolTableRecord return this; } - get LParam() - { - return this.GetParam("L"); - } - get WParam() - { - return this.GetParam("W"); - } - get HParam() - { - return this.GetParam("H"); - } + get LParam() { return this.Params[0]; } + get WParam() { return this.Params[1]; } + get HParam() { return this.Params[2]; } + + get PXParam() { return this.Params[3]; } + get PYParam() { return this.Params[4]; } + get PZParam() { return this.Params[5]; } + + get RXParam() { return this.Params[6]; } + get RYParam() { return this.Params[7]; } + get RZParam() { return this.Params[8]; } + - UpdateAllTemplate(newParams: []) - { - for (let i = 0; i < this.Params.length; i++) - { - let oldParam = this.Params[i]; - oldParam.UpdateParam(newParams[i]); - } - } + /** + * 通常只有真的只需要更新一个参数的时候需要调用这个方法 + * 如果同时修改多个参数,因为参数可能互相引用,不应该直接调用这个方法 + */ UpdateParam(name: string, value: any, expr?: string) { let param = this.GetParam(name); if (param) - { param.UpdateParam(value); - } } - GetParam(paramName: string) + + GetParam(paramName: string): TemplateParam | undefined { - for (let param of this.Params) - { - if (param.name === paramName) - return param; - } + return this.Params.find(param => param.name === paramName); } - /** - * + //#endregion param + + /** + * 通常UI操作的时候,都需要更新整个树,所以隐藏这个API. + * see `UpdateTemplateTree` + * + * 更新当前节点 + * + * ### 定位 (大小,方位) + * + * - (未优化)如果当前节点是更新树的最高层(但当前节点不是根节点) + * 那么当存在夹层空间定位的时候,可以不重复进行夹层空间定位,因为此时该空间不会发生变化. + * + * - [更新优先]在没有实现变量依赖收集(类似mobx)时,我们认为`positioning`的优先级最高. + * 所以`positioning`会被优先更新. `LWH`,`XYZ`. + * + * - (未实现)在使用变量定位时,需要传入上层的坐标系,以便进行相对定位. + * 使用空间分析时,已经不需要上层坐标系. + * + * - (未实现)辅助定位:帮助空间坐标系进行旋转 + * 辅助定位的参数变量将暴露出来.{RX RY RZ} + * 辅助定位作为参数变量时,用户使用值时很难正确的计算值,应~加入交互选择.(辅助计算) + * + * ### 变量表达式计算 + * + * - `LWH`将被`positioning`替代,但变量定义仍然正常存在. + * + * - (未实现)变量大部分时候都是被批量更新,(同时传入许多参数). + * */ - async UpdateTemplate(params?: {}, recursion = false) + private async Update() { - if (!params) - params = this.GetParentParams(); - else - { - let newParams = {}; - for (let key in params) - { - if (key[0] !== "@") - newParams["_" + key] = params[key]; - else - newParams[key] = params[key]; - } - params = newParams; - } + let vardefines = this.GetParameterDefinition(false); + let brs = this.Objects.filter(id => !id.IsErase).map(id => id.Object as Board); + let evaled = new Set(); + + let spaceCS = this.GetTemplateSpaceCS(false); + let spaceSize: Vector3; + + let paramMap = new Map(); + for (let param of this.Params) + paramMap.set(param.name, param); + + for (let br of brs) + br.ApplyMatrix(br.SpaceOCSInv); + //#region 1.定位(坐标系和大小) - let objs = this.Objects.filter(id => !id.IsErase).map(id => id.Object as Board); if (this.positioning) { await this.positioning.Positioning(); if (!this.positioning.SpaceCS) return;//出事故 - let p = this.positioning.SpaceBox.min.applyMatrix4(this.positioning.SpaceCS); - this.positioning.SpaceCS.setPosition(p); - for (let ent of objs) - { - // let m = ent.SpaceOCSInv.multiply(this.positioning.SpaceCS); - ent.ApplyMatrix(ent.SpaceOCSInv); - ent.ApplyMatrix(this.positioning.SpaceCS); - } + spaceCS = this.positioning.SpaceCS; + spaceSize = this.positioning.SpaceSize; - if (this.positioning.SpaceSize) - { - this.LParam.UpdateParam(this.positioning.SpaceSize.x); - this.WParam.UpdateParam(this.positioning.SpaceSize.y); - this.HParam.UpdateParam(this.positioning.SpaceSize.z); - } + spaceCS = this.RotateSpaceCS(vardefines, evaled, spaceCS, spaceSize); + //更新LWH(通过定位空间) + this.LParam.UpdateParam(spaceSize.x); + this.WParam.UpdateParam(spaceSize.y); + this.HParam.UpdateParam(spaceSize.z); + } + else + { + + this.LParam.EvalUpdate(vardefines, paramMap, evaled); + this.WParam.EvalUpdate(vardefines, paramMap, evaled); + this.HParam.EvalUpdate(vardefines, paramMap, evaled); + + this.PXParam.EvalUpdate(vardefines, paramMap, evaled); + this.PYParam.EvalUpdate(vardefines, paramMap, evaled); + this.PZParam.EvalUpdate(vardefines, paramMap, evaled); + + let l = this.LParam.value as number; + let w = this.WParam.value as number; + let h = this.HParam.value as number; + spaceSize = new Vector3(l, w, h); - //保持SpaceCS - for (let ent of objs) + //相对定位. use PX PY pZ + let baseP = new Vector3(this.PXParam.value as number, this.PYParam.value as number, this.PZParam.value as number); + baseP.applyMatrix4(spaceCS); + spaceCS.setPosition(baseP); + + if (!this.Parent) { - ent.SpaceOCS = this.positioning.SpaceCS; + this.PXParam.value = 0; + this.PXParam.expr = ""; + this.PYParam.value = 0; + this.PYParam.expr = ""; + this.PZParam.value = 0; + this.PZParam.expr = ""; } + + spaceCS = this.RotateSpaceCS(vardefines, evaled, spaceCS, spaceSize); + //更新LWH(通过定位空间) + this.LParam.UpdateParam(spaceSize.x); + this.WParam.UpdateParam(spaceSize.y); + this.HParam.UpdateParam(spaceSize.z); } - if (recursion) + vardefines["L"] = spaceSize.x; + vardefines["W"] = spaceSize.y; + vardefines["H"] = spaceSize.z; + + //#endregion + + + //#region 3.应用更新到定位空间 + + //变换到新的模版空间 + for (let br of brs) + br.ApplyMatrix(spaceCS); + + //#endregion + + //更新其他参数变量 Eval local params + for (const param of this.Params) { - //广度优先 - let updateStack = [...this.Children]; - let childrenCount = this.Children.length; - let index = 0; - while (updateStack.length > 0) + param.EvalUpdate(vardefines, paramMap, evaled); + } + + //保持SpaceCS + for (let ent of brs) + { + ent.SpaceOCS = spaceCS; + } + + //Cache + this._CacheParamVars = vardefines; + this._CacheSpaceCS = spaceCS; + } + + /** + * 旋转空间定位,如果旋转成功,那么SpaceSize和SpaceCS都可能被更新 + */ + private RotateSpaceCS(vardefines: any, evaled: Set, spaceCS: Matrix4, spaceSize: Vector3) + { + this.RXParam.EvalUpdate(vardefines, this.Params, evaled); + this.RYParam.EvalUpdate(vardefines, this.Params, evaled); + this.RZParam.EvalUpdate(vardefines, this.Params, evaled); + //use RX RY RZ + let rx = Math.degToRad(this.RXParam.value as number); + let ry = Math.degToRad(this.RYParam.value as number); + let rz = Math.degToRad(this.RZParam.value as number); + if (rx !== 0 || ry !== 0 || rz !== 0) + { + let mrx = new Matrix4().makeRotationX(rx); + let mry = new Matrix4().makeRotationY(ry); + let mrz = new Matrix4().makeRotationZ(rz); + let mro = mrz.multiply(mry.multiply(mrx)); + let roSpace = mro.multiply(spaceCS); + let roSpaceInv = mrx.getInverse(roSpace); //变量复用 + let transfromToRoSpace = roSpaceInv.multiply(spaceCS); + let box = new Box3(new Vector3(), spaceSize.clone()); + box.applyMatrix4(transfromToRoSpace); + box.getSize(spaceSize); + let baseP = box.min.clone().applyMatrix4(roSpace); + roSpace.setPosition(baseP); + //更新LWH(通过定位空间) + this.LParam.UpdateParam(spaceSize.x); + this.WParam.UpdateParam(spaceSize.y); + this.HParam.UpdateParam(spaceSize.z); + spaceCS = roSpace; + } + return spaceCS; + } + + /** 以广度搜索优先更新节点树 */ + async UpdateTemplateTree() + { + let stack: TemplateRecord[] = [this]; + await this.Update(); + while (stack.length > 0) + { + let template = stack.pop(); + + //计算DIV(给子空间使用) + if (template.Children.length > 0 && template.SplitType !== TemplateSplitType.None) { - if (index < childrenCount) + let vardefines = (template.Children[0].Object).GetParameterDefinition(); + vardefines._DIV = 0; + let sum = 0; + for (let c of template.Children) { - params["index"] = index; - params["childrenCount"] = childrenCount; + let ctemplate = c.Object as TemplateRecord; + let param = ctemplate.Params[template.SplitType]; + sum += param.EvalUpdate(vardefines, [], new Set(), false); } - else + + let sumDiv = 0; + vardefines._DIV = 1; + for (let c of template.Children) { - delete params["index"]; - delete params["childrenCount"]; + let ctemplate = c.Object as TemplateRecord; + let param = ctemplate.Params[template.SplitType]; + sum += param.EvalUpdate(vardefines, [], new Set(), false); } - let id = updateStack.shift(); - let template = id.Object as TemplateRecord; - await template.UpdateTemplate(params); - updateStack.push(...template.Children); + let divCount = sum - sumDiv; + + if (divCount > 0) + template._CacheParamVars.DIV = (template.Params[template.SplitType].value as number - sum) / divCount; + template._CacheParamVars.POS = 0; + } + + for (let c of template.Children) + { + let ctemplate = c.Object as TemplateRecord; + stack.push(ctemplate); + await ctemplate.Update(); - index++; + if (template._CacheParamVars.DIV !== undefined)//更新POS + template._CacheParamVars.POS += ctemplate.Params[template.SplitType].value as number; } } } - private GetParentParams() + /** 缓存本节点的变量定义值,当子层需要本层的参数时,可以直接获取 */ + private _CacheParamVars: any; + private _CacheSpaceCS: Matrix4; + + /** + * 本节点可用的所有变量定义.(包括变量继承) + * @param [useCache=true] 当更新当前节点的时候,我们不希望使用缓存,(因为父节点的参数可能已经被更新) + * @returns + */ + private GetParameterDefinition(useCache = true): any { - let params = {}; - let parent = this.Parent; - let index = 1; - while (parent) + if (this._CacheParamVars && useCache) + return this._CacheParamVars; + + let vars = this.GetParentParams(); + for (const param of this.Params) { - let parentTemplate = parent.Object as TemplateRecord; - let frontStr = new Array(index).fill("_").join(); - for (let param of parentTemplate.Params) - params[frontStr + param.name] = param.value; - if (index === 1) - { - let recordIndex = parentTemplate.Children.indexOf(this.Id); - } - parent = parentTemplate.Parent; - if (!parent) + vars[param.name] = param.value; + if (!this.Parent) + vars["$" + param.name] = param.value;//root + } + + this._CacheParamVars = vars; + return vars; + } + + /** + * 变量继承 + * - 每继承一层,前缀增加一个`_` + * - 顶层前缀`$` + * + * @returns 继承于父空间的变量定义列表 + */ + private GetParentParams(): Object + { + if (!this.Parent) return {}; + + let parent = this.Parent.Object as TemplateRecord; + let params = parent.GetParameterDefinition(); + + let newParams = {}; + for (let key in params) + { + if (key[0] !== "$") + newParams["_" + key] = params[key]; + else + newParams[key] = params[key]; + } + params = newParams; + + return params; + } + + + /** + * 获得当前的模版空间的相对坐标系 + * - 存在父节点的时候使用父节点的模版空间坐标系 + * - 不存在父节点的时候使用自身的模版空间坐标系 + * + * - 空间坐标系将被定位更新 + * + * @param [useCache=true] 当更新当前节点的时候,我们不希望使用缓存,(因为父节点的参数可能已经被更新) + */ + private GetTemplateSpaceCS(useCache = true): Matrix4 + { + if (useCache && this._CacheSpaceCS) + return this._CacheSpaceCS.clone(); + + if (this.Parent) + { + let template = this.Parent.Object as TemplateRecord; + return template.GetTemplateSpaceCS(); + } + + for (let brId of this.Objects) + { + if (brId.Object && !brId.IsErase) { - for (let param of parentTemplate.Params) - params["@" + param.name] = param.value; + let br = brId.Object as Board; + return br.SpaceOCS; } - index++; } - return params; + + return new Matrix4(); } //#region -------------------------File------------------------- @@ -307,11 +513,14 @@ export class TemplateRecord extends SymbolTableRecord this.Objects.push(file.ReadObjectId()); this.positioning = file.ReadObject(); + + if (ver > 1) + this.SplitType = file.Read(); } //对象将自身数据写入到文件. WriteFile(file: CADFiler) { - file.Write(1); + file.Write(2); super.WriteFile(file); file.Write(this.Type); file.WriteObjectId(this.Parent); @@ -326,6 +535,8 @@ export class TemplateRecord extends SymbolTableRecord file.WriteObjectId(id); file.WriteObject(this.positioning); + + file.Write(this.SplitType); } //#endregion } diff --git a/src/DatabaseServices/Template/TemplateTest.ts b/src/DatabaseServices/Template/TemplateTest.ts index 31ca0073b..04ebddcfe 100644 --- a/src/DatabaseServices/Template/TemplateTest.ts +++ b/src/DatabaseServices/Template/TemplateTest.ts @@ -15,39 +15,22 @@ import { TemplateStretchScaleBoxAction } from "./Action/TemplateStretchScaleBoxA import { TemplateRecord } from "./TemplateRecord"; import { ClampSpaceParse } from "../../Geometry/SpaceParse/ClampSpaceParse"; import { PositioningClampSpace } from "./Positioning/PositioningClampSpace"; - -/** - * 参数注册 - */ -function ParamRegister(params: {}, name: string, value: string, isRoot = false) -{ - params[name] = value; - if (isRoot) - params["@" + name] = value; -} - -/** - * 参数列表成了爹 - */ -function ParameterBecomeFater(params: {}) -{ - let nparams = {}; - for (let name in params) - { - if (name[0] === "@") - nparams[name] = params[name]; - else - nparams["_" + name] = params[name]; - } - return nparams; -} +import { Sleep } from "../../Common/Sleep"; +import { TemplateSplitType } from "./TemplateType"; +import { TemplateParam } from "./Param/TemplateParam"; +import { TemplateParamType } from "./Param/TemplateParamType"; +import { FilletUtils } from "../../Add-on/FilletUtils"; +import { Polyline } from "../Entity/Polyline"; +import { Line } from "../Entity/Line"; +import { IntersectOption } from "../../GraphicsSystem/IntersectWith"; +import { TemplateFilletAction } from "./Action/TemplateFilletAction"; export function GetDefaultTemplate() { let templates = app.Database.TemplateTable.Objects; if (templates.length === 0) { - let template = new TemplateRecord().InitSizeParams(); + let template = new TemplateRecord().InitBaseParams(); app.Database.TemplateTable.Append(template); return template; } @@ -178,6 +161,7 @@ export class AutoTempateSizeAction //归0? let keyRes = await app.Editor.GetKeyWords({ + Msg: "自动将所有的板件移动到0点后初始化动作?", KeyWordList: [ { key: "Y", msg: "是" }, { key: "N", msg: "否" }, @@ -214,7 +198,14 @@ export class AutoTempateSizeAction //上缩 let topBox = new Box3(box.min.clone().add(new Vector3(-1, -1, size.z / 2)), box.max.clone().add(v)); - let template = new TemplateRecord().InitSizeParams(); + let template = new TemplateRecord().InitBaseParams(); + + let rParam = new TemplateParam(); + rParam.name = "R"; + rParam.value = 0; + rParam.type = TemplateParamType.Float; + template.Params.push(rParam); + app.Database.TemplateTable.Append(template); template.GetParam("L").value = size.x; template.GetParam("W").value = size.y; @@ -343,8 +334,8 @@ export class UpdateTemplate if (newV.Status === PromptStatus.OK) { - param.UpdateParam(newV.Distance); - template.UpdateTemplate(undefined, true); + param.expr = newV.Distance; + await template.UpdateTemplateTree(); } } else @@ -353,6 +344,41 @@ export class UpdateTemplate } } +class UpdateTemplateRo +{ + async exec() + { + let enRes = await app.Editor.GetSelection({ + Msg: "选择要修改的模版:", + Once: true, + Filter: { + filterFunction: (obj, ent) => + { + return ent.Template !== undefined; + } + } + }); + + if (enRes.Status !== PromptStatus.OK) return; + + + let template = enRes.SelectSet.SelectEntityList[0].Template.Object as TemplateRecord; + + let ros = [0, 90, 180, 270, 0]; + + for (let param of [template.RXParam, template.RYParam, template.RZParam]) + { + for (let r of ros) + { + param.expr = r; + await template.UpdateTemplateTree(); + app.Editor.UpdateScreen(); + await Sleep(1000); + } + } + } +} + export class UpdateParam2 { timer; @@ -395,7 +421,7 @@ export class UpdateParam2 param.UpdateParam(param.value as number - 150); } - template.UpdateTemplate(undefined, true); + template.UpdateTemplateTree(); app.Editor.UpdateScreen(); index++; }, 16) @@ -447,7 +473,7 @@ export class TemplateAttach let positioning = new PositioningClampSpace(); positioning.FromSpaceParse(parse); template.Positioning = positioning; - await template.UpdateTemplate();//单独更新 不继承 + await template.UpdateTemplateTree();//单独更新 不继承 let tbrs = brs.filter(br => br.Template !== undefined); if (tbrs.length === 0) @@ -471,13 +497,172 @@ export class TemplateAttach } } + +export class TemplateAttach2 +{ + async exec() + { + let enRes = await app.Editor.GetEntity({ + Msg: "选择附加的模版", + NotNone: true, + Filter: { + filterFunction: (obj, ent) => + { + return ent.Template !== undefined; + } + } + }); + if (enRes.Status !== PromptStatus.OK) return; + + let e = enRes.Entity; + let template = e.Template.Object as TemplateRecord; + + let selectSpace = new PointSelectSpaceClamp(); + selectSpace.Enable = EnableSelectType.Stretch; + await selectSpace.Select(); + + if (!selectSpace.ParseOK) + { + app.Editor.Prompt("未能分析出有效空间!"); + return; + } + + let parse = selectSpace.SpaceParse as ClampSpaceParse; + let brs = parse.m_Boards; + + let positioning = new PositioningClampSpace(); + positioning.FromSpaceParse(parse); + + let tbrs = brs.filter(br => br.Template !== undefined); + if (tbrs.length === 0) + { + app.Editor.Prompt("选取的空间不存在模版!"); + } + else + { + tbrs.sort((b1, b2) => + { + let t1 = b1.Template.Object as TemplateRecord; + let t2 = b2.Template.Object as TemplateRecord; + + return t2.NodeDepth - t1.NodeDepth; + }); + + let templateSpace = new TemplateRecord().InitBaseParams(); + templateSpace.SplitType = TemplateSplitType.Z; + templateSpace.Positioning = positioning; + app.Database.TemplateTable.Append(templateSpace); + + let template1 = new TemplateRecord().InitBaseParams(); + template1.LParam.expr = "_L"; + template1.WParam.expr = "_W"; + template1.HParam.expr = "_DIV"; + template1.PZParam.expr = "_POS"; + let template2 = template1.Clone(); + app.Database.TemplateTable.Append(template1); + app.Database.TemplateTable.Append(template2); + + template.LParam.expr = "_L"; + template.WParam.expr = "_W"; + template.HParam.expr = "_DIV"; + template.PZParam.expr = "_POS"; + + templateSpace.Children.push(template1.Id); + templateSpace.Children.push(template.Id); + templateSpace.Children.push(template2.Id); + + let templateSource = tbrs[0].Template.Object as TemplateRecord; + + templateSource.Children.push(templateSpace.Id); + + await templateSpace.UpdateTemplateTree(); + } + } +} + +export class TemplateAddFilletAction +{ + + async exec() + { + let brRes = await app.Editor.GetEntity({ + Msg: "选择附加的模版", + NotNone: true, + Filter: { + filterFunction: (obj, ent) => + { + if (ent instanceof Board) + return ent.Template !== undefined; + + return false; + } + } + }); + if (brRes.Status !== PromptStatus.OK) return; + + //------1.画辅助线 + let ptRes = await app.Editor.GetPoint({ + BasePoint: brRes.Point, + AllowDrawRubberBand: true, + AllowNone: true, + Msg: "选择下一个点:" + }); + if (ptRes.Status === PromptStatus.Cancel) + return; + + //------2.倒角 + let fillet = new FilletUtils(); + fillet.FilletRadius = 100; + let fres = fillet.FilletBoard(brRes, ptRes); + if (fres instanceof Polyline) + { + let br = brRes.Entity as Board; + br.ContourCurve = fres; + let brContour = fres; + + //------1.求交 + let brResPt = brRes.Point.clone().applyMatrix4(br.OCSInv).setZ(0); + let ptResPt = ptRes.Point.clone().applyMatrix4(br.OCSInv).setZ(0); + let l = new Line(brResPt, ptResPt); + + let ipts = l.IntersectWith(brContour, IntersectOption.ExtendThis); + + if (ipts.length > 2)//超过2个则有可能有多余交点 + //找最近点 + ipts = [fillet.FindNearestPt(ipts, brResPt), fillet.FindNearestPt(ipts, ptResPt)] + + if (ipts.length !== 2) + return "倒角失败!交点个数异常."; + + let param1 = brContour.GetParamAtPoint(ipts[0]); + let param2 = brContour.GetParamAtPoint(ipts[1]); + + let filletAction = new TemplateFilletAction(br.Id, param1, param2); + + let template = br.Template.Object as TemplateRecord; + + let rParam = template.GetParam("R"); + rParam.actions.push(filletAction); + } + else + app.Editor.Prompt(fres); + + } +} + + commandMachine.RegisterCommand("Attach", new TemplateAttach()); +commandMachine.RegisterCommand("Attach2", new TemplateAttach2()); + + +commandMachine.RegisterCommand("templateAddFilletAction", new TemplateAddFilletAction()); commandMachine.RegisterCommand("al", new AddTemplateAction("L")); commandMachine.RegisterCommand("aw", new AddTemplateAction("W")); commandMachine.RegisterCommand("ah", new AddTemplateAction("H")); commandMachine.RegisterCommand("uu", new UpdateTemplate()); +commandMachine.RegisterCommand("uur", new UpdateTemplateRo()); commandMachine.RegisterCommand("uuu", new UpdateParam2()); commandMachine.RegisterCommand("clear", new Clear); diff --git a/src/DatabaseServices/Template/TemplateType.ts b/src/DatabaseServices/Template/TemplateType.ts index b54af4de5..ed3c219dc 100644 --- a/src/DatabaseServices/Template/TemplateType.ts +++ b/src/DatabaseServices/Template/TemplateType.ts @@ -11,3 +11,11 @@ export enum TemplateType //阵列模版 Array } + +export enum TemplateSplitType +{ + None = -1, + X = 0, + Y = 1, + Z = 2, +}