@reekon-tools/boldr-cad 0.1.0 → 1.0.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 +81 -9
- 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 +5 -0
- package/dist/data/memory.js +2 -2
- package/dist/data/memory.js.map +1 -1
- package/dist/data/provider.d.ts +19 -7
- package/dist/data/sync.d.ts +7 -2
- package/dist/data/sync.js +115 -64
- 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 +19 -4
- package/dist/editor/keypad.js +39 -9
- 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 +36 -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 +167 -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 +5 -0
- 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 +46 -3
- package/dist/react/CadEditor.js +77 -20
- 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 +581 -96
- package/dist/tools/dim.js.map +1 -1
- package/dist/tools/elevation.js +1 -1
- 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 +22 -15
- 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 +10 -2
- package/dist/units/format.js +18 -3
- package/dist/units/format.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 +2 -2
- package/src/data/provider.ts +15 -7
- package/src/data/sync.ts +87 -50
- 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 +43 -11
- 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 +37 -6
- package/src/editor/project.ts +155 -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 +5 -0
- 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 +133 -30
- 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 +524 -92
- package/src/tools/elevation.ts +1 -1
- 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 +8 -13
- 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 +18 -3
- 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/annotate.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { targetImage } from '../editor/project';
|
|
2
|
+
import { imageById } from '../model/images';
|
|
1
3
|
import { strokeToLines } from './freehand';
|
|
2
4
|
import { afterEdit } from '../app/edit';
|
|
3
5
|
import { ed } from '../app/state';
|
|
@@ -5,10 +7,12 @@ import { hyp } from '../model/geom';
|
|
|
5
7
|
import { dropHistory, pushHistory } from '../model/history';
|
|
6
8
|
import { uid } from '../model/ids';
|
|
7
9
|
import { page, SK } from '../model/store';
|
|
8
|
-
import
|
|
10
|
+
import { dashFor, DEFAULT_NOTE_WIDTH, NOTE_TEXT_IN, noteLook, shapeOutline } from '../model/markup';
|
|
11
|
+
import type { Note, NoteType } from '../model/types';
|
|
9
12
|
import { cloudPath } from '../render/draw';
|
|
13
|
+
import { strokeMarkup, strokeOutline } from '../render/markup';
|
|
10
14
|
import { TH } from '../render/theme';
|
|
11
|
-
import { fitView, mainT, requestDraw, S, s2w, underlayH } from '../render/view';
|
|
15
|
+
import { fitView, mainT, requestDraw, requestOverlayDraw, S, s2w, underlayH } from '../render/view';
|
|
12
16
|
import { tools, type PEv, type Tool } from './registry';
|
|
13
17
|
import { hitAny } from '../render/hit';
|
|
14
18
|
import { selectTool } from './select';
|
|
@@ -16,6 +20,15 @@ import { setMode } from '../editor/mode';
|
|
|
16
20
|
import { hideNoteInput, openKeypad, setCursor, showNoteInput, toast, updateHint } from '../editor/ports';
|
|
17
21
|
import { selectionHint } from '../editor/selection';
|
|
18
22
|
|
|
23
|
+
/** A new note in the current markup look (text takes no stroke width). */
|
|
24
|
+
function newNote(type: NoteType, id = ''): Note {
|
|
25
|
+
const n: Note = { id, type, color: ed.noteColor };
|
|
26
|
+
if (type !== 'text' && ed.noteWidth !== DEFAULT_NOTE_WIDTH) n.width = ed.noteWidth;
|
|
27
|
+
if (type === 'text' && ed.noteTextIn !== NOTE_TEXT_IN) n.ts = ed.noteTextIn;
|
|
28
|
+
if (ed.noteStyle !== 'solid') n.style = ed.noteStyle;
|
|
29
|
+
return n;
|
|
30
|
+
}
|
|
31
|
+
|
|
19
32
|
/** Markup select: the draft select tool with its own state and hint. */
|
|
20
33
|
export const makeAselectTool = (): Tool => Object.assign({}, selectTool, {
|
|
21
34
|
st: {},
|
|
@@ -41,12 +54,9 @@ export const penTool = {
|
|
|
41
54
|
prev() {
|
|
42
55
|
const pts = this.st!.pts;
|
|
43
56
|
ed.preview = (c, T) => {
|
|
44
|
-
|
|
45
|
-
c.beginPath();
|
|
46
|
-
pts.forEach((p, i) => { const [x, y] = S(T, p[0], p[1]); i ? c.lineTo(x, y) : c.moveTo(x, y); });
|
|
47
|
-
c.stroke(); c.restore();
|
|
57
|
+
strokeMarkup(c, pts.map(p => S(T, p[0], p[1])), noteLook(newNote('pen')));
|
|
48
58
|
};
|
|
49
|
-
|
|
59
|
+
requestOverlayDraw();
|
|
50
60
|
},
|
|
51
61
|
up() {
|
|
52
62
|
if (!this.st) return;
|
|
@@ -55,14 +65,16 @@ export const penTool = {
|
|
|
55
65
|
if (pts.length < 2) { requestDraw(); return; }
|
|
56
66
|
pushHistory();
|
|
57
67
|
const id = uid('n');
|
|
58
|
-
SK().notes.push({
|
|
59
|
-
|
|
68
|
+
SK().notes.push({ ...newNote('pen'), id, pts });
|
|
69
|
+
// (drawn, not selected: nothing new is highlighted — tap it with Select to change it)
|
|
70
|
+
ed.sel = [];
|
|
60
71
|
afterEdit();
|
|
61
72
|
},
|
|
62
73
|
hint() { return 'Draw freehand markup. Select a stroke to turn it into straight, constrained lines.'; },
|
|
63
74
|
};
|
|
64
75
|
|
|
65
|
-
|
|
76
|
+
/** Markup drawn by a drag: a line or arrow from end to end, or a cloud or shape filling the box the drag makes. */
|
|
77
|
+
function dragShapeTool(type: Exclude<NoteType, 'pen' | 'text'>, hintText: string) {
|
|
66
78
|
return {
|
|
67
79
|
st: null as { a: [number, number]; b: [number, number] } | null,
|
|
68
80
|
reset() { this.st = null; ed.preview = null; },
|
|
@@ -74,24 +86,27 @@ function dragShapeTool(type: Extract<NoteType, 'arrow' | 'cloud'>, hintText: str
|
|
|
74
86
|
this.st.b = s2w(ev.sx, ev.sy);
|
|
75
87
|
const { a, b } = this.st as { a: [number, number]; b: [number, number] };
|
|
76
88
|
ed.preview = (c, T) => {
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
if (
|
|
80
|
-
else
|
|
81
|
-
|
|
89
|
+
const look = noteLook(newNote(type)), [x1, y1] = S(T, a[0], a[1]), [x2, y2] = S(T, b[0], b[1]);
|
|
90
|
+
const outline = shapeOutline({ ...newNote(type), pts: [a, b] });
|
|
91
|
+
if (outline) strokeOutline(c, outline.map(p => S(T, p[0], p[1])), look);
|
|
92
|
+
else if (type === 'cloud') {
|
|
93
|
+
c.save(); c.strokeStyle = look.color; c.lineWidth = look.width; c.lineCap = 'round'; c.setLineDash(dashFor(look.style, look.width));
|
|
94
|
+
cloudPath(c, Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), Math.abs(y2 - y1)); c.stroke();
|
|
95
|
+
c.restore();
|
|
96
|
+
} else strokeMarkup(c, [[x1, y1], [x2, y2]], look);
|
|
82
97
|
};
|
|
83
|
-
|
|
98
|
+
requestOverlayDraw();
|
|
84
99
|
},
|
|
85
100
|
up() {
|
|
86
101
|
if (!this.st) return;
|
|
87
102
|
const { a, b } = this.st as { a: [number, number]; b: [number, number] };
|
|
88
103
|
this.reset!();
|
|
89
104
|
const T = mainT();
|
|
90
|
-
if (hyp(a[0] - b[0], a[1] - b[1]) * T.s < 10) { requestDraw(); if (type
|
|
105
|
+
if (hyp(a[0] - b[0], a[1] - b[1]) * T.s < 10) { requestDraw(); if (type !== 'cloud') toast('Drag to draw ' + DRAG_NAME[type] + '.', { warn: true }); return; }
|
|
91
106
|
pushHistory();
|
|
92
107
|
const id = uid('n');
|
|
93
|
-
SK().notes.push({
|
|
94
|
-
ed.sel = [
|
|
108
|
+
SK().notes.push({ ...newNote(type), id, pts: [a, b] });
|
|
109
|
+
ed.sel = [];
|
|
95
110
|
afterEdit();
|
|
96
111
|
},
|
|
97
112
|
hint() { return hintText; },
|
|
@@ -100,6 +115,13 @@ function dragShapeTool(type: Extract<NoteType, 'arrow' | 'cloud'>, hintText: str
|
|
|
100
115
|
|
|
101
116
|
export const arrowTool = dragShapeTool('arrow', 'Drag from the note toward what it points at.');
|
|
102
117
|
export const cloudTool = dragShapeTool('cloud', 'Drag a box around the area to flag. Revision clouds read like a red pen on paper.');
|
|
118
|
+
export const markLineTool = dragShapeTool('line', 'Drag from one end of the line to the other.');
|
|
119
|
+
export const markRectTool = dragShapeTool('rect', 'Drag from one corner of the rectangle to the opposite one.');
|
|
120
|
+
export const markTriangleTool = dragShapeTool('triangle', 'Drag a box for the triangle to fill, its point at the top.');
|
|
121
|
+
export const markCircleTool = dragShapeTool('ellipse', 'Drag a box for the circle to fill (an oval when the box isn’t square).');
|
|
122
|
+
|
|
123
|
+
/** What a drag too short to draw anything was meant to draw ('Drag to draw …'). */
|
|
124
|
+
const DRAG_NAME: Record<string, string> = { arrow: 'an arrow', line: 'a line', rect: 'a rectangle', triangle: 'a triangle', ellipse: 'a circle' };
|
|
103
125
|
|
|
104
126
|
export const textTool = {
|
|
105
127
|
reset() { hideNoteInput(); },
|
|
@@ -112,19 +134,26 @@ export const textTool = {
|
|
|
112
134
|
if (!txt) return;
|
|
113
135
|
pushHistory();
|
|
114
136
|
const id = uid('n');
|
|
115
|
-
SK().notes.push({
|
|
116
|
-
ed.sel = [
|
|
137
|
+
SK().notes.push({ ...newNote('text'), id, x, y, text: txt });
|
|
138
|
+
ed.sel = [];
|
|
117
139
|
afterEdit();
|
|
118
140
|
});
|
|
119
141
|
},
|
|
120
142
|
hint() { return 'Tap where the note goes, type, then press Enter.'; },
|
|
121
143
|
};
|
|
122
144
|
|
|
123
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* Two-point image calibration: tap two points, type the real distance, the image rescales. It scales the image that
|
|
147
|
+
* was selected when it started (or the page's only image); the others keep their own scale.
|
|
148
|
+
*/
|
|
124
149
|
export const calibTool = {
|
|
125
|
-
st: { pts: [] as [number, number][] },
|
|
126
|
-
enter() {
|
|
127
|
-
|
|
150
|
+
st: { pts: [] as [number, number][], id: null as string | null },
|
|
151
|
+
enter() {
|
|
152
|
+
const u = targetImage();
|
|
153
|
+
this.st = { pts: [], id: u?.id ?? null };
|
|
154
|
+
if (!u) toast(page().underlays.length ? 'Select the image to scale first, then Set scale.' : 'Add a photo or plan first, then calibrate it.', { warn: true });
|
|
155
|
+
},
|
|
156
|
+
reset() { this.st = { pts: [], id: this.st?.id ?? null }; ed.preview = null; },
|
|
128
157
|
busy() { return this.st.pts.length > 0; },
|
|
129
158
|
hover(ev: PEv) {
|
|
130
159
|
if (this.st.pts.length === 1) {
|
|
@@ -134,14 +163,14 @@ export const calibTool = {
|
|
|
134
163
|
c.save(); c.strokeStyle = TH.purple; c.lineWidth = 2; c.setLineDash([6, 4]);
|
|
135
164
|
c.beginPath(); c.moveTo(x1, y1); c.lineTo(x2, y2); c.stroke(); c.restore();
|
|
136
165
|
};
|
|
137
|
-
|
|
166
|
+
requestOverlayDraw();
|
|
138
167
|
}
|
|
139
168
|
},
|
|
140
169
|
down() {},
|
|
141
170
|
move(ev: PEv) { this.hover(ev); },
|
|
142
171
|
up(ev: PEv) {
|
|
143
|
-
const u =
|
|
144
|
-
if (!u) { toast('Add a photo or plan first.'); return; }
|
|
172
|
+
const u = this.st.id ? imageById(this.st.id) : undefined;
|
|
173
|
+
if (!u) { toast(page().underlays.length ? 'Select the image to scale first, then Set scale.' : 'Add a photo or plan first.', { warn: true }); return; }
|
|
145
174
|
this.st.pts.push(s2w(ev.sx, ev.sy));
|
|
146
175
|
updateHint();
|
|
147
176
|
if (this.st.pts.length < 2) { this.hover(ev); return; }
|
|
@@ -172,7 +201,7 @@ export function convertNote(id: string): void {
|
|
|
172
201
|
if (!n || !n.pts) return;
|
|
173
202
|
pushHistory();
|
|
174
203
|
const r = strokeToLines(n.pts.map(p => ({ x: p[0], y: p[1] })));
|
|
175
|
-
if (!r) { dropHistory(); toast('That stroke is too small to turn into lines.'); return; }
|
|
204
|
+
if (!r) { dropHistory(); toast('That stroke is too small to turn into lines.', { warn: true }); return; }
|
|
176
205
|
sk.notes = sk.notes.filter(x => x.id !== id);
|
|
177
206
|
setMode('draft');
|
|
178
207
|
ed.sel = r.lines.map(id => ({ type: 'line' as const, id }));
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { afterEdit, tryAddConstraint } from '../app/edit';
|
|
2
|
+
import { ed } from '../app/state';
|
|
3
|
+
import { dropHistory, pushHistory } from '../model/history';
|
|
4
|
+
import { addLine } from '../model/sketch';
|
|
5
|
+
import { replaceSK, SK } from '../model/store';
|
|
6
|
+
import type { Sketch } from '../model/types';
|
|
7
|
+
import { requestDraw } from '../render/view';
|
|
8
|
+
import { solver } from '../solver';
|
|
9
|
+
import { fmtLen } from '../units';
|
|
10
|
+
import { closeKeypad, openKeypad, toast, updateCtx } from '../editor/ports';
|
|
11
|
+
import { cornerAt, cutCorner } from './corner';
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
Chamfer: cut a corner off straight. Both lines stop a distance back from the corner — the same on both sides
|
|
15
|
+
unless it's set as unequal — and a straight cut joins them (a wall of the same thickness between two walls). A
|
|
16
|
+
chamfer is measured from its corner, so the corner stays as a virtual sharp (dashed lines out to it): the distance
|
|
17
|
+
back along one side is a driving dimension, and "equal" holds the other side to it (or a second dimension, unequal).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** The corner's two lines, if it can be cut off. */
|
|
21
|
+
export const chamferable = (sk: Sketch, pid: string): boolean => !!cornerAt(sk, pid);
|
|
22
|
+
|
|
23
|
+
/** The most a corner can be cut back along each line. */
|
|
24
|
+
export function maxChamfer(sk: Sketch, pid: string): [number, number] {
|
|
25
|
+
const k = cornerAt(sk, pid);
|
|
26
|
+
return k ? [k.len1, k.len2] : [0, 0];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Show which side a distance is along while it's entered (the line selected). */
|
|
30
|
+
function showSide(lid: string | null): void { ed.sel = lid ? [{ type: 'line', id: lid }] : []; updateCtx(); requestDraw(); }
|
|
31
|
+
|
|
32
|
+
/** The Chamfer action: the keypad for the distance back from the corner (Unequal: one for each side), then the corner is cut off. */
|
|
33
|
+
export function chamferPrompt(pid: string): void {
|
|
34
|
+
const sk = SK(), k = cornerAt(sk, pid);
|
|
35
|
+
if (!k) { toast('Select a corner where two lines meet to cut it off.', { warn: true }); return; }
|
|
36
|
+
const most = Math.min(k.len1, k.len2), start = Math.round(Math.min(6, most * 0.5) * 16) / 16;
|
|
37
|
+
openKeypad({
|
|
38
|
+
title: 'Chamfer', sub: 'Back from the corner along each side — up to ' + fmtLen(most), initial: fmtLen(start), selectAll: true, laser: true,
|
|
39
|
+
extras: () => [{ label: 'Unequal', ariaLabel: 'Unequal: a distance for each side', onPress: () => { closeKeypad(); unequal(pid); } }],
|
|
40
|
+
onDone: d => { chamferCorner(pid, d, d); },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Unequal: the distance along one side (selected while it's entered), then along the other. */
|
|
45
|
+
function unequal(pid: string): void {
|
|
46
|
+
const k = cornerAt(SK(), pid);
|
|
47
|
+
if (!k) return;
|
|
48
|
+
const back = ed.sel;
|
|
49
|
+
showSide(k.l1);
|
|
50
|
+
openKeypad({
|
|
51
|
+
title: 'Chamfer · first side', sub: 'Along the selected line — up to ' + fmtLen(k.len1), initial: fmtLen(Math.round(Math.min(6, k.len1 * 0.5) * 16) / 16), selectAll: true, laser: true,
|
|
52
|
+
onClose: () => { if (ed.sel.length === 1 && ed.sel[0].id === k.l1) { ed.sel = back; updateCtx(); requestDraw(); } },
|
|
53
|
+
onDone: d1 => {
|
|
54
|
+
showSide(k.l2);
|
|
55
|
+
openKeypad({
|
|
56
|
+
title: 'Chamfer · second side', sub: 'Along the selected line — up to ' + fmtLen(k.len2), initial: fmtLen(Math.round(Math.min(6, k.len2 * 0.5) * 16) / 16), selectAll: true, laser: true,
|
|
57
|
+
onClose: () => { if (ed.sel.length === 1 && ed.sel[0].id === k.l2) { ed.sel = back; updateCtx(); requestDraw(); } },
|
|
58
|
+
onDone: d2 => { chamferCorner(pid, d1, d2); },
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Cut corner `pid` off: `d1` back along its first line, `d2` along its second (the same: an equal chamfer, held equal).
|
|
66
|
+
* Returns the cut's line id, or null (and says why) if it can't.
|
|
67
|
+
*/
|
|
68
|
+
export function chamferCorner(pid: string, d1: number, d2: number = d1): string | null {
|
|
69
|
+
const sk = SK(), k = cornerAt(sk, pid);
|
|
70
|
+
if (!k) { toast('Select a corner where two lines meet to cut it off.', { warn: true }); return null; }
|
|
71
|
+
if (!(d1 > 0) || !(d2 > 0)) return null;
|
|
72
|
+
if (d1 >= k.len1 - 1e-6 || d2 >= k.len2 - 1e-6) {
|
|
73
|
+
const most = d1 === d2 ? fmtLen(Math.min(k.len1, k.len2)) : fmtLen(d1 >= k.len1 - 1e-6 ? k.len1 : k.len2);
|
|
74
|
+
toast('That’s more than this corner’s side: ' + most + ' at most.', { warn: true });
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
pushHistory();
|
|
78
|
+
const before = JSON.stringify(sk), L1 = sk.lines[k.l1], L2 = sk.lines[k.l2], wall = L1.kind === 'wall' && L2.kind === 'wall';
|
|
79
|
+
const cut = cutCorner(sk, k, d1, d2, (t1, t2) => {
|
|
80
|
+
const id = addLine(sk, t1, t2, { kind: wall ? 'wall' : 'line', constr: L1.constr && L2.constr, thick: wall ? L1.thick : undefined });
|
|
81
|
+
return id;
|
|
82
|
+
}, true);
|
|
83
|
+
const { c1, c2 } = cut.sharp!;
|
|
84
|
+
// the distance back along the first side drives it; the second is equal to it, or its own
|
|
85
|
+
const cons = [
|
|
86
|
+
...cut.cons,
|
|
87
|
+
{ type: 'dist' as const, a: cut.t1, b: pid, line: c1, value: d1, driving: true },
|
|
88
|
+
d1 === d2 ? { type: 'eq' as const, l1: c1, l2: c2 } : { type: 'dist' as const, a: cut.t2, b: pid, line: c2, value: d2, driving: true },
|
|
89
|
+
];
|
|
90
|
+
for (const cn of cons) tryAddConstraint(cn, { silent: true });
|
|
91
|
+
if (!solver.solve(sk).ok) {
|
|
92
|
+
replaceSK(JSON.parse(before) as Sketch);
|
|
93
|
+
dropHistory();
|
|
94
|
+
toast('That corner can’t be cut off with the constraints it has.', { warn: true });
|
|
95
|
+
afterEdit();
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
ed.sel = [{ type: 'line', id: cut.edge }];
|
|
99
|
+
afterEdit();
|
|
100
|
+
updateCtx(); requestDraw();
|
|
101
|
+
toast('Corner cut off: ' + (d1 === d2 ? fmtLen(d1) + ' each side' : fmtLen(d1) + ' × ' + fmtLen(d2)));
|
|
102
|
+
return cut.edge;
|
|
103
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { cornerLines } from '../editor/selection';
|
|
2
|
+
import { hyp } from '../model/geom';
|
|
3
|
+
import { roomEdges } from '../model/rooms';
|
|
4
|
+
import { addLine, addPoint } from '../model/sketch';
|
|
5
|
+
import type { Constraint, Sketch, WallFace, XY } from '../model/types';
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
Cutting a corner back, for Fillet and Chamfer: where two lines (or walls) meet, each line stops a distance short of
|
|
9
|
+
the corner and something new joins the two ends (an arc, a straight cut). What hangs on the lines follows: doors
|
|
10
|
+
and windows keep their place, level/plumb moves to the new ends, a space around the corner takes the new edge.
|
|
11
|
+
Whatever was measured to the corner keeps its meaning: the corner stays as a "virtual sharp" — dashed lines from
|
|
12
|
+
the new ends out to where the two lines would meet.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const norm = (x: number, y: number): XY => { const L = hyp(x, y) || 1; return { x: x / L, y: y / L }; };
|
|
16
|
+
|
|
17
|
+
/** A corner where two lines meet: the point, the two lines with their far ends, directions away from the corner and lengths, and the angle between them. */
|
|
18
|
+
export interface Corner {
|
|
19
|
+
pid: string; P: XY;
|
|
20
|
+
l1: string; l2: string; q1: string; q2: string;
|
|
21
|
+
u1: XY; u2: XY; len1: number; len2: number;
|
|
22
|
+
/** the angle between the lines, radians (0 < th < π) */
|
|
23
|
+
th: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The corner at `pid`, if it can be cut back: two lines meet there, not in a straight line, neither of them a wall face's own edge. */
|
|
27
|
+
export function cornerAt(sk: Sketch, pid: string): Corner | null {
|
|
28
|
+
const pair = cornerLines(sk, pid), P = sk.points[pid];
|
|
29
|
+
if (!pair || !P || P.anchor || pair.some(id => sk.lines[id].anchor)) return null;
|
|
30
|
+
const [l1, l2] = pair, L1 = sk.lines[l1], L2 = sk.lines[l2];
|
|
31
|
+
const q1 = L1.a === pid ? L1.b : L1.a, q2 = L2.a === pid ? L2.b : L2.a, Q1 = sk.points[q1], Q2 = sk.points[q2];
|
|
32
|
+
const u1 = norm(Q1.x - P.x, Q1.y - P.y), u2 = norm(Q2.x - P.x, Q2.y - P.y);
|
|
33
|
+
const th = Math.acos(Math.max(-1, Math.min(1, u1.x * u2.x + u1.y * u2.y)));
|
|
34
|
+
if (!(th > 0.5 * Math.PI / 180 && th < 179.5 * Math.PI / 180)) return null;
|
|
35
|
+
return { pid, P, l1, l2, q1, q2, u1, u2, len1: hyp(Q1.x - P.x, Q1.y - P.y), len2: hyp(Q2.x - P.x, Q2.y - P.y), th };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Whether anything besides its two lines hangs on the corner (dimensions, constraints, curves, door/window dimensions, a cabinet). */
|
|
39
|
+
function cornerUsed(sk: Sketch, k: Corner): boolean {
|
|
40
|
+
const { pid, l1, l2 } = k;
|
|
41
|
+
return Object.values(sk.cons).some(cn => cn.a === pid || cn.b === pid || cn.p === pid || (cn.type === 'angle' && [cn.l1, cn.l2].includes(l1) && [cn.l1, cn.l2].includes(l2)))
|
|
42
|
+
|| Object.values(sk.circles).some(ci => ci.c === pid) || Object.values(sk.arcs).some(a => [a.c, a.a, a.b].includes(pid))
|
|
43
|
+
|| Object.values(sk.openings).some(o => (o.dims || []).some(dm => 'pt' in dm.ref && dm.ref.pt === pid))
|
|
44
|
+
|| Object.values((sk as WallFace).cabs ?? {}).some(cb => [cb.bl, cb.br, cb.tr, cb.tl].includes(pid));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface Cut {
|
|
48
|
+
/** the new ends of the two lines */
|
|
49
|
+
t1: string; t2: string;
|
|
50
|
+
/** what joins them */
|
|
51
|
+
edge: string;
|
|
52
|
+
/** the virtual sharp's dashed lines (new end → corner), when the corner is kept */
|
|
53
|
+
sharp: { c1: string; c2: string } | null;
|
|
54
|
+
/** constraints to add for the cut (the dashed lines staying on the lines' lines) */
|
|
55
|
+
cons: Constraint[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Cut corner `k` back: line 1 stops `d1` short of it, line 2 `d2`, and `join(t1, t2)` makes what joins the new ends
|
|
60
|
+
* (returning its id). `keepSharp`: keep the corner as a virtual sharp even when nothing else hangs on it (a chamfer
|
|
61
|
+
* is measured from it). The caller has pushed history; it adds the returned constraints and its own, and solves.
|
|
62
|
+
*/
|
|
63
|
+
export function cutCorner(sk: Sketch, k: Corner, d1: number, d2: number, join: (t1: string, t2: string) => string, keepSharp = false): Cut {
|
|
64
|
+
const { pid, P, l1, l2, q1, q2, u1, u2 } = k, L1 = sk.lines[l1], L2 = sk.lines[l2];
|
|
65
|
+
// the spaces around the corner, as they are now (their sides are found from the corner's lines)
|
|
66
|
+
const rooms = Object.entries(sk.rooms).map(([id, rm]) => ({ id, i: rm.loop.indexOf(pid), edges: rm.loop.includes(pid) ? roomEdges(sk, rm) : null })).filter(x => x.i >= 0 && x.edges);
|
|
67
|
+
const t1 = addPoint(sk, P.x + u1.x * d1, P.y + u1.y * d1), t2 = addPoint(sk, P.x + u2.x * d2, P.y + u2.y * d2);
|
|
68
|
+
// doors and windows keep their place along a line whose start moves
|
|
69
|
+
for (const o of Object.values(sk.openings)) {
|
|
70
|
+
if (o.line === l1 && L1.a === pid) o.at -= d1;
|
|
71
|
+
if (o.line === l2 && L2.a === pid) o.at -= d2;
|
|
72
|
+
}
|
|
73
|
+
// level/plumb on a line, set between the corner and its other end: between the new end and that end now
|
|
74
|
+
for (const cn of Object.values(sk.cons)) {
|
|
75
|
+
if (cn.type !== 'h' && cn.type !== 'v') continue;
|
|
76
|
+
if ((cn.a === pid && cn.b === q1) || (cn.b === pid && cn.a === q1)) { if (cn.a === pid) cn.a = t1; else cn.b = t1; }
|
|
77
|
+
else if ((cn.a === pid && cn.b === q2) || (cn.b === pid && cn.a === q2)) { if (cn.a === pid) cn.a = t2; else cn.b = t2; }
|
|
78
|
+
}
|
|
79
|
+
if (L1.a === pid) L1.a = t1; else L1.b = t1;
|
|
80
|
+
if (L2.a === pid) L2.a = t2; else L2.b = t2;
|
|
81
|
+
const keep = keepSharp || cornerUsed(sk, k);
|
|
82
|
+
const edge = join(t1, t2), cons: Constraint[] = [];
|
|
83
|
+
let sharp: Cut['sharp'] = null;
|
|
84
|
+
if (keep) {
|
|
85
|
+
const c1 = addLine(sk, t1, pid, { constr: true }), c2 = addLine(sk, t2, pid, { constr: true });
|
|
86
|
+
sharp = { c1, c2 };
|
|
87
|
+
cons.push({ type: 'col', l1, l2: c1 }, { type: 'col', l1: l2, l2: c2 });
|
|
88
|
+
// an angle at the corner is between the dashed lines now (they still meet there)
|
|
89
|
+
for (const cn of Object.values(sk.cons)) if (cn.type === 'angle' && [cn.l1, cn.l2].includes(l1) && [cn.l1, cn.l2].includes(l2)) { cn.l1 = cn.l1 === l1 ? c1 : c2; cn.l2 = cn.l2 === l2 ? c2 : c1; }
|
|
90
|
+
}
|
|
91
|
+
// spaces: the corner becomes the two new ends, with the new edge between them
|
|
92
|
+
for (const rm of rooms) {
|
|
93
|
+
const room = sk.rooms[rm.id], i = rm.i, n = room.loop.length, edges = rm.edges!;
|
|
94
|
+
const into = edges[(i - 1 + n) % n], tIn = into === l1 ? t1 : t2, tOut = into === l1 ? t2 : t1;
|
|
95
|
+
room.loop = [...room.loop.slice(0, i), tIn, tOut, ...room.loop.slice(i + 1)];
|
|
96
|
+
room.edges = [...edges.slice(0, i), edge, ...edges.slice(i)];
|
|
97
|
+
}
|
|
98
|
+
return { t1, t2, edge, sharp, cons };
|
|
99
|
+
}
|