@reekon-tools/boldr-cad 0.1.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -16
- package/dist/app/edit.d.ts +12 -1
- package/dist/app/edit.js +96 -11
- package/dist/app/edit.js.map +1 -1
- package/dist/app/state.d.ts +31 -4
- package/dist/app/state.js +31 -2
- package/dist/app/state.js.map +1 -1
- package/dist/data/memory.d.ts +9 -1
- package/dist/data/memory.js +13 -8
- package/dist/data/memory.js.map +1 -1
- package/dist/data/provider.d.ts +30 -10
- package/dist/data/sync.d.ts +14 -3
- package/dist/data/sync.js +133 -66
- package/dist/data/sync.js.map +1 -1
- package/dist/editor/actions.d.ts +1 -2
- package/dist/editor/actions.js +391 -113
- package/dist/editor/actions.js.map +1 -1
- package/dist/editor/confirm.d.ts +23 -0
- package/dist/editor/confirm.js +35 -0
- package/dist/editor/confirm.js.map +1 -0
- package/dist/editor/elevation.d.ts +191 -90
- package/dist/editor/elevation.js +812 -616
- package/dist/editor/elevation.js.map +1 -1
- package/dist/editor/face.d.ts +73 -0
- package/dist/editor/face.js +269 -0
- package/dist/editor/face.js.map +1 -0
- package/dist/editor/files.d.ts +6 -0
- package/dist/editor/files.js +50 -4
- package/dist/editor/files.js.map +1 -1
- package/dist/editor/gestures.d.ts +18 -3
- package/dist/editor/gestures.js +145 -30
- package/dist/editor/gestures.js.map +1 -1
- package/dist/editor/keypad.d.ts +20 -5
- package/dist/editor/keypad.js +62 -15
- package/dist/editor/keypad.js.map +1 -1
- package/dist/editor/keys.js +36 -4
- package/dist/editor/keys.js.map +1 -1
- package/dist/editor/laser.d.ts +20 -4
- package/dist/editor/laser.js +104 -21
- package/dist/editor/laser.js.map +1 -1
- package/dist/editor/linkActions.d.ts +49 -0
- package/dist/editor/linkActions.js +210 -0
- package/dist/editor/linkActions.js.map +1 -0
- package/dist/editor/linkFollow.d.ts +2 -0
- package/dist/editor/linkFollow.js +53 -0
- package/dist/editor/linkFollow.js.map +1 -0
- package/dist/editor/linkWrite.d.ts +13 -0
- package/dist/editor/linkWrite.js +84 -0
- package/dist/editor/linkWrite.js.map +1 -0
- package/dist/editor/links.d.ts +84 -0
- package/dist/editor/links.js +130 -0
- package/dist/editor/links.js.map +1 -0
- package/dist/editor/markup.d.ts +29 -3
- package/dist/editor/markup.js +95 -10
- package/dist/editor/markup.js.map +1 -1
- package/dist/editor/mode.d.ts +40 -2
- package/dist/editor/mode.js +71 -4
- package/dist/editor/mode.js.map +1 -1
- package/dist/editor/ports.d.ts +38 -6
- package/dist/editor/ports.js +10 -1
- package/dist/editor/ports.js.map +1 -1
- package/dist/editor/project.d.ts +38 -6
- package/dist/editor/project.js +170 -27
- package/dist/editor/project.js.map +1 -1
- package/dist/editor/selection.d.ts +10 -0
- package/dist/editor/selection.js +62 -8
- package/dist/editor/selection.js.map +1 -1
- package/dist/editor/status.js +3 -0
- package/dist/editor/status.js.map +1 -1
- package/dist/editor/tooltips.d.ts +5 -0
- package/dist/editor/tooltips.js +48 -0
- package/dist/editor/tooltips.js.map +1 -0
- package/dist/editor/view3d.d.ts +17 -0
- package/dist/editor/view3d.js +135 -0
- package/dist/editor/view3d.js.map +1 -0
- package/dist/editor/view3dState.d.ts +39 -0
- package/dist/editor/view3dState.js +44 -0
- package/dist/editor/view3dState.js.map +1 -0
- package/dist/export/dxf.js +1 -1
- package/dist/export/dxf.js.map +1 -1
- package/dist/export/pdf.js +17 -12
- package/dist/export/pdf.js.map +1 -1
- package/dist/export/plan.d.ts +23 -6
- package/dist/export/plan.js +127 -44
- package/dist/export/plan.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/model/constraints.js +15 -4
- package/dist/model/constraints.js.map +1 -1
- package/dist/model/demo.js +3 -3
- package/dist/model/demo.js.map +1 -1
- package/dist/model/elevation.d.ts +6 -9
- package/dist/model/elevation.js +5 -13
- package/dist/model/elevation.js.map +1 -1
- package/dist/model/faces.d.ts +58 -0
- package/dist/model/faces.js +197 -0
- package/dist/model/faces.js.map +1 -0
- package/dist/model/facesolve.d.ts +72 -0
- package/dist/model/facesolve.js +369 -0
- package/dist/model/facesolve.js.map +1 -0
- package/dist/model/history.d.ts +3 -2
- package/dist/model/history.js +17 -11
- package/dist/model/history.js.map +1 -1
- package/dist/model/images.d.ts +18 -0
- package/dist/model/images.js +26 -0
- package/dist/model/images.js.map +1 -0
- package/dist/model/links.d.ts +46 -0
- package/dist/model/links.js +73 -0
- package/dist/model/links.js.map +1 -0
- package/dist/model/markup.d.ts +46 -0
- package/dist/model/markup.js +71 -0
- package/dist/model/markup.js.map +1 -0
- package/dist/model/measurement.d.ts +11 -0
- package/dist/model/measurement.js +24 -0
- package/dist/model/measurement.js.map +1 -0
- package/dist/model/openings.d.ts +4 -0
- package/dist/model/openings.js +4 -0
- package/dist/model/openings.js.map +1 -1
- package/dist/model/planGeom.d.ts +72 -2
- package/dist/model/planGeom.js +184 -10
- package/dist/model/planGeom.js.map +1 -1
- package/dist/model/project.js +3 -2
- package/dist/model/project.js.map +1 -1
- package/dist/model/projectfile.js +2 -1
- package/dist/model/projectfile.js.map +1 -1
- package/dist/model/rooms.js +2 -2
- package/dist/model/rooms.js.map +1 -1
- package/dist/model/settings.d.ts +6 -0
- package/dist/model/settings.js +2 -2
- package/dist/model/settings.js.map +1 -1
- package/dist/model/sketch.d.ts +14 -0
- package/dist/model/sketch.js +143 -24
- package/dist/model/sketch.js.map +1 -1
- package/dist/model/smooth.d.ts +6 -0
- package/dist/model/smooth.js +52 -0
- package/dist/model/smooth.js.map +1 -0
- package/dist/model/store.d.ts +12 -0
- package/dist/model/store.js +35 -3
- package/dist/model/store.js.map +1 -1
- package/dist/model/types.d.ts +173 -8
- package/dist/model/wallfaces.d.ts +32 -0
- package/dist/model/wallfaces.js +133 -20
- package/dist/model/wallfaces.js.map +1 -1
- package/dist/model/wallshape.d.ts +15 -0
- package/dist/model/wallshape.js +89 -0
- package/dist/model/wallshape.js.map +1 -0
- package/dist/react/BottomBar.d.ts +9 -5
- package/dist/react/BottomBar.js +213 -24
- package/dist/react/BottomBar.js.map +1 -1
- package/dist/react/Buttons.d.ts +13 -2
- package/dist/react/Buttons.js +37 -5
- package/dist/react/Buttons.js.map +1 -1
- package/dist/react/CadEditor.d.ts +54 -4
- package/dist/react/CadEditor.js +79 -21
- package/dist/react/CadEditor.js.map +1 -1
- package/dist/react/ConfirmSheet.d.ts +4 -0
- package/dist/react/ConfirmSheet.js +31 -0
- package/dist/react/ConfirmSheet.js.map +1 -0
- package/dist/react/ConflictSheet.d.ts +2 -1
- package/dist/react/ConflictSheet.js +16 -8
- package/dist/react/ConflictSheet.js.map +1 -1
- package/dist/react/Drawer.d.ts +8 -2
- package/dist/react/Drawer.js +167 -38
- package/dist/react/Drawer.js.map +1 -1
- package/dist/react/Duck.d.ts +13 -0
- package/dist/react/Duck.js +45 -0
- package/dist/react/Duck.js.map +1 -0
- package/dist/react/Empty3D.d.ts +2 -0
- package/dist/react/Empty3D.js +39 -0
- package/dist/react/Empty3D.js.map +1 -0
- package/dist/react/FitScroll.d.ts +6 -0
- package/dist/react/FitScroll.js +14 -0
- package/dist/react/FitScroll.js.map +1 -0
- package/dist/react/Icon.native.d.ts +6 -0
- package/dist/react/Icon.native.js +4 -0
- package/dist/react/Icon.native.js.map +1 -1
- package/dist/react/Icon.web.d.ts +6 -0
- package/dist/react/Icon.web.js +4 -0
- package/dist/react/Icon.web.js.map +1 -1
- package/dist/react/KeypadSheet.d.ts +9 -2
- package/dist/react/KeypadSheet.js +174 -42
- package/dist/react/KeypadSheet.js.map +1 -1
- package/dist/react/LaserTray.js +8 -5
- package/dist/react/LaserTray.js.map +1 -1
- package/dist/react/MarkupPanel.d.ts +8 -0
- package/dist/react/MarkupPanel.js +67 -0
- package/dist/react/MarkupPanel.js.map +1 -0
- package/dist/react/PageThumb.native.d.ts +7 -2
- package/dist/react/PageThumb.native.js +19 -7
- package/dist/react/PageThumb.native.js.map +1 -1
- package/dist/react/PageThumb.web.d.ts +3 -2
- package/dist/react/PageThumb.web.js +4 -4
- package/dist/react/PageThumb.web.js.map +1 -1
- package/dist/react/PlanCanvas.native.d.ts +1 -1
- package/dist/react/PlanCanvas.native.js +134 -36
- package/dist/react/PlanCanvas.native.js.map +1 -1
- package/dist/react/PlanCanvas.web.js +16 -5
- package/dist/react/PlanCanvas.web.js.map +1 -1
- package/dist/react/Popovers.d.ts +12 -6
- package/dist/react/Popovers.js +53 -26
- package/dist/react/Popovers.js.map +1 -1
- package/dist/react/Rail.d.ts +24 -0
- package/dist/react/Rail.js +324 -34
- package/dist/react/Rail.js.map +1 -1
- package/dist/react/ShapeMenu.d.ts +7 -0
- package/dist/react/ShapeMenu.js +58 -0
- package/dist/react/ShapeMenu.js.map +1 -0
- package/dist/react/Sheet.d.ts +14 -4
- package/dist/react/Sheet.js +54 -9
- package/dist/react/Sheet.js.map +1 -1
- package/dist/react/Slider.d.ts +18 -0
- package/dist/react/Slider.js +27 -0
- package/dist/react/Slider.js.map +1 -0
- package/dist/react/Toast.js +14 -6
- package/dist/react/Toast.js.map +1 -1
- package/dist/react/ToolTip.d.ts +7 -0
- package/dist/react/ToolTip.js +43 -0
- package/dist/react/ToolTip.js.map +1 -0
- package/dist/react/TopBar.d.ts +6 -3
- package/dist/react/TopBar.js +48 -17
- package/dist/react/TopBar.js.map +1 -1
- package/dist/react/Tween.d.ts +7 -0
- package/dist/react/Tween.js +26 -0
- package/dist/react/Tween.js.map +1 -0
- package/dist/react/Tween.native.d.ts +6 -0
- package/dist/react/Tween.native.js +43 -0
- package/dist/react/Tween.native.js.map +1 -0
- package/dist/react/View3D.native.d.ts +4 -0
- package/dist/react/View3D.native.js +103 -0
- package/dist/react/View3D.native.js.map +1 -0
- package/dist/react/View3D.web.d.ts +4 -0
- package/dist/react/View3D.web.js +151 -0
- package/dist/react/View3D.web.js.map +1 -0
- package/dist/react/View3DHost.d.ts +143 -0
- package/dist/react/View3DHost.js +377 -0
- package/dist/react/View3DHost.js.map +1 -0
- package/dist/react/corners.d.ts +7 -0
- package/dist/react/corners.js +51 -0
- package/dist/react/corners.js.map +1 -0
- package/dist/react/easing.d.ts +15 -0
- package/dist/react/easing.js +10 -0
- package/dist/react/easing.js.map +1 -0
- package/dist/react/host.d.ts +7 -2
- package/dist/react/host.js +8 -3
- package/dist/react/host.js.map +1 -1
- package/dist/react/hostKeypad.d.ts +56 -0
- package/dist/react/hostKeypad.js +5 -0
- package/dist/react/hostKeypad.js.map +1 -0
- package/dist/react/motion.d.ts +77 -0
- package/dist/react/motion.js +173 -0
- package/dist/react/motion.js.map +1 -0
- package/dist/react/ports.js +90 -8
- package/dist/react/ports.js.map +1 -1
- package/dist/react/saving.d.ts +15 -0
- package/dist/react/saving.js +76 -0
- package/dist/react/saving.js.map +1 -0
- package/dist/react/store.d.ts +38 -1
- package/dist/react/store.js +31 -1
- package/dist/react/store.js.map +1 -1
- package/dist/react/theme.d.ts +10 -0
- package/dist/react/theme.js +8 -0
- package/dist/react/theme.js.map +1 -1
- package/dist/react/topRow.d.ts +22 -0
- package/dist/react/topRow.js +61 -0
- package/dist/react/topRow.js.map +1 -0
- package/dist/react/touch.d.ts +6 -0
- package/dist/react/touch.js +9 -0
- package/dist/react/touch.js.map +1 -0
- package/dist/render/ctx.d.ts +8 -0
- package/dist/render/draw.d.ts +33 -1
- package/dist/render/draw.js +593 -215
- package/dist/render/draw.js.map +1 -1
- package/dist/render/elevation.d.ts +65 -43
- package/dist/render/elevation.js +414 -140
- package/dist/render/elevation.js.map +1 -1
- package/dist/render/figmaIcons.d.ts +12 -0
- package/dist/render/figmaIcons.js +21 -0
- package/dist/render/figmaIcons.js.map +1 -0
- package/dist/render/hit.d.ts +22 -2
- package/dist/render/hit.js +168 -25
- package/dist/render/hit.js.map +1 -1
- package/dist/render/icons.js +69 -10
- package/dist/render/icons.js.map +1 -1
- package/dist/render/linkTile.d.ts +15 -0
- package/dist/render/linkTile.js +369 -0
- package/dist/render/linkTile.js.map +1 -0
- package/dist/render/markup.d.ts +11 -0
- package/dist/render/markup.js +112 -0
- package/dist/render/markup.js.map +1 -0
- package/dist/render/navigate.d.ts +88 -0
- package/dist/render/navigate.js +100 -0
- package/dist/render/navigate.js.map +1 -0
- package/dist/render/snap.d.ts +20 -1
- package/dist/render/snap.js +118 -25
- package/dist/render/snap.js.map +1 -1
- package/dist/render/squircle.d.ts +8 -0
- package/dist/render/squircle.js +71 -0
- package/dist/render/squircle.js.map +1 -0
- package/dist/render/view.d.ts +115 -2
- package/dist/render/view.js +244 -19
- package/dist/render/view.js.map +1 -1
- package/dist/skia/SkiaDrawCtx.d.ts +30 -2
- package/dist/skia/SkiaDrawCtx.js +198 -43
- package/dist/skia/SkiaDrawCtx.js.map +1 -1
- package/dist/skia/host.d.ts +18 -2
- package/dist/skia/host.js +46 -1
- package/dist/skia/host.js.map +1 -1
- package/dist/solver/equations.js +37 -2
- package/dist/solver/equations.js.map +1 -1
- package/dist/solver/gauss-newton.d.ts +17 -0
- package/dist/solver/gauss-newton.js +144 -36
- package/dist/solver/gauss-newton.js.map +1 -1
- package/dist/solver/linalg.d.ts +45 -2
- package/dist/solver/linalg.js +164 -10
- package/dist/solver/linalg.js.map +1 -1
- package/dist/solver/types.d.ts +5 -0
- package/dist/tools/annotate.d.ts +57 -1
- package/dist/tools/annotate.js +64 -43
- package/dist/tools/annotate.js.map +1 -1
- package/dist/tools/chamfer.d.ts +12 -0
- package/dist/tools/chamfer.js +114 -0
- package/dist/tools/chamfer.js.map +1 -0
- package/dist/tools/corner.d.ts +38 -0
- package/dist/tools/corner.js +99 -0
- package/dist/tools/corner.js.map +1 -0
- package/dist/tools/dim.d.ts +86 -9
- package/dist/tools/dim.js +582 -97
- package/dist/tools/dim.js.map +1 -1
- package/dist/tools/elevation.js +5 -5
- package/dist/tools/elevation.js.map +1 -1
- package/dist/tools/fillet.d.ts +9 -0
- package/dist/tools/fillet.js +86 -0
- package/dist/tools/fillet.js.map +1 -0
- package/dist/tools/freehand.d.ts +3 -2
- package/dist/tools/freehand.js +19 -12
- package/dist/tools/freehand.js.map +1 -1
- package/dist/tools/index.js +14 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/line.d.ts +8 -3
- package/dist/tools/line.js +66 -30
- package/dist/tools/line.js.map +1 -1
- package/dist/tools/modify.d.ts +13 -0
- package/dist/tools/modify.js +105 -25
- package/dist/tools/modify.js.map +1 -1
- package/dist/tools/openings.js +25 -18
- package/dist/tools/openings.js.map +1 -1
- package/dist/tools/registry.d.ts +8 -0
- package/dist/tools/registry.js +42 -2
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/select.d.ts +77 -3
- package/dist/tools/select.js +560 -74
- package/dist/tools/select.js.map +1 -1
- package/dist/tools/shapes.d.ts +14 -0
- package/dist/tools/shapes.js +154 -75
- package/dist/tools/shapes.js.map +1 -1
- package/dist/units/format.d.ts +11 -3
- package/dist/units/format.js +27 -6
- package/dist/units/format.js.map +1 -1
- package/dist/units/parse.d.ts +2 -1
- package/dist/units/parse.js +5 -4
- package/dist/units/parse.js.map +1 -1
- package/dist/units/prefs.d.ts +9 -1
- package/dist/units/prefs.js +6 -0
- package/dist/units/prefs.js.map +1 -1
- package/dist/view3d/build.d.ts +35 -0
- package/dist/view3d/build.js +630 -0
- package/dist/view3d/build.js.map +1 -0
- package/dist/view3d/camera.d.ts +60 -0
- package/dist/view3d/camera.js +84 -0
- package/dist/view3d/camera.js.map +1 -0
- package/dist/view3d/fatLines.d.ts +11 -0
- package/dist/view3d/fatLines.js +83 -0
- package/dist/view3d/fatLines.js.map +1 -0
- package/dist/view3d/lines.d.ts +17 -0
- package/dist/view3d/lines.js +271 -0
- package/dist/view3d/lines.js.map +1 -0
- package/dist/view3d/mesh.d.ts +51 -0
- package/dist/view3d/mesh.js +101 -0
- package/dist/view3d/mesh.js.map +1 -0
- package/dist/view3d/poly.d.ts +16 -0
- package/dist/view3d/poly.js +109 -0
- package/dist/view3d/poly.js.map +1 -0
- package/dist/view3d/viewer.d.ts +86 -0
- package/dist/view3d/viewer.js +350 -0
- package/dist/view3d/viewer.js.map +1 -0
- package/package.json +15 -2
- package/src/app/edit.ts +83 -12
- package/src/app/main.ts +11 -5
- package/src/app/state.ts +38 -5
- package/src/data/memory.ts +15 -10
- package/src/data/provider.ts +25 -10
- package/src/data/sync.ts +107 -53
- package/src/editor/actions.ts +292 -83
- package/src/editor/confirm.ts +58 -0
- package/src/editor/elevation.ts +698 -478
- package/src/editor/face.ts +227 -0
- package/src/editor/files.ts +40 -4
- package/src/editor/gestures.ts +147 -31
- package/src/editor/keypad.ts +67 -18
- package/src/editor/keys.ts +25 -5
- package/src/editor/laser.ts +98 -20
- package/src/editor/linkActions.ts +194 -0
- package/src/editor/linkFollow.ts +43 -0
- package/src/editor/linkWrite.ts +77 -0
- package/src/editor/links.ts +181 -0
- package/src/editor/markup.ts +92 -8
- package/src/editor/mode.ts +83 -7
- package/src/editor/ports.ts +39 -6
- package/src/editor/project.ts +157 -26
- package/src/editor/selection.ts +54 -8
- package/src/editor/status.ts +2 -0
- package/src/editor/tooltips.ts +47 -0
- package/src/editor/view3d.ts +119 -0
- package/src/editor/view3dState.ts +61 -0
- package/src/export/dxf.ts +1 -1
- package/src/export/pdf.ts +15 -7
- package/src/export/plan.ts +119 -35
- package/src/index.ts +7 -1
- package/src/input/keyboard.ts +2 -4
- package/src/model/constraints.ts +13 -4
- package/src/model/demo.ts +3 -3
- package/src/model/elevation.ts +6 -16
- package/src/model/faces.ts +153 -0
- package/src/model/facesolve.ts +322 -0
- package/src/model/history.ts +19 -11
- package/src/model/images.ts +35 -0
- package/src/model/links.ts +77 -0
- package/src/model/markup.ts +80 -0
- package/src/model/measurement.ts +26 -0
- package/src/model/openings.ts +6 -0
- package/src/model/planGeom.ts +174 -8
- package/src/model/project.ts +3 -2
- package/src/model/projectfile.ts +2 -1
- package/src/model/rooms.ts +1 -1
- package/src/model/settings.ts +8 -2
- package/src/model/sketch.ts +101 -14
- package/src/model/smooth.ts +51 -0
- package/src/model/store.ts +35 -4
- package/src/model/types.ts +170 -8
- package/src/model/wallfaces.ts +126 -17
- package/src/model/wallshape.ts +97 -0
- package/src/react/BottomBar.tsx +263 -46
- package/src/react/Buttons.tsx +50 -9
- package/src/react/CadEditor.tsx +142 -34
- package/src/react/ConfirmSheet.tsx +38 -0
- package/src/react/ConflictSheet.tsx +19 -12
- package/src/react/Drawer.tsx +213 -110
- package/src/react/Duck.tsx +50 -0
- package/src/react/Empty3D.tsx +32 -0
- package/src/react/FitScroll.tsx +19 -0
- package/src/react/Icon.native.tsx +5 -0
- package/src/react/Icon.web.tsx +5 -0
- package/src/react/KeypadSheet.tsx +253 -60
- package/src/react/LaserTray.tsx +12 -8
- package/src/react/MarkupPanel.tsx +150 -0
- package/src/react/PageThumb.native.tsx +18 -7
- package/src/react/PageThumb.web.tsx +4 -4
- package/src/react/PlanCanvas.native.tsx +151 -34
- package/src/react/PlanCanvas.web.tsx +15 -5
- package/src/react/Popovers.tsx +116 -51
- package/src/react/Rail.tsx +360 -27
- package/src/react/ShapeMenu.tsx +66 -0
- package/src/react/Sheet.tsx +56 -11
- package/src/react/Slider.tsx +48 -0
- package/src/react/Toast.tsx +16 -9
- package/src/react/ToolTip.tsx +49 -0
- package/src/react/TopBar.tsx +97 -39
- package/src/react/Tween.native.tsx +36 -0
- package/src/react/Tween.tsx +24 -0
- package/src/react/View3D.native.tsx +131 -0
- package/src/react/View3D.web.tsx +131 -0
- package/src/react/View3DHost.ts +367 -0
- package/src/react/corners.ts +52 -0
- package/src/react/easing.ts +14 -0
- package/src/react/host.ts +8 -3
- package/src/react/hostKeypad.ts +66 -0
- package/src/react/motion.tsx +239 -0
- package/src/react/ports.ts +78 -9
- package/src/react/saving.ts +80 -0
- package/src/react/store.ts +35 -3
- package/src/react/theme.ts +8 -0
- package/src/react/topRow.ts +71 -0
- package/src/react/touch.ts +10 -0
- package/src/render/ctx.ts +8 -0
- package/src/render/draw.ts +437 -175
- package/src/render/elevation.ts +349 -143
- package/src/render/figmaIcons.ts +22 -0
- package/src/render/hit.ts +140 -21
- package/src/render/icons.ts +69 -10
- package/src/render/linkTile.ts +297 -0
- package/src/render/markup.ts +91 -0
- package/src/render/navigate.ts +114 -0
- package/src/render/snap.ts +105 -22
- package/src/render/squircle.ts +73 -0
- package/src/render/view.ts +255 -19
- package/src/skia/SkiaDrawCtx.ts +163 -41
- package/src/skia/host.ts +46 -1
- package/src/solver/equations.ts +37 -2
- package/src/solver/gauss-newton.ts +125 -28
- package/src/solver/linalg.ts +141 -5
- package/src/solver/types.ts +5 -0
- package/src/styles.css +91 -42
- package/src/tools/annotate.ts +58 -29
- package/src/tools/chamfer.ts +103 -0
- package/src/tools/corner.ts +99 -0
- package/src/tools/dim.ts +525 -93
- package/src/tools/elevation.ts +5 -5
- package/src/tools/fillet.ts +82 -0
- package/src/tools/freehand.ts +20 -16
- package/src/tools/index.ts +17 -4
- package/src/tools/line.ts +59 -23
- package/src/tools/modify.ts +86 -24
- package/src/tools/openings.ts +11 -16
- package/src/tools/registry.ts +27 -2
- package/src/tools/select.ts +424 -75
- package/src/tools/shapes.ts +119 -58
- package/src/ui/ctx.ts +2 -1
- package/src/ui/domPorts.ts +23 -4
- package/src/ui/drawer.ts +6 -26
- package/src/ui/elevation.ts +10 -101
- package/src/ui/keypad.ts +8 -14
- package/src/ui/laser.ts +2 -2
- package/src/ui/menu.ts +63 -17
- package/src/ui/noteinput.ts +3 -3
- package/src/ui/opacity.ts +108 -0
- package/src/ui/palette.ts +67 -12
- package/src/ui/rail.ts +21 -7
- package/src/ui/topbar.ts +4 -14
- package/src/units/format.ts +24 -6
- package/src/units/parse.ts +4 -3
- package/src/units/prefs.ts +13 -1
- package/src/view3d/build.ts +607 -0
- package/src/view3d/camera.ts +113 -0
- package/src/view3d/fatLines.ts +83 -0
- package/src/view3d/lines.ts +258 -0
- package/src/view3d/mesh.ts +111 -0
- package/src/view3d/poly.ts +104 -0
- package/src/view3d/viewer.ts +348 -0
- package/dist/react/ElevCanvas.native.d.ts +0 -3
- package/dist/react/ElevCanvas.native.js +0 -58
- package/dist/react/ElevCanvas.native.js.map +0 -1
- package/dist/react/ElevCanvas.web.d.ts +0 -4
- package/dist/react/ElevCanvas.web.js +0 -44
- package/dist/react/ElevCanvas.web.js.map +0 -1
- package/dist/react/ElevationSheet.d.ts +0 -2
- package/dist/react/ElevationSheet.js +0 -38
- package/dist/react/ElevationSheet.js.map +0 -1
- package/dist/render/elevationView.d.ts +0 -3
- package/dist/render/elevationView.js +0 -100
- package/dist/render/elevationView.js.map +0 -1
- package/src/react/ElevCanvas.native.tsx +0 -68
- package/src/react/ElevCanvas.web.tsx +0 -42
- package/src/react/ElevationSheet.tsx +0 -88
- package/src/render/elevationView.ts +0 -73
package/src/tools/elevation.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { openingHeights, wallHeight } from '../model/elevation';
|
|
|
3
3
|
import { pushHistory } from '../model/history';
|
|
4
4
|
import { uid } from '../model/ids';
|
|
5
5
|
import { driveCheck, edgeS, openingSpan, refS, rerootLock, sameRef } from '../model/openings';
|
|
6
|
-
import { SK } from '../model/store';
|
|
6
|
+
import { planSK as SK } from '../model/store';
|
|
7
7
|
import type { Opening, OpeningDim, OpeningEdge, OpeningRef } from '../model/types';
|
|
8
8
|
import { fmtLen } from '../units';
|
|
9
9
|
import { editPlacedOpeningDim, fits, overDefined } from './openings';
|
|
@@ -25,7 +25,7 @@ export function editSill(opId: string): void {
|
|
|
25
25
|
if (!l) return;
|
|
26
26
|
const { sill, head } = openingHeights(o), h = head - sill, H = wallHeight(l);
|
|
27
27
|
openKeypad({
|
|
28
|
-
title: Name(o) + ' sill height', sub: 'Floor to the bottom of the ' + nameOf(o) + ' — locks its height above the floor', initial: fmtLen(sill), selectAll: true, laser: true,
|
|
28
|
+
title: Name(o) + ' sill height', sub: 'Floor to the bottom of the ' + nameOf(o) + ' — locks its height above the floor', initial: fmtLen(sill), value: sill, selectAll: true, laser: true,
|
|
29
29
|
onDone: v => {
|
|
30
30
|
if (v < 0) return;
|
|
31
31
|
if (v + h > H + 1e-6) {
|
|
@@ -46,7 +46,7 @@ export function editOpeningHeight(opId: string): void {
|
|
|
46
46
|
if (!l) return;
|
|
47
47
|
const { sill, head } = openingHeights(o), H = wallHeight(l);
|
|
48
48
|
openKeypad({
|
|
49
|
-
title: Name(o) + ' height', sub: 'Bottom to top of the ' + nameOf(o), initial: fmtLen(head - sill), selectAll: true, laser: true,
|
|
49
|
+
title: Name(o) + ' height', sub: 'Bottom to top of the ' + nameOf(o), initial: fmtLen(head - sill), value: head - sill, selectAll: true, laser: true,
|
|
50
50
|
onDone: v => {
|
|
51
51
|
if (v <= 0) return;
|
|
52
52
|
if (sill + v > H + 1e-6) {
|
|
@@ -67,7 +67,7 @@ export function editWallHeight(lid: string): void {
|
|
|
67
67
|
if (!l) return;
|
|
68
68
|
const tallest = Math.max(0, ...Object.values(sk.openings).filter(o => o.line === lid).map(o => openingHeights(o).head));
|
|
69
69
|
openKeypad({
|
|
70
|
-
title: 'Wall height', sub: 'Floor to ceiling, this wall only', initial: fmtLen(wallHeight(l)), selectAll: true, laser: true,
|
|
70
|
+
title: 'Wall height', sub: 'Floor to ceiling, this wall only', initial: fmtLen(wallHeight(l)), value: wallHeight(l), selectAll: true, laser: true,
|
|
71
71
|
onDone: v => {
|
|
72
72
|
if (v <= 0) return;
|
|
73
73
|
if (v < tallest - 1e-6) { toast('A door or window here reaches ' + fmtLen(tallest) + '. Enter ' + fmtLen(tallest) + ' or more, or lower it first.', { warn: true }); return; }
|
|
@@ -96,7 +96,7 @@ export function editElevationPosition(opId: string, edge: OpeningEdge, ref: Open
|
|
|
96
96
|
const d: OpeningDim = { id: uid('od'), edge, ref, value: cur, sign: e >= r ? 1 : -1, driving: true };
|
|
97
97
|
openKeypad({
|
|
98
98
|
title, sub: 'Locks the ' + nameOf(o) + '’s position',
|
|
99
|
-
initial: fmtLen(cur), selectAll: true, laser: true,
|
|
99
|
+
initial: fmtLen(cur), value: cur, selectAll: true, laser: true,
|
|
100
100
|
onDone: v => {
|
|
101
101
|
if (v < 0) return;
|
|
102
102
|
if (!fits(o, d, v)) return;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { afterEdit, tryAddConstraint } from '../app/edit';
|
|
2
|
+
import { ed } from '../app/state';
|
|
3
|
+
import { dropHistory, pushHistory } from '../model/history';
|
|
4
|
+
import { uid } from '../model/ids';
|
|
5
|
+
import { addPoint } from '../model/sketch';
|
|
6
|
+
import { replaceSK, SK } from '../model/store';
|
|
7
|
+
import type { Sketch } from '../model/types';
|
|
8
|
+
import { requestDraw } from '../render/view';
|
|
9
|
+
import { solver } from '../solver';
|
|
10
|
+
import { fmtLen } from '../units';
|
|
11
|
+
import { openKeypad, toast, updateCtx } from '../editor/ports';
|
|
12
|
+
import { cornerAt, cutCorner } from './corner';
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
Fillet: round off a corner where two lines (or walls) meet. The corner becomes an arc tangent to both, the lines
|
|
16
|
+
stop where it starts (tools/corner.ts), and a driving radius holds it. Two walls get a curved wall of the same
|
|
17
|
+
thickness, and a space around the corner follows the curve.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** The corner's two lines, if it can be rounded off. */
|
|
21
|
+
export function filletable(sk: Sketch, pid: string): [string, string] | null {
|
|
22
|
+
const k = cornerAt(sk, pid);
|
|
23
|
+
return k ? [k.l1, k.l2] : null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The biggest radius that fits the corner (the arc would reach the end of the shorter line). */
|
|
27
|
+
export function maxFillet(sk: Sketch, pid: string): number {
|
|
28
|
+
const k = cornerAt(sk, pid);
|
|
29
|
+
return k ? Math.min(k.len1, k.len2) * Math.tan(k.th / 2) : 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The Fillet action: the keypad for the radius, then the corner is rounded off. */
|
|
33
|
+
export function filletPrompt(pid: string): void {
|
|
34
|
+
const sk = SK();
|
|
35
|
+
if (!cornerAt(sk, pid)) { toast('Select a corner where two lines meet to round it off.', { warn: true }); return; }
|
|
36
|
+
const most = maxFillet(sk, pid), start = Math.min(6, most * 0.5);
|
|
37
|
+
openKeypad({
|
|
38
|
+
title: 'Fillet radius', sub: 'Up to ' + fmtLen(most) + ' fits this corner', initial: fmtLen(Math.round(start * 16) / 16), value: Math.round(start * 16) / 16, selectAll: true, laser: true,
|
|
39
|
+
onDone: r => { filletCorner(pid, r); },
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Round off corner `pid` with an arc of radius `r`. Returns the arc's id, or null (and says why) if it can't. */
|
|
44
|
+
export function filletCorner(pid: string, r: number): string | null {
|
|
45
|
+
const sk = SK(), k = cornerAt(sk, pid);
|
|
46
|
+
if (!k) { toast('Select a corner where two lines meet to round it off.', { warn: true }); return null; }
|
|
47
|
+
if (!(r > 0)) return null;
|
|
48
|
+
const d = r / Math.tan(k.th / 2);
|
|
49
|
+
if (d >= k.len1 - 1e-6 || d >= k.len2 - 1e-6) {
|
|
50
|
+
toast('That radius doesn’t fit this corner: ' + fmtLen(maxFillet(sk, pid)) + ' at most.', { warn: true });
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
pushHistory();
|
|
54
|
+
const before = JSON.stringify(sk), L1 = sk.lines[k.l1], L2 = sk.lines[k.l2], P = { ...k.P };
|
|
55
|
+
// the centre on the bisector, the radius away from both lines
|
|
56
|
+
const bx = k.u1.x + k.u2.x, by = k.u1.y + k.u2.y, bl = Math.hypot(bx, by) || 1, far = r / Math.sin(k.th / 2);
|
|
57
|
+
const c = addPoint(sk, P.x + (bx / bl) * far, P.y + (by / bl) * far), wall = L1.kind === 'wall' && L2.kind === 'wall';
|
|
58
|
+
const cut = cutCorner(sk, k, d, d, (t1, t2) => {
|
|
59
|
+
const id = uid('ar'), C = sk.points[c], A = sk.points[t1], B = sk.points[t2];
|
|
60
|
+
// counter-clockwise from its start to its end: the short way round, from one line to the other
|
|
61
|
+
const ccw = (A.x - C.x) * (B.y - C.y) - (A.y - C.y) * (B.x - C.x) > 0;
|
|
62
|
+
sk.arcs[id] = { c, a: ccw ? t1 : t2, b: ccw ? t2 : t1, constr: L1.constr && L2.constr, ...(wall ? { kind: 'wall' as const, thick: L1.thick } : {}) };
|
|
63
|
+
return id;
|
|
64
|
+
});
|
|
65
|
+
// tangent to both lines (on the side the centre is), and the radius
|
|
66
|
+
const side = (lid: string) => { const l = sk.lines[lid], A = sk.points[l.a], B = sk.points[l.b], o = sk.points[c]; return (B.x - A.x) * (o.y - A.y) - (B.y - A.y) * (o.x - A.x) < 0 ? -1 : 1; };
|
|
67
|
+
for (const cn of [...cut.cons, { type: 'tan' as const, l: k.l1, c: cut.edge, sign: side(k.l1) }, { type: 'tan' as const, l: k.l2, c: cut.edge, sign: side(k.l2) }]) tryAddConstraint(cn, { silent: true });
|
|
68
|
+
// its radius, the leader out through the middle of the arc (toward the old corner)
|
|
69
|
+
tryAddConstraint({ type: 'diam', c: cut.edge, value: 2 * r, driving: true, rad: true, ang: Math.atan2(P.y - sk.points[c].y, P.x - sk.points[c].x) }, { silent: true });
|
|
70
|
+
if (!solver.solve(sk).ok) {
|
|
71
|
+
replaceSK(JSON.parse(before) as Sketch);
|
|
72
|
+
dropHistory();
|
|
73
|
+
toast('That corner can’t be rounded off with the constraints it has.', { warn: true });
|
|
74
|
+
afterEdit();
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
ed.sel = [{ type: 'arc', id: cut.edge }];
|
|
78
|
+
afterEdit();
|
|
79
|
+
updateCtx(); requestDraw();
|
|
80
|
+
toast('Corner rounded off: ' + fmtLen(r) + ' radius');
|
|
81
|
+
return cut.edge;
|
|
82
|
+
}
|
package/src/tools/freehand.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { afterEdit, tryAddConstraint } from '../app/edit';
|
|
2
2
|
import { ed } from '../app/state';
|
|
3
|
-
import type
|
|
3
|
+
import { wallBand, type PEv } from './registry';
|
|
4
4
|
import { hyp } from '../model/geom';
|
|
5
5
|
import { dropHistory, pushHistory } from '../model/history';
|
|
6
6
|
import { maybeRoom } from '../model/rooms';
|
|
@@ -8,10 +8,12 @@ import { settings } from '../model/settings';
|
|
|
8
8
|
import { addLine, addPoint } from '../model/sketch';
|
|
9
9
|
import { deg, JOIN_PX, simplifyStroke, SQUARE_DEG, squareUp, turn } from '../model/stroke';
|
|
10
10
|
import { SK } from '../model/store';
|
|
11
|
-
import type { Constraint, XY } from '../model/types';
|
|
11
|
+
import type { Constraint, LineKind, XY } from '../model/types';
|
|
12
12
|
import { TH } from '../render/theme';
|
|
13
|
-
import { mainT, requestDraw, S, s2w } from '../render/view';
|
|
13
|
+
import { mainT, requestDraw, requestOverlayDraw, S, s2w } from '../render/view';
|
|
14
14
|
import { toast } from '../editor/ports';
|
|
15
|
+
import { intoWall } from '../editor/elevation';
|
|
16
|
+
import { drawsWalls } from '../editor/mode';
|
|
15
17
|
|
|
16
18
|
/*
|
|
17
19
|
Draw to shape: sketch freehand, and on release the stroke becomes the line segments it most likely meant —
|
|
@@ -25,7 +27,7 @@ import { toast } from '../editor/ports';
|
|
|
25
27
|
* Turn a stroke (world points) into lines in the current sketch, as one edit the caller wraps in history.
|
|
26
28
|
* Returns the new line ids, or null if the stroke was too small to mean anything.
|
|
27
29
|
*/
|
|
28
|
-
export function strokeToLines(stroke: XY[], opts: { constr?: boolean } = {}): { lines: string[]; closed: boolean } | null {
|
|
30
|
+
export function strokeToLines(stroke: XY[], opts: { constr?: boolean; kind?: LineKind } = {}): { lines: string[]; closed: boolean } | null {
|
|
29
31
|
const sk = SK(), T = mainT(), simp = simplifyStroke(stroke, T.s);
|
|
30
32
|
if (!simp) return null;
|
|
31
33
|
const closed = simp.closed, pts = squareUp(simp.pts, closed);
|
|
@@ -37,7 +39,7 @@ export function strokeToLines(stroke: XY[], opts: { constr?: boolean } = {}): {
|
|
|
37
39
|
};
|
|
38
40
|
const ids = pts.map((p, i) => (!closed && (i === 0 || i === pts.length - 1) && near(p)) || addPoint(sk, p.x, p.y));
|
|
39
41
|
const segs = closed ? ids.length : ids.length - 1, lines: string[] = [];
|
|
40
|
-
for (let i = 0; i < segs; i++) lines.push(addLine(sk, ids[i], ids[(i + 1) % ids.length], { constr: !!opts.constr }));
|
|
42
|
+
for (let i = 0; i < segs; i++) lines.push(addLine(sk, ids[i], ids[(i + 1) % ids.length], { kind: opts.kind, constr: !!opts.constr }));
|
|
41
43
|
if (settings.autoCons) {
|
|
42
44
|
// level / plumb segments, then square corners that aren't already square by H + V
|
|
43
45
|
const axis: ('h' | 'v' | null)[] = [];
|
|
@@ -65,7 +67,7 @@ export const freehandTool = {
|
|
|
65
67
|
busy() { return !!this.st; },
|
|
66
68
|
hover() {},
|
|
67
69
|
down(ev: PEv) {
|
|
68
|
-
const [x, y] = s2w(ev.sx, ev.sy);
|
|
70
|
+
const [x, y] = intoWall(...s2w(ev.sx, ev.sy)); // (in the wall view, inside the wall)
|
|
69
71
|
this.st = { pts: [{ x, y }], last: [ev.sx, ev.sy] };
|
|
70
72
|
this.prev();
|
|
71
73
|
},
|
|
@@ -73,32 +75,34 @@ export const freehandTool = {
|
|
|
73
75
|
if (!this.st) return;
|
|
74
76
|
if (hyp(ev.sx - this.st.last[0], ev.sy - this.st.last[1]) < 2) return;
|
|
75
77
|
this.st.last = [ev.sx, ev.sy];
|
|
76
|
-
const [x, y] = s2w(ev.sx, ev.sy);
|
|
78
|
+
const [x, y] = intoWall(...s2w(ev.sx, ev.sy));
|
|
77
79
|
this.st.pts.push({ x, y });
|
|
78
80
|
this.prev();
|
|
79
81
|
},
|
|
80
82
|
prev() {
|
|
81
83
|
const pts = this.st!.pts;
|
|
82
84
|
ed.preview = (c, T) => {
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
c.
|
|
86
|
-
|
|
85
|
+
const wall = drawsWalls(), trace = () => pts.forEach((p, i) => { const [x, y] = S(T, p.x, p.y); i ? c.lineTo(x, y) : c.moveTo(x, y); });
|
|
86
|
+
if (wall) wallBand(c, T, trace);
|
|
87
|
+
c.save(); c.strokeStyle = ed.drawConstr && !wall ? TH.constr : TH.ink; c.lineWidth = 2.5; c.lineCap = 'round'; c.lineJoin = 'round';
|
|
88
|
+
if (ed.drawConstr && !wall) c.setLineDash([6, 5]);
|
|
89
|
+
c.beginPath(); trace();
|
|
87
90
|
c.stroke(); c.restore();
|
|
88
91
|
};
|
|
89
|
-
|
|
92
|
+
requestOverlayDraw();
|
|
90
93
|
},
|
|
91
94
|
up() {
|
|
92
95
|
if (!this.st) return;
|
|
93
96
|
const pts = this.st.pts;
|
|
94
97
|
this.reset();
|
|
95
98
|
pushHistory();
|
|
96
|
-
|
|
99
|
+
// (walls with the Walls toggle on; a wall is never a guide)
|
|
100
|
+
const wall = drawsWalls(), r = strokeToLines(pts, wall ? { kind: 'wall' } : { constr: ed.drawConstr });
|
|
97
101
|
if (!r) { dropHistory(); requestDraw(); return; }
|
|
98
|
-
ed.sel =
|
|
102
|
+
ed.sel = [];
|
|
99
103
|
afterEdit();
|
|
100
|
-
const n = r.lines.length;
|
|
101
|
-
toast((r.closed ? 'Closed shape, ' : '') + n + '
|
|
104
|
+
const n = r.lines.length, what = wall ? 'wall' : 'line';
|
|
105
|
+
toast((r.closed ? 'Closed shape, ' : '') + n + ' ' + what + (n > 1 ? 's' : '') + '. Drag points or add constraints to adjust — select a ' + what + ' and tap + for its length.');
|
|
102
106
|
},
|
|
103
107
|
hint() { return 'Draw freehand. When you lift, it becomes straight lines — then adjust with constraints and lengths.'; },
|
|
104
108
|
};
|
package/src/tools/index.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
makeAselectTool, withHandles, arrowTool, calibTool, cloudTool, markCircleTool, markLineTool, markRectTool, markTriangleTool,
|
|
3
|
+
penTool, textTool,
|
|
4
|
+
} from './annotate';
|
|
2
5
|
import { dimTool } from './dim';
|
|
3
6
|
import { freehandTool } from './freehand';
|
|
4
7
|
import { makeLineTool, type LineTool } from './line';
|
|
@@ -7,6 +10,7 @@ import { ed } from '../app/state';
|
|
|
7
10
|
import { tools } from './registry';
|
|
8
11
|
import { selectTool } from './select';
|
|
9
12
|
import { arcTool, circleTool, rectTool } from './shapes';
|
|
13
|
+
import { cabinetTool, heightTool, wallDoorTool, wallWindowTool } from '../editor/elevation';
|
|
10
14
|
|
|
11
15
|
export { tools, setTool, cancelTool } from './registry';
|
|
12
16
|
export type { LineTool } from './line';
|
|
@@ -15,9 +19,8 @@ export type { LineTool } from './line';
|
|
|
15
19
|
export function registerTools(): void {
|
|
16
20
|
Object.assign(tools, {
|
|
17
21
|
select: selectTool,
|
|
18
|
-
line: makeLineTool(
|
|
22
|
+
line: makeLineTool(),
|
|
19
23
|
freehand: freehandTool,
|
|
20
|
-
wall: makeLineTool('wall'),
|
|
21
24
|
rect: rectTool,
|
|
22
25
|
circle: circleTool,
|
|
23
26
|
arc: arcTool,
|
|
@@ -33,9 +36,19 @@ export function registerTools(): void {
|
|
|
33
36
|
pen: withHandles(penTool),
|
|
34
37
|
arrow: withHandles(arrowTool),
|
|
35
38
|
cloud: withHandles(cloudTool),
|
|
39
|
+
// the markup shapes (Arrow's group on the rail): 'm' for markup, apart from Draft's Line, Rectangle and Circle
|
|
40
|
+
mline: withHandles(markLineTool),
|
|
41
|
+
mrect: withHandles(markRectTool),
|
|
42
|
+
mtri: withHandles(markTriangleTool),
|
|
43
|
+
mcircle: withHandles(markCircleTool),
|
|
36
44
|
text: textTool,
|
|
37
45
|
calib: calibTool,
|
|
46
|
+
// the wall view's own
|
|
47
|
+
wdoor: wallDoorTool,
|
|
48
|
+
wwindow: wallWindowTool,
|
|
49
|
+
cabinet: cabinetTool,
|
|
50
|
+
height: heightTool,
|
|
38
51
|
});
|
|
39
52
|
}
|
|
40
53
|
|
|
41
|
-
export const lineTool = (t: string): LineTool | null => (t === 'line'
|
|
54
|
+
export const lineTool = (t: string): LineTool | null => (t === 'line' ? (tools[t] as LineTool) : null);
|
package/src/tools/line.ts
CHANGED
|
@@ -5,15 +5,18 @@ import { pushHistory } from '../model/history';
|
|
|
5
5
|
import { roomGeom, maybeRoom } from '../model/rooms';
|
|
6
6
|
import { settings } from '../model/settings';
|
|
7
7
|
import { addLine, gc, pinnedPoints } from '../model/sketch';
|
|
8
|
-
import { SK } from '../model/store';
|
|
8
|
+
import { SK, store } from '../model/store';
|
|
9
9
|
import type { LineKind } from '../model/types';
|
|
10
10
|
import { TH } from '../render/theme';
|
|
11
|
-
import { snapAt } from '../render/snap';
|
|
12
|
-
import { requestDraw, S } from '../render/view';
|
|
11
|
+
import { shiftSnap, snap45, snapAt } from '../render/snap';
|
|
12
|
+
import { requestDraw, requestOverlayDraw, S } from '../render/view';
|
|
13
13
|
import { fmtArea } from '../units';
|
|
14
|
-
import { commitPoint, applyPending, lengthPreview, PT, rememberHover, type PEv, type Tool } from './registry';
|
|
14
|
+
import { commitPoint, applyPending, lengthPreview, PT, rememberHover, wallBand, type PEv, type Tool } from './registry';
|
|
15
15
|
import { openKeypad, toast, updateCtx, updateHint } from '../editor/ports';
|
|
16
16
|
import type { KeyIn } from '../editor/keys';
|
|
17
|
+
import { intoWall, PAST_WALL } from '../editor/elevation';
|
|
18
|
+
import { drawsWalls } from '../editor/mode';
|
|
19
|
+
import { splitWallsAtEnds } from './modify';
|
|
17
20
|
|
|
18
21
|
export interface LineState {
|
|
19
22
|
start: string | null;
|
|
@@ -21,6 +24,8 @@ export interface LineState {
|
|
|
21
24
|
prev: string | null;
|
|
22
25
|
chain: string[];
|
|
23
26
|
down: { sx: number; sy: number } | null;
|
|
27
|
+
/** where a press that may drag out the first segment snapped (the segment's start, if it drags) */
|
|
28
|
+
downSnap: Snap | null;
|
|
24
29
|
dir: [number, number];
|
|
25
30
|
}
|
|
26
31
|
|
|
@@ -31,9 +36,13 @@ export interface LineTool extends Tool {
|
|
|
31
36
|
finish(): void;
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
/**
|
|
40
|
+
* The Line tool: tap-tap chains, drag for one segment, type a length, close back to the start. With the rail's Walls
|
|
41
|
+
* toggle on it draws walls (along their centre), as every drawing tool does.
|
|
42
|
+
*/
|
|
43
|
+
export function makeLineTool(): LineTool {
|
|
44
|
+
const kindNow = (): LineKind => (drawsWalls() ? 'wall' : 'line');
|
|
45
|
+
const fresh = (dir: [number, number]): LineState => ({ start: null, first: null, prev: null, chain: [], down: null, downSnap: null, dir });
|
|
37
46
|
const t = {
|
|
38
47
|
st: fresh([1, 0]),
|
|
39
48
|
reset() {
|
|
@@ -41,44 +50,65 @@ export function makeLineTool(kind: LineKind): LineTool {
|
|
|
41
50
|
if (had) pinnedPoints.delete(had);
|
|
42
51
|
this.st = fresh(this.st ? this.st.dir : [1, 0]);
|
|
43
52
|
ed.snapState = null; ed.preview = null;
|
|
44
|
-
|
|
53
|
+
// tidy the drawing it was building on — unless a new session cleared it (left the editor mid-chain)
|
|
54
|
+
if (had && store.project) { gc(SK()); analyze(); requestDraw(); }
|
|
45
55
|
updateCtx(); updateHint();
|
|
46
56
|
},
|
|
47
57
|
busy() { return !!this.st.start; },
|
|
48
58
|
opts(ev: PEv) { return { from: this.st.start, prevLine: this.st.prev, touch: ev.touch }; },
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
/** where the pointer snaps to (with Shift, from the line's start: level, plumb or 45°) */
|
|
60
|
+
at(ev: PEv): Snap {
|
|
61
|
+
const s = snapAt(ev.sx, ev.sy, this.opts(ev)), from = this.st.start ? null : this.dragFrom(ev);
|
|
62
|
+
if (!ev.shift) return s;
|
|
63
|
+
if (this.st.start) return shiftSnap(this.st.start, s);
|
|
64
|
+
if (from) { const q = snap45(from, s); return { x: q.x, y: q.y, kind: 'free', guides: [] }; }
|
|
65
|
+
return s;
|
|
66
|
+
},
|
|
67
|
+
/** dragging out the first segment: where it starts (null for a press that hasn't moved, or a hover) */
|
|
68
|
+
dragFrom(ev: PEv): Snap | null {
|
|
69
|
+
const st = this.st;
|
|
70
|
+
return st.down && st.downSnap && hyp(ev.sx - st.down.sx, ev.sy - st.down.sy) > 12 ? st.downSnap : null;
|
|
71
|
+
},
|
|
72
|
+
hover(ev: PEv) { ed.snapState = this.at(ev); rememberHover(ed.snapState); this.setPreview(this.st.start ? null : this.dragFrom(ev)); requestOverlayDraw(); },
|
|
73
|
+
/** the segment being drawn: from the last point (or, dragging out the first one, from where the drag began) */
|
|
74
|
+
setPreview(from: Snap | null = null) {
|
|
51
75
|
const st = this.st;
|
|
52
|
-
if (!st.start || !ed.snapState) { ed.preview = null; return; }
|
|
53
|
-
const a = PT(st.start), b = { x: ed.snapState.x, y: ed.snapState.y };
|
|
76
|
+
if ((!st.start && !from) || !ed.snapState) { ed.preview = null; return; }
|
|
77
|
+
const a = st.start ? PT(st.start) : { x: from!.x, y: from!.y }, b = { x: ed.snapState.x, y: ed.snapState.y };
|
|
54
78
|
if (hyp(b.x - a.x, b.y - a.y) > 1e-6) st.dir = norm2(b.x - a.x, b.y - a.y);
|
|
55
79
|
ed.preview = (c, T) => {
|
|
56
|
-
const [x1, y1] = S(T, a.x, a.y), [x2, y2] = S(T, b.x, b.y);
|
|
80
|
+
const [x1, y1] = S(T, a.x, a.y), [x2, y2] = S(T, b.x, b.y), wall = kindNow() === 'wall';
|
|
81
|
+
if (wall) wallBand(c, T, () => { c.moveTo(x1, y1); c.lineTo(x2, y2); });
|
|
57
82
|
c.save(); c.lineCap = 'butt';
|
|
58
|
-
if (kind === 'wall') { c.strokeStyle = 'rgba(0,102,255,.25)'; c.lineWidth = 4.5 * T.s; c.beginPath(); c.moveTo(x1, y1); c.lineTo(x2, y2); c.stroke(); }
|
|
59
83
|
c.strokeStyle = TH.blue; c.lineWidth = 2;
|
|
60
|
-
if (ed.drawConstr) c.setLineDash([6, 5]);
|
|
84
|
+
if (ed.drawConstr && !wall) c.setLineDash([6, 5]);
|
|
61
85
|
c.beginPath(); c.moveTo(x1, y1); c.lineTo(x2, y2); c.stroke(); c.restore();
|
|
62
86
|
lengthPreview(c, T, a, b);
|
|
63
87
|
};
|
|
64
88
|
},
|
|
65
|
-
down(ev: PEv) {
|
|
89
|
+
down(ev: PEv) {
|
|
90
|
+
this.st.down = { sx: ev.sx, sy: ev.sy };
|
|
91
|
+
this.st.downSnap = this.st.start ? null : snapAt(ev.sx, ev.sy, { touch: ev.touch });
|
|
92
|
+
this.hover(ev);
|
|
93
|
+
},
|
|
66
94
|
move(ev: PEv) { this.hover(ev); },
|
|
67
95
|
up(ev: PEv) {
|
|
68
96
|
const st = this.st;
|
|
69
97
|
const dragged = !!st.down && hyp(ev.sx - st.down.sx, ev.sy - st.down.sy) > 12;
|
|
70
98
|
if (!st.start) {
|
|
71
99
|
pushHistory();
|
|
72
|
-
const s0 = dragged ? snapAt(st.down!.sx, st.down!.sy, { touch: ev.touch }) : snapAt(ev.sx, ev.sy, { touch: ev.touch });
|
|
100
|
+
const s0 = dragged ? st.downSnap ?? snapAt(st.down!.sx, st.down!.sy, { touch: ev.touch }) : snapAt(ev.sx, ev.sy, { touch: ev.touch });
|
|
73
101
|
const a = commitPoint(s0);
|
|
74
102
|
applyPending(a.cons);
|
|
75
103
|
st.start = a.id; st.first = a.id; st.chain = [a.id];
|
|
104
|
+
// (drawing: nothing left highlighted — neither what was selected before nor, as it goes, what's drawn)
|
|
105
|
+
ed.sel = [];
|
|
76
106
|
pinnedPoints.clear(); pinnedPoints.add(a.id);
|
|
77
|
-
if (dragged) this.segmentTo(
|
|
107
|
+
if (dragged) this.segmentTo(this.at(ev));
|
|
78
108
|
afterEdit(); this.hover(ev); updateCtx(); updateHint();
|
|
79
109
|
return;
|
|
80
110
|
}
|
|
81
|
-
const s =
|
|
111
|
+
const s = this.at(ev);
|
|
82
112
|
if (s.kind === 'point' && s.ref === st.start) { this.finish(); return; }
|
|
83
113
|
pushHistory(); this.segmentTo(s); afterEdit();
|
|
84
114
|
if (this.st.start) this.hover(ev);
|
|
@@ -87,10 +117,16 @@ export function makeLineTool(kind: LineKind): LineTool {
|
|
|
87
117
|
const st = this.st;
|
|
88
118
|
const a = PT(st.start!);
|
|
89
119
|
if (hyp(s.x - a.x, s.y - a.y) < 1e-6) return;
|
|
120
|
+
// in the wall view: not past the wall (a typed length that would run out of it)
|
|
121
|
+
const [ix, iy] = intoWall(s.x, s.y);
|
|
122
|
+
if (Math.abs(ix - s.x) > 1e-6 || Math.abs(iy - s.y) > 1e-6) { toast(PAST_WALL, { warn: true }); return; }
|
|
90
123
|
const b = commitPoint(s);
|
|
91
124
|
if (b.id === st.start) return;
|
|
92
|
-
|
|
125
|
+
// walls are always solid (the construction-lines toggle is for lines)
|
|
126
|
+
const kind = kindNow(), lid = addLine(SK(), st.start!, b.id, { kind, constr: kind !== 'wall' && ed.drawConstr });
|
|
93
127
|
applyPending(b.cons);
|
|
128
|
+
// (a wall ending partway along another wall splits that one there)
|
|
129
|
+
splitWallsAtEnds(SK(), lid);
|
|
94
130
|
if (settings.autoCons) {
|
|
95
131
|
for (const g of s.guides || []) {
|
|
96
132
|
if (g.kind === 'H') tryAddConstraint({ type: 'h', a: g.from, b: b.id }, { silent: true });
|
|
@@ -105,7 +141,7 @@ export function makeLineTool(kind: LineKind): LineTool {
|
|
|
105
141
|
if (closed) {
|
|
106
142
|
const loop = st.chain.slice(0, -1);
|
|
107
143
|
const sk = SK();
|
|
108
|
-
const rid =
|
|
144
|
+
const rid = kindNow() === 'wall' || !ed.drawConstr ? maybeRoom(sk, loop) : null;
|
|
109
145
|
this.reset();
|
|
110
146
|
if (rid) toast(sk.rooms[rid].name + ' added — ' + fmtArea(roomGeom(sk, sk.rooms[rid]).area));
|
|
111
147
|
}
|
|
@@ -150,8 +186,8 @@ export function makeLineTool(kind: LineKind): LineTool {
|
|
|
150
186
|
return false;
|
|
151
187
|
},
|
|
152
188
|
hint() {
|
|
153
|
-
if (this.st.start) return ed.lastPointerTouch ? 'Tap the next point. Tap
|
|
154
|
-
return
|
|
189
|
+
if (this.st.start) return ed.lastPointerTouch ? 'Tap the next point. Tap the last point again to finish, or the first to close the shape.' : 'Click the next point (hold Shift for level, plumb or 45°), or just type a length and press Enter. Esc finishes.';
|
|
190
|
+
return kindNow() === 'wall' ? 'Draw wall centerlines. Tap to start — walls are 4 1/2" thick; change it after.' : 'Tap to place points. Drawing back to the start closes the shape.';
|
|
155
191
|
},
|
|
156
192
|
};
|
|
157
193
|
return t;
|
package/src/tools/modify.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { afterEdit, solveOrRevert, tryAddConstraint } from '../app/edit';
|
|
1
|
+
import { afterEdit, holdFaces, solveOrRevert, tryAddConstraint, undo } from '../app/edit';
|
|
2
2
|
import { ed } from '../app/state';
|
|
3
|
+
import { splitFaces } from '../model/faces';
|
|
3
4
|
import { clamp, hyp, lineCircleT, norm2, projT, segInter } from '../model/geom';
|
|
4
5
|
import { dropHistory, pushHistory } from '../model/history';
|
|
5
6
|
import { uid } from '../model/ids';
|
|
6
7
|
import { findFace, maybeRoom, roomGeom } from '../model/rooms';
|
|
7
8
|
import { addLine, addPoint } from '../model/sketch';
|
|
8
9
|
import { SK } from '../model/store';
|
|
9
|
-
import type { Constraint } from '../model/types';
|
|
10
|
+
import type { Constraint, Sketch } from '../model/types';
|
|
10
11
|
import { hitAny } from '../render/hit';
|
|
11
12
|
import { outsideSide } from '../model/planGeom';
|
|
12
13
|
import { mainT, requestDraw, s2w } from '../render/view';
|
|
14
|
+
import { placeTargets, snapSlide } from '../editor/elevation';
|
|
13
15
|
import { fmtArea, fmtLen } from '../units';
|
|
14
16
|
import type { PEv } from './registry';
|
|
15
17
|
import { openKeypad, toast } from '../editor/ports';
|
|
@@ -141,20 +143,31 @@ export const openingTool = {
|
|
|
141
143
|
move(ev: PEv) { this.hover(ev); },
|
|
142
144
|
up(ev: PEv) {
|
|
143
145
|
const h = hitAny(ev.sx, ev.sy, { touch: ev.touch, only: 'geom' });
|
|
144
|
-
if (!h || h.type !== 'line') { toast('Tap a wall to place it.'); return; }
|
|
146
|
+
if (!h || h.type !== 'line') { toast('Tap a wall to place it.', { warn: true }); return; }
|
|
145
147
|
const sk = SK(), l = sk.lines[h.id], A = sk.points[l.a], B = sk.points[l.b];
|
|
148
|
+
// doors and windows are a wall's: not on a guide; a plain line becomes a wall to take one
|
|
149
|
+
if (l.constr) { toast('Doors and windows go in walls — that’s a construction line.', { warn: true }); return; }
|
|
150
|
+
const toWall = l.kind !== 'wall';
|
|
146
151
|
const [wx, wy] = s2w(ev.sx, ev.sy);
|
|
147
152
|
const L = hyp(B.x - A.x, B.y - A.y), w = 36;
|
|
148
|
-
if (L < w + 2) { toast('That wall is too short for a ' + fmtLen(w) + ' ' + ed.openingType + '.', { warn: true }); return; }
|
|
149
|
-
|
|
153
|
+
if (L < w + 2) { toast('That ' + (toWall ? 'line' : 'wall') + ' is too short for a ' + fmtLen(w) + ' ' + ed.openingType + '.', { warn: true }); return; }
|
|
154
|
+
// (snapped, as one slid along the wall is: an edge to the wall's ends, its corners, another door's or window's edge)
|
|
155
|
+
const at0 = projT(A, B, { x: wx, y: wy }) * L;
|
|
156
|
+
const at = clamp(at0 + snapSlide(at0, w, 0, placeTargets(sk, h.id), (ev.touch ? 16 : 10) / mainT().s), w / 2, L - w / 2);
|
|
150
157
|
const side = (B.x - A.x) * (wy - A.y) - (B.y - A.y) * (wx - A.x) >= 0;
|
|
151
158
|
pushHistory();
|
|
159
|
+
if (toWall) { l.kind = 'wall'; l.thick = l.thick || 4.5; }
|
|
152
160
|
const id = uid('o');
|
|
153
|
-
sk.openings[id] = { line: h.id, at, w, type: ed.openingType, flip: !side };
|
|
154
|
-
|
|
161
|
+
sk.openings[id] = { line: h.id, at, w, wSet: false, type: ed.openingType, flip: !side };
|
|
162
|
+
// (placed, not selected: tap it to size it, flip it or slide it)
|
|
163
|
+
ed.sel = [];
|
|
164
|
+
// (a line made a wall: what's measured on the faces of the walls it meets holds)
|
|
165
|
+
if (toWall) holdFaces();
|
|
155
166
|
afterEdit();
|
|
167
|
+
// (one undo step takes back both)
|
|
168
|
+
if (toWall) toast('Made that line a wall for the ' + ed.openingType + '.', { action: { label: 'Undo', fn: undo } });
|
|
156
169
|
},
|
|
157
|
-
hint() { return 'Tap a wall to place a 3\'-0" ' + ed.openingType + '. Slide it along the wall with Select.'; },
|
|
170
|
+
hint() { return 'Tap a wall to place a 3\'-0" ' + ed.openingType + ' (a line you tap becomes a wall). Slide it along the wall with Select.'; },
|
|
158
171
|
};
|
|
159
172
|
|
|
160
173
|
export const roomTool = {
|
|
@@ -169,8 +182,8 @@ export const roomTool = {
|
|
|
169
182
|
if (!face) { toast('Tap inside a closed shape. That spot isn’t enclosed by lines.', { warn: true }); return; }
|
|
170
183
|
pushHistory();
|
|
171
184
|
const id = maybeRoom(sk, face.loop, face.edges);
|
|
172
|
-
if (!id) { dropHistory(); toast('That area is too small for a space.'); return; }
|
|
173
|
-
ed.sel = [
|
|
185
|
+
if (!id) { dropHistory(); toast('That area is too small for a space.', { warn: true }); return; }
|
|
186
|
+
ed.sel = [];
|
|
174
187
|
afterEdit();
|
|
175
188
|
toast(sk.rooms[id].name + ' — ' + fmtArea(roomGeom(sk, sk.rooms[id]).area));
|
|
176
189
|
},
|
|
@@ -185,44 +198,93 @@ export const roomTool = {
|
|
|
185
198
|
export function splitLine(lid: string): string | null {
|
|
186
199
|
const sk = SK(), l = sk.lines[lid];
|
|
187
200
|
if (!l) return null;
|
|
201
|
+
const A = sk.points[l.a], B = sk.points[l.b];
|
|
202
|
+
if (hyp(B.x - A.x, B.y - A.y) < 1e-6) return null;
|
|
203
|
+
pushHistory();
|
|
204
|
+
const nid = splitLineAt(sk, lid, addPoint(sk, (A.x + B.x) / 2, (A.y + B.y) / 2));
|
|
205
|
+
ed.sel = nid ? [{ type: 'line', id: lid }, { type: 'line', id: nid }] : [];
|
|
206
|
+
afterEdit();
|
|
207
|
+
toast('Split into two lines. Drag the middle point to move the split.');
|
|
208
|
+
return nid;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Split line `lid` at point `pid`, which lies on it between its ends: it runs to pid, and a new line (returned) runs on
|
|
213
|
+
* from there to its old end — the same kind, thickness and height. The two stay collinear and keep its constraints
|
|
214
|
+
* (see splitLine), its spaces, its doors and windows (each on the piece it's on, measured from where it was) and what's
|
|
215
|
+
* drawn on its faces. Part of an edit: the caller pushes history and runs afterEdit.
|
|
216
|
+
*/
|
|
217
|
+
export function splitLineAt(sk: Sketch, lid: string, pid: string): string | null {
|
|
218
|
+
const l = sk.lines[lid], P = sk.points[pid];
|
|
219
|
+
if (!l || !P) return null;
|
|
188
220
|
const A = sk.points[l.a], B = sk.points[l.b], L = hyp(B.x - A.x, B.y - A.y);
|
|
189
221
|
if (L < 1e-6) return null;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
l.
|
|
194
|
-
const nid = addLine(sk, m, oldB, { kind: l.kind, constr: l.constr, thick: l.thick });
|
|
222
|
+
const s = clamp(projT(A, B, P), 0, 1) * L, oldA = l.a, oldB = l.b, side = outsideSide(sk, lid);
|
|
223
|
+
l.b = pid;
|
|
224
|
+
const nid = addLine(sk, pid, oldB, { kind: l.kind, constr: l.constr, thick: l.thick });
|
|
225
|
+
if (l.height != null) sk.lines[nid].height = l.height;
|
|
195
226
|
const copies: Constraint[] = [];
|
|
196
227
|
for (const id in sk.cons) {
|
|
197
228
|
const c = sk.cons[id];
|
|
198
229
|
const onAB = (c.a === oldA && c.b === oldB) || (c.a === oldB && c.b === oldA);
|
|
199
|
-
if ((c.type === 'h' || c.type === 'v') && onAB) { c.a = oldA; c.b =
|
|
230
|
+
if ((c.type === 'h' || c.type === 'v') && onAB) { c.a = oldA; c.b = pid; copies.push({ type: c.type, a: pid, b: oldB }); }
|
|
200
231
|
else if ((c.type === 'par' || c.type === 'perp' || c.type === 'col') && (c.l1 === lid || c.l2 === lid)) copies.push(Object.assign({}, c, c.l1 === lid ? { l1: nid } : { l2: nid }));
|
|
201
232
|
else if (c.type === 'dist' && c.line === lid) {
|
|
202
233
|
// still the full end-to-end distance; moved out beside the wall so the new middle point stays reachable
|
|
203
234
|
c.line = null;
|
|
204
235
|
if (!c.off) c.off = (30 / mainT().s) * side * (c.a === oldA ? 1 : -1);
|
|
236
|
+
// (one measured on the wall's face no longer has one wall to be measured along: end to end, as it is now)
|
|
237
|
+
if (c.face) { delete c.face; if (c.driving !== false) c.value = L; }
|
|
205
238
|
}
|
|
206
239
|
else if (c.type === 'mid' && c.l === lid) delete sk.cons[id]; // "midpoint of" no longer means the same line
|
|
240
|
+
// (the point it's split at is an end of both pieces now, not on the line)
|
|
241
|
+
else if (c.type === 'online' && c.p === pid && c.l === lid) delete sk.cons[id];
|
|
207
242
|
}
|
|
208
243
|
for (const c of copies) tryAddConstraint(c, { silent: true });
|
|
209
244
|
tryAddConstraint({ type: 'col', l1: lid, l2: nid }, { silent: true });
|
|
210
|
-
// rooms: put the
|
|
245
|
+
// rooms: put the split point into any loop that ran along this line
|
|
211
246
|
for (const id in sk.rooms) {
|
|
212
247
|
const r = sk.rooms[id], loop = r.loop;
|
|
213
248
|
for (let i = 0; i < loop.length; i++) {
|
|
214
249
|
const p = loop[i], q = loop[(i + 1) % loop.length];
|
|
215
250
|
if (r.edges ? r.edges[i] !== lid : !((p === oldA && q === oldB) || (p === oldB && q === oldA))) continue;
|
|
216
|
-
loop.splice(i + 1, 0,
|
|
217
|
-
// edges follow the walk direction: oldA→
|
|
251
|
+
loop.splice(i + 1, 0, pid);
|
|
252
|
+
// edges follow the walk direction: oldA→pid is the original line, pid→oldB the new one
|
|
218
253
|
if (r.edges) r.edges.splice(i, 1, ...(p === oldA ? [lid, nid] : [nid, lid]));
|
|
219
254
|
break;
|
|
220
255
|
}
|
|
221
256
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
257
|
+
for (const id in sk.openings) {
|
|
258
|
+
const o = sk.openings[id];
|
|
259
|
+
// doors/windows past the split move to the second piece
|
|
260
|
+
if (o.line === lid && o.at > s) { o.line = nid; o.at -= s; }
|
|
261
|
+
// and what's measured from the far corner of the wall is measured from that corner still — the new piece's
|
|
262
|
+
for (const d of o.dims ?? []) if ('face' in d.ref && d.ref.line === lid && d.ref.face === 'b') d.ref = { ...d.ref, line: nid };
|
|
263
|
+
}
|
|
264
|
+
// and so does what's drawn on the wall's faces past it
|
|
265
|
+
splitFaces(sk, lid, nid, s);
|
|
227
266
|
return nid;
|
|
228
267
|
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* A wall drawn to (or from) partway along another wall splits that one there, so they meet at a corner they share — as
|
|
271
|
+
* three walls meeting at a point do. Lines never split anything, and nothing splits a line. Part of an edit.
|
|
272
|
+
*/
|
|
273
|
+
export function splitWallsAtEnds(sk: Sketch, lid: string): void {
|
|
274
|
+
const l = sk.lines[lid];
|
|
275
|
+
if (!l || l.kind !== 'wall' || l.constr) return;
|
|
276
|
+
for (const pid of [l.a, l.b]) {
|
|
277
|
+
const P = sk.points[pid];
|
|
278
|
+
if (!P) continue;
|
|
279
|
+
for (const [wid, w] of Object.entries(sk.lines)) {
|
|
280
|
+
if (wid === lid || w.kind !== 'wall' || w.constr || w.a === pid || w.b === pid) continue;
|
|
281
|
+
const A = sk.points[w.a], B = sk.points[w.b], L = hyp(B.x - A.x, B.y - A.y);
|
|
282
|
+
if (!A || !B || L < 1e-6) continue;
|
|
283
|
+
const t = projT(A, B, P), off = Math.abs((P.x - A.x) * (B.y - A.y) - (P.y - A.y) * (B.x - A.x)) / L;
|
|
284
|
+
// (on it, and an inch or more from either end)
|
|
285
|
+
if (off > 1e-3 || t * L < 1 || (1 - t) * L < 1) continue;
|
|
286
|
+
splitLineAt(sk, wid, pid);
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|