@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
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { afterEdit, tryAddConstraint } from '../app/edit';
|
|
2
|
+
import { ed } from '../app/state';
|
|
3
|
+
import { measure } from '../model/constraints';
|
|
4
|
+
import { cabPoints, cornerAnchor, endEdge } from '../model/faces';
|
|
5
|
+
import { dropHistory, pushHistory } from '../model/history';
|
|
6
|
+
import { uid } from '../model/ids';
|
|
7
|
+
import { settings } from '../model/settings';
|
|
8
|
+
import { addLine, addPoint } from '../model/sketch';
|
|
9
|
+
import { planSK, SK } from '../model/store';
|
|
10
|
+
import { elevOpenings } from '../model/elevation';
|
|
11
|
+
import type { Cabinet, Constraint, WallFace } from '../model/types';
|
|
12
|
+
import { requestDraw } from '../render/view';
|
|
13
|
+
import { editDim } from '../tools/dim';
|
|
14
|
+
import { fmtLen } from '../units';
|
|
15
|
+
import { elev } from './elevation';
|
|
16
|
+
import { openKeypad, toast, updateCtx } from './ports';
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
Things the wall view adds to a wall face beyond what the plan's tools draw: cabinets, height lines, and the
|
|
20
|
+
dimensions behind the wall view's chips (along the floor, off the floor, a cabinet's size). The face is the sketch
|
|
21
|
+
being edited (SK()) while the wall view is open; x runs across the wall as seen, y up from the floor.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** A new cabinet: one standard size (24" wide, 34½" high, 24" deep) — change it once it's placed. */
|
|
25
|
+
export const CAB = { w: 24, h: 34.5, d: 24 };
|
|
26
|
+
|
|
27
|
+
/** The wall face being edited. */
|
|
28
|
+
export const curFace = (): WallFace => SK() as WallFace;
|
|
29
|
+
|
|
30
|
+
const q = () => 1 / settings.denom;
|
|
31
|
+
const round = (v: number) => Math.round(v / q()) * q();
|
|
32
|
+
|
|
33
|
+
/** The cabinets on the face with their extent. */
|
|
34
|
+
export function cabExtents(face: WallFace = curFace()): { id: string; u0: number; u1: number; z0: number; z1: number }[] {
|
|
35
|
+
return Object.entries(face.cabs ?? {}).map(([id, cb]) => {
|
|
36
|
+
const ps = cabPoints(cb).map(p => face.points[p]).filter(Boolean);
|
|
37
|
+
return { id, u0: Math.min(...ps.map(p => p.x)), u1: Math.max(...ps.map(p => p.x)), z0: Math.min(...ps.map(p => p.y)), z1: Math.max(...ps.map(p => p.y)) };
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Where a new cabinet goes for a tap at (u, z): centred on it, inside the wall, and pulled against a corner or the
|
|
43
|
+
* side of a cabinet within 8" (and onto the floor, the ceiling or a cabinet's top or bottom within 6"). Nothing holds
|
|
44
|
+
* it there: like a door or window, it's placed, and its dimensions lock it.
|
|
45
|
+
*/
|
|
46
|
+
export function cabinetSpot(u: number, z: number): { u0: number; z0: number } | null {
|
|
47
|
+
const f = elev.frame;
|
|
48
|
+
if (!f || f.face[1] - f.face[0] < CAB.w - 1e-6 || f.H < CAB.h - 1e-6) return null;
|
|
49
|
+
let u0 = Math.min(Math.max(round(u - CAB.w / 2), f.face[0]), f.face[1] - CAB.w);
|
|
50
|
+
let z0 = Math.min(Math.max(round(z - CAB.h / 2), 0), f.H - CAB.h);
|
|
51
|
+
const cabs = settings.layers.objSnap ? cabExtents() : [], ops = settings.layers.objSnap ? elevOpenings(planSK(), f) : [];
|
|
52
|
+
// its sides against the corners and other cabinets, or in line with a door's or window's sides
|
|
53
|
+
const sides = [f.face[0], ...cabs.map(c => c.u1), ...ops.flatMap(o => [o.u0, o.u1])], ends = [f.face[1], ...cabs.map(c => c.u0), ...ops.flatMap(o => [o.u0, o.u1])];
|
|
54
|
+
let best = settings.layers.objSnap ? 8 : 0, nu = u0;
|
|
55
|
+
for (const x of sides) if (Math.abs(u0 - x) < best) { best = Math.abs(u0 - x); nu = x; }
|
|
56
|
+
for (const x of ends) if (Math.abs(u0 + CAB.w - x) < best) { best = Math.abs(u0 + CAB.w - x); nu = x - CAB.w; }
|
|
57
|
+
u0 = nu;
|
|
58
|
+
// its bottom on the floor or another cabinet (or just over a window's head); its top under a cabinet, or a window's sill
|
|
59
|
+
const floors = [0, ...cabs.map(c => c.z1), ...ops.filter(o => o.type === 'window').map(o => o.head)];
|
|
60
|
+
const ceilings = [f.H, ...cabs.map(c => c.z0), ...ops.filter(o => o.type === 'window').map(o => o.sill)];
|
|
61
|
+
let nz = z0;
|
|
62
|
+
best = settings.layers.objSnap ? 6 : 0;
|
|
63
|
+
for (const y of floors) if (Math.abs(z0 - y) < best) { best = Math.abs(z0 - y); nz = y; }
|
|
64
|
+
for (const y of ceilings) if (Math.abs(z0 + CAB.h - y) < best) { best = Math.abs(z0 + CAB.h - y); nz = y - CAB.h; }
|
|
65
|
+
return { u0, z0: nz };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Snap a cabinet being dragged (by du, dz from where its box started): its sides to the wall's corners, other cabinets'
|
|
70
|
+
* sides and doors' and windows' edges, its bottom and top to the floor, the ceiling, other cabinets' tops and bottoms
|
|
71
|
+
* and windows' sills and heads — whichever is nearest within `tol` inches, across and up separately.
|
|
72
|
+
*/
|
|
73
|
+
export function snapCabinetMove(id: string, box: { u0: number; u1: number; z0: number; z1: number }, du: number, dz: number, tol: number): [number, number] {
|
|
74
|
+
return snapObjectMove({ cab: id }, box, du, dz, tol);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Snap anything being dragged on the wall (a cabinet, a door or window — `self`, left out of the targets): its sides to
|
|
79
|
+
* the wall's corners and the other objects' sides, its bottom and top to the floor, the ceiling and the other objects'
|
|
80
|
+
* tops and bottoms (sills and heads), whichever is nearest within `tol` inches, across and up separately. Off when
|
|
81
|
+
* Object snapping is (Layers).
|
|
82
|
+
*/
|
|
83
|
+
export function snapObjectMove(self: { cab?: string; op?: string }, box: { u0: number; u1: number; z0: number; z1: number }, du: number, dz: number, tol: number): [number, number] {
|
|
84
|
+
const f = elev.frame;
|
|
85
|
+
if (!f || !settings.layers.objSnap) return [du, dz];
|
|
86
|
+
const others = cabExtents().filter(c => c.id !== self.cab), ops = elevOpenings(planSK(), f).filter(o => o.id !== self.op);
|
|
87
|
+
const xs = [f.face[0], f.face[1], ...others.flatMap(c => [c.u0, c.u1]), ...ops.flatMap(o => [o.u0, o.u1])];
|
|
88
|
+
const zs = [0, f.H, ...others.flatMap(c => [c.z0, c.z1]), ...ops.flatMap(o => [o.sill, o.head])];
|
|
89
|
+
const pull = (edges: number[], targets: number[]) => {
|
|
90
|
+
let best: number | null = null;
|
|
91
|
+
for (const e of edges) for (const t of targets) if (Math.abs(t - e) < tol && (best == null || Math.abs(t - e) < Math.abs(best))) best = t - e;
|
|
92
|
+
return best ?? 0;
|
|
93
|
+
};
|
|
94
|
+
return [du + pull([box.u0 + du, box.u1 + du], xs), dz + pull([box.z0 + dz, box.z1 + dz], zs)];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Place a cabinet (the standard size) for a tap at (u, z), and select it. Returns its id. */
|
|
98
|
+
export function addCabinet(u: number, z: number): string | null {
|
|
99
|
+
const spot = cabinetSpot(u, z);
|
|
100
|
+
if (!spot) { toast('That wall is too small for a ' + fmtLen(CAB.w) + ' × ' + fmtLen(CAB.h) + ' cabinet.', { warn: true }); return null; }
|
|
101
|
+
pushHistory();
|
|
102
|
+
const face = curFace(), id = uid('cab'), { u0, z0 } = spot;
|
|
103
|
+
const cb: Cabinet = {
|
|
104
|
+
bl: addPoint(face, u0, z0), br: addPoint(face, u0 + CAB.w, z0), tr: addPoint(face, u0 + CAB.w, z0 + CAB.h), tl: addPoint(face, u0, z0 + CAB.h), depth: CAB.d,
|
|
105
|
+
};
|
|
106
|
+
face.cabs = face.cabs ?? {};
|
|
107
|
+
face.cabs[id] = cb;
|
|
108
|
+
for (const [a, b] of [[cb.bl, cb.br], [cb.br, cb.tr], [cb.tr, cb.tl], [cb.tl, cb.bl]]) face.lines[addLine(face, a, b)].cab = id;
|
|
109
|
+
// square, and its standard size (not a measurement until it's entered)
|
|
110
|
+
const shape: Constraint[] = [
|
|
111
|
+
{ type: 'h', a: cb.bl, b: cb.br, elev: true }, { type: 'h', a: cb.tl, b: cb.tr, elev: true },
|
|
112
|
+
{ type: 'v', a: cb.bl, b: cb.tl, elev: true }, { type: 'v', a: cb.br, b: cb.tr, elev: true },
|
|
113
|
+
];
|
|
114
|
+
for (const c of shape) tryAddConstraint(c, { silent: true });
|
|
115
|
+
const w = tryAddConstraint({ type: 'dist', kind: 'h', a: cb.bl, b: cb.br, value: CAB.w, driving: true, sign: 1, auto: true, elev: true }, { silent: true });
|
|
116
|
+
const h = tryAddConstraint({ type: 'dist', kind: 'v', a: cb.br, b: cb.tr, value: CAB.h, driving: true, sign: 1, auto: true, elev: true }, { silent: true });
|
|
117
|
+
if (w.ok && w.id) cb.w = w.id;
|
|
118
|
+
if (h.ok && h.id) cb.h = h.id;
|
|
119
|
+
// (placed, not selected: tap it to size it)
|
|
120
|
+
ed.sel = [];
|
|
121
|
+
afterEdit();
|
|
122
|
+
return id;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** A level line all the way across the wall, `z` above the floor (a counter, a chair rail), held there by a height dimension. */
|
|
126
|
+
export function addHeightLine(z: number): string | null {
|
|
127
|
+
const f = elev.frame;
|
|
128
|
+
if (!f) return null;
|
|
129
|
+
if (z <= 0 || z >= f.H) { toast('Enter a height between the floor and the ceiling (' + fmtLen(f.H) + ').', { warn: true }); return null; }
|
|
130
|
+
pushHistory();
|
|
131
|
+
const face = curFace(), right = f.leftEnd === 'a' ? 'b' : 'a';
|
|
132
|
+
const pa = addPoint(face, f.face[0], z), pb = addPoint(face, f.face[1], z), id = addLine(face, pa, pb);
|
|
133
|
+
for (const c of [
|
|
134
|
+
{ type: 'online', p: pa, l: endEdge(f.leftEnd) }, { type: 'online', p: pb, l: endEdge(right) }, { type: 'h', a: pa, b: pb },
|
|
135
|
+
{ type: 'dist', kind: 'v', a: cornerAnchor(f.leftEnd), b: pa, value: z, driving: true, sign: 1 },
|
|
136
|
+
] as Constraint[]) tryAddConstraint(c, { silent: true });
|
|
137
|
+
ed.sel = [];
|
|
138
|
+
afterEdit();
|
|
139
|
+
return id;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** The face dimension between two points across ('h') or up ('v') the wall, if there is one. */
|
|
143
|
+
export function faceDimBetween(face: WallFace, kind: 'h' | 'v', a: string, b: string): string | null {
|
|
144
|
+
for (const [id, c] of Object.entries(face.cons)) {
|
|
145
|
+
if (c.type === 'dist' && c.kind === kind && ((c.a === a && c.b === b) || (c.a === b && c.b === a))) return id;
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Dimension from `a` to `b`, across or up the wall: the keypad, then it's set — a driving dimension the wall view
|
|
152
|
+
* draws (along the floor or beside what it measures). An existing one between them is changed instead.
|
|
153
|
+
*/
|
|
154
|
+
export function dimensionFace(kind: 'h' | 'v', a: string, b: string, title: string): void {
|
|
155
|
+
const face = curFace(), had = faceDimBetween(face, kind, a, b);
|
|
156
|
+
if (had) { editDim(had); return; }
|
|
157
|
+
if (!face.points[a] || !face.points[b]) return;
|
|
158
|
+
const cur = measure(face, { type: 'dist', kind, a, b });
|
|
159
|
+
openKeypad({
|
|
160
|
+
title, initial: fmtLen(cur), selectAll: true, laser: true,
|
|
161
|
+
onDone: v => {
|
|
162
|
+
if (v < 0) return;
|
|
163
|
+
const face = curFace(), pa = face.points[a], pb = face.points[b];
|
|
164
|
+
if (!pa || !pb) return;
|
|
165
|
+
pushHistory();
|
|
166
|
+
const sign = (kind === 'h' ? pb.x - pa.x : pb.y - pa.y) < -1e-9 ? -1 : 1;
|
|
167
|
+
const r = tryAddConstraint({ type: 'dist', kind, a, b, value: v, driving: true, sign, elev: true }, { onResolved: () => afterEdit() });
|
|
168
|
+
if (!r.ok) dropHistory();
|
|
169
|
+
// a grey chip that was selected is this dimension now (a selected cabinet stays selected)
|
|
170
|
+
else if (r.id && ed.sel.length === 1 && ed.sel[0].type === 'elev') ed.sel = [{ type: 'dim', id: r.id }];
|
|
171
|
+
afterEdit();
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** The floor point a cabinet's height above the floor is measured from. */
|
|
177
|
+
const floorAnchor = (): string => cornerAnchor(elev.frame?.leftEnd ?? 'a');
|
|
178
|
+
|
|
179
|
+
/** A cabinet's dimension that sets its width, height, or height off the floor (null if there's none). */
|
|
180
|
+
export function cabDim(face: WallFace, cabId: string, part: 'w' | 'h' | 'z'): string | null {
|
|
181
|
+
const cb = face.cabs?.[cabId];
|
|
182
|
+
if (!cb) return null;
|
|
183
|
+
if (part !== 'z') return cb[part] && face.cons[cb[part]!] ? cb[part]! : null;
|
|
184
|
+
for (const [id, c] of Object.entries(face.cons)) {
|
|
185
|
+
const ends = [c.a, c.b];
|
|
186
|
+
if (c.type === 'dist' && c.kind === 'v' && ends.some(v => !!v && !!face.points[v]?.anchor && face.points[v].y === 0) && ends.some(v => v === cb.bl || v === cb.br)) return id;
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** The keypad for a cabinet's width, height or height off the floor (its standard size until it's entered). */
|
|
192
|
+
export function editCabinet(cabId: string, part: 'w' | 'h' | 'z'): void {
|
|
193
|
+
const face = curFace(), cb = face.cabs?.[cabId];
|
|
194
|
+
if (!cb) return;
|
|
195
|
+
const had = cabDim(face, cabId, part);
|
|
196
|
+
if (had) { const keep = ed.sel; editDim(had); ed.sel = keep; updateCtx(); return; }
|
|
197
|
+
if (part === 'z') { dimensionFace('v', floorAnchor(), cb.bl, 'Cabinet off the floor'); return; }
|
|
198
|
+
const [a, b] = part === 'w' ? [cb.bl, cb.br] : [cb.br, cb.tr];
|
|
199
|
+
dimensionFace(part === 'w' ? 'h' : 'v', a, b, part === 'w' ? 'Cabinet width' : 'Cabinet height');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** A cabinet's depth (out from the wall): a note on it, not drawn in the elevation. */
|
|
203
|
+
export function editCabinetDepth(cabId: string): void {
|
|
204
|
+
const cb = curFace().cabs?.[cabId];
|
|
205
|
+
if (!cb) return;
|
|
206
|
+
openKeypad({
|
|
207
|
+
title: 'Cabinet depth', sub: 'From the wall out into the room', initial: fmtLen(cb.depth), selectAll: true, laser: true,
|
|
208
|
+
onDone: v => { if (v <= 0) return; pushHistory(); cb.depth = v; cb.depthSet = true; afterEdit(); },
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Delete cabinets (their corners, edges, shape and size go with them). */
|
|
213
|
+
export function deleteCabinets(ids: string[]): void {
|
|
214
|
+
const face = curFace();
|
|
215
|
+
for (const id of ids) {
|
|
216
|
+
const cb = face.cabs?.[id];
|
|
217
|
+
if (!cb) continue;
|
|
218
|
+
for (const p of cabPoints(cb)) delete face.points[p];
|
|
219
|
+
delete face.cabs[id];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Select a cabinet. */
|
|
224
|
+
export function selectCabinet(id: string): void {
|
|
225
|
+
ed.sel = [{ type: 'cab', id }];
|
|
226
|
+
updateCtx(); requestDraw();
|
|
227
|
+
}
|
package/src/editor/files.ts
CHANGED
|
@@ -6,22 +6,26 @@ import { seedProject } from '../model/demo';
|
|
|
6
6
|
import { clearAllHistory } from '../model/history';
|
|
7
7
|
import { projectFileName, projectFromFile, projectToFile } from '../model/projectfile';
|
|
8
8
|
import { sessionState } from '../model/session';
|
|
9
|
-
import {
|
|
9
|
+
import { settings } from '../model/settings';
|
|
10
|
+
import { page, planSK, saveSoon, store } from '../model/store';
|
|
10
11
|
import type { Project } from '../model/types';
|
|
11
12
|
import { fitView } from '../render/view';
|
|
12
|
-
import { saveFile, toast, updateTopBar } from './ports';
|
|
13
|
+
import { refreshDrawerSoon, saveFile, toast, updateTopBar } from './ports';
|
|
14
|
+
import { closeElevation } from './elevation';
|
|
15
|
+
import { switchPage } from './project';
|
|
16
|
+
import { uid } from '../model/ids';
|
|
13
17
|
|
|
14
18
|
/* Files in and out: PDF and DXF of the page, the whole project as a .boldr.json file, and starting over. */
|
|
15
19
|
|
|
16
20
|
export function exportPdf(): void {
|
|
17
21
|
const name = pdfFileName(store.project.name, page().name);
|
|
18
|
-
saveFile(name, 'application/pdf', sketchToPdf(
|
|
22
|
+
saveFile(name, 'application/pdf', sketchToPdf(planSK(), { project: store.project.name, subtitle: page().name, cabinets: settings.layers.cabinets }));
|
|
19
23
|
toast('Exported ' + name);
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
export function exportDxf(): void {
|
|
23
27
|
const name = dxfFileName(store.project.name, page().name);
|
|
24
|
-
saveFile(name, 'application/dxf', sketchToDxf(
|
|
28
|
+
saveFile(name, 'application/dxf', sketchToDxf(planSK(), { cabinets: settings.layers.cabinets }));
|
|
25
29
|
toast('Exported ' + name + ' (inches)');
|
|
26
30
|
}
|
|
27
31
|
|
|
@@ -47,6 +51,38 @@ export function openProject(p: Project): void {
|
|
|
47
51
|
toast('Opened ' + p.name, { action: { label: 'Undo', fn: () => { show(before.project, before.page); toast('Back to ' + before.project.name); } } });
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Open file (the ⋯ menu): a project file's pages are added to the drawing that's open — nothing in it is replaced —
|
|
56
|
+
* and the first one is shown. A page named like one already here takes the file's project name in front (and a
|
|
57
|
+
* number if it still clashes). The message's Undo takes them out again. Saved with the rest when the drawing is.
|
|
58
|
+
*/
|
|
59
|
+
export function importPagesText(text: string): void {
|
|
60
|
+
let p: Project;
|
|
61
|
+
try { p = projectFromFile(text); } catch (e) { toast((e as Error).message, { warn: true }); return; }
|
|
62
|
+
closeElevation();
|
|
63
|
+
const names = new Set(store.project.pages.map(x => x.name)), from = p.name && p.name !== store.project.name ? p.name + ' · ' : '';
|
|
64
|
+
const added = p.pages.map(pg => {
|
|
65
|
+
let name = names.has(pg.name) ? from + pg.name : pg.name;
|
|
66
|
+
for (let n = 2, base = name; names.has(name); n++) name = base + ' ' + n;
|
|
67
|
+
names.add(name);
|
|
68
|
+
return { ...pg, id: uid('pg'), name, view: null };
|
|
69
|
+
});
|
|
70
|
+
const back = store.curPageId, ids = new Set(added.map(x => x.id));
|
|
71
|
+
store.project.pages.push(...added);
|
|
72
|
+
switchPage(added[0].id);
|
|
73
|
+
refreshDrawerSoon();
|
|
74
|
+
const n = added.length;
|
|
75
|
+
toast('Added ' + (n === 1 ? 'a page' : n + ' pages') + ' from ' + p.name, {
|
|
76
|
+
action: { label: 'Undo', fn: () => {
|
|
77
|
+
if (!store.project.pages.some(x => !ids.has(x.id))) return;
|
|
78
|
+
if (ids.has(store.curPageId)) switchPage(store.project.pages.some(x => x.id === back) ? back : store.project.pages.find(x => !ids.has(x.id))!.id);
|
|
79
|
+
store.project.pages = store.project.pages.filter(x => !ids.has(x.id));
|
|
80
|
+
updateTopBar(); refreshDrawerSoon(); saveSoon();
|
|
81
|
+
toast('Import taken out');
|
|
82
|
+
} },
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
50
86
|
/** Open a project file's text; a message says what's wrong if it isn't one. */
|
|
51
87
|
export function openProjectText(text: string): void {
|
|
52
88
|
try { openProject(projectFromFile(text)); }
|
package/src/editor/gestures.ts
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import { redo, undo } from '../app/edit';
|
|
2
|
-
import { ed } from '../app/state';
|
|
2
|
+
import { ed, type Preview, type Snap } from '../app/state';
|
|
3
3
|
import { hyp } from '../model/geom';
|
|
4
4
|
import { revertLast } from '../model/history';
|
|
5
5
|
import { onSessionReset } from '../model/session';
|
|
6
|
-
import {
|
|
7
|
-
import { panBy, requestDraw, s2w, zoomAt } from '../render/view';
|
|
6
|
+
import { saveViewSoon, setSaveIdle } from '../model/store';
|
|
7
|
+
import { paintPending, panBy, requestDraw, s2w, zoomAt } from '../render/view';
|
|
8
8
|
import { tools, type PEv } from '../tools/registry';
|
|
9
|
-
import { dismissPopovers, setCursor } from './ports';
|
|
9
|
+
import { closeKeypad, dismissConflict, dismissPopovers, interacting, setCursor, updateCtx, updateHint } from './ports';
|
|
10
|
+
import { intoWall } from './elevation';
|
|
11
|
+
import { keypadOnHost, keypadOpen, keypadSpec } from './keypad';
|
|
12
|
+
import { conflictOpen } from './conflict';
|
|
13
|
+
import { confirmCancel, confirmOpen } from './confirm';
|
|
14
|
+
import { readOnlyTap } from './linkActions';
|
|
15
|
+
import type { SelItem } from '../model/types';
|
|
10
16
|
|
|
11
17
|
/*
|
|
12
18
|
Pointer routing for the plan canvas, whatever delivers the pointers (DOM pointer events, React Native touches).
|
|
13
19
|
Pen draws, fingers navigate: once a pen has been seen, touch only pans and zooms. Without a pen, one finger drives
|
|
14
20
|
the tool and a second finger turns the action into a pan/pinch. Two-finger tap undoes, three-finger tap redoes.
|
|
15
21
|
Mouse: middle or right button, or Space held, drags to pan; the wheel zooms.
|
|
22
|
+
With a sheet up over the drawing (the keypad, a conflict, a question), the drawing only pans and zooms, by any
|
|
23
|
+
pointer, and a tap on it closes the sheet. View only, a tap on a link's tile asks to open what it's for.
|
|
16
24
|
*/
|
|
17
25
|
|
|
18
26
|
/**
|
|
@@ -28,17 +36,69 @@ export interface PointerIn {
|
|
|
28
36
|
interface Gesture { t0: number; max: number; moved: number; last: GState | null }
|
|
29
37
|
interface GState { x: number; y: number; d: number; n: number }
|
|
30
38
|
|
|
39
|
+
/**
|
|
40
|
+
* A host that pans and zooms the view itself (a device does it on its UI thread, so a pinch never waits for this
|
|
41
|
+
* thread): fingers still end a tool's action and two- and three-finger taps still undo and redo here, but the view is
|
|
42
|
+
* left to the host, which hands it back with setViewFromHost.
|
|
43
|
+
*/
|
|
44
|
+
let hostNavigates = false;
|
|
45
|
+
export function setHostNavigation(on: boolean): void { hostNavigates = on; }
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Past this far (screen px) pointers on the drawing are moving — a stroke, a drag, a pan or a pinch — not tapping. It
|
|
49
|
+
* tells a two-finger tap (undo) from a pan or pinch, and it's when the interface hears that a gesture is in motion
|
|
50
|
+
* (the `interacting` port: a phone's chrome steps aside) until the last pointer lifts — so the chrome never blinks on
|
|
51
|
+
* a tap.
|
|
52
|
+
*/
|
|
53
|
+
const MOTION_PX = 14;
|
|
54
|
+
|
|
31
55
|
const ptrs = new Map<number, PointerIn>();
|
|
32
|
-
let penSeen = false, gesture: Gesture | null = null, primary: PointerIn | null = null
|
|
56
|
+
let penSeen = false, gesture: Gesture | null = null, primary: PointerIn | null = null;
|
|
57
|
+
/** close: a press under a sheet (see sheetUp) — a tap closes it; open: a view-only press — a tap opens a link's tile */
|
|
58
|
+
let panDrag: { x: number; y: number; id: number; moved: number; close?: boolean; open?: boolean } | null = null;
|
|
59
|
+
/** where the primary pointer went down, and whether a gesture on the drawing is in motion */
|
|
60
|
+
let primaryAt: { x: number; y: number } | null = null, inMotion = false;
|
|
61
|
+
/**
|
|
62
|
+
* What a press can change, as it was before the primary pointer went down: if a second finger turns the press into a
|
|
63
|
+
* pan or pinch, it's put back (see cancelPrimary). A tool's own state is kept a level deep — its presses set its fields,
|
|
64
|
+
* they don't change what's in them.
|
|
65
|
+
*/
|
|
66
|
+
let beforePress: { tool: string; st: unknown; sel: SelItem[]; preview: Preview | null; snap: Snap | null } | null = null;
|
|
33
67
|
let wheelT: ReturnType<typeof setTimeout> | undefined;
|
|
34
68
|
export const keys = { spaceDown: false };
|
|
35
69
|
onSessionReset(() => {
|
|
36
|
-
ptrs.clear(); penSeen = false; gesture = null; primary = null; panDrag = null; keys.spaceDown = false;
|
|
70
|
+
ptrs.clear(); penSeen = false; gesture = null; primary = null; panDrag = null; keys.spaceDown = false; primaryAt = null; beforePress = null;
|
|
71
|
+
setMotion(false);
|
|
37
72
|
clearTimeout(wheelT);
|
|
38
73
|
});
|
|
39
74
|
|
|
75
|
+
// (a save waits until nothing is pressed on the drawing: see setSaveIdle)
|
|
76
|
+
setSaveIdle(() => ptrs.size === 0);
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A sheet is up over the drawing: the keypad (not one that leaves the drawing live, nor the host's own, which covers
|
|
80
|
+
* it) or a conflict to resolve. On a tablet or a computer the drawing stays in reach around it (react/CadEditor.tsx):
|
|
81
|
+
* it can be panned and zoomed to see what's being measured, and a tap on it closes the sheet — nothing on it is
|
|
82
|
+
* picked or changed meanwhile. (A phone's sheet has a backdrop: the drawing gets no pointers at all.)
|
|
83
|
+
*/
|
|
84
|
+
export const sheetUp = (): boolean => (keypadOpen() && !keypadSpec()?.modeless && !keypadOnHost()) || conflictOpen() || confirmOpen();
|
|
85
|
+
|
|
86
|
+
/** Pointers only move the view: a drawing that can't be edited, or one with a sheet up over it. */
|
|
87
|
+
export const navigationOnly = (): boolean => ed.readOnly || sheetUp();
|
|
88
|
+
|
|
89
|
+
function closeSheet(): void {
|
|
90
|
+
if (keypadOpen()) closeKeypad(); else if (confirmOpen()) confirmCancel(); else dismissConflict();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function setMotion(on: boolean): void {
|
|
94
|
+
if (on === inMotion) return;
|
|
95
|
+
inMotion = on;
|
|
96
|
+
interacting(on);
|
|
97
|
+
}
|
|
98
|
+
|
|
40
99
|
function evOf(p: PointerIn): PEv {
|
|
41
|
-
|
|
100
|
+
// in the wall view, tools work inside the wall
|
|
101
|
+
const [wx, wy] = intoWall(...s2w(p.x, p.y));
|
|
42
102
|
return { sx: p.x, sy: p.y, wx, wy, touch: p.kind !== 'mouse', shift: !!p.shift };
|
|
43
103
|
}
|
|
44
104
|
|
|
@@ -62,10 +122,14 @@ function updateGesture(): void {
|
|
|
62
122
|
const s = gState(ts), L = g.last;
|
|
63
123
|
if (!s || !L) { g.last = s; return; }
|
|
64
124
|
if (s.n !== L.n) { g.last = s; return; }
|
|
65
|
-
const dx = s.x - L.x, dy = s.y - L.y;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (
|
|
125
|
+
const dx = s.x - L.x, dy = s.y - L.y, pinch = s.n >= 2 && L.d > 0 && s.d > 0;
|
|
126
|
+
// (how far the fingers went tells a tap — undo, redo — from a pan or pinch, whoever moves the view)
|
|
127
|
+
g.moved += hyp(dx, dy) + (pinch ? Math.abs(s.d - L.d) : 0);
|
|
128
|
+
if (g.moved >= MOTION_PX) setMotion(true);
|
|
129
|
+
if (!hostNavigates) {
|
|
130
|
+
panBy(dx, dy);
|
|
131
|
+
if (pinch) zoomAt(s.x, s.y, s.d / L.d);
|
|
132
|
+
}
|
|
69
133
|
g.last = s;
|
|
70
134
|
}
|
|
71
135
|
|
|
@@ -73,25 +137,43 @@ function endGesture(t: number): void {
|
|
|
73
137
|
const g = gesture;
|
|
74
138
|
gesture = null;
|
|
75
139
|
if (!g) return;
|
|
76
|
-
|
|
77
|
-
|
|
140
|
+
// (under a sheet a tap closes it, however many fingers: nothing is undone under it)
|
|
141
|
+
if (g.moved < MOTION_PX && sheetUp()) closeSheet();
|
|
142
|
+
// (view only: a finger's tap on a link's tile opens it, once the person says so)
|
|
143
|
+
else if (g.moved < MOTION_PX && ed.readOnly && g.max === 1 && g.last && t - g.t0 < 500) readOnlyTap(g.last.x, g.last.y, true);
|
|
144
|
+
else if (g.moved < MOTION_PX && t - g.t0 < 380 && !ed.readOnly) { if (g.max === 2) undo(); else if (g.max >= 3) redo(); }
|
|
145
|
+
saveViewSoon();
|
|
78
146
|
}
|
|
79
147
|
|
|
80
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* A second finger landed mid-action: the first finger's press never happened. A drag in progress is reverted, and the
|
|
150
|
+
* tool, the selection and what's shown go back to how they were before it — so a shape being drawn keeps the points
|
|
151
|
+
* already placed (a rectangle's first corner, an arc's ends, a line's last point), nothing is left where the first
|
|
152
|
+
* finger landed (a line or rectangle to it, a mark under it), and the next tap carries on where it was.
|
|
153
|
+
*/
|
|
81
154
|
function cancelPrimary(): void {
|
|
82
|
-
const t = curTool();
|
|
155
|
+
const t = curTool(), was = beforePress;
|
|
83
156
|
if (t && t.st && t.st.dragging && t.st.drag) revertLast();
|
|
84
|
-
if (t &&
|
|
157
|
+
if (t && was && was.tool === ed.tool) {
|
|
158
|
+
t.st = was.st;
|
|
159
|
+
ed.sel = was.sel; ed.preview = was.preview; ed.snapState = was.snap;
|
|
160
|
+
} else if (t && t.reset) t.reset();
|
|
161
|
+
beforePress = null;
|
|
85
162
|
primary = null;
|
|
86
163
|
ed.boxSel = null;
|
|
87
|
-
|
|
88
|
-
requestDraw();
|
|
164
|
+
updateCtx(); updateHint(); requestDraw();
|
|
89
165
|
}
|
|
90
166
|
|
|
91
167
|
function endPtr(e: PointerIn, cancel: boolean): void {
|
|
92
168
|
const p = ptrs.get(e.id);
|
|
93
169
|
ptrs.delete(e.id);
|
|
94
|
-
if (panDrag && panDrag.id === e.id) {
|
|
170
|
+
if (panDrag && panDrag.id === e.id) {
|
|
171
|
+
const still = panDrag.moved < MOTION_PX && !cancel, close = !!panDrag.close && still, open = !!panDrag.open && still;
|
|
172
|
+
panDrag = null; setCursor(''); saveViewSoon();
|
|
173
|
+
if (close && sheetUp()) closeSheet();
|
|
174
|
+
else if (open) readOnlyTap(e.x, e.y, e.kind !== 'mouse');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
95
177
|
if (gesture && p && p.kind === 'touch') { if (!touchList().length) endGesture(e.t); return; }
|
|
96
178
|
if (primary && e.id === primary.id) {
|
|
97
179
|
primary = null;
|
|
@@ -107,40 +189,74 @@ export function pointerDown(e: PointerIn): void {
|
|
|
107
189
|
ed.lastPointerTouch = e.kind !== 'mouse';
|
|
108
190
|
const p = { ...e };
|
|
109
191
|
ptrs.set(e.id, p);
|
|
110
|
-
|
|
192
|
+
// (under a sheet a pen moves the view as a mouse does, and a plain press of either that doesn't move closes it)
|
|
193
|
+
const under = sheetUp(), navOnly = under || ed.readOnly;
|
|
194
|
+
if ((e.kind === 'mouse' && (e.button === 1 || e.button === 2 || keys.spaceDown || navOnly)) || (e.kind === 'pen' && (under || ed.readOnly))) {
|
|
195
|
+
panDrag = { x: p.x, y: p.y, id: e.id, moved: 0, close: under && !e.button, open: ed.readOnly && !under && !e.button && !keys.spaceDown };
|
|
196
|
+
setCursor('grabbing');
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
111
199
|
if (e.kind === 'touch') {
|
|
112
200
|
const touches = touchList();
|
|
113
|
-
if (penSeen || touches.length >= 2 ||
|
|
201
|
+
if (penSeen || touches.length >= 2 || navOnly) {
|
|
114
202
|
if (primary && primary.kind === 'touch') cancelPrimary();
|
|
115
|
-
|
|
203
|
+
// (a finger joining one already moving the view: the pinch is measured from here, none of its first move lost)
|
|
204
|
+
if (!gesture) startGesture(e.t); else { gesture.max = Math.max(gesture.max, touches.length); gesture.last = gState(touches); }
|
|
116
205
|
return;
|
|
117
206
|
}
|
|
118
207
|
}
|
|
119
|
-
if (primary ||
|
|
208
|
+
if (primary || navOnly) return;
|
|
120
209
|
primary = p;
|
|
210
|
+
primaryAt = { x: e.x, y: e.y };
|
|
211
|
+
// (what it lands on is picked from the drawing as it is now, not as the last frame painted it)
|
|
212
|
+
paintPending();
|
|
121
213
|
const t = curTool();
|
|
214
|
+
beforePress = { tool: ed.tool, st: t && t.st && typeof t.st === 'object' ? { ...t.st } : t?.st, sel: [...ed.sel], preview: ed.preview, snap: ed.snapState };
|
|
122
215
|
if (t && t.down) t.down(evOf(e));
|
|
123
216
|
}
|
|
124
217
|
|
|
125
218
|
export function pointerMove(e: PointerIn): void {
|
|
126
219
|
const p = ptrs.get(e.id);
|
|
127
|
-
if (panDrag && panDrag.id === e.id) {
|
|
220
|
+
if (panDrag && panDrag.id === e.id) {
|
|
221
|
+
panDrag.moved += hyp(e.x - panDrag.x, e.y - panDrag.y);
|
|
222
|
+
if (panDrag.moved >= MOTION_PX) setMotion(true);
|
|
223
|
+
panBy(e.x - panDrag.x, e.y - panDrag.y); panDrag.x = e.x; panDrag.y = e.y;
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
128
226
|
if (p) { p.x = e.x; p.y = e.y; }
|
|
129
227
|
if (gesture && p && p.kind === 'touch') { updateGesture(); return; }
|
|
130
228
|
const t = curTool();
|
|
131
229
|
if (!t) return;
|
|
132
|
-
if (primary && e.id === primary.id) {
|
|
133
|
-
|
|
230
|
+
if (primary && e.id === primary.id) {
|
|
231
|
+
if (primaryAt && hyp(e.x - primaryAt.x, e.y - primaryAt.y) >= MOTION_PX) setMotion(true);
|
|
232
|
+
if (t.move) t.move(evOf(e));
|
|
233
|
+
}
|
|
234
|
+
else if (!primary && e.kind !== 'touch' && !navigationOnly()) { ed.lastPointerTouch = false; if (t.hover) t.hover(evOf(e)); }
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function pointerUp(e: PointerIn): void { endPtr(e, false); settle(); }
|
|
238
|
+
export function pointerCancel(e: PointerIn): void { endPtr(e, true); settle(); }
|
|
239
|
+
|
|
240
|
+
/** The last pointer lifted: nothing on the drawing is moving any more. */
|
|
241
|
+
function settle(): void {
|
|
242
|
+
if (ptrs.size) return;
|
|
243
|
+
primaryAt = null;
|
|
244
|
+
setMotion(false);
|
|
134
245
|
}
|
|
135
246
|
|
|
136
|
-
|
|
137
|
-
|
|
247
|
+
/**
|
|
248
|
+
* Every pointer the host still has down (a device says so as each new one lands): any other was lost — its lift
|
|
249
|
+
* never came — and is let go now, so it can't hold a gesture, or the chrome, open.
|
|
250
|
+
*/
|
|
251
|
+
export function pointersStill(ids: number[]): void {
|
|
252
|
+
for (const [id, p] of [...ptrs]) if (!ids.includes(id)) pointerCancel({ ...p, t: Date.now() });
|
|
253
|
+
}
|
|
138
254
|
|
|
139
|
-
/** A mouse left the canvas: drop hover highlights (the
|
|
255
|
+
/** A mouse left the canvas: drop hover highlights (the Line tool keeps its snap marker). */
|
|
140
256
|
export function pointerLeave(kind: string): void {
|
|
141
257
|
if (kind === 'mouse' && !primary) {
|
|
142
258
|
ed.hover = null;
|
|
143
|
-
if (ed.tool !== 'line'
|
|
259
|
+
if (ed.tool !== 'line') ed.snapState = null;
|
|
144
260
|
requestDraw();
|
|
145
261
|
}
|
|
146
262
|
}
|
|
@@ -153,5 +269,5 @@ export function wheelZoom(x: number, y: number, dy: number, pinch: boolean, line
|
|
|
153
269
|
if (pinch) zoomAt(x, y, Math.exp(-dy * 0.01));
|
|
154
270
|
else zoomAt(x, y, Math.exp(-dy * (lines ? 0.06 : 0.0016)));
|
|
155
271
|
clearTimeout(wheelT);
|
|
156
|
-
wheelT = setTimeout(
|
|
272
|
+
wheelT = setTimeout(saveViewSoon, 400);
|
|
157
273
|
}
|