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); }} + /> +
+
+ +
柜体参数
+
+
+
+ +
+ +
+ +
+
背板规则
+
+ + + + +
+
前缩
+
+ +
+
解析板件名称与板件内缩
+
+
+ + { + 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.showShops.get() && + } +
+ +
+
封边
+
+ { + + } +
+
备注
+
+ +
+
+
+ + { + store.m_Option.backBrUseTemplate = !store.m_Option.backBrUseTemplate; + store.UIOption.backBrUseTemplate = store.m_Option.backBrUseTemplate; + }} + /> + +
+
+ +
+ {/* + + */} +
+
+ +
+ {/* { + 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 {