@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/editor/project.ts
CHANGED
|
@@ -2,16 +2,21 @@ import { afterEdit, analyze } from '../app/edit';
|
|
|
2
2
|
import { ed } from '../app/state';
|
|
3
3
|
import { newPage } from '../model/demo';
|
|
4
4
|
import { pushHistory } from '../model/history';
|
|
5
|
-
import { deep } from '../model/ids';
|
|
5
|
+
import { deep, uid } from '../model/ids';
|
|
6
|
+
import { imageById } from '../model/images';
|
|
6
7
|
import { roomGeom } from '../model/rooms';
|
|
7
8
|
import { sessionState, onSessionReset } from '../model/session';
|
|
8
9
|
import { settings, type Layers } from '../model/settings';
|
|
9
10
|
import { roomValid } from '../model/sketch';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
11
|
+
import type { Underlay } from '../model/types';
|
|
12
|
+
import { allPages, page, planSK, saveSoon, store } from '../model/store';
|
|
13
|
+
import { closeElevation } from './elevation';
|
|
14
|
+
import { cvs, fitView, mainT, requestDraw, requestImageDraw, s2w, underlayH } from '../render/view';
|
|
12
15
|
import { cancelTool, setTool } from '../tools/registry';
|
|
16
|
+
import { fmtLen, followingHostUnits, hostUnits, setOwnUnits, type Unit } from '../units';
|
|
17
|
+
import { openingHeights } from '../model/elevation';
|
|
13
18
|
import { setMode } from './mode';
|
|
14
|
-
import { closeKeypad, dismissConflict, refreshDrawerSoon, toast, updateCtx, updateStatus, updateTopBar } from './ports';
|
|
19
|
+
import { closeKeypad, dismissConflict, openKeypad, refreshDrawerSoon, toast, updateCtx, updateStatus, updateTopBar } from './ports';
|
|
15
20
|
|
|
16
21
|
/*
|
|
17
22
|
The project panel's decisions, whatever draws it: pages (switch, add, duplicate, delete with a second tap to
|
|
@@ -25,6 +30,7 @@ onSessionReset(() => clearTimeout(pendingT));
|
|
|
25
30
|
/** Show another page: its drawing, view and selection. */
|
|
26
31
|
export function switchPage(id: string): void {
|
|
27
32
|
if (id === store.curPageId) return;
|
|
33
|
+
closeElevation(); // the wall view is of this page's walls
|
|
28
34
|
cancelTool(); closeKeypad(); dismissConflict();
|
|
29
35
|
store.curPageId = id;
|
|
30
36
|
ed.sel = []; ed.hover = null;
|
|
@@ -41,8 +47,8 @@ export function addPage(): void {
|
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
export function duplicatePage(): void {
|
|
44
|
-
const p = newPage(page().name + ' copy', deep(
|
|
45
|
-
p.
|
|
50
|
+
const p = newPage(page().name + ' copy', deep(planSK()));
|
|
51
|
+
p.underlays = deep(page().underlays);
|
|
46
52
|
store.project.pages.splice(store.project.pages.indexOf(page()) + 1, 0, p);
|
|
47
53
|
switchPage(p.id);
|
|
48
54
|
toast('Page duplicated');
|
|
@@ -52,7 +58,7 @@ export function duplicatePage(): void {
|
|
|
52
58
|
export const deletePending = (): boolean => pj.pendingDelete === store.curPageId;
|
|
53
59
|
|
|
54
60
|
export function deletePage(): void {
|
|
55
|
-
if (allPages().length <= 1) { toast('A project needs at least one page.'); return; }
|
|
61
|
+
if (allPages().length <= 1) { toast('A project needs at least one page.', { warn: true }); return; }
|
|
56
62
|
if (pj.pendingDelete !== store.curPageId) {
|
|
57
63
|
pj.pendingDelete = store.curPageId;
|
|
58
64
|
refreshDrawerSoon();
|
|
@@ -79,7 +85,8 @@ export const LAYER_ROWS: [LayerKey, string, string][] = [
|
|
|
79
85
|
['dims', 'Driving dimensions', 'Blue chips that set sizes'],
|
|
80
86
|
['print', 'Print dimensions', 'Labels for the crew; never move geometry'],
|
|
81
87
|
['rooms', 'Spaces', 'Fill, name and area'],
|
|
82
|
-
['
|
|
88
|
+
['cabinets', 'Cabinets', 'Their footprints, from each wall’s view'],
|
|
89
|
+
['annot', 'Markup', 'Pen, arrows, shapes, clouds and notes'],
|
|
83
90
|
['showCons', 'Constraint icons', 'H, V, ∥, ⊥ badges'],
|
|
84
91
|
];
|
|
85
92
|
|
|
@@ -91,36 +98,79 @@ export function toggleLayer(k: LayerKey): void {
|
|
|
91
98
|
requestDraw(); updateCtx(); refreshDrawerSoon(); saveSoon();
|
|
92
99
|
}
|
|
93
100
|
|
|
94
|
-
// ---- the page's
|
|
101
|
+
// ---- the page's photos and plans ----
|
|
95
102
|
|
|
96
|
-
|
|
97
|
-
|
|
103
|
+
/** The image being worked on: the one selected (or, if there's just one on the page, that one). */
|
|
104
|
+
export function targetImage(id?: string): Underlay | undefined {
|
|
105
|
+
const ims = page().underlays;
|
|
106
|
+
if (id) return imageById(id);
|
|
107
|
+
const sel = ed.sel.find(x => x.type === 'underlay');
|
|
108
|
+
return sel ? imageById(sel.id) : ims.length === 1 ? ims[0] : undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The steps an image's opacity takes (percent). */
|
|
112
|
+
export const OPACITY_MIN = 10, OPACITY_STEP = 5;
|
|
113
|
+
|
|
114
|
+
export function setUnderlayOpacity(id: string, v: number): void {
|
|
115
|
+
const u = imageById(id);
|
|
98
116
|
if (!u) return;
|
|
99
117
|
u.opacity = v;
|
|
100
|
-
|
|
118
|
+
requestImageDraw(); saveSoon();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** How far an image turns (degrees, clockwise as seen, either way from upright), in steps of half a degree. */
|
|
122
|
+
export const ROTATE_MAX = 180, ROTATE_STEP = 0.5;
|
|
123
|
+
|
|
124
|
+
/** Turn an image about its middle: degrees clockwise as it's seen (0: upright). */
|
|
125
|
+
export function setUnderlayRotation(id: string, deg: number): void {
|
|
126
|
+
const u = imageById(id);
|
|
127
|
+
if (!u) return;
|
|
128
|
+
// (-180, 180]: a whole turn either way is the same as none
|
|
129
|
+
const r = ((((deg + 180) % 360) + 360) % 360) - 180 || 0;
|
|
130
|
+
if (Math.abs(r) < 1e-9) delete u.rot; else u.rot = r === -180 ? 180 : r;
|
|
131
|
+
requestImageDraw(); saveSoon();
|
|
101
132
|
}
|
|
102
133
|
|
|
103
|
-
export function toggleUnderlayLock(): void {
|
|
104
|
-
const u =
|
|
134
|
+
export function toggleUnderlayLock(id: string): void {
|
|
135
|
+
const u = imageById(id);
|
|
105
136
|
if (!u) return;
|
|
106
137
|
pushHistory();
|
|
107
138
|
u.locked = !u.locked;
|
|
108
139
|
afterEdit();
|
|
109
140
|
}
|
|
110
141
|
|
|
111
|
-
/** Calibrate
|
|
112
|
-
export function calibrateUnderlay(): void {
|
|
142
|
+
/** Calibrate an image: the Set scale tool (two points you know the distance between), that image selected. */
|
|
143
|
+
export function calibrateUnderlay(id?: string): void {
|
|
144
|
+
const u = targetImage(id);
|
|
145
|
+
setMode('annotate');
|
|
146
|
+
if (u) ed.sel = [{ type: 'underlay', id: u.id }];
|
|
147
|
+
setTool('calib');
|
|
148
|
+
}
|
|
113
149
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
150
|
+
/** Take an image off the page (one undo step). */
|
|
151
|
+
export function removeUnderlay(id: string): void {
|
|
152
|
+
if (!imageById(id)) return;
|
|
153
|
+
pushHistory();
|
|
154
|
+
page().underlays = page().underlays.filter(u => u.id !== id);
|
|
155
|
+
ed.sel = ed.sel.filter(x => !(x.type === 'underlay' && x.id === id));
|
|
156
|
+
afterEdit(); refreshDrawerSoon();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Bring an image in front of the others, or send it behind them (one undo step). */
|
|
160
|
+
export function restackImage(id: string, to: 'front' | 'back'): void {
|
|
161
|
+
const ims = page().underlays, u = imageById(id);
|
|
162
|
+
if (!u || ims.length < 2) return;
|
|
163
|
+
pushHistory();
|
|
164
|
+
const rest = ims.filter(x => x !== u);
|
|
165
|
+
page().underlays = to === 'front' ? [...rest, u] : [u, ...rest];
|
|
166
|
+
afterEdit();
|
|
117
167
|
}
|
|
118
168
|
|
|
119
169
|
// ---- spaces ----
|
|
120
170
|
|
|
121
171
|
/** The page's spaces with their floor area and perimeter (to inside wall faces), and the total area. */
|
|
122
172
|
export function spaceSchedule(): { rows: { id: string; name: string; area: number; per: number }[]; total: number } {
|
|
123
|
-
const sk =
|
|
173
|
+
const sk = planSK(), rows = Object.entries(sk.rooms).filter(([, r]) => roomValid(sk, r)).map(([id, r]) => {
|
|
124
174
|
const g = roomGeom(sk, r);
|
|
125
175
|
return { id, name: r.name, area: g.area, per: g.per };
|
|
126
176
|
});
|
|
@@ -129,7 +179,7 @@ export function spaceSchedule(): { rows: { id: string; name: string; area: numbe
|
|
|
129
179
|
|
|
130
180
|
export function renameSpace(id: string, name: string): void {
|
|
131
181
|
pushHistory();
|
|
132
|
-
|
|
182
|
+
planSK().rooms[id].name = name.trim() || 'Space';
|
|
133
183
|
afterEdit();
|
|
134
184
|
}
|
|
135
185
|
|
|
@@ -144,7 +194,79 @@ export function setPreference<K extends 'unit' | 'denom' | 'wallH' | 'theme'>(k:
|
|
|
144
194
|
afterEdit();
|
|
145
195
|
}
|
|
146
196
|
|
|
147
|
-
|
|
197
|
+
/** The heights the Wall height setting offers in one tap (8', 9', 10'); anything else is Custom. */
|
|
198
|
+
export const WALL_PRESETS = [96, 108, 120];
|
|
199
|
+
/** The lowest and highest default wall height accepted. */
|
|
200
|
+
const WALL_MIN = 72, WALL_MAX = 360;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Settings → Wall height: the height of every wall that hasn't one of its own. Refused (and says why) below a door's
|
|
204
|
+
* or window's head on such a wall, or outside 6'–30'. Returns whether it was set.
|
|
205
|
+
*/
|
|
206
|
+
export function setDefaultWallHeight(v: number): boolean {
|
|
207
|
+
if (!(v >= WALL_MIN && v <= WALL_MAX)) { toast('Enter a wall height between ' + fmtLen(WALL_MIN) + ' and ' + fmtLen(WALL_MAX) + '.', { warn: true }); return false; }
|
|
208
|
+
const sk = planSK(), own = (lid: string) => sk.lines[lid]?.height != null;
|
|
209
|
+
const tallest = Math.max(0, ...Object.values(sk.openings).filter(o => !own(o.line)).map(o => openingHeights(o).head));
|
|
210
|
+
if (v < tallest - 1e-6) { toast('A door or window reaches ' + fmtLen(tallest) + '. Enter ' + fmtLen(tallest) + ' or more, or lower it first.', { warn: true }); return false; }
|
|
211
|
+
setPreference('wallH', v);
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Settings → Wall height → Custom: any height, on the keypad. */
|
|
216
|
+
export function editDefaultWallHeight(): void {
|
|
217
|
+
openKeypad({
|
|
218
|
+
title: 'Wall height', sub: 'Walls without their own height', initial: fmtLen(settings.wallH), selectAll: true, laser: true,
|
|
219
|
+
onDone: v => { setDefaultWallHeight(v); },
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export type UnitChoice = 'account' | Unit;
|
|
224
|
+
const UNITS_KEY = 'boldr-cad-units';
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Settings → Units: the editor's own units (ft-in, in, mm), or 'account' — the host's (BOLDR: the person's account
|
|
228
|
+
* settings). Remembered in this browser, so it sticks from drawing to drawing.
|
|
229
|
+
*/
|
|
230
|
+
export function setUnits(choice: UnitChoice): void {
|
|
231
|
+
const host = hostUnits();
|
|
232
|
+
if (choice === 'account') {
|
|
233
|
+
if (host) { settings.unit = host.unit; settings.denom = host.denom; }
|
|
234
|
+
setOwnUnits(false);
|
|
235
|
+
} else {
|
|
236
|
+
settings.unit = choice;
|
|
237
|
+
setOwnUnits(!!host);
|
|
238
|
+
}
|
|
239
|
+
rememberUnits();
|
|
240
|
+
afterEdit();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Settings → Precision (the editor's own inch units). */
|
|
244
|
+
export function setPrecision(denom: number): void {
|
|
245
|
+
settings.denom = denom;
|
|
246
|
+
rememberUnits();
|
|
247
|
+
afterEdit();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function rememberUnits(): void {
|
|
251
|
+
if (!hostUnits()) return; // without a host the units are saved with the project
|
|
252
|
+
try {
|
|
253
|
+
localStorage.setItem(UNITS_KEY, JSON.stringify(followingHostUnits() ? 'account' : { unit: settings.unit, denom: settings.denom }));
|
|
254
|
+
} catch { /* no storage here: just this session */ }
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** Put back the units the person picked last time (after the host's units are set). */
|
|
258
|
+
export function restoreUnits(): void {
|
|
259
|
+
if (!hostUnits()) return;
|
|
260
|
+
try {
|
|
261
|
+
const v = JSON.parse(localStorage.getItem(UNITS_KEY) || 'null') as 'account' | { unit?: string; denom?: number } | null;
|
|
262
|
+
if (!v || v === 'account' || !['ftin', 'in', 'mm'].includes(v.unit ?? '')) return;
|
|
263
|
+
settings.unit = v.unit as Unit;
|
|
264
|
+
if (v.denom === 4 || v.denom === 8 || v.denom === 16) settings.denom = v.denom;
|
|
265
|
+
setOwnUnits(true);
|
|
266
|
+
} catch { /* nothing remembered */ }
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function togglePreference(k: 'snapGrid' | 'autoCons' | 'toolTips'): void {
|
|
148
270
|
settings[k] = !settings[k];
|
|
149
271
|
updateCtx(); saveSoon();
|
|
150
272
|
}
|
|
@@ -157,12 +279,19 @@ export function togglePreference(k: 'snapGrid' | 'autoCons'): void {
|
|
|
157
279
|
*/
|
|
158
280
|
export function placeUnderlay(src: string, w: number, h: number): void {
|
|
159
281
|
const T = mainT(), ww = (cvs.W / T.s) * 0.6, hh = (ww * h) / w;
|
|
160
|
-
|
|
161
|
-
|
|
282
|
+
let [cx, cy] = s2w(cvs.W / 2, cvs.H / 2);
|
|
283
|
+
// one already exactly there (the last one added): this one goes a little down and to the right, so both show
|
|
284
|
+
const step = 24 / T.s;
|
|
285
|
+
while (page().underlays.some(u => Math.abs(u.x + u.w / 2 - cx) < step / 2 && Math.abs(u.y - underlayH(u) / 2 - cy) < step / 2)) { cx += step; cy -= step; }
|
|
286
|
+
pushHistory();
|
|
287
|
+
const id = uid('img');
|
|
288
|
+
page().underlays.push({ id, src, x: cx - ww / 2, y: cy + hh / 2, w: ww, h: hh, opacity: 0.6, locked: false });
|
|
162
289
|
settings.layers.underlay = true;
|
|
163
290
|
setMode('annotate');
|
|
164
291
|
setTool('aselect');
|
|
165
|
-
ed.sel = [{ type: 'underlay', id
|
|
292
|
+
ed.sel = [{ type: 'underlay', id }];
|
|
166
293
|
afterEdit();
|
|
167
|
-
toast(
|
|
294
|
+
toast(page().underlays.length > 1
|
|
295
|
+
? 'Image added on top of the others. Drag the corners to size it, or Set scale with two points you know.'
|
|
296
|
+
: 'Image added. Drag the corners to size it, or use Set scale with two points you know. Lock it once it’s placed.');
|
|
168
297
|
}
|
package/src/editor/selection.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { imageById } from '../model/images';
|
|
2
|
+
import { linkName } from '../model/links';
|
|
1
3
|
import { ed } from '../app/state';
|
|
2
|
-
import {
|
|
4
|
+
import { offersJobMeasurements } from './links';
|
|
5
|
+
import { consLabel, lineDefined, measure } from '../model/constraints';
|
|
3
6
|
import { drivingDim } from '../model/openings';
|
|
4
7
|
import { roomGeom } from '../model/rooms';
|
|
5
8
|
import { lengthDimOf, lineLen } from '../model/sketch';
|
|
6
|
-
import {
|
|
9
|
+
import { edgeLabel, edgeOf } from '../model/planGeom';
|
|
10
|
+
import { faceLength } from '../model/wallfaces';
|
|
11
|
+
import { planSK, SK } from '../model/store';
|
|
7
12
|
import type { Sketch } from '../model/types';
|
|
8
13
|
import { fmtArea, fmtLen } from '../units';
|
|
9
14
|
|
|
@@ -14,21 +19,53 @@ export function selInfo() {
|
|
|
14
19
|
const circles = g('circle'), arcs = g('arc');
|
|
15
20
|
return {
|
|
16
21
|
points: g('point'), lines: g('line'), circles, arcs, cons: g('cons'), dims: g('dim'), rooms: g('room'),
|
|
17
|
-
openings: g('opening'), odims: g('odim'), notes: g('note'), image: g('underlay').length > 0, curves: [...circles, ...arcs], n: ed.sel.length,
|
|
22
|
+
openings: g('opening'), odims: g('odim'), oanchors: g('oanchor'), notes: g('note'), image: g('underlay').length > 0, curves: [...circles, ...arcs], n: ed.sel.length,
|
|
23
|
+
cabs: g('cab'), elevs: g('elev'),
|
|
24
|
+
/** wall edges picked on their own: '<line id>|<side>' (see edgeOf) */
|
|
25
|
+
wfaces: g('wface'),
|
|
18
26
|
};
|
|
19
27
|
}
|
|
20
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Two things the + measures between, at least one a wall's edge: a straight wall's edge with another one, a line or a
|
|
31
|
+
* point — or the two edges of one wall (its thickness), straight or curved.
|
|
32
|
+
*/
|
|
33
|
+
export function edgePair(s: ReturnType<typeof selInfo>): boolean {
|
|
34
|
+
if (s.n !== 2 || !s.wfaces.length) return false;
|
|
35
|
+
const ids = s.wfaces.map(w => edgeOf(w)[0]);
|
|
36
|
+
if (ids.length === 2 && ids[0] === ids[1]) return true;
|
|
37
|
+
return s.wfaces.length + s.lines.length + s.points.length === 2 && ids.every(id => !!SK().lines[id]);
|
|
38
|
+
}
|
|
39
|
+
|
|
21
40
|
/** The two lines meeting at a point, or null if it isn't a two-line corner (an end, or a junction of 3+). */
|
|
22
41
|
export function cornerLines(sk: Sketch, pid: string): [string, string] | null {
|
|
23
42
|
const ids = Object.keys(sk.lines).filter(id => sk.lines[id].a === pid || sk.lines[id].b === pid);
|
|
24
|
-
|
|
43
|
+
// (a cabinet's corner isn't one: the cabinet is one object)
|
|
44
|
+
return ids.length === 2 && !sk.lines[ids[0]].cab && !sk.lines[ids[1]].cab ? [ids[0], ids[1]] : null;
|
|
25
45
|
}
|
|
26
46
|
|
|
47
|
+
/** What a line's + is for: its length — or, where the host lists them, one of the job's measurements. */
|
|
48
|
+
const forLength = (): string => (offersJobMeasurements() ? 'for its length or a measurement of the job' : 'for its length');
|
|
49
|
+
|
|
27
50
|
export function selectionHint(): string {
|
|
28
51
|
const sk = SK(), s = selInfo();
|
|
52
|
+
if (s.n === 1 && s.cabs.length) return '<b>Cabinet</b> · drag it, or tap <b>+</b> for its width; its height, depth and height off the floor are below';
|
|
53
|
+
if (s.n === 1 && s.elevs.length) return '<b>Dimension</b> · tap <b>+</b> to set it';
|
|
54
|
+
if (s.n === 1 && s.lines.length && sk.lines[s.lines[0]]?.anchor) return '<b>Set by the plan</b> · pick a point or line too, to constrain or dimension to it';
|
|
29
55
|
if (s.n === 1 && s.lines.length) {
|
|
30
56
|
const l = sk.lines[s.lines[0]];
|
|
31
|
-
return '<b>' + (l.kind === 'wall' ? 'Wall' : l.constr ? 'Construction line' : 'Line') + ' ' + fmtLen(lineLen(sk, l)) + '</b> · ' + (lineDefined(ed.ana, l) ? 'fully defined' : lengthDimOf(sk, s.lines[0]) ? 'pick a constraint below or drag to adjust' : 'tap <b>+</b> below
|
|
57
|
+
return '<b>' + (l.kind === 'wall' ? 'Wall' : l.constr ? 'Construction line' : 'Line') + ' ' + fmtLen(lineLen(sk, l)) + '</b> · ' + (lineDefined(ed.ana, l) ? 'fully defined' : lengthDimOf(sk, s.lines[0]) ? 'pick a constraint below or drag to adjust' : 'tap <b>+</b> below ' + forLength() + ', or pick a constraint');
|
|
58
|
+
}
|
|
59
|
+
if (s.n === 1 && s.wfaces.length) {
|
|
60
|
+
const [id, side] = edgeOf(s.wfaces[0]);
|
|
61
|
+
// (a curved wall's: its diameter across it)
|
|
62
|
+
if (!sk.lines[id]) return '<b>' + edgeLabel(sk, id, side) + ' Ø ' + fmtLen(measure(sk, { type: 'diam', c: id, face: side })) + '</b> · tap <b>+</b> for its diameter';
|
|
63
|
+
return '<b>' + edgeLabel(sk, id, side) + ' ' + fmtLen(faceLength(sk, id, side)) + '</b> · tap <b>+</b> ' + forLength() + ', or pick another edge or a point to measure from it';
|
|
64
|
+
}
|
|
65
|
+
if (edgePair(s)) {
|
|
66
|
+
const one = s.wfaces.length === 2 && edgeOf(s.wfaces[0])[0] === edgeOf(s.wfaces[1])[0];
|
|
67
|
+
if (one) return '<b>Both edges of a wall</b> · tap <b>+</b> for its thickness';
|
|
68
|
+
return '<b>' + (s.wfaces.length === 2 ? 'Two edges' : 'An edge and ' + (s.points.length ? 'a point' : 'a line')) + '</b> · tap <b>+</b> for the distance between them';
|
|
32
69
|
}
|
|
33
70
|
if (s.n === 1 && s.points.length) {
|
|
34
71
|
const name = cornerLines(sk, s.points[0]) ? 'Corner' : 'Point';
|
|
@@ -41,19 +78,28 @@ export function selectionHint(): string {
|
|
|
41
78
|
return '<b>' + r.name + '</b> · ' + fmtArea(g.area) + ' · ' + fmtLen(g.per) + ' perimeter';
|
|
42
79
|
}
|
|
43
80
|
if (s.n === 1 && s.openings.length) {
|
|
44
|
-
const o =
|
|
81
|
+
const o = planSK().openings[s.openings[0]];
|
|
82
|
+
if (!o) return '';
|
|
45
83
|
return '<b>' + (o.type === 'door' ? 'Door' : 'Window') + ' ' + fmtLen(o.w) + '</b> · ' + (drivingDim(o)
|
|
46
84
|
? 'locked by a dimension — tap it to change'
|
|
47
85
|
: 'drag to slide it along the wall, or tap a distance to lock it');
|
|
48
86
|
}
|
|
49
|
-
if (s.n === 1 && s.dims.length) {
|
|
87
|
+
if (s.n === 1 && s.dims.length) {
|
|
88
|
+
const c = sk.cons[s.dims[0]];
|
|
89
|
+
if (c.layer === 'print') return '<b>Print dimension</b> · drag to move the label';
|
|
90
|
+
if (c.measurementRef) return '<b>' + consLabel(c) + '</b> · linked to <b>' + linkName(c.measurementRef) + '</b> · tap <b>+</b> to change it';
|
|
91
|
+
return '<b>' + consLabel(c) + '</b> · tap <b>+</b> to change it';
|
|
92
|
+
}
|
|
93
|
+
if (s.n === 1 && s.oanchors.length) return '<b>' + (planSK().openings[s.oanchors[0].split('|')[0]]?.type === 'door' ? 'Door' : 'Window') + ' edge</b> · pick a point, or another door or window, to measure from it';
|
|
50
94
|
if (s.n === 1 && s.odims.length) {
|
|
51
95
|
const part = s.odims[0].split('|')[1];
|
|
96
|
+
if (part === 'w') return '<b>' + (planSK().openings[s.odims[0].split('|')[0]]?.type === 'door' ? 'Door' : 'Window') + ' width</b> · tap <b>+</b> to change it';
|
|
52
97
|
return part.startsWith('d:') ? '<b>Door/window dimension</b> · tap <b>+</b> to change it' : '<b>Distance to the wall end</b> · tap <b>+</b> to lock it';
|
|
53
98
|
}
|
|
54
|
-
if (s.n === 1 && s.image) return
|
|
99
|
+
if (s.n === 1 && s.image) return imageById(ed.sel[0].id)?.locked
|
|
55
100
|
? '<b>Image</b> · locked — tap Unlock to move or resize it'
|
|
56
101
|
: '<b>Image</b> · drag to move · corners scale, side handles stretch';
|
|
102
|
+
if (s.n === 1 && s.notes.length && SK().notes.find(n => n.id === s.notes[0])?.type === 'link') return '<b>Link</b> · drag to move · open it, or point it at the drawing with a leader, below · Size sizes every tile on the page';
|
|
57
103
|
if (s.notes.length) return 'Drag to move. Turn a pen stroke into lines below.';
|
|
58
104
|
return '<b>' + s.n + ' selected</b> · choose a constraint below';
|
|
59
105
|
}
|
package/src/editor/status.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { ed } from '../app/state';
|
|
|
2
2
|
import { SK } from '../model/store';
|
|
3
3
|
import { tools } from '../tools/registry';
|
|
4
4
|
import { selectionHint } from './selection';
|
|
5
|
+
import { hint3d, is3d } from './view3dState';
|
|
5
6
|
|
|
6
7
|
/** The definition-state pill: how many free movements are left, fully defined, empty, or a conflict. */
|
|
7
8
|
export function statusPill(): { text: string; state: 'open' | 'full' | 'bad' } {
|
|
@@ -14,6 +15,7 @@ export function statusPill(): { text: string; state: 'open' | 'full' | 'bad' } {
|
|
|
14
15
|
|
|
15
16
|
/** What to do next with the current tool or selection (bold parts in <b>). */
|
|
16
17
|
export function currentHint(): string {
|
|
18
|
+
if (is3d()) return hint3d();
|
|
17
19
|
const t = tools[ed.tool];
|
|
18
20
|
let h = t && t.hint ? t.hint() : '';
|
|
19
21
|
if ((ed.tool === 'select' || ed.tool === 'aselect') && ed.sel.length) h = ed.mode === 'draft' ? selectionHint() : t.hint!();
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ed } from '../app/state';
|
|
2
|
+
import { settings } from '../model/settings';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
What a tool does, said in a line when it's picked from the rail (a quick note under the Annotate / Draft tabs that
|
|
6
|
+
goes by itself — not the step-by-step hint, which follows what you're doing).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const TIPS: Record<string, string> = {
|
|
10
|
+
// Draft
|
|
11
|
+
select: 'Tap to pick, drag to move. Drag across space to pick several.',
|
|
12
|
+
line: 'Tap each point. Tap the last again to finish.',
|
|
13
|
+
freehand: 'Sketch it; it straightens when you lift.',
|
|
14
|
+
rect: 'Tap or drag corner to corner.',
|
|
15
|
+
circle: 'Tap the center, then the edge.',
|
|
16
|
+
arc: 'Tap both ends, then drag to bend.',
|
|
17
|
+
door: 'Tap a wall to add a 3\'-0" door.',
|
|
18
|
+
window: 'Tap a wall to add a 3\'-0" window.',
|
|
19
|
+
room: 'Tap inside a closed shape.',
|
|
20
|
+
dim: 'Tap an element to measure.',
|
|
21
|
+
// the wall view
|
|
22
|
+
wdoor: 'Tap the wall to add a door.',
|
|
23
|
+
wwindow: 'Tap the wall to add a window.',
|
|
24
|
+
cabinet: 'Tap the wall to add a cabinet.',
|
|
25
|
+
// Annotate
|
|
26
|
+
aselect: 'Tap to pick, drag to move.',
|
|
27
|
+
arrow: 'Drag toward what it points at.',
|
|
28
|
+
mline: 'Drag end to end.',
|
|
29
|
+
mrect: 'Drag corner to corner.',
|
|
30
|
+
mtri: 'Drag to size it.',
|
|
31
|
+
mcircle: 'Drag to size it.',
|
|
32
|
+
pen: 'Draw freehand.',
|
|
33
|
+
cloud: 'Drag around the area to flag.',
|
|
34
|
+
text: 'Tap where it goes, then type.',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The line for a rail item just picked (its name in bold), or null if it has none (Undo, a colour, an action) or the
|
|
39
|
+
* notes are turned off (••• → Tool tips).
|
|
40
|
+
*/
|
|
41
|
+
export function toolTip(id: string, label: string): string | null {
|
|
42
|
+
if (settings.toolTips === false) return null;
|
|
43
|
+
if (id === 'constr') return ed.drawConstr ? '<b>Construction lines on</b> · new lines are dashed guides.' : '<b>Construction lines off</b> · new lines are solid.';
|
|
44
|
+
if (id === 'wall') return ed.drawWalls ? '<b>Walls on</b> · what you draw is walls.' : '<b>Walls off</b> · what you draw is lines.';
|
|
45
|
+
const t = TIPS[id];
|
|
46
|
+
return t ? '<b>' + label + '</b> · ' + t : null;
|
|
47
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { ed } from '../app/state';
|
|
2
|
+
import { openingHeights, wallHeight } from '../model/elevation';
|
|
3
|
+
import { faceKeyParts } from '../model/faces';
|
|
4
|
+
import { hyp } from '../model/geom';
|
|
5
|
+
import { curveFrame } from '../model/planGeom';
|
|
6
|
+
import { roomGeom } from '../model/rooms';
|
|
7
|
+
import { settings } from '../model/settings';
|
|
8
|
+
import { planSK, store } from '../model/store';
|
|
9
|
+
import { requestDraw } from '../render/view';
|
|
10
|
+
import { editWallHeight } from '../tools/elevation';
|
|
11
|
+
import { cancelTool } from '../tools/registry';
|
|
12
|
+
import { fmtArea, fmtLen } from '../units';
|
|
13
|
+
import type { CtxEntry } from './actions';
|
|
14
|
+
import { closeElevation, elevOpen, openElevation } from './elevation';
|
|
15
|
+
import type { KeyIn } from './keys';
|
|
16
|
+
import { setMode } from './mode';
|
|
17
|
+
import { dismissConflict, dismissPopovers, refreshModes, updateCtx, updateHint, updateRail } from './ports';
|
|
18
|
+
import { command3d, pick3d, v3 } from './view3dState';
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
The 3D tab: opening and leaving it, its rail's views, what the bar shows for something tapped in it (its sizes, the
|
|
22
|
+
wall's height to change, its wall view to open), and its keys. The plan stays as it was underneath: the 3D view is
|
|
23
|
+
made from it, and follows each change to it.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* What the 3D view shows, summed up as one string (it's drawn again when this changes): the page and its edits, the
|
|
28
|
+
* default wall height, the cutaway, a camera move asked for, and what's tapped.
|
|
29
|
+
*/
|
|
30
|
+
export const view3dKey = (): string =>
|
|
31
|
+
[store.curPageId, ed.editSeq, settings.wallH, v3.cutaway, v3.cmd.seq, v3.pick ? v3.pick.kind + ':' + v3.pick.id : ''].join('|');
|
|
32
|
+
|
|
33
|
+
/** Show the 3D tab: whatever tool was in hand is put down, and nothing is selected. */
|
|
34
|
+
export function open3d(): void {
|
|
35
|
+
if (v3.open) return;
|
|
36
|
+
cancelTool();
|
|
37
|
+
dismissPopovers();
|
|
38
|
+
if (elevOpen()) closeElevation();
|
|
39
|
+
ed.sel = []; ed.hover = null; ed.preview = null; ed.boxSel = null;
|
|
40
|
+
v3.open = true;
|
|
41
|
+
v3.pick = null;
|
|
42
|
+
refreshModes(); updateCtx(); updateHint(); updateRail();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Back to the plan (the tab picked next decides Draft or Annotate). */
|
|
46
|
+
export function close3d(): void {
|
|
47
|
+
if (!v3.open) return;
|
|
48
|
+
v3.open = false;
|
|
49
|
+
v3.pick = null;
|
|
50
|
+
refreshModes(); updateCtx(); updateHint(); updateRail(); requestDraw();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** A cabinet's sizes from its wall face: across, up, and out from the wall. */
|
|
54
|
+
function cabinetSizes(pickId: string): { w: number; h: number; d: number } | null {
|
|
55
|
+
const [key, cid] = pickId.split('#'), face = planSK().faces?.[key], cb = face?.cabs?.[cid];
|
|
56
|
+
if (!face || !cb) return null;
|
|
57
|
+
const ps = [cb.bl, cb.br, cb.tr, cb.tl].map(p => face.points[p]);
|
|
58
|
+
if (ps.some(p => !p)) return null;
|
|
59
|
+
const xs = ps.map(p => p.x), ys = ps.map(p => p.y);
|
|
60
|
+
return { w: Math.max(...xs) - Math.min(...xs), h: Math.max(...ys) - Math.min(...ys), d: cb.depth };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The bar for what's tapped in the 3D view: what it is and its sizes, and what can be done with it from here. */
|
|
64
|
+
export function bar3d(): CtxEntry[] {
|
|
65
|
+
const p = v3.pick, sk = planSK();
|
|
66
|
+
if (!p) return [];
|
|
67
|
+
const wallView: CtxEntry = { ic: 'elev', label: 'Wall view', act: 'wall3d' };
|
|
68
|
+
switch (p.kind) {
|
|
69
|
+
case 'wall': {
|
|
70
|
+
const l = sk.lines[p.id], A = l && sk.points[l.a], B = l && sk.points[l.b];
|
|
71
|
+
if (!A || !B) return [];
|
|
72
|
+
return [{ lbl: 'Wall · ' + fmtLen(hyp(B.x - A.x, B.y - A.y)) + ' long · ' + fmtLen(wallHeight(l)) + ' high' }, { ic: 'v', label: 'Height', act: 'height3d' }, wallView];
|
|
73
|
+
}
|
|
74
|
+
case 'curve': {
|
|
75
|
+
const f = curveFrame(sk, p.id);
|
|
76
|
+
return f ? [{ lbl: 'Curved wall · ' + fmtLen(2 * f.r) + ' across · ' + fmtLen(settings.wallH) + ' high' }] : [];
|
|
77
|
+
}
|
|
78
|
+
case 'opening': {
|
|
79
|
+
const o = sk.openings[p.id];
|
|
80
|
+
if (!o) return [];
|
|
81
|
+
const { sill, head } = openingHeights(o), door = o.type === 'door';
|
|
82
|
+
return [{ lbl: (door ? 'Door · ' : 'Window · ') + fmtLen(o.w) + ' × ' + fmtLen(head - sill) + (door ? '' : ' · sill ' + fmtLen(sill)) }, wallView];
|
|
83
|
+
}
|
|
84
|
+
case 'cab': {
|
|
85
|
+
const c = cabinetSizes(p.id);
|
|
86
|
+
return c ? [{ lbl: 'Cabinet · ' + fmtLen(c.w) + ' × ' + fmtLen(c.h) + ' · ' + fmtLen(c.d) + ' deep' }, wallView] : [];
|
|
87
|
+
}
|
|
88
|
+
case 'floor': {
|
|
89
|
+
const r = sk.rooms[p.id];
|
|
90
|
+
return r ? [{ lbl: r.name + ' · ' + fmtArea(roomGeom(sk, r).area) }] : [];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** A press on the 3D bar. Whether it was one of its own. */
|
|
96
|
+
export function run3d(act: string): boolean {
|
|
97
|
+
const p = v3.pick, sk = planSK();
|
|
98
|
+
if (!p) return false;
|
|
99
|
+
if (act === 'height3d' && p.kind === 'wall') { editWallHeight(p.id); return true; }
|
|
100
|
+
if (act !== 'wall3d') return false;
|
|
101
|
+
// its wall straight on, from the side it was tapped on (a door's or window's: from its room)
|
|
102
|
+
const at = p.kind === 'wall' ? { lid: p.id, side: p.side } : p.kind === 'opening' ? { lid: sk.openings[p.id]?.line, side: undefined }
|
|
103
|
+
: p.kind === 'cab' ? faceKeyParts(p.id.split('#')[0]) : null;
|
|
104
|
+
if (!at?.lid || !sk.lines[at.lid]) return true;
|
|
105
|
+
close3d();
|
|
106
|
+
setMode('draft');
|
|
107
|
+
openElevation(at.lid, at.side);
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** A key while the 3D tab shows (after the keypad's, undo's and save's): Esc lets go of what's tapped, F frames all. */
|
|
112
|
+
export function key3d(e: KeyIn): boolean {
|
|
113
|
+
if (e.key === 'Escape') {
|
|
114
|
+
if (!dismissConflict() && !dismissPopovers() && v3.pick) pick3d(null);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
if (e.key.toLowerCase() === 'f') command3d('fit');
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { sessionState } from '../model/session';
|
|
2
|
+
import type { Orbit } from '../view3d/camera';
|
|
3
|
+
import type { PartKind } from '../view3d/mesh';
|
|
4
|
+
import { refreshModes, updateCtx, updateHint, updateRail } from './ports';
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
The 3D tab's state (beside Annotate and Draft): open or not, the cutaway, what's been tapped, and the camera, kept
|
|
8
|
+
while the tab is left so coming back finds the model as it was. Nothing here needs three.js: the viewer
|
|
9
|
+
(react/View3D*) reads it, and the bar and keys change it (editor/view3d.ts). The tab has no rail.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Something tapped in the 3D view. `side`: a straight wall's face that was tapped (+1 the left of its A → B). */
|
|
13
|
+
export interface Pick3 { kind: PartKind; id: string; side?: 1 | -1 }
|
|
14
|
+
|
|
15
|
+
/** Camera moves asked for from outside the view: frame everything, the corner view, or straight down. */
|
|
16
|
+
export type Command3 = 'fit' | 'reset' | 'top';
|
|
17
|
+
|
|
18
|
+
export const v3 = sessionState(() => ({
|
|
19
|
+
open: false,
|
|
20
|
+
/** the walls in front drop low, so the rooms can be seen into */
|
|
21
|
+
cutaway: false,
|
|
22
|
+
pick: null as Pick3 | null,
|
|
23
|
+
/** where the camera is, and the page it was for (another page starts from the corner view) */
|
|
24
|
+
orbit: null as Orbit | null,
|
|
25
|
+
orbitPage: '',
|
|
26
|
+
/**
|
|
27
|
+
* the view the camera was put at, while it hasn't been moved from it (null once it has): framed again when the
|
|
28
|
+
* screen changes shape, so a device turned on its side still shows everything
|
|
29
|
+
*/
|
|
30
|
+
framed: null as Command3 | null,
|
|
31
|
+
/** the last camera move asked for; the viewer follows each new seq */
|
|
32
|
+
cmd: { seq: 0, what: 'fit' as Command3 },
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
/** Whether the 3D tab is the one showing. */
|
|
36
|
+
export const is3d = (): boolean => v3.open;
|
|
37
|
+
|
|
38
|
+
/** The walls in front drop low, or stand to full height again. (No button for it: the 3D tab's rail is gone.) */
|
|
39
|
+
export function toggleCutaway(): void {
|
|
40
|
+
v3.cutaway = !v3.cutaway;
|
|
41
|
+
updateRail(); updateHint();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Move the camera: frame everything, the corner view, or straight down. */
|
|
45
|
+
export function command3d(what: Command3): void {
|
|
46
|
+
v3.cmd = { seq: v3.cmd.seq + 1, what };
|
|
47
|
+
refreshModes();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** What a tap in the 3D view landed on (null: nothing). Its sizes go in the bar. */
|
|
51
|
+
export function pick3d(p: Pick3 | null): void {
|
|
52
|
+
v3.pick = p;
|
|
53
|
+
updateCtx(); updateHint();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The hint for screen readers while the 3D tab shows. */
|
|
57
|
+
export function hint3d(): string {
|
|
58
|
+
return v3.pick
|
|
59
|
+
? 'Its sizes are in the bar. Tap something else, or empty space to let go.'
|
|
60
|
+
: 'Drag to turn the model. Pinch or scroll to zoom; drag with two fingers or the right mouse button to move it. Tap a wall, a door or a cabinet to see its sizes.';
|
|
61
|
+
}
|
package/src/export/dxf.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { emitDrawing, exportFileName, type DrawOptions, type Plan } from './plan
|
|
|
9
9
|
|
|
10
10
|
const LAYERS: [name: string, color: number][] = [
|
|
11
11
|
['WALLS', 7], ['LINES', 7], ['CONSTRUCTION', 8], ['DOORS', 4], ['WINDOWS', 4],
|
|
12
|
-
['SPACES', 3], ['DIMENSIONS', 5], ['MARKUP', 6],
|
|
12
|
+
['SPACES', 3], ['DIMENSIONS', 5], ['MARKUP', 6], ['CABINETS', 8], ['CABINETS_UPPER', 8], ['LINKS', 150],
|
|
13
13
|
];
|
|
14
14
|
|
|
15
15
|
class Dxf implements Plan {
|