@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/tools/dim.js
CHANGED
|
@@ -1,30 +1,47 @@
|
|
|
1
|
-
import { afterEdit, tryAddConstraint } from '../app/edit';
|
|
1
|
+
import { afterEdit, holdGroups, tryAddConstraint } from '../app/edit';
|
|
2
2
|
import { ed } from '../app/state';
|
|
3
3
|
import { measure } from '../model/constraints';
|
|
4
4
|
import { hyp, norm2, pointInPoly, projT } from '../model/geom';
|
|
5
5
|
import { dropHistory, pushHistory } from '../model/history';
|
|
6
|
-
import { addCons, lengthDimOf, lineLen,
|
|
6
|
+
import { addCons, lengthDimOf, lineLen, roomValid } from '../model/sketch';
|
|
7
7
|
import { SK } from '../model/store';
|
|
8
|
+
import { askLinkedChange, changesValue } from '../editor/linkWrite';
|
|
9
|
+
import { offersJobMeasurements } from '../editor/links';
|
|
10
|
+
import { fromUm, linkInfoOf, linkSnapshot } from '../model/links';
|
|
11
|
+
import { defaultFace, dimEnds, edgeId, edgeLabel, edgeOf, faceName, sideOf, spaceBeside, wallOf } from '../model/planGeom';
|
|
12
|
+
import { faceLength } from '../model/wallfaces';
|
|
13
|
+
import { fitState, overflow, pastWallMsg, restoreFit } from '../editor/elevation';
|
|
14
|
+
import { pushedOut } from '../model/facesolve';
|
|
8
15
|
import { hitAny } from '../render/hit';
|
|
9
16
|
import { chooseEdges, edgePoint, nearerEdge, openingOnLineAt } from '../model/openings';
|
|
10
17
|
import { drawPendingDim, drawRubberBand } from '../render/draw';
|
|
11
|
-
import { mainT, requestDraw, s2w, V } from '../render/view';
|
|
18
|
+
import { mainT, requestDraw, requestOverlayDraw, s2w, V } from '../render/view';
|
|
12
19
|
import { solver } from '../solver';
|
|
13
20
|
import { fmtAng, fmtLen } from '../units';
|
|
14
21
|
import { addOpeningDim } from './openings';
|
|
22
|
+
import { keypadFresh, keypadOpen, keypadSetReading } from '../editor/keypad';
|
|
23
|
+
import { cornerLines } from '../editor/selection';
|
|
15
24
|
import { closeKeypad, kpTyped, openKeypad, showConflict, toast, updateCtx, updateHint } from '../editor/ports';
|
|
16
25
|
export const dimTool = {
|
|
17
|
-
|
|
18
|
-
|
|
26
|
+
/** angle: the first pick is a corner whose angle keypad is open (the next tap closes it) */
|
|
27
|
+
st: { first: null, angle: false },
|
|
28
|
+
reset() { if (this.st.angle && keypadOpen())
|
|
29
|
+
closeKeypad(); this.st = { first: null, angle: false }; ed.hover = null; ed.pickedAnchor = null; },
|
|
19
30
|
busy() { return !!this.st.first || !!ed.pickedAnchor; },
|
|
20
31
|
hover(ev) {
|
|
32
|
+
const was = ed.hover;
|
|
21
33
|
ed.hover = pickForDim(ev);
|
|
22
34
|
if (ed.hover && ed.hover.type === 'room')
|
|
23
35
|
ed.hover = null;
|
|
24
36
|
// after the first pick: a rubber band from it to what's under the pointer
|
|
25
37
|
const band = this.band(ev);
|
|
26
38
|
ed.preview = band ? (c, T) => drawRubberBand(c, T, band.from, band.to, band.snapped) : null;
|
|
27
|
-
|
|
39
|
+
// what's under the pointer is highlighted in the drawing itself; otherwise only the band moved
|
|
40
|
+
const same = was?.type === ed.hover?.type && was?.id === ed.hover?.id && was?.part === ed.hover?.part;
|
|
41
|
+
if (same)
|
|
42
|
+
requestOverlayDraw();
|
|
43
|
+
else
|
|
44
|
+
requestDraw();
|
|
28
45
|
},
|
|
29
46
|
/** Ends of the rubber band: the first pick (an opening body resolves to the edge facing the target) and the target. */
|
|
30
47
|
band(ev) {
|
|
@@ -69,6 +86,12 @@ export const dimTool = {
|
|
|
69
86
|
up(ev) {
|
|
70
87
|
const h = pickForDim(ev);
|
|
71
88
|
const print = ed.dimLayer === 'print', st = this.st;
|
|
89
|
+
// a corner's angle keypad is open: the next tap closes it and goes on (a second point measures from the corner)
|
|
90
|
+
if (st.angle) {
|
|
91
|
+
st.angle = false;
|
|
92
|
+
if (keypadOpen())
|
|
93
|
+
closeKeypad();
|
|
94
|
+
}
|
|
72
95
|
if (!h || h.type === 'room') {
|
|
73
96
|
st.first = null;
|
|
74
97
|
ed.pickedAnchor = null;
|
|
@@ -83,7 +106,7 @@ export const dimTool = {
|
|
|
83
106
|
if (h.type === 'oanchor') {
|
|
84
107
|
const edge = h.part;
|
|
85
108
|
if (print) {
|
|
86
|
-
toast('Print dimensions can’t attach to doors or windows yet. Switch to Driving in the bar below.');
|
|
109
|
+
toast('Print dimensions can’t attach to doors or windows yet. Switch to Driving in the bar below.', { warn: true });
|
|
87
110
|
return;
|
|
88
111
|
}
|
|
89
112
|
if (st.first) {
|
|
@@ -120,13 +143,16 @@ export const dimTool = {
|
|
|
120
143
|
updateHint();
|
|
121
144
|
return;
|
|
122
145
|
}
|
|
123
|
-
toast('Tap a corner, a point, or another door or window edge to measure to.');
|
|
146
|
+
toast('Tap a corner, a point, or another door or window edge to measure to.', { warn: true });
|
|
124
147
|
return;
|
|
125
148
|
}
|
|
126
149
|
if (h.type === 'point') {
|
|
127
150
|
if (!st.first) {
|
|
128
151
|
st.first = h.id;
|
|
129
152
|
ed.sel = [{ type: 'point', id: h.id }];
|
|
153
|
+
// a corner: its angle straight away (close the keypad to measure from the corner to another point instead)
|
|
154
|
+
if (!print)
|
|
155
|
+
st.angle = cornerAngle(h.id, () => { this.st.first = null; this.st.angle = false; ed.sel = []; updateHint(); requestDraw(); }, true);
|
|
130
156
|
updateHint();
|
|
131
157
|
requestDraw();
|
|
132
158
|
return;
|
|
@@ -137,7 +163,30 @@ export const dimTool = {
|
|
|
137
163
|
updateHint();
|
|
138
164
|
return;
|
|
139
165
|
}
|
|
166
|
+
// a wall's edge: its length on that face — or, after a point, the distance from the face to it; a curved wall's,
|
|
167
|
+
// its diameter across that face
|
|
168
|
+
if (h.type === 'wface') {
|
|
169
|
+
const [lid, side] = edgeOf(h.id);
|
|
170
|
+
if (!SK().lines[lid]) {
|
|
171
|
+
dimCurve(lid, side);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (st.first) {
|
|
175
|
+
const p = st.first;
|
|
176
|
+
st.first = null;
|
|
177
|
+
if (print)
|
|
178
|
+
dimPointLine(p, lid);
|
|
179
|
+
else
|
|
180
|
+
dimEdgePoint(lid, side, p);
|
|
181
|
+
updateHint();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
dimLine(lid, print, side);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
140
187
|
if (h.type === 'line') {
|
|
188
|
+
if (h.side)
|
|
189
|
+
ed.wallTap = { id: h.id, side: h.side };
|
|
141
190
|
if (st.first) {
|
|
142
191
|
dimPointLine(st.first, h.id);
|
|
143
192
|
st.first = null;
|
|
@@ -147,8 +196,11 @@ export const dimTool = {
|
|
|
147
196
|
dimLine(h.id, print);
|
|
148
197
|
return;
|
|
149
198
|
}
|
|
150
|
-
if (h.type === 'circle' || h.type === 'arc')
|
|
199
|
+
if (h.type === 'circle' || h.type === 'arc') {
|
|
200
|
+
if (h.side)
|
|
201
|
+
ed.wallTap = { id: h.id, side: h.side };
|
|
151
202
|
dimCurve(h.id);
|
|
203
|
+
}
|
|
152
204
|
},
|
|
153
205
|
hint() {
|
|
154
206
|
if (ed.pickedAnchor)
|
|
@@ -163,13 +215,17 @@ export const dimTool = {
|
|
|
163
215
|
* counts as its nearer edge, so openings are as easy to dimension as points.
|
|
164
216
|
*/
|
|
165
217
|
function pickForDim(ev) {
|
|
166
|
-
const h = hitAny(ev.sx, ev.sy, { touch: ev.touch, only: 'geom' });
|
|
167
|
-
if (!h || h.type !== 'line')
|
|
218
|
+
const h = hitAny(ev.sx, ev.sy, { touch: ev.touch, only: 'geom', faces: true });
|
|
219
|
+
if (!h || (h.type !== 'line' && h.type !== 'wface'))
|
|
168
220
|
return h;
|
|
169
|
-
const
|
|
221
|
+
const lid = h.type === 'wface' ? edgeOf(h.id)[0] : h.id;
|
|
222
|
+
const sk = SK(), l = sk.lines[lid];
|
|
223
|
+
if (!l)
|
|
224
|
+
return h; // (a curved wall's edge)
|
|
225
|
+
const A = sk.points[l.a], B = sk.points[l.b];
|
|
170
226
|
const [wx, wy] = s2w(ev.sx, ev.sy);
|
|
171
227
|
const s = projT(A, B, { x: wx, y: wy }) * hyp(B.x - A.x, B.y - A.y);
|
|
172
|
-
const a = openingOnLineAt(sk,
|
|
228
|
+
const a = openingOnLineAt(sk, lid, s, 2 / mainT().s);
|
|
173
229
|
// weak = the body was tapped: the edge gets chosen to face whatever it's measured to
|
|
174
230
|
return a ? { type: 'oanchor', id: a.id, part: a.edge, weak: true } : h;
|
|
175
231
|
}
|
|
@@ -185,38 +241,272 @@ function sideAwayFromRooms(lid) {
|
|
|
185
241
|
}
|
|
186
242
|
return 1;
|
|
187
243
|
}
|
|
188
|
-
/**
|
|
189
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Tap on a line (or its + chip): open the keypad for its length, or edit the existing length. A wall's is measured on
|
|
246
|
+
* `side`, the edge picked — or, picked whole, on the side a space is on (the side it was tapped on, between two), and
|
|
247
|
+
* along its centerline where there's no space; the keypad can switch it.
|
|
248
|
+
*/
|
|
249
|
+
export function dimLine(lid, print, side) {
|
|
190
250
|
const sk = SK(), l = sk.lines[lid];
|
|
191
251
|
if (!l)
|
|
192
252
|
return;
|
|
253
|
+
const wall = faced(sk, lid);
|
|
193
254
|
if (print) {
|
|
194
255
|
pushHistory();
|
|
195
|
-
|
|
196
|
-
|
|
256
|
+
// (on a wall's edge: that face's length, labelled on that side)
|
|
257
|
+
const f = wall ? side : undefined;
|
|
258
|
+
const off = (36 / V().s) * (f ?? sideAwayFromRooms(lid));
|
|
259
|
+
addCons(sk, { type: 'dist', a: l.a, b: l.b, kind: 'al', value: f ? faceLength(sk, lid, f) : lineLen(sk, l), line: lid, driving: false, layer: 'print', off, ...(f ? { face: f } : {}) });
|
|
197
260
|
afterEdit();
|
|
198
261
|
toast('Print dimension added. Drag it to move it.');
|
|
199
262
|
return;
|
|
200
263
|
}
|
|
201
264
|
const ex = lengthDimOf(sk, lid);
|
|
202
265
|
if (ex) {
|
|
203
|
-
editDim(ex);
|
|
266
|
+
editDim(ex, undefined, false, wall && side ? { face: side } : undefined);
|
|
204
267
|
return;
|
|
205
268
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
},
|
|
219
|
-
|
|
269
|
+
let face = wall ? side ?? defaultFace(sk, lid, ed.wallTap?.id === lid ? ed.wallTap.side : undefined) ?? 0 : 0;
|
|
270
|
+
const len = () => (face ? faceLength(sk, lid, face) : lineLen(sk, l));
|
|
271
|
+
// (while it's being entered, what it measures is what's highlighted: the edge, or the wall)
|
|
272
|
+
const was = ed.sel;
|
|
273
|
+
const show = () => { ed.sel = face ? [{ type: 'wface', id: edgeId(lid, face) }] : [{ type: 'line', id: lid }]; requestDraw(); };
|
|
274
|
+
show();
|
|
275
|
+
const spec = {
|
|
276
|
+
title: 'Length', sub: wall ? lengthSub(sk, lid, face) : '', initial: fmtLen(len()), selectAll: true, laser: true,
|
|
277
|
+
extras: wall ? () => lengthChoices(sk, lid).map(ch => ({
|
|
278
|
+
label: ch.label, on: ch.face === face,
|
|
279
|
+
onPress: () => { face = ch.face; spec.sub = lengthSub(sk, lid, face); show(); if (keypadFresh())
|
|
280
|
+
keypadSetReading(len()); },
|
|
281
|
+
})) : undefined,
|
|
282
|
+
onClose: () => { ed.sel = was; updateCtx(); requestDraw(); },
|
|
283
|
+
onDone: (v, capture) => { if (v > 0)
|
|
284
|
+
addLength(v, capture !== undefined ? { capture } : {}); },
|
|
285
|
+
// (or a length of the job's, from its list: the line takes it, linked to it)
|
|
286
|
+
measurements: offersJobMeasurements() ? {
|
|
287
|
+
kind: 'length', pick: t => {
|
|
288
|
+
const v = jobValue(t, 'length');
|
|
289
|
+
if (v == null)
|
|
290
|
+
return;
|
|
291
|
+
closeKeypad();
|
|
292
|
+
addLength(v, { measurementRef: linkSnapshot(t) });
|
|
293
|
+
},
|
|
294
|
+
} : undefined,
|
|
295
|
+
};
|
|
296
|
+
/** The line's length dimension, driving, measured where the keypad says (`extra`: what it's from). */
|
|
297
|
+
function addLength(v, extra) {
|
|
298
|
+
pushHistory();
|
|
299
|
+
const r = tryAddConstraint({ type: 'dist', a: l.a, b: l.b, kind: 'al', value: v, line: lid, driving: true, off: face ? (face * 30) / V().s : 0, ...(face ? { face } : {}), ...extra });
|
|
300
|
+
// a measurement of the job drives a line, or nothing: not one whose length the drawing already sets
|
|
301
|
+
if (r.ok && r.ref && extra.measurementRef) {
|
|
302
|
+
delete SK().cons[r.id];
|
|
303
|
+
dropHistory();
|
|
304
|
+
toast(LENGTH_SET_ALREADY, { warn: true });
|
|
305
|
+
}
|
|
306
|
+
else if (!r.ok)
|
|
307
|
+
dropHistory();
|
|
308
|
+
afterEdit();
|
|
309
|
+
}
|
|
310
|
+
openKeypad(spec);
|
|
311
|
+
}
|
|
312
|
+
/** Why a measurement of the job can't drive a line the drawing already sets the length of. */
|
|
313
|
+
export const LENGTH_SET_ALREADY = 'The drawing already sets that line’s length. Free it first (make one of its dimensions a reference), then pick the measurement.';
|
|
314
|
+
/** Why a measurement of the job can't drive an angle the drawing already sets. */
|
|
315
|
+
export const ANGLE_SET_ALREADY = 'The drawing already sets that angle. Free it first (make one of its dimensions a reference), then pick the measurement.';
|
|
316
|
+
/**
|
|
317
|
+
* A measurement of the job's as a keypad of `kind` takes it — inches for a length, degrees for an angle (what the host
|
|
318
|
+
* says it is now, or what it was when listed); null, said why, if it has none, or isn't of that kind.
|
|
319
|
+
*/
|
|
320
|
+
function jobValue(t, kind) {
|
|
321
|
+
if ((t.mkind ?? 'length') !== kind) {
|
|
322
|
+
toast(kind === 'angle' ? 'That’s a length. Pick an angle, or type one in.' : 'That’s an angle. Pick a length, or type one in.', { warn: true });
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
const v = linkInfoOf(t)?.value ?? t.value;
|
|
326
|
+
if (v == null || !(v > 0)) {
|
|
327
|
+
toast(kind === 'angle' ? 'That measurement has no angle to use. Pick another, or type one in.' : 'That measurement has no length to use. Pick another, or type one in.', { warn: true });
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
return kind === 'angle' ? v : fromUm(v);
|
|
331
|
+
}
|
|
332
|
+
/* Measuring off a wall's faces (its edges) rather than its centerline. */
|
|
333
|
+
/** A straight wall, whose length can be measured on either face (not a guide). */
|
|
334
|
+
function faced(sk, lid) {
|
|
335
|
+
const l = lid ? sk.lines[lid] : undefined;
|
|
336
|
+
return !!l && l.kind === 'wall' && !l.constr;
|
|
337
|
+
}
|
|
338
|
+
/** What a wall's length (a curved one's diameter) is measured on, as the keypad says it. */
|
|
339
|
+
const lengthSub = (sk, lid, face) => (face ? edgeLabel(sk, lid, face) : 'Wall centerline');
|
|
340
|
+
/** A wall's face as an icon: the one on top, at the bottom, left or right (as the page shows it) — or its centerline. */
|
|
341
|
+
function faceIcon(sk, lid, side) {
|
|
342
|
+
const l = sk.lines[lid], a = sk.points[l.a], b = sk.points[l.b], level = Math.abs(b.x - a.x) >= Math.abs(b.y - a.y);
|
|
343
|
+
if (!side)
|
|
344
|
+
return level ? 'faceMidH' : 'faceMidV';
|
|
345
|
+
// (the face's outward direction: the left of A → B for +1; y is up)
|
|
346
|
+
const nx = -(b.y - a.y) * side, ny = (b.x - a.x) * side;
|
|
347
|
+
return level ? (ny > 0 ? 'faceT' : 'faceB') : nx > 0 ? 'faceR' : 'faceL';
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* A wall's length: on its inside face (or one space's side, or — named by the way they face — its top or left), its
|
|
351
|
+
* centerline, its other face.
|
|
352
|
+
*/
|
|
353
|
+
function lengthChoices(sk, lid) {
|
|
354
|
+
const n = faceName(sk, lid, 1), byWay = ['Top', 'Bottom', 'Left', 'Right'].includes(n);
|
|
355
|
+
const first = byWay ? (n === 'Top' || n === 'Left' ? 1 : -1) : spaceBeside(sk, lid, 1) ? 1 : -1;
|
|
356
|
+
const side = (f) => ({ label: edgeLabel(sk, lid, f), short: faceName(sk, lid, f).replace(/ side$/, ''), icon: faceIcon(sk, lid, f), face: f });
|
|
357
|
+
return [side(first), { label: 'Centerline', short: 'Center', icon: faceIcon(sk, lid, 0), face: 0 }, side(first === 1 ? -1 : 1)];
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* A curved wall's diameter (or radius): across its inner face, its centerline, its outer face — its outer face first
|
|
361
|
+
* when that's the one a space is against (a round column in a room).
|
|
362
|
+
*/
|
|
363
|
+
function curveChoices(sk, id) {
|
|
364
|
+
const first = spaceBeside(sk, id, 1) && !spaceBeside(sk, id, -1) ? 1 : -1;
|
|
365
|
+
const side = (f) => ({ label: edgeLabel(sk, id, f), short: faceName(sk, id, f), icon: f === 1 ? 'ringOut' : 'ringIn', face: f });
|
|
366
|
+
return [side(first), { label: 'Centerline', short: 'Center', icon: 'ringMid', face: 0 }, side(first === 1 ? -1 : 1)];
|
|
367
|
+
}
|
|
368
|
+
/** The faces a dimension on walls can be measured on, as choices; none when it isn't on a wall. */
|
|
369
|
+
export function faceChoices(sk, c) {
|
|
370
|
+
if (c.type === 'dist' && c.line && (!c.kind || c.kind === 'al') && faced(sk, c.line))
|
|
371
|
+
return lengthChoices(sk, c.line);
|
|
372
|
+
if (c.type === 'diam' && c.c && !sk.lines[c.c] && wallOf(sk, c.c))
|
|
373
|
+
return curveChoices(sk, c.c);
|
|
374
|
+
if (c.type === 'pldist' && c.l && sk.lines[c.l] && sk.points[c.p]) {
|
|
375
|
+
const w1 = faced(sk, c.l), w2 = faced(sk, c.l2);
|
|
376
|
+
if (!w1 && !w2)
|
|
377
|
+
return [];
|
|
378
|
+
const k = (v) => [w1 ? v : 0, w2 ? v : 0];
|
|
379
|
+
if (w1 && w2) {
|
|
380
|
+
const l = sk.lines[c.l], a = sk.points[l.a], b = sk.points[l.b], H = Math.abs(b.x - a.x) >= Math.abs(b.y - a.y) ? 'H' : '';
|
|
381
|
+
return [
|
|
382
|
+
{ label: 'Inside faces', short: 'Inside', icon: 'spanIn' + H, faces: k(1) },
|
|
383
|
+
{ label: 'Centerlines', short: 'Centers', icon: 'spanMid' + H, faces: [0, 0] },
|
|
384
|
+
{ label: 'Outside faces', short: 'Outside', icon: 'spanOut' + H, faces: k(-1) },
|
|
385
|
+
];
|
|
386
|
+
}
|
|
387
|
+
// one wall: its face toward the other end, its centerline, its far face
|
|
388
|
+
const wl = w1 ? c.l : c.l2, other = w1 ? sk.points[c.p] : sk.points[sk.lines[c.l].a], toward = sideOf(sk, wl, other);
|
|
389
|
+
return [
|
|
390
|
+
{ label: 'Near face', short: 'Near', icon: faceIcon(sk, wl, toward), faces: k(1) },
|
|
391
|
+
{ label: 'Centerline', short: 'Center', icon: faceIcon(sk, wl, 0), faces: [0, 0] },
|
|
392
|
+
{ label: 'Far face', short: 'Far', icon: faceIcon(sk, wl, -toward), faces: k(-1) },
|
|
393
|
+
];
|
|
394
|
+
}
|
|
395
|
+
return [];
|
|
396
|
+
}
|
|
397
|
+
/** Whether a dimension is measured on the faces a choice says. */
|
|
398
|
+
export function onChoice(c, ch) {
|
|
399
|
+
if (ch.face !== undefined)
|
|
400
|
+
return (c.face ?? 0) === ch.face;
|
|
401
|
+
return !!ch.faces && (c.faces?.[0] ?? 0) === ch.faces[0] && (c.faces?.[1] ?? 0) === ch.faces[1];
|
|
402
|
+
}
|
|
403
|
+
/** Measure a dimension on the faces a choice says (its number left as it is). A length's label moves to that side. */
|
|
404
|
+
function applyFace(c, ch) {
|
|
405
|
+
if (ch.face !== undefined) {
|
|
406
|
+
if (ch.face)
|
|
407
|
+
c.face = ch.face;
|
|
408
|
+
else
|
|
409
|
+
delete c.face;
|
|
410
|
+
if (ch.face && c.type === 'dist') {
|
|
411
|
+
const l = SK().lines[c.line], k = l && c.a === l.a ? 1 : -1;
|
|
412
|
+
c.off = (Math.abs(c.off ?? 0) || 30 / V().s) * ch.face * k;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (ch.faces) {
|
|
416
|
+
if (ch.l2)
|
|
417
|
+
c.l2 = ch.l2;
|
|
418
|
+
if (ch.faces[0] || ch.faces[1])
|
|
419
|
+
c.faces = [ch.faces[0], ch.faces[1]];
|
|
420
|
+
else
|
|
421
|
+
delete c.faces;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/** A copy of a dimension, measured on the faces a choice says. */
|
|
425
|
+
function withFace(c, ch) {
|
|
426
|
+
const n = { ...c };
|
|
427
|
+
if (ch)
|
|
428
|
+
applyFace(n, ch);
|
|
429
|
+
return n;
|
|
430
|
+
}
|
|
431
|
+
/** What a dimension's faces are, to put back. */
|
|
432
|
+
const faceState = (c) => ({ face: c.face, faces: c.faces, l2: c.l2, off: c.off });
|
|
433
|
+
function restoreFace(c, f) {
|
|
434
|
+
if (f.face === undefined)
|
|
435
|
+
delete c.face;
|
|
436
|
+
else
|
|
437
|
+
c.face = f.face;
|
|
438
|
+
if (f.faces === undefined)
|
|
439
|
+
delete c.faces;
|
|
440
|
+
else
|
|
441
|
+
c.faces = f.faces;
|
|
442
|
+
if (f.l2 === undefined)
|
|
443
|
+
delete c.l2;
|
|
444
|
+
else
|
|
445
|
+
c.l2 = f.l2;
|
|
446
|
+
if (f.off === undefined)
|
|
447
|
+
delete c.off;
|
|
448
|
+
else
|
|
449
|
+
c.off = f.off;
|
|
450
|
+
}
|
|
451
|
+
/** Measure a dimension on other faces of its walls, the drawing kept as it is: its number is what it measures there now. */
|
|
452
|
+
export function setDimFace(id, ch) {
|
|
453
|
+
const sk = SK(), c = sk.cons[id];
|
|
454
|
+
if (!c || onChoice(c, ch))
|
|
455
|
+
return;
|
|
456
|
+
pushHistory();
|
|
457
|
+
applyFace(c, ch);
|
|
458
|
+
if (c.driving !== false)
|
|
459
|
+
c.value = measure(sk, c);
|
|
460
|
+
afterEdit();
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* The face of wall `lid` to measure from, to something at `at`: +1 its face toward it, −1 its far face, 0 its centerline.
|
|
464
|
+
* `side` is the edge picked (+1 / −1: the left / right of its A → B; 0 its centerline). Picked whole, to a point: the
|
|
465
|
+
* face toward it where a space is against it. To another wall (`wall`): its room's side, as its length is measured —
|
|
466
|
+
* the side toward the other between two spaces; with no space beside it, its centerline.
|
|
467
|
+
*/
|
|
468
|
+
function faceToward(sk, lid, at, side, wall = false) {
|
|
469
|
+
if (!faced(sk, lid))
|
|
470
|
+
return 0;
|
|
471
|
+
const toward = sideOf(sk, lid, at);
|
|
472
|
+
if (side === undefined)
|
|
473
|
+
return spaceBeside(sk, lid, toward) ? 1 : wall && spaceBeside(sk, lid, toward === 1 ? -1 : 1) ? -1 : 0;
|
|
474
|
+
return side === 0 ? 0 : side === toward ? 1 : -1;
|
|
475
|
+
}
|
|
476
|
+
/** From a wall's edge to a point: square to the wall, from that face. */
|
|
477
|
+
export function dimEdgePoint(lid, side, p) {
|
|
478
|
+
const sk = SK(), l = sk.lines[lid];
|
|
479
|
+
// (one of the wall's own ends is no distance from it)
|
|
480
|
+
if (!l || p === l.a || p === l.b) {
|
|
481
|
+
toast('Pick a point off the wall to measure from its edge.', { warn: true });
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
dimPointLine(p, lid, { faces: [faceToward(sk, lid, sk.points[p], side), 0], title: 'Distance from the edge' });
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Two things picked, at least one a wall's edge: the distance between them — two edges of one wall, its thickness
|
|
488
|
+
* (`thickness` opens it); of two walls, face to face; an edge and a line, from that face; an edge and a point, from it.
|
|
489
|
+
*/
|
|
490
|
+
export function dimEdges(items, thickness) {
|
|
491
|
+
const edges = items.filter(x => x.type === 'wface').map(x => edgeOf(x.id));
|
|
492
|
+
const other = items.find(x => x.type !== 'wface');
|
|
493
|
+
if (edges.length === 2) {
|
|
494
|
+
const [[a, sa], [b, sb]] = edges;
|
|
495
|
+
if (a === b) {
|
|
496
|
+
thickness(a);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (SK().lines[a] && SK().lines[b])
|
|
500
|
+
dimTwoLines(a, b, { [a]: sa, [b]: sb });
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
if (edges.length !== 1 || !other)
|
|
504
|
+
return;
|
|
505
|
+
const [lid, side] = edges[0];
|
|
506
|
+
if (other.type === 'point')
|
|
507
|
+
dimEdgePoint(lid, side, other.id);
|
|
508
|
+
else if (other.type === 'line')
|
|
509
|
+
dimTwoLines(lid, other.id, { [lid]: side });
|
|
220
510
|
}
|
|
221
511
|
export function dimPoints(a, b, print) {
|
|
222
512
|
const sk = SK(), A = sk.points[a], B = sk.points[b];
|
|
@@ -234,9 +524,9 @@ export function dimPoints(a, b, print) {
|
|
|
234
524
|
openKeypad({
|
|
235
525
|
onClose: () => { ed.preview = null; requestDraw(); },
|
|
236
526
|
title: 'Distance', initial: fmtLen(L), selectAll: true, laser: true,
|
|
237
|
-
onDone: v => {
|
|
527
|
+
onDone: (v, capture) => {
|
|
238
528
|
pushHistory();
|
|
239
|
-
const r = tryAddConstraint({ type: 'dist', a, b, kind: 'al', value: v, driving: true, off: 0 });
|
|
529
|
+
const r = tryAddConstraint({ type: 'dist', a, b, kind: 'al', value: v, driving: true, off: 0, ...(capture !== undefined ? { capture } : {}) });
|
|
240
530
|
if (!r.ok)
|
|
241
531
|
dropHistory();
|
|
242
532
|
ed.sel = [];
|
|
@@ -246,9 +536,11 @@ export function dimPoints(a, b, print) {
|
|
|
246
536
|
}
|
|
247
537
|
/**
|
|
248
538
|
* Dimension between two lines: their distance if they're parallel (measured square to them, and kept parallel),
|
|
249
|
-
* otherwise the angle between them. An existing dimension between them is edited instead of stacking another.
|
|
539
|
+
* otherwise the angle between them. An existing dimension between them is edited instead of stacking another. Between
|
|
540
|
+
* walls it's measured on `sides`, the edges picked (+1 / −1: the face to the left / right of A → B; 0 its centerline) —
|
|
541
|
+
* or, picked whole, each wall's room side (see faceToward).
|
|
250
542
|
*/
|
|
251
|
-
export function dimTwoLines(l1, l2) {
|
|
543
|
+
export function dimTwoLines(l1, l2, sides = {}) {
|
|
252
544
|
const sk = SK(), L1 = sk.lines[l1], L2 = sk.lines[l2];
|
|
253
545
|
if (!L1 || !L2)
|
|
254
546
|
return;
|
|
@@ -264,8 +556,15 @@ export function dimTwoLines(l1, l2) {
|
|
|
264
556
|
return (c.l1 === l1 && c.l2 === l2) || (c.l1 === l2 && c.l2 === l1);
|
|
265
557
|
return (c.l === l1 && (c.p === L2.a || c.p === L2.b)) || (c.l === l2 && (c.p === L1.a || c.p === L1.b));
|
|
266
558
|
});
|
|
559
|
+
/** The faces between line `l` (the one measured square to) and the other, from point `p` on the other. */
|
|
560
|
+
const facesFrom = (l, p) => {
|
|
561
|
+
const o = l === l1 ? l2 : l1, ol = sk.lines[o], on = sk.lines[l];
|
|
562
|
+
return { faces: [faceToward(sk, l, sk.points[p], sides[l], true), faceToward(sk, o, sk.points[on.a], sides[o], true)], ...(faced(sk, o) && ol ? { l2: o } : {}) };
|
|
563
|
+
};
|
|
267
564
|
if (existing) {
|
|
268
|
-
|
|
565
|
+
const c = sk.cons[existing];
|
|
566
|
+
// (the edges picked: it's measured between them now)
|
|
567
|
+
editDim(existing, undefined, false, c.type === 'pldist' && Object.keys(sides).length ? facesFrom(c.l, c.p) : undefined);
|
|
269
568
|
return;
|
|
270
569
|
}
|
|
271
570
|
if (!parallel) {
|
|
@@ -276,23 +575,38 @@ export function dimTwoLines(l1, l2) {
|
|
|
276
575
|
const A = sk.points[L1.a], B = sk.points[L1.b], len2 = (B.x - A.x) ** 2 + (B.y - A.y) ** 2 || 1;
|
|
277
576
|
const t = (pid) => { const P = sk.points[pid]; return ((P.x - A.x) * (B.x - A.x) + (P.y - A.y) * (B.y - A.y)) / len2; };
|
|
278
577
|
const p = Math.abs(t(L2.a) - 0.5) <= Math.abs(t(L2.b) - 0.5) ? L2.a : L2.b;
|
|
279
|
-
|
|
578
|
+
const f = facesFrom(l1, p);
|
|
579
|
+
dimPointLine(p, l1, { keepParallel: l2, title: 'Distance between lines', faces: f.faces, l2: f.l2 });
|
|
280
580
|
}
|
|
581
|
+
/**
|
|
582
|
+
* The distance from point `p` to line `lid`, square to it. On a wall it's measured from `faces[0]`, its face toward
|
|
583
|
+
* the point (+1), its far face (−1) or its centerline (0) — by default the face toward it where a space is against it;
|
|
584
|
+
* `faces[1]` is the same for wall `l2`, which the point is the end of (a distance between two walls). The keypad can
|
|
585
|
+
* switch them.
|
|
586
|
+
*/
|
|
281
587
|
export function dimPointLine(p, lid, opts = {}) {
|
|
282
588
|
const sk = SK(), l = sk.lines[lid], P = sk.points[p], A = sk.points[l.a], B = sk.points[l.b];
|
|
283
589
|
const sd = ((B.x - A.x) * (P.y - A.y) - (B.y - A.y) * (P.x - A.x)) / (hyp(B.x - A.x, B.y - A.y) || 1);
|
|
284
590
|
const sign = sd < 0 ? -1 : 1;
|
|
285
|
-
const
|
|
286
|
-
|
|
591
|
+
const c = { type: 'pldist', p, l: lid, value: 0, sign, driving: true, ...(opts.l2 ? { l2: opts.l2 } : {}) };
|
|
592
|
+
applyFace(c, { faces: opts.faces ?? [faceToward(sk, lid, P), 0] });
|
|
593
|
+
const choices = faceChoices(sk, c);
|
|
287
594
|
ed.sel = [];
|
|
288
|
-
ed.preview = (
|
|
595
|
+
ed.preview = (cx, T) => { const e = dimEnds(sk, c); if (e)
|
|
596
|
+
drawPendingDim(cx, T, { from: e.a, to: e.b, a: e.a, b: e.b, nx: 0, ny: 0, off: 0, label: fmtLen(kpTyped() ?? measure(sk, c)) }); };
|
|
289
597
|
requestDraw();
|
|
290
|
-
|
|
598
|
+
const spec = {
|
|
291
599
|
onClose: () => { ed.preview = null; requestDraw(); },
|
|
292
|
-
|
|
600
|
+
// (which faces it's measured between: the choices beside the keys)
|
|
601
|
+
title: opts.title || 'Distance to line', sub: opts.keepParallel ? 'Measured square to the lines' : '', initial: fmtLen(measure(sk, c)), selectAll: true,
|
|
602
|
+
extras: choices.length ? () => choices.map(ch => ({
|
|
603
|
+
label: ch.label, on: onChoice(c, ch),
|
|
604
|
+
onPress: () => { applyFace(c, ch); requestOverlayDraw(); if (keypadFresh())
|
|
605
|
+
keypadSetReading(measure(sk, c)); },
|
|
606
|
+
})) : undefined,
|
|
293
607
|
onDone: v => {
|
|
294
608
|
pushHistory();
|
|
295
|
-
const r = tryAddConstraint({
|
|
609
|
+
const r = tryAddConstraint({ ...c, value: v });
|
|
296
610
|
if (!r.ok)
|
|
297
611
|
dropHistory();
|
|
298
612
|
// a distance between two lines only holds if they stay parallel (no-op if something already keeps them so)
|
|
@@ -301,49 +615,102 @@ export function dimPointLine(p, lid, opts = {}) {
|
|
|
301
615
|
ed.sel = [];
|
|
302
616
|
afterEdit();
|
|
303
617
|
},
|
|
304
|
-
}
|
|
618
|
+
};
|
|
619
|
+
openKeypad(spec);
|
|
305
620
|
}
|
|
306
|
-
|
|
307
|
-
|
|
621
|
+
/**
|
|
622
|
+
* A circle's or arc's diameter: its keypad, or its dimension's. A curved wall's is measured across `side`, the edge
|
|
623
|
+
* picked (+1 its outer face, −1 its inner) — or, picked whole, the side a space is on (the side it was tapped on,
|
|
624
|
+
* between two), and its centerline where there's no space; the keypad can switch it.
|
|
625
|
+
*/
|
|
626
|
+
export function dimCurve(id, side) {
|
|
627
|
+
const sk = SK(), wall = !!wallOf(sk, id);
|
|
308
628
|
for (const k in sk.cons) {
|
|
309
629
|
const c = sk.cons[k];
|
|
310
630
|
if (c.type === 'diam' && c.c === id) {
|
|
311
|
-
editDim(k);
|
|
631
|
+
editDim(k, undefined, false, wall && side ? { face: side } : undefined);
|
|
312
632
|
return;
|
|
313
633
|
}
|
|
314
634
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
635
|
+
let face = wall ? side ?? defaultFace(sk, id, ed.wallTap?.id === id ? ed.wallTap.side : undefined) ?? 0 : 0;
|
|
636
|
+
const now = () => measure(sk, { type: 'diam', c: id, ...(face ? { face } : {}) });
|
|
637
|
+
// (while it's being entered, what it measures across is what's highlighted: the edge, or the wall)
|
|
638
|
+
const was = ed.sel, type = sk.circles[id] ? 'circle' : 'arc';
|
|
639
|
+
const show = () => { ed.sel = face ? [{ type: 'wface', id: edgeId(id, face) }] : [{ type, id }]; requestDraw(); };
|
|
640
|
+
show();
|
|
641
|
+
const spec = {
|
|
642
|
+
title: 'Diameter', sub: wall ? lengthSub(sk, id, face) : '', initial: fmtLen(now()), selectAll: true,
|
|
643
|
+
extras: wall ? () => curveChoices(sk, id).map(ch => ({
|
|
644
|
+
label: ch.label, on: ch.face === face,
|
|
645
|
+
onPress: () => { face = ch.face; spec.sub = lengthSub(sk, id, face); show(); if (keypadFresh())
|
|
646
|
+
keypadSetReading(now()); },
|
|
647
|
+
})) : undefined,
|
|
648
|
+
onClose: () => { ed.sel = was; requestDraw(); },
|
|
320
649
|
onDone: v => {
|
|
321
650
|
if (v <= 0)
|
|
322
651
|
return;
|
|
323
652
|
pushHistory();
|
|
324
|
-
const r = tryAddConstraint({ type: 'diam', c: id, value: v, driving: true });
|
|
653
|
+
const r = tryAddConstraint({ type: 'diam', c: id, value: v, driving: true, ...(face ? { face } : {}) });
|
|
325
654
|
if (!r.ok)
|
|
326
655
|
dropHistory();
|
|
656
|
+
// (as a length: the dimension made, nothing left highlighted)
|
|
657
|
+
ed.sel = [];
|
|
327
658
|
afterEdit();
|
|
328
659
|
},
|
|
329
|
-
}
|
|
660
|
+
};
|
|
661
|
+
openKeypad(spec);
|
|
662
|
+
}
|
|
663
|
+
/** A corner's angle (two lines meeting at `pid`): its angle dimension's keypad, or a new one's. False if it isn't a corner. */
|
|
664
|
+
export function cornerAngle(pid, done, modeless = false) {
|
|
665
|
+
const sk = SK(), pair = cornerLines(sk, pid);
|
|
666
|
+
if (!pair)
|
|
667
|
+
return false;
|
|
668
|
+
// edit the corner's existing angle dimension rather than stacking a second one
|
|
669
|
+
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])); });
|
|
670
|
+
if (ex)
|
|
671
|
+
editDim(ex, done, modeless);
|
|
672
|
+
else
|
|
673
|
+
dimAngle(pair[0], pair[1], done, modeless);
|
|
674
|
+
return true;
|
|
330
675
|
}
|
|
331
|
-
export function dimAngle(l1, l2) {
|
|
676
|
+
export function dimAngle(l1, l2, done, modeless = false) {
|
|
332
677
|
const c = { type: 'angle', l1, l2, value: 0, driving: true };
|
|
333
678
|
const cur = measure(SK(), c);
|
|
679
|
+
/** The angle dimension, driving, at `v` degrees (`extra`: what it's from). */
|
|
680
|
+
const addAngle = (v, extra) => {
|
|
681
|
+
pushHistory();
|
|
682
|
+
const r = tryAddConstraint({ ...c, value: v, ...extra });
|
|
683
|
+
// a measurement of the job drives an angle, or nothing: not one the drawing already sets
|
|
684
|
+
if (r.ok && r.ref && extra.measurementRef) {
|
|
685
|
+
delete SK().cons[r.id];
|
|
686
|
+
dropHistory();
|
|
687
|
+
toast(ANGLE_SET_ALREADY, { warn: true });
|
|
688
|
+
}
|
|
689
|
+
else if (!r.ok)
|
|
690
|
+
dropHistory();
|
|
691
|
+
afterEdit();
|
|
692
|
+
done?.();
|
|
693
|
+
};
|
|
334
694
|
openKeypad({
|
|
335
|
-
title: 'Angle', sub: 'Degrees', numeric: true, initial: String(Math.round(cur * 10) / 10), selectAll: true,
|
|
336
|
-
onDone: v => {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
695
|
+
title: 'Angle', sub: 'Degrees', numeric: true, initial: String(Math.round(cur * 10) / 10), selectAll: true, modeless,
|
|
696
|
+
onDone: v => addAngle(v, {}),
|
|
697
|
+
// (or an angle of the job's, from its list: the corner takes it, linked to it)
|
|
698
|
+
measurements: offersJobMeasurements() ? {
|
|
699
|
+
kind: 'angle', pick: t => {
|
|
700
|
+
const v = jobValue(t, 'angle');
|
|
701
|
+
if (v == null)
|
|
702
|
+
return;
|
|
703
|
+
closeKeypad();
|
|
704
|
+
addAngle(v, { measurementRef: linkSnapshot(t) });
|
|
705
|
+
},
|
|
706
|
+
} : undefined,
|
|
344
707
|
});
|
|
345
708
|
}
|
|
346
|
-
|
|
709
|
+
/**
|
|
710
|
+
* The keypad for a dimension. One measured on walls can be switched to their other faces (or centerlines) there — to
|
|
711
|
+
* `pick` straight away — taken with the value on Apply.
|
|
712
|
+
*/
|
|
713
|
+
export function editDim(id, done, modeless = false, pick) {
|
|
347
714
|
const sk = SK(), c = sk.cons[id];
|
|
348
715
|
if (!c)
|
|
349
716
|
return;
|
|
@@ -351,57 +718,175 @@ export function editDim(id) {
|
|
|
351
718
|
requestDraw();
|
|
352
719
|
updateCtx();
|
|
353
720
|
const isAng = c.type === 'angle';
|
|
354
|
-
|
|
355
|
-
const
|
|
356
|
-
|
|
721
|
+
// a fillet's radius is kept as its diameter: shown and entered as the radius
|
|
722
|
+
const k = c.rad ? 2 : 1;
|
|
723
|
+
let face = pick && !onChoice(c, pick) ? pick : null;
|
|
724
|
+
/** What it measures on the faces chosen (as it stands, for one that doesn't change them). */
|
|
725
|
+
const now = () => (face ? measure(sk, withFace(c, face)) : c.driving === false ? measure(sk, c) : c.value) / k;
|
|
726
|
+
const title = c.type === 'diam' ? (c.rad ? 'Radius' : 'Diameter') : isAng ? 'Angle' : c.type === 'pldist' ? 'Offset distance' : c.line ? 'Length' : 'Distance';
|
|
727
|
+
// (which faces it's measured on: the choices beside the keys)
|
|
728
|
+
const spec = {
|
|
357
729
|
title,
|
|
358
730
|
sub: c.driving === false ? (c.layer === 'print' ? 'Print dimension (label only)' : 'Reference — not driving the shape') : 'Driving dimension',
|
|
359
|
-
numeric: isAng, initial: isAng ? String(Math.round(
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
731
|
+
numeric: isAng, initial: isAng ? String(Math.round(now() * 10) / 10) : fmtLen(now()), selectAll: true, laser: !isAng, modeless,
|
|
732
|
+
// (just the faces it's measured on: Reference and Delete are the bar's, for a dimension selected)
|
|
733
|
+
extras: () => faceChoices(sk, withFace(c, face)).map(ch => ({
|
|
734
|
+
label: ch.label, on: onChoice(withFace(c, face), ch),
|
|
735
|
+
onPress: () => { face = onChoice(c, ch) ? null : { ...ch, l2: face?.l2 ?? ch.l2 }; if (keypadFresh())
|
|
736
|
+
keypadSetReading(now()); },
|
|
737
|
+
})),
|
|
738
|
+
onDone: (v, capture) => { setDimValue(id, v * k, face ?? undefined, { capture }); done?.(); },
|
|
739
|
+
// a line's length or an angle: or one of the job's, from its list — the dimension takes it, linked to it (a
|
|
740
|
+
// dimension already linked to another, re-linked)
|
|
741
|
+
measurements: ((c.type === 'dist' && c.line) || isAng) && c.layer !== 'print' && offersJobMeasurements() ? {
|
|
742
|
+
kind: isAng ? 'angle' : 'length', pick: t => {
|
|
743
|
+
const v = jobValue(t, isAng ? 'angle' : 'length');
|
|
744
|
+
if (v == null)
|
|
745
|
+
return;
|
|
746
|
+
closeKeypad();
|
|
747
|
+
setDimValue(id, v, face ?? undefined, { link: linkSnapshot(t), asked: true });
|
|
748
|
+
done?.();
|
|
749
|
+
},
|
|
750
|
+
} : undefined,
|
|
751
|
+
};
|
|
752
|
+
openKeypad(spec);
|
|
366
753
|
}
|
|
367
754
|
const fmtValue = (c, v) => (c.type === 'angle' ? fmtAng(v) : fmtLen(v));
|
|
368
|
-
|
|
755
|
+
/**
|
|
756
|
+
* For a cabinet's width or height on a wall face: which of its corners to keep still while the new size is met, in
|
|
757
|
+
* the order to try — its bottom, then its top (height); its left side, then its right (width). Empty otherwise.
|
|
758
|
+
*/
|
|
759
|
+
function holdFirst(sk, id) {
|
|
760
|
+
for (const cb of Object.values(sk.cabs ?? {})) {
|
|
761
|
+
if (cb.h === id)
|
|
762
|
+
return [[cb.bl, cb.br], [cb.tl, cb.tr]];
|
|
763
|
+
if (cb.w === id)
|
|
764
|
+
return [[cb.bl, cb.tl], [cb.br, cb.tr]];
|
|
765
|
+
}
|
|
766
|
+
return [];
|
|
767
|
+
}
|
|
768
|
+
/** What a new value brings with it, once it's taken: a link (or none), and the laser reading it's from (or none). */
|
|
769
|
+
function takeLink(c, o) {
|
|
770
|
+
if (o.link) {
|
|
771
|
+
c.measurementRef = o.link;
|
|
772
|
+
delete c.measurementId;
|
|
773
|
+
}
|
|
774
|
+
else if (o.link === null)
|
|
775
|
+
delete c.measurementRef;
|
|
776
|
+
// (a reading's record is kept for saving to the group; any other value isn't a reading's)
|
|
777
|
+
if (o.capture !== undefined && !c.measurementRef)
|
|
778
|
+
c.capture = o.capture;
|
|
779
|
+
else if (!o.quiet)
|
|
780
|
+
delete c.capture;
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Give dimension `id` value `v`, solving the drawing to it (or explaining why it can't). Whether it took — false too
|
|
784
|
+
* when the person is asked first, for a dimension linked to a measurement of the job (see editor/linkWrite.ts).
|
|
785
|
+
*/
|
|
786
|
+
export function setDimValue(id, v, face, opts = {}) {
|
|
369
787
|
const sk = SK(), c = sk.cons[id];
|
|
370
788
|
if (!c)
|
|
371
|
-
return;
|
|
789
|
+
return false;
|
|
372
790
|
if (c.layer === 'print') {
|
|
373
|
-
toast('Print dimensions follow the drawing. Change a driving dimension instead.');
|
|
374
|
-
return;
|
|
791
|
+
toast('Print dimensions follow the drawing. Change a driving dimension instead.', { warn: true });
|
|
792
|
+
return false;
|
|
793
|
+
}
|
|
794
|
+
if (c.measurementRef && opts.link === undefined && !opts.asked && !opts.quiet && changesValue(c, v)) {
|
|
795
|
+
askLinkedChange(c, v, unlink => setDimValue(id, v, face, { ...opts, asked: true, ...(unlink ? { link: null } : {}) }), opts.capture);
|
|
796
|
+
return false;
|
|
375
797
|
}
|
|
798
|
+
const push = opts.quiet ? () => { } : pushHistory, drop = opts.quiet ? () => { } : dropHistory;
|
|
376
799
|
if (c.driving === false) {
|
|
377
|
-
|
|
800
|
+
push();
|
|
378
801
|
delete sk.cons[id];
|
|
379
|
-
const
|
|
802
|
+
const n = withFace(Object.assign({}, c, { driving: true, value: v }), face ?? null);
|
|
803
|
+
delete n.auto; // entered: a measurement now
|
|
804
|
+
takeLink(n, opts);
|
|
805
|
+
const r = tryAddConstraint(n);
|
|
380
806
|
if (!r.ok) {
|
|
381
807
|
sk.cons[id] = c;
|
|
382
|
-
|
|
808
|
+
drop();
|
|
809
|
+
}
|
|
810
|
+
else if (r.id) {
|
|
811
|
+
// a cabinet's size on a wall face: it's this one now
|
|
812
|
+
for (const cb of Object.values(sk.cabs ?? {})) {
|
|
813
|
+
if (cb.w === id)
|
|
814
|
+
cb.w = r.id;
|
|
815
|
+
if (cb.h === id)
|
|
816
|
+
cb.h = r.id;
|
|
817
|
+
}
|
|
383
818
|
}
|
|
384
819
|
afterEdit();
|
|
385
|
-
return;
|
|
820
|
+
return r.ok;
|
|
386
821
|
}
|
|
387
|
-
|
|
388
|
-
const old = c.value;
|
|
822
|
+
push();
|
|
823
|
+
const old = c.value, wasAuto = c.auto, was = overflow(sk), oldFace = faceState(c);
|
|
389
824
|
c.value = v;
|
|
390
|
-
|
|
391
|
-
|
|
825
|
+
if (face)
|
|
826
|
+
applyFace(c, face);
|
|
827
|
+
delete c.auto; // entered: a measurement now
|
|
828
|
+
const snap = fitState(sk);
|
|
829
|
+
// a cabinet's own size grows from the side it stands on: its height upward (its foot stays on the floor), its
|
|
830
|
+
// width to the right — and only the other way if that would run past the wall
|
|
831
|
+
const holding = (hold) => {
|
|
832
|
+
// (held exactly: fixed points aren't moved by the solver at all; put back as they were after)
|
|
833
|
+
const pts = hold.map(pid => sk.points[pid]).filter(Boolean), had = pts.map(p => p.fixed);
|
|
834
|
+
pts.forEach(p => { p.fixed = true; });
|
|
835
|
+
const r = solver.solve(sk);
|
|
836
|
+
pts.forEach((p, i) => { if (had[i])
|
|
837
|
+
p.fixed = true;
|
|
838
|
+
else
|
|
839
|
+
delete p.fixed; });
|
|
840
|
+
return r;
|
|
841
|
+
};
|
|
842
|
+
let s = null, fit = false, solved = false;
|
|
843
|
+
// (a cabinet's own size: its sides in order; any other dimension: as it lands, then keeping each thing it ties still)
|
|
844
|
+
const own = holdFirst(sk, id), holds = own.length ? [...own, null] : [null, ...holdGroups(sk, c)];
|
|
845
|
+
for (const hold of holds) {
|
|
846
|
+
if (s)
|
|
847
|
+
restoreFit(sk, snap);
|
|
848
|
+
s = hold ? holding(hold) : solver.solve(sk);
|
|
849
|
+
solved ||= s.ok;
|
|
850
|
+
if (s.ok && !pushedOut(was, overflow(sk))) {
|
|
851
|
+
fit = true;
|
|
852
|
+
break;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
s = s;
|
|
856
|
+
// a value that would push anything past its wall (or make a wall shorter than what's on it) is refused
|
|
857
|
+
if (solved && !fit) {
|
|
858
|
+
restoreFit(sk, snap);
|
|
859
|
+
c.value = old;
|
|
860
|
+
restoreFace(c, oldFace);
|
|
861
|
+
if (wasAuto)
|
|
862
|
+
c.auto = true;
|
|
863
|
+
drop();
|
|
864
|
+
if (!opts.quiet)
|
|
865
|
+
toast(pastWallMsg(), { warn: true });
|
|
866
|
+
afterEdit();
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
392
869
|
if (!s.ok) {
|
|
393
|
-
|
|
870
|
+
restoreFit(sk, snap);
|
|
394
871
|
c.value = old;
|
|
395
|
-
|
|
872
|
+
restoreFace(c, oldFace);
|
|
873
|
+
if (wasAuto)
|
|
874
|
+
c.auto = true;
|
|
875
|
+
drop();
|
|
396
876
|
const bad = s.bad.filter(cid => cid !== id && sk.cons[cid]);
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
877
|
+
if (!opts.quiet)
|
|
878
|
+
showConflict({
|
|
879
|
+
title: fmtValue(c, v) + ' doesn’t fit',
|
|
880
|
+
msg: 'The other constraints can’t hold with that value. Remove one of these, or try a different number.',
|
|
881
|
+
ids: bad.slice(0, 8),
|
|
882
|
+
retry: () => setDimValue(id, v, face, { ...opts, asked: true }),
|
|
883
|
+
});
|
|
884
|
+
afterEdit();
|
|
885
|
+
return false;
|
|
403
886
|
}
|
|
887
|
+
takeLink(c, opts);
|
|
404
888
|
afterEdit();
|
|
889
|
+
return true;
|
|
405
890
|
}
|
|
406
891
|
export function toggleRef(id) {
|
|
407
892
|
const sk = SK(), c = sk.cons[id];
|