From c38e8a2275279570aec4b9771ee3bdb435a5ed3c Mon Sep 17 00:00:00 2001 From: ChenX Date: Wed, 16 Nov 2022 07:41:27 +0000 Subject: [PATCH] =?UTF-8?q?!2031=20=E5=8A=9F=E8=83=BD:=E7=BB=98=E5=88=B6?= =?UTF-8?q?=E9=97=A8=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FeedingToolPath.test.ts.snap | 4 +- __test__/Geometry/intersect.test.ts | 7 +- .../__snapshots__/offset.test.ts.snap | 20 +- package.json | 1 - src/Add-on/Room/DrawHole.ts | 24 +- src/Add-on/Room/DrawRoomDoor.ts | 74 ++++ src/Add-on/Room/DrawRoomWindow.ts | 148 +++++++ src/Add-on/Room/OneKeyDrawWindow.ts | 74 ++++ src/Add-on/testEntity/test.ts | 14 + src/Common/CommandNames.ts | 15 +- src/DatabaseServices/Entity/Arc.ts | 6 +- src/DatabaseServices/Entity/Circle.ts | 10 +- .../Room/Entity/Wall/Hole/RoomHolePolyline.ts | 171 +++++++- .../Wall/Hole/Window/DrawWindowPanel.tsx | 295 +++++++++++++ .../Wall/Hole/Window/WindowPanelStore.ts | 72 ++++ .../Hole/Window/WindowParamsComponent.tsx | 101 +++++ .../Wall/Hole/Window/WindowTempInfo.tsx | 180 ++++++++ .../Wall/Hole/Window/WindowTempSelect.tsx | 200 +++++++++ .../Positioning/PositioningBoardSpace.ts | 4 +- .../Template/Positioning/PositioningFixed.ts | 38 ++ .../ProgramTempate/TemplateArcWindowRecord.ts | 280 +++++++++++++ .../ProgramTempate/TemplateRoomDoorRecord.ts | 93 +++++ .../ProgramTempate/TemplateWindowRecord.ts | 394 ++++++++++++++++++ src/Editor/CommandRegister.ts | 16 + src/Editor/DefaultConfig.ts | 22 +- src/GraphicsSystem/IntersectWith.ts | 18 +- src/Reactor/RelevanceCuttingReactor.ts | 7 +- src/Reactor/RoomHoleReactor.ts | 21 +- src/UI/Components/Board/Door/DoorModal.tsx | 2 + src/UI/Components/Modal/ModalStyle/Modal.less | 7 +- src/UI/Components/Template/Template.less | 8 +- src/UI/Components/Template/TemplateDetail.tsx | 67 +-- src/UI/Components/Template/TemplateList.tsx | 2 + src/UI/Components/Template/TemplateSelect.tsx | 1 + src/UI/Components/Template/TemplateTagCom.tsx | 246 +++++++++++ .../ToolBar/ModifyModel/ModifyModel.less | 10 + .../ToolBar/ModifyModel/ModifyModelPanel.tsx | 47 ++- .../ToolBar/ModifyModel/ModuleBaseParams.tsx | 35 +- .../ToolBar/ModifyModel/RoomBaseParams.tsx | 247 +++++++++-- .../ModifyModel/RoomBaseParamsStore.ts | 80 +++- .../Components/TopToolBar/RoomDesignPanel.tsx | 10 +- .../Components/TopToolBar/ToolsBlockStore.ts | 2 + src/UI/Components/TopToolBar/TopToolBar.tsx | 19 +- .../TopToolBar/TopToolBarInterface.ts | 3 +- src/UI/Css/style.less | 109 ++++- src/UI/Store/BoardInterface.ts | 20 + src/UI/Store/DoorInterface.ts | 9 +- 47 files changed, 3063 insertions(+), 170 deletions(-) create mode 100644 src/Add-on/Room/DrawRoomDoor.ts create mode 100644 src/Add-on/Room/DrawRoomWindow.ts create mode 100644 src/Add-on/Room/OneKeyDrawWindow.ts create mode 100644 src/DatabaseServices/Room/Entity/Wall/Hole/Window/DrawWindowPanel.tsx create mode 100644 src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowPanelStore.ts create mode 100644 src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowParamsComponent.tsx create mode 100644 src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowTempInfo.tsx create mode 100644 src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowTempSelect.tsx create mode 100644 src/DatabaseServices/Template/Positioning/PositioningFixed.ts create mode 100644 src/DatabaseServices/Template/ProgramTempate/TemplateArcWindowRecord.ts create mode 100644 src/DatabaseServices/Template/ProgramTempate/TemplateRoomDoorRecord.ts create mode 100644 src/DatabaseServices/Template/ProgramTempate/TemplateWindowRecord.ts create mode 100644 src/UI/Components/Template/TemplateTagCom.tsx diff --git a/__test__/FeedingToolPath/__snapshots__/FeedingToolPath.test.ts.snap b/__test__/FeedingToolPath/__snapshots__/FeedingToolPath.test.ts.snap index 286c6bcf1..e0f13b0b7 100644 --- a/__test__/FeedingToolPath/__snapshots__/FeedingToolPath.test.ts.snap +++ b/__test__/FeedingToolPath/__snapshots__/FeedingToolPath.test.ts.snap @@ -180,7 +180,7 @@ exports[`精度不一致导致的错误 4`] = `"55.04455"`; exports[`精度不一致导致的错误: 走刀数量 1`] = `4`; -exports[`超级复杂造型01 1`] = `"23338.03269"`; +exports[`超级复杂造型01 1`] = `"23338.03271"`; exports[`超级复杂造型01 2`] = `"11855.39981"`; @@ -260,7 +260,7 @@ exports[`超级复杂造型01 39`] = `"34.11041"`; exports[`超级复杂造型01 40`] = `"3.00516"`; -exports[`超级复杂造型01 41`] = `"3.00517"`; +exports[`超级复杂造型01 41`] = `"3.00516"`; exports[`超级复杂造型01 42`] = `"15.55863"`; diff --git a/__test__/Geometry/intersect.test.ts b/__test__/Geometry/intersect.test.ts index b76dc8a9c..b3205bc3b 100644 --- a/__test__/Geometry/intersect.test.ts +++ b/__test__/Geometry/intersect.test.ts @@ -150,8 +150,9 @@ test('直线和圆相切', () => test("圆弧相交精度调整", () => { - let data = { "file": [2, "Arc", 8, 2, 134, false, 1, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -30.303763742321802, 350.79539874943686, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -30.303763742321802, 350.79539874943686, 0, 1], 0, 2, 3.0000000000000084, 0, 1.686785963338877, false, "Arc", 8, 2, 135, false, 1, 2, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -14.303763742321621, 213.47228144573236, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -14.303763742321621, 213.47228144573236, 0, 1], 0, 2, 141.25208333333353, 1.6629607907756918, 2.5287104082377034, false], "basePt": { "x": -129.84704567597646, "y": 294.72436477906575, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }; + let data = + { "file": [2, "Arc", 8, 2, 134, false, 1, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -30.303763742321802, 350.79539874943686, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -30.303763742321802, 350.79539874943686, 0, 1], 0, 2, 3.0000000000000084, 0, 1.686785963338877, false, "Arc", 8, 2, 135, false, 1, 2, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -14.303763742321621, 213.47228144573236, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -14.303763742321621, 213.47228144573236, 0, 1], 0, 2, 141.25208333333353, 1.6629607907756918, 2.5287104082377034, false], "basePt": { "x": -129.84704567597646, "y": 294.72436477906575, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }; let cus = LoadEntityFromFileData(data) as Array; - let pts = cus[0].IntersectWith(cus[1], 0); - expect(pts.length).toBe(2); + let pts = cus[0].IntersectWith(cus[1], 0, 1e-5); + expect(pts.length).toBe(1); }); diff --git a/__test__/Polyline/__snapshots__/offset.test.ts.snap b/__test__/Polyline/__snapshots__/offset.test.ts.snap index fe16dcc6e..943009655 100644 --- a/__test__/Polyline/__snapshots__/offset.test.ts.snap +++ b/__test__/Polyline/__snapshots__/offset.test.ts.snap @@ -88,11 +88,11 @@ exports[`圆求交错误导致的线丢失 1`] = `4148.655275462343`; exports[`圆求交错误导致的线丢失 2`] = `4425.280774659384`; -exports[`圆求交错误导致的线丢失 3`] = `4021.9003146960804`; +exports[`圆求交错误导致的线丢失 3`] = `4021.9003160283755`; exports[`圆求交错误导致的线丢失 4`] = `4581.224228650708`; -exports[`圆求交错误导致的线丢失 5`] = `3900.6078799293005`; +exports[`圆求交错误导致的线丢失 5`] = `3900.6078808627653`; exports[`圆求交错误导致的线丢失 6`] = `4757.468532252447`; @@ -100,17 +100,17 @@ exports[`圆求交错误导致的线丢失 7`] = `3783.7486269392593`; exports[`圆求交错误导致的线丢失 8`] = `4972.012479701948`; -exports[`圆求交错误导致的线丢失 9`] = `1148.662629878697`; +exports[`圆求交错误导致的线丢失 9`] = `1148.6626298787855`; -exports[`圆求交错误导致的线丢失 10`] = `5979.881803017036`; +exports[`圆求交错误导致的线丢失 10`] = `5979.881810920421`; -exports[`圆求交错误导致的线丢失 11`] = `1049.9590630921507`; +exports[`圆求交错误导致的线丢失 11`] = `1049.959063092209`; -exports[`圆求交错误导致的线丢失 12`] = `6051.226633493529`; +exports[`圆求交错误导致的线丢失 12`] = `6051.226641876335`; exports[`圆求交错误导致的线丢失 13`] = `722.4732418587947`; -exports[`圆求交错误导致的线丢失 14`] = `6316.980878376697`; +exports[`圆求交错误导致的线丢失 14`] = `6316.98088721295`; exports[`复杂圆盘选点 1`] = `1`; @@ -184,7 +184,7 @@ exports[`简单图形因为点在线内算法错误导致的丢失 3`] = `7.1494 exports[`简单图形因为点在线内算法错误导致的丢失 4`] = `6.693604273021893`; -exports[`精度过高导致无法连接 1`] = `"6723.54634"`; +exports[`精度过高导致无法连接 1`] = `"6723.54642"`; exports[`精度过高导致的曲线丢失 1`] = `1`; @@ -196,11 +196,11 @@ exports[`精度过高导致的曲线丢失 4`] = `"97661.61008"`; exports[`精度过高导致直连失败 1`] = `1`; -exports[`精度过高导致直连失败 2`] = `"32040.01389"`; +exports[`精度过高导致直连失败 2`] = `"32040.01390"`; exports[`精度过高导致直连失败 3`] = `1`; -exports[`精度过高导致直连失败 4`] = `"32045.24857"`; +exports[`精度过高导致直连失败 4`] = `"32045.24858"`; exports[`精度过高导致连接失败 1`] = `1`; diff --git a/package.json b/package.json index 783c2b386..6a3e18b89 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "@rollup/plugin-typescript": "^8.3.2", "@types/blueimp-md5": "^2.18.0", "@types/flatbush": "^3.3.0", - "@types/html-webpack-plugin": "^3.2.6", "@types/jest": "^27.5.2", "@types/jsdom": "^20.0.0", "@types/node": "^17.0.41", diff --git a/src/Add-on/Room/DrawHole.ts b/src/Add-on/Room/DrawHole.ts index fc5748057..9e9672452 100644 --- a/src/Add-on/Room/DrawHole.ts +++ b/src/Add-on/Room/DrawHole.ts @@ -6,6 +6,7 @@ import { HostApplicationServices } from '../../ApplicationServices/HostApplicati import { arrayLast } from '../../Common/ArrayExt'; import { Draw } from '../../Common/Draw'; import { RoomHolePolyline } from '../../DatabaseServices/Room/Entity/Wall/Hole/RoomHolePolyline'; +import { RoomWallArc } from '../../DatabaseServices/Room/Entity/Wall/RoomWallArc'; import { RoomWallBase, WallFaceType } from "../../DatabaseServices/Room/Entity/Wall/RoomWallBase"; import { RoomWallLine } from '../../DatabaseServices/Room/Entity/Wall/RoomWallLine'; import { ParseWallRange, RoomWallPlaceIHoleHelper } from '../../DatabaseServices/Room/ParseService/Hole/RoomWallPlaceIHoleHelper'; @@ -19,6 +20,7 @@ import { PromptStatus } from '../../Editor/PromptResult'; import { Route } from '../../Geometry/CurveMap'; import { equalv3, midPoint } from '../../Geometry/GeUtils'; import { RenderType } from '../../GraphicsSystem/RenderType'; +import { ModalState } from '../../UI/Components/Modal/ModalInterface'; import { AppToaster } from '../../UI/Components/Toaster'; import { DynamicInputManage } from '../../UI/DynamicPrompt/DynamicInputManage'; import { PromptBlock } from '../../UI/DynamicPrompt/PromptBlock'; @@ -76,13 +78,24 @@ export class Command_DrawHole implements Command let dyn = new PromptBlock(DynamicInputManage.GetManage()); - await this.do(dyn); + let state = await this.BeferDrawHole(this._DrawType); + + if (state === ModalState.Ok) + { + let hole = await this.do(dyn); + if (hole) await this.AfterDrawHole(hole, this._DrawType); + } dyn.Destroy(); app.Editor.GetPointServices.snapServices.Disabled = bak; } - async do(dyn: PromptBlock) + //绘制洞之前,重载这个呼出对话框 + async BeferDrawHole(drawHoleType: DrawHoleType): Promise { return ModalState.Ok; } + //绘制洞之后,设置 + async AfterDrawHole(hole: RoomHolePolyline, drawHoleType: DrawHoleType) { } + + async do(dyn: PromptBlock): Promise { if (this._DrawType === DrawHoleType.I)//直线洞 { @@ -127,6 +140,9 @@ export class Command_DrawHole implements Command { preWall = d.wall; + //弧形墙不能画门 + if (this._HoleIType === IHoleType.Door && preWall instanceof RoomWallArc) return; + let { wall, getParam, range } = d; let wallLength = wall.Length; let length = (range[1] - range[0]) * wallLength; @@ -249,6 +265,7 @@ export class Command_DrawHole implements Command { Draw(hole); d.wall.RelevancyHoles.push(hole.Id); + return hole; } return; } @@ -504,6 +521,8 @@ export class Command_DrawHole implements Command wall.RelevancyHoles.push(hole.Id); hole.RelevancyWalls.push(wall.Id); } + + return hole; } return; } @@ -747,6 +766,7 @@ export class Command_DrawHole implements Command wall.RelevancyHoles.push(hole.Id); hole.RelevancyWalls.push(wall.Id); } + return hole; } return; } diff --git a/src/Add-on/Room/DrawRoomDoor.ts b/src/Add-on/Room/DrawRoomDoor.ts new file mode 100644 index 000000000..8d3abbd0d --- /dev/null +++ b/src/Add-on/Room/DrawRoomDoor.ts @@ -0,0 +1,74 @@ +import { app } from "../../ApplicationServices/Application"; +import { DuplicateRecordCloning } from "../../Common/Status"; +import { RoomHolePolyline } from "../../DatabaseServices/Room/Entity/Wall/Hole/RoomHolePolyline"; +import { DrawDoorWindowPanel } from "../../DatabaseServices/Room/Entity/Wall/Hole/Window/DrawWindowPanel"; +import { DoorWindowPanelStore } from "../../DatabaseServices/Room/Entity/Wall/Hole/Window/WindowPanelStore"; +import { TemplateRoomDoorRecord } from "../../DatabaseServices/Template/ProgramTempate/TemplateRoomDoorRecord"; +import { GetOnlineTemplate } from "../../DatabaseServices/Template/TempateUtils"; +import { TemplateRecord } from "../../DatabaseServices/Template/TemplateRecord"; +import { ModalState } from "../../UI/Components/Modal/ModalInterface"; +import { Command_DrawHole, DrawHoleType, IHoleType } from "./DrawHole"; + +export class Command_DrawRoomDoor extends Command_DrawHole +{ + constructor(drawType: DrawHoleType, holeIType: IHoleType, private _IsOneKeyDraw: boolean = false) + { + super(drawType, holeIType); + } + + //一键画门配置 + async SetStoreParam(store: DoorWindowPanelStore) { } + + override async BeferDrawHole(drawHoleType: DrawHoleType): Promise + { + //呼出对话框 + let store = DoorWindowPanelStore.GetSingleInstance(); + store.m_Option.Height = 2200; + store.m_Option.Length = 800; + + if (this._IsOneKeyDraw) + { + store.InitOption(); + this.SetStoreParam(store); + } + else + { + app.Editor.ModalManage.RenderModal(DrawDoorWindowPanel, { store, drawHoleType, holeIType: IHoleType.Door }); + let state = await app.Editor.ModalManage.Wait(); + if (state.Status !== ModalState.Ok) + return ModalState.Cancel; + } + this.holeLength = store.m_Option.Length; + this.holeHeight = store.m_Option.Height; + return ModalState.Ok; + } + + override async AfterDrawHole(hole: RoomHolePolyline): Promise + { + let store = DoorWindowPanelStore.GetSingleInstance(); + let tr = new TemplateRoomDoorRecord();//顶层节点 + tr.HoleObjectId = hole.Id; + + tr.InitBaseParams(); + tr.InitHoleParams(); + + tr.GetParam("L").expr = store.m_Option.Length; + tr.GetParam("H").expr = store.m_Option.Height; + + tr.DoorLogo = store.currentDoorWindowsInfo?.temp?.logo; + + app.Database.TemplateTable.Add(tr); + + hole.Template = tr.Id; + + let doorTr = await GetOnlineTemplate(store.currentDoorWindowsInfo?.temp?.id || "11777"); //窗户模板 默认ID:11777 + + for (let w of hole.FakerWalls) + { + let template = app.Database.WblockCloneObejcts([doorTr], app.Database.TemplateTable, new Map(), DuplicateRecordCloning.Ignore)[0] as TemplateRecord; + tr.Children.push(template.Id); + } + + await tr.UpdateTemplateTree(); + } +} diff --git a/src/Add-on/Room/DrawRoomWindow.ts b/src/Add-on/Room/DrawRoomWindow.ts new file mode 100644 index 000000000..a0c924572 --- /dev/null +++ b/src/Add-on/Room/DrawRoomWindow.ts @@ -0,0 +1,148 @@ +import { app } from "../../ApplicationServices/Application"; +import { DuplicateRecordCloning } from "../../Common/Status"; +import { RoomHolePolyline } from "../../DatabaseServices/Room/Entity/Wall/Hole/RoomHolePolyline"; +import { DrawDoorWindowPanel } from "../../DatabaseServices/Room/Entity/Wall/Hole/Window/DrawWindowPanel"; +import { DoorWindowPanelStore } from "../../DatabaseServices/Room/Entity/Wall/Hole/Window/WindowPanelStore"; +import { RoomWallArc } from "../../DatabaseServices/Room/Entity/Wall/RoomWallArc"; +import { TemplateArcWindowRecord } from "../../DatabaseServices/Template/ProgramTempate/TemplateArcWindowRecord"; +import { TemplateWindowRecord } from "../../DatabaseServices/Template/ProgramTempate/TemplateWindowRecord"; +import { GetOnlineTemplate } from "../../DatabaseServices/Template/TempateUtils"; +import { TemplateRecord } from "../../DatabaseServices/Template/TemplateRecord"; +import { ModalState } from "../../UI/Components/Modal/ModalInterface"; +import { Command_DrawHole, DrawHoleType, IHoleType } from "./DrawHole"; + +export class Command_DrawRoomWindow extends Command_DrawHole +{ + constructor(drawType: DrawHoleType, holeIType: IHoleType, private _IsOneKeyDraw: boolean = false) + { + super(drawType, holeIType); + } + + //一键绘制窗户配置 + async SetStoreParam(store: DoorWindowPanelStore) { } + + override async BeferDrawHole(drawHoleType: DrawHoleType): Promise + { + //呼出对话框 + let store = DoorWindowPanelStore.GetSingleInstance(); + if (this._IsOneKeyDraw) + { + store.InitOption(); + this.SetStoreParam(store); + } + else + { + app.Editor.ModalManage.RenderModal(DrawDoorWindowPanel, { store, drawHoleType, holeIType: IHoleType.Window }); + let state = await app.Editor.ModalManage.Wait(); + if (state.Status !== ModalState.Ok) + return ModalState.Cancel; + } + this.holeLength = store.m_Option.Length; + this.holeHeight = store.m_Option.Height; + this.offGround = store.m_Option.WindowOffGround; + return ModalState.Ok; + } + + override async AfterDrawHole(hole: RoomHolePolyline, drawHoleType: DrawHoleType): Promise + { + let store = DoorWindowPanelStore.GetSingleInstance(); + let tr = new TemplateWindowRecord();//顶层节点 + + tr.HoleObjectId = hole.Id; + + tr.InitBaseParams(); + tr.InitHoleParams(); + + tr.GetParam("WZ").expr = 25;//位置 + tr.GetParam("L").expr = store.m_Option.Length; + tr.GetParam("H").expr = store.m_Option.Height; + + if (drawHoleType === DrawHoleType.I) + { + if (store.m_Option.BayDist <= 1) + store.m_Option.IsBayWindow = false; + + tr.GetParam("WP").expr = store.m_Option.IsBayWindow ? store.m_Option.BayDist : 0; + } + else if (drawHoleType === DrawHoleType.L) + { + store.m_Option.IsBayWindow = store.m_Option.IsBayWindow && hole.FakerWalls.every(wall => !(wall instanceof RoomWallArc)); + tr.GetParam("ZWP").expr = store.m_Option.IsBayWindow ? store.m_Option.BayLeftDist : 0; + tr.GetParam("YWP").expr = store.m_Option.IsBayWindow ? store.m_Option.BayRightDist : 0; + } + else if (drawHoleType === DrawHoleType.U) + { + store.m_Option.IsBayWindow = store.m_Option.IsBayWindow && hole.FakerWalls.every(wall => !(wall instanceof RoomWallArc)); + tr.GetParam("ZWP").expr = store.m_Option.IsBayWindow ? store.m_Option.BayLeftDist : 0; + tr.GetParam("MWP").expr = store.m_Option.IsBayWindow ? store.m_Option.BayMiddleDist : 0; + tr.GetParam("YWP").expr = store.m_Option.IsBayWindow ? store.m_Option.BayRightDist : 0; + } + + tr.GetParam("CTS").expr = store.m_Option.HasWindowStone ? 1 : 0; + if (store.m_Option.HasWindowStone) + { + tr.GetParam("CTSH").expr = store.m_Option.StoneThick; + tr.GetParam("CTSTC").expr = store.m_Option.StoneBulge; + tr.GetParam("CTSZYTC").expr = store.m_Option.StoneLeftRightBulge; + } + + tr.LeftIsWall = store.m_Option.IsBayWindow ? store.m_Option.BayLeftIsWall : true; + tr.RightIsWall = store.m_Option.IsBayWindow ? store.m_Option.BayRightIsWall : true; + tr.WindowLogo = store.currentDoorWindowsInfo?.temp?.logo; + + app.Database.TemplateTable.Add(tr); + + hole.Template = tr.Id; + + let windowTr = await GetOnlineTemplate(store.currentDoorWindowsInfo?.temp?.id || "11777"); //窗户模板 默认ID:11777 + + //左飘窗 + if (store.m_Option.IsBayWindow) + { + let leftWindowTemplateId = store.currentLeftBayWindowsInfo?.temp?.id || "11777";//窗户模板 默认ID:11777 + + tr.LeftWindowLogo = store.currentLeftBayWindowsInfo?.temp?.logo; + tr.LeftWindowTemplateId = leftWindowTemplateId; + if (!tr.LeftIsWall) + { + let leftWindowTr = await GetOnlineTemplate(leftWindowTemplateId); + let template = app.Database.WblockCloneObejcts([leftWindowTr], app.Database.TemplateTable, new Map(), DuplicateRecordCloning.Ignore)[0] as TemplateRecord; + tr.Children.push(template.Id); + } + } + + for (let i = 0; i < hole.FakerWalls.length; i++) + { + if (hole.FakerWalls[i] instanceof RoomWallArc) + { + let template = new TemplateArcWindowRecord(); + template.InitBaseParams(); + app.Database.TemplateTable.Add(template); + template.InitWindowFrame(hole, i); + tr.Children.push(template.Id); + } + else + { + let template = app.Database.WblockCloneObejcts([windowTr], app.Database.TemplateTable, new Map(), DuplicateRecordCloning.Ignore)[0] as TemplateRecord; + tr.Children.push(template.Id); + } + } + + //右飘窗 + if (store.m_Option.IsBayWindow) + { + let rightWindowTemplateId = store.currentRightBayWindowsInfo?.temp?.id || "11777";//窗户模板 默认ID:11777 + + tr.RightWindowLogo = store.currentRightBayWindowsInfo?.temp?.logo; + tr.RightWindowTemplateId = rightWindowTemplateId; + if (!tr.RightIsWall) + { + let rightWindowTr = await GetOnlineTemplate(rightWindowTemplateId); + let template = app.Database.WblockCloneObejcts([rightWindowTr], app.Database.TemplateTable, new Map(), DuplicateRecordCloning.Ignore)[0] as TemplateRecord; + tr.Children.push(template.Id); + } + } + + await tr.UpdateTemplateTree(); + } +} diff --git a/src/Add-on/Room/OneKeyDrawWindow.ts b/src/Add-on/Room/OneKeyDrawWindow.ts new file mode 100644 index 000000000..8c93f2346 --- /dev/null +++ b/src/Add-on/Room/OneKeyDrawWindow.ts @@ -0,0 +1,74 @@ +import { DoorWindowPanelStore } from "../../DatabaseServices/Room/Entity/Wall/Hole/Window/WindowPanelStore"; +import { Command_DrawRoomDoor } from "./DrawRoomDoor"; +import { Command_DrawRoomWindow } from "./DrawRoomWindow"; + +export class Command_OneKeyDrawYZCWindow extends Command_DrawRoomWindow +{ + override async SetStoreParam(store: DoorWindowPanelStore): Promise + { + //一字窗 + store.m_Option.Length = 1800; + store.m_Option.Height = 1500; + store.m_Option.Thick = 280; + store.m_Option.IsBayWindow = false; + } +} + +export class Command_OneKeyDrawLDCWindow extends Command_DrawRoomWindow +{ + override async SetStoreParam(store: DoorWindowPanelStore): Promise + { + //落地窗 + store.m_Option.Length = 3000; + store.m_Option.Height = 2000; + store.m_Option.Thick = 280; + store.m_Option.WindowOffGround = 0; + store.m_Option.IsBayWindow = false; + } +} + +export class Command_OneKeyDrawPCWindow extends Command_DrawRoomWindow +{ + override async SetStoreParam(store: DoorWindowPanelStore): Promise + { + //飘窗 + store.m_Option.Length = 1900; + store.m_Option.Height = 1750; + store.m_Option.BayDist = 600; + store.m_Option.IsBayWindow = true; + } +} + +export class Command_OneKeyDrawZJCWindow extends Command_DrawRoomWindow +{ + override async SetStoreParam(store: DoorWindowPanelStore): Promise + { + //转角窗 + store.m_Option.Height = 1500; + store.m_Option.IsBayWindow = false; + store.m_Option.BayLeftIsWall = true; + store.m_Option.BayRightIsWall = true; + } +} + +export class Command_OneKeyDrawZJPCWindow extends Command_DrawRoomWindow +{ + override async SetStoreParam(store: DoorWindowPanelStore): Promise + { + //转角飘窗 + store.m_Option.Height = 1500; + store.m_Option.IsBayWindow = true; + store.m_Option.BayLeftDist = 600; + store.m_Option.BayRightDist = 600; + } +} + +export class Command_OneKeyDrawYJHMWindow extends Command_DrawRoomDoor +{ + override async SetStoreParam(store: DoorWindowPanelStore): Promise + { + //一键画门 + store.m_Option.Height = 2200; + store.m_Option.Length = 800; + } +} diff --git a/src/Add-on/testEntity/test.ts b/src/Add-on/testEntity/test.ts index c10ac9934..03c188e85 100644 --- a/src/Add-on/testEntity/test.ts +++ b/src/Add-on/testEntity/test.ts @@ -1,4 +1,9 @@ +import { app } from "../../ApplicationServices/Application"; +import { Entity } from "../../DatabaseServices/Entity/Entity"; +import { RoomHolePolyline } from "../../DatabaseServices/Room/Entity/Wall/Hole/RoomHolePolyline"; +import { TemplateArcWindowRecord } from "../../DatabaseServices/Template/ProgramTempate/TemplateArcWindowRecord"; import { Command } from "../../Editor/CommandMachine"; +import { PromptStatus } from "../../Editor/PromptResult"; import { HotCMD } from "../../Hot/HotCommand"; @@ -7,5 +12,14 @@ export class Test implements Command { async exec() { + let enRes = await app.Editor.GetEntity({ Filter: { filterTypes: [Entity] } }); + if (enRes.Status !== PromptStatus.OK) return; + let hole = enRes.Entity as RoomHolePolyline; + + let tr = new TemplateArcWindowRecord();//顶层节点 + tr.InitBaseParams(); + + app.Database.TemplateTable.Add(tr); + await tr.UpdateTemplateTree(); } } diff --git a/src/Common/CommandNames.ts b/src/Common/CommandNames.ts index e90e24e2f..d1adbac65 100644 --- a/src/Common/CommandNames.ts +++ b/src/Common/CommandNames.ts @@ -312,7 +312,20 @@ export enum CommandNames DrawIHole = "DRAWIHOLE", DrawLHole = "DRAWLHOLE", DrawUHole = "DRAWUHOLE", - + DrawIDoor = "DRAWIDOOR",//画门 + DrawIWindow = "DRAWIWINDOW", + DrawLWindow = "DRAWLWINDOW", + DrawUWindow = "DRAWUWINDOW",//弧形U型窗 + DrawUWindow2 = "DRAWUWINDOW2",//圆角U型窗 DrawDoorHole = "DRAWDOORHOLE", //画门洞 + + //一键画门窗 + YZC = "YZC",//一字窗 + LDC = "LDC",//落地窗 + PC = "PC",//飘窗 + ZJC = "ZJC",//转角窗 + ZJPC = "ZJPC", //转角飘窗 + YJHM = "YJHM",//一键画门 + Gallery = "GALLERY", //打开画廊 } diff --git a/src/DatabaseServices/Entity/Arc.ts b/src/DatabaseServices/Entity/Arc.ts index e46885325..fbd0cad3d 100644 --- a/src/DatabaseServices/Entity/Arc.ts +++ b/src/DatabaseServices/Entity/Arc.ts @@ -6,7 +6,7 @@ import { ObjectSnapMode } from '../../Editor/ObjectSnapMode'; import { Box3Ext } from '../../Geometry/Box'; import { angle, AsVector3, clampRad, equaln, equalv2, equalv3, MoveMatrix, polar, ZeroVec } from '../../Geometry/GeUtils'; import { Orbit } from '../../Geometry/Orbit'; -import { IntersectArcAndArc, IntersectCircleAndArc, IntersectEllipseAndCircleOrArc, IntersectLineAndArc, IntersectOption, IntersectPolylineAndCurve, reverseIntersectOption } from '../../GraphicsSystem/IntersectWith'; +import { IntersectArcAndArc, IntersectCircleAndArc, IntersectEllipseAndCircleOrArc, IntersectLineAndArc, IntersectOption, IntersectPolylineAndCurve, IntersectResult, reverseIntersectOption } from '../../GraphicsSystem/IntersectWith'; import { Factory } from '../CADFactory'; import { CADFiler } from '../CADFiler'; import { Shape2 } from '../Shape2'; @@ -491,11 +491,11 @@ export class Arc extends Curve return this; } - IntersectWith2(curve: Curve, intType: IntersectOption, tolerance = 1e-4) + IntersectWith2(curve: Curve, intType: IntersectOption, tolerance = 1e-4): IntersectResult[] { if (curve instanceof Arc || curve.constructor.name === "RoomWallArc") { - return IntersectArcAndArc(this, curve as Arc, intType); + return IntersectArcAndArc(this, curve as Arc, intType, tolerance); } if (curve instanceof Line || curve.constructor.name === "RoomWallLine") { diff --git a/src/DatabaseServices/Entity/Circle.ts b/src/DatabaseServices/Entity/Circle.ts index 406e7598d..f4f62b209 100644 --- a/src/DatabaseServices/Entity/Circle.ts +++ b/src/DatabaseServices/Entity/Circle.ts @@ -215,26 +215,26 @@ export class Circle extends Curve return []; } - override IntersectWith2(curve: Curve, intType: IntersectOption): IntersectResult[] + override IntersectWith2(curve: Curve, intType: IntersectOption, tolerance = 1e-5): IntersectResult[] { if (curve instanceof Arc) { - return IntersectCircleAndArc(this, curve, intType); + return IntersectCircleAndArc(this, curve, intType, tolerance); } if (curve instanceof Line) { - return SwapParam(IntersectLineAndCircle(curve, this, reverseIntersectOption(intType))); + return SwapParam(IntersectLineAndCircle(curve, this, reverseIntersectOption(intType), tolerance)); } if (curve instanceof Circle) { - return IntersectCircleAndCircle(this, curve); + return IntersectCircleAndCircle(this, curve, tolerance); } if (curve instanceof Ellipse) { return SwapParam(IntersectEllipseAndCircleOrArc(curve, this, intType)); } if (curve instanceof Polyline) - return SwapParam(IntersectPolylineAndCurve(curve, this, reverseIntersectOption(intType))); + return SwapParam(IntersectPolylineAndCurve(curve, this, reverseIntersectOption(intType), tolerance)); return []; } //******************** Curve function end*****************// diff --git a/src/DatabaseServices/Room/Entity/Wall/Hole/RoomHolePolyline.ts b/src/DatabaseServices/Room/Entity/Wall/Hole/RoomHolePolyline.ts index f0f6b1925..cd30eeb3d 100644 --- a/src/DatabaseServices/Room/Entity/Wall/Hole/RoomHolePolyline.ts +++ b/src/DatabaseServices/Room/Entity/Wall/Hole/RoomHolePolyline.ts @@ -1,18 +1,21 @@ import { Face3, Geometry, LineSegments, Matrix3, Mesh, Object3D, ShapeUtils, Vector2, Vector3 } from "three"; -import { arrayPushArray } from "../../../../../Common/ArrayExt"; +import { arrayLast, arrayPushArray } from "../../../../../Common/ArrayExt"; import { ColorMaterial } from "../../../../../Common/ColorPalette"; import { ObjectSnapMode } from "../../../../../Editor/ObjectSnapMode"; import { Box3Ext } from "../../../../../Geometry/Box"; import { CreatePolylinePath } from "../../../../../Geometry/CreatePolylinePath"; import { AsVector3, equalv2, ZAxis } from "../../../../../Geometry/GeUtils"; +import { IntersectOption } from "../../../../../GraphicsSystem/IntersectWith"; import { RenderType } from "../../../../../GraphicsSystem/RenderType"; import { Factory } from "../../../../CADFactory"; import { CADFiler } from "../../../../CADFiler"; import { CADObject } from "../../../../CADObject"; +import { Arc } from "../../../../Entity/Arc"; import { Curve } from "../../../../Entity/Curve"; import { Line } from "../../../../Entity/Line"; import { Polyline } from "../../../../Entity/Polyline"; import { LEFT_ROTATE_MTX2 } from "../../../ParseService/GetCurveParam"; +import { RoomWallArc } from "../RoomWallArc"; import { RoomWallBase } from "../RoomWallBase"; import { RoomWallLine } from "../RoomWallLine"; import { RoomHoleBase } from "./RoomHoleBase"; @@ -34,6 +37,11 @@ export class RoomHolePolyline extends RoomHoleBase //虽然使用了三维的点,但是我们实际使用的是二维的点 z总是等于0 private _Points: Vector3[] = []; private _FakerWalls: RoomWallBase[] = []; + private _WpDists: number[];//外飘距离 + private _WpLeftWall = false; + private _WpRightWall = false; + + get FakerWalls(): RoomWallBase[] { return this._FakerWalls; } public set FakerWalls(_FakerWalls: RoomWallBase[]) { this._FakerWalls = _FakerWalls; @@ -44,16 +52,141 @@ export class RoomHolePolyline extends RoomHoleBase arrayPushArray(this.LidCurves, w.LidCurves); if (w.Region) this.Regions.push(w.Region); - w.OCSNoClone.elements[14] = this._Matrix.elements[14]; + w.Z = this.Z; w.Height = this.Height; if (w instanceof RoomWallLine && w.Length > 1e-6) w.UpdateOCSToMinBox(); } + + this.UpdateWpDraw(); } - get FakerWalls(): RoomWallBase[] { return this._FakerWalls; } - constructor() { super(); } + set WpDist(wpdists: number[]) + { + this.WriteAllObjectRecord(); + this._WpDists = wpdists; + this.FakerWalls = this._FakerWalls;//这样是保险的 为了避免wpdist = undefine + this.Update(); + } + + get WpDist() { return this._WpDists; } + get WpLeftWall() { return this._WpLeftWall; } + get WpRightWall() { return this._WpRightWall; } + + set WpLeftWall(v) + { + if (v === this._WpLeftWall) return; + + this.WriteAllObjectRecord(); + this._WpLeftWall = v; + this.Update(); + } + + set WpRightWall(v) + { + if (v === this._WpRightWall) return; + + this.WriteAllObjectRecord(); + this._WpRightWall = v; + this.Update(); + } + + UpdateWpDraw() + { + //外飘 + if (this._WpDists?.length === this._FakerWalls.length) + { + this.LidCurves = this.LidCurves.map(c => c.Clone());//拷贝一份 避免修改源数据 + if (this._WpLeftWall) + this.LidCurves[0].StartPoint = this.LidCurves[0].GetPointAtDistance(-this._WpDists[0]); + if (this._WpRightWall) + this.LidCurves[1].EndPoint = this.LidCurves[1].GetPointAtDistance(this._WpDists[this._WpDists.length - 1] + this.LidCurves[1].Length); + + this.Regions = [this.GetWpRegion(this._WpDists)]; + } + } + + /** + * 根据外飘参数得到区域 + * + * @param wpdists 外飘距离 + * @cts 窗台石距离 + */ + GetWpRegion(wpdists: number[], cts: number = 0, ctszytc: number = 0): Polyline + { + let leftCurves: Curve[] = []; + let rightCurves: Curve[] = []; + + let wallCurves = this._FakerWalls.map((w: (RoomWallLine | RoomWallArc)) => w instanceof RoomWallLine ? new Line(w.StartPoint, w.EndPoint) : new Arc(w.Center, w.Radius, w.StartAngle, w.EndAngle, w.IsClockWise)); + + if (ctszytc)//窗台石左右延伸 + { + wallCurves[0].StartPoint = wallCurves[0].GetPointAtDistance(-ctszytc); + let lastW = arrayLast(this._FakerWalls); + let lastC = arrayLast(wallCurves); + lastC.EndPoint = lastC.GetPointAtDistance(lastW.Length + ctszytc); + } + + for (let i = 0; i < this._FakerWalls.length; i++) + { + let w = this._FakerWalls[i] as (RoomWallLine | RoomWallArc); + let c = wallCurves[i];//曲线 + let d = wpdists[i];//外飘距离 + + if (w instanceof RoomWallArc && d > 0) + { + //弧形墙凸出的一边为窗外 + leftCurves.push(c.GetOffsetCurves((w.Thickness * 0.5 + cts) * (w.IsClockWise ? 1 : -1))[0]); + rightCurves.push(c.GetOffsetCurves((w.Thickness * 0.5 + d) * (w.IsClockWise ? -1 : 1))[0]); + } + else + { + leftCurves.push(c.GetOffsetCurves((w.Thickness * 0.5 + cts))[0]); + rightCurves.push(c.GetOffsetCurves(-(w.Thickness * 0.5 + d))[0]); + } + } + + //获取合适相交点 + const getMinDistPoint = (pts: Vector3[], refer: Vector3) => + { + let pt = pts[0]; + let length = refer.distanceTo(pt); + for (let y = 1; y < pts.length; y++) + { + if (refer.distanceTo(pts[y]) < length) + pt = pts[y]; + } + return pt; + }; + + for (let i = 1; i < leftCurves.length; i++) + { + let pre = leftCurves[i - 1]; + let now = leftCurves[i]; + let iPt = getMinDistPoint(pre.IntersectWith(now, IntersectOption.ExtendBoth), now.StartPoint); + pre.EndPoint = iPt; + now.StartPoint = iPt; + } + + for (let i = 1; i < rightCurves.length; i++) + { + let pre = rightCurves[i - 1]; + let now = rightCurves[i]; + let iPt = getMinDistPoint(pre.IntersectWith(now, IntersectOption.ExtendBoth), now.StartPoint); + + pre.EndPoint = iPt; + now.StartPoint = iPt; + } + + let curves: Curve[] = [new Line(rightCurves[0].StartPoint, leftCurves[0].StartPoint)]; + arrayPushArray(curves, leftCurves); + curves.push(new Line(arrayLast(leftCurves).EndPoint, arrayLast(rightCurves).EndPoint)); + arrayPushArray(curves, rightCurves.reverse()); + let polyline = Polyline.Combine(curves, 1e-3); + + return polyline; + } override get BoundingBoxInOCS(): Box3Ext { @@ -65,6 +198,8 @@ export class RoomHolePolyline extends RoomHoleBase return new Box3Ext().copy(this.BoundingBox).applyMatrix4(this.OCSInv); } + get PointsCount() { return this._Points.length; } + get Points() { return this._Points.map(p => p.clone().applyMatrix4(this.OCSNoClone)); } set Points(pts: Vector3[]) { @@ -435,11 +570,27 @@ export class RoomHolePolyline extends RoomHoleBase let p = new Vector3(file.Read(), file.Read(), 0); this._Points.push(p); } + + if (ver > 1) + { + this._WpDists = []; + count = file.Read(); + for (let i = 0; i < count; i++) + this._WpDists.push(file.Read()); + } + + if (ver > 2) + { + let wpLeftRightWall = file.Read() as number; + + this._WpLeftWall = (wpLeftRightWall & 1) !== 0; + this._WpRightWall = (wpLeftRightWall & 2) !== 0; + } } //对象将自身数据写入到文件. override WriteFile(file: CADFiler) { - file.Write(1); + file.Write(3); super.WriteFile(file); file.Write(this._Points.length); @@ -448,6 +599,16 @@ export class RoomHolePolyline extends RoomHoleBase file.Write(p.x); file.Write(p.y); } + + file.Write(this._WpDists?.length ?? 0); + if (this._WpDists) + for (let d of this._WpDists) + file.Write(d); + + let wpLeftRightWall = 0; + if (this._WpLeftWall) wpLeftRightWall = 1; + if (this._WpRightWall) wpLeftRightWall += 2; + file.Write(wpLeftRightWall); } //局部撤销 ApplyPartialUndo(undoData: CADObject) diff --git a/src/DatabaseServices/Room/Entity/Wall/Hole/Window/DrawWindowPanel.tsx b/src/DatabaseServices/Room/Entity/Wall/Hole/Window/DrawWindowPanel.tsx new file mode 100644 index 000000000..5236015a1 --- /dev/null +++ b/src/DatabaseServices/Room/Entity/Wall/Hole/Window/DrawWindowPanel.tsx @@ -0,0 +1,295 @@ +import { Button, Checkbox, Classes, Collapse, Divider, Icon } from "@blueprintjs/core"; +import { observable } from "mobx"; +import { observer } from "mobx-react"; +import React from "react"; +import ReactDOM from "react-dom"; +import { DrawHoleType, IHoleType } from "../../../../../../Add-on/Room/DrawHole"; +import { app } from "../../../../../../ApplicationServices/Application"; +import { ModalState } from "../../../../../../UI/Components/Modal/ModalInterface"; +import { Location } from "../../../../../../UI/Components/ToolBar/ModifyModel/RoomBaseParams"; +import { DoorWindowPanelStore, DoorWindowParamsNames } from "./WindowPanelStore"; +import { WindowParamsComponent } from "./WindowParamsComponent"; +import { DrawWindowTempInfo } from "./WindowTempInfo"; +import { WindowTempSelect } from "./WindowTempSelect"; + +export enum Visibility +{ + Visible = "visible", + Hidden = "hidden", +} + +@observer +export class DrawDoorWindowPanel extends React.Component<{ store: DoorWindowPanelStore; drawHoleType: DrawHoleType; holeIType: IHoleType; }> +{ + private _CameraStateContainer: HTMLElement; + private startLocation = observable.box(Location.Middle); + + constructor(props) + { + super(props); + this._CameraStateContainer = document.createElement('div'); + this._CameraStateContainer.id = 'template-select'; + this._CameraStateContainer.style.zIndex = "99"; + document.getElementById('modal').appendChild(this._CameraStateContainer); + + if (window.screen.width <= 1450) + this._CameraStateContainer.style.left = '120px'; + else + this._CameraStateContainer.style.left = `calc(50vw - 650px)`; + + if (window.screen.height <= 750) + this._CameraStateContainer.style.top = `0px`; + else + this._CameraStateContainer.style.top = `calc(50vh - 425px)`; + + this._CameraStateContainer.style.visibility = Visibility.Hidden; + + ReactDOM.render(, this._CameraStateContainer); + } + + componentWillUnmount() + { + document.getElementById('modal').removeChild(this._CameraStateContainer); + this._CameraStateContainer = undefined; + } + + private startSelectTemplate(style: Visibility) + { + this._CameraStateContainer.style.visibility = style; + }; + + render() + { + let store = this.props.store; + return ( +
+
+
+ +

门窗参数

+
+
+
+ +
+
+ { + this.props.drawHoleType === DrawHoleType.I && + + } + + { + this.props.holeIType === IHoleType.Window && + } +
+ { + this.props.holeIType === IHoleType.Window &&
+ +
+ + { + this.props.store.m_Option.IsBayWindow = !this.props.store.m_Option.IsBayWindow; + }} + /> + +
+ + { + this.props.store.m_Option.BayLeftIsWall = !this.props.store.m_Option.BayLeftIsWall; + }} + /> + + { + this.props.store.m_Option.BayRightIsWall = !this.props.store.m_Option.BayRightIsWall; + }} + /> +
+
+ + +
+
+ { + this.props.drawHoleType === DrawHoleType.I && + + } + { + this.props.drawHoleType !== DrawHoleType.I && + + } + { + this.props.drawHoleType === DrawHoleType.U && + + } + { + this.props.drawHoleType !== DrawHoleType.I && + + } +
+
+
+
+ } + { + this.props.holeIType === IHoleType.Window &&
+ +
+ + { + this.props.store.m_Option.HasWindowStone = !this.props.store.m_Option.HasWindowStone; + }} + /> + +
+ + + +
+
+
+
+ } +
+
+
+
+
+
+
+
+
+ ); + } +} diff --git a/src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowPanelStore.ts b/src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowPanelStore.ts new file mode 100644 index 000000000..78c2e5a09 --- /dev/null +++ b/src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowPanelStore.ts @@ -0,0 +1,72 @@ +import { observable, toJS } from "mobx"; +import { DefaultWindowPanelOption as DefaultDoorWindowPanelOption } from "../../../../../../Editor/DefaultConfig"; +import { IConfigOption } from "../../../../../../UI/Components/Board/UserConfig"; +import { WindowPanelConfigOption as DoorWindowPanelConfigOption } from "../../../../../../UI/Store/BoardInterface"; +import { IConfigStore } from "../../../../../../UI/Store/BoardStore"; +import { ISelectTempInfo } from "../../../../../../UI/Store/DoorInterface"; +import { TemplateWindowRecord } from "../../../../../Template/ProgramTempate/TemplateWindowRecord"; + +export enum DoorWindowParamsNames +{ + Length = "Length", + Height = "Height", + Thick = "Thick", + WindowOffGround = "WindowOffGround", + IsBayWindow = "IsBayWindow", + BayLeftIsWall = "BayLeftIsWall", + BayRightIsWall = "BayRightIsWall", + BayDist = "BayDist", + BayLeftDist = "BayLeftDist", + BayMiddleDist = "BayMiddleDist", + BayRightDist = "BayRightDist", + HasWindowStone = "HasWindowStone", + StoneThick = "StoneThick", + StoneBulge = "StoneBulge", + StoneLeftRightBulge = "StoneLeftRightBulge", +} + +export class DoorWindowPanelStore implements IConfigStore +{ + @observable configName = "默认"; + @observable m_Option: DoorWindowPanelConfigOption = { ...DefaultDoorWindowPanelOption }; + @observable configsNames: string[] = []; + @observable selectTemplateInfo: ISelectTempInfo = { temp: { id: "", name: "", logo: "" } }; + @observable currentDoorWindowsInfo: ISelectTempInfo; //应用的门窗信息 + @observable currentLeftBayWindowsInfo: ISelectTempInfo; //应用的左飘窗信息 + @observable currentRightBayWindowsInfo: ISelectTempInfo; //应用的右飘窗信息 + + SaveConfig() + { + let newConfig: IConfigOption = {}; + newConfig.option = toJS(this.m_Option); + return newConfig; + }; + + InitOption() + { + Object.assign(this.m_Option, DefaultDoorWindowPanelOption); + this.currentDoorWindowsInfo = undefined; + this.currentLeftBayWindowsInfo = undefined; + this.currentRightBayWindowsInfo = undefined; + } + + InitCurrentWindowOption(windowTemp: TemplateWindowRecord) + { + if (!windowTemp) return; + this.m_Option.BayLeftIsWall = windowTemp.LeftIsWall; + this.m_Option.BayRightIsWall = windowTemp.RightIsWall; + } + + UpdateOption(cof: IConfigOption) + { + this.m_Option = cof.option; + } + + private static _SingleInstance: DoorWindowPanelStore; + static GetSingleInstance(): DoorWindowPanelStore + { + if (this._SingleInstance) return this._SingleInstance; + this._SingleInstance = new DoorWindowPanelStore; + return this._SingleInstance; + } +} diff --git a/src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowParamsComponent.tsx b/src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowParamsComponent.tsx new file mode 100644 index 000000000..08752d642 --- /dev/null +++ b/src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowParamsComponent.tsx @@ -0,0 +1,101 @@ +import { Intent, NumericInput, Position, Slider, Tooltip } from "@blueprintjs/core"; +import { observable } from "mobx"; +import { observer } from "mobx-react"; +import React from "react"; +import { safeEval } from "../../../../../../Common/eval"; +import { KeyBoard } from "../../../../../../Common/KeyEnum"; +import { DoorWindowPanelStore } from "./WindowPanelStore"; + + +interface SizeComponentProps +{ + store: DoorWindowPanelStore; + title: string; //标题提示字符串 + sizeKey: string; //字段 + sliderMin: number; + sliderMax: number; +} +@observer +export class WindowParamsComponent extends React.Component +{ + @observable _Value: number = parseInt(this.props.store.m_Option[this.props.sizeKey]); + @observable _IsPopoverOpen: boolean = false; + _SizeRef = React.createRef(); + + render() + { + let store = this.props.store; + return ( +
+ {this.props.title} + + { + this._Value = value; + this._SizeRef.current.inputElement.value = value.toString(); + }} + onRelease={(value) => + { + store.m_Option[this.props.sizeKey] = value; + }} + /> + + + + { + let val = safeEval(e.value); + if (!isNaN(val)) + { + if (val < this.props.sliderMin || val > this.props.sliderMax) + this._IsPopoverOpen = true; + else + this._IsPopoverOpen = false; + } + else + this._IsPopoverOpen = true; + }} + onKeyDown={(e) => + { + if (e.keyCode === KeyBoard.Enter) + { + this._SizeRef.current.inputElement.blur(); + } + else if (e.keyCode === KeyBoard.Escape) + { + this._SizeRef.current.inputElement.value = store.m_Option[this.props.sizeKey].toFixed(); + this._SizeRef.current.inputElement.blur(); + } + e.stopPropagation(); + }} + onBlur={(e) => + { + if (this._IsPopoverOpen) + { + e.target.value = store.m_Option[this.props.sizeKey]; + this._IsPopoverOpen = false; + } + else + { + this._Value = parseFloat(e.currentTarget.value); + store.m_Option[this.props.sizeKey] = parseFloat(e.currentTarget.value); + } + }} + /> + +
+ ); + } +} diff --git a/src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowTempInfo.tsx b/src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowTempInfo.tsx new file mode 100644 index 000000000..7da987703 --- /dev/null +++ b/src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowTempInfo.tsx @@ -0,0 +1,180 @@ +import { Card, Elevation, Icon } from "@blueprintjs/core"; +import { IObservableValue, observable } from "mobx"; +import { observer } from "mobx-react"; +import React from "react"; +import { app } from "../../../../../../ApplicationServices/Application"; +import { CURRENT_HOST } from "../../../../../../Common/HostUrl"; +import { DuplicateRecordCloning } from "../../../../../../Common/Status"; +import { CommandWrap } from "../../../../../../Editor/CommandMachine"; +import { ModalState } from "../../../../../../UI/Components/Modal/ModalInterface"; +import { Location } from "../../../../../../UI/Components/ToolBar/ModifyModel/RoomBaseParams"; +import { TemplateArcWindowRecord } from "../../../../../Template/ProgramTempate/TemplateArcWindowRecord"; +import { TemplateRoomDoorRecord } from "../../../../../Template/ProgramTempate/TemplateRoomDoorRecord"; +import { TemplateWindowRecord } from "../../../../../Template/ProgramTempate/TemplateWindowRecord"; +import { DeleteTempate, GetOnlineTemplate, ReplaceTemplate } from "../../../../../Template/TempateUtils"; +import { TemplateRecord } from "../../../../../Template/TemplateRecord"; +import { RoomHolePolyline } from "../RoomHolePolyline"; +import { Visibility } from "./DrawWindowPanel"; +import { DoorWindowPanelStore } from "./WindowPanelStore"; +import { WindowTempSelect } from "./WindowTempSelect"; + +interface WindowTempInfoProps +{ + store: DoorWindowPanelStore; + doorWindowTemp?: TemplateWindowRecord | TemplateRoomDoorRecord; + logo?: string; + startLocation?: IObservableValue; + location?: string; + showSizeInfo?: boolean; + interactive?: boolean; + selectDiv?: HTMLElement; +} + +@observer +export class WindowTempInfo extends React.Component +{ + @observable logo: string = this.props.logo; + render() + { + let store = this.props.store; + return ( +
+ + { + this.logo ? : + } + + +
+ {this.props.doorWindowTemp?.Name || "默认"} + {store.m_Option.Length}*{store.m_Option.Height}*{store.m_Option.Thick} +
+
+ ); + } + + private startSelectTemplate = async () => + { + this.props.store.selectTemplateInfo.temp = { id: "", name: "", logo: this.logo }; + app.Editor.ModalManage.RenderModal(WindowTempSelect, { store: this.props.store, filter: ["门窗"] }); + let state = await app.Editor.ModalManage.Wait(); + if (state.Status === ModalState.Ok) + { + CommandWrap(async () => + { + for (let index of this.getWindowLocation()) + { + let oldTr = this.props.doorWindowTemp.Children[index].Object as TemplateRecord; + if (oldTr instanceof TemplateArcWindowRecord) continue; //弧形窗 + let selectWindowTr = await GetOnlineTemplate(this.props.store.currentDoorWindowsInfo.temp.id || "11777"); + let newTr = app.Database.WblockCloneObejcts([selectWindowTr], app.Database.TemplateTable, new Map(), DuplicateRecordCloning.Ignore)[0] as TemplateRecord; + + //记录左右飘窗Tr.id + if (this.props.location === Location.Right) + (this.props.doorWindowTemp as TemplateWindowRecord).RightWindowTemplateId = this.props.store.currentDoorWindowsInfo.temp.id || "11777"; + else if (this.props.location === Location.Left) + (this.props.doorWindowTemp as TemplateWindowRecord).LeftWindowTemplateId = this.props.store.currentDoorWindowsInfo.temp.id || "11777"; + + await ReplaceTemplate(oldTr, newTr, false, true); + DeleteTempate(oldTr); + } + await this.props.doorWindowTemp.UpdateTemplateTree(); + }); + } + }; + + + /** + * @private + * @return {*} {number[]} 单个object所处下标 + * @memberof WindowTempInfo + */ + private getWindowLocation(): number[] + { + let strs = []; + + this.logo = this.props.store.currentDoorWindowsInfo.temp.logo; + if (this.props.doorWindowTemp instanceof TemplateWindowRecord) + { + //窗 + let isBayWindow = this.props.doorWindowTemp.WpDists.every(x => x !== 0); + + //object[] => strs[] + if (isBayWindow && !this.props.doorWindowTemp.LeftIsWall) + strs.push(Location.Left); + + let hole = this.props.doorWindowTemp.HoleObjectId.Object as RoomHolePolyline; + for (let w of hole.FakerWalls) + strs.push(Location.Middle); + + if (isBayWindow && !this.props.doorWindowTemp.RightIsWall) + strs.push(Location.Right); + + //return [对应下标] + if (this.props.location === Location.Middle) + { + this.props.doorWindowTemp.WindowLogo = this.props.store.currentDoorWindowsInfo.temp.logo; + let indexs = []; + for (let i = 0; i < hole.FakerWalls.length; i++) + indexs.push(strs.indexOf(this.props.location) + i); + return indexs; + } + else if (this.props.location === Location.Right) + this.props.doorWindowTemp.RightWindowLogo = this.props.store.currentDoorWindowsInfo.temp.logo; + else if (this.props.location === Location.Left) + this.props.doorWindowTemp.LeftWindowLogo = this.props.store.currentDoorWindowsInfo.temp.logo; + + return [strs.indexOf(this.props.location)]; + } + else + { + //门 + this.props.doorWindowTemp.DoorLogo = this.props.store.currentDoorWindowsInfo.temp.logo; + return [0]; + } + }; +} + +@observer +export class DrawWindowTempInfo extends React.Component +{ + render() + { + let store = this.props.store; + return ( +
+ + { + this.props.logo ? : + } + + { + this.props.showSizeInfo && +
+ 默认 + {store.m_Option.Length}*{store.m_Option.Height}*{store.m_Option.Thick} +
+ } +
+ ); + } + + private startSelectTemplate = () => + { + if (!this.props.interactive) return; + this.props.startLocation.set(this.props.location); + this.props.selectDiv.style.visibility = Visibility.Visible; + }; +} diff --git a/src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowTempSelect.tsx b/src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowTempSelect.tsx new file mode 100644 index 000000000..1f2d6b136 --- /dev/null +++ b/src/DatabaseServices/Room/Entity/Wall/Hole/Window/WindowTempSelect.tsx @@ -0,0 +1,200 @@ +import { Button, Classes } from "@blueprintjs/core"; +import { IObservableValue, observable } from "mobx"; +import { observer } from "mobx-react"; +import React from "react"; +import { app } from "../../../../../../ApplicationServices/Application"; +import { TemplateUrls } from "../../../../../../Common/HostUrl"; +import { KeyBoard } from "../../../../../../Common/KeyEnum"; +import { DirectoryId } from "../../../../../../Common/Request"; +import { ModalFooter, ModalHeader } from "../../../../../../UI/Components/Modal/ModalContainer"; +import { ModalState } from "../../../../../../UI/Components/Modal/ModalInterface"; +import { CommonPanel } from "../../../../../../UI/Components/SourceManage/CommonPanel"; +import { TemplateList } from "../../../../../../UI/Components/Template/TemplateList"; +import { Location } from "../../../../../../UI/Components/ToolBar/ModifyModel/RoomBaseParams"; +import { IDrawerDoorTempInfo } from "../../../../../../UI/Store/DoorInterface"; +import { ITemplateParam } from "../../../../../../UI/Store/RightPanelStore/ITemplateParam"; +import { Visibility } from "./DrawWindowPanel"; +import { DoorWindowPanelStore } from "./WindowPanelStore"; + +export interface IWindowTempSelectProps +{ + selectDiv?: HTMLElement; + store: DoorWindowPanelStore; + filter: string[], + location?: IObservableValue; +} + +@observer +export class WindowTempSelect extends React.Component { + @observable private currentProps: ITemplateParam[] = []; + @observable private currentInfo: IDrawerDoorTempInfo = { id: "", name: "" }; + private container: HTMLElement; + constructor(props) + { + super(props); + } + + componentDidMount() + { + this.container.addEventListener("keydown", e => + { + if (e.keyCode === KeyBoard.Escape) + { + this.handleClose(ModalState.Cancel); + } + else if (e.keyCode === KeyBoard.Space || e.keyCode === KeyBoard.Enter) + { + this.applySelectTemp(); + this.handleClose(ModalState.Ok); + } + e.stopPropagation(); + }); + this.container.focus(); + + if (!this.props.store.selectTemplateInfo) + { + if (this.props.store instanceof DoorWindowPanelStore) + { + this.props.store.selectTemplateInfo = { + temp: { id: "", name: "", logo: "", title: "选择窗户" }, + }; + } + } + } + + public render() + { + return ( +
this.container = el} + tabIndex={-1} + > + { this.handleClose(ModalState.Cancel); }} + /> +
+ + { + this.currentProps.length = 0; + }} + dirNameFilter={this.props.filter} + > + + +
+ +
+ ); + } + + private handleClose = (modalState: ModalState) => + { + if (this.props.selectDiv) + this.props.selectDiv.style.visibility = Visibility.Hidden; + else + { + app.Editor.ModalManage.m_PromisRes({ Status: modalState }); + app.Editor.ModalManage.Destory(); + } + }; + + private applySelectTemp() + { + switch (this.props.location?.get()) + { + case Location.Middle: + this.props.store.currentDoorWindowsInfo = { + temp: { + name: "", + id: this.props.store.selectTemplateInfo.temp.id, + logo: this.props.store.selectTemplateInfo.temp.logo + } + }; + break; + case Location.Left: + this.props.store.currentLeftBayWindowsInfo = { + temp: { + name: "", + id: this.props.store.selectTemplateInfo.temp.id, + logo: this.props.store.selectTemplateInfo.temp.logo + } + }; + break; + case Location.Right: + this.props.store.currentRightBayWindowsInfo = { + temp: { + name: "", + + id: this.props.store.selectTemplateInfo.temp.id, + logo: this.props.store.selectTemplateInfo.temp.logo + } + }; + break; + case Location.All: + this.props.store.currentDoorWindowsInfo = { + temp: { + name: "", + id: this.props.store.selectTemplateInfo.temp.id, + logo: this.props.store.selectTemplateInfo.temp.logo + } + }; + this.props.store.currentLeftBayWindowsInfo = { + temp: { + name: "", + id: this.props.store.selectTemplateInfo.temp.id, + logo: this.props.store.selectTemplateInfo.temp.logo + } + }; + this.props.store.currentRightBayWindowsInfo = { + temp: { + name: "", + id: this.props.store.selectTemplateInfo.temp.id, + logo: this.props.store.selectTemplateInfo.temp.logo + } + }; + break; + default: + this.props.store.currentDoorWindowsInfo = { + temp: { + name: "", + id: this.props.store.selectTemplateInfo.temp.id, + logo: this.props.store.selectTemplateInfo.temp.logo + } + }; + break; + } + }; +} diff --git a/src/DatabaseServices/Template/Positioning/PositioningBoardSpace.ts b/src/DatabaseServices/Template/Positioning/PositioningBoardSpace.ts index 04c88d97d..466a830a2 100644 --- a/src/DatabaseServices/Template/Positioning/PositioningBoardSpace.ts +++ b/src/DatabaseServices/Template/Positioning/PositioningBoardSpace.ts @@ -9,7 +9,7 @@ import { Positioning } from "./Positioning"; @Factory export class PositioningBoardSpace extends Positioning { - @AutoRecord ObjectId: ObjectId; + @AutoRecord ObjectId: ObjectId; /** * 定位 */ @@ -30,7 +30,7 @@ export class PositioningBoardSpace extends Positioning ReadFile(file: CADFiler): void { let ver = file.Read(); - this.ObjectId = file.ReadObjectId(); + this.ObjectId = file.ReadObjectId() as ObjectId; } WriteFile(file: CADFiler): void diff --git a/src/DatabaseServices/Template/Positioning/PositioningFixed.ts b/src/DatabaseServices/Template/Positioning/PositioningFixed.ts new file mode 100644 index 000000000..f7e4e1a95 --- /dev/null +++ b/src/DatabaseServices/Template/Positioning/PositioningFixed.ts @@ -0,0 +1,38 @@ +import { Matrix4, Vector3 } from "three"; +import { Factory } from "../../CADFactory"; +import { CADFiler } from "../../CADFiler"; +import { Positioning } from "./Positioning"; + +@Factory +export class PositioningFixed extends Positioning +{ + override async Positioning() + { + } + + //#region File + ReadFile(file: CADFiler): void + { + let ver = file.Read(); + + if (!this.SpaceCS) this.SpaceCS = new Matrix4; + if (!this.SpaceSize) this.SpaceSize = new Vector3; + + for (let i = 0; i < 16; i++) + this.SpaceCS.elements[i] = file.Read(); + + this.SpaceSize.set(file.Read(), file.Read(), file.Read()); + } + + WriteFile(file: CADFiler): void + { + file.Write(1); + + for (let e of this.SpaceCS.elements) + file.Write(e); + file.Write(this.SpaceSize.x); + file.Write(this.SpaceSize.y); + file.Write(this.SpaceSize.z); + } + //#endregion +} diff --git a/src/DatabaseServices/Template/ProgramTempate/TemplateArcWindowRecord.ts b/src/DatabaseServices/Template/ProgramTempate/TemplateArcWindowRecord.ts new file mode 100644 index 000000000..91464988c --- /dev/null +++ b/src/DatabaseServices/Template/ProgramTempate/TemplateArcWindowRecord.ts @@ -0,0 +1,280 @@ +import { MathUtils, Matrix4, Vector3 } from "three"; +import { app } from "../../../ApplicationServices/Application"; +import { equaln, ZAxis } from "../../../Geometry/GeUtils"; +import { BoardType } from "../../../UI/Store/BoardInterface"; +import { AutoRecord } from "../../AutoRecord"; +import { Factory } from "../../CADFactory"; +import { CADFiler } from "../../CADFiler"; +import { CADObject } from "../../CADObject"; +import { Arc } from "../../Entity/Arc"; +import { Board } from "../../Entity/Board"; +import { Line } from "../../Entity/Line"; +import { Polyline } from "../../Entity/Polyline"; +import { HardwareCompositeEntity } from "../../Hardware/HardwareCompositeEntity"; +import { DefaultParamMap, SetMaterialParams } from "../../IMaterialDefaultParam"; +import { ObjectId } from "../../ObjectId"; +import { PhysicalMaterialRecord } from "../../PhysicalMaterialRecord"; +import { RoomHolePolyline } from "../../Room/Entity/Wall/Hole/RoomHolePolyline"; +import { RoomWallArc } from "../../Room/Entity/Wall/RoomWallArc"; +import { TemplateRecord } from "../TemplateRecord"; + +/** + * 弧形窗 + */ +@Factory +export class TemplateArcWindowRecord extends TemplateRecord +{ + constructor() + { + super(); + this.name = "弧形窗(自动)"; + } + + @AutoRecord HoleObjectId: ObjectId; + @AutoRecord ArcWallIndex: number; + + InitWindowFrame(hole: RoomHolePolyline, arcWallIndex: number) + { + if (!hole.objectId) return; + this.HoleObjectId = hole.objectId; + this.ArcWallIndex = arcWallIndex; + + //下边框 + let bottomFrame = Board.CreateBoard(50, 50, 50, BoardType.Layer); + bottomFrame.ApplyMatrix(bottomFrame.OCSInv); + bottomFrame.ColorIndex = 8; + + //玻璃 + let glass = bottomFrame.Clone(); + glass.ColorIndex = 7; + + //其余边框 + let leftFrame = bottomFrame.Clone(); + + let rightFrame = bottomFrame.Clone(); + + let topFrame = bottomFrame.Clone(); + + let windowModel = new HardwareCompositeEntity(); + windowModel.Entitys.push(leftFrame, rightFrame, topFrame, bottomFrame, glass); + app.Database.ModelSpace.Append(windowModel); + this.Objects.push(windowModel.Id); + + //边框材质 + let frameMaterial = app.Database.MaterialTable.GetAt("弧形窗边框"); + if (!frameMaterial) + { + frameMaterial = new PhysicalMaterialRecord(); + SetMaterialParams(frameMaterial, DefaultParamMap.金属); + frameMaterial.Name = "弧形窗边框"; + frameMaterial.type = "金属"; + frameMaterial.color = "#333333"; + frameMaterial.roughness = 0.4;//粗糙度 + frameMaterial.specular = 0.5;//高光 + frameMaterial.Update(); + app.Database.MaterialTable.Add(frameMaterial); + } + windowModel.Material = frameMaterial.Id; + + //玻璃材质 + let glassMaterial = app.Database.MaterialTable.GetAt("弧形窗玻璃"); + if (!glassMaterial) + { + glassMaterial = new PhysicalMaterialRecord(); + SetMaterialParams(glassMaterial, DefaultParamMap.玻璃); + glassMaterial.Name = "弧形窗玻璃"; + glassMaterial.type = "玻璃"; + glassMaterial.transparent = true; + glassMaterial.opacity = 0.4; + glassMaterial.Update(); + app.Database.MaterialTable.Add(glassMaterial); + } + glass.Material = glassMaterial.Id; + } + + protected async Update() + { + await super.Update(); + let [windowModelId] = this.Objects; + if (!windowModelId || windowModelId.IsErase) return; + + let hole = this.HoleObjectId?.Object; + let arcWall = hole?.FakerWalls[this.ArcWallIndex] as RoomWallArc; + + if (!hole || !arcWall) return; + + let windowModel = windowModelId.Object as HardwareCompositeEntity; + + let leftFrame = windowModel.Entitys[0] as Board; + let rightFrame = windowModel.Entitys[1] as Board; + let topFrame = windowModel.Entitys[2] as Board; + let bottomFrame = windowModel.Entitys[3] as Board; + let glass = windowModel.Entitys[4] as Board; + + bottomFrame.ApplyMatrix(bottomFrame.OCSInv); + + let wpdist = hole.WpDist[0] !== 0 ? hole.WpDist[0] + arcWall.Thickness / 2 + 25 : 0;//外飘时考虑 墙厚 + 边框厚 + 外飘距离 + + const radius = arcWall.Radius; + let leftArc = arcWall.LeftCurves[0] as Arc; + let rightArc = arcWall.RightCurves[0] as Arc; + + //弧形窗角度 + let allAngle = arcWall.AllAngle; + let midAngle = allAngle / 2; + let midDistance = (leftArc.EndPoint.distanceTo(leftArc.StartPoint) + rightArc.EndPoint.distanceTo(rightArc.StartPoint)) / 4; + + let maxThanPI = allAngle > Math.PI; + + //弧形圆点 + let center: Vector3; + if (equaln(allAngle, Math.PI, 1e-7)) + center = new Vector3(radius); + else if (allAngle < Math.PI) + center = new Vector3(midDistance, - Math.sqrt(radius * radius - midDistance * midDistance)); + else if (maxThanPI) //弧形大于Π时,会翻转 + { + let radian = 2 * Math.PI / 360 * MathUtils.radToDeg(Math.PI - midAngle); + center = new Vector3(radius + wpdist, Math.cos(radian) * (radius + wpdist)); + } + + // let reduceAngle = MathUtils.degToRad(25 * 180 / (Math.PI * radius)); + + let startAngle = Math.PI / 2 + midAngle; + let endAngle = Math.PI / 2 - midAngle; + + //内缩 + // if (hole.FakerWalls.length === 2) + // { + // if (equalv3(arcWall.StartPoint.clone().applyMatrix4(hole.OCSInv), new Vector3(0))) + // startAngle = startAngle + reduceAngle * (maxThanPI ? 1 : -1); + // else + // endAngle = endAngle + reduceAngle * (maxThanPI ? -1 : 1);; + // } + + //下边框弧形轮廓 + let bottomArc1 = new Arc(center, radius - 25 + wpdist, startAngle, endAngle); + let bottomArc2 = new Arc(center, radius + 25 + wpdist, startAngle, endAngle); + let bottomCloseLine1 = new Line(bottomArc1.StartPoint, bottomArc2.StartPoint); + let bottomCloseLine2 = new Line(bottomArc1.EndPoint, bottomArc2.EndPoint); + let bottomContourCurve = Polyline.Combine([bottomArc1, bottomCloseLine1, bottomArc2, bottomCloseLine2]); + bottomFrame.ContourCurve = bottomContourCurve; + + //玻璃弧长50 的角度 + let glassAngle1 = MathUtils.degToRad(50 * 180 / (Math.PI * (radius - 2))); + let glassAngle2 = MathUtils.degToRad(50 * 180 / (Math.PI * (radius + 2))); + + //玻璃 + let glassArc1 = new Arc(center, radius - 2 + wpdist, startAngle - glassAngle1, endAngle + glassAngle1); + let glassArc2 = new Arc(center, radius + 2 + wpdist, startAngle - glassAngle2, endAngle + glassAngle2); + let glassCloseLine1 = new Line(glassArc1.StartPoint, glassArc2.StartPoint); + let glassCloseLine2 = new Line(glassArc1.EndPoint, glassArc2.EndPoint); + let glassContourCurve = Polyline.Combine([glassArc1, glassCloseLine1, glassArc2, glassCloseLine2]); + + glass.Thickness = hole.Height - 100; + glass.ApplyMatrix(glass.OCSInv); + + glass.ContourCurve = glassContourCurve; + glass.Position = glass.Position.add(new Vector3(0, 0, 50)); + + //上边框 + topFrame.CopyFrom(bottomFrame); + topFrame.Position = bottomFrame.Position.add(new Vector3(0, 0, hole.Height - 50)); + + //边框弧长50 的角度 + let angle1 = MathUtils.degToRad(50 * 180 / (Math.PI * (radius - 25))); + let angle2 = MathUtils.degToRad(50 * 180 / (Math.PI * (radius + 25))); + + //左边框弧形轮廓 + let leftArc1 = bottomArc1.Clone(); + let leftArc2 = bottomArc2.Clone(); + if (maxThanPI) + { + leftArc1.StartAngle = leftArc1.EndAngle + angle1; + leftArc2.StartAngle = leftArc2.EndAngle + angle2; + } + else + { + leftArc1.EndAngle = leftArc1.StartAngle - angle1; + leftArc2.EndAngle = leftArc2.StartAngle - angle2; + } + + let leftCloseLine1 = new Line(leftArc1.StartPoint, leftArc2.StartPoint); + let leftCloseLine2 = new Line(leftArc1.EndPoint, leftArc2.EndPoint); + let leftFrameContourCurve = Polyline.Combine([leftArc1, leftCloseLine1, leftArc2, leftCloseLine2]); + + leftFrame.Thickness = hole.Height - 100; + leftFrame.ApplyMatrix(leftFrame.OCSInv); + + leftFrame.ContourCurve = leftFrameContourCurve; + leftFrame.Position = leftFrame.Position.add(new Vector3(0, 0, 50)); + + //右边框弧形轮廓 + let rightArc1 = bottomArc1.Clone(); + let rightArc2 = bottomArc2.Clone(); + if (maxThanPI) + { + rightArc1.EndAngle = rightArc1.StartAngle - angle1; + rightArc2.EndAngle = rightArc2.StartAngle - angle2; + } + else + { + rightArc1.StartAngle = rightArc1.EndAngle + angle1; + rightArc2.StartAngle = rightArc2.EndAngle + angle2; + } + let rightCloseLine1 = new Line(rightArc1.StartPoint, rightArc2.StartPoint); + let rightCloseLine2 = new Line(rightArc1.EndPoint, rightArc2.EndPoint); + let rightFrameContourCurve = Polyline.Combine([rightArc1, rightCloseLine1, rightArc2, rightCloseLine2]); + + rightFrame.Thickness = hole.Height - 100; + rightFrame.ApplyMatrix(rightFrame.OCSInv); + + rightFrame.ContourCurve = rightFrameContourCurve; + rightFrame.Position = rightFrame.Position.add(new Vector3(0, 0, 50)); + + //定位 + let pos = arcWall.IsClockWise ? arcWall.StartPoint.clone() : arcWall.EndPoint.clone(); + let x = arcWall.EndPoint.sub(arcWall.StartPoint).normalize().multiplyScalar(arcWall.IsClockWise ? 1 : -1); + let z = ZAxis; + let y = new Vector3().crossVectors(ZAxis, x); + + if (maxThanPI) + { + //外飘时基点外移 + let v = arcWall.Center.clone().sub(pos).normalize(); + pos.subVectors(pos, v.multiplyScalar(wpdist)); + + //边框处在第一象限 + let scalar = (rightArc1.StartPoint.x + rightArc2.StartPoint.x) / 2; + pos.subVectors(pos, x.clone().multiplyScalar(scalar)); + } + + windowModel.OCS = new Matrix4().makeBasis(x, y, z).setPosition(pos); + windowModel.Update(); + } + + //#region -------------------------File------------------------- + //对象从文件中读取数据,初始化自身 + override ReadFile(file: CADFiler) + { + let ver = file.Read(); + super.ReadFile(file); + this.HoleObjectId = file.ReadObjectId() as any; + this.ArcWallIndex = file.Read(); + } + //对象将自身数据写入到文件. + override WriteFile(file: CADFiler) + { + file.Write(1); + super.WriteFile(file); + file.WriteObjectId(this.HoleObjectId); + file.Write(this.ArcWallIndex); + } + + //局部撤销 + override ApplyPartialUndo(undoData: CADObject) + { + super.ApplyPartialUndo(undoData); + } + //#endregion +} diff --git a/src/DatabaseServices/Template/ProgramTempate/TemplateRoomDoorRecord.ts b/src/DatabaseServices/Template/ProgramTempate/TemplateRoomDoorRecord.ts new file mode 100644 index 000000000..83ffb31c8 --- /dev/null +++ b/src/DatabaseServices/Template/ProgramTempate/TemplateRoomDoorRecord.ts @@ -0,0 +1,93 @@ +import { Vector3 } from "three"; +import { AutoRecord } from "../../AutoRecord"; +import { Factory } from "../../CADFactory"; +import { CADFiler } from "../../CADFiler"; +import { CADObject } from "../../CADObject"; +import { ObjectId } from "../../ObjectId"; +import { RoomHolePolyline } from "../../Room/Entity/Wall/Hole/RoomHolePolyline"; +import { PositioningFixed } from "../Positioning/PositioningFixed"; +import { TemplateRecord } from "../TemplateRecord"; + + +/** + * 房门模块建模 + * 禁止使用板来建模(因为板会被倒角) + * 默认左侧为门铰链位置 右边为门把手位置 + * 需要提供门开启旋转轴(门铰链所在的位置) + * 移动门外开时门位置的移动动作 所需的移动值?(用于旋转轴的切换?) + * + */ + + +@Factory +export class TemplateRoomDoorRecord extends TemplateRecord +{ + @AutoRecord HoleObjectId: ObjectId; + @AutoRecord DoorLogo: string; + + constructor() + { + super(); + } + + InitHoleParams() + { + let hole = this.HoleObjectId?.Object; + if (!hole) return; + return this; + } + + protected override async Update() + { + super.Update(); + + let hole = this.HoleObjectId?.Object; + if (!hole) return; + + if (!this.Children[0]?.Object) return; + + let tr = this.Children[0].Object as TemplateRecord; + + let pos = new PositioningFixed; + pos.SpaceCS = hole.FakerWalls[0].OCS; + let yDir = new Vector3().setFromMatrixColumn(pos.SpaceCS, 1); + pos.SpaceCS.elements[12] -= yDir.x * hole.FakerWalls[0].Thickness * 0.5; + pos.SpaceCS.elements[13] -= yDir.y * hole.FakerWalls[0].Thickness * 0.5; + pos.SpaceCS.elements[14] -= yDir.z * hole.FakerWalls[0].Thickness * 0.5; + + let wall = hole.FakerWalls[0]; + pos.SpaceSize = new Vector3(wall.Length, wall.Thickness, wall.Height); + tr.Positioning = pos; + } + + + //#region -------------------------File------------------------- + //对象从文件中读取数据,初始化自身 + override ReadFile(file: CADFiler) + { + let ver = file.Read(); + super.ReadFile(file); + this.HoleObjectId = file.ReadObjectId() as any; + + if (ver > 1) + { + this.DoorLogo = file.Read(); + } + } + //对象将自身数据写入到文件. + override WriteFile(file: CADFiler) + { + file.Write(2); + super.WriteFile(file); + file.WriteObjectId(this.HoleObjectId); + + file.Write(this.DoorLogo); + } + + //局部撤销 + override ApplyPartialUndo(undoData: CADObject) + { + super.ApplyPartialUndo(undoData); + } + //#endregion +} diff --git a/src/DatabaseServices/Template/ProgramTempate/TemplateWindowRecord.ts b/src/DatabaseServices/Template/ProgramTempate/TemplateWindowRecord.ts new file mode 100644 index 000000000..dc6ddd206 --- /dev/null +++ b/src/DatabaseServices/Template/ProgramTempate/TemplateWindowRecord.ts @@ -0,0 +1,394 @@ +import { Matrix4, Vector3 } from "three"; +import { arrayLast } from "../../../Common/ArrayExt"; +import { EntityUpdateWrap } from "../../../Common/EntityUpdateWrap"; +import { ZAxis } from "../../../Geometry/GeUtils"; +import { IntersectOption } from "../../../GraphicsSystem/IntersectWith"; +import { AutoRecord } from "../../AutoRecord"; +import { Factory } from "../../CADFactory"; +import { CADFiler } from "../../CADFiler"; +import { CADObject } from "../../CADObject"; +import { Curve } from "../../Entity/Curve"; +import { ObjectId } from "../../ObjectId"; +import { RoomHolePolyline } from "../../Room/Entity/Wall/Hole/RoomHolePolyline"; +import { RoomWallArc } from "../../Room/Entity/Wall/RoomWallArc"; +import { RoomWallLine } from "../../Room/Entity/Wall/RoomWallLine"; +import { TemplateParam } from "../Param/TemplateParam"; +import { PositioningFixed } from "../Positioning/PositioningFixed"; +import { TemplateRecord } from "../TemplateRecord"; + + +/** + * 窗: + * I(窗) + * L(转角窗,2面墙,4面飘窗) (左边墙?) (右边墙?) (左边窗?) + * U(转角窗,2面墙,6面飘窗) (左边墙?) (右边墙?) + * + * 对于这种转角窗 需要生成一个V型的实体来补一下 + * + * 圆弧窗? + * + * 参数: + * + * 窗框材质 + * + * 窗台石? 台面厚度? 台面凸出? 台面左右凸出 + * + * I 外飘 = W/2 居中 + * L 左外飘 右外飘 + * U 左外飘 中外飘 右外飘? + */ + +//窗台石默认参数 +const DefaultCTSarams: [string, string, string][] = [ + ["CTS", "窗台石?", "1"], + ["CTSH", "窗石厚", "30"], + ["CTSTC", "窗石凸", "30"], + ["CTSZYTC", "左右凸出", "30"], + ["WZ", "窗位置", "25"],//在外飘未启用之前 + ["HD", "窗厚度", "50"],//在外飘未启用之前 +]; + +const DefaultIWindowParams: [string, string, string][] = [ + ["WP", "外飘", "500"], +]; + +const DefaultLWindowParams: [string, string, string][] = [ + ["ZWP", "左外飘", "500"], + ["YWP", "右外飘", "500"], +]; + +const DefaultUWindowParams: [string, string, string][] = [ + ["ZWP", "左外飘", "500"], + ["MWP", "中外飘", "500"], + ["YWP", "右外飘", "500"], +]; + +function InitParam(tr: TemplateRecord, paramD: [string, string, string]) +{ + let [name, description, expr] = paramD; + let param = tr.GetParam(name); + if (param) return; + + param = new TemplateParam(); + param.name = name; + param.expr = expr; + param.value = 0; + param.description = description; + tr.Params.push(param); +} + + +@Factory +export class TemplateWindowRecord extends TemplateRecord +{ + @AutoRecord HoleObjectId: ObjectId; + + @AutoRecord CTSId: ObjectId;//窗台石id + @AutoRecord LeftIsWall = false; + @AutoRecord RightIsWall = false; + + @AutoRecord WindowLogo: string; + @AutoRecord LeftWindowLogo: string; + @AutoRecord RightWindowLogo: string; + + @AutoRecord LeftWindowTemplateId: string;//飘窗左侧窗 + @AutoRecord RightWindowTemplateId: string;//飘窗右侧窗 + + constructor() + { + super(); + } + + InitHoleParams() + { + let hole = this.HoleObjectId?.Object; + if (!hole) return; + + //初始化窗台石参数 + for (let p of DefaultCTSarams) InitParam(this, p); + + let holePtsCount = hole.PointsCount; + if (holePtsCount === 2) + { + for (let p of DefaultIWindowParams) InitParam(this, p); + } + else if (holePtsCount === 3) + { + for (let p of DefaultLWindowParams) InitParam(this, p); + } + else if (holePtsCount === 4) + { + for (let p of DefaultUWindowParams) InitParam(this, p); + } + return this; + } + + set WpDist(v: number[]) + { + let count = this.HoleObjectId?.Object?.PointsCount; + if (!count) return; + + if (count === 2) + this.GetParam("WP").expr = v[0]; + else if (count === 3) + { + this.GetParam("ZWP").expr = v[0]; + this.GetParam("YWP").expr = v[1]; + } + else + { + this.GetParam("ZWP").expr = v[0]; + this.GetParam("MWP").expr = v[1]; + this.GetParam("YWP").expr = v[2]; + } + } + + get WpDists(): number[] + { + let hole = this.HoleObjectId?.Object; + if (!hole) return; + + let wpdists: number[] = []; + + let walls = hole.FakerWalls; + if (walls.length === 1) + { + let wp = this.GetParam("WP").value as number; + wpdists.push(wp); + } + else if (walls.length === 2) + { + let zwp = this.GetParam("ZWP").value as number; + let ywp = this.GetParam("YWP").value as number; + + wpdists.push(zwp, ywp); + } + else if (walls.length === 3) + { + let zwp = this.GetParam("ZWP").value as number; + let mwp = this.GetParam("MWP").value as number; + let ywp = this.GetParam("YWP").value as number; + + wpdists.push(zwp, mwp, ywp); + } + return wpdists; + } + + protected override async Update() + { + super.Update(); + + let hole = this.HoleObjectId?.Object; + if (!hole) return; + + let ctsParam = this.GetParam("CTS"); + + //#region 计算窗台石轮廓 + let ctstc = this.GetParam("CTSTC").value as number;//窗台石凸出 + let ctszytc = this.GetParam("CTSZYTC").value as number;//窗台石左右凸出 + + let wpdists = this.WpDists; + + let polyline = hole.GetWpRegion(wpdists, ctstc, ctszytc); + if (ctsParam.value)//1 使用窗台石? + { + // Draw(polyline); + } + + EntityUpdateWrap(hole, () => + { + hole.WpDist = wpdists; + hole.WpLeftWall = this.LeftIsWall; + hole.WpRightWall = this.RightIsWall; + }); + + if (wpdists.every(d => d === 0))//没有外飘 + { + let wzParam = this.GetParam("WZ"); + + let fakerWalls = hole.FakerWalls; + if (wzParam.value !== 0) + { + fakerWalls = fakerWalls.map(w => w.GetOffsetCurves(wzParam.value as number)[0] as any); + + //窗户相交最近点 + const getMinDistPoint = (pts: Vector3[], curve: Vector3) => + { + let pt: Vector3 = pts[0]; + let length = curve.distanceTo(pt); + for (let y = 1; y < pts.length; y++) + { + if (curve.distanceTo(pts[y]) < length) + pt = pts[y]; + } + return pt; + }; + + for (let i = 0; i < fakerWalls.length - 1; i++) + { + let pre = fakerWalls[i]; + let next = fakerWalls[i + 1]; + let pt = getMinDistPoint(pre.IntersectWith(next, IntersectOption.ExtendBoth), pre.EndPoint); + + pre.EndPoint = pt; + next.StartPoint = pt; + } + + for (let w of fakerWalls) + { + if (w instanceof RoomWallLine) + w.UpdateOCSToMinBox(); + } + } + + for (let i = 0; i < this.Children.length; i++) + { + if (i > hole.PointsCount) break; + + let wall = fakerWalls[i]; + + let ctemplate = this.Children[i].Object as TemplateRecord; + + if (!ctemplate) continue; + + let pos = new PositioningFixed; + pos.SpaceCS = wall.OCS; + pos.SpaceSize = new Vector3(wall.Length, ctemplate.WParam.value as number, wall.Height); + ctemplate.Positioning = pos; + } + } + else + { + //根据外飘偏移 + let wpCurves: Curve[] = []; + for (let i = 0; i < hole.FakerWalls.length; i++) + { + let w = hole.FakerWalls[i] as (RoomWallLine | RoomWallArc); + let d = wpdists[i];//外飘距离 + if (w instanceof RoomWallArc) + wpCurves.push(w.GetOffsetCurves((w.Thickness * 0.5 + d) * (w.IsClockWise ? -1 : 1))[0]); + else + wpCurves.push(w.GetOffsetCurves(-(w.Thickness * 0.5 + d))[0]); + } + //求交连接 + for (let i = 1; i < wpCurves.length; i++) + { + let pre = wpCurves[i - 1]; + let now = wpCurves[i]; + let iPt = pre.IntersectWith(now, IntersectOption.ExtendBoth)[0]; + pre.EndPoint = iPt; + now.StartPoint = iPt; + } + + //左侧非墙 + let templateIndex = 0; + if (!this.LeftIsWall) + { + templateIndex++; + + let sp = hole.LidCurves[0].StartPoint.setZ(hole.Z); + let x = hole.LidCurves[0].GetFistDeriv(0).normalize().negate(); + let z = ZAxis; + let y = new Vector3().crossVectors(ZAxis, x); + + let tr = this.Children[0].Object as TemplateRecord; + if (!tr) return; + + let pos = new PositioningFixed; + pos.SpaceCS = new Matrix4().makeBasis(x, y, z).setPosition(sp); + pos.SpaceSize = new Vector3(wpdists[0], 50, hole.Height); + tr.Positioning = pos; + } + + for (let i = 0; i < wpCurves.length; i++) + { + let c = wpCurves[i]; + let tr = this.Children[templateIndex]?.Object as TemplateRecord; + if (!tr) return; + + let sp = c.StartPoint; + let x = c.GetFistDeriv(0).normalize(); + let z = ZAxis; + let y = new Vector3().crossVectors(ZAxis, x); + + let pos = new PositioningFixed; + pos.SpaceCS = new Matrix4().makeBasis(x, y, z).setPosition(sp); + pos.SpaceSize = new Vector3(c.Length, 50, hole.Height); + tr.Positioning = pos; + + templateIndex++; + } + + if (!this.RightIsWall && arrayLast(wpdists) > 0) + { + let tr = this.Children[templateIndex]?.Object as TemplateRecord; + if (!tr) return; + + let sp = arrayLast(wpCurves).EndPoint; + let w = arrayLast(hole.FakerWalls); + let x = w.EndPoint.sub(sp).normalize(); + let z = ZAxis; + let y = new Vector3().crossVectors(ZAxis, x); + + let pos = new PositioningFixed; + pos.SpaceCS = new Matrix4().makeBasis(x, y, z).setPosition(sp); + pos.SpaceSize = new Vector3(arrayLast(wpdists), 50, hole.Height); + tr.Positioning = pos; + } + } + } + + + //#region -------------------------File------------------------- + //对象从文件中读取数据,初始化自身 + override ReadFile(file: CADFiler) + { + let ver = file.Read(); + super.ReadFile(file); + + if (ver > 1) + { + let wallFlag = file.Read() as number; + this.LeftIsWall = (wallFlag & 1) === 1; + this.RightIsWall = (wallFlag & 2) === 2; + } + if (ver > 2) + this.HoleObjectId = file.ReadObjectId() as any; + + if (ver > 3) + { + this.WindowLogo = file.Read(); + this.LeftWindowLogo = file.Read(); + this.RightWindowLogo = file.Read(); + this.LeftWindowTemplateId = file.Read(); + this.RightWindowTemplateId = file.Read(); + } + } + //对象将自身数据写入到文件. + override WriteFile(file: CADFiler) + { + file.Write(4); + super.WriteFile(file); + + let wallFlag = 0; + if (this.LeftIsWall) wallFlag = 1; + if (this.RightIsWall) wallFlag += 2; + + file.Write(wallFlag); + + file.WriteObjectId(this.HoleObjectId); + + file.Write(this.WindowLogo); + file.Write(this.LeftWindowLogo); + file.Write(this.RightWindowLogo); + file.Write(this.LeftWindowTemplateId); + file.Write(this.RightWindowTemplateId); + } + + //局部撤销 + override ApplyPartialUndo(undoData: CADObject) + { + super.ApplyPartialUndo(undoData); + } + //#endregion +} diff --git a/src/Editor/CommandRegister.ts b/src/Editor/CommandRegister.ts index 95215a3aa..94bed63c2 100644 --- a/src/Editor/CommandRegister.ts +++ b/src/Editor/CommandRegister.ts @@ -172,7 +172,10 @@ import { Command_Reverse } from "../Add-on/Reverse"; import { Command_Curve2Wall } from "../Add-on/Room/Curve2Wall"; import { Command_DrawHole, DrawHoleType, IHoleType } from "../Add-on/Room/DrawHole"; import { Command_DrawRectWall } from "../Add-on/Room/DrawRectWall"; +import { Command_DrawRoomDoor } from "../Add-on/Room/DrawRoomDoor"; +import { Command_DrawRoomWindow } from "../Add-on/Room/DrawRoomWindow"; import { Command_DrawWall, Command_DrawWallInside } from "../Add-on/Room/DrawWall"; +import { Command_OneKeyDrawLDCWindow, Command_OneKeyDrawPCWindow, Command_OneKeyDrawYJHMWindow, Command_OneKeyDrawYZCWindow, Command_OneKeyDrawZJCWindow, Command_OneKeyDrawZJPCWindow } from "../Add-on/Room/OneKeyDrawWindow"; import { Command_ParseRoomWall } from "../Add-on/Room/ParseRoomWall"; import { Command_SetWallThick } from "../Add-on/Room/SetWallThick"; import { Command_Rotate, Command_RotateRefer } from "../Add-on/Rotate"; @@ -780,6 +783,19 @@ export function registerCommand() commandMachine.RegisterCommand(CommandNames.DrawLHole, new Command_DrawHole(DrawHoleType.L)); commandMachine.RegisterCommand(CommandNames.DrawUHole, new Command_DrawHole(DrawHoleType.U)); + commandMachine.RegisterCommand(CommandNames.DrawIWindow, new Command_DrawRoomWindow(DrawHoleType.I, IHoleType.Window)); + commandMachine.RegisterCommand(CommandNames.DrawLWindow, new Command_DrawRoomWindow(DrawHoleType.L, IHoleType.Window)); + commandMachine.RegisterCommand(CommandNames.DrawUWindow, new Command_DrawRoomWindow(DrawHoleType.U, IHoleType.Window)); + commandMachine.RegisterCommand(CommandNames.DrawIDoor, new Command_DrawRoomDoor(DrawHoleType.I, IHoleType.Door)); + + commandMachine.RegisterCommand(CommandNames.YZC, new Command_OneKeyDrawYZCWindow(DrawHoleType.I, IHoleType.Window, true));//一字窗 + commandMachine.RegisterCommand(CommandNames.LDC, new Command_OneKeyDrawLDCWindow(DrawHoleType.I, IHoleType.Window, true));//落地窗 + commandMachine.RegisterCommand(CommandNames.PC, new Command_OneKeyDrawPCWindow(DrawHoleType.I, IHoleType.Window, true));//飘窗 + commandMachine.RegisterCommand(CommandNames.ZJC, new Command_OneKeyDrawZJCWindow(DrawHoleType.L, IHoleType.Window, true));//转角窗 + commandMachine.RegisterCommand(CommandNames.ZJPC, new Command_OneKeyDrawZJPCWindow(DrawHoleType.L, IHoleType.Window, true));//转角飘窗 + + commandMachine.RegisterCommand(CommandNames.YJHM, new Command_OneKeyDrawYJHMWindow(DrawHoleType.I, IHoleType.Door, true));//一键画门 + if (IsTest()) commandMachine.RegisterCommand("ParseWall", new Command_ParseRoomWall()); //画廊命令 diff --git a/src/Editor/DefaultConfig.ts b/src/Editor/DefaultConfig.ts index ca1d2230f..bd4041279 100644 --- a/src/Editor/DefaultConfig.ts +++ b/src/Editor/DefaultConfig.ts @@ -6,7 +6,7 @@ import { IUpdateBoardInfosOption } from "../UI/Components/Board/UpdateBoardInfoi import { EMetalsType, ICompHardwareOption, ICylMetalsOption, IExtMetalsOption, IToplineOption } from "../UI/Components/RightPanel/RightPanelInterface"; import { IKuGangDrawOption } from "../UI/Components/Template/TemplateInterface"; import { ECompareType, IBoardFindOption } from "../UI/Store/BoardFindInterface"; -import { BehindBoardOption, BehindHeightPositon, BoardProcessOption, BoardType, BrRelativePos, ClosingStripOption, CommonPanelConfigOption, ComposingType, CurtailType, FaceDirection, IAutoDimBrsOption, IBatchModifyPanelOption, IBoardBatchCurtailOption, KJLImportConfigOption, LayerBoardOption, LayerNailOption, LinesType, ModifyTextsConfigOption, PointLightOption, RadioType, RectAreaLightOption, RightPlaneLightOption, SideBoardOption, SingleBoardOption, SpotLightOption, StripType, TBBoardOption, VerticalBoardOption, Viewport2ConfigOption, Viewport3ConfigOption, Viewport4ConfigOption, ViewportConfigOption } from "../UI/Store/BoardInterface"; +import { BehindBoardOption, BehindHeightPositon, BoardProcessOption, BoardType, BrRelativePos, ClosingStripOption, CommonPanelConfigOption, ComposingType, CurtailType, FaceDirection, IAutoDimBrsOption, IBatchModifyPanelOption, IBoardBatchCurtailOption, KJLImportConfigOption, LayerBoardOption, LayerNailOption, LinesType, ModifyTextsConfigOption, PointLightOption, RadioType, RectAreaLightOption, RightPlaneLightOption, SideBoardOption, SingleBoardOption, SpotLightOption, StripType, TBBoardOption, VerticalBoardOption, Viewport2ConfigOption, Viewport3ConfigOption, Viewport4ConfigOption, ViewportConfigOption, WindowPanelConfigOption } from "../UI/Store/BoardInterface"; import { DoorPosType, HandleHorPos, HandleVePos, IDoorConfigOption, IDrawerConfigOption, IHingeConfigOption } from "../UI/Store/DoorInterface"; import { IHSOption } from "../UI/Store/HSInterface"; import { ELatticeArrayType, ILatticeOption } from "../UI/Store/LatticeInterface"; @@ -882,3 +882,23 @@ export const DefaultAutoDimBrsOption: IAutoDimBrsOption = { useParseGroups: "0" }; Object.freeze(DefaultAutoDimBrsOption); + + +export const DefaultWindowPanelOption: WindowPanelConfigOption = { + Length: 1100, + Height: 1500, + Thick: 280, + WindowOffGround: 900, + IsBayWindow: false, + BayLeftIsWall: false, + BayRightIsWall: false, + BayDist: 500, + BayLeftDist: 600, + BayMiddleDist: 600, + BayRightDist: 600, + HasWindowStone: false, + StoneThick: 50, + StoneBulge: 50, + StoneLeftRightBulge: 50, +}; +Object.freeze(DefaultWindowPanelOption); diff --git a/src/GraphicsSystem/IntersectWith.ts b/src/GraphicsSystem/IntersectWith.ts index 43920b928..15b37d73c 100644 --- a/src/GraphicsSystem/IntersectWith.ts +++ b/src/GraphicsSystem/IntersectWith.ts @@ -74,12 +74,12 @@ function CheckPointOnCurve(intRes: IntersectResult[], c1: Curve, c2: Curve, extT return true; }); } -export function IntersectCircleAndCircle(cu1: Circle | Arc, cu2: Circle | Arc): IntersectResult[] +export function IntersectCircleAndCircle(cu1: Circle | Arc, cu2: Circle | Arc, tolerance = 1e-4): IntersectResult[] { if (!cu1.IsCoplaneTo(cu2)) return []; let c1OcsInv = cu1.OCSInv; - let c1Ocs = cu1.OCS; + let c1Ocs = cu1.OCSNoClone; let center1 = cu1.Center.applyMatrix4(c1OcsInv); let center2 = cu2.Center.applyMatrix4(c1OcsInv); @@ -92,7 +92,7 @@ export function IntersectCircleAndCircle(cu1: Circle | Arc, cu2: Circle | Arc): if (dist < Math.abs(radius1 - radius2) - 1e-3 || dist > (radius1 + radius2 + 1e-3)) return pts; - if (equaln(dist, 0, 1e-6)) return pts; + if (equaln(dist, 0, tolerance)) return pts; let dstsqr = dist * dist; let r1sqr = radius1 * radius1; @@ -123,7 +123,7 @@ export function IntersectCircleAndCircle(cu1: Circle | Arc, cu2: Circle | Arc): thisParam: cu1.GetParamAtPoint(p1), argParam: cu2.GetParamAtPoint(p1), }); - if (!equalv3(p1, p2))//防止点重复 + if (!equalv3(p1, p2, tolerance))//防止点重复 pts.push({ pt: p2, thisParam: cu1.GetParamAtPoint(p2), @@ -158,7 +158,7 @@ export function IntersectCircleAndArc(circle: Circle, arc: Arc, extType: Interse */ export function IntersectArcAndArc(arc1: Arc, arc2: Arc, extType: IntersectOption, tolerance = 1e-5) { - let pts = IntersectCircleAndCircle(arc1, arc2); + let pts = IntersectCircleAndCircle(arc1, arc2, tolerance); return CheckPointOnCurve(pts, arc1, arc2, extType, tolerance); } @@ -176,7 +176,7 @@ export function IntersectEllipseAndLine(l: Line, el: Ellipse, extType: Intersect * @param {(Circle | Arc)} circle 圆或圆弧 * @returns 交点集合 */ -function IntersectLineAndCircleOrArc(line: Line, circle: Circle | Arc): IntersectResult[] +function IntersectLineAndCircleOrArc(line: Line, circle: Circle | Arc, tolerance = 1e-6): IntersectResult[] { let lineOrg = line.StartPoint; let lineDirection = line.EndPoint.sub(lineOrg); @@ -189,7 +189,7 @@ function IntersectLineAndCircleOrArc(line: Line, circle: Circle | Arc): Intersec let a1 = lineDirection.dot(diff); let discr = a1 ** 2 - a0; - if (equaln(discr, 0, 1e-7)) + if (equaln(discr, 0, tolerance)) { let pt = lineOrg.add(lineDirection.multiplyScalar(-a1)); @@ -223,13 +223,13 @@ function IntersectLineAndCircleOrArc(line: Line, circle: Circle | Arc): Intersec //直线和圆 export function IntersectLineAndCircle(line: Line, circle: Circle, extType: IntersectOption, tolerance = 1e-6) { - let ptArr = IntersectLineAndCircleOrArc(line, circle); + let ptArr = IntersectLineAndCircleOrArc(line, circle, tolerance); return CheckPointOnCurve(ptArr, line, circle, extType | IntersectOption.ExtendArg); } //直线和圆弧 export function IntersectLineAndArc(line: Line, arc: Arc, extType: IntersectOption, tolerance = 1e-6) { - let ptArr = IntersectLineAndCircleOrArc(line, arc); + let ptArr = IntersectLineAndCircleOrArc(line, arc, tolerance); return CheckPointOnCurve(ptArr, line, arc, extType, tolerance); } //直线和直线 diff --git a/src/Reactor/RelevanceCuttingReactor.ts b/src/Reactor/RelevanceCuttingReactor.ts index 50653d937..eb83c2fb7 100644 --- a/src/Reactor/RelevanceCuttingReactor.ts +++ b/src/Reactor/RelevanceCuttingReactor.ts @@ -113,7 +113,7 @@ export class RelevanceCuttingReactor } let objects = createEntitys.concat(changeEntitys).concat(deleteEntitys); - UpdateRelevanceWallHole(objects); + UpdateRelevanceWallHole(objects, true); //不等待这个,因为这个方法不需要改变实体,只需要改变绘制 UpdateRelevanceGroove(objects); }); @@ -161,7 +161,7 @@ export class RelevanceCuttingReactor } let objects = createEntitys.concat(changeEntitys).concat(deleteEntitys); - UpdateRelevanceWallHole(objects); + UpdateRelevanceWallHole(objects, true); //不等待这个,因为这个方法不需要改变实体,只需要改变绘制 UpdateRelevanceGroove(objects); }); @@ -208,7 +208,8 @@ export class RelevanceCuttingReactor if (ExtrudeConfig.DisableRefCut) return; let objects = changeObjects.concat(createObjects); - UpdateRelevanceWallHole(objects); + await UpdateRelevanceWallHole(objects); + //不等待这个,因为这个方法不需要改变实体,只需要改变绘制 UpdateRelevanceGroove(objects); }); diff --git a/src/Reactor/RoomHoleReactor.ts b/src/Reactor/RoomHoleReactor.ts index a58c8a401..f1c93657f 100644 --- a/src/Reactor/RoomHoleReactor.ts +++ b/src/Reactor/RoomHoleReactor.ts @@ -10,6 +10,7 @@ import { RoomWallLine } from "../DatabaseServices/Room/Entity/Wall/RoomWallLine" import { CreateGetCurveParam } from "../DatabaseServices/Room/ParseService/GetCurveParam"; import { FindBestRange, ParseWallRange } from "../DatabaseServices/Room/ParseService/Hole/RoomWallPlaceIHoleHelper"; import { RoomWallParse } from "../DatabaseServices/Room/ParseService/RoomWallParse"; +import { TemplateRecord } from "../DatabaseServices/Template/TemplateRecord"; import { CurveMap } from "../Geometry/CurveMap"; import { equalv2 } from "../Geometry/GeUtils"; @@ -17,12 +18,12 @@ import { equalv2 } from "../Geometry/GeUtils"; /** * 更新墙与洞的关联性 */ -export async function UpdateRelevanceWallHole(ents: CADObject[]) +export async function UpdateRelevanceWallHole(ents: CADObject[], isUndoRedo = false) { let updated = new Set(); //更新墙,顺便更新洞 - const UpdateWall = (wall: RoomWallBase) => + const UpdateWall = async (wall: RoomWallBase) => { if (updated.has(wall)) return; updated.add(wall);//避免重入 @@ -36,14 +37,14 @@ export async function UpdateRelevanceWallHole(ents: CADObject[]) if (wall.IsErase) hole.Erase(); - UpdateHole(hole);//这里如果只更新洞,那么有可能关联的其他的墙会逃逸 + await UpdateHole(hole);//这里如果只更新洞,那么有可能关联的其他的墙会逃逸 } UpdateWallHolesDataAndUpdateDraw(wall); }; //更新洞,顺便更新关联的墙? - const UpdateHole = (hole: RoomHolePolyline) => + const UpdateHole = async (hole: RoomHolePolyline) => { if (updated.has(hole)) return; updated.add(hole);//避免重入 @@ -58,7 +59,13 @@ export async function UpdateRelevanceWallHole(ents: CADObject[]) let wall = wallId.Object as RoomWallBase; if (updated.has(wall)) continue; - UpdateWall(wall); + await UpdateWall(wall); + } + + if (!isUndoRedo && hole.Template?.IsErase === false) + { + let tr = hole.Template.Object as TemplateRecord; + await tr.UpdateTemplateTree(); } }; @@ -66,9 +73,9 @@ export async function UpdateRelevanceWallHole(ents: CADObject[]) for (let en of ents) { if (en instanceof RoomWallBase) - UpdateWall(en); + await UpdateWall(en); else if (en instanceof RoomHolePolyline) - UpdateHole(en); + await UpdateHole(en); } } diff --git a/src/UI/Components/Board/Door/DoorModal.tsx b/src/UI/Components/Board/Door/DoorModal.tsx index 2a50f4018..b08a25678 100644 --- a/src/UI/Components/Board/Door/DoorModal.tsx +++ b/src/UI/Components/Board/Door/DoorModal.tsx @@ -101,11 +101,13 @@ export class DoorModal extends React.Component<{ store: DoorDrawerStore, type: B updateParams={this.props.store.currentTempProp} disableKeys={DisableChangeParName} onBlur={(n, v) => this.changeTemplateProps(n, v)} + isShowDetail={true} />
拉手参数
{ // isDoor && diff --git a/src/UI/Components/Modal/ModalStyle/Modal.less b/src/UI/Components/Modal/ModalStyle/Modal.less index 4ff8dc243..6eab5bda3 100644 --- a/src/UI/Components/Modal/ModalStyle/Modal.less +++ b/src/UI/Components/Modal/ModalStyle/Modal.less @@ -26,8 +26,11 @@ } .modal-container { - position: fixed; - outline : none; + position : fixed; + outline : none; + align-items : center; + display : flex; + justify-content: center; .bp3-dialog-container { outline: none; diff --git a/src/UI/Components/Template/Template.less b/src/UI/Components/Template/Template.less index e87721e77..eb0a9c3be 100644 --- a/src/UI/Components/Template/Template.less +++ b/src/UI/Components/Template/Template.less @@ -437,7 +437,9 @@ } } -#commonModal .template-select { +#commonModal .template-select, +#template-select .template-select, +#modal .template-select{ position: absolute; z-index: 30; @@ -489,3 +491,7 @@ min-width: 200px; max-height: 70vh; } + +#template-select{ + position: absolute; +} diff --git a/src/UI/Components/Template/TemplateDetail.tsx b/src/UI/Components/Template/TemplateDetail.tsx index 857bb6bfa..da850b23c 100644 --- a/src/UI/Components/Template/TemplateDetail.tsx +++ b/src/UI/Components/Template/TemplateDetail.tsx @@ -37,6 +37,7 @@ export interface ITemplateDetailProps onBlur?: (name: string, val: string) => void; isShowTag?: boolean; currentDir?: IDirectoryProps; + isShowDetail?: boolean; } const POSITION_PAR = ["PX", "PY", "PZ"]; @@ -169,40 +170,42 @@ export class TemplateDetail extends React.Component {