diff --git a/__test__/EdgeSealing/__snapshots__/EdgeSealing.test.ts.snap b/__test__/EdgeSealing/__snapshots__/EdgeSealing.test.ts.snap
index fea1e72f7..106257779 100644
--- a/__test__/EdgeSealing/__snapshots__/EdgeSealing.test.ts.snap
+++ b/__test__/EdgeSealing/__snapshots__/EdgeSealing.test.ts.snap
@@ -2,7 +2,7 @@
exports[`丢失线段板件 1`] = `554052.5007766776`;
-exports[`丢失线段板件 2`] = `398758.87896958215`;
+exports[`丢失线段板件 2`] = `398758.8789695821`;
exports[`常规板件,常规坐标系 1`] = `716404`;
@@ -28,4 +28,4 @@ exports[`异型板件,非常规坐标系 3`] = `75694680.60847883`;
exports[`异型板件,非相切圆弧 1`] = `635612.2751433643`;
-exports[`异型板件,非相切圆弧 2`] = `626242.2196800548`;
+exports[`异型板件,非相切圆弧 2`] = `626242.2196800549`;
diff --git a/__test__/Interest/line.test.ts b/__test__/Interest/line.test.ts
index 70927a659..274a07a3d 100644
--- a/__test__/Interest/line.test.ts
+++ b/__test__/Interest/line.test.ts
@@ -20,7 +20,7 @@ test('直线相交,共线', () =>
expect(pts.length).toBe(0);
pts = IntersectLineAndLine(l1, l2, IntersectOption.ExtendBoth);//?
- expect(pts.length).toBe(0);
+ expect(pts.length).toBe(4);
});
diff --git a/__test__/Polyline/__snapshots__/offset.test.ts.snap b/__test__/Polyline/__snapshots__/offset.test.ts.snap
index 7af8983c5..9dcd95710 100644
--- a/__test__/Polyline/__snapshots__/offset.test.ts.snap
+++ b/__test__/Polyline/__snapshots__/offset.test.ts.snap
@@ -342,10 +342,10 @@ exports[`连续丢圆弧后无法连接 10`] = `"574.66475"`;
exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 1`] = `54789.24964851236`;
-exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 2`] = `54907.28173780604`;
+exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 2`] = `54907.28173780605`;
-exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 3`] = `55497.50212266886`;
+exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 3`] = `55497.50212266887`;
-exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 4`] = `56678.24106604484`;
+exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 4`] = `56678.241066044844`;
-exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 5`] = `57859.37443960544`;
+exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 5`] = `57859.37443960543`;
diff --git a/__test__/Polyline/__snapshots__/offsetbug.test.ts.snap b/__test__/Polyline/__snapshots__/offsetbug.test.ts.snap
index 7b619331b..e5ee5dd79 100644
--- a/__test__/Polyline/__snapshots__/offsetbug.test.ts.snap
+++ b/__test__/Polyline/__snapshots__/offsetbug.test.ts.snap
@@ -14,7 +14,7 @@ exports[`补充bug测试#IKWGF 6`] = `0.6246933440840138`;
exports[`补充bug测试#IKWGF 7`] = `14.067113755971711`;
-exports[`补充bug测试#IKWGF 8`] = `11.891017922899252`;
+exports[`补充bug测试#IKWGF 8`] = `11.891017922899254`;
exports[`补充bug测试#IKWGF 9`] = `2.168984971098264`;
diff --git a/src/Add-on/EraseLineAndArc.ts b/src/Add-on/EraseLineAndArc.ts
new file mode 100644
index 000000000..2268c0d21
--- /dev/null
+++ b/src/Add-on/EraseLineAndArc.ts
@@ -0,0 +1,17 @@
+import { app } from "../ApplicationServices/Application";
+import { Arc } from "../DatabaseServices/Entity/Arc";
+import { Line } from "../DatabaseServices/Entity/Line";
+import { Command } from "../Editor/CommandMachine";
+import { PromptStatus } from "../Editor/PromptResult";
+
+export class Command_EraseLineAndArc implements Command
+{
+ async exec()
+ {
+ let ssRes = await app.Editor.GetSelection({ UseSelect: true, Msg: "选择要被删除的直线和圆弧:", Filter: { filterTypes: [Line, Arc] } });
+ if (ssRes.Status !== PromptStatus.OK) return;
+ let ents = ssRes.SelectSet.SelectEntityList;
+ for (let e of ents)
+ e.Erase();
+ }
+}
diff --git a/src/Add-on/testEntity/TestRegionParse.ts b/src/Add-on/testEntity/TestRegionParse.ts
new file mode 100644
index 000000000..d58105c48
--- /dev/null
+++ b/src/Add-on/testEntity/TestRegionParse.ts
@@ -0,0 +1,27 @@
+import { Command } from "../../Editor/CommandMachine";
+import { Line } from "../../DatabaseServices/Entity/Line";
+import { app } from "../../ApplicationServices/Application";
+import { PromptStatus } from "../../Editor/PromptResult";
+import { RegionParse } from "../../Geometry/RegionParse";
+import { Polyline } from "../../DatabaseServices/Entity/Polyline";
+import { TestDraw } from "../test/TestUtil";
+
+export class Command_TestRegionParse implements Command
+{
+ async exec()
+ {
+ let ssRes = await app.Editor.GetSelection({ Filter: { filterTypes: [Line] } });
+ if (ssRes.Status !== PromptStatus.OK) return;
+ let ents = ssRes.SelectSet.SelectEntityList as Line[];
+
+ let region = new RegionParse(ents);
+ for (let rs of [...region.RegionsOutline, ...region.RegionsInternal])
+ {
+ let pl = Polyline.Combine(rs.map(r => r.curve));
+ TestDraw(pl);
+ }
+
+ for (let e of ents)
+ e.Erase();
+ }
+}
diff --git a/src/Add-on/twoD2threeD/CreatePolyLineFromCurve.ts b/src/Add-on/twoD2threeD/CreatePolyLineFromCurve.ts
new file mode 100644
index 000000000..cc5c8a7cc
--- /dev/null
+++ b/src/Add-on/twoD2threeD/CreatePolyLineFromCurve.ts
@@ -0,0 +1,240 @@
+import { Matrix4, Vector3 } from "three";
+import { app } from "../../ApplicationServices/Application";
+import { ComputerCurvesNormalOCS } from "../../Common/CurveUtils";
+import { matrixIsCoplane } from "../../Common/Matrix4Utils";
+import { Arc } from "../../DatabaseServices/Entity/Arc";
+import { Curve } from "../../DatabaseServices/Entity/Curve";
+import { Line } from "../../DatabaseServices/Entity/Line";
+import { Polyline } from "../../DatabaseServices/Entity/Polyline";
+import { Command } from "../../Editor/CommandMachine";
+import { PromptStatus } from "../../Editor/PromptResult";
+import { CurveIntersection } from "../../Geometry/CurveIntersection";
+import { Route } from "../../Geometry/CurveMap";
+import { equaln, IdentityMtx4 } from "../../Geometry/GeUtils";
+import { RegionParse } from "../../Geometry/RegionParse";
+import { IntersectOption } from "../../GraphicsSystem/IntersectWith";
+import { Curve2RecModal, Curve2RecModalStore } from "./Modals/Curve2RecModal";
+
+//这里认为最大的板厚可能有50,当多段线的宽度小于50时,我们认为它是一个板
+//这个参数以后可能开放
+const MaxBoardThickness = 50;
+
+export class CreatePolyLineFromCurve implements Command
+{
+ IsSaveMax: boolean;//保留最大
+
+ IsOnlySaveSmall: boolean;//只保留小于指定宽度的矩形
+ SmallWidth: number;//矩形的最小宽度
+
+ BreakCurve: boolean;//打断曲线(分析相交区域)
+ ExtendsMinDist: number;//最小延伸范围
+
+ async exec()
+ {
+ let store = Curve2RecModalStore.GetInstance() as Curve2RecModalStore;
+ app.Editor.ModalManage.RenderModal(Curve2RecModal, { store: store });
+
+ app.Editor.ModalManage.SetCallback(async () =>
+ {
+ this.IsSaveMax = store.option.isSaveMax;
+ this.IsOnlySaveSmall = store.option.isSaveSmall;
+ this.BreakCurve = store.option.isAnaly;
+ this.SmallWidth = store.option.width;
+ this.ExtendsMinDist = store.option.gap;
+ let ss = await app.Editor.GetSelection({
+ Msg: "请选择曲线:",
+ UseSelect: true,
+ Filter: { filterTypes: [Curve] }
+ });
+ if (ss.Status === PromptStatus.OK)
+ {
+ let ens = ss.SelectSet.SelectEntityList as Curve[];
+ this.Doit(ens);
+ }
+ });
+ }
+ Doit(selectedCurs: Curve[])
+ {
+ let extendsMinDistSq = this.ExtendsMinDist ^ 2;
+
+ let groups = this.GroupCurves(selectedCurs);
+ let ocsInv = new Matrix4;
+ for (let group of groups)
+ {
+ ocsInv.getInverse(group.ocs);
+
+ //->wcs z0
+ for (let c of group.cus)
+ c.ApplyMatrix(ocsInv).Z0();
+
+ let intersect = new CurveIntersection(group.cus, false, IntersectOption.ExtendBoth);
+
+ let curves2: Curve[] = [];
+
+ //延伸+打断
+ for (let [cu, pmap] of intersect.intersect)
+ {
+ let sp = cu.StartPoint;
+ let ep = cu.EndPoint;
+
+ let epExtend: Vector3;
+ let epDist = Infinity;
+ let spExtend: Vector3;
+ let spDist = Infinity;
+
+ let ipts: Vector3[] = [];
+ for (let [, pts] of pmap)
+ {
+ ipts.push(...pts);
+ for (let p of pts)
+ {
+ let d = p.distanceToSquared(ep);
+ if (d < epDist)
+ {
+ epDist = d;
+ epExtend = p;
+ }
+ d = p.distanceToSquared(sp);
+ if (d < spDist)
+ {
+ spDist = d;
+ spExtend = p;
+ }
+ }
+ }
+
+ //延伸
+ if (epDist < extendsMinDistSq)
+ {
+ let param = cu.GetParamAtPoint(epExtend);
+ cu.Extend(param);
+ }
+ if (spDist < extendsMinDistSq)
+ {
+ let param = cu.GetParamAtPoint(spExtend);
+ cu.Extend(param);
+ }
+
+ //打断
+ let curves: Curve[];
+ if (this.BreakCurve)
+ curves = cu.GetSplitCurvesByPts(ipts);
+ else
+ curves = [cu];
+
+ for (let c of curves)
+ {
+ if (c instanceof Polyline)
+ curves2.push(...c.Explode());
+ else
+ curves2.push(c);
+ }
+ }
+
+ let parse = new RegionParse(curves2);
+
+ for (let rs of parse.RegionsInternal)
+ this.Draw(rs, group.ocs, true);
+
+ if (this.IsSaveMax)
+ for (let rs of parse.RegionsOutline)
+ this.Draw(rs, group.ocs, false, 3);
+ }
+
+ for (let e of selectedCurs)
+ e.Erase();
+ }
+ //曲线按所在平面分组
+ GroupCurves(curs: Curve[]): { ocs: Matrix4, cus: Curve[]; }[]
+ {
+ let curveGroup: { ocs: Matrix4, cus: Curve[]; }[] = [];
+
+ while (curs.length)
+ {
+ let ocs = ComputerCurvesNormalOCS(curs);
+ let ocsInv = new Matrix4().getInverse(ocs);
+
+ let cus: Curve[] = [];
+ curs = curs.filter(cu =>
+ {
+ if (CurveInOCS(cu, ocs, ocsInv))
+ {
+ cus.push(cu);
+ return false;
+ }
+ return true;
+ });
+ curveGroup.push({ ocs, cus });
+ }
+ return curveGroup;
+ }
+
+ Draw(routes: Route[], ocs: Matrix4, isOnlySaveSmall: boolean, color?: number)
+ {
+ let curves = routes.map(r => r.curve);
+ let polyline = Polyline.Combine(curves);
+ if (polyline && polyline.IsClose)
+ {
+ if (polyline.Area < 10) return;
+ if (isOnlySaveSmall)
+ {
+ let size = polyline.BoundingBox.getSize(new Vector3);
+ if (size.x > this.SmallWidth && size.y > this.SmallWidth)
+ {
+ //都大于最小宽度,不绘制
+ return;
+ }
+ }
+ if (color === undefined)
+ {
+ //解析类型
+ let size = polyline.BoundingBox.getSize(new Vector3);
+ if (size.x > MaxBoardThickness && size.y > MaxBoardThickness)
+ color = 3;
+ else if (size.x <= MaxBoardThickness)
+ color = 1;
+ else if (size.y <= MaxBoardThickness)
+ color = 2;
+ else color = 7;
+ }
+ polyline.UpdateMatrixTo(IdentityMtx4);
+ polyline.ApplyMatrix(ocs);
+ polyline.ColorIndex = color;
+ app.Database.ModelSpace.Append(polyline);
+ }
+ else
+ {
+ console.warn("未预料到的错误,连接后的多段线不是闭合的");
+ }
+ }
+}
+
+export function CurveInOCS(curve: Curve, Ocs: Matrix4, OcsInv: Matrix4): boolean
+{
+ if (curve instanceof Line)
+ {
+ let p = curve.StartPoint.applyMatrix4(OcsInv);
+ if (!equaln(p.z, 0)) return false;
+ p = curve.EndPoint.applyMatrix4(OcsInv);
+ if (!equaln(p.z, 0)) return false;
+ return true;
+ }
+ else if (curve instanceof Arc)
+ {
+ return matrixIsCoplane(curve.OCS, Ocs);
+ }
+ else if (curve instanceof Polyline)
+ {
+ if (matrixIsCoplane(curve.OCS, Ocs))
+ return true;
+ else
+ {
+ let cus = curve.Explode();
+ return cus.every(c =>
+ {
+ return this.CurveInOCS(c, Ocs, OcsInv);
+ });
+ }
+ }
+ return true;
+}
diff --git a/src/Add-on/twoD2threeD/Modals/Curve2Rec.less b/src/Add-on/twoD2threeD/Modals/Curve2Rec.less
new file mode 100644
index 000000000..e27aeb3bc
--- /dev/null
+++ b/src/Add-on/twoD2threeD/Modals/Curve2Rec.less
@@ -0,0 +1,39 @@
+.curve2rec {
+ .bp3-input {
+ width: 100px;
+ height: 25px;
+ }
+
+ .checkbox-input-group {
+ display: flex;
+ }
+
+ .checkbox-analy {
+ display: flex;
+ }
+
+ .bp3-dialog-footer {
+ padding: 10px 0 0 0 !important;
+
+ .bp3-dialog-footer-actions {
+ width: 100%;
+
+ .bp3-label.bp3-inline {
+ margin: 0 auto !important;
+
+ .cf-select {
+ padding-bottom: 5px;
+
+ .bp3-input-group.input-select {
+ width: 100px !important;
+
+ >input {
+ width: inherit !important;
+ height: inherit !important;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Add-on/twoD2threeD/Modals/Curve2RecModal.tsx b/src/Add-on/twoD2threeD/Modals/Curve2RecModal.tsx
new file mode 100644
index 000000000..9b8abc6ad
--- /dev/null
+++ b/src/Add-on/twoD2threeD/Modals/Curve2RecModal.tsx
@@ -0,0 +1,180 @@
+import React from "react";
+import { app } from "../../../ApplicationServices/Application";
+import { KeyBoard } from "../../../Common/KeyEnum";
+import { Classes, Card, Intent, Button, Checkbox, Divider } from "@blueprintjs/core";
+import { ModalHeader, ModalFooter } from "../../../UI/Components/Modal/ModalContainer";
+import { UserConfig, IConfigOption } from "../../../UI/Components/Board/UserConfig";
+import { IConfigStore } from "../../../UI/Store/BoardStore";
+import { BoardModalType } from "../../../UI/Components/Board/BoardModal";
+import { observable, toJS } from "mobx";
+import { observer } from "mobx-react";
+import { safeEval } from "../../../Common/eval";
+import '../Modals/Curve2Rec.less';
+import { begin } from "xaop";
+import { ToasterInput, AppToaster } from "../../../UI/Components/Toaster";
+import { CheckObjectType } from "../../../Common/CheckoutVaildValue";
+import { IBaseOption } from "../../../UI/Store/BoardInterface";
+import { DataAdapter } from "../../../Common/DataAdapter";
+import { DefaultCurve2RecOption } from "../../../Editor/DefaultConfig";
+import { Singleton } from "../../../Common/Singleton";
+
+export interface Curve2RecOption extends IBaseOption
+{
+ isSaveMax: boolean; //保留最大轮廓
+ isSaveSmall: boolean; //保留小于width的轮廓
+ width: number; //保留轮廓的宽度
+ isAnaly: boolean; //分析相交区域
+ gap: number; //最小间隙
+}
+export class Curve2RecModalStore extends Singleton implements IConfigStore
+{
+ @observable configName = "默认";
+ @observable configsNames: string[] = [];
+ @observable option: Curve2RecOption = { ...DefaultCurve2RecOption };
+ protected uiOption;
+ get UIOption()
+ {
+ if (!this.uiOption)
+ this.uiOption = DataAdapter.ConvertUIData(this.option);
+ return this.uiOption;
+ }
+ InitOption = () =>
+ {
+ this.option = {
+ isSaveMax: false,
+ isSaveSmall: true,
+ width: 90,
+ isAnaly: true,
+ gap: 3
+ };
+ if (this.uiOption)
+ Object.assign(this.uiOption, DataAdapter.ConvertUIData(this.option));
+ };
+ SaveConfig = () =>
+ {
+ let newConfig: IConfigOption = {};
+ newConfig.option = toJS(this.option);
+ return newConfig;
+ };
+ UpdateOption = (cof: IConfigOption) =>
+ {
+ Object.assign(this.option, cof.option);
+ if (this.uiOption)
+ Object.assign(this.uiOption, DataAdapter.ConvertUIData(cof.option));
+ };
+ HasInvailValue = () =>
+ {
+ //如果为空或NaN: 有非法值
+ return (this.uiOption.gap === "" || isNaN(this.uiOption.gap)) || (this.uiOption.width === "" || isNaN(this.uiOption.width));
+ };
+ Cancel()
+ {
+ app.Editor.ModalManage.Destory();
+ }
+ OnOk()
+ {
+ let invail = this.HasInvailValue();
+ if (invail)
+ AppToaster.show({ message: "存在无效数值,请修正!", timeout: 1000, intent: "danger" });
+ else
+ app.Editor.ModalManage.DestoryAndExec();
+ }
+}
+@observer
+export class Curve2RecModal extends React.Component<{ store: Curve2RecModalStore; }, {}>{
+ private removeFuncs: Function[] = []; //移除注入
+ componentDidMount()
+ {
+ this.removeFuncs.push(
+ begin(app.Editor.ModalManage, app.Editor.ModalManage.OnKeyDown, (e: KeyboardEvent) =>
+ {
+ if (e.keyCode === KeyBoard.Enter || e.keyCode === KeyBoard.Space)
+ this.props.store.OnOk();
+ e.stopPropagation();
+ return true;
+ })
+ );
+ }
+ render()
+ {
+ const store = this.props.store;
+ const option = store.option;
+ return (
+
+
+
{ this.props.store.Cancel(); }} />
+
+
+
{ this.props.store.option.isSaveMax = !this.props.store.option.isSaveMax; }}
+ >
+
+
+
{ this.props.store.option.isSaveSmall = !this.props.store.option.isSaveSmall; }}>
+
+
+ 只保留宽度小于
+
+ {
+ console.log(e.target.value.trim());
+ if (e.target.value === "")
+ {
+ this.props.store.option.width = 0;
+ return;
+ }
+ let num = safeEval(e.target.value.trim());
+ this.props.store.option.width = (!isNaN(num)) ? num : undefined;
+ }}
+ />
+ 的轮廓
+
+
+
+
{ this.props.store.option.isAnaly = !this.props.store.option.isAnaly; }}
+ >
+
+
+
分析相交区域
+
+ 删除重复线和延伸最小间隙
+
+ {
+ let num = safeEval(e.target.value.trim());
+ this.props.store.option.gap = (!isNaN(num)) ? num : undefined;
+ }}
+ />
+
+
+
+
+
+ {/* 配置 */}
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/Add-on/twoD2threeD/Modals/Rec2Br.less b/src/Add-on/twoD2threeD/Modals/Rec2Br.less
new file mode 100644
index 000000000..48e0a93c6
--- /dev/null
+++ b/src/Add-on/twoD2threeD/Modals/Rec2Br.less
@@ -0,0 +1,67 @@
+@noteInputHeight: 2.5rem;
+
+#rec2Br {
+ .boardSize {
+ .bp3-input {
+ width: 4rem;
+ }
+ }
+ .undefined.br-set{
+ >span:first-child{
+ width: 4.5rem;
+ }
+ }
+ .select-drillType
+ {
+ width: 9rem;
+ }
+ .note-card {
+ padding: 10px !important;
+
+ /* 备注信息 */
+ .notes {
+ width: 20vmin;
+
+ .bp3-input {
+ height: @noteInputHeight;
+ }
+
+ .bp3-label {
+ line-height: @noteInputHeight;
+ }
+
+ >div {
+ display: flex;
+
+ input:hover {
+ background: #ccc;
+ }
+
+ >label {
+ height: @noteInputHeight;
+ line-height: @noteInputHeight;
+ }
+ }
+ }
+ }
+
+ .boardModel {
+ display: inline-block;
+ width: 35px;
+ height: 50px;
+ border: 1px solid #000;
+ vertical-align: middle;
+ margin: 10px;
+ }
+}
+
+/* 备注信息 */
+#rec2Br .notes>div>label:first-child,
+#rec2Br .notes>div>input:first-child {
+ width: 30%;
+}
+
+#rec2Br .notes>div>label:last-child,
+#rec2Br .notes>div>input:last-child {
+ width: 70%;
+}
diff --git a/src/Add-on/twoD2threeD/Modals/Rec2Br.tsx b/src/Add-on/twoD2threeD/Modals/Rec2Br.tsx
new file mode 100644
index 000000000..9c4b80591
--- /dev/null
+++ b/src/Add-on/twoD2threeD/Modals/Rec2Br.tsx
@@ -0,0 +1,519 @@
+import { Button, Card, Checkbox, Classes, H5, HTMLSelect, Radio, RadioGroup } from '@blueprintjs/core';
+import { observable } from 'mobx';
+import { observer } from 'mobx-react';
+import React from 'react';
+import { Vector3 } from 'three';
+import { begin } from 'xaop';
+import { app } from '../../../ApplicationServices/Application';
+import { EBoardKeyList } from '../../../Common/BoardKeyList';
+import { CheckObjectType } from '../../../Common/CheckoutVaildValue';
+import { KeyBoard } from '../../../Common/KeyEnum';
+import { ToFixed } from '../../../Common/Utils';
+import { Board } from '../../../DatabaseServices/Entity/Board';
+import { TemplateRecord } from '../../../DatabaseServices/Template/TemplateRecord';
+import { PromptStatus } from '../../../Editor/PromptResult';
+import { userConfig } from '../../../Editor/UserConfig';
+import { Input5Or4Component, Notes, SetBoardDataBlock, SetBoardDataItem } from '../../../UI/Components/Board/BoardCommon';
+import { BoardModalType } from '../../../UI/Components/Board/BoardModal';
+import { ModalFooter, ModalHeader } from '../../../UI/Components/Modal/ModalContainer';
+import { ModalState } from '../../../UI/Components/Modal/ModalInterface';
+import { ToasterInput } from '../../../UI/Components/Toaster';
+import { GoodsList, IGoodInfo } from '../../../UI/MaterialEditor/GoodsList';
+import '../Modals/Rec2Br.less';
+import { Rec2BrStore } from '../Rec2BrStore';
+enum GetOptionState
+{
+ GetDistance = 0,
+ GetBoard = 1,
+ GetTemplate = 2,
+}
+@observer
+export class Rec2BrModal extends React.Component<{ store: Rec2BrStore; }, {}>
+{
+ private showShops = observable.box(false);
+ private startSelect = observable.box(false);
+ private m_ScaleParameter = [
+ ["knifeRadius", "刀半径"],
+ ["grooveAddLength", "槽加长"], ["grooveAddWidth", "槽加宽"], ["grooveAddDepth", "槽加深"]
+ ];
+ private matPars = [
+ ["boardMatName", "板材名"], ["material", "材料"], ["color", "颜色"]
+ ];
+ private removeFuncs: Function[] = []; //移除注入
+ componentDidMount()
+ {
+ this.removeFuncs.push(
+ begin(app.Editor.ModalManage, app.Editor.ModalManage.OnKeyDown, (e: KeyboardEvent) =>
+ {
+ if (e.keyCode === KeyBoard.Enter || e.keyCode === KeyBoard.Space)
+ this.props.store.OnOk(ModalState.Ok);
+ e.stopPropagation();
+ return true;
+ })
+ );
+ }
+ componentWillUnmount()
+ {
+ for (let f of this.removeFuncs)
+ f();
+ this.removeFuncs.length = 0;
+ }
+ //拾取 距离 板件 模板
+ private getOption = async (state: GetOptionState) =>
+ {
+ app.Editor.ModalManage.ToggleShow();
+ app.Editor.MaskManage.Clear();
+ switch (state)
+ {
+ case GetOptionState.GetBoard:
+ let enRes1 = await app.Editor.GetEntity({
+ Msg: "选择板件",
+ Filter: { filterTypes: [Board] }
+ });
+ if (enRes1.Status === PromptStatus.OK)
+ {
+ const data = (enRes1.Entity as Board).BoardProcessOption;
+ this.props.store.m_Option.roomName = data[EBoardKeyList.RoomName];
+ this.props.store.UIOption.roomName = data[EBoardKeyList.RoomName];
+ }
+ break;
+ case GetOptionState.GetDistance:
+ let basept: Vector3;
+ let ptRes = await app.Editor.GetPoint({ Msg: "点取距离或者输入" });
+ if (ptRes.Status === PromptStatus.OK)
+ basept = ptRes.Point;
+
+ let enRes2 = await app.Editor.GetDistance({
+ Msg: "点取距离或者输入",
+ BasePoint: basept,
+ Default: 1
+ });
+ if (enRes2.Status === PromptStatus.OK)
+ {
+ let dis = ToFixed(enRes2.Distance, 2);
+ this.props.store.m_Option.cabinetDeep = parseFloat(dis);
+ this.props.store.UIOption.cabinetDeep = dis;
+ }
+ break;
+ case GetOptionState.GetTemplate:
+ let enRes3 = await app.Editor.GetSelection({
+ Msg: "选择模板",
+ Once: true,
+ Filter: { filterFunction: (obj, ent) => { return ent.Template !== undefined; } }
+ });
+ if (enRes3.Status === PromptStatus.OK)
+ {
+ let ens = enRes3.SelectSet.SelectEntityList;
+
+ let template = ens[0].Template.Object as TemplateRecord;
+ this.props.store.m_Option.backBrTemplate = template.Root;
+ }
+ break;
+ default: break;
+ }
+ app.Editor.MaskManage.ShowMask();
+ app.Editor.ModalManage.ToggleShow();
+ };
+ private startSelectTemplate = () =>
+ {
+ this.startSelect.set(true);
+ };
+ private selectGoods = (good: IGoodInfo) =>
+ {
+ this.props.store.m_Option.boardMatName = good.name;//是否需要更新UIOption
+ this.props.store.m_Option.material = good.material;
+ this.props.store.m_Option.color = good.color;
+ this.showShops.set(false);
+ };
+ render()
+ {
+ const store = this.props.store;
+ const selectOptions = userConfig.DrillConfigs.size > 0 ? [...userConfig.DrillConfigs.keys(), "不排"] : [];
+ return (
+
+
+
{ store.OnOk(ModalState.Cancel); }}
+ />
+
+
+
+ 柜体参数
+
+
+
+
+
+
this.getOption(GetOptionState.GetDistance)}
+ >拾取
+
+
+
+ 背板规则
+
+
+
+
+
+
+ 前缩
+
+
+
+ 解析板件名称与板件内缩
+
+
+
+ {
+ store.m_Option.isfarLeftVerticalBrName = !store.m_Option.isfarLeftVerticalBrName;
+ store.UIOption.isfarLeftVerticalBrName = store.m_Option.isfarLeftVerticalBrName;
+ }}
+ />
+
+
+
+
+ {
+ store.m_Option.isfarRightVerticalBrName = !store.m_Option.isfarRightVerticalBrName;
+ store.UIOption.isfarRightVerticalBrName = store.m_Option.isfarRightVerticalBrName;
+ }}
+ />
+
+
+
+
+ {
+ store.m_Option.istopMostLayerBrName = !store.m_Option.istopMostLayerBrName;
+ store.UIOption.istopMostLayerBrName = store.m_Option.istopMostLayerBrName;
+ }}
+ />
+
+
+
+
+ {
+ store.m_Option.isbottomMostLayerBrName = !store.m_Option.isbottomMostLayerBrName;
+ store.UIOption.isbottomMostLayerBrName = store.m_Option.isbottomMostLayerBrName;
+ }}
+ />
+
+
+
+
+ {
+ store.m_Option.isbottomMostBackBrName = !store.m_Option.isbottomMostBackBrName;
+ store.UIOption.isbottomMostBackBrName = store.m_Option.isbottomMostBackBrName;
+ }}
+ />
+
+
+
+
+ {
+ store.m_Option.isstripeBrName = !store.m_Option.isstripeBrName;
+ store.UIOption.isstripeBrName = store.m_Option.isstripeBrName;
+ }}
+ />
+
+
+
+
+ 根据背板区分柜体
+
+
+ {
+ store.m_Option.isMultiBackBr = e.currentTarget.value === "1";
+ store.UIOption.isMultiBackBr = store.m_Option.isMultiBackBr;
+ }}
+ >
+
+
+
+
+
+
+
+ {
+ store.m_Option.iscabinetName = !store.m_Option.iscabinetName;
+ store.UIOption.iscabinetName = store.m_Option.iscabinetName;
+ }}
+ />
+
+
+
+
+
+ 材料信息
+
+
+
+
+
+
+
this.getOption(GetOptionState.GetBoard)}
+ >拾取
+
+
+
+ this.showShops.set(true)}
+ >选择
+ {
+ this.showShops.get() &&
+ }
+
+
+
+ 封边
+
+ {
+
+ }
+
+ 备注
+
+
+
+
+
+
+ {
+ store.m_Option.backBrUseTemplate = !store.m_Option.backBrUseTemplate;
+ store.UIOption.backBrUseTemplate = store.m_Option.backBrUseTemplate;
+ }}
+ />
+ this.getOption(GetOptionState.GetTemplate)}
+ >选择模板
+
+
+
+
+ {/*
+
+ */}
+
+
+
+
+ {
+ if (this.props.store.m_Option.drillType === "")
+ this.props.store.m_Option.drillType = selectOptions[0];
+ store.OnOk(ModalState.Ok);
+ }}
+ />
+ store.OnOk(ModalState.Cancel)}
+ />
+
+
+ {/* {
+ this.startSelect.get() &&
+
+ } */}
+
+ );
+ }
+}
diff --git a/src/Add-on/twoD2threeD/ParseBoardData.ts b/src/Add-on/twoD2threeD/ParseBoardData.ts
new file mode 100644
index 000000000..94c9fdb71
--- /dev/null
+++ b/src/Add-on/twoD2threeD/ParseBoardData.ts
@@ -0,0 +1,62 @@
+import { Polyline } from "../../DatabaseServices/Entity/Polyline";
+import { Board, BoardType } from "../../DatabaseServices/Entity/Board";
+import { Matrix4, Vector3, Box3 } from "three";
+import { BoardGetFace } from "../../Geometry/DrillParse/BoardGetFace";
+
+export class ParseBoardData
+{
+ pl: Polyline;
+ faceList: BoardGetFace;//
+ board: Board;
+ constructor(br: Board, fontMatInv?: Matrix4)
+ {
+ if (!fontMatInv)
+ //求前视图的正矩阵和逆矩阵//?
+ fontMatInv = br.SpaceOCSInv;
+ this.Parse(br, fontMatInv);
+ }
+ IsCollisionTo(br: ParseBoardData)
+ {
+ let faceList = this.faceList.IntersectFace(br.faceList);
+ return !(faceList.length === 0);
+ }
+ isIntersectOrContain(br: ParseBoardData)
+ {
+ let box1 = this.pl.BoundingBox;
+ let box2 = br.pl.BoundingBox;
+ if (box1.intersectsBox(box2))
+ {
+ let unionBoxs = new Box3();//?
+ unionBoxs.union(box1);
+ unionBoxs.union(box2);
+ let vec = unionBoxs.max.clone().sub(unionBoxs.min);
+ return vec.x > 1 && vec.y > 1;
+ }
+ else
+ return false;
+ }
+ private Parse(br: Board, fontMatInv: Matrix4)
+ {
+ this.board = br;
+ this.faceList = new BoardGetFace(br);//?
+ let brminpt = br.MinPoint;
+ brminpt.applyMatrix4(fontMatInv);
+ brminpt.setZ(0);
+ let addVec: Vector3;
+ switch (br.BoardType)
+ {
+ case BoardType.Layer:
+ addVec = new Vector3(br.Height, br.Thickness);//?
+ break;
+ case BoardType.Vertical:
+ addVec = new Vector3(br.Thickness, br.Height);//?
+ break;
+ case BoardType.Behind:
+ addVec = new Vector3(br.Width, br.Height);//?
+ break;
+ default:
+ break;
+ }
+ this.pl = new Polyline().RectangleFrom2Pt(brminpt, brminpt.clone().add(addVec));
+ }
+}
diff --git a/src/Add-on/twoD2threeD/ParseBoardNameUtil.ts b/src/Add-on/twoD2threeD/ParseBoardNameUtil.ts
new file mode 100644
index 000000000..830fe71fc
--- /dev/null
+++ b/src/Add-on/twoD2threeD/ParseBoardNameUtil.ts
@@ -0,0 +1,482 @@
+import { Board, BoardType } from "../../DatabaseServices/Entity/Board";
+import { Vector3, Matrix4, Box3 } from "three";
+import { equaln, isParallelTo } from "../../Geometry/GeUtils";
+import { ParseBoardData } from "./ParseBoardData";
+import { EBoardKeyList } from "../../Common/BoardKeyList";
+import { DrillType, LinesType, FaceDirection } from "../../UI/Store/BoardInterface";
+import { app } from "../../ApplicationServices/Application";
+import { Rec2BrStore } from "./Rec2BrStore";
+
+/*解析板件名称 根据板件的位置.
+可能出现 顶底板.
+地脚线.
+左侧版
+右侧板.
+*/
+interface BrLocDataValue
+{
+ vec: Vector3;
+ br: Board;
+}
+enum SortType
+{
+ x = "x",
+ xdesc = "X",
+ y = "y",
+ ydesc = "Y",
+ z = "z",
+ zdesc = "Z",
+}
+enum ParseType //解析类型. 整体解析 根据背板
+{
+ ParseAll = 0,
+ ParseFormBackBoard = 1
+}
+export class ParseBoardNameUtil
+{
+ isParseLeftBr: boolean;//左侧版.
+ isParseRightBr: boolean;//右侧版.
+ isParseTopBr: boolean;//顶板
+ isParseDownBr: boolean;//底板.
+ isParseGroundLineBr: boolean;//地脚.
+ isParseStripeBr: boolean;//收口条
+ isChangeCabinetName: boolean;//修改柜体名称
+ leftBrName: string;
+ rightBrName: string;
+ topBrName: string;
+ downBrName: string;
+ groundLineBrName: string;
+ stripeBrName: string;
+ cabinetName: string;//柜体名称
+ roomName: string;//房间名称
+ topBrShrink: number;//顶底板 立板层板前缩
+ downBrShrink: number;
+ verticalBrShrink: number;
+ layerBrShrink: number;
+ groundLineBrMoveBack: number;//地脚线往后面移动
+ //背板大小
+ backBrLength: number;
+ backBrWidth: number;
+ parseType: ParseType;
+ constructor(store: Rec2BrStore)
+ {
+ this.InitData(store);
+ }
+ InitData(store: Rec2BrStore)
+ {
+ let option = store.m_Option;
+ this.isParseLeftBr = option.isfarLeftVerticalBrName;
+ this.leftBrName = option.farLeftVerticalBrName;
+ this.isParseRightBr = option.isfarRightVerticalBrName;
+ this.rightBrName = option.farRightVerticalBrName;
+ this.isParseTopBr = option.istopMostLayerBrName;
+ this.topBrName = option.topMostLayerBrName;
+ this.isParseDownBr = option.isbottomMostLayerBrName;
+ this.downBrName = option.bottomMostLayerBrName;
+ this.isParseStripeBr = option.isstripeBrName;
+ this.stripeBrName = option.stripeBrName;
+ this.isParseGroundLineBr = option.isbottomMostBackBrName;
+ this.groundLineBrName = option.bottomMostBackBrName;
+ this.roomName = option.roomName;
+ this.backBrLength = option.backBrBiggerThanHeight;//
+ this.backBrWidth = option.backBrBiggerThanWidth;//
+ this.parseType = option.isMultiBackBr ? ParseType.ParseFormBackBoard : ParseType.ParseAll;
+ this.verticalBrShrink = option.verticalBrShrink;
+ this.layerBrShrink = option.layerBrShrink;
+ this.isChangeCabinetName = option.iscabinetName;
+ this.cabinetName = option.cabinetName;
+ this.topBrShrink = option.topBrShrink;
+ this.downBrShrink = option.bottomBrShrink;
+ this.groundLineBrMoveBack = option.groundLineBrShrink;
+ }
+ ParseBoardNameFormLocation(brarr: Board[])
+ {
+ if (brarr.length < 1)
+ return;
+ //板件位置数据.
+ let boardLocData: Map = new Map();
+ let brSpcMatInv = brarr[0].SpaceOCSInv;
+ //构建数据
+ for (let br of brarr)
+ {
+ if (br.BoardType === BoardType.Behind)
+ {
+ if (br.Height > this.backBrLength && br.Width > this.backBrWidth)
+ {
+ this.ChangeBeiBan(br);
+ continue;
+ }
+ }
+ let basePt = br.Position;
+ basePt.applyMatrix4(brSpcMatInv);
+ let arr = boardLocData.get(br.BoardType);
+ if (!arr)
+ boardLocData.set(br.BoardType, [{ vec: basePt, br: br }]);
+ else
+ {
+ arr.push({ vec: basePt, br: br });
+ boardLocData.set(br.BoardType, arr);
+ }
+ }
+ //解析与设置柜体名称.
+ let FuncParseBrName = (bool: boolean, name: string, sortType: SortType, boardType: BoardType) =>
+ {
+ if (bool)
+ {
+ let br = this.FindBr(boardLocData, sortType, boardType);
+ if (br)
+ br.Name = name;
+ }
+ };
+ FuncParseBrName(this.isParseLeftBr, this.leftBrName, SortType.x, BoardType.Vertical);
+ //左侧版修改孔面
+ if (this.isParseLeftBr && (boardLocData.get(BoardType.Vertical)?.length > 0))
+ this.ChangeZuoCeBan(boardLocData.get(BoardType.Vertical)[0].br);
+ FuncParseBrName(this.isParseRightBr, this.rightBrName, SortType.xdesc, BoardType.Vertical);
+ //右侧版修改孔面
+ if (this.isParseRightBr && (boardLocData.get(BoardType.Vertical)?.length > 0))
+ this.ChangeYouCeBan(boardLocData.get(BoardType.Vertical)[0].br);
+ FuncParseBrName(this.isParseTopBr, this.topBrName, SortType.zdesc, BoardType.Layer);
+ //顶版修改孔面
+ if (this.isParseTopBr && (boardLocData.get(BoardType.Layer)?.length > 0))
+ {
+ let arr = boardLocData.get(BoardType.Layer);
+ let z = arr[0].vec.z;
+ for (let data of arr)
+ {
+ if (equaln(z, data.vec.z, 1e-2))
+ this.ChangeDingBan(data.br);
+ else
+ break;
+ }
+ }
+ FuncParseBrName(this.isParseDownBr, this.downBrName, SortType.z, BoardType.Layer);
+ //底板
+ if (this.isParseDownBr && (boardLocData.get(BoardType.Layer)?.length > 0))
+ {
+ let arr = boardLocData.get(BoardType.Layer);
+ let z = arr[0].vec.z;
+ for (let data of arr)
+ {
+ if (equaln(z, data.vec.z, 1e-2))
+ this.ChangeDiBan(data.br);
+ else
+ break;
+ }
+ }
+ //地脚
+ FuncParseBrName(this.isParseGroundLineBr, this.groundLineBrName, SortType.z, BoardType.Behind);
+ //地脚线处理
+ if (this.isParseGroundLineBr && (boardLocData.get(BoardType.Behind)?.length > 0))
+ {
+ let arr = boardLocData.get(BoardType.Behind);
+ let z = arr[0].vec.z;
+ for (let data of arr)
+ {
+ if (equaln(z, data.vec.z, 1e-2))
+ {
+ let br = data.br;
+ if (br.Height < br.Width)
+ this.ChangeDiJiao(br);
+ }
+ else
+ break;
+ }
+ }
+ this.CurtailBoard(boardLocData, brarr);
+ }
+ //分堆 来自背板
+ //解析板件数组.
+ ParseBoardArrFormBackBoard(arr: Board[][], brarr: Board[])
+ {
+ if (brarr.length === 0)
+ return;
+
+ let parseBrData: ParseBoardData[] = [];
+ let backBrData: ParseBoardData[] = [];
+ for (let br of brarr)
+ {
+ let data = new ParseBoardData(br, brarr[0].SpaceOCSInv);
+ if (br.BoardType === BoardType.Behind && br.Width > this.backBrWidth && br.Height > this.backBrLength)
+ {
+ backBrData.push(data);
+ }
+ else
+ parseBrData.push(data);
+ }
+ for (let bdata of backBrData)
+ {
+ let ar: Board[] = [];
+ ar.push(bdata.board);//加入背板???
+ for (let pdata of parseBrData)
+ {
+ if (bdata.isIntersectOrContain(pdata) || bdata.IsCollisionTo(pdata))
+ {
+ ar.push(pdata.board);
+ continue;
+ }
+ }
+ arr.push(ar);
+ }
+ }
+ Doit(brarr: Board[])
+ {
+ this.InitBoardName(brarr);
+ let boardarrHeap: Board[][] = [];
+ this.ParseBoardArrFormBackBoard(boardarrHeap, brarr);
+ // console.log(boardarrHeap, brarr);
+ if (boardarrHeap.length === 0)
+ boardarrHeap.push(brarr);
+ this.ParseShouKouTiao(brarr, boardarrHeap);
+ switch (this.parseType)
+ {
+ case ParseType.ParseAll://?单背板柜子
+ let calbrarr: Board[] = [];
+ for (let brarr of boardarrHeap)
+ {
+ calbrarr.push(...brarr);
+ }
+ this.ParseBoardNameFormLocation(calbrarr);
+ for (let br of calbrarr)
+ {
+ br.BoardProcessOption[EBoardKeyList.CabinetName] = this.cabinetName;
+ br.BoardProcessOption[EBoardKeyList.RoomName] = this.roomName;
+ }
+ break;
+ case ParseType.ParseFormBackBoard://?多背板柜子
+ let index = 1;
+ for (let brarr of boardarrHeap)
+ {
+ this.ParseBoardNameFormLocation(brarr);
+ for (let br of brarr)
+ {
+ br.BoardProcessOption[EBoardKeyList.CabinetName] = this.cabinetName + index;//?
+ br.BoardProcessOption[EBoardKeyList.RoomName] = this.roomName;//
+ }
+ index++;
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+ private FindBr(map: Map, sortType: SortType, boardType: BoardType)//
+ {
+ let arr = map.get(boardType);
+ if (arr && arr.length > 0)
+ {
+ switch (sortType.toUpperCase())
+ {
+ case "X":
+ arr.sort((a, b) => sortType === SortType.x ? a.vec.x - b.vec.x : b.vec.x - a.vec.x);
+ break;
+ case "Y":
+ arr.sort((a, b) => sortType === SortType.y ? a.vec.y - b.vec.y : b.vec.y - a.vec.y);
+ break;
+ case "Z":
+ arr.sort((a, b) => sortType === SortType.z ? a.vec.z - b.vec.z : b.vec.z - a.vec.z);
+ break;
+ }
+ return arr[0].br;
+ }
+ }
+ //修改地脚线 前缩和改变纹路
+ private ChangeDiJiao(br: Board)
+ {
+ br.Name = this.groundLineBrName;
+ br.BoardProcessOption[EBoardKeyList.BigHole] = FaceDirection.Back;
+ br.BoardProcessOption[EBoardKeyList.Lines] = LinesType.Reverse;
+ let n = br.Normal.clone().multiplyScalar(this.groundLineBrMoveBack);
+ let moveMatrix = new Matrix4().setPosition(br.Normal.clone().sub(n));
+ br.ApplyMatrix(moveMatrix);
+ }
+ //左侧板
+ private ChangeZuoCeBan(br: Board)
+ {
+ br.BoardProcessOption[EBoardKeyList.BigHole] = FaceDirection.Front;
+ br.BoardProcessOption[EBoardKeyList.Lines] = LinesType.Positive;
+ }
+ //右侧板
+ private ChangeYouCeBan(br: Board)
+ {
+ br.BoardProcessOption[EBoardKeyList.BigHole] = FaceDirection.Back;
+ br.BoardProcessOption[EBoardKeyList.Lines] = LinesType.Positive;
+ }
+ //顶板
+ private ChangeDingBan(br: Board)
+ {
+ br.Name = this.topBrName;
+ br.BoardProcessOption[EBoardKeyList.BigHole] = FaceDirection.Front;
+ br.BoardProcessOption[EBoardKeyList.Lines] = LinesType.Positive;
+ }
+ //底板
+ private ChangeDiBan(br: Board)
+ {
+ br.Name = this.downBrName;
+ br.BoardProcessOption[EBoardKeyList.BigHole] = FaceDirection.Back;
+ br.BoardProcessOption[EBoardKeyList.Lines] = LinesType.Positive;
+ }
+ //背板
+ private ChangeBeiBan(br: Board)
+ {
+ br.BoardProcessOption[EBoardKeyList.BigHole] = FaceDirection.Back;
+ br.BoardProcessOption[EBoardKeyList.Lines] = LinesType.Positive;
+ }
+ private CurtailBoard(map: Map, brarr: Board[])//
+ {
+ //todo地脚
+ let cbarr: Board[] = [];
+ let lbarr: Board[] = [];
+ let map1 = map.get(BoardType.Vertical);
+ let map0 = map.get(BoardType.Layer);
+ if (map1)
+ for (let lb of map1)
+ {
+ if (lb.br.Name === "立板")
+ {
+ lb.br.BoardProcessOption[EBoardKeyList.Lines] = LinesType.Positive;
+ lb.br.BoardProcessOption[EBoardKeyList.BigHole] = FaceDirection.Back;
+ // lb.br//纹路 正
+ //孔面 反
+ lbarr.push(lb.br);
+ }
+ }
+ let topbrarr: Board[] = [];
+ let downbrarr: Board[] = [];
+ if (map0)
+ for (let cb of map0)
+ {
+ let name = cb.br.Name;
+ switch (name)
+ {
+ case "层板":
+ cb.br.BoardProcessOption[EBoardKeyList.Lines] = LinesType.Positive;
+ cb.br.BoardProcessOption[EBoardKeyList.BigHole] = FaceDirection.Back;
+ //正纹
+ //孔面 反
+ cbarr.push(cb.br);
+ break;
+ case "顶板":
+ topbrarr.push(cb.br);
+ break;
+ case "底板":
+ downbrarr.push(cb.br);
+ break;
+ }
+ }
+ this.FrontCurtailBrs(topbrarr, this.topBrShrink, "顶板");
+ this.FrontCurtailBrs(downbrarr, this.downBrShrink, "底板");
+ this.FrontCurtailBrs(lbarr, this.verticalBrShrink, "立板");
+ this.FrontCurtailBrs(cbarr, this.layerBrShrink, "层板");
+ }
+ //计算收口条
+ private ParseShouKouTiao(brarr: Board[], calbrarr: Board[][])
+ {
+ if (!this.isParseStripeBr)
+ return;
+ for (let calbrs of calbrarr)//boardHeap
+ {
+ for (let calbr of calbrs)
+ {
+ let index = brarr.findIndex((br) => br.Id.Index === calbr.Id.Index);
+ brarr.splice(index, 1);
+ }
+ }
+
+ for (let br of brarr)
+ {
+ br.Name = this.stripeBrName;
+ br.BoardProcessOption[EBoardKeyList.DrillType] = DrillType.None;
+ if (br.Height > br.Width)
+ br.BoardProcessOption[EBoardKeyList.Lines] = LinesType.Positive;
+ else
+ br.BoardProcessOption[EBoardKeyList.Lines] = LinesType.Reverse;
+ }
+ }
+ private InitBoardName(brarr: Board[])
+ {
+ for (let br of brarr)
+ {
+ switch (br.BoardType)
+ {
+ case BoardType.Layer:
+ br.Name = "层板";
+ break;
+ case BoardType.Vertical:
+ br.Name = "立板";
+ break;
+ case BoardType.Behind:
+ br.Name = "背板";
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ //前缩
+ private FrontCurtailBrs(brs: Board[], curtailNum: number, promptMsg: string)
+ {
+ if (brs.length < 1)
+ return;
+ let sizeBox = new Box3();
+ let spaceOcsInv = brs[0].SpaceOCSInv;
+ let boardBoxCache = new Map();
+ for (let br of brs)
+ {
+ let box = br.BoundingBoxInOCS;
+ box.applyMatrix4(spaceOcsInv.clone().multiply(br.OCS));
+ boardBoxCache.set(br, { box, size: box.getSize(new Vector3) });
+ sizeBox.union(box);
+ }
+
+ //构造前缩盒子
+ const GenCurtailBox = (sizeBox: Box3): Box3 =>
+ {
+ let size = sizeBox.getSize(new Vector3);
+ let v = new Vector3(1, 1, 1);
+ let frontBox = new Box3(sizeBox.min.clone().sub(v), sizeBox.max.clone().sub(new Vector3(-1, size.y / 2, -1)));
+ return frontBox;
+ };
+
+ let curBox = GenCurtailBox(sizeBox);
+
+ let index = 1;
+ let allowDist = 0;
+ let minDist = Infinity;
+ for (let br of brs)
+ {
+ let boxData = boardBoxCache.get(br);
+ if (boxData.box.intersectsBox(curBox)
+ && !curBox.containsBox(boxData.box)
+ && !isParallelTo(br.Normal, new Vector3().setComponent(index, 1))
+ )
+ {
+ minDist = Math.min(minDist, boxData.box.max.getComponent(index) - boxData.box.min.getComponent(index));
+ }
+ allowDist = minDist - 1;
+ }
+ if (curtailNum > allowDist)
+ {
+ app.Editor.Prompt(`${promptMsg}的前缩值不得超过${allowDist}`);
+ return;
+ }
+
+ for (let br of brs)
+ {
+ //逐板计算拉伸盒子
+ let curBox2 = GenCurtailBox(br.BoundingBox);
+ let pts = br.GetStretchPoints();
+ let curtailVec = new Vector3(0, curtailNum);
+ let curtailIndexs = [];
+ //获取索引
+ for (let i = 0, length = pts.length; i < length; i++)
+ {
+ if (curBox2.containsPoint(pts[i]))
+ curtailIndexs.push(i);
+ }
+ if (!isParallelTo(curtailVec, br.Normal))
+ br.MoveStretchPoints(curtailIndexs, curtailVec);
+ }
+ }
+}
diff --git a/src/Add-on/twoD2threeD/Rec2BrStore.ts b/src/Add-on/twoD2threeD/Rec2BrStore.ts
new file mode 100644
index 000000000..e09feae55
--- /dev/null
+++ b/src/Add-on/twoD2threeD/Rec2BrStore.ts
@@ -0,0 +1,148 @@
+import { BoardStore } from "../../UI/Store/BoardStore";
+import { IBaseOption, IGrooveOption } from "../../UI/Store/BoardInterface";
+import { CheckoutValid, CheckObjectType } from "../../Common/CheckoutVaildValue";
+import { DataAdapter } from "../../Common/DataAdapter";
+import { TemplateRecord } from "../../DatabaseServices/Template/TemplateRecord";
+import { observable } from "mobx";
+
+export class Rec2BrStore extends BoardStore
+{
+ @observable m_Option: IRec2BrOption;
+ protected m_UiOption;
+ constructor()
+ {
+ super();
+ this.InitOption();
+ }
+ private GetDefaultOption()
+ {
+ let originOption: IRec2BrOption = {
+ cabinetDeep: 400,
+ cabinetBrThick: 18,
+ cabinetCurtail: 0,
+ backBrThick: 18,
+ backBrBiggerThanHeight: 200,
+ backBrBiggerThanWidth: 200,
+ backBrFrontMove: 0,
+ backBrLeftExtend: 0,
+ backBrRightExtend: 0,
+ backBrUpExtend: 0,
+ backBrDownExtend: 0,
+ verticalBrShrink: 0,
+ layerBrShrink: 0,
+ topBrShrink: 0,
+ bottomBrShrink: 0,
+ groundLineBrShrink: 0,
+ farLeftVerticalBrName: "左侧板",
+ farRightVerticalBrName: "右侧板",
+ topMostLayerBrName: "顶板",
+ bottomMostLayerBrName: "底板",
+ bottomMostBackBrName: "地脚线",
+ stripeBrName: "收口条",
+ cabinetName: "",
+ isfarLeftVerticalBrName: true,//最左侧立板名称
+ isfarRightVerticalBrName: true,
+ istopMostLayerBrName: true,
+ isbottomMostLayerBrName: true,
+ isbottomMostBackBrName: true,
+ isstripeBrName: true,
+ iscabinetName: false,//修改柜名
+ isMultiBackBr: false,
+ grooveOption: {
+ grooveAddLength: "0",
+ grooveAddWidth: "0",
+ grooveAddDepth: "0",
+ knifeRadius: "3",
+ },
+ roomName: "",
+ boardMatName: "",
+ material: "",
+ color: "",
+ drillType: "",
+ sealedDown: "1",
+ sealedLeft: "1",
+ sealedRight: "1",
+ sealedUp: "1",
+ backBrUseTemplate: false,
+ backBrTemplate: null,
+ remarks: Array.from({ length: 12 }, () => ["", ""]),
+ };
+ return originOption;
+ }
+ InitOption()
+ {
+ this.m_Option = this.GetDefaultOption();
+ if (this.m_UiOption)
+ Object.assign(this.m_UiOption, DataAdapter.ConvertUIData(this.m_Option));
+ }
+ HasInvailValue()
+ {
+ return CheckoutValid.HasInvailValue(this.m_Option, CheckObjectType.R2B);//
+ }
+ // OnOk = () =>
+ // {
+
+ // //检查option 封边排钻等是否有效
+ // };
+}
+
+
+interface IRec2BrOption extends IBaseOption
+{
+ //柜体参数
+ cabinetDeep: number;//柜深
+ cabinetBrThick: number;//板厚
+ cabinetCurtail: number;//柜体内缩
+ //背板规则
+ backBrThick: number;//背板厚
+ backBrBiggerThanHeight: number;//背板大于高
+ backBrBiggerThanWidth: number;//背板大于宽
+ backBrFrontMove: number;//背板前移
+ backBrLeftExtend: number;//背板上下左右延伸
+ backBrRightExtend: number;
+ backBrUpExtend: number;
+ backBrDownExtend: number;
+ //前缩
+ verticalBrShrink: number;//立板 层板 底板 顶板 地脚线前缩
+ layerBrShrink: number;
+ bottomBrShrink: number;
+ topBrShrink: number;
+ groundLineBrShrink: number;
+ //解析板件名
+ farLeftVerticalBrName: string;//最左侧立板名称
+ farRightVerticalBrName: string;
+ topMostLayerBrName: string;
+ bottomMostLayerBrName: string;
+ bottomMostBackBrName: string;
+ stripeBrName: string;
+ cabinetName: string;//修改柜名
+ //以上的checkbox是否选择
+ isfarLeftVerticalBrName: boolean;//最左侧立板名称
+ isfarRightVerticalBrName: boolean;
+ istopMostLayerBrName: boolean;
+ isbottomMostLayerBrName: boolean;
+ isbottomMostBackBrName: boolean;
+ isstripeBrName: boolean;
+ iscabinetName: boolean;//修改柜名
+ isMultiBackBr: boolean;//是否多背板柜子
+ backBrUseTemplate: boolean;
+ backBrTemplate: TemplateRecord;//背板模板
+ //材料信息
+ //刀半径
+ //槽加长
+ //槽加深
+ //槽加宽
+ grooveOption: IGrooveOption;
+ roomName: string;//房间名
+ boardMatName: string;//板材名
+ material: string;//材料
+ color: string;//颜色
+ drillType: string;//排钻
+ //封边
+ sealedDown: string,
+ sealedLeft: string,
+ sealedRight: string,
+ sealedUp: string,
+ //备注
+ remarks: [string, string][];
+}
diff --git a/src/Add-on/twoD2threeD/RectConvertToBr.ts b/src/Add-on/twoD2threeD/RectConvertToBr.ts
new file mode 100644
index 000000000..05d74e0d6
--- /dev/null
+++ b/src/Add-on/twoD2threeD/RectConvertToBr.ts
@@ -0,0 +1,517 @@
+import { Box3, Matrix4, Vector3 } from "three";
+import { app } from "../../ApplicationServices/Application";
+import { arrayRemoveIf } from "../../Common/ArrayExt";
+import { IsDev } from "../../Common/Deving";
+import { safeEval } from "../../Common/eval";
+import { matrixSetVector } from "../../Common/Matrix4Utils";
+import { Board, BoardType } from "../../DatabaseServices/Entity/Board";
+import { Polyline } from "../../DatabaseServices/Entity/Polyline";
+import { TemplateRecord } from "../../DatabaseServices/Template/TemplateRecord";
+import { Command } from "../../Editor/CommandMachine";
+import { PromptStatus } from "../../Editor/PromptResult";
+import { equaln } from "../../Geometry/GeUtils";
+import { IntersectOption } from "../../GraphicsSystem/IntersectWith";
+import { HotCMD } from "../../Hot/HotCommand";
+import { CurveInOCS } from "./CreatePolyLineFromCurve";
+import { Rec2BrModal } from "./Modals/Rec2Br";
+import { Rec2BrStore } from "./Rec2BrStore";
+
+//已经实现:矩形转板件 位置正确 切割正常
+//已处理名称解析,前缩部分,根据背板区分柜体部分 材料信息 备注部分. 未测试
+
+/*
+ 前期
+ pl.TempData = size;
+ pl.TempData2 = box;
+
+ 后期
+ pl.TempData = br;
+*/
+
+@HotCMD
+export class RectConvertToBoard implements Command
+{
+ store = Rec2BrStore.GetInstance() as Rec2BrStore;
+ SpaceOCS: Matrix4;
+ SpaceOCSInv: Matrix4;
+ async exec()
+ {
+ if (!IsDev())
+ alert("收费功能免费试用中!截至至2020年6月1日!");
+ app.Editor.ModalManage.RenderModal(Rec2BrModal, { store: this.store });
+
+ app.Editor.ModalManage.SetCallback(async () =>
+ {
+ this.InitData(this.store);
+
+ let ss = await app.Editor.GetSelection({
+ Msg: "请选择曲线:",
+ UseSelect: true,
+ Filter: { filterTypes: [Polyline] }
+ });
+ if (ss.Status === PromptStatus.Cancel) return;
+
+ let ens = ss.SelectSet.SelectEntityList as Polyline[];
+ this.Doit(ens);
+ });
+ };
+ boardThick: number; //板件厚度.
+ boardDepth: number; //板件的深度
+ fontDis: number; //前距
+ // 刀半径
+ knifeRadius: number;
+ // 槽加长
+ grooveLength: number;
+ // 槽加深
+ grooveDepth: number;
+ // 槽加宽
+ grooveWidth: number;
+ // 背板数据
+ // 上下左右延伸
+ backLeftExtend: number; backRightExtend: number; backTopExtend: number; backDownExtend: number;
+ //背板厚度
+ backBoardThick: number;
+ //需要移动的背板的大小
+ moveBackLen: number; moveBackWidth: number;
+ //背板移动的距离
+ backBoardMoveDis: number;
+ //背板是模板
+ backIsTemplate: boolean;
+ //背板模板
+ backTemplate: TemplateRecord;
+ //使用板名设置属性
+ isUseBoardNameToAttr: boolean;
+ //多背板柜子
+ isMultiBackCab: boolean = false;
+ //板名设置属性配置
+ strBoardNameConfig: string;//?
+ boardData;//?
+ InitData(store: Rec2BrStore)
+ {
+ let option = store.m_Option;
+ this.boardThick = option.cabinetBrThick;//18;
+ this.boardDepth = option.cabinetDeep;//300;
+ this.fontDis = option.cabinetCurtail;//0;
+ this.backBoardThick = option.backBrThick;//18;
+ this.backIsTemplate = option.backBrUseTemplate;//false;
+ // this.backTemplate = option.backBrTemplate;
+ this.backBoardMoveDis = option.backBrFrontMove;//0;
+ this.isUseBoardNameToAttr = false;
+ this.moveBackLen = option.backBrBiggerThanHeight;
+ this.moveBackWidth = option.backBrBiggerThanWidth;//
+ this.backLeftExtend = option.backBrLeftExtend;//0;
+ this.backRightExtend = option.backBrRightExtend;
+ this.backTopExtend = option.backBrUpExtend;
+ this.backDownExtend = option.backBrDownExtend;//0;
+ this.isMultiBackCab = option.isMultiBackBr;
+ //材料信息
+ this.knifeRadius = safeEval(option.grooveOption.knifeRadius);
+ this.grooveDepth = safeEval(option.grooveOption.grooveAddDepth);
+ this.grooveLength = safeEval(option.grooveOption.grooveAddLength);
+ this.grooveWidth = safeEval(option.grooveOption.grooveAddWidth);
+ }
+ //绘制 传入曲线id表
+ Doit(pls: Polyline[])
+ {
+ //默认认为用户传进来的pls都平行,都在一个平面内
+ if (!pls || pls.length === 0) return;
+
+ let pl0 = pls[0];
+ //希望根据当前的ucs进行绘制,避免坐标系没有对齐的问题
+ let ucs = app.Editor.UCSMatrix;
+ //对齐到pl0点
+ matrixSetVector(ucs, 3, pl0.Position);
+ let ucsinv = new Matrix4().getInverse(ucs);
+ pls = pls.filter(pl => CurveInOCS(pl, ucs, ucsinv));
+ for (let pl of pls)
+ pl.Erase();
+
+ let backPls: Polyline[] = [];//背板 //下面会处理成带孩子们
+ let facePls: Polyline[] = [];//条子或者地脚
+ let otherPls: Polyline[] = [];//层板立板//下面会过滤掉被背板包含的
+ for (let pl of pls)
+ {
+ pl.ApplyMatrix(ucsinv);
+ let box = pl.BoundingBox;
+ let size = box.getSize(new Vector3);
+ pl.TempData = size;
+ pl.TempData2 = box;
+ if (this.IsBackBoard(size))
+ backPls.push(pl);
+ else if (this.IsFaceBoard(size))
+ facePls.push(pl);
+ else
+ otherPls.push(pl);
+ }
+
+ let backAndChildrens: Polyline[][] = [];//背板带着孩子们 [back,x,x,x,x][]
+ for (let pl of backPls)
+ {
+ let g = [pl];
+ otherPls = otherPls.filter(pln =>
+ {
+ if (this.IsPolyLineContain(pl, pln))
+ {
+ g.push(pln);
+ return false;
+ }
+ return true;
+ });
+ backAndChildrens.push(g);
+ }
+
+ //此时被剩下的otherpls 黄金圣斗士 孤立无援
+
+ //绘制空间坐标系
+ this.SpaceOCS = ucs.clone();
+ let y = new Vector3().setFromMatrixColumn(this.SpaceOCS, 1);
+ let z = new Vector3().setFromMatrixColumn(this.SpaceOCS, 2);
+ matrixSetVector(this.SpaceOCS, 1, z.negate());
+ matrixSetVector(this.SpaceOCS, 2, y);
+
+ let allBoards: Board[] = [];
+
+ let layerVerBoards: Board[] = this.DrawBoard(otherPls);
+ let tzdj = this.DrawBoard(facePls, false, 4);//条子或地脚板
+ allBoards.push(...tzdj);
+
+ let backBoards: Board[] = [];
+ for (let g of backAndChildrens)
+ {
+ let brs = this.DrawBoard(g, true);
+ let br = brs.shift();
+ backBoards.push(br);
+ layerVerBoards.push(...brs);
+ }
+
+ allBoards.push(...backBoards, ...layerVerBoards);
+
+ let m = new Matrix4;
+ const CurtailFunc = (br: Board, curtail: number, changeWidth: boolean = true) =>
+ {
+ if (changeWidth)
+ br.Width -= curtail;
+ m.setPosition(0, curtail, 0);
+ br.ApplyMatrix(m);
+ };
+
+ //切割
+ for (let br of layerVerBoards)
+ {
+ br.Subtract(backBoards);
+ //柜体内缩
+ CurtailFunc(br, this.fontDis);
+ }
+ for (let br of tzdj)
+ {
+ CurtailFunc(br, this.fontDis, false);
+ }
+
+ //修改材料数据,封边数据,备注数据
+ let storeOption = this.store.m_Option;
+ allBoards.forEach((b) =>
+ {
+ b.KnifeRadius = this.knifeRadius;
+ b.GroovesAddDepth = this.grooveDepth;
+ b.GroovesAddWidth = this.grooveWidth;
+ b.GroovesAddLength = this.grooveLength;
+ b.BoardProcessOption.material = storeOption.material;
+ b.BoardProcessOption.color = storeOption.color;
+ b.BoardProcessOption.boardName = storeOption.boardMatName;
+ b.BoardProcessOption.roomName = storeOption.roomName;
+ b.BoardProcessOption.drillType = storeOption.drillType;
+ b.BoardProcessOption.sealedUp = storeOption.sealedUp;
+ b.BoardProcessOption.sealedDown = storeOption.sealedDown;
+ b.BoardProcessOption.sealedLeft = storeOption.sealedLeft;
+ b.BoardProcessOption.sealedRight = storeOption.sealedRight;
+ let remarks: [string, string][] = [];
+ for (let d of storeOption.remarks)
+ {
+ if (d[0] && d[1])
+ remarks.push([d[0], d[1]]);
+ }
+ b.BoardProcessOption.remarks = remarks;
+ });
+
+ let cabGroup: Board[][] = [];
+
+ if (!this.isMultiBackCab)
+ {
+ //这里我们开始分柜子,共有几个柜子?
+ //1.将背板的范围拉大
+ for (let pl of backPls)
+ {
+ let box = pl.TempData2 as Box3;
+ let [x1, y1, x2, y2] = [box.min.x, box.min.y, box.max.x, box.max.y];
+ let pts = [
+ new Vector3(x1 - 50, y1),
+ new Vector3(x1, y1 - 50),
+
+ new Vector3(x2 + 50, y2),
+ new Vector3(x2, y2 + 50),
+ ];
+ for (let p of pts)
+ {
+ let boxTemp = box.clone();
+ boxTemp.expandByPoint(p);
+
+ //如果不相交则延伸成功
+ if (!backPls.some(pl2 => { return pl2 !== pl && boxTemp.intersectsBox(pl2.TempData2); }))
+ box = boxTemp;
+ }
+ pl.TempData2 = box;
+ }
+
+ for (let pl of backPls)
+ {
+ let group: Polyline[] = [pl];
+ let box1 = pl.TempData2 as Box3;
+ pls = pls.filter(pln =>
+ {
+ if (pln === pl) return false;
+ let box2 = pln.TempData2 as Box3;
+ if (box1.intersectsBox(box2))
+ {
+ group.push(pln);
+ return false;
+ }
+ return true;
+ });
+ cabGroup.push(group.map(pl => pl.TempData));
+ }
+ }
+ else
+ cabGroup = [allBoards];
+
+ let spaceInv = new Matrix4().getInverse(this.SpaceOCS);
+ for (let i = 0; i < cabGroup.length; i++)
+ {
+ let group = cabGroup[i];
+
+ //改柜名
+ if (storeOption.iscabinetName)
+ {
+ group.forEach((br) => { br.BoardProcessOption.cabinetName = storeOption.cabinetName + (this.isMultiBackCab ? "" : (i + 1)); });
+ }
+ //#region 这里我们开始解析板件类型
+ //此时我们分析收口条和地脚线
+ let skts: Board[] = [];//收口条
+ let djxs: Board[] = [];//地脚
+
+ let topBoards: Board[] = [];//顶部的板
+ let downBoards: Board[] = [];//底部的板
+
+ let leftBoards: Board[] = [];//左侧的立板
+ let rightBoards: Board[] = [];//右侧的立板
+
+ let ordinarylays: Board[] = [];//普通的层板
+ let ordinaryvers: Board[] = [];//普通的立板
+
+ let layers = group.filter(br => br.BoardType === BoardType.Layer);
+ let vers = group.filter(br => br.BoardType === BoardType.Vertical);
+ let backs = group.filter(br => br.BoardType === BoardType.Behind);
+
+ //左右侧板
+ let left = Infinity;
+ let right = - Infinity;
+ for (let br of vers)
+ {
+ let p = br.MinPoint.applyMatrix4(spaceInv);
+ if (p.x < left)
+ left = p.x;
+ if (p.x > right)
+ right = p.x;
+ }
+
+ for (let br of vers)
+ {
+ let p = br.MinPoint.applyMatrix4(spaceInv);
+ if (equaln(p.x, left))
+ leftBoards.push(br);
+ else if (equaln(p.x, right))
+ rightBoards.push(br);
+ else
+ ordinaryvers.push(br);
+ }
+
+ //顶底板
+ let top = -Infinity;
+ let down = Infinity;
+ for (let br of layers)
+ {
+ let p = br.MinPoint.applyMatrix4(spaceInv);
+ if (p.z > top) top = p.z;
+ if (p.z < down) down = p.z;
+ }
+
+ for (let br of layers)
+ {
+ let p = br.MinPoint.applyMatrix4(spaceInv);
+ if (equaln(p.z, top))
+ topBoards.push(br);
+ else if (equaln(p.z, down))
+ downBoards.push(br);
+ else
+ ordinarylays.push(br);
+ }
+
+ //收口条
+ skts = backs.filter(br => br.ColorIndex === 4);
+
+ down = Infinity;
+ for (let br of skts)
+ {
+ let p = br.MinPoint.applyMatrix4(spaceInv);
+ if (p.z < down) down = p.z;
+ }
+ //地脚线
+ arrayRemoveIf(skts, br =>
+ {
+ let isdjx = br.Width > br.Height && equaln(br.MinPoint.applyMatrix4(spaceInv).z, down);
+ if (isdjx)
+ djxs.push(br);
+ return isdjx;
+ });
+
+ //修改板名和前缩
+ let curtail = 0;
+ let leftrightbrs = [...leftBoards, ...rightBoards];//左右侧
+ for (let i = 0; i < leftrightbrs.length; i++)
+ {
+ let br = leftrightbrs[i];
+ if (i < leftrightbrs.length - 1)
+ {
+ if (storeOption.isfarLeftVerticalBrName)
+ br.Name = storeOption.farLeftVerticalBrName;
+ }
+ else
+ if (storeOption.isfarRightVerticalBrName)
+ br.Name = storeOption.farRightVerticalBrName;
+ br.ColorIndex = 1;
+ }
+ for (let br of ordinaryvers)//普通立板
+ {
+ CurtailFunc(br, storeOption.verticalBrShrink);
+ }
+
+ let topbottombrs = [...topBoards, ...downBoards];//顶底板
+ for (let i = 0; i < topbottombrs.length; i++)
+ {
+ let br = topbottombrs[i];
+ if (i < topBoards.length)
+ {
+ if (storeOption.topMostLayerBrName)
+ br.Name = storeOption.topMostLayerBrName;
+ curtail = storeOption.topBrShrink;
+ }
+ else
+ {
+ if (storeOption.bottomMostLayerBrName)
+ br.Name = storeOption.bottomMostLayerBrName;
+ curtail = storeOption.bottomBrShrink;
+ }
+ CurtailFunc(br, curtail);
+ br.ColorIndex = 7;
+ }
+ for (let br of ordinarylays)//普通层板
+ {
+ CurtailFunc(br, storeOption.layerBrShrink);
+ }
+
+ //地脚和收口条
+ for (let br of djxs)
+ {
+ if (storeOption.bottomMostBackBrName)
+ br.Name = storeOption.bottomMostBackBrName;
+ CurtailFunc(br, storeOption.groundLineBrShrink, false);
+ br.ColorIndex = 5;
+ }
+ for (let br of skts)
+ {
+ if (storeOption.isstripeBrName)
+ br.Name = storeOption.stripeBrName;
+ }
+ }
+ //#endregion
+ }
+
+ DrawBoard(pls: Polyline[], isBack = false, color?: number): Board[]
+ {
+ let depth = this.boardDepth;
+ if (isBack)
+ depth -= this.backBoardMoveDis + this.backBoardThick;
+
+ let brs: Board[] = [];
+
+ for (let pl of pls)
+ {
+ let size = pl.TempData as Vector3;
+ if (size.x < 1 || size.y < 1)
+ {
+ console.log("奇怪的矩形");
+ continue;
+ }
+ let box = pl.TempData2 as Box3;
+ let minp = box.min.clone();
+ let br: Board;
+ if (size.x > size.y && size.y < 50) //层板
+ {
+ br = Board.CreateBoard(size.x, depth, size.y, BoardType.Layer);
+ [minp.x, minp.y, minp.z] = [minp.x + size.x, minp.z, minp.y];
+ }
+ else if (size.x < size.y && size.x < 50)//立板
+ {
+ br = Board.CreateBoard(size.y, depth, size.x, BoardType.Vertical);
+ [minp.x, minp.y, minp.z] = [minp.x, minp.z, minp.y];
+ }
+ else
+ {
+ [minp.x, minp.y, minp.z] = [minp.x, minp.z + this.backBoardThick, minp.y];
+ if (isBack)
+ {
+ minp.y += this.boardDepth - this.backBoardThick - this.backBoardMoveDis;//移动到后面
+
+ minp.x -= this.backLeftExtend;
+ minp.z -= this.backDownExtend;
+
+ size.x += this.backLeftExtend + this.backRightExtend;
+ size.y += this.backTopExtend + this.backDownExtend;
+ }
+ br = Board.CreateBoard(size.y, size.x, this.backBoardThick, BoardType.Behind);
+ }
+ let m = new Matrix4().setPosition(minp);
+ br.ApplyMatrix(m);
+ br.ApplyMatrix(this.SpaceOCS);
+ if (color !== undefined) br.ColorIndex = color;
+ app.Database.ModelSpace.Append(br);
+
+ brs.push(br);
+ pl.TempData = br;
+ }
+
+ return brs;
+ }
+
+ //pl1是否包含pl2
+ IsPolyLineContain(pl1: Polyline, pl2: Polyline)
+ {
+ let box1 = pl1.TempData2 as Box3;
+ let box2 = pl2.TempData2 as Box3;
+ let box = box1.clone().intersect(box2);
+ let s = box.getSize(new Vector3);
+ if (s.x < 1 || s.y < 1) return false;
+ let int = pl1.IntersectWith(pl2, IntersectOption.OnBothOperands);
+ return int.length > 0 || pl1.PtInCurve(pl2.StartPoint);
+ }
+
+ //是否为一个背板的尺寸大小
+ private IsBackBoard(size: Vector3): boolean
+ {
+ return size.x >= this.moveBackWidth && size.y >= this.moveBackLen;
+ }
+
+ private IsFaceBoard(size: Vector3): boolean
+ {
+ return size.x >= 50 && size.y >= 50;
+ }
+}
diff --git a/src/Common/CheckoutVaildValue.ts b/src/Common/CheckoutVaildValue.ts
index 11dbc908d..9333645be 100644
--- a/src/Common/CheckoutVaildValue.ts
+++ b/src/Common/CheckoutVaildValue.ts
@@ -14,6 +14,7 @@ export enum CheckObjectType
RLB = "rotateLayerBoard",
BBC = "boardbatchcurtail",
BBS = "lookoverboardinfos",
+ R2B = "rec2br",
OnlyNumber = "onlyNum",
None = "none",
}
@@ -64,6 +65,10 @@ export namespace CheckoutValid
return !Object.keys(obj).every(k =>
CheckoutDrillOption(k, obj[k]) === ""
);
+ case CheckObjectType.R2B:
+ return !Object.keys(obj).every(k =>
+ CheckoutRec2BrOption(k, obj[k]) === ""
+ );
default:
return true;
}
@@ -92,6 +97,8 @@ export namespace CheckoutValid
return CheckBoardBatchCurtail(k, v);
case CheckObjectType.BBS:
return CheckLookOverBoardInfos(k, v);
+ case CheckObjectType.R2B:
+ return CheckoutRec2BrOption(k, v);
case CheckObjectType.OnlyNumber:
if (!isNum(v))
return "数值不能为空且必须为数字";
@@ -233,6 +240,46 @@ export namespace CheckoutValid
}
return "";
}
+ export function CheckoutRec2BrOption(k: string, v: string): string
+ {
+ let val = safeEval(v);
+ switch (k)
+ {
+ case "sealedUp":
+ case "sealedDown":
+ case "sealedLeft":
+ case "sealedRight":
+ case "knifeRadius":
+ case "cabinetDeep":
+ case "cabinetBrThick":
+ case "backBrThick":
+ if (isNaN(val))
+ return "数值不能为空且必须为数字";
+ if (!(val >= 0))
+ return "数值必须大于等于0";
+ return "";
+ case "cabinetCurtail":
+ case "backBrBiggerThanHeight":
+ case "backBrBiggerThanWidth":
+ case "backBrFrontMove":
+ case "backBrLeftExtend":
+ case "backBrRightExtend":
+ case "backBrUpExtend":
+ case "backBrDownExtend":
+ case "verticalBrShrink":
+ case "layerBrShrink":
+ case "bottomBrShrink":
+ case "topBrShrink":
+ case "groundLineBrShrink":
+ case "grooveAddLength":
+ case "grooveAddDepth":
+ case "grooveAddWidth":
+ if (isNaN(val))
+ return "数值不能为空且必须为数字";
+ default:
+ return "";
+ }
+ }
export function CheckoutArrayOption(k: string, v: string): string
{
switch (k)
diff --git a/src/DatabaseServices/CADObject.ts b/src/DatabaseServices/CADObject.ts
index 920c65f87..0aa81cb30 100644
--- a/src/DatabaseServices/CADObject.ts
+++ b/src/DatabaseServices/CADObject.ts
@@ -16,6 +16,8 @@ export abstract class CADObject
* 用于储存临时数据
*/
public TempData: any;
+ public TempData2: any;
+
set Owner(owner: ObjectId)
{
this._Owner = owner;
diff --git a/src/DatabaseServices/Entity/Circle.ts b/src/DatabaseServices/Entity/Circle.ts
index bd2be2987..ee2895ac5 100644
--- a/src/DatabaseServices/Entity/Circle.ts
+++ b/src/DatabaseServices/Entity/Circle.ts
@@ -192,9 +192,7 @@ export class Circle extends Curve
GetParamAtPoint(pt?: Vector3)
{
if (!this.PtOnCurve(pt))
- {
return NaN;
- }
return angle(pt.clone().applyMatrix4(this.OCSInv)) / (Math.PI * 2);
}
diff --git a/src/Editor/CommandRegister.ts b/src/Editor/CommandRegister.ts
index 6e57fcafd..7f04e12a1 100644
--- a/src/Editor/CommandRegister.ts
+++ b/src/Editor/CommandRegister.ts
@@ -166,6 +166,10 @@ import { BuyMaterial } from './../Add-on/BuyMaterial';
import { Interfere } from './../Add-on/interfere';
import { ShowKinfeManageModal } from './../Add-on/showModal/ShowKnifeManageModal';
import { commandMachine } from './CommandMachine';
+import { CreatePolyLineFromCurve } from "../Add-on/twoD2threeD/CreatePolyLineFromCurve";
+import { RectConvertToBoard } from "../Add-on/twoD2threeD/RectConvertToBr";
+import { Command_EraseLineAndArc } from "../Add-on/EraseLineAndArc";
+import { Command_TestRegionParse } from "../Add-on/testEntity/TestRegionParse";
export function registerCommand()
{
@@ -184,6 +188,7 @@ export function registerCommand()
commandMachine.RegisterCommand("dxf", new Command_DXFImport());
commandMachine.RegisterCommand("tt", new Test());
+ commandMachine.RegisterCommand("testRegionParse", new Command_TestRegionParse());
commandMachine.RegisterCommand("TestBoundaryBox", new Command_TestBoundaryBox());
commandMachine.RegisterCommand("insert", new Command_Insert());
@@ -211,6 +216,7 @@ export function registerCommand()
commandMachine.RegisterCommand("Erase", new Command_Erase());
commandMachine.RegisterCommand("deletecurve", new DeleteCurve());
+ commandMachine.RegisterCommand("EraseLineArc", new Command_EraseLineAndArc());
commandMachine.RegisterCommand("c0", new DrawCircle0());
commandMachine.RegisterCommand("break", new Command_Break());
@@ -460,6 +466,9 @@ export function registerCommand()
commandMachine.RegisterCommand("showknifeMg", new ShowKinfeManageModal());
commandMachine.RegisterCommand("showdoors", new Command_SwitchDoor(true));
commandMachine.RegisterCommand("hidedoors", new Command_SwitchDoor(false));
+ //线条变矩形
+ commandMachine.RegisterCommand("curve2rec", new CreatePolyLineFromCurve());
+ commandMachine.RegisterCommand("r2b", new RectConvertToBoard());
RegistCustomCommand();
}
diff --git a/src/Editor/DefaultConfig.ts b/src/Editor/DefaultConfig.ts
index 4d7498bd5..3db201878 100644
--- a/src/Editor/DefaultConfig.ts
+++ b/src/Editor/DefaultConfig.ts
@@ -6,6 +6,7 @@ import { ILatticeOption, ELatticeArrayType } from "../UI/Store/LatticeInterface"
import { HandleVePos, DoorPosType, HandleHorPos, IDrawerConfigOption } from "../UI/Store/DoorInterface";
import { EBoardKeyList } from "../Common/BoardKeyList";
import { ICylMetalsOption, IExtMetalsOption, ICompHardwareOption, EMetalsType, IToplineOption } from "../UI/Components/RightPanel/RightPanelInterface";
+import { Curve2RecOption } from "../Add-on/twoD2threeD/Modals/Curve2RecModal";
export const DefaultLayerBoardConfig: LayerBoardOption = {
version: 1,
@@ -517,3 +518,13 @@ export const DefaultBoardProcessOption: BoardProcessOption = {
remarks: [],
};
Object.freeze(DefaultBoardProcessOption);
+
+export const DefaultCurve2RecOption: Curve2RecOption = {
+ version: 1,
+ isSaveMax: false,
+ isSaveSmall: true,
+ width: 90,
+ isAnaly: true,
+ gap: 3
+};
+Object.freeze(DefaultCurve2RecOption);
diff --git a/src/Geometry/CurveIntersection.ts b/src/Geometry/CurveIntersection.ts
index 864905ca4..7bbe00f29 100644
--- a/src/Geometry/CurveIntersection.ts
+++ b/src/Geometry/CurveIntersection.ts
@@ -3,31 +3,28 @@ import { Curve } from "../DatabaseServices/Entity/Curve";
import { IntersectOption } from "../GraphicsSystem/IntersectWith";
/**
- *
* 简化优化版本的曲线求交, 优化版本可以参考(算法导论33.2 确定任意一对线段是否相交 p599)
*/
export class CurveIntersection
{
//用来缓存的曲线包围盒
- private boxMap: Map = new Map();
+ protected boxMap: Map = new Map();
/**
* 交点数据集,key 为曲线 value 为和它相交的(曲线和交点的Map)
- *
- * @type {Map>}
- * @memberof CurveIntersection
*/
intersect: Map> = new Map();
+ intersect2: Map = new Map();
/**
* @param {Curve[]} cus 请注意数组的顺序会被更改,如果你在意数组的顺序,请拷贝数组后传进来
* @memberof CurveIntersection
*/
- constructor(cus: Curve[])
+ constructor(cus: Curve[], parseIntersectionParam = false, intType = IntersectOption.OnBothOperands)
{
- this.genBox(cus);
+ this.GenBox(cus);
//按x排序
- this.sortCurve(cus);
+ this.SortCurve(cus);
let count = cus.length;
for (let i = 0; i < count; i++)
@@ -48,24 +45,46 @@ export class CurveIntersection
if (c2b.min.y - c1b.max.y > 1e-6)
continue;
- let pts = c1.IntersectWith(c2, IntersectOption.OnBothOperands);
- if (pts.length > 0)
+ let ints = this.IntersectWith2(c1, c2, intType);
+ if (ints.length > 0)
{
+ let pts = ints.map(i => i.pt);
c1d.set(c2, pts);
this.GetIntersect(c2).set(c1, pts);
+
+ if (parseIntersectionParam)
+ {
+ this.AppendIntersectionParams(c1, ints.map(i => i.thisParam));
+ this.AppendIntersectionParams(c2, ints.map(i => i.argParam));
+ }
}
}
}
}
- private genBox(cus: Curve[])
+
+ protected IntersectWith2(c1: Curve, c2: Curve, intType: IntersectOption)
+ {
+ return c1.IntersectWith2(c2, intType);
+ }
+
+ protected AppendIntersectionParams(curve: Curve, params: number[])
{
- cus.forEach(c =>
+ let arr = this.intersect2.get(curve);
+ if (!arr)
{
+ arr = [];
+ this.intersect2.set(curve, arr);
+ }
+ arr.push(...params);
+ }
+
+ protected GenBox(cus: Curve[])
+ {
+ for (let c of cus)
this.boxMap.set(c, c.BoundingBox);
- });
}
- private sortCurve(cus: Curve[])
+ protected SortCurve(cus: Curve[])
{
cus.sort((c1, c2) =>
{
diff --git a/src/Geometry/CurveMap.ts b/src/Geometry/CurveMap.ts
index daf52225c..d4ed82866 100644
--- a/src/Geometry/CurveMap.ts
+++ b/src/Geometry/CurveMap.ts
@@ -55,7 +55,13 @@ export class CurveMap
return this._Vertices;
}
- AddCurveToMap(curve: Curve, isArc: boolean = curve instanceof Arc, removeDuplicate: boolean = false)
+ /**
+ * @param curve
+ * @param [isArc=curve instanceof Arc]
+ * @param [removeDuplicate=false]
+ * @returns 加入成功?
+ */
+ AddCurveToMap(curve: Curve, isArc: boolean = curve instanceof Arc, removeDuplicate: boolean = false): boolean
{
let sp = curve.StartPoint;
let ep = curve.EndPoint;
@@ -64,7 +70,7 @@ export class CurveMap
//在面域分析中,路线指向同一个顶点已经没有意义了
if (this._RemoveSortLine && startS === endS)
- return;
+ return false;
if (removeDuplicate)//删除重复
{
@@ -77,7 +83,7 @@ export class CurveMap
return true;
}
});
- if (index !== -1) return;
+ if (index !== -1) return false;
}
let length = curve.Length;
@@ -94,6 +100,8 @@ export class CurveMap
}
startS.routes.push(routeS2E);
endS.routes.push(routeE2S);
+
+ return true;
}
/**
diff --git a/src/GraphicsSystem/IntersectWith.ts b/src/GraphicsSystem/IntersectWith.ts
index dc764bb51..7baa8bb5c 100644
--- a/src/GraphicsSystem/IntersectWith.ts
+++ b/src/GraphicsSystem/IntersectWith.ts
@@ -233,7 +233,7 @@ export function IntersectLineAndArc(line: Line, arc: Arc, extType: IntersectOpti
return CheckPointOnCurve(ptArr, line, arc, extType, tolerance);
}
//直线和直线
-export function IntersectLAndLFor2D(p1: Vector3, p2: Vector3, p3: Vector3, p4: Vector3, tolerance = 1e-6): Vector3
+export function IntersectLAndLFor2D(p1: Vector3, p2: Vector3, p3: Vector3, p4: Vector3): Vector3
{
let dx1 = p1.x - p2.x;
let dx2 = p3.x - p4.x;
@@ -244,7 +244,6 @@ export function IntersectLAndLFor2D(p1: Vector3, p2: Vector3, p3: Vector3, p4: V
let det = (dx2 * dy1) - (dy2 * dx1);
- let pt = new Vector3(0, 0, 0);
if (equaln(det, 0.0, 1e-5))
{
// if (equaln(dx2 * dy3, dy2 * dx3, 1e-5))
@@ -254,6 +253,7 @@ export function IntersectLAndLFor2D(p1: Vector3, p2: Vector3, p3: Vector3, p4: V
return;
}
+ let pt = new Vector3;
let ratio = ((dx1 * dy3) - (dy1 * dx3)) / det;
pt.x = (ratio * dx2) + p4.x;
pt.y = (ratio * dy2) + p4.y;
@@ -261,6 +261,32 @@ export function IntersectLAndLFor2D(p1: Vector3, p2: Vector3, p3: Vector3, p4: V
return pt;
}
+export function IntersectLAndLFor2D2(p1: Vector3, p2: Vector3, p3: Vector3, p4: Vector3): Vector3[]
+{
+ let dx1 = p1.x - p2.x;
+ let dx2 = p3.x - p4.x;
+ let dx3 = p4.x - p2.x;
+ let dy1 = p1.y - p2.y;
+ let dy2 = p3.y - p4.y;
+ let dy3 = p4.y - p2.y;
+
+ let det = (dx2 * dy1) - (dy2 * dx1);
+
+ if (equaln(det, 0.0, 1e-5))
+ {
+ if (equaln(dx2 * dy3, dy2 * dx3, 1e-5))
+ return [p1, p2, p3, p4];
+ return [];
+ }
+
+ let pt = new Vector3;
+ let ratio = ((dx1 * dy3) - (dy1 * dx3)) / det;
+ pt.x = (ratio * dx2) + p4.x;
+ pt.y = (ratio * dy2) + p4.y;
+
+ return [pt];
+}
+
export function IntersectLine3AndLine3(p1: Vector3, p2: Vector3, p3: Vector3, p4: Vector3, epsilon = 1e-6)
{
let pts = ShortestLine3AndLine3(p1, p2, p3, p4);
@@ -320,43 +346,39 @@ function ShortestLine3AndLine3(p1: Vector3, p2: Vector3, p3: Vector3, p4: Vector
//直线和直线
export function IntersectLineAndLine(l1: Line, l2: Line, extType: IntersectOption, fuzz = 1e-4): IntersectResult[]
{
- // if (l1.Is2D && l2.Is2D)
- // {
- // let p = IntersectLAndLFor2D(l1.StartPoint, l1.EndPoint, l2.StartPoint, l2.EndPoint);
- // if (p) return [p];
- // else return [];
- // }
let [pt1, pt2, pt3, pt4] = [l1.StartPoint, l1.EndPoint, l2.StartPoint, l2.EndPoint];
- let pts = ShortestLine3AndLine3(pt1, pt2, pt3, pt4);
- if (!pts) return [];
- let pt = pts[0];
-
- let { closestPt: p1, param: param1 } = l1.GetClosestAtPoint(pt, true);
- let { closestPt: p2, param: param2 } = l2.GetClosestAtPoint(pt, true);
-
- if (!equalv3(pt, p1, fuzz) || !equalv3(pt, p2, fuzz))
- return [];
-
- if (!(extType & IntersectOption.ExtendThis
- || l1.ParamOnCurve(param1, 0)
- || equalv3(pt1, pt, fuzz)
- || equalv3(pt2, pt, fuzz)
- ))
- return [];
-
- if (!(extType & IntersectOption.ExtendArg
- || l2.ParamOnCurve(param2, 0)
- || equalv3(pt3, pt, fuzz)
- || equalv3(pt4, pt, fuzz)
- ))
- return [];
+ let ipts: Vector3[];
+ if (equaln(pt1.z, 0, fuzz) && equaln(pt2.z, 0, fuzz) && equaln(pt3.z, 0, fuzz) && equaln(pt4.z, 0, fuzz))
+ {
+ ipts = IntersectLAndLFor2D2(pt1, pt2, pt3, pt4);
+ ipts.sort(comparePoint("xy"));
+ arrayRemoveDuplicateBySort(ipts, (p1, p2) => equalv3(p1, p2, fuzz));
+ }
+ else
+ {
+ ipts = ShortestLine3AndLine3(pt1, pt2, pt3, pt4);
+ if (!ipts) return [];
+ if (ipts.length === 2)
+ ipts.pop();
+ }
- return [{
- pt,
- thisParam: param1,
- argParam: param2
- }];
+ let ints: IntersectResult[] = [];
+ for (let pt of ipts)
+ {
+ let { closestPt: p1, param: param1 } = l1.GetClosestAtPoint(pt, true);
+ if (!equalv3(pt, p1, fuzz)) return [];
+ if (!(extType & IntersectOption.ExtendThis))
+ if (!(l1.ParamOnCurve(param1, 0) || equalv3(pt1, pt, fuzz) || equalv3(pt2, pt, fuzz)))
+ return [];
+ let { closestPt: p2, param: param2 } = l2.GetClosestAtPoint(pt, true);
+ if (!equalv3(pt, p2, fuzz)) return [];
+ if (!(extType & IntersectOption.ExtendArg))
+ if (!(l2.ParamOnCurve(param2, 0) || equalv3(pt3, pt, fuzz) || equalv3(pt4, pt, fuzz)))
+ return [];
+ ints.push({ pt, thisParam: param1, argParam: param2 });
+ }
+ return ints;
}
export function IntersectPolylineAndCurve(pl: Polyline, cu: Curve, extType: IntersectOption, tolerance = 1e-6): IntersectResult[]
diff --git a/src/UI/Components/Board/BoardModal.tsx b/src/UI/Components/Board/BoardModal.tsx
index 2b66bd55c..9a30ce2b3 100644
--- a/src/UI/Components/Board/BoardModal.tsx
+++ b/src/UI/Components/Board/BoardModal.tsx
@@ -40,6 +40,8 @@ export enum BoardModalType
CompMetals = "compmetals", //复合实体
ToplineMetals = "toplinemetals", //顶线五金
UserConfig = "userConfig", //用户配置
+ Curves2Rec = "curves2rec",//线条转矩形
+ Rec2Br = "rec2br"
}
export interface BoardModalProps
{