@reekon-tools/boldr-cad 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -9
- package/dist/app/edit.d.ts +12 -1
- package/dist/app/edit.js +96 -11
- package/dist/app/edit.js.map +1 -1
- package/dist/app/state.d.ts +31 -4
- package/dist/app/state.js +31 -2
- package/dist/app/state.js.map +1 -1
- package/dist/data/memory.d.ts +5 -0
- package/dist/data/memory.js +2 -2
- package/dist/data/memory.js.map +1 -1
- package/dist/data/provider.d.ts +19 -7
- package/dist/data/sync.d.ts +7 -2
- package/dist/data/sync.js +115 -64
- package/dist/data/sync.js.map +1 -1
- package/dist/editor/actions.d.ts +1 -2
- package/dist/editor/actions.js +391 -113
- package/dist/editor/actions.js.map +1 -1
- package/dist/editor/confirm.d.ts +23 -0
- package/dist/editor/confirm.js +35 -0
- package/dist/editor/confirm.js.map +1 -0
- package/dist/editor/elevation.d.ts +191 -90
- package/dist/editor/elevation.js +812 -616
- package/dist/editor/elevation.js.map +1 -1
- package/dist/editor/face.d.ts +73 -0
- package/dist/editor/face.js +269 -0
- package/dist/editor/face.js.map +1 -0
- package/dist/editor/files.d.ts +6 -0
- package/dist/editor/files.js +50 -4
- package/dist/editor/files.js.map +1 -1
- package/dist/editor/gestures.d.ts +18 -3
- package/dist/editor/gestures.js +145 -30
- package/dist/editor/gestures.js.map +1 -1
- package/dist/editor/keypad.d.ts +19 -4
- package/dist/editor/keypad.js +39 -9
- package/dist/editor/keypad.js.map +1 -1
- package/dist/editor/keys.js +36 -4
- package/dist/editor/keys.js.map +1 -1
- package/dist/editor/laser.d.ts +20 -4
- package/dist/editor/laser.js +104 -21
- package/dist/editor/laser.js.map +1 -1
- package/dist/editor/linkActions.d.ts +49 -0
- package/dist/editor/linkActions.js +210 -0
- package/dist/editor/linkActions.js.map +1 -0
- package/dist/editor/linkFollow.d.ts +2 -0
- package/dist/editor/linkFollow.js +53 -0
- package/dist/editor/linkFollow.js.map +1 -0
- package/dist/editor/linkWrite.d.ts +13 -0
- package/dist/editor/linkWrite.js +84 -0
- package/dist/editor/linkWrite.js.map +1 -0
- package/dist/editor/links.d.ts +84 -0
- package/dist/editor/links.js +130 -0
- package/dist/editor/links.js.map +1 -0
- package/dist/editor/markup.d.ts +29 -3
- package/dist/editor/markup.js +95 -10
- package/dist/editor/markup.js.map +1 -1
- package/dist/editor/mode.d.ts +40 -2
- package/dist/editor/mode.js +71 -4
- package/dist/editor/mode.js.map +1 -1
- package/dist/editor/ports.d.ts +36 -6
- package/dist/editor/ports.js +10 -1
- package/dist/editor/ports.js.map +1 -1
- package/dist/editor/project.d.ts +38 -6
- package/dist/editor/project.js +167 -27
- package/dist/editor/project.js.map +1 -1
- package/dist/editor/selection.d.ts +10 -0
- package/dist/editor/selection.js +62 -8
- package/dist/editor/selection.js.map +1 -1
- package/dist/editor/status.js +3 -0
- package/dist/editor/status.js.map +1 -1
- package/dist/editor/tooltips.d.ts +5 -0
- package/dist/editor/tooltips.js +48 -0
- package/dist/editor/tooltips.js.map +1 -0
- package/dist/editor/view3d.d.ts +17 -0
- package/dist/editor/view3d.js +135 -0
- package/dist/editor/view3d.js.map +1 -0
- package/dist/editor/view3dState.d.ts +39 -0
- package/dist/editor/view3dState.js +44 -0
- package/dist/editor/view3dState.js.map +1 -0
- package/dist/export/dxf.js +1 -1
- package/dist/export/dxf.js.map +1 -1
- package/dist/export/pdf.js +17 -12
- package/dist/export/pdf.js.map +1 -1
- package/dist/export/plan.d.ts +23 -6
- package/dist/export/plan.js +127 -44
- package/dist/export/plan.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/model/constraints.js +15 -4
- package/dist/model/constraints.js.map +1 -1
- package/dist/model/demo.js +3 -3
- package/dist/model/demo.js.map +1 -1
- package/dist/model/elevation.d.ts +6 -9
- package/dist/model/elevation.js +5 -13
- package/dist/model/elevation.js.map +1 -1
- package/dist/model/faces.d.ts +58 -0
- package/dist/model/faces.js +197 -0
- package/dist/model/faces.js.map +1 -0
- package/dist/model/facesolve.d.ts +72 -0
- package/dist/model/facesolve.js +369 -0
- package/dist/model/facesolve.js.map +1 -0
- package/dist/model/history.d.ts +3 -2
- package/dist/model/history.js +17 -11
- package/dist/model/history.js.map +1 -1
- package/dist/model/images.d.ts +18 -0
- package/dist/model/images.js +26 -0
- package/dist/model/images.js.map +1 -0
- package/dist/model/links.d.ts +46 -0
- package/dist/model/links.js +73 -0
- package/dist/model/links.js.map +1 -0
- package/dist/model/markup.d.ts +46 -0
- package/dist/model/markup.js +71 -0
- package/dist/model/markup.js.map +1 -0
- package/dist/model/measurement.d.ts +11 -0
- package/dist/model/measurement.js +24 -0
- package/dist/model/measurement.js.map +1 -0
- package/dist/model/openings.d.ts +4 -0
- package/dist/model/openings.js +4 -0
- package/dist/model/openings.js.map +1 -1
- package/dist/model/planGeom.d.ts +72 -2
- package/dist/model/planGeom.js +184 -10
- package/dist/model/planGeom.js.map +1 -1
- package/dist/model/project.js +3 -2
- package/dist/model/project.js.map +1 -1
- package/dist/model/projectfile.js +2 -1
- package/dist/model/projectfile.js.map +1 -1
- package/dist/model/rooms.js +2 -2
- package/dist/model/rooms.js.map +1 -1
- package/dist/model/settings.d.ts +6 -0
- package/dist/model/settings.js +2 -2
- package/dist/model/settings.js.map +1 -1
- package/dist/model/sketch.d.ts +14 -0
- package/dist/model/sketch.js +143 -24
- package/dist/model/sketch.js.map +1 -1
- package/dist/model/smooth.d.ts +6 -0
- package/dist/model/smooth.js +52 -0
- package/dist/model/smooth.js.map +1 -0
- package/dist/model/store.d.ts +12 -0
- package/dist/model/store.js +35 -3
- package/dist/model/store.js.map +1 -1
- package/dist/model/types.d.ts +173 -8
- package/dist/model/wallfaces.d.ts +32 -0
- package/dist/model/wallfaces.js +133 -20
- package/dist/model/wallfaces.js.map +1 -1
- package/dist/model/wallshape.d.ts +15 -0
- package/dist/model/wallshape.js +89 -0
- package/dist/model/wallshape.js.map +1 -0
- package/dist/react/BottomBar.d.ts +9 -5
- package/dist/react/BottomBar.js +213 -24
- package/dist/react/BottomBar.js.map +1 -1
- package/dist/react/Buttons.d.ts +13 -2
- package/dist/react/Buttons.js +37 -5
- package/dist/react/Buttons.js.map +1 -1
- package/dist/react/CadEditor.d.ts +46 -3
- package/dist/react/CadEditor.js +77 -20
- package/dist/react/CadEditor.js.map +1 -1
- package/dist/react/ConfirmSheet.d.ts +4 -0
- package/dist/react/ConfirmSheet.js +31 -0
- package/dist/react/ConfirmSheet.js.map +1 -0
- package/dist/react/ConflictSheet.d.ts +2 -1
- package/dist/react/ConflictSheet.js +16 -8
- package/dist/react/ConflictSheet.js.map +1 -1
- package/dist/react/Drawer.d.ts +8 -2
- package/dist/react/Drawer.js +167 -38
- package/dist/react/Drawer.js.map +1 -1
- package/dist/react/Duck.d.ts +13 -0
- package/dist/react/Duck.js +45 -0
- package/dist/react/Duck.js.map +1 -0
- package/dist/react/Empty3D.d.ts +2 -0
- package/dist/react/Empty3D.js +39 -0
- package/dist/react/Empty3D.js.map +1 -0
- package/dist/react/FitScroll.d.ts +6 -0
- package/dist/react/FitScroll.js +14 -0
- package/dist/react/FitScroll.js.map +1 -0
- package/dist/react/Icon.native.d.ts +6 -0
- package/dist/react/Icon.native.js +4 -0
- package/dist/react/Icon.native.js.map +1 -1
- package/dist/react/Icon.web.d.ts +6 -0
- package/dist/react/Icon.web.js +4 -0
- package/dist/react/Icon.web.js.map +1 -1
- package/dist/react/KeypadSheet.d.ts +9 -2
- package/dist/react/KeypadSheet.js +174 -42
- package/dist/react/KeypadSheet.js.map +1 -1
- package/dist/react/LaserTray.js +8 -5
- package/dist/react/LaserTray.js.map +1 -1
- package/dist/react/MarkupPanel.d.ts +8 -0
- package/dist/react/MarkupPanel.js +67 -0
- package/dist/react/MarkupPanel.js.map +1 -0
- package/dist/react/PageThumb.native.d.ts +7 -2
- package/dist/react/PageThumb.native.js +19 -7
- package/dist/react/PageThumb.native.js.map +1 -1
- package/dist/react/PageThumb.web.d.ts +3 -2
- package/dist/react/PageThumb.web.js +4 -4
- package/dist/react/PageThumb.web.js.map +1 -1
- package/dist/react/PlanCanvas.native.d.ts +1 -1
- package/dist/react/PlanCanvas.native.js +134 -36
- package/dist/react/PlanCanvas.native.js.map +1 -1
- package/dist/react/PlanCanvas.web.js +16 -5
- package/dist/react/PlanCanvas.web.js.map +1 -1
- package/dist/react/Popovers.d.ts +12 -6
- package/dist/react/Popovers.js +53 -26
- package/dist/react/Popovers.js.map +1 -1
- package/dist/react/Rail.d.ts +24 -0
- package/dist/react/Rail.js +324 -34
- package/dist/react/Rail.js.map +1 -1
- package/dist/react/ShapeMenu.d.ts +7 -0
- package/dist/react/ShapeMenu.js +58 -0
- package/dist/react/ShapeMenu.js.map +1 -0
- package/dist/react/Sheet.d.ts +14 -4
- package/dist/react/Sheet.js +54 -9
- package/dist/react/Sheet.js.map +1 -1
- package/dist/react/Slider.d.ts +18 -0
- package/dist/react/Slider.js +27 -0
- package/dist/react/Slider.js.map +1 -0
- package/dist/react/Toast.js +14 -6
- package/dist/react/Toast.js.map +1 -1
- package/dist/react/ToolTip.d.ts +7 -0
- package/dist/react/ToolTip.js +43 -0
- package/dist/react/ToolTip.js.map +1 -0
- package/dist/react/TopBar.d.ts +6 -3
- package/dist/react/TopBar.js +48 -17
- package/dist/react/TopBar.js.map +1 -1
- package/dist/react/Tween.d.ts +7 -0
- package/dist/react/Tween.js +26 -0
- package/dist/react/Tween.js.map +1 -0
- package/dist/react/Tween.native.d.ts +6 -0
- package/dist/react/Tween.native.js +43 -0
- package/dist/react/Tween.native.js.map +1 -0
- package/dist/react/View3D.native.d.ts +4 -0
- package/dist/react/View3D.native.js +103 -0
- package/dist/react/View3D.native.js.map +1 -0
- package/dist/react/View3D.web.d.ts +4 -0
- package/dist/react/View3D.web.js +151 -0
- package/dist/react/View3D.web.js.map +1 -0
- package/dist/react/View3DHost.d.ts +143 -0
- package/dist/react/View3DHost.js +377 -0
- package/dist/react/View3DHost.js.map +1 -0
- package/dist/react/corners.d.ts +7 -0
- package/dist/react/corners.js +51 -0
- package/dist/react/corners.js.map +1 -0
- package/dist/react/easing.d.ts +15 -0
- package/dist/react/easing.js +10 -0
- package/dist/react/easing.js.map +1 -0
- package/dist/react/host.d.ts +7 -2
- package/dist/react/host.js +8 -3
- package/dist/react/host.js.map +1 -1
- package/dist/react/hostKeypad.d.ts +56 -0
- package/dist/react/hostKeypad.js +5 -0
- package/dist/react/hostKeypad.js.map +1 -0
- package/dist/react/motion.d.ts +77 -0
- package/dist/react/motion.js +173 -0
- package/dist/react/motion.js.map +1 -0
- package/dist/react/ports.js +90 -8
- package/dist/react/ports.js.map +1 -1
- package/dist/react/saving.d.ts +15 -0
- package/dist/react/saving.js +76 -0
- package/dist/react/saving.js.map +1 -0
- package/dist/react/store.d.ts +38 -1
- package/dist/react/store.js +31 -1
- package/dist/react/store.js.map +1 -1
- package/dist/react/theme.d.ts +10 -0
- package/dist/react/theme.js +8 -0
- package/dist/react/theme.js.map +1 -1
- package/dist/react/topRow.d.ts +22 -0
- package/dist/react/topRow.js +61 -0
- package/dist/react/topRow.js.map +1 -0
- package/dist/react/touch.d.ts +6 -0
- package/dist/react/touch.js +9 -0
- package/dist/react/touch.js.map +1 -0
- package/dist/render/ctx.d.ts +8 -0
- package/dist/render/draw.d.ts +33 -1
- package/dist/render/draw.js +593 -215
- package/dist/render/draw.js.map +1 -1
- package/dist/render/elevation.d.ts +65 -43
- package/dist/render/elevation.js +414 -140
- package/dist/render/elevation.js.map +1 -1
- package/dist/render/figmaIcons.d.ts +12 -0
- package/dist/render/figmaIcons.js +21 -0
- package/dist/render/figmaIcons.js.map +1 -0
- package/dist/render/hit.d.ts +22 -2
- package/dist/render/hit.js +168 -25
- package/dist/render/hit.js.map +1 -1
- package/dist/render/icons.js +69 -10
- package/dist/render/icons.js.map +1 -1
- package/dist/render/linkTile.d.ts +15 -0
- package/dist/render/linkTile.js +369 -0
- package/dist/render/linkTile.js.map +1 -0
- package/dist/render/markup.d.ts +11 -0
- package/dist/render/markup.js +112 -0
- package/dist/render/markup.js.map +1 -0
- package/dist/render/navigate.d.ts +88 -0
- package/dist/render/navigate.js +100 -0
- package/dist/render/navigate.js.map +1 -0
- package/dist/render/snap.d.ts +20 -1
- package/dist/render/snap.js +118 -25
- package/dist/render/snap.js.map +1 -1
- package/dist/render/squircle.d.ts +8 -0
- package/dist/render/squircle.js +71 -0
- package/dist/render/squircle.js.map +1 -0
- package/dist/render/view.d.ts +115 -2
- package/dist/render/view.js +244 -19
- package/dist/render/view.js.map +1 -1
- package/dist/skia/SkiaDrawCtx.d.ts +30 -2
- package/dist/skia/SkiaDrawCtx.js +198 -43
- package/dist/skia/SkiaDrawCtx.js.map +1 -1
- package/dist/skia/host.d.ts +18 -2
- package/dist/skia/host.js +46 -1
- package/dist/skia/host.js.map +1 -1
- package/dist/solver/equations.js +37 -2
- package/dist/solver/equations.js.map +1 -1
- package/dist/solver/gauss-newton.d.ts +17 -0
- package/dist/solver/gauss-newton.js +144 -36
- package/dist/solver/gauss-newton.js.map +1 -1
- package/dist/solver/linalg.d.ts +45 -2
- package/dist/solver/linalg.js +164 -10
- package/dist/solver/linalg.js.map +1 -1
- package/dist/solver/types.d.ts +5 -0
- package/dist/tools/annotate.d.ts +57 -1
- package/dist/tools/annotate.js +64 -43
- package/dist/tools/annotate.js.map +1 -1
- package/dist/tools/chamfer.d.ts +12 -0
- package/dist/tools/chamfer.js +114 -0
- package/dist/tools/chamfer.js.map +1 -0
- package/dist/tools/corner.d.ts +38 -0
- package/dist/tools/corner.js +99 -0
- package/dist/tools/corner.js.map +1 -0
- package/dist/tools/dim.d.ts +86 -9
- package/dist/tools/dim.js +581 -96
- package/dist/tools/dim.js.map +1 -1
- package/dist/tools/elevation.js +1 -1
- package/dist/tools/elevation.js.map +1 -1
- package/dist/tools/fillet.d.ts +9 -0
- package/dist/tools/fillet.js +86 -0
- package/dist/tools/fillet.js.map +1 -0
- package/dist/tools/freehand.d.ts +3 -2
- package/dist/tools/freehand.js +19 -12
- package/dist/tools/freehand.js.map +1 -1
- package/dist/tools/index.js +14 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/line.d.ts +8 -3
- package/dist/tools/line.js +66 -30
- package/dist/tools/line.js.map +1 -1
- package/dist/tools/modify.d.ts +13 -0
- package/dist/tools/modify.js +105 -25
- package/dist/tools/modify.js.map +1 -1
- package/dist/tools/openings.js +22 -15
- package/dist/tools/openings.js.map +1 -1
- package/dist/tools/registry.d.ts +8 -0
- package/dist/tools/registry.js +42 -2
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/select.d.ts +77 -3
- package/dist/tools/select.js +560 -74
- package/dist/tools/select.js.map +1 -1
- package/dist/tools/shapes.d.ts +14 -0
- package/dist/tools/shapes.js +154 -75
- package/dist/tools/shapes.js.map +1 -1
- package/dist/units/format.d.ts +10 -2
- package/dist/units/format.js +18 -3
- package/dist/units/format.js.map +1 -1
- package/dist/view3d/build.d.ts +35 -0
- package/dist/view3d/build.js +630 -0
- package/dist/view3d/build.js.map +1 -0
- package/dist/view3d/camera.d.ts +60 -0
- package/dist/view3d/camera.js +84 -0
- package/dist/view3d/camera.js.map +1 -0
- package/dist/view3d/fatLines.d.ts +11 -0
- package/dist/view3d/fatLines.js +83 -0
- package/dist/view3d/fatLines.js.map +1 -0
- package/dist/view3d/lines.d.ts +17 -0
- package/dist/view3d/lines.js +271 -0
- package/dist/view3d/lines.js.map +1 -0
- package/dist/view3d/mesh.d.ts +51 -0
- package/dist/view3d/mesh.js +101 -0
- package/dist/view3d/mesh.js.map +1 -0
- package/dist/view3d/poly.d.ts +16 -0
- package/dist/view3d/poly.js +109 -0
- package/dist/view3d/poly.js.map +1 -0
- package/dist/view3d/viewer.d.ts +86 -0
- package/dist/view3d/viewer.js +350 -0
- package/dist/view3d/viewer.js.map +1 -0
- package/package.json +15 -2
- package/src/app/edit.ts +83 -12
- package/src/app/main.ts +11 -5
- package/src/app/state.ts +38 -5
- package/src/data/memory.ts +2 -2
- package/src/data/provider.ts +15 -7
- package/src/data/sync.ts +87 -50
- package/src/editor/actions.ts +292 -83
- package/src/editor/confirm.ts +58 -0
- package/src/editor/elevation.ts +698 -478
- package/src/editor/face.ts +227 -0
- package/src/editor/files.ts +40 -4
- package/src/editor/gestures.ts +147 -31
- package/src/editor/keypad.ts +43 -11
- package/src/editor/keys.ts +25 -5
- package/src/editor/laser.ts +98 -20
- package/src/editor/linkActions.ts +194 -0
- package/src/editor/linkFollow.ts +43 -0
- package/src/editor/linkWrite.ts +77 -0
- package/src/editor/links.ts +181 -0
- package/src/editor/markup.ts +92 -8
- package/src/editor/mode.ts +83 -7
- package/src/editor/ports.ts +37 -6
- package/src/editor/project.ts +155 -26
- package/src/editor/selection.ts +54 -8
- package/src/editor/status.ts +2 -0
- package/src/editor/tooltips.ts +47 -0
- package/src/editor/view3d.ts +119 -0
- package/src/editor/view3dState.ts +61 -0
- package/src/export/dxf.ts +1 -1
- package/src/export/pdf.ts +15 -7
- package/src/export/plan.ts +119 -35
- package/src/index.ts +5 -0
- package/src/input/keyboard.ts +2 -4
- package/src/model/constraints.ts +13 -4
- package/src/model/demo.ts +3 -3
- package/src/model/elevation.ts +6 -16
- package/src/model/faces.ts +153 -0
- package/src/model/facesolve.ts +322 -0
- package/src/model/history.ts +19 -11
- package/src/model/images.ts +35 -0
- package/src/model/links.ts +77 -0
- package/src/model/markup.ts +80 -0
- package/src/model/measurement.ts +26 -0
- package/src/model/openings.ts +6 -0
- package/src/model/planGeom.ts +174 -8
- package/src/model/project.ts +3 -2
- package/src/model/projectfile.ts +2 -1
- package/src/model/rooms.ts +1 -1
- package/src/model/settings.ts +8 -2
- package/src/model/sketch.ts +101 -14
- package/src/model/smooth.ts +51 -0
- package/src/model/store.ts +35 -4
- package/src/model/types.ts +170 -8
- package/src/model/wallfaces.ts +126 -17
- package/src/model/wallshape.ts +97 -0
- package/src/react/BottomBar.tsx +263 -46
- package/src/react/Buttons.tsx +50 -9
- package/src/react/CadEditor.tsx +133 -30
- package/src/react/ConfirmSheet.tsx +38 -0
- package/src/react/ConflictSheet.tsx +19 -12
- package/src/react/Drawer.tsx +213 -110
- package/src/react/Duck.tsx +50 -0
- package/src/react/Empty3D.tsx +32 -0
- package/src/react/FitScroll.tsx +19 -0
- package/src/react/Icon.native.tsx +5 -0
- package/src/react/Icon.web.tsx +5 -0
- package/src/react/KeypadSheet.tsx +253 -60
- package/src/react/LaserTray.tsx +12 -8
- package/src/react/MarkupPanel.tsx +150 -0
- package/src/react/PageThumb.native.tsx +18 -7
- package/src/react/PageThumb.web.tsx +4 -4
- package/src/react/PlanCanvas.native.tsx +151 -34
- package/src/react/PlanCanvas.web.tsx +15 -5
- package/src/react/Popovers.tsx +116 -51
- package/src/react/Rail.tsx +360 -27
- package/src/react/ShapeMenu.tsx +66 -0
- package/src/react/Sheet.tsx +56 -11
- package/src/react/Slider.tsx +48 -0
- package/src/react/Toast.tsx +16 -9
- package/src/react/ToolTip.tsx +49 -0
- package/src/react/TopBar.tsx +97 -39
- package/src/react/Tween.native.tsx +36 -0
- package/src/react/Tween.tsx +24 -0
- package/src/react/View3D.native.tsx +131 -0
- package/src/react/View3D.web.tsx +131 -0
- package/src/react/View3DHost.ts +367 -0
- package/src/react/corners.ts +52 -0
- package/src/react/easing.ts +14 -0
- package/src/react/host.ts +8 -3
- package/src/react/hostKeypad.ts +66 -0
- package/src/react/motion.tsx +239 -0
- package/src/react/ports.ts +78 -9
- package/src/react/saving.ts +80 -0
- package/src/react/store.ts +35 -3
- package/src/react/theme.ts +8 -0
- package/src/react/topRow.ts +71 -0
- package/src/react/touch.ts +10 -0
- package/src/render/ctx.ts +8 -0
- package/src/render/draw.ts +437 -175
- package/src/render/elevation.ts +349 -143
- package/src/render/figmaIcons.ts +22 -0
- package/src/render/hit.ts +140 -21
- package/src/render/icons.ts +69 -10
- package/src/render/linkTile.ts +297 -0
- package/src/render/markup.ts +91 -0
- package/src/render/navigate.ts +114 -0
- package/src/render/snap.ts +105 -22
- package/src/render/squircle.ts +73 -0
- package/src/render/view.ts +255 -19
- package/src/skia/SkiaDrawCtx.ts +163 -41
- package/src/skia/host.ts +46 -1
- package/src/solver/equations.ts +37 -2
- package/src/solver/gauss-newton.ts +125 -28
- package/src/solver/linalg.ts +141 -5
- package/src/solver/types.ts +5 -0
- package/src/styles.css +91 -42
- package/src/tools/annotate.ts +58 -29
- package/src/tools/chamfer.ts +103 -0
- package/src/tools/corner.ts +99 -0
- package/src/tools/dim.ts +524 -92
- package/src/tools/elevation.ts +1 -1
- package/src/tools/fillet.ts +82 -0
- package/src/tools/freehand.ts +20 -16
- package/src/tools/index.ts +17 -4
- package/src/tools/line.ts +59 -23
- package/src/tools/modify.ts +86 -24
- package/src/tools/openings.ts +8 -13
- package/src/tools/registry.ts +27 -2
- package/src/tools/select.ts +424 -75
- package/src/tools/shapes.ts +119 -58
- package/src/ui/ctx.ts +2 -1
- package/src/ui/domPorts.ts +23 -4
- package/src/ui/drawer.ts +6 -26
- package/src/ui/elevation.ts +10 -101
- package/src/ui/keypad.ts +8 -14
- package/src/ui/laser.ts +2 -2
- package/src/ui/menu.ts +63 -17
- package/src/ui/noteinput.ts +3 -3
- package/src/ui/opacity.ts +108 -0
- package/src/ui/palette.ts +67 -12
- package/src/ui/rail.ts +21 -7
- package/src/ui/topbar.ts +4 -14
- package/src/units/format.ts +18 -3
- package/src/view3d/build.ts +607 -0
- package/src/view3d/camera.ts +113 -0
- package/src/view3d/fatLines.ts +83 -0
- package/src/view3d/lines.ts +258 -0
- package/src/view3d/mesh.ts +111 -0
- package/src/view3d/poly.ts +104 -0
- package/src/view3d/viewer.ts +348 -0
- package/dist/react/ElevCanvas.native.d.ts +0 -3
- package/dist/react/ElevCanvas.native.js +0 -58
- package/dist/react/ElevCanvas.native.js.map +0 -1
- package/dist/react/ElevCanvas.web.d.ts +0 -4
- package/dist/react/ElevCanvas.web.js +0 -44
- package/dist/react/ElevCanvas.web.js.map +0 -1
- package/dist/react/ElevationSheet.d.ts +0 -2
- package/dist/react/ElevationSheet.js +0 -38
- package/dist/react/ElevationSheet.js.map +0 -1
- package/dist/render/elevationView.d.ts +0 -3
- package/dist/render/elevationView.js +0 -100
- package/dist/render/elevationView.js.map +0 -1
- package/src/react/ElevCanvas.native.tsx +0 -68
- package/src/react/ElevCanvas.web.tsx +0 -42
- package/src/react/ElevationSheet.tsx +0 -88
- package/src/render/elevationView.ts +0 -73
package/dist/editor/actions.js
CHANGED
|
@@ -1,28 +1,36 @@
|
|
|
1
|
-
import { afterEdit, solveSketch, tryAddConstraint, undo } from '../app/edit';
|
|
1
|
+
import { afterEdit, holdFaces, solveSketch, tryAddConstraint, undo } from '../app/edit';
|
|
2
2
|
import { ed } from '../app/state';
|
|
3
|
-
import { CONS_LABEL } from '../model/constraints';
|
|
3
|
+
import { CONS_LABEL, measure } from '../model/constraints';
|
|
4
4
|
import { dropHistory, pushHistory } from '../model/history';
|
|
5
|
-
import { createsCycle, facingEdges, nearerEdge } from '../model/openings';
|
|
5
|
+
import { createsCycle, edgePoint, facingEdges, nearerEdge } from '../model/openings';
|
|
6
|
+
import { cabOfPoint } from '../model/faces';
|
|
6
7
|
import { settings } from '../model/settings';
|
|
7
|
-
import { connectedGeometry, curveInfo, deleteSel,
|
|
8
|
-
import { page, saveSoon, SK } from '../model/store';
|
|
8
|
+
import { connectedGeometry, curveInfo, deleteSel, joinPoints } from '../model/sketch';
|
|
9
|
+
import { page, planSK, replaceSK, saveSoon, SK } from '../model/store';
|
|
9
10
|
import { fitView, requestDraw } from '../render/view';
|
|
10
11
|
import { convertNote } from '../tools/annotate';
|
|
11
|
-
import {
|
|
12
|
+
import { cornerAngle, dimCurve, dimEdges, dimLine, dimPointLine, dimPoints, dimTwoLines, editDim, faceChoices, onChoice, setDimFace, toggleRef } from '../tools/dim';
|
|
13
|
+
import { edgeLabel, edgeOf } from '../model/planGeom';
|
|
12
14
|
import { offsetPrompt, splitLine } from '../tools/modify';
|
|
15
|
+
import { filletable, filletPrompt } from '../tools/fillet';
|
|
16
|
+
import { chamferPrompt } from '../tools/chamfer';
|
|
13
17
|
import { addOpeningDim, editOpeningDim, toggleOpeningDim } from '../tools/openings';
|
|
14
18
|
import { setTool, tools } from '../tools/registry';
|
|
15
19
|
import { fmtLen } from '../units';
|
|
16
|
-
import { openElevation } from './elevation';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
20
|
+
import { elevOpen, fitState, OPENING_SIZES, openElevation, openingLabel, overflow, restoreFit, TOO_SHORT, wallAction, wallBar, wallDelete, wallPlus } from './elevation';
|
|
21
|
+
import { pushedOut } from '../model/facesolve';
|
|
22
|
+
import { editRoomName, openKeypad, openPalette, toast, toggleImageOpacity, toggleImageRotation, toggleTileSize, updateCtx, updateTopBar } from './ports';
|
|
23
|
+
import { cornerLines, edgePair, selInfo } from './selection';
|
|
24
|
+
import { restackImage } from './project';
|
|
25
|
+
import { imageById } from '../model/images';
|
|
26
|
+
import { canOpenLink, canSaveDim, linkSizeVisible, openDimLink, openSelectedLink, saveDimToGroup, selectedLink, toggleLeader, unlinkDim } from './linkActions';
|
|
19
27
|
/** Points a Lock on the current selection would pin (the origin is always fixed). */
|
|
20
28
|
function lockTargets() {
|
|
21
29
|
const sk = SK(), s = selInfo(), ids = new Set(s.points);
|
|
22
30
|
s.lines.forEach(id => { ids.add(sk.lines[id].a); ids.add(sk.lines[id].b); });
|
|
23
31
|
s.curves.forEach(id => { const ci = curveInfo(sk, id); if (ci)
|
|
24
32
|
ids.add(ci.o.c); });
|
|
25
|
-
return [...ids].filter(id => sk.points[id] && !sk.points[id].origin);
|
|
33
|
+
return [...ids].filter(id => sk.points[id] && !sk.points[id].origin && !sk.points[id].anchor);
|
|
26
34
|
}
|
|
27
35
|
/**
|
|
28
36
|
* Lock is always the first action, set off by a divider, and shows its state:
|
|
@@ -34,7 +42,7 @@ function arrangeLock(items) {
|
|
|
34
42
|
return items;
|
|
35
43
|
const it = items[i];
|
|
36
44
|
const sk = SK();
|
|
37
|
-
const locked = it.act === 'imgLock' ?
|
|
45
|
+
const locked = it.act === 'imgLock' ? selImages().length > 0 && selImages().every(u => u.locked) : (() => { const l = lockTargets(); return l.length > 0 && l.every(id => sk.points[id].fixed); })();
|
|
38
46
|
Object.assign(it, { ic: locked ? 'lock' : 'unlock', label: locked ? 'Locked' : 'Lock', on: undefined, locked });
|
|
39
47
|
const rest = items.filter((_, k) => k !== i);
|
|
40
48
|
const out = [it];
|
|
@@ -48,11 +56,21 @@ function arrangeLock(items) {
|
|
|
48
56
|
out.pop();
|
|
49
57
|
return out;
|
|
50
58
|
}
|
|
59
|
+
/** What the bar calls one end of a door or window. */
|
|
60
|
+
function anchorLabel(id) {
|
|
61
|
+
return (planSK().openings[id.split('|')[0]]?.type === 'door' ? 'Door' : 'Window') + ' edge';
|
|
62
|
+
}
|
|
63
|
+
/** The selected images. */
|
|
64
|
+
const selImages = () => ed.sel.filter(x => x.type === 'underlay').map(x => imageById(x.id)).filter((u) => !!u);
|
|
65
|
+
/** The tools that place a door, window or cabinet (in the plan or the wall view), which select what they've placed. */
|
|
66
|
+
const PLACING = new Set(['door', 'window', 'wdoor', 'wwindow', 'cabinet']);
|
|
51
67
|
function ctxItems() {
|
|
52
68
|
const items = [], sk = SK(), s = selInfo();
|
|
53
69
|
const add = (ic, label, act, o = {}) => items.push(Object.assign({ ic, label, act }, o));
|
|
54
70
|
/** To wall (all the selected lines and curves) — or To line + Thickness when they're all walls already. */
|
|
55
71
|
const wallItems = () => {
|
|
72
|
+
if (elevOpen())
|
|
73
|
+
return;
|
|
56
74
|
const kinds = [...s.lines.map(id => sk.lines[id].kind), ...s.curves.map(id => curveInfo(sk, id)?.o.kind)];
|
|
57
75
|
if (!kinds.length)
|
|
58
76
|
return;
|
|
@@ -64,52 +82,118 @@ function ctxItems() {
|
|
|
64
82
|
add('wall', 'To wall', 'kind');
|
|
65
83
|
};
|
|
66
84
|
if (ed.mode === 'annotate') {
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
const ims = selImages();
|
|
86
|
+
if (ims.length && ims.length === s.n) {
|
|
87
|
+
// an image (or several): lock, its own scale, and which goes in front where they overlap
|
|
88
|
+
// (showing and hiding them all is the Layers menu's)
|
|
69
89
|
add('lock', 'Lock', 'imgLock');
|
|
70
|
-
if (!locked)
|
|
90
|
+
if (ims.length === 1 && !ims[0].locked)
|
|
71
91
|
add('calib', 'Set scale', 'calibtool');
|
|
72
|
-
|
|
92
|
+
if (ims.length === 1)
|
|
93
|
+
add('opacity', 'Opacity', 'imgOpacity');
|
|
94
|
+
// (turning moves it: not while it's locked, as it can't be moved or sized then)
|
|
95
|
+
if (ims.length === 1 && !ims[0].locked)
|
|
96
|
+
add('rotate', 'Rotate', 'imgRotate');
|
|
97
|
+
if (ims.length === 1 && page().underlays.length > 1) {
|
|
98
|
+
const at = page().underlays.indexOf(ims[0]), last = page().underlays.length - 1;
|
|
99
|
+
if (at < last)
|
|
100
|
+
add('tofront', 'To front', 'imgFront');
|
|
101
|
+
if (at > 0)
|
|
102
|
+
add('toback', 'To back', 'imgBack');
|
|
103
|
+
}
|
|
104
|
+
return items;
|
|
105
|
+
}
|
|
106
|
+
const lk = selectedLink();
|
|
107
|
+
if (lk) {
|
|
108
|
+
// a link's tile: opening it, its leader, its size (its name is on the tile, not here: the user's call, 2026-10-21)
|
|
109
|
+
if (canOpenLink(lk.link))
|
|
110
|
+
add('open', 'Open', 'linkOpen');
|
|
111
|
+
add('leader', 'Leader', 'linkLeader', { on: !!lk.leader });
|
|
112
|
+
// (its leader's colour, as all markup's, is the rail's colour dot's)
|
|
113
|
+
// (every tile on the page: a slider above the bar)
|
|
114
|
+
add('tilesize', 'Size', 'linkSize');
|
|
73
115
|
return items;
|
|
74
116
|
}
|
|
75
117
|
if (s.notes.length) {
|
|
118
|
+
// (no Color: the rail's colour dot styles what's selected)
|
|
76
119
|
const n = sk.notes.find(n => n.id === s.notes[0]);
|
|
77
|
-
add('color', 'Color', 'palette');
|
|
78
120
|
if (n && n.type === 'pen' && s.notes.length === 1)
|
|
79
121
|
add('convert', 'To lines', 'convert');
|
|
122
|
+
if (linkSizeVisible())
|
|
123
|
+
add('tilesize', 'Size', 'linkSize');
|
|
80
124
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (page().underlay) {
|
|
84
|
-
add('calib', 'Scale', 'calibtool');
|
|
85
|
-
add('eye', settings.layers.underlay ? 'Hide image' : 'Show image', 'toggleUnderlay');
|
|
86
|
-
}
|
|
87
|
-
}
|
|
125
|
+
// nothing selected: no bar (the markup colour is the rail's colour dot, for what's selected too; the image's own
|
|
126
|
+
// options come with selecting it)
|
|
88
127
|
return items;
|
|
89
128
|
}
|
|
90
|
-
if (ed.tool === 'line'
|
|
129
|
+
if (ed.tool === 'line') {
|
|
91
130
|
const st = tools[ed.tool].st;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (st.chain.length >= 3)
|
|
95
|
-
add('closeShape', 'Close', 'closeShape');
|
|
96
|
-
add('finish', 'Finish', 'finish');
|
|
131
|
+
// drawing: no bar (tap the last point again to finish, the first to close the shape; Esc or Enter on a keyboard)
|
|
132
|
+
if (st.start)
|
|
97
133
|
return items;
|
|
98
|
-
}
|
|
99
134
|
}
|
|
100
135
|
if (ed.tool === 'dim') {
|
|
101
136
|
add('dim', 'Driving', 'dimDrive', { on: ed.dimLayer === 'drive' });
|
|
102
137
|
add('ref', 'Print', 'dimPrint', { on: ed.dimLayer === 'print' });
|
|
103
138
|
return items;
|
|
104
139
|
}
|
|
105
|
-
// nothing selected: the bar stays empty (Fit and Icons live in the top bar; grid snap and auto-constrain in Settings)
|
|
106
|
-
|
|
140
|
+
// nothing selected: the bar stays empty (Fit and Icons live in the top bar; grid snap and auto-constrain in Settings).
|
|
141
|
+
// Just placed a door, window or cabinet (its tool still on): its bar, for its sizes
|
|
142
|
+
if (!s.n || (ed.tool !== 'select' && !PLACING.has(ed.tool)))
|
|
143
|
+
return items;
|
|
144
|
+
// the wall view's own things: a door or window, a cabinet, one of its dimension chips, the plan's edges
|
|
145
|
+
const wb = wallBar();
|
|
146
|
+
if (wb) {
|
|
147
|
+
if (wb.label)
|
|
148
|
+
items.push({ lbl: wb.label });
|
|
149
|
+
for (const it of wb.items)
|
|
150
|
+
add(it.ic, it.label, it.act);
|
|
107
151
|
return items;
|
|
152
|
+
}
|
|
153
|
+
// on a wall face, walls don't exist: no To wall / Thickness
|
|
154
|
+
const wall = elevOpen();
|
|
108
155
|
const P = s.points.length, L = s.lines.length, C = s.curves.length;
|
|
156
|
+
// Guide (dashed) is for lines: a wall is always solid (one that's a guide from before can still go back to Solid)
|
|
157
|
+
const allWalls = L + C > 0 && s.lines.every(id => sk.lines[id].kind === 'wall' && !sk.lines[id].constr) && s.curves.every(id => { const o = curveInfo(sk, id)?.o; return o?.kind === 'wall' && !o.constr; });
|
|
158
|
+
const guide = () => { if (!allWalls)
|
|
159
|
+
add('constr', 'Guide', 'constr'); };
|
|
160
|
+
// a wall's edge: named, with its wall view; with another edge, a line or a point, the distance between them (two
|
|
161
|
+
// edges of one wall: its thickness)
|
|
162
|
+
if (s.wfaces.length) {
|
|
163
|
+
// (a curved wall has no wall view: just its name)
|
|
164
|
+
if (s.n === 1) {
|
|
165
|
+
const [id, side] = edgeOf(s.wfaces[0]);
|
|
166
|
+
items.push({ lbl: edgeLabel(sk, id, side) });
|
|
167
|
+
if (sk.lines[id])
|
|
168
|
+
add('elev', 'Elevation', 'elev');
|
|
169
|
+
}
|
|
170
|
+
else if (edgePair(s)) {
|
|
171
|
+
const one = s.wfaces.length === 2 && edgeOf(s.wfaces[0])[0] === edgeOf(s.wfaces[1])[0];
|
|
172
|
+
add(one ? 'thick' : 'dim', one ? 'Thickness' : 'Distance', 'edgeDist');
|
|
173
|
+
}
|
|
174
|
+
return items;
|
|
175
|
+
}
|
|
109
176
|
if (s.n === 1 && s.dims.length) {
|
|
110
177
|
const c = sk.cons[s.dims[0]];
|
|
111
|
-
|
|
178
|
+
// linked to a measurement of the job: opening it, letting go of it — no text saying what it's linked to (the user's
|
|
179
|
+
// call, 2026-10-21; its chip is drawn linked), and no Reference (a measurement drives its line, or it isn't linked)
|
|
180
|
+
if (c.measurementRef) {
|
|
181
|
+
const t = c.measurementRef;
|
|
182
|
+
if (canOpenLink(t))
|
|
183
|
+
add('open', 'Open', 'dimLinkOpen');
|
|
184
|
+
add('unlink', 'Unlink', 'dimUnlink');
|
|
185
|
+
}
|
|
186
|
+
else if (canSaveDim(s.dims[0]))
|
|
187
|
+
add('savegroup', 'Save to group', 'dimSave');
|
|
188
|
+
// Reference frees what a dimension holds — a distance, say; not a cabinet's own width or height, which it keeps
|
|
189
|
+
const cabSize = Object.values(sk.cabs ?? {}).some(cb => cb.w === s.dims[0] || cb.h === s.dims[0]);
|
|
190
|
+
if (c.layer !== 'print' && !cabSize && !c.measurementRef)
|
|
112
191
|
add('ref', c.driving === false ? 'Drive' : 'Reference', 'toggleref');
|
|
192
|
+
// measured on walls: which of their faces (the drawing stays as it is; the number becomes what it measures there)
|
|
193
|
+
const fc = faceChoices(sk, c);
|
|
194
|
+
if (fc.length && items.length)
|
|
195
|
+
items.push('sep');
|
|
196
|
+
fc.forEach((ch, i) => add(ch.icon, ch.short, 'dimFace' + i, { on: onChoice(c, ch) }));
|
|
113
197
|
return items;
|
|
114
198
|
}
|
|
115
199
|
if (s.n === 1 && s.odims.length) {
|
|
@@ -125,53 +209,82 @@ function ctxItems() {
|
|
|
125
209
|
add('rename', 'Rename', 'renameRoom');
|
|
126
210
|
return items;
|
|
127
211
|
}
|
|
212
|
+
// one end of a door or window: named; with another point, end or door/window, the distance between them
|
|
213
|
+
if (s.oanchors.length && s.n === 1) {
|
|
214
|
+
items.push({ lbl: anchorLabel(s.oanchors[0]) });
|
|
215
|
+
return items;
|
|
216
|
+
}
|
|
217
|
+
if (s.oanchors.length && s.n === 2 && s.oanchors.length + s.points.length + s.openings.length === 2) {
|
|
218
|
+
add('dim', 'Distance', 'anchorDist');
|
|
219
|
+
return items;
|
|
220
|
+
}
|
|
128
221
|
// two doors/windows, or one and a point: the distance between them (locks the second one picked)
|
|
129
222
|
if (s.n === 2 && (s.openings.length === 2 || (s.openings.length === 1 && s.points.length === 1))) {
|
|
130
223
|
add('dim', 'Distance', 'opDist');
|
|
131
224
|
return items;
|
|
132
225
|
}
|
|
133
226
|
if (s.n === 1 && s.openings.length) {
|
|
134
|
-
//
|
|
135
|
-
|
|
227
|
+
// its sizes by name, as in the wall view (no +: it isn't one dimension); a door's swing too. (The type is fixed
|
|
228
|
+
// once placed: Door and Window are separate tools.)
|
|
229
|
+
const o = sk.openings[s.openings[0]];
|
|
230
|
+
items.push({ lbl: openingLabel(o) });
|
|
231
|
+
for (const it of OPENING_SIZES)
|
|
232
|
+
add(it.ic, it.label, it.act);
|
|
233
|
+
if (o.type === 'door') {
|
|
234
|
+
items.push('sep');
|
|
136
235
|
add('flip', 'Flip', 'opFlip');
|
|
236
|
+
}
|
|
137
237
|
return items;
|
|
138
238
|
}
|
|
139
239
|
if (L === 1 && s.n === 1) {
|
|
140
240
|
const l = sk.lines[s.lines[0]];
|
|
141
241
|
add('h', 'Horizontal', 'h', { key: '⇧H' });
|
|
142
|
-
add('v', 'Vertical', 'v', { key: '⇧V' });
|
|
143
|
-
add('dim', 'Length', 'len');
|
|
242
|
+
add('v', 'Vertical', 'v', { key: '⇧V' }); // (its length: the yellow +)
|
|
144
243
|
items.push('sep');
|
|
145
244
|
add('lock', 'Lock', 'lock', { key: '⇧F' });
|
|
146
245
|
add('split', 'Split', 'split');
|
|
147
246
|
add('offset', 'Offset', 'offset');
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (l.kind === 'wall') {
|
|
151
|
-
add('thick', 'Thickness', 'thick');
|
|
152
|
-
add('line', 'To line', 'kind');
|
|
153
|
-
}
|
|
247
|
+
if (l.constr)
|
|
248
|
+
add('constr', 'Solid', 'constr');
|
|
154
249
|
else
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
250
|
+
guide();
|
|
251
|
+
if (!wall) {
|
|
252
|
+
if (l.kind === 'wall') {
|
|
253
|
+
add('thick', 'Thickness', 'thick');
|
|
254
|
+
add('line', 'To line', 'kind');
|
|
255
|
+
}
|
|
256
|
+
else
|
|
257
|
+
add('wall', 'To wall', 'kind');
|
|
258
|
+
if (l.kind === 'wall' && !l.constr) {
|
|
259
|
+
items.push('sep');
|
|
260
|
+
add('elev', 'Elevation', 'elev');
|
|
261
|
+
}
|
|
159
262
|
}
|
|
160
263
|
return items;
|
|
161
264
|
}
|
|
162
265
|
if (L === 2 && s.n === 2) {
|
|
266
|
+
// (no Angle here: the yellow + is the angle between them — or, parallel, the distance)
|
|
163
267
|
add('par', 'Parallel', 'par', { key: '⇧P' });
|
|
164
268
|
add('perp', 'Perpendicular', 'perp', { key: '⇧R' });
|
|
165
269
|
add('col', 'Collinear', 'col', { key: '⇧L' });
|
|
166
270
|
add('eq', 'Equal', 'eq', { key: '⇧E' });
|
|
167
|
-
|
|
271
|
+
// two lines that meet: round off (or cut off) the corner between them
|
|
272
|
+
if (filletCorner2()) {
|
|
273
|
+
add('fillet', 'Fillet', 'fillet');
|
|
274
|
+
add('chamfer', 'Chamfer', 'chamfer');
|
|
275
|
+
}
|
|
168
276
|
wallItems();
|
|
169
277
|
return items;
|
|
170
278
|
}
|
|
279
|
+
// a cabinet's corner: nothing of its own (pick another point or line with it to dimension or constrain)
|
|
280
|
+
if (P === 1 && s.n === 1 && cabOfPoint(sk, s.points[0]))
|
|
281
|
+
return items;
|
|
171
282
|
if (P === 1 && s.n === 1) {
|
|
172
|
-
// a corner where exactly two lines meet:
|
|
173
|
-
if (
|
|
174
|
-
add('
|
|
283
|
+
// a corner where exactly two lines meet: rounding it off or cutting it off (its angle is the yellow +)
|
|
284
|
+
if (filletable(sk, s.points[0])) {
|
|
285
|
+
add('fillet', 'Fillet', 'fillet');
|
|
286
|
+
add('chamfer', 'Chamfer', 'chamfer');
|
|
287
|
+
}
|
|
175
288
|
add('lock', 'Lock', 'lock', { key: '⇧F' });
|
|
176
289
|
return items;
|
|
177
290
|
}
|
|
@@ -200,7 +313,9 @@ function ctxItems() {
|
|
|
200
313
|
// a circle or arc: the yellow + sets its diameter
|
|
201
314
|
if (C === 1 && s.n === 1) {
|
|
202
315
|
const ci = curveInfo(sk, s.curves[0]);
|
|
203
|
-
|
|
316
|
+
guide();
|
|
317
|
+
if (wall)
|
|
318
|
+
return items;
|
|
204
319
|
if (ci && ci.o.kind === 'wall') {
|
|
205
320
|
add('thick', 'Thickness', 'thick');
|
|
206
321
|
add('arc', 'To line', 'kind');
|
|
@@ -223,40 +338,91 @@ function ctxItems() {
|
|
|
223
338
|
add('col', 'Collinear', 'col', { key: '⇧L' });
|
|
224
339
|
add('eq', 'Equal', 'eq', { key: '⇧E' });
|
|
225
340
|
items.push('sep');
|
|
226
|
-
add('chain', 'Connected', 'chain');
|
|
227
341
|
add('lock', 'Lock', 'lock');
|
|
228
|
-
|
|
342
|
+
guide();
|
|
229
343
|
wallItems();
|
|
230
344
|
return items;
|
|
231
345
|
}
|
|
232
346
|
if (L >= 2) {
|
|
233
347
|
add('eq', 'Equal', 'eqall');
|
|
234
|
-
add('chain', 'Connected', 'chain');
|
|
235
348
|
add('lock', 'Lock', 'lock');
|
|
236
|
-
|
|
349
|
+
guide();
|
|
237
350
|
wallItems();
|
|
238
351
|
return items;
|
|
239
352
|
}
|
|
240
353
|
add('lock', 'Lock', 'lock');
|
|
241
354
|
return items;
|
|
242
355
|
}
|
|
356
|
+
/** The keypad for walls' thickness, finished face to face. */
|
|
357
|
+
function thicknessPrompt(walls) {
|
|
358
|
+
const sk = SK();
|
|
359
|
+
if (!walls.length)
|
|
360
|
+
return;
|
|
361
|
+
openKeypad({
|
|
362
|
+
title: 'Wall thickness', sub: 'Finished face to face', initial: fmtLen(walls[0].thick || 4.5), selectAll: true,
|
|
363
|
+
onDone: v => {
|
|
364
|
+
if (v <= 0)
|
|
365
|
+
return;
|
|
366
|
+
// a thicker wall's faces are shorter (its corners come in): not shorter than what's on them — and what's measured
|
|
367
|
+
// on walls' faces stays as measured, the centrelines moving to keep it
|
|
368
|
+
const was = overflow(sk), held = fitState(sk), had = walls.map(w => w.thick);
|
|
369
|
+
pushHistory();
|
|
370
|
+
walls.forEach(w => (w.thick = v));
|
|
371
|
+
const faces = holdFaces();
|
|
372
|
+
if (!faces || pushedOut(was, overflow(sk))) {
|
|
373
|
+
walls.forEach((w, i) => (w.thick = had[i]));
|
|
374
|
+
restoreFit(sk, held);
|
|
375
|
+
dropHistory();
|
|
376
|
+
toast(faces ? TOO_SHORT : 'The dimensions measured on walls’ faces can’t hold at that thickness.', { warn: true });
|
|
377
|
+
}
|
|
378
|
+
afterEdit();
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
/** Two selected lines that meet at a corner of just those two: that corner (to round it off). */
|
|
383
|
+
function filletCorner2() {
|
|
384
|
+
const sk = SK(), s = selInfo();
|
|
385
|
+
if (s.lines.length !== 2 || s.n !== 2)
|
|
386
|
+
return null;
|
|
387
|
+
const [a, b] = s.lines.map(id => sk.lines[id]);
|
|
388
|
+
const p = [a.a, a.b].find(x => x === b.a || x === b.b);
|
|
389
|
+
return p && filletable(sk, p) ? p : null;
|
|
390
|
+
}
|
|
243
391
|
/** Buttons for the bottom bar: they adapt to the tool and the selection (Lock first, when offered). */
|
|
244
392
|
export const barItems = () => arrangeLock(ctxItems());
|
|
245
393
|
/** The yellow + only appears for something it can measure: not for constraint badges or spaces alone. */
|
|
394
|
+
const DRAW_TOOLS = new Set(['line', 'freehand', 'rect', 'circle', 'arc']);
|
|
246
395
|
export function plusVisible() {
|
|
396
|
+
// markup isn't measured: Annotate has no + (notes are the rail's Text tool)
|
|
397
|
+
if (ed.mode === 'annotate')
|
|
398
|
+
return false;
|
|
399
|
+
const wb = wallBar();
|
|
400
|
+
if (wb)
|
|
401
|
+
return wb.plus;
|
|
402
|
+
// a shape just drawn (Rectangle, Circle, Arc keep their tool, the shape selected) gets no + of its own;
|
|
403
|
+
// a door or window just placed does (its width)
|
|
404
|
+
if (ed.mode === 'draft' && DRAW_TOOLS.has(ed.tool))
|
|
405
|
+
return false;
|
|
406
|
+
// one end of a door or window, alone: nothing to measure yet
|
|
407
|
+
if (ed.sel.length === 1 && ed.sel[0].type === 'oanchor')
|
|
408
|
+
return false;
|
|
409
|
+
// one door or window picked as a whole: its sizes are in the bar, by name — no single dimension for a +
|
|
410
|
+
// (two, or one and a point: the + is the distance between them)
|
|
411
|
+
if (ed.sel.length === 1 && ed.sel[0].type === 'opening')
|
|
412
|
+
return false;
|
|
247
413
|
return ed.sel.length > 0 && !ed.sel.every(x => x.type === 'cons') && !ed.sel.every(x => x.type === 'room');
|
|
248
414
|
}
|
|
249
|
-
export const plusLabel = () =>
|
|
250
|
-
/** The yellow +:
|
|
415
|
+
export const plusLabel = () => 'Add a measurement';
|
|
416
|
+
/** The yellow +: the measurement that fits the selection (opens the keypad). */
|
|
251
417
|
export function plusAction() {
|
|
252
|
-
if (ed.mode === 'annotate')
|
|
253
|
-
|
|
418
|
+
if (ed.mode === 'annotate')
|
|
419
|
+
return;
|
|
420
|
+
if (wallPlus())
|
|
254
421
|
return;
|
|
255
|
-
}
|
|
256
422
|
const s = selInfo(), sk = SK();
|
|
257
423
|
if (s.dims.length === 1 && s.n === 1) {
|
|
258
424
|
if (sk.cons[s.dims[0]]?.layer === 'print')
|
|
259
|
-
toast('Print dimensions are labels only — drag to move them.');
|
|
425
|
+
toast('Print dimensions are labels only — drag to move them.', { warn: true });
|
|
260
426
|
else
|
|
261
427
|
editDim(s.dims[0]);
|
|
262
428
|
}
|
|
@@ -264,12 +430,28 @@ export function plusAction() {
|
|
|
264
430
|
const [op, part] = s.odims[0].split('|');
|
|
265
431
|
editOpeningDim(op, part);
|
|
266
432
|
}
|
|
433
|
+
// a wall's edge: its length on that face; with another edge, a line or a point, the distance from it
|
|
434
|
+
// (a curved wall's: its diameter across that face)
|
|
435
|
+
else if (s.wfaces.length === 1 && s.n === 1) {
|
|
436
|
+
const [id, side] = edgeOf(s.wfaces[0]);
|
|
437
|
+
if (sk.lines[id])
|
|
438
|
+
dimLine(id, false, side);
|
|
439
|
+
else
|
|
440
|
+
dimCurve(id, side);
|
|
441
|
+
}
|
|
442
|
+
else if (edgePair(s))
|
|
443
|
+
applyCtxAction('edgeDist');
|
|
267
444
|
else if (s.lines.length === 1 && s.n === 1)
|
|
268
445
|
dimLine(s.lines[0], false);
|
|
269
446
|
else if (s.curves.length === 1 && s.n === 1)
|
|
270
447
|
dimCurve(s.curves[0]);
|
|
271
448
|
else if (s.openings.length === 1 && s.n === 1)
|
|
272
449
|
editOpeningDim(s.openings[0], 'w');
|
|
450
|
+
// a corner where two lines meet: its angle
|
|
451
|
+
else if (s.points.length === 1 && s.n === 1 && cornerLines(sk, s.points[0]))
|
|
452
|
+
applyCtxAction('cornerAngle');
|
|
453
|
+
else if (s.n === 2 && s.oanchors.length)
|
|
454
|
+
applyCtxAction('anchorDist');
|
|
273
455
|
else if (s.n === 2 && (s.openings.length === 2 || (s.openings.length === 1 && s.points.length === 1)))
|
|
274
456
|
applyCtxAction('opDist');
|
|
275
457
|
// two things selected: the dimension between them
|
|
@@ -285,29 +467,26 @@ export function plusAction() {
|
|
|
285
467
|
export function deleteSelection() {
|
|
286
468
|
if (!ed.sel.length)
|
|
287
469
|
return;
|
|
288
|
-
|
|
470
|
+
if (wallDelete())
|
|
471
|
+
return;
|
|
472
|
+
// (a wall's edge picked: the wall)
|
|
473
|
+
const sk = SK(), wallType = (id) => (sk.lines[id] ? 'line' : sk.circles[id] ? 'circle' : 'arc');
|
|
474
|
+
const items = ed.sel.map(x => (x.type === 'wface' ? { type: wallType(edgeOf(x.id)[0]), id: edgeOf(x.id)[0] } : x)).filter((x, i, a) => a.findIndex(y => y.type === x.type && y.id === x.id) === i);
|
|
289
475
|
if (items.length === 1 && items.some(s => s.type === 'point' && sk.points[s.id] && sk.points[s.id].origin)) {
|
|
290
|
-
toast('The origin can’t be deleted.');
|
|
476
|
+
toast('The origin can’t be deleted.', { warn: true });
|
|
291
477
|
return;
|
|
292
478
|
}
|
|
293
|
-
//
|
|
294
|
-
const
|
|
479
|
+
// images are part of the page's undo history, so they go in the same step as the rest
|
|
480
|
+
const imgIds = new Set(items.filter(s => s.type === 'underlay').map(s => s.id));
|
|
295
481
|
const rest = items.filter(s => s.type !== 'underlay');
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
482
|
+
pushHistory();
|
|
483
|
+
if (imgIds.size)
|
|
484
|
+
page().underlays = page().underlays.filter(u => !imgIds.has(u.id));
|
|
485
|
+
if (rest.length)
|
|
300
486
|
deleteSel(sk, rest);
|
|
301
|
-
}
|
|
302
487
|
ed.sel = [];
|
|
303
488
|
afterEdit();
|
|
304
|
-
toast(
|
|
305
|
-
action: { label: 'Undo', fn: () => { if (rest.length)
|
|
306
|
-
undo(); if (removed && !page().underlay) {
|
|
307
|
-
page().underlay = removed;
|
|
308
|
-
afterEdit();
|
|
309
|
-
} } },
|
|
310
|
-
});
|
|
489
|
+
toast(imgIds.size && !rest.length ? (imgIds.size > 1 ? 'Images removed' : 'Image removed') : 'Deleted', { action: { label: 'Undo', fn: () => undo() } });
|
|
311
490
|
}
|
|
312
491
|
function consFromSel(type) {
|
|
313
492
|
const sk = SK(), s = selInfo(), L = s.lines.map(id => sk.lines[id]);
|
|
@@ -376,7 +555,7 @@ function applyMulti(type, list) {
|
|
|
376
555
|
}
|
|
377
556
|
if (!added && !failed.length) {
|
|
378
557
|
dropHistory();
|
|
379
|
-
toast('They’re already ' + ADJ[type] + '.');
|
|
558
|
+
toast('They’re already ' + ADJ[type] + '.', { warn: true });
|
|
380
559
|
afterEdit();
|
|
381
560
|
return;
|
|
382
561
|
}
|
|
@@ -403,7 +582,7 @@ function applyCons(type) {
|
|
|
403
582
|
}
|
|
404
583
|
const c = consFromSel(type);
|
|
405
584
|
if (!c) {
|
|
406
|
-
toast('Select ' + (NEEDS[type] || 'geometry') + ' first.');
|
|
585
|
+
toast('Select ' + (NEEDS[type] || 'geometry') + ' first.', { warn: true });
|
|
407
586
|
return;
|
|
408
587
|
}
|
|
409
588
|
pushHistory();
|
|
@@ -416,6 +595,8 @@ function applyCons(type) {
|
|
|
416
595
|
}
|
|
417
596
|
/** Run a bottom-bar action. `anchor` is the button pressed, for actions that open a popover next to it. */
|
|
418
597
|
export function applyCtxAction(act, anchor) {
|
|
598
|
+
if (wallAction(act))
|
|
599
|
+
return;
|
|
419
600
|
const sk = SK(), s = selInfo();
|
|
420
601
|
switch (act) {
|
|
421
602
|
case 'fit':
|
|
@@ -424,7 +605,21 @@ export function applyCtxAction(act, anchor) {
|
|
|
424
605
|
case 'elev':
|
|
425
606
|
if (s.lines.length === 1)
|
|
426
607
|
openElevation(s.lines[0]);
|
|
608
|
+
// (an edge: seen from that face)
|
|
609
|
+
else if (s.wfaces.length === 1 && sk.lines[edgeOf(s.wfaces[0])[0]])
|
|
610
|
+
openElevation(...edgeOf(s.wfaces[0]));
|
|
611
|
+
return;
|
|
612
|
+
case 'edgeDist':
|
|
613
|
+
dimEdges(ed.sel, id => thicknessPrompt([sk.lines[id] ?? sk.circles[id] ?? sk.arcs[id]]));
|
|
427
614
|
return;
|
|
615
|
+
case 'dimFace0':
|
|
616
|
+
case 'dimFace1':
|
|
617
|
+
case 'dimFace2': {
|
|
618
|
+
const ch = s.dims.length === 1 && sk.cons[s.dims[0]] ? faceChoices(sk, sk.cons[s.dims[0]])[+act.slice(7)] : undefined;
|
|
619
|
+
if (ch)
|
|
620
|
+
setDimFace(s.dims[0], ch);
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
428
623
|
case 'grid':
|
|
429
624
|
settings.snapGrid = !settings.snapGrid;
|
|
430
625
|
break;
|
|
@@ -470,6 +665,10 @@ export function applyCtxAction(act, anchor) {
|
|
|
470
665
|
case 'len':
|
|
471
666
|
if (s.lines.length === 1)
|
|
472
667
|
dimLine(s.lines[0], false);
|
|
668
|
+
else if (s.wfaces.length === 1 && sk.lines[edgeOf(s.wfaces[0])[0]]) {
|
|
669
|
+
const [lid, side] = edgeOf(s.wfaces[0]);
|
|
670
|
+
dimLine(lid, false, side);
|
|
671
|
+
}
|
|
473
672
|
return;
|
|
474
673
|
case 'pdist':
|
|
475
674
|
if (s.points.length === 2)
|
|
@@ -483,29 +682,23 @@ export function applyCtxAction(act, anchor) {
|
|
|
483
682
|
if (s.curves.length === 1)
|
|
484
683
|
dimCurve(s.curves[0]);
|
|
485
684
|
return;
|
|
486
|
-
case '
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
const pair = cornerLines(sk, s.points[0]);
|
|
492
|
-
if (!pair)
|
|
493
|
-
return;
|
|
494
|
-
// edit the corner's existing angle dimension rather than stacking a second one
|
|
495
|
-
const ex = Object.keys(sk.cons).find(id => { const c = sk.cons[id]; return c.type === 'angle' && c.layer !== 'print' && ((c.l1 === pair[0] && c.l2 === pair[1]) || (c.l1 === pair[1] && c.l2 === pair[0])); });
|
|
496
|
-
if (ex)
|
|
497
|
-
editDim(ex);
|
|
498
|
-
else
|
|
499
|
-
dimAngle(pair[0], pair[1]);
|
|
685
|
+
case 'fillet':
|
|
686
|
+
case 'chamfer': {
|
|
687
|
+
const p = s.points.length === 1 ? s.points[0] : filletCorner2();
|
|
688
|
+
if (p)
|
|
689
|
+
(act === 'fillet' ? filletPrompt : chamferPrompt)(p);
|
|
500
690
|
return;
|
|
501
691
|
}
|
|
692
|
+
case 'cornerAngle':
|
|
693
|
+
cornerAngle(s.points[0]);
|
|
694
|
+
return;
|
|
502
695
|
case 'merge': {
|
|
503
696
|
pushHistory();
|
|
504
697
|
const snap = JSON.stringify(sk);
|
|
505
|
-
if (
|
|
698
|
+
if (joinPoints(sk, s.points[0], s.points[1])) {
|
|
506
699
|
const r = solveSketch();
|
|
507
700
|
if (!r.ok) {
|
|
508
|
-
|
|
701
|
+
replaceSK(JSON.parse(snap));
|
|
509
702
|
dropHistory();
|
|
510
703
|
toast('Can’t join those points — it conflicts with other constraints.', { warn: true });
|
|
511
704
|
}
|
|
@@ -544,7 +737,8 @@ export function applyCtxAction(act, anchor) {
|
|
|
544
737
|
}
|
|
545
738
|
case 'constr':
|
|
546
739
|
pushHistory();
|
|
547
|
-
s.lines.forEach(id =>
|
|
740
|
+
s.lines.forEach(id => { if (!sk.lines[id].anchor)
|
|
741
|
+
sk.lines[id].constr = !sk.lines[id].constr; });
|
|
548
742
|
s.curves.forEach(id => { const ci = curveInfo(sk, id); if (ci)
|
|
549
743
|
ci.o.constr = !ci.o.constr; });
|
|
550
744
|
afterEdit();
|
|
@@ -556,26 +750,47 @@ export function applyCtxAction(act, anchor) {
|
|
|
556
750
|
return;
|
|
557
751
|
const toWall = !targets.every(t => t.kind === 'wall');
|
|
558
752
|
pushHistory();
|
|
559
|
-
targets.forEach(t => { t.kind = toWall ? 'wall' : 'line'; t.thick = t.thick || 4.5;
|
|
753
|
+
targets.forEach(t => { t.kind = toWall ? 'wall' : 'line'; t.thick = t.thick || 4.5; if (toWall && t.constr)
|
|
754
|
+
t.constr = false; });
|
|
755
|
+
// made lines: what was measured on their faces is measured as they are now (nothing moves); the walls beside
|
|
756
|
+
// them keep theirs
|
|
757
|
+
if (!toWall) {
|
|
758
|
+
const ids = new Set([...s.lines, ...s.curves]);
|
|
759
|
+
for (const c of Object.values(sk.cons)) {
|
|
760
|
+
if (c.type === 'dist' && c.face && c.line && ids.has(c.line)) {
|
|
761
|
+
delete c.face;
|
|
762
|
+
if (c.driving !== false)
|
|
763
|
+
c.value = measure(sk, c);
|
|
764
|
+
}
|
|
765
|
+
if (c.type === 'diam' && c.face && c.c && ids.has(c.c)) {
|
|
766
|
+
delete c.face;
|
|
767
|
+
if (c.driving !== false)
|
|
768
|
+
c.value = measure(sk, c);
|
|
769
|
+
}
|
|
770
|
+
if (c.type === 'pldist' && c.faces && ((c.l && ids.has(c.l)) || (c.l2 && ids.has(c.l2)))) {
|
|
771
|
+
if (c.driving !== false)
|
|
772
|
+
c.value = measure(sk, c);
|
|
773
|
+
if (c.faces[0] && c.l && ids.has(c.l))
|
|
774
|
+
c.faces = [0, c.faces[1]];
|
|
775
|
+
if (c.faces[1] && c.l2 && ids.has(c.l2))
|
|
776
|
+
c.faces = [c.faces[0], 0];
|
|
777
|
+
if (!c.faces[0] && !c.faces[1])
|
|
778
|
+
delete c.faces;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
holdFaces();
|
|
560
783
|
afterEdit();
|
|
561
784
|
toast(targets.length > 1 ? (toWall ? targets.length + ' items are now walls' : targets.length + ' items are now lines') : toWall ? 'Now a wall' : 'Now a line');
|
|
562
785
|
return;
|
|
563
786
|
}
|
|
564
|
-
case 'thick':
|
|
787
|
+
case 'thick':
|
|
565
788
|
// straight and curved walls in the selection
|
|
566
|
-
|
|
789
|
+
thicknessPrompt([
|
|
567
790
|
...s.lines.map(id => sk.lines[id]).filter(l => l.kind === 'wall'),
|
|
568
791
|
...s.curves.map(id => curveInfo(sk, id)?.o).filter((o) => !!o && o.kind === 'wall'),
|
|
569
|
-
];
|
|
570
|
-
if (!walls.length)
|
|
571
|
-
return;
|
|
572
|
-
openKeypad({
|
|
573
|
-
title: 'Wall thickness', sub: 'Finished face to face', initial: fmtLen(walls[0].thick || 4.5), selectAll: true,
|
|
574
|
-
onDone: v => { if (v <= 0)
|
|
575
|
-
return; pushHistory(); walls.forEach(w => (w.thick = v)); afterEdit(); },
|
|
576
|
-
});
|
|
792
|
+
]);
|
|
577
793
|
return;
|
|
578
|
-
}
|
|
579
794
|
case 'eqall': {
|
|
580
795
|
// lines mixed with other things: equal across just the lines
|
|
581
796
|
if (s.lines.length < 2)
|
|
@@ -620,12 +835,60 @@ export function applyCtxAction(act, anchor) {
|
|
|
620
835
|
}
|
|
621
836
|
return;
|
|
622
837
|
}
|
|
838
|
+
case 'anchorDist': {
|
|
839
|
+
// from one end of a door or window to a point, another end, or another door or window (its nearer end): the
|
|
840
|
+
// second one picked is placed from the first (the other way round if that would tie them in a loop)
|
|
841
|
+
const end = (id) => { const [op, edge] = id.split('|'); return { op, edge: edge }; };
|
|
842
|
+
const [first, second] = ed.sel;
|
|
843
|
+
if (!first || !second)
|
|
844
|
+
return;
|
|
845
|
+
if (first.type === 'point' || second.type === 'point') {
|
|
846
|
+
const e = end((first.type === 'oanchor' ? first : second).id), pt = (first.type === 'point' ? first : second).id;
|
|
847
|
+
addOpeningDim(e.op, e.edge, { pt });
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
const nearer = (op, from) => {
|
|
851
|
+
const p = edgePoint(sk, from.op, from.edge), a = edgePoint(sk, op, 's0'), b = edgePoint(sk, op, 's1');
|
|
852
|
+
return !p || !a || !b || Math.hypot(a.x - p.x, a.y - p.y) <= Math.hypot(b.x - p.x, b.y - p.y) ? 's0' : 's1';
|
|
853
|
+
};
|
|
854
|
+
let a = first.type === 'oanchor' ? end(first.id) : null, b = second.type === 'oanchor' ? end(second.id) : null;
|
|
855
|
+
if (!a)
|
|
856
|
+
a = { op: first.id, edge: nearer(first.id, b) };
|
|
857
|
+
if (!b)
|
|
858
|
+
b = { op: second.id, edge: nearer(second.id, a) };
|
|
859
|
+
if (a.op !== b.op && createsCycle(sk, b.op, { op: a.op, edge: a.edge }))
|
|
860
|
+
[a, b] = [b, a];
|
|
861
|
+
addOpeningDim(b.op, b.edge, { op: a.op, edge: a.edge });
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
623
864
|
case 'palette':
|
|
624
865
|
openPalette(anchor);
|
|
625
866
|
return;
|
|
626
867
|
case 'convert':
|
|
627
868
|
convertNote(s.notes[0]);
|
|
628
869
|
return;
|
|
870
|
+
case 'linkOpen':
|
|
871
|
+
openSelectedLink();
|
|
872
|
+
return;
|
|
873
|
+
case 'linkSize':
|
|
874
|
+
toggleTileSize(anchor);
|
|
875
|
+
return;
|
|
876
|
+
case 'dimLinkOpen':
|
|
877
|
+
if (s.dims.length === 1)
|
|
878
|
+
openDimLink(s.dims[0]);
|
|
879
|
+
return;
|
|
880
|
+
case 'dimUnlink':
|
|
881
|
+
if (s.dims.length === 1)
|
|
882
|
+
unlinkDim(s.dims[0]);
|
|
883
|
+
return;
|
|
884
|
+
case 'dimSave':
|
|
885
|
+
if (s.dims.length === 1)
|
|
886
|
+
void saveDimToGroup(s.dims[0]);
|
|
887
|
+
return;
|
|
888
|
+
case 'linkLeader':
|
|
889
|
+
if (s.notes.length === 1)
|
|
890
|
+
toggleLeader(s.notes[0]);
|
|
891
|
+
return;
|
|
629
892
|
case 'calibtool':
|
|
630
893
|
setTool('calib');
|
|
631
894
|
return;
|
|
@@ -635,13 +898,28 @@ export function applyCtxAction(act, anchor) {
|
|
|
635
898
|
ed.sel = ed.sel.filter(x => x.type !== 'underlay');
|
|
636
899
|
break;
|
|
637
900
|
case 'imgLock': {
|
|
638
|
-
const
|
|
639
|
-
if (!
|
|
901
|
+
const ims = selImages();
|
|
902
|
+
if (!ims.length)
|
|
640
903
|
return;
|
|
904
|
+
const lock = !ims.every(u => u.locked), many = ims.length > 1;
|
|
641
905
|
pushHistory();
|
|
642
|
-
u.locked =
|
|
906
|
+
ims.forEach(u => { u.locked = lock; });
|
|
643
907
|
afterEdit();
|
|
644
|
-
toast(
|
|
908
|
+
toast(lock ? (many ? 'Images locked. They won’t move while you trace over them.' : 'Image locked. It won’t move while you trace over it.')
|
|
909
|
+
: many ? 'Images unlocked.' : 'Image unlocked. Drag it, or drag its handles to size it.');
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
case 'imgOpacity':
|
|
913
|
+
toggleImageOpacity(anchor);
|
|
914
|
+
return;
|
|
915
|
+
case 'imgRotate':
|
|
916
|
+
toggleImageRotation(anchor);
|
|
917
|
+
return;
|
|
918
|
+
case 'imgFront':
|
|
919
|
+
case 'imgBack': {
|
|
920
|
+
const ims = selImages();
|
|
921
|
+
if (ims.length === 1)
|
|
922
|
+
restackImage(ims[0].id, act === 'imgFront' ? 'front' : 'back');
|
|
645
923
|
return;
|
|
646
924
|
}
|
|
647
925
|
}
|