@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,77 @@
|
|
|
1
|
+
import { linkInfoOf, linkKey, linkName } from '../model/links';
|
|
2
|
+
import { onSessionReset } from '../model/session';
|
|
3
|
+
import { dimMeasurement } from '../model/measurement';
|
|
4
|
+
import { store } from '../model/store';
|
|
5
|
+
import type { Constraint, LinkTarget } from '../model/types';
|
|
6
|
+
import { fmtAng, fmtLen } from '../units';
|
|
7
|
+
import { askConfirm } from './confirm';
|
|
8
|
+
import { linksHost } from './links';
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
A dimension linked to a measurement of the job is that measurement: giving it a new value — typed, from a laser
|
|
12
|
+
reading, dropped from the tray — changes the job's too, so the person is asked first. Change both writes it to the
|
|
13
|
+
job (through the host); Only this drawing keeps the new value here and lets go of the link; Cancel leaves both.
|
|
14
|
+
|
|
15
|
+
What the editor writes comes back from the job a moment later, and an answer from before it may arrive first: for a
|
|
16
|
+
few seconds after a write, the job saying anything else about that measurement is taken to be the old news it is.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** Writes to the job's measurements not yet heard back: by linkKey, the value written and when. */
|
|
20
|
+
const wrote = new Map<string, { value: number; at: number }>();
|
|
21
|
+
const ECHO_MS = 8000;
|
|
22
|
+
onSessionReset(() => wrote.clear());
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Whether what the job says of `t` (its value `value`) is older than what was just written to it — to be ignored.
|
|
26
|
+
* Hearing the written value back, it's settled.
|
|
27
|
+
*/
|
|
28
|
+
export function staleEcho(t: LinkTarget, value: number): boolean {
|
|
29
|
+
const k = linkKey(t), w = wrote.get(k);
|
|
30
|
+
if (!w) return false;
|
|
31
|
+
if (w.value === value || Date.now() - w.at > ECHO_MS) { wrote.delete(k); return false; }
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Whether a new value for `c` would change it (a linked one is asked about only then). */
|
|
36
|
+
export const changesValue = (c: Constraint, v: number): boolean => c.value == null || Math.abs(c.value - v) > 1e-9;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Ask before giving linked dimension `c` value `v` (inches, or degrees). `apply(unlink)` sets it — letting go of the
|
|
40
|
+
* link with `unlink` — and says whether it took; `capture` (a laser reading's) goes to the job with it.
|
|
41
|
+
*/
|
|
42
|
+
export function askLinkedChange(c: Constraint, v: number, apply: (unlink: boolean) => boolean, capture?: unknown): void {
|
|
43
|
+
const t = c.measurementRef;
|
|
44
|
+
if (!t) return;
|
|
45
|
+
const info = linkInfoOf(t), host = linksHost(), m = dimMeasurement(c, v);
|
|
46
|
+
const name = linkName(t), group = info?.groupName ?? t.groupName, gone = info?.exists === false;
|
|
47
|
+
const canWrite = !!host?.update && !!m && !gone && info?.canEdit !== false;
|
|
48
|
+
const pageId = store.curPageId, still = () => store.curPageId === pageId && linksHost() === host;
|
|
49
|
+
const what = c.type === 'angle' ? fmtAng(v) : fmtLen(c.rad ? v / 2 : v);
|
|
50
|
+
if (canWrite) {
|
|
51
|
+
askConfirm({
|
|
52
|
+
title: 'Change ' + name + (group ? ' in ' + group : '') + ' to ' + what + '?',
|
|
53
|
+
msg: 'This dimension is linked to it. Only this drawing keeps ' + what + ' here and unlinks them.',
|
|
54
|
+
choices: [
|
|
55
|
+
{ label: 'Change both', run: () => {
|
|
56
|
+
if (!still()) return;
|
|
57
|
+
// (before it's set: the drawing hearing the job's old value meanwhile mustn't put it back)
|
|
58
|
+
const k = linkKey(t), was = wrote.get(k);
|
|
59
|
+
wrote.set(k, { value: m!.value, at: Date.now() });
|
|
60
|
+
if (apply(false)) host!.update!(t, m!.value, capture);
|
|
61
|
+
else if (was) wrote.set(k, was); else wrote.delete(k);
|
|
62
|
+
} },
|
|
63
|
+
{ label: 'Only this drawing', run: () => { if (still()) apply(true); } },
|
|
64
|
+
{ label: 'Cancel', run: () => {} },
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
askConfirm({
|
|
70
|
+
title: gone ? name + ' was removed from the job' : name + ' can’t be changed from here',
|
|
71
|
+
msg: 'Keep ' + what + ' in this drawing (it unlinks from ' + name + '), or cancel.',
|
|
72
|
+
choices: [
|
|
73
|
+
{ label: 'Keep in this drawing', run: () => { if (still()) apply(true); } },
|
|
74
|
+
{ label: 'Cancel', run: () => {} },
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { linkInfo, linkKey, linkTargets } from '../model/links';
|
|
2
|
+
import { onSessionReset } from '../model/session';
|
|
3
|
+
import { store } from '../model/store';
|
|
4
|
+
import type { LinkInfo, LinkTarget } from '../model/types';
|
|
5
|
+
import { requestDraw } from '../render/view';
|
|
6
|
+
import { updateCtx } from './ports';
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
The job's files and measurements, as the host has them (CadEditorProps.links). The editor asks it to pick files
|
|
10
|
+
(Annotate's +), to list the job's measurements or pick one itself (a line's length keypad and a dimension's angle
|
|
11
|
+
keypad in Draft offer them: the only way a measurement comes into a drawing), to say what each linked one is now —
|
|
12
|
+
its name, its picture, its value, and whether it's still there (model/links.ts's linkInfo, which the tiles draw) —
|
|
13
|
+
to open one, and, once the person has said so, to change one or to save a dimension to the drawing's group. A host
|
|
14
|
+
without it shows none of this.
|
|
15
|
+
|
|
16
|
+
The editor asks about everything the drawing links to at once, on every page, and asks again only when that
|
|
17
|
+
changes (after an edit, pages taken in from elsewhere, a new session).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** What a keypad takes from the job's measurements: a line's length, or a dimension's angle. */
|
|
21
|
+
export type MeasurementKind = 'length' | 'angle';
|
|
22
|
+
|
|
23
|
+
/** What the editor asks the host to pick from (Annotate's +). */
|
|
24
|
+
export interface CadLinkPick {
|
|
25
|
+
/**
|
|
26
|
+
* 'files': the job's files and objects to pick. Never its measurements: those come into a drawing only through a
|
|
27
|
+
* line's length keypad in Draft (CadLinks.lengths), as the dimensions they drive. A host may still show them, greyed
|
|
28
|
+
* out, saying so when one's tapped (BOLDR's do); one it hands back anyway is dropped.
|
|
29
|
+
*/
|
|
30
|
+
want: 'files';
|
|
31
|
+
/** what the drawing links to already */
|
|
32
|
+
linked: LinkTarget[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A dimension of the drawing, saved to the drawing's group as a measurement of the job. */
|
|
36
|
+
export interface CadSaveMeasurement {
|
|
37
|
+
kind: 'length' | 'angle';
|
|
38
|
+
/** micrometres for a length, degrees for an angle */
|
|
39
|
+
value: number;
|
|
40
|
+
/** the dimension's label, if it has one */
|
|
41
|
+
label?: string;
|
|
42
|
+
/** the host's record of the laser reading that set it (as laserReading was given it), if one did */
|
|
43
|
+
capture?: unknown;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The host's side of links (CadEditorProps.links). Pass one object for as long as the editor's open (memoized). */
|
|
47
|
+
export interface CadLinks {
|
|
48
|
+
/** Let the person pick from the job's files; resolves with what they picked ([] for nothing). */
|
|
49
|
+
pick(req: CadLinkPick): Promise<LinkTarget[]>;
|
|
50
|
+
/**
|
|
51
|
+
* The job's measurements, lengths and angles, for a keypad's own list (a page swiped to on a touch screen): call
|
|
52
|
+
* `cb` with them, in the order to show them, as targets with what they are now (name, group, `mkind`, value, text)
|
|
53
|
+
* — and again as they change, but not until the whole job's been heard from (never none while it's loading). The
|
|
54
|
+
* keypad greys out those that aren't its kind. Returns how to stop.
|
|
55
|
+
*/
|
|
56
|
+
measurements?(cb: (list: LinkTarget[]) => void): () => void;
|
|
57
|
+
/**
|
|
58
|
+
* Let the person pick one of the job's measurements for a keypad from the host's own list (Select From List, on a
|
|
59
|
+
* desktop: its side drawer), those of `kind` pickable and the rest greyed out. Resolves with the one picked (null:
|
|
60
|
+
* none). Absent: the keypad's own list (`measurements`) is shown instead.
|
|
61
|
+
*/
|
|
62
|
+
pickMeasurement?(req: { kind: MeasurementKind }): Promise<LinkTarget | null>;
|
|
63
|
+
/**
|
|
64
|
+
* Say what each of `targets` is now, by linkKey, and again whenever that changes — one that's gone with
|
|
65
|
+
* `exists: false`, but never while it's still loading. Returns how to stop.
|
|
66
|
+
*/
|
|
67
|
+
subscribe(targets: LinkTarget[], cb: (info: Record<string, LinkInfo>) => void): () => void;
|
|
68
|
+
/** Open what `target` is for (another screen, or a sheet over the drawing). */
|
|
69
|
+
open(target: LinkTarget): void;
|
|
70
|
+
/** Change a linked measurement (micrometres, or degrees), the person having said to; `capture` as saveMeasurement's. */
|
|
71
|
+
update?(target: LinkTarget, value: number, capture?: unknown): void;
|
|
72
|
+
/** Save a dimension as a new measurement in the drawing's own group; resolves with it (null: not saved). */
|
|
73
|
+
saveMeasurement?(m: CadSaveMeasurement): Promise<LinkTarget | null>;
|
|
74
|
+
/** whether saveMeasurement can be used here (not in a group whose measurements need a column, for one) */
|
|
75
|
+
canSave?: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let host: CadLinks | null = null;
|
|
79
|
+
/** The host's answers for what's linked now, as asked (keys: what was asked about). */
|
|
80
|
+
let sub: { keys: string; stop: () => void } | null = null;
|
|
81
|
+
/** Bumped when the host or the session changes: an answer to an older question is dropped. */
|
|
82
|
+
let epoch = 0;
|
|
83
|
+
/** The picker's open: a second + meanwhile (a double press) asks nothing more of the host. */
|
|
84
|
+
let picking = false;
|
|
85
|
+
/** Told of each answer (editor/linkFollow.ts: linked dimensions follow their measurements). */
|
|
86
|
+
const heard: ((info: Record<string, LinkInfo>) => void)[] = [];
|
|
87
|
+
|
|
88
|
+
export const linksHost = (): CadLinks | null => host;
|
|
89
|
+
export const linksEpoch = (): number => epoch;
|
|
90
|
+
/** Whether the host can link things (Annotate's + shows). */
|
|
91
|
+
export const canLink = (): boolean => !!host;
|
|
92
|
+
|
|
93
|
+
/** The host's links from now on (CadEditor), or null for none. */
|
|
94
|
+
export function setLinksHost(h: CadLinks | null): void {
|
|
95
|
+
if (h === host) return;
|
|
96
|
+
stop();
|
|
97
|
+
host = h;
|
|
98
|
+
epoch++;
|
|
99
|
+
picking = false;
|
|
100
|
+
refreshLinks();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function stop(): void {
|
|
104
|
+
const s = sub;
|
|
105
|
+
sub = null;
|
|
106
|
+
s?.stop();
|
|
107
|
+
}
|
|
108
|
+
onSessionReset(() => { stop(); host = null; epoch++; picking = false; });
|
|
109
|
+
|
|
110
|
+
/** Call `fn` with each answer about what's linked (after it's been taken in). */
|
|
111
|
+
export function onLinkInfo(fn: (info: Record<string, LinkInfo>) => void): void { heard.push(fn); }
|
|
112
|
+
|
|
113
|
+
/** Ask the host about what the drawing links to now — if that's changed since it was last asked. */
|
|
114
|
+
export function refreshLinks(): void {
|
|
115
|
+
const p = store.project;
|
|
116
|
+
if (!host || !p) { stop(); return; }
|
|
117
|
+
const targets = linkTargets(p), keys = targets.map(linkKey).sort().join('\n');
|
|
118
|
+
if (sub && sub.keys === keys) return;
|
|
119
|
+
stop();
|
|
120
|
+
if (!targets.length) return;
|
|
121
|
+
const s = { keys, stop: () => {} };
|
|
122
|
+
// (before asking: the host may answer at once)
|
|
123
|
+
sub = s;
|
|
124
|
+
const unsub = host.subscribe(targets, info => {
|
|
125
|
+
if (sub !== s) return;
|
|
126
|
+
// (what's been heard of stays: another question about it, as the links change, needn't wait for the answer)
|
|
127
|
+
Object.assign(linkInfo.byKey, info);
|
|
128
|
+
for (const fn of heard) fn(info);
|
|
129
|
+
requestDraw();
|
|
130
|
+
updateCtx();
|
|
131
|
+
});
|
|
132
|
+
if (sub === s) s.stop = unsub;
|
|
133
|
+
else unsub();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Have the person pick from the job's files: what they picked, or [] if nothing (or the editor moved on meanwhile). */
|
|
137
|
+
export async function pickLinks(): Promise<LinkTarget[]> {
|
|
138
|
+
const h = host, p = store.project;
|
|
139
|
+
if (!h || !p || picking) return [];
|
|
140
|
+
const mine = epoch;
|
|
141
|
+
picking = true;
|
|
142
|
+
let out: LinkTarget[];
|
|
143
|
+
try { out = await h.pick({ want: 'files', linked: linkTargets(p) }); } catch { return []; } finally { if (mine === epoch) picking = false; }
|
|
144
|
+
// (its files: a measurement isn't put on the plan as a tile)
|
|
145
|
+
return mine === epoch && h === host ? (out ?? []).filter(t => t.kind === 'file') : [];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Whether the host offers the job's measurements to a keypad: listing them, or picking one itself. */
|
|
149
|
+
export const offersJobMeasurements = (): boolean => !!host?.measurements || !!host?.pickMeasurement;
|
|
150
|
+
/** Whether the host picks a measurement itself (Select From List opens its own list). */
|
|
151
|
+
export const hostPicksMeasurements = (): boolean => !!host?.pickMeasurement;
|
|
152
|
+
|
|
153
|
+
/** Whether a measurement of the job's can go in a keypad of `kind`: one of that kind, more than nothing. */
|
|
154
|
+
export const measurementFits = (t: LinkTarget, kind: MeasurementKind): boolean =>
|
|
155
|
+
t.kind === 'measurement' && (t.mkind ?? 'length') === kind && (t.value ?? 0) > 0;
|
|
156
|
+
|
|
157
|
+
/** Hear the job's measurements from the host, and again as they change, until the returned stop — or the host changes. */
|
|
158
|
+
export function watchJobMeasurements(cb: (list: LinkTarget[]) => void): () => void {
|
|
159
|
+
const h = host;
|
|
160
|
+
if (!h?.measurements) return () => {};
|
|
161
|
+
let live = true;
|
|
162
|
+
const stop = h.measurements(list => { if (live && h === host) cb(list.filter(t => t.kind === 'measurement' && typeof t.value === 'number')); });
|
|
163
|
+
return () => { live = false; stop(); };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Have the host's own list pick a measurement of `kind` (Select From List): the one picked, or null — none picked, one
|
|
168
|
+
* that isn't of `kind`, or the editor moved on meanwhile. Asks once at a time, as the picker's +.
|
|
169
|
+
*/
|
|
170
|
+
export async function pickJobMeasurement(kind: MeasurementKind): Promise<LinkTarget | null> {
|
|
171
|
+
const h = host;
|
|
172
|
+
if (!h?.pickMeasurement || picking) return null;
|
|
173
|
+
const mine = epoch;
|
|
174
|
+
picking = true;
|
|
175
|
+
let t: LinkTarget | null;
|
|
176
|
+
try { t = await h.pickMeasurement({ kind }); } catch { return null; } finally { if (mine === epoch) picking = false; }
|
|
177
|
+
return mine === epoch && h === host && t && measurementFits(t, kind) ? t : null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Open what a link is for. */
|
|
181
|
+
export function openLink(t: LinkTarget): void { host?.open(t); }
|
package/src/editor/markup.ts
CHANGED
|
@@ -1,16 +1,100 @@
|
|
|
1
1
|
import { afterEdit } from '../app/edit';
|
|
2
2
|
import { ed } from '../app/state';
|
|
3
3
|
import { pushHistory } from '../model/history';
|
|
4
|
+
import { DEFAULT_NOTE_WIDTH, hasEnds, NOTE_TEXT_IN, noteLook, noteTextIn, type NoteStyle } from '../model/markup';
|
|
4
5
|
import { SK } from '../model/store';
|
|
5
|
-
import {
|
|
6
|
+
import type { Note, NoteCap } from '../model/types';
|
|
7
|
+
import { mainT, noteTextBox } from '../render/view';
|
|
8
|
+
import { showNoteInput, updateCtx, updateRail } from './ports';
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
export const NOTE_COLORS = ['#F070EE', '#E83D2F', '#FFAD00', '#0066FF', '#00E920', '#181A1C'];
|
|
10
|
+
export { NOTE_CAPS, NOTE_COLORS, NOTE_STYLES, NOTE_TEXT_SIZES, NOTE_WIDTHS } from '../model/markup';
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
/*
|
|
13
|
+
The stroke panel (the rail's color dot): BOLDR's annotation set — color, width, solid / dashed /
|
|
14
|
+
dotted, and the two ends of a pen stroke or arrow. With markup selected it changes that markup (one undo step); with
|
|
15
|
+
nothing selected it sets the look of the next markup drawn.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const selectedNotes = (): Note[] => {
|
|
19
|
+
const ids = new Set(ed.sel.filter(s => s.type === 'note').map(s => s.id));
|
|
20
|
+
return ids.size ? SK().notes.filter(n => ids.has(n.id)) : [];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export interface MarkupTarget {
|
|
24
|
+
/** the selected markup's look (the first of them), or new markup's */
|
|
25
|
+
color: string; width: number; style: NoteStyle; startCap?: NoteCap; endCap?: NoteCap;
|
|
26
|
+
/** what applies: width (not for notes alone), the ends (pen strokes and arrows only) */
|
|
27
|
+
widthApplies: boolean; ends: boolean;
|
|
28
|
+
/** text size (inches on the drawing): for selected text notes, or new ones while the Note tool is on */
|
|
29
|
+
textIn?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** What the panel shows and edits. */
|
|
33
|
+
export function markupTarget(): MarkupTarget {
|
|
34
|
+
const notes = selectedNotes();
|
|
35
|
+
if (!notes.length) return { color: ed.noteColor, width: ed.noteWidth, style: ed.noteStyle, widthApplies: true, ends: false, textIn: ed.tool === 'text' ? ed.noteTextIn : undefined };
|
|
36
|
+
const look = noteLook(notes.find(n => n.type !== 'text') ?? notes[0]);
|
|
37
|
+
const ends = notes.every(hasEnds);
|
|
38
|
+
return {
|
|
39
|
+
color: notes[0].color, width: look.width, style: look.style,
|
|
40
|
+
startCap: ends ? look.startCap : undefined, endCap: ends ? look.endCap : undefined,
|
|
41
|
+
widthApplies: notes.some(n => n.type !== 'text'), ends,
|
|
42
|
+
textIn: notes.find(n => n.type === 'text') ? noteTextIn(notes.find(n => n.type === 'text')!) : undefined,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Change the selected markup (one undo step), or remember it for new markup. */
|
|
47
|
+
function apply(onNote: (n: Note) => void, onDefault?: () => void): void {
|
|
48
|
+
const notes = selectedNotes();
|
|
49
|
+
if (notes.length) { pushHistory(); notes.forEach(onNote); afterEdit(); }
|
|
50
|
+
else onDefault?.();
|
|
15
51
|
updateRail(); updateCtx();
|
|
16
52
|
}
|
|
53
|
+
|
|
54
|
+
/** Pick the markup color: selected markup changes to it, or (nothing selected) new markup uses it. */
|
|
55
|
+
export function setNoteColor(c: string): void {
|
|
56
|
+
// (recolouring one thing isn't choosing the colour to draw with next: a blue leader left the pen blue)
|
|
57
|
+
apply(n => { n.color = c; }, () => { ed.noteColor = c; });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The stroke width (not for notes, which have no stroke of their own). */
|
|
61
|
+
export function setNoteWidth(w: number): void {
|
|
62
|
+
apply(n => { if (n.type === 'text') return; if (w === (n.type === 'cloud' ? 2.5 : DEFAULT_NOTE_WIDTH)) delete n.width; else n.width = w; },
|
|
63
|
+
() => { ed.noteWidth = w; });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A text note's size on the drawing (inches): selected notes change to it, and new ones use it. */
|
|
67
|
+
export function setNoteTextSize(inches: number): void {
|
|
68
|
+
apply(n => { if (n.type !== 'text') return; delete n.size; if (inches === NOTE_TEXT_IN) delete n.ts; else n.ts = inches; },
|
|
69
|
+
() => { ed.noteTextIn = inches; });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Solid, dashed or dotted. */
|
|
73
|
+
export function setNoteStyle(st: NoteStyle): void {
|
|
74
|
+
apply(n => { if (st === 'solid') delete n.style; else n.style = st; }, () => { ed.noteStyle = st; });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** How a pen stroke's, arrow's or leader's start or end finishes (its kind's own way is left unset). */
|
|
78
|
+
export function setNoteCap(which: 'start' | 'end', cap: NoteCap): void {
|
|
79
|
+
apply(n => {
|
|
80
|
+
if (!hasEnds(n)) return;
|
|
81
|
+
const own = noteLook({ ...n, startCap: undefined, cap: undefined });
|
|
82
|
+
if (which === 'start') { if (cap === own.startCap) delete n.startCap; else n.startCap = cap; }
|
|
83
|
+
else if (cap === own.endCap) delete n.cap;
|
|
84
|
+
else n.cap = cap;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Edit a note's text: the text field opens over it with its text (Enter or leaving the field keeps it, Esc cancels). */
|
|
89
|
+
export function editNoteText(id: string): void {
|
|
90
|
+
const n = SK().notes.find(x => x.id === id);
|
|
91
|
+
if (!n || n.type !== 'text') return;
|
|
92
|
+
const b = noteTextBox(mainT(), n);
|
|
93
|
+
showNoteInput(b.x, b.mid, txt => {
|
|
94
|
+
const cur = SK().notes.find(x => x.id === id);
|
|
95
|
+
if (!cur || !txt || txt === cur.text) return;
|
|
96
|
+
pushHistory();
|
|
97
|
+
cur.text = txt;
|
|
98
|
+
afterEdit();
|
|
99
|
+
}, n.text);
|
|
100
|
+
}
|
package/src/editor/mode.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ed, type Mode } from '../app/state';
|
|
2
2
|
import { requestDraw } from '../render/view';
|
|
3
3
|
import { cancelTool } from '../tools/registry';
|
|
4
|
+
import { elevOpen, WALL_ICON, WALL_RAIL } from './elevation';
|
|
4
5
|
import { refreshModes, updateCtx, updateHint, updateRail } from './ports';
|
|
5
6
|
|
|
6
7
|
/** Switch between Draft and Annotate: each has its own tools, and the selection starts empty. */
|
|
@@ -17,23 +18,98 @@ export function setMode(m: Mode): void {
|
|
|
17
18
|
/** New lines become construction lines (guides that never make spaces), or regular lines again. */
|
|
18
19
|
export function toggleConstr(): void {
|
|
19
20
|
ed.drawConstr = !ed.drawConstr;
|
|
20
|
-
|
|
21
|
+
// (a wall is never a guide)
|
|
22
|
+
if (ed.drawConstr) ed.drawWalls = false;
|
|
23
|
+
toggled();
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
/**
|
|
24
|
-
export
|
|
26
|
+
/** What's drawn — lines, rectangles, circles, arcs, Draw to shape — becomes walls, or lines again. */
|
|
27
|
+
export function toggleWalls(): void {
|
|
28
|
+
ed.drawWalls = !ed.drawWalls;
|
|
29
|
+
if (ed.drawWalls) ed.drawConstr = false;
|
|
30
|
+
toggled();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A toggle flipped: the rail shows it, and a shape being drawn (and the hint) shows what it'll now make. */
|
|
34
|
+
function toggled(): void {
|
|
35
|
+
updateRail(); updateHint(); requestDraw();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Whether what's drawn now is walls: the Walls toggle, except in the wall view (what's drawn on a wall's face never is). */
|
|
39
|
+
export const drawsWalls = (): boolean => ed.drawWalls && !elevOpen();
|
|
40
|
+
|
|
41
|
+
/** The rail's toggles: whether each is on, and flipping it. */
|
|
42
|
+
export const TOGGLES: Record<string, { on: () => boolean; flip: () => void }> = {
|
|
43
|
+
wall: { on: () => ed.drawWalls, flip: toggleWalls },
|
|
44
|
+
constr: { on: () => ed.drawConstr, flip: toggleConstr },
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* A tool rail entry: [tool or action id, label, shortcut, kind], or '|' for a divider. A 'group' holds several tools in
|
|
49
|
+
* one place (see SHAPE_TOOLS).
|
|
50
|
+
*/
|
|
51
|
+
export type RailItem = '|' | [id: string, label: string, key: string, kind?: 'toggle' | 'act' | 'group'];
|
|
52
|
+
|
|
53
|
+
/** The tool rail now: the wall view's while a wall is open, otherwise the current mode's (the 3D tab has none). */
|
|
54
|
+
export const railItems = (): RailItem[] => (elevOpen() ? WALL_RAIL : RAIL[ed.mode]);
|
|
55
|
+
/** The icon for a rail item. */
|
|
56
|
+
export const railIcon = (id: string): string => WALL_ICON[id] ?? id;
|
|
25
57
|
|
|
26
58
|
/** The tool rail for each mode. 'toggle' items switch something on and off; 'act' items run an action. */
|
|
27
59
|
export const RAIL: Record<Mode, RailItem[]> = {
|
|
28
60
|
draft: [
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
['
|
|
61
|
+
// drawing; then the building (walls, what goes in them, spaces); then measuring and guides
|
|
62
|
+
['select', 'Select', 'V'], ['line', 'Line', 'L'], ['freehand', 'Draw to shape', 'S'], ['rect', 'Rectangle', 'R'], ['circle', 'Circle', 'C'], ['arc', 'Arc', 'A'], '|',
|
|
63
|
+
['door', 'Door', 'B'], ['window', 'Window', 'N'], ['room', 'Space', 'M'], '|',
|
|
64
|
+
// Trim (T) and Offset (O) stay on the keyboard; Offset is also in the bar for a selected line.
|
|
65
|
+
// The toggles together: what's drawn is walls, or guides (one or the other, never both)
|
|
66
|
+
['dim', 'Dimension', 'D'], ['wall', 'Walls', 'W', 'toggle'], ['constr', 'Construction lines', 'X', 'toggle'], '|',
|
|
32
67
|
['undo', 'Undo', '⌘Z', 'act'], ['redo', 'Redo', '⇧⌘Z', 'act'],
|
|
33
68
|
],
|
|
34
69
|
annotate: [
|
|
35
|
-
|
|
70
|
+
// the markup shapes, Arrow first (on a phone they share Arrow's place: MARKS_GROUP)
|
|
71
|
+
['aselect', 'Select markup', 'V'], ['arrow', 'Arrow', 'K'], ['mline', 'Line', 'L'], ['mrect', 'Rectangle', 'R'], ['mtri', 'Triangle', ''],
|
|
72
|
+
['mcircle', 'Circle', 'C'], ['pen', 'Pen', 'P'], ['cloud', 'Revision cloud', 'Q'], ['text', 'Note', 'N'], ['image', 'Add photo or plan', '', 'act'], '|',
|
|
36
73
|
['color', 'Markup color', '', 'act'], '|',
|
|
37
74
|
['undo', 'Undo', '⌘Z', 'act'], ['redo', 'Redo', '⇧⌘Z', 'act'],
|
|
38
75
|
],
|
|
39
76
|
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Tools that share one place on a phone's rail, which has no room for each: a tap draws with the one used last (the
|
|
80
|
+
* place shows it), and a long press opens a menu of them all (react/ShapeMenu.tsx) — Draft's Line, Rectangle, Circle
|
|
81
|
+
* and Arc, and Annotate's markup shapes. A tablet's or a browser's rail shows every one in its own place.
|
|
82
|
+
*/
|
|
83
|
+
export const SHAPE_TOOLS = ['line', 'rect', 'circle', 'arc'];
|
|
84
|
+
export const SHAPES_GROUP = 'shapes';
|
|
85
|
+
export const MARK_TOOLS = ['arrow', 'mline', 'mrect', 'mtri', 'mcircle'];
|
|
86
|
+
export const MARKS_GROUP = 'marks';
|
|
87
|
+
|
|
88
|
+
export interface ToolGroup { tools: string[]; label: string; mode: Mode; phoneOnly: boolean }
|
|
89
|
+
export const TOOL_GROUPS: Record<string, ToolGroup> = {
|
|
90
|
+
[SHAPES_GROUP]: { tools: SHAPE_TOOLS, label: 'Shapes', mode: 'draft', phoneOnly: true },
|
|
91
|
+
[MARKS_GROUP]: { tools: MARK_TOOLS, label: 'Markup shapes', mode: 'annotate', phoneOnly: true },
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/** The group a tool shares its place with on this rail, if any. */
|
|
95
|
+
export const groupOf = (tool: string, phone: boolean): string | null =>
|
|
96
|
+
Object.keys(TOOL_GROUPS).find(g => TOOL_GROUPS[g].tools.includes(tool) && (phone || !TOOL_GROUPS[g].phoneOnly)) ?? null;
|
|
97
|
+
|
|
98
|
+
/** A rail with each group's tools folded into one place, where the first of them was; the rest as it was. */
|
|
99
|
+
export function groupTools(items: RailItem[], phone: boolean): RailItem[] {
|
|
100
|
+
if (!items.some(it => it !== '|' && groupOf(it[0], phone))) return items;
|
|
101
|
+
const out: RailItem[] = [];
|
|
102
|
+
for (const it of items) {
|
|
103
|
+
const g = it === '|' ? null : groupOf(it[0], phone);
|
|
104
|
+
if (!g) out.push(it);
|
|
105
|
+
else if (!out.some(o => o !== '|' && o[0] === g)) out.push([g, TOOL_GROUPS[g].label, '', 'group']);
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** A tool's name on the rail ('Line', 'Rectangle'…). */
|
|
111
|
+
export const toolLabel = (id: string): string => {
|
|
112
|
+
const it = [...RAIL.draft, ...RAIL.annotate].find(r => r !== '|' && r[0] === id);
|
|
113
|
+
return it && it !== '|' ? it[1] : id;
|
|
114
|
+
};
|
|
115
|
+
|
package/src/editor/ports.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Constraint } from '../model/types';
|
|
1
|
+
import type { Constraint, LinkTarget } from '../model/types';
|
|
2
|
+
import type { MeasurementKind } from './links';
|
|
2
3
|
|
|
3
4
|
/*
|
|
4
5
|
The editor's ports: everything the drawing logic (edits, tools, view) asks of the user interface. The logic calls
|
|
@@ -33,9 +34,18 @@ export interface KeypadSpec {
|
|
|
33
34
|
laser?: boolean;
|
|
34
35
|
/** extra buttons, read again after each press so their state can change */
|
|
35
36
|
extras?: () => KeypadExtra[];
|
|
36
|
-
|
|
37
|
+
/** the value; `capture`, a laser reading's record (laserReading's), when it's that reading applied as it came in */
|
|
38
|
+
onDone?: (v: number, capture?: unknown) => void;
|
|
39
|
+
/**
|
|
40
|
+
* A line's length, or a dimension's angle: the job's measurements are offered with the keys (a page swiped to on a
|
|
41
|
+
* touch screen, Select From List on a desktop), those of `kind` pickable — the one picked handed to `pick` (the
|
|
42
|
+
* keypad's for closing, and the value for setting from it, linked to it).
|
|
43
|
+
*/
|
|
44
|
+
measurements?: { kind: MeasurementKind; pick: (t: LinkTarget) => void };
|
|
37
45
|
/** called whenever the keypad closes (applied or cancelled), e.g. to clear a preview */
|
|
38
46
|
onClose?: () => void;
|
|
47
|
+
/** no backdrop: the drawing stays live behind it (a tool can take the next tap, and close it) */
|
|
48
|
+
modeless?: boolean;
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
export interface ConflictSpec {
|
|
@@ -50,14 +60,14 @@ export interface ConflictSpec {
|
|
|
50
60
|
onResolved?: (() => void) | null;
|
|
51
61
|
}
|
|
52
62
|
|
|
53
|
-
export type DrawerTab = 'pages' | '
|
|
63
|
+
export type DrawerTab = 'pages' | 'rooms';
|
|
54
64
|
|
|
55
65
|
/**
|
|
56
66
|
* Parts of the interface that show editor state and must re-read it after a change:
|
|
57
67
|
* the contextual bar, the hint, the definition-state pill, the tool rail, the mode tabs (and the rail's tool set),
|
|
58
68
|
* the project drawer, the wall elevation view, the top bar's view toggles, the keypad's display, and the laser tray.
|
|
59
69
|
*/
|
|
60
|
-
export type ChromePart = 'ctx' | 'hint' | 'status' | 'rail' | 'mode' | 'drawer' | 'elevation' | 'topbar' | 'keypad' | 'tray';
|
|
70
|
+
export type ChromePart = 'ctx' | 'hint' | 'status' | 'rail' | 'mode' | 'drawer' | 'elevation' | 'topbar' | 'keypad' | 'tray' | 'confirm';
|
|
61
71
|
|
|
62
72
|
export interface EditorPorts {
|
|
63
73
|
refresh(part: ChromePart): void;
|
|
@@ -68,16 +78,28 @@ export interface EditorPorts {
|
|
|
68
78
|
keypadTyped(): number | null;
|
|
69
79
|
showConflict(spec: ConflictSpec): void;
|
|
70
80
|
openDrawer(tab?: DrawerTab): void;
|
|
71
|
-
|
|
81
|
+
/** the text field for a note at a screen point; `initial`: the text it starts with (editing a note) */
|
|
82
|
+
showNoteInput(sx: number, sy: number, cb: (text: string) => void, initial?: string): void;
|
|
72
83
|
hideNoteInput(): void;
|
|
73
84
|
/** the markup color picker, next to `anchor` (the button that asked for it, if any) */
|
|
74
85
|
openPalette(anchor?: unknown): void;
|
|
86
|
+
/** the selected image's opacity slider, above the bar (its button again closes it) */
|
|
87
|
+
toggleImageOpacity(anchor?: unknown): void;
|
|
88
|
+
/** the selected image's rotation slider, above the bar (its button again closes it) */
|
|
89
|
+
toggleImageRotation(anchor?: unknown): void;
|
|
90
|
+
/** the link tiles' size slider, above the bar (its button again closes it) */
|
|
91
|
+
toggleTileSize(anchor?: unknown): void;
|
|
75
92
|
/** let the user type a new name for a space */
|
|
76
93
|
editRoomName(roomId: string): void;
|
|
77
94
|
/** the mouse cursor over the plan (or the wall elevation), as a CSS cursor name ('' = the default); ignored without a mouse */
|
|
78
95
|
setCursor(cursor: string, where?: 'plan' | 'elevation'): void;
|
|
79
96
|
/** close any open popover (the settings menu, the color palette); true if one was open */
|
|
80
97
|
dismissPopovers(): boolean;
|
|
98
|
+
/**
|
|
99
|
+
* a gesture on the drawing started moving — a stroke, a drag, a pan or a pinch (never a tap) — or its last pointer
|
|
100
|
+
* lifted: the interface can step out of the way meanwhile
|
|
101
|
+
*/
|
|
102
|
+
interacting(on: boolean): void;
|
|
81
103
|
/** close the conflict sheet; true if it was open */
|
|
82
104
|
dismissConflict(): boolean;
|
|
83
105
|
/** save now (⌘S) */
|
|
@@ -98,9 +120,13 @@ export const basePorts = (): EditorPorts => ({
|
|
|
98
120
|
showNoteInput() {},
|
|
99
121
|
hideNoteInput() {},
|
|
100
122
|
openPalette() {},
|
|
123
|
+
toggleImageOpacity() {},
|
|
124
|
+
toggleImageRotation() {},
|
|
125
|
+
toggleTileSize() {},
|
|
101
126
|
editRoomName() {},
|
|
102
127
|
setCursor() {},
|
|
103
128
|
dismissPopovers: () => false,
|
|
129
|
+
interacting() {},
|
|
104
130
|
dismissConflict: () => false,
|
|
105
131
|
save() {},
|
|
106
132
|
saveFile() {},
|
|
@@ -120,12 +146,16 @@ export const closeKeypad = (): void => ports.closeKeypad();
|
|
|
120
146
|
export const kpTyped = (): number | null => ports.keypadTyped();
|
|
121
147
|
export const showConflict = (spec: ConflictSpec): void => ports.showConflict(spec);
|
|
122
148
|
export const openDrawer = (tab?: DrawerTab): void => ports.openDrawer(tab);
|
|
123
|
-
export const showNoteInput = (sx: number, sy: number, cb: (text: string) => void): void => ports.showNoteInput(sx, sy, cb);
|
|
149
|
+
export const showNoteInput = (sx: number, sy: number, cb: (text: string) => void, initial?: string): void => ports.showNoteInput(sx, sy, cb, initial);
|
|
124
150
|
export const hideNoteInput = (): void => ports.hideNoteInput();
|
|
125
151
|
export const openPalette = (anchor?: unknown): void => ports.openPalette(anchor);
|
|
152
|
+
export const toggleImageOpacity = (anchor?: unknown): void => ports.toggleImageOpacity(anchor);
|
|
153
|
+
export const toggleImageRotation = (anchor?: unknown): void => ports.toggleImageRotation(anchor);
|
|
154
|
+
export const toggleTileSize = (anchor?: unknown): void => ports.toggleTileSize(anchor);
|
|
126
155
|
export const editRoomName = (roomId: string): void => ports.editRoomName(roomId);
|
|
127
156
|
export const setCursor = (cursor: string, where: 'plan' | 'elevation' = 'plan'): void => ports.setCursor(cursor, where);
|
|
128
157
|
export const dismissPopovers = (): boolean => ports.dismissPopovers();
|
|
158
|
+
export const interacting = (on: boolean): void => ports.interacting(on);
|
|
129
159
|
export const dismissConflict = (): boolean => ports.dismissConflict();
|
|
130
160
|
export const saveNowPort = (): void => ports.save();
|
|
131
161
|
export const saveFile = (name: string, mime: string, data: string): void => ports.saveFile(name, mime, data);
|
|
@@ -140,3 +170,4 @@ export const renderElevation = (): void => ports.refresh('elevation');
|
|
|
140
170
|
export const updateTopBar = (): void => ports.refresh('topbar');
|
|
141
171
|
export const refreshKeypad = (): void => ports.refresh('keypad');
|
|
142
172
|
export const refreshTray = (): void => ports.refresh('tray');
|
|
173
|
+
export const refreshConfirm = (): void => ports.refresh('confirm');
|