@reekon-tools/boldr-cad 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -9
- package/dist/app/edit.d.ts +12 -1
- package/dist/app/edit.js +96 -11
- package/dist/app/edit.js.map +1 -1
- package/dist/app/state.d.ts +31 -4
- package/dist/app/state.js +31 -2
- package/dist/app/state.js.map +1 -1
- package/dist/data/memory.d.ts +5 -0
- package/dist/data/memory.js +2 -2
- package/dist/data/memory.js.map +1 -1
- package/dist/data/provider.d.ts +19 -7
- package/dist/data/sync.d.ts +7 -2
- package/dist/data/sync.js +115 -64
- package/dist/data/sync.js.map +1 -1
- package/dist/editor/actions.d.ts +1 -2
- package/dist/editor/actions.js +391 -113
- package/dist/editor/actions.js.map +1 -1
- package/dist/editor/confirm.d.ts +23 -0
- package/dist/editor/confirm.js +35 -0
- package/dist/editor/confirm.js.map +1 -0
- package/dist/editor/elevation.d.ts +191 -90
- package/dist/editor/elevation.js +812 -616
- package/dist/editor/elevation.js.map +1 -1
- package/dist/editor/face.d.ts +73 -0
- package/dist/editor/face.js +269 -0
- package/dist/editor/face.js.map +1 -0
- package/dist/editor/files.d.ts +6 -0
- package/dist/editor/files.js +50 -4
- package/dist/editor/files.js.map +1 -1
- package/dist/editor/gestures.d.ts +18 -3
- package/dist/editor/gestures.js +145 -30
- package/dist/editor/gestures.js.map +1 -1
- package/dist/editor/keypad.d.ts +19 -4
- package/dist/editor/keypad.js +39 -9
- package/dist/editor/keypad.js.map +1 -1
- package/dist/editor/keys.js +36 -4
- package/dist/editor/keys.js.map +1 -1
- package/dist/editor/laser.d.ts +20 -4
- package/dist/editor/laser.js +104 -21
- package/dist/editor/laser.js.map +1 -1
- package/dist/editor/linkActions.d.ts +49 -0
- package/dist/editor/linkActions.js +210 -0
- package/dist/editor/linkActions.js.map +1 -0
- package/dist/editor/linkFollow.d.ts +2 -0
- package/dist/editor/linkFollow.js +53 -0
- package/dist/editor/linkFollow.js.map +1 -0
- package/dist/editor/linkWrite.d.ts +13 -0
- package/dist/editor/linkWrite.js +84 -0
- package/dist/editor/linkWrite.js.map +1 -0
- package/dist/editor/links.d.ts +84 -0
- package/dist/editor/links.js +130 -0
- package/dist/editor/links.js.map +1 -0
- package/dist/editor/markup.d.ts +29 -3
- package/dist/editor/markup.js +95 -10
- package/dist/editor/markup.js.map +1 -1
- package/dist/editor/mode.d.ts +40 -2
- package/dist/editor/mode.js +71 -4
- package/dist/editor/mode.js.map +1 -1
- package/dist/editor/ports.d.ts +36 -6
- package/dist/editor/ports.js +10 -1
- package/dist/editor/ports.js.map +1 -1
- package/dist/editor/project.d.ts +38 -6
- package/dist/editor/project.js +167 -27
- package/dist/editor/project.js.map +1 -1
- package/dist/editor/selection.d.ts +10 -0
- package/dist/editor/selection.js +62 -8
- package/dist/editor/selection.js.map +1 -1
- package/dist/editor/status.js +3 -0
- package/dist/editor/status.js.map +1 -1
- package/dist/editor/tooltips.d.ts +5 -0
- package/dist/editor/tooltips.js +48 -0
- package/dist/editor/tooltips.js.map +1 -0
- package/dist/editor/view3d.d.ts +17 -0
- package/dist/editor/view3d.js +135 -0
- package/dist/editor/view3d.js.map +1 -0
- package/dist/editor/view3dState.d.ts +39 -0
- package/dist/editor/view3dState.js +44 -0
- package/dist/editor/view3dState.js.map +1 -0
- package/dist/export/dxf.js +1 -1
- package/dist/export/dxf.js.map +1 -1
- package/dist/export/pdf.js +17 -12
- package/dist/export/pdf.js.map +1 -1
- package/dist/export/plan.d.ts +23 -6
- package/dist/export/plan.js +127 -44
- package/dist/export/plan.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/model/constraints.js +15 -4
- package/dist/model/constraints.js.map +1 -1
- package/dist/model/demo.js +3 -3
- package/dist/model/demo.js.map +1 -1
- package/dist/model/elevation.d.ts +6 -9
- package/dist/model/elevation.js +5 -13
- package/dist/model/elevation.js.map +1 -1
- package/dist/model/faces.d.ts +58 -0
- package/dist/model/faces.js +197 -0
- package/dist/model/faces.js.map +1 -0
- package/dist/model/facesolve.d.ts +72 -0
- package/dist/model/facesolve.js +369 -0
- package/dist/model/facesolve.js.map +1 -0
- package/dist/model/history.d.ts +3 -2
- package/dist/model/history.js +17 -11
- package/dist/model/history.js.map +1 -1
- package/dist/model/images.d.ts +18 -0
- package/dist/model/images.js +26 -0
- package/dist/model/images.js.map +1 -0
- package/dist/model/links.d.ts +46 -0
- package/dist/model/links.js +73 -0
- package/dist/model/links.js.map +1 -0
- package/dist/model/markup.d.ts +46 -0
- package/dist/model/markup.js +71 -0
- package/dist/model/markup.js.map +1 -0
- package/dist/model/measurement.d.ts +11 -0
- package/dist/model/measurement.js +24 -0
- package/dist/model/measurement.js.map +1 -0
- package/dist/model/openings.d.ts +4 -0
- package/dist/model/openings.js +4 -0
- package/dist/model/openings.js.map +1 -1
- package/dist/model/planGeom.d.ts +72 -2
- package/dist/model/planGeom.js +184 -10
- package/dist/model/planGeom.js.map +1 -1
- package/dist/model/project.js +3 -2
- package/dist/model/project.js.map +1 -1
- package/dist/model/projectfile.js +2 -1
- package/dist/model/projectfile.js.map +1 -1
- package/dist/model/rooms.js +2 -2
- package/dist/model/rooms.js.map +1 -1
- package/dist/model/settings.d.ts +6 -0
- package/dist/model/settings.js +2 -2
- package/dist/model/settings.js.map +1 -1
- package/dist/model/sketch.d.ts +14 -0
- package/dist/model/sketch.js +143 -24
- package/dist/model/sketch.js.map +1 -1
- package/dist/model/smooth.d.ts +6 -0
- package/dist/model/smooth.js +52 -0
- package/dist/model/smooth.js.map +1 -0
- package/dist/model/store.d.ts +12 -0
- package/dist/model/store.js +35 -3
- package/dist/model/store.js.map +1 -1
- package/dist/model/types.d.ts +173 -8
- package/dist/model/wallfaces.d.ts +32 -0
- package/dist/model/wallfaces.js +133 -20
- package/dist/model/wallfaces.js.map +1 -1
- package/dist/model/wallshape.d.ts +15 -0
- package/dist/model/wallshape.js +89 -0
- package/dist/model/wallshape.js.map +1 -0
- package/dist/react/BottomBar.d.ts +9 -5
- package/dist/react/BottomBar.js +213 -24
- package/dist/react/BottomBar.js.map +1 -1
- package/dist/react/Buttons.d.ts +13 -2
- package/dist/react/Buttons.js +37 -5
- package/dist/react/Buttons.js.map +1 -1
- package/dist/react/CadEditor.d.ts +46 -3
- package/dist/react/CadEditor.js +77 -20
- package/dist/react/CadEditor.js.map +1 -1
- package/dist/react/ConfirmSheet.d.ts +4 -0
- package/dist/react/ConfirmSheet.js +31 -0
- package/dist/react/ConfirmSheet.js.map +1 -0
- package/dist/react/ConflictSheet.d.ts +2 -1
- package/dist/react/ConflictSheet.js +16 -8
- package/dist/react/ConflictSheet.js.map +1 -1
- package/dist/react/Drawer.d.ts +8 -2
- package/dist/react/Drawer.js +167 -38
- package/dist/react/Drawer.js.map +1 -1
- package/dist/react/Duck.d.ts +13 -0
- package/dist/react/Duck.js +45 -0
- package/dist/react/Duck.js.map +1 -0
- package/dist/react/Empty3D.d.ts +2 -0
- package/dist/react/Empty3D.js +39 -0
- package/dist/react/Empty3D.js.map +1 -0
- package/dist/react/FitScroll.d.ts +6 -0
- package/dist/react/FitScroll.js +14 -0
- package/dist/react/FitScroll.js.map +1 -0
- package/dist/react/Icon.native.d.ts +6 -0
- package/dist/react/Icon.native.js +4 -0
- package/dist/react/Icon.native.js.map +1 -1
- package/dist/react/Icon.web.d.ts +6 -0
- package/dist/react/Icon.web.js +4 -0
- package/dist/react/Icon.web.js.map +1 -1
- package/dist/react/KeypadSheet.d.ts +9 -2
- package/dist/react/KeypadSheet.js +174 -42
- package/dist/react/KeypadSheet.js.map +1 -1
- package/dist/react/LaserTray.js +8 -5
- package/dist/react/LaserTray.js.map +1 -1
- package/dist/react/MarkupPanel.d.ts +8 -0
- package/dist/react/MarkupPanel.js +67 -0
- package/dist/react/MarkupPanel.js.map +1 -0
- package/dist/react/PageThumb.native.d.ts +7 -2
- package/dist/react/PageThumb.native.js +19 -7
- package/dist/react/PageThumb.native.js.map +1 -1
- package/dist/react/PageThumb.web.d.ts +3 -2
- package/dist/react/PageThumb.web.js +4 -4
- package/dist/react/PageThumb.web.js.map +1 -1
- package/dist/react/PlanCanvas.native.d.ts +1 -1
- package/dist/react/PlanCanvas.native.js +134 -36
- package/dist/react/PlanCanvas.native.js.map +1 -1
- package/dist/react/PlanCanvas.web.js +16 -5
- package/dist/react/PlanCanvas.web.js.map +1 -1
- package/dist/react/Popovers.d.ts +12 -6
- package/dist/react/Popovers.js +53 -26
- package/dist/react/Popovers.js.map +1 -1
- package/dist/react/Rail.d.ts +24 -0
- package/dist/react/Rail.js +324 -34
- package/dist/react/Rail.js.map +1 -1
- package/dist/react/ShapeMenu.d.ts +7 -0
- package/dist/react/ShapeMenu.js +58 -0
- package/dist/react/ShapeMenu.js.map +1 -0
- package/dist/react/Sheet.d.ts +14 -4
- package/dist/react/Sheet.js +54 -9
- package/dist/react/Sheet.js.map +1 -1
- package/dist/react/Slider.d.ts +18 -0
- package/dist/react/Slider.js +27 -0
- package/dist/react/Slider.js.map +1 -0
- package/dist/react/Toast.js +14 -6
- package/dist/react/Toast.js.map +1 -1
- package/dist/react/ToolTip.d.ts +7 -0
- package/dist/react/ToolTip.js +43 -0
- package/dist/react/ToolTip.js.map +1 -0
- package/dist/react/TopBar.d.ts +6 -3
- package/dist/react/TopBar.js +48 -17
- package/dist/react/TopBar.js.map +1 -1
- package/dist/react/Tween.d.ts +7 -0
- package/dist/react/Tween.js +26 -0
- package/dist/react/Tween.js.map +1 -0
- package/dist/react/Tween.native.d.ts +6 -0
- package/dist/react/Tween.native.js +43 -0
- package/dist/react/Tween.native.js.map +1 -0
- package/dist/react/View3D.native.d.ts +4 -0
- package/dist/react/View3D.native.js +103 -0
- package/dist/react/View3D.native.js.map +1 -0
- package/dist/react/View3D.web.d.ts +4 -0
- package/dist/react/View3D.web.js +151 -0
- package/dist/react/View3D.web.js.map +1 -0
- package/dist/react/View3DHost.d.ts +143 -0
- package/dist/react/View3DHost.js +377 -0
- package/dist/react/View3DHost.js.map +1 -0
- package/dist/react/corners.d.ts +7 -0
- package/dist/react/corners.js +51 -0
- package/dist/react/corners.js.map +1 -0
- package/dist/react/easing.d.ts +15 -0
- package/dist/react/easing.js +10 -0
- package/dist/react/easing.js.map +1 -0
- package/dist/react/host.d.ts +7 -2
- package/dist/react/host.js +8 -3
- package/dist/react/host.js.map +1 -1
- package/dist/react/hostKeypad.d.ts +56 -0
- package/dist/react/hostKeypad.js +5 -0
- package/dist/react/hostKeypad.js.map +1 -0
- package/dist/react/motion.d.ts +77 -0
- package/dist/react/motion.js +173 -0
- package/dist/react/motion.js.map +1 -0
- package/dist/react/ports.js +90 -8
- package/dist/react/ports.js.map +1 -1
- package/dist/react/saving.d.ts +15 -0
- package/dist/react/saving.js +76 -0
- package/dist/react/saving.js.map +1 -0
- package/dist/react/store.d.ts +38 -1
- package/dist/react/store.js +31 -1
- package/dist/react/store.js.map +1 -1
- package/dist/react/theme.d.ts +10 -0
- package/dist/react/theme.js +8 -0
- package/dist/react/theme.js.map +1 -1
- package/dist/react/topRow.d.ts +22 -0
- package/dist/react/topRow.js +61 -0
- package/dist/react/topRow.js.map +1 -0
- package/dist/react/touch.d.ts +6 -0
- package/dist/react/touch.js +9 -0
- package/dist/react/touch.js.map +1 -0
- package/dist/render/ctx.d.ts +8 -0
- package/dist/render/draw.d.ts +33 -1
- package/dist/render/draw.js +593 -215
- package/dist/render/draw.js.map +1 -1
- package/dist/render/elevation.d.ts +65 -43
- package/dist/render/elevation.js +414 -140
- package/dist/render/elevation.js.map +1 -1
- package/dist/render/figmaIcons.d.ts +12 -0
- package/dist/render/figmaIcons.js +21 -0
- package/dist/render/figmaIcons.js.map +1 -0
- package/dist/render/hit.d.ts +22 -2
- package/dist/render/hit.js +168 -25
- package/dist/render/hit.js.map +1 -1
- package/dist/render/icons.js +69 -10
- package/dist/render/icons.js.map +1 -1
- package/dist/render/linkTile.d.ts +15 -0
- package/dist/render/linkTile.js +369 -0
- package/dist/render/linkTile.js.map +1 -0
- package/dist/render/markup.d.ts +11 -0
- package/dist/render/markup.js +112 -0
- package/dist/render/markup.js.map +1 -0
- package/dist/render/navigate.d.ts +88 -0
- package/dist/render/navigate.js +100 -0
- package/dist/render/navigate.js.map +1 -0
- package/dist/render/snap.d.ts +20 -1
- package/dist/render/snap.js +118 -25
- package/dist/render/snap.js.map +1 -1
- package/dist/render/squircle.d.ts +8 -0
- package/dist/render/squircle.js +71 -0
- package/dist/render/squircle.js.map +1 -0
- package/dist/render/view.d.ts +115 -2
- package/dist/render/view.js +244 -19
- package/dist/render/view.js.map +1 -1
- package/dist/skia/SkiaDrawCtx.d.ts +30 -2
- package/dist/skia/SkiaDrawCtx.js +198 -43
- package/dist/skia/SkiaDrawCtx.js.map +1 -1
- package/dist/skia/host.d.ts +18 -2
- package/dist/skia/host.js +46 -1
- package/dist/skia/host.js.map +1 -1
- package/dist/solver/equations.js +37 -2
- package/dist/solver/equations.js.map +1 -1
- package/dist/solver/gauss-newton.d.ts +17 -0
- package/dist/solver/gauss-newton.js +144 -36
- package/dist/solver/gauss-newton.js.map +1 -1
- package/dist/solver/linalg.d.ts +45 -2
- package/dist/solver/linalg.js +164 -10
- package/dist/solver/linalg.js.map +1 -1
- package/dist/solver/types.d.ts +5 -0
- package/dist/tools/annotate.d.ts +57 -1
- package/dist/tools/annotate.js +64 -43
- package/dist/tools/annotate.js.map +1 -1
- package/dist/tools/chamfer.d.ts +12 -0
- package/dist/tools/chamfer.js +114 -0
- package/dist/tools/chamfer.js.map +1 -0
- package/dist/tools/corner.d.ts +38 -0
- package/dist/tools/corner.js +99 -0
- package/dist/tools/corner.js.map +1 -0
- package/dist/tools/dim.d.ts +86 -9
- package/dist/tools/dim.js +581 -96
- package/dist/tools/dim.js.map +1 -1
- package/dist/tools/elevation.js +1 -1
- package/dist/tools/elevation.js.map +1 -1
- package/dist/tools/fillet.d.ts +9 -0
- package/dist/tools/fillet.js +86 -0
- package/dist/tools/fillet.js.map +1 -0
- package/dist/tools/freehand.d.ts +3 -2
- package/dist/tools/freehand.js +19 -12
- package/dist/tools/freehand.js.map +1 -1
- package/dist/tools/index.js +14 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/line.d.ts +8 -3
- package/dist/tools/line.js +66 -30
- package/dist/tools/line.js.map +1 -1
- package/dist/tools/modify.d.ts +13 -0
- package/dist/tools/modify.js +105 -25
- package/dist/tools/modify.js.map +1 -1
- package/dist/tools/openings.js +22 -15
- package/dist/tools/openings.js.map +1 -1
- package/dist/tools/registry.d.ts +8 -0
- package/dist/tools/registry.js +42 -2
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/select.d.ts +77 -3
- package/dist/tools/select.js +560 -74
- package/dist/tools/select.js.map +1 -1
- package/dist/tools/shapes.d.ts +14 -0
- package/dist/tools/shapes.js +154 -75
- package/dist/tools/shapes.js.map +1 -1
- package/dist/units/format.d.ts +10 -2
- package/dist/units/format.js +18 -3
- package/dist/units/format.js.map +1 -1
- package/dist/view3d/build.d.ts +35 -0
- package/dist/view3d/build.js +630 -0
- package/dist/view3d/build.js.map +1 -0
- package/dist/view3d/camera.d.ts +60 -0
- package/dist/view3d/camera.js +84 -0
- package/dist/view3d/camera.js.map +1 -0
- package/dist/view3d/fatLines.d.ts +11 -0
- package/dist/view3d/fatLines.js +83 -0
- package/dist/view3d/fatLines.js.map +1 -0
- package/dist/view3d/lines.d.ts +17 -0
- package/dist/view3d/lines.js +271 -0
- package/dist/view3d/lines.js.map +1 -0
- package/dist/view3d/mesh.d.ts +51 -0
- package/dist/view3d/mesh.js +101 -0
- package/dist/view3d/mesh.js.map +1 -0
- package/dist/view3d/poly.d.ts +16 -0
- package/dist/view3d/poly.js +109 -0
- package/dist/view3d/poly.js.map +1 -0
- package/dist/view3d/viewer.d.ts +86 -0
- package/dist/view3d/viewer.js +350 -0
- package/dist/view3d/viewer.js.map +1 -0
- package/package.json +15 -2
- package/src/app/edit.ts +83 -12
- package/src/app/main.ts +11 -5
- package/src/app/state.ts +38 -5
- package/src/data/memory.ts +2 -2
- package/src/data/provider.ts +15 -7
- package/src/data/sync.ts +87 -50
- package/src/editor/actions.ts +292 -83
- package/src/editor/confirm.ts +58 -0
- package/src/editor/elevation.ts +698 -478
- package/src/editor/face.ts +227 -0
- package/src/editor/files.ts +40 -4
- package/src/editor/gestures.ts +147 -31
- package/src/editor/keypad.ts +43 -11
- package/src/editor/keys.ts +25 -5
- package/src/editor/laser.ts +98 -20
- package/src/editor/linkActions.ts +194 -0
- package/src/editor/linkFollow.ts +43 -0
- package/src/editor/linkWrite.ts +77 -0
- package/src/editor/links.ts +181 -0
- package/src/editor/markup.ts +92 -8
- package/src/editor/mode.ts +83 -7
- package/src/editor/ports.ts +37 -6
- package/src/editor/project.ts +155 -26
- package/src/editor/selection.ts +54 -8
- package/src/editor/status.ts +2 -0
- package/src/editor/tooltips.ts +47 -0
- package/src/editor/view3d.ts +119 -0
- package/src/editor/view3dState.ts +61 -0
- package/src/export/dxf.ts +1 -1
- package/src/export/pdf.ts +15 -7
- package/src/export/plan.ts +119 -35
- package/src/index.ts +5 -0
- package/src/input/keyboard.ts +2 -4
- package/src/model/constraints.ts +13 -4
- package/src/model/demo.ts +3 -3
- package/src/model/elevation.ts +6 -16
- package/src/model/faces.ts +153 -0
- package/src/model/facesolve.ts +322 -0
- package/src/model/history.ts +19 -11
- package/src/model/images.ts +35 -0
- package/src/model/links.ts +77 -0
- package/src/model/markup.ts +80 -0
- package/src/model/measurement.ts +26 -0
- package/src/model/openings.ts +6 -0
- package/src/model/planGeom.ts +174 -8
- package/src/model/project.ts +3 -2
- package/src/model/projectfile.ts +2 -1
- package/src/model/rooms.ts +1 -1
- package/src/model/settings.ts +8 -2
- package/src/model/sketch.ts +101 -14
- package/src/model/smooth.ts +51 -0
- package/src/model/store.ts +35 -4
- package/src/model/types.ts +170 -8
- package/src/model/wallfaces.ts +126 -17
- package/src/model/wallshape.ts +97 -0
- package/src/react/BottomBar.tsx +263 -46
- package/src/react/Buttons.tsx +50 -9
- package/src/react/CadEditor.tsx +133 -30
- package/src/react/ConfirmSheet.tsx +38 -0
- package/src/react/ConflictSheet.tsx +19 -12
- package/src/react/Drawer.tsx +213 -110
- package/src/react/Duck.tsx +50 -0
- package/src/react/Empty3D.tsx +32 -0
- package/src/react/FitScroll.tsx +19 -0
- package/src/react/Icon.native.tsx +5 -0
- package/src/react/Icon.web.tsx +5 -0
- package/src/react/KeypadSheet.tsx +253 -60
- package/src/react/LaserTray.tsx +12 -8
- package/src/react/MarkupPanel.tsx +150 -0
- package/src/react/PageThumb.native.tsx +18 -7
- package/src/react/PageThumb.web.tsx +4 -4
- package/src/react/PlanCanvas.native.tsx +151 -34
- package/src/react/PlanCanvas.web.tsx +15 -5
- package/src/react/Popovers.tsx +116 -51
- package/src/react/Rail.tsx +360 -27
- package/src/react/ShapeMenu.tsx +66 -0
- package/src/react/Sheet.tsx +56 -11
- package/src/react/Slider.tsx +48 -0
- package/src/react/Toast.tsx +16 -9
- package/src/react/ToolTip.tsx +49 -0
- package/src/react/TopBar.tsx +97 -39
- package/src/react/Tween.native.tsx +36 -0
- package/src/react/Tween.tsx +24 -0
- package/src/react/View3D.native.tsx +131 -0
- package/src/react/View3D.web.tsx +131 -0
- package/src/react/View3DHost.ts +367 -0
- package/src/react/corners.ts +52 -0
- package/src/react/easing.ts +14 -0
- package/src/react/host.ts +8 -3
- package/src/react/hostKeypad.ts +66 -0
- package/src/react/motion.tsx +239 -0
- package/src/react/ports.ts +78 -9
- package/src/react/saving.ts +80 -0
- package/src/react/store.ts +35 -3
- package/src/react/theme.ts +8 -0
- package/src/react/topRow.ts +71 -0
- package/src/react/touch.ts +10 -0
- package/src/render/ctx.ts +8 -0
- package/src/render/draw.ts +437 -175
- package/src/render/elevation.ts +349 -143
- package/src/render/figmaIcons.ts +22 -0
- package/src/render/hit.ts +140 -21
- package/src/render/icons.ts +69 -10
- package/src/render/linkTile.ts +297 -0
- package/src/render/markup.ts +91 -0
- package/src/render/navigate.ts +114 -0
- package/src/render/snap.ts +105 -22
- package/src/render/squircle.ts +73 -0
- package/src/render/view.ts +255 -19
- package/src/skia/SkiaDrawCtx.ts +163 -41
- package/src/skia/host.ts +46 -1
- package/src/solver/equations.ts +37 -2
- package/src/solver/gauss-newton.ts +125 -28
- package/src/solver/linalg.ts +141 -5
- package/src/solver/types.ts +5 -0
- package/src/styles.css +91 -42
- package/src/tools/annotate.ts +58 -29
- package/src/tools/chamfer.ts +103 -0
- package/src/tools/corner.ts +99 -0
- package/src/tools/dim.ts +524 -92
- package/src/tools/elevation.ts +1 -1
- package/src/tools/fillet.ts +82 -0
- package/src/tools/freehand.ts +20 -16
- package/src/tools/index.ts +17 -4
- package/src/tools/line.ts +59 -23
- package/src/tools/modify.ts +86 -24
- package/src/tools/openings.ts +8 -13
- package/src/tools/registry.ts +27 -2
- package/src/tools/select.ts +424 -75
- package/src/tools/shapes.ts +119 -58
- package/src/ui/ctx.ts +2 -1
- package/src/ui/domPorts.ts +23 -4
- package/src/ui/drawer.ts +6 -26
- package/src/ui/elevation.ts +10 -101
- package/src/ui/keypad.ts +8 -14
- package/src/ui/laser.ts +2 -2
- package/src/ui/menu.ts +63 -17
- package/src/ui/noteinput.ts +3 -3
- package/src/ui/opacity.ts +108 -0
- package/src/ui/palette.ts +67 -12
- package/src/ui/rail.ts +21 -7
- package/src/ui/topbar.ts +4 -14
- package/src/units/format.ts +18 -3
- package/src/view3d/build.ts +607 -0
- package/src/view3d/camera.ts +113 -0
- package/src/view3d/fatLines.ts +83 -0
- package/src/view3d/lines.ts +258 -0
- package/src/view3d/mesh.ts +111 -0
- package/src/view3d/poly.ts +104 -0
- package/src/view3d/viewer.ts +348 -0
- package/dist/react/ElevCanvas.native.d.ts +0 -3
- package/dist/react/ElevCanvas.native.js +0 -58
- package/dist/react/ElevCanvas.native.js.map +0 -1
- package/dist/react/ElevCanvas.web.d.ts +0 -4
- package/dist/react/ElevCanvas.web.js +0 -44
- package/dist/react/ElevCanvas.web.js.map +0 -1
- package/dist/react/ElevationSheet.d.ts +0 -2
- package/dist/react/ElevationSheet.js +0 -38
- package/dist/react/ElevationSheet.js.map +0 -1
- package/dist/render/elevationView.d.ts +0 -3
- package/dist/render/elevationView.js +0 -100
- package/dist/render/elevationView.js.map +0 -1
- package/src/react/ElevCanvas.native.tsx +0 -68
- package/src/react/ElevCanvas.web.tsx +0 -42
- package/src/react/ElevationSheet.tsx +0 -88
- package/src/render/elevationView.ts +0 -73
package/src/render/elevation.ts
CHANGED
|
@@ -1,52 +1,36 @@
|
|
|
1
|
-
import { ed } from '../app/state';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
1
|
+
import { ed, isSel } from '../app/state';
|
|
2
|
+
import { measure } from '../model/constraints';
|
|
3
|
+
import { elevOpenings, toU, type ElevFrame, type ElevOpening } from '../model/elevation';
|
|
4
|
+
import { cabPoints, cornerAnchor, emptyFace, openingAnchor } from '../model/faces';
|
|
5
|
+
import { edgeS, openingSpan, pickBetween, refS, sameRef, widthEntered } from '../model/openings';
|
|
6
|
+
import type { Constraint, OpeningDim, OpeningEdge, OpeningRef, Sketch, WallFace } from '../model/types';
|
|
5
7
|
import { fmtLen } from '../units';
|
|
6
|
-
import { chip } from './draw';
|
|
8
|
+
import { chip, linkBlue, linkMark } from './draw';
|
|
7
9
|
import { FONT, TH } from './theme';
|
|
8
10
|
import { S, type Xform } from './view';
|
|
9
11
|
import type { DrawCtx } from './ctx';
|
|
10
12
|
|
|
11
13
|
/*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
How the wall view draws a wall face (see model/elevation.ts and model/faces.ts): x across the wall as seen, y up
|
|
15
|
+
from the floor, through the plan's Xform. Under what's drawn on the face: the wall itself, the walls cut at its
|
|
16
|
+
corners, its doors, windows and cabinets. Over it: the dimensions doors, windows and cabinets get on their own —
|
|
17
|
+
the strings along the floor, heights beside them, the wall's height and length — all tappable.
|
|
14
18
|
*/
|
|
15
19
|
|
|
16
20
|
type C2D = DrawCtx;
|
|
17
21
|
|
|
18
|
-
/**
|
|
19
|
-
export interface ElevView { cu: number; cz: number; s: number }
|
|
20
|
-
|
|
21
|
-
export const elevT = (v: ElevView, W: number, H: number): Xform => ({ s: v.s, ox: W / 2 - v.cu * v.s, oy: H / 2 + v.cz * v.s });
|
|
22
|
-
|
|
23
|
-
/** Screen room taken by dimensions around the wall, pixels: below the floor, right of the face. */
|
|
24
|
-
const DIM_BELOW = 92, DIM_RIGHT = 70, PAD = 36;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Fit the wall to a W × H view, leaving pixel margins for the dimensions and for anything overlaid
|
|
28
|
-
* (`keep`: space taken at the top or the right, e.g. the key plan).
|
|
29
|
-
*/
|
|
30
|
-
export function elevFit(f: ElevFrame, W: number, H: number, keep: { top?: number; right?: number; left?: number; bottom?: number } = {}): ElevView {
|
|
31
|
-
const u0 = Math.min(f.face[0], ...f.strips.map(s => s[0])), u1 = Math.max(f.face[1], ...f.strips.map(s => s[1]));
|
|
32
|
-
const w = u1 - u0, h = f.H, top = keep.top || 0, right = Math.max(DIM_RIGHT, keep.right || 0), left = keep.left || 0, below = DIM_BELOW + (keep.bottom || 0);
|
|
33
|
-
const s = Math.max(0.05, Math.min((W - 2 * PAD - right - left) / (w || 1), (H - 2 * PAD - below - top) / (h || 1)));
|
|
34
|
-
// centre the drawing in the space left after the margins
|
|
35
|
-
return { s, cu: (u0 + u1) / 2 + (right - left) / 2 / s, cz: h / 2 - (below - top) / 2 / s };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** What tapping an elevation dimension does. */
|
|
22
|
+
/** What tapping one of the wall view's dimensions does (stored as JSON in the selection and the tap targets). */
|
|
39
23
|
export type ElevAct =
|
|
40
24
|
| { k: 'w' | 'sill' | 'h'; op: string }
|
|
41
25
|
| { k: 'wallH' }
|
|
42
26
|
| { k: 'len' }
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
| { k: '
|
|
27
|
+
| { k: 'pos'; op: string; edge: OpeningEdge; ref: OpeningRef; title: string }
|
|
28
|
+
/** a dimension on the face (a constraint of its sketch) */
|
|
29
|
+
| { k: 'fdim'; id: string }
|
|
30
|
+
/** a distance nothing sets yet between two face points (grey): entering it makes it a face dimension */
|
|
31
|
+
| { k: 'gap'; a: string; b: string; kind: 'h' | 'v'; title: string }
|
|
32
|
+
/** a cabinet's depth (out from the wall: it shows in the plan) */
|
|
33
|
+
| { k: 'depth'; cab: string };
|
|
50
34
|
export interface ElevHit { x: number; y: number; w: number; h: number; act: ElevAct; /** chip colour: drive (blue), ref, free (grey), bad (red) */ look?: string }
|
|
51
35
|
|
|
52
36
|
/**
|
|
@@ -58,27 +42,15 @@ const LOOK = (look: Look) => look === 'drive' ? { bg: TH.blue, fg: '#fff', line:
|
|
|
58
42
|
: look === 'linked' ? { bg: TH.bg, fg: linkBlue(), line: linkBlue() }
|
|
59
43
|
: look === 'ref' ? { bg: TH.ref, fg: TH.reftext, line: TH.chipline } : { bg: TH.chip, fg: TH.ink, line: TH.ink2 };
|
|
60
44
|
|
|
61
|
-
/** Blue for outlined (linked) chips: the lighter blue on a dark canvas, so the text stays readable. */
|
|
62
|
-
function linkBlue(): string {
|
|
63
|
-
const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i.exec(TH.bg);
|
|
64
|
-
const dark = !!m && (parseInt(m[1], 16) * 299 + parseInt(m[2], 16) * 587 + parseInt(m[3], 16) * 114) / 1000 < 128;
|
|
65
|
-
return dark ? TH.lblue : TH.blue;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** A small chain-link mark centred on (x, y). */
|
|
69
|
-
function linkMark(c: C2D, x: number, y: number, color: string): void {
|
|
70
|
-
c.save();
|
|
71
|
-
c.translate(x, y); c.rotate(-Math.PI / 4);
|
|
72
|
-
c.strokeStyle = color; c.lineWidth = 1.4;
|
|
73
|
-
c.beginPath(); c.roundRect(-6.5, -2.25, 7.5, 4.5, 2.25); c.roundRect(-1, -2.25, 7.5, 4.5, 2.25); c.stroke();
|
|
74
|
-
c.restore();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
45
|
/**
|
|
78
46
|
* Linear dimension between screen points p and q, its line offset by (nx, ny) pixels: ticks, extension lines and
|
|
79
47
|
* the value chip. Returns the chip's box (for taps). `force` draws the chip even for a zero-length dimension.
|
|
80
48
|
*/
|
|
81
|
-
|
|
49
|
+
/**
|
|
50
|
+
* A dimension: extension lines, the dimension line with 45° ticks, the chip. `dashed`: a number that holds nothing (only
|
|
51
|
+
* measured, or a reference) — its dimension line dashed and its value in parentheses, as in the plan.
|
|
52
|
+
*/
|
|
53
|
+
function dim(c: C2D, p: [number, number], q: [number, number], nx: number, ny: number, label: string, look: Look = 'free', force = false, selected = false, dashed = false): Omit<ElevHit, 'act'> | null {
|
|
82
54
|
const len = Math.hypot(q[0] - p[0], q[1] - p[1]);
|
|
83
55
|
if (len < 2 && !force) return null;
|
|
84
56
|
const a = [p[0] + nx, p[1] + ny], b = [q[0] + nx, q[1] + ny], L = Math.hypot(nx, ny) || 1, st = LOOK(look);
|
|
@@ -87,7 +59,11 @@ function dim(c: C2D, p: [number, number], q: [number, number], nx: number, ny: n
|
|
|
87
59
|
c.beginPath();
|
|
88
60
|
for (const [o, e] of [[p, a], [q, b]]) { c.moveTo(o[0] + (nx / L) * 4, o[1] + (ny / L) * 4); c.lineTo(e[0] + (nx / L) * 5, e[1] + (ny / L) * 5); }
|
|
89
61
|
if (len >= 2) {
|
|
90
|
-
|
|
62
|
+
if (dashed) {
|
|
63
|
+
c.stroke(); c.beginPath(); c.setLineDash([3, 3]);
|
|
64
|
+
c.moveTo(a[0], a[1]); c.lineTo(b[0], b[1]);
|
|
65
|
+
c.stroke(); c.beginPath(); c.setLineDash([]);
|
|
66
|
+
} else { c.moveTo(a[0], a[1]); c.lineTo(b[0], b[1]); }
|
|
91
67
|
// 45° architectural ticks
|
|
92
68
|
const ux = (b[0] - a[0]) / len, uy = (b[1] - a[1]) / len;
|
|
93
69
|
const tx = (ux + nx / L) * 4, ty = (uy + ny / L) * 4;
|
|
@@ -96,7 +72,8 @@ function dim(c: C2D, p: [number, number], q: [number, number], nx: number, ny: n
|
|
|
96
72
|
c.stroke();
|
|
97
73
|
c.restore();
|
|
98
74
|
const cx = (a[0] + b[0]) / 2 + (len < 2 ? (nx / L) * 4 : 0), cy = (a[1] + b[1]) / 2 - (len < 2 ? 12 : 0), h = 20;
|
|
99
|
-
|
|
75
|
+
// a number that holds nothing (dashed) reads in parentheses, as in the plan
|
|
76
|
+
const linked = look === 'linked', text = dashed ? '(' + label + ')' : linked ? ' ' + label : label; // (figure spaces: room for the link mark)
|
|
100
77
|
const w = chip(c, text, cx, cy, { bg: st.bg, fg: st.fg, font: '600 11.5px', h, pad: 6, border: selected ? [TH.yellow, 2] : linked ? [st.line, 1.5] : null });
|
|
101
78
|
if (linked) linkMark(c, cx - w / 2 + 11, cy, st.fg);
|
|
102
79
|
return { x: cx - w / 2 - 4, y: cy - h / 2 - 4, w: w + 8, h: h + 8 };
|
|
@@ -135,113 +112,345 @@ function drawOpening(c: C2D, T: Xform, o: ElevOpening): void {
|
|
|
135
112
|
c.restore();
|
|
136
113
|
}
|
|
137
114
|
|
|
138
|
-
/**
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
115
|
+
/** A cabinet's tint and panel lines: neutral grey (no yellow — that's the selection's), against a window's blue glass. */
|
|
116
|
+
const WOOD = 'rgba(128,128,128,.14)', WOOD_LINE = 'rgba(128,128,128,.9)';
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* A cabinet, as cabinetry is drawn in elevation: a wood-tinted box, a drawer across its top with a pull, and a door
|
|
120
|
+
* below it with a handle — so it reads as a cabinet at a glance, not a window. Its name and size sit on the door.
|
|
121
|
+
*/
|
|
122
|
+
function drawCabinet(c: C2D, T: Xform, x: number, y: number, w: number, h: number, name: string): void {
|
|
123
|
+
const pad = Math.min(1.5 * T.s, w / 10, h / 10), drawer = Math.min(6 * T.s, h * 0.24);
|
|
124
|
+
c.save();
|
|
125
|
+
c.fillStyle = TH.bg; c.fillRect(x, y, w, h);
|
|
126
|
+
c.fillStyle = WOOD; c.fillRect(x, y, w, h);
|
|
127
|
+
c.strokeStyle = TH.ink; c.lineWidth = 1.5; c.strokeRect(x, y, w, h);
|
|
128
|
+
// the drawer front and the door, inset in the box
|
|
129
|
+
const ix = x + pad, iw = w - 2 * pad, dy = y + pad, dh = drawer - pad, oy = y + drawer + pad * 0.5, oh = h - drawer - pad * 1.5;
|
|
130
|
+
c.strokeStyle = WOOD_LINE; c.lineWidth = 1;
|
|
131
|
+
if (dh > 3) c.strokeRect(ix, dy, iw, dh);
|
|
132
|
+
if (oh > 6) c.strokeRect(ix, oy, iw, oh);
|
|
133
|
+
// a pull on the drawer, a handle at the top of the door on its right
|
|
134
|
+
c.strokeStyle = TH.ink; c.lineWidth = 2; c.lineCap = 'round';
|
|
135
|
+
c.beginPath();
|
|
136
|
+
if (dh > 6 && iw > 16) { const pw = Math.min(iw * 0.3, 4 * T.s); c.moveTo(ix + iw / 2 - pw / 2, dy + dh / 2); c.lineTo(ix + iw / 2 + pw / 2, dy + dh / 2); }
|
|
137
|
+
if (oh > 24 && iw > 16) { const hx = ix + iw - Math.max(5, pad * 2), hl = Math.min(oh * 0.18, 4 * T.s); c.moveTo(hx, oy + Math.max(6, pad * 1.5)); c.lineTo(hx, oy + Math.max(6, pad * 1.5) + hl); }
|
|
138
|
+
c.stroke();
|
|
139
|
+
// its name, on the door (its sizes are in the bar when it's picked: on the drawing they'd be noise)
|
|
140
|
+
c.fillStyle = TH.ink2; c.textAlign = 'center'; c.textBaseline = 'middle';
|
|
141
|
+
const cx = x + w / 2, cy = oy + oh / 2;
|
|
142
|
+
if (w > 44 && oh > 22) {
|
|
143
|
+
c.font = '600 11px ' + FONT;
|
|
144
|
+
c.fillText(name, cx, cy);
|
|
145
|
+
}
|
|
146
|
+
c.restore();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** A cabinet's extent on the face. */
|
|
150
|
+
export function cabBox(face: WallFace, id: string): { u0: number; u1: number; z0: number; z1: number } | null {
|
|
151
|
+
const cb = face.cabs?.[id], ps = cb ? cabPoints(cb).map(p => face.points[p]) : [];
|
|
152
|
+
if (!cb || ps.some(p => !p)) return null;
|
|
153
|
+
return { u0: Math.min(...ps.map(p => p.x)), u1: Math.max(...ps.map(p => p.x)), z0: Math.min(...ps.map(p => p.y)), z1: Math.max(...ps.map(p => p.y)) };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** A cabinet's left and right bottom corners as seen. */
|
|
157
|
+
const cabSides = (face: WallFace, id: string): [string, string] => {
|
|
158
|
+
const cb = face.cabs[id];
|
|
159
|
+
return face.points[cb.bl].x <= face.points[cb.br].x ? [cb.bl, cb.br] : [cb.br, cb.bl];
|
|
160
|
+
};
|
|
144
161
|
|
|
145
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Under what's drawn on the face: the walls cut at its corners, the face, its doors and windows, its cabinets (a door
|
|
164
|
+
* panel and their name and size; their edges are the face's lines, drawn over this), the face's outline and the floor.
|
|
165
|
+
* (The selected door, window or cabinet's outline: drawWallSelection, over all of it.)
|
|
166
|
+
*/
|
|
167
|
+
export function drawWallBackdrop(c: C2D, T: Xform, sk: Sketch, f: ElevFrame, face: WallFace): void {
|
|
168
|
+
const [fx0, fyTop] = S(T, f.face[0], f.H), [fx1, fyBot] = S(T, f.face[1], 0);
|
|
146
169
|
for (const [a, b] of f.strips) { const [x0] = S(T, a, 0), [x1] = S(T, b, 0); hatch(c, x0, fyTop, x1 - x0, fyBot - fyTop); }
|
|
147
170
|
c.fillStyle = TH.room; c.fillRect(fx0, fyTop, fx1 - fx0, fyBot - fyTop);
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const [
|
|
153
|
-
|
|
171
|
+
for (const o of elevOpenings(sk, f)) drawOpening(c, T, o);
|
|
172
|
+
for (const [id, cb] of Object.entries(face.cabs ?? {})) {
|
|
173
|
+
const b = cabBox(face, id);
|
|
174
|
+
if (!b) continue;
|
|
175
|
+
const [x0, y0] = S(T, b.u0, b.z1), [x1, y1] = S(T, b.u1, b.z0);
|
|
176
|
+
drawCabinet(c, T, x0, y0, x1 - x0, y1 - y0, cb.label || 'Cabinet');
|
|
154
177
|
}
|
|
155
|
-
|
|
156
|
-
drawOpening(c, T, o);
|
|
157
|
-
const [x0, y0] = S(T, o.u0, o.head), [x1, y1] = S(T, o.u1, o.sill);
|
|
158
|
-
hits.push({ x: x0, y: y0, w: x1 - x0, h: y1 - y0, act: { k: 'body', op: o.id } }); // first, so chips on top win taps
|
|
159
|
-
}
|
|
160
|
-
// lines drawn on this face: solid, or dashed guides
|
|
161
|
-
c.save(); c.lineCap = 'round';
|
|
162
|
-
for (const ln of elevLines(sk, f)) {
|
|
163
|
-
const [a, b] = elevLineUZ(f, ln), p = S(T, a[0], a[1]), q = S(T, b[0], b[1]);
|
|
164
|
-
c.strokeStyle = ln.guide ? TH.constr : TH.ink; c.lineWidth = ln.guide ? 1.5 : 2;
|
|
165
|
-
c.setLineDash(ln.guide ? [6, 5] : []);
|
|
166
|
-
c.beginPath(); c.moveTo(p[0], p[1]); c.lineTo(q[0], q[1]); c.stroke();
|
|
167
|
-
}
|
|
168
|
-
c.restore();
|
|
178
|
+
c.save();
|
|
169
179
|
c.strokeStyle = TH.ink; c.lineWidth = 2; c.strokeRect(fx0, fyTop, fx1 - fx0, fyBot - fyTop);
|
|
170
|
-
// floor line across
|
|
180
|
+
// the floor line, across the cut walls too
|
|
171
181
|
const u0 = Math.min(f.face[0], ...f.strips.map(s => s[0])), u1 = Math.max(f.face[1], ...f.strips.map(s => s[1]));
|
|
172
182
|
const [gx0] = S(T, u0, 0), [gx1] = S(T, u1, 0);
|
|
173
183
|
c.lineWidth = 3; c.beginPath(); c.moveTo(gx0 - 14, fyBot); c.lineTo(gx1 + 14, fyBot); c.stroke();
|
|
184
|
+
c.restore();
|
|
185
|
+
}
|
|
174
186
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
187
|
+
/**
|
|
188
|
+
* The selected door, window or cabinet (and the one under the pointer, fainter): a yellow outline right on its edges,
|
|
189
|
+
* drawn over everything else on the wall — its own edges and its neighbours' — so the whole of it shows, even where
|
|
190
|
+
* it shares a side with another cabinet, or lines up with a window.
|
|
191
|
+
*/
|
|
192
|
+
export function drawWallSelection(c: C2D, T: Xform, sk: Sketch, f: ElevFrame, face: WallFace): void {
|
|
193
|
+
const lit = (type: 'opening' | 'cab', id: string) => isSel(type, id) || (ed.hover?.type === type && ed.hover.id === id);
|
|
194
|
+
const ring = (u0: number, z1: number, u1: number, z0: number, selected: boolean) => {
|
|
195
|
+
const [x0, y0] = S(T, u0, z1), [x1, y1] = S(T, u1, z0);
|
|
196
|
+
c.save(); c.strokeStyle = TH.yellow; c.globalAlpha = selected ? 1 : 0.5; c.lineWidth = 3; c.lineJoin = 'miter';
|
|
197
|
+
c.strokeRect(x0, y0, x1 - x0, y1 - y0);
|
|
198
|
+
c.restore();
|
|
181
199
|
};
|
|
182
|
-
const
|
|
183
|
-
for (const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
200
|
+
for (const o of elevOpenings(sk, f)) if (lit('opening', o.id)) ring(o.u0, o.head, o.u1, o.sill, isSel('opening', o.id));
|
|
201
|
+
for (const id of Object.keys(face.cabs ?? {})) {
|
|
202
|
+
if (!lit('cab', id)) continue;
|
|
203
|
+
const b = cabBox(face, id);
|
|
204
|
+
if (b) ring(b.u0, b.z1, b.u1, b.z0, isSel('cab', id));
|
|
187
205
|
}
|
|
188
|
-
// saved dimensions that aren't one step of the string (e.g. window → door across another window, or from a
|
|
189
|
-
// plan wall point): each on its own row below it, so it can be seen, selected and deleted
|
|
190
|
-
const extra = extraDims(sk, f);
|
|
191
|
-
for (const x of extra) tag(S(T, x.u0, 0), S(T, x.u1, 0), 0, ROW0 + ROW * (x.row + 1), fmtLen(x.u1 - x.u0), lookOf(x.d), x.act);
|
|
192
|
-
const rows = extra.reduce((m, x) => Math.max(m, x.row + 1), 0);
|
|
193
|
-
// the wall's length comes from the floor plan: linked (outlined blue) when a dimension locks it there, grey when free
|
|
194
|
-
const planLocked = Object.values(sk.cons).some(cn => cn.type === 'dist' && cn.line === f.lid && cn.driving !== false && cn.layer !== 'print');
|
|
195
|
-
tag(S(T, f.face[0], 0), S(T, f.face[1], 0), 0, segs.length || rows ? ROW0 + ROW * (rows + 1) : ROW0, fmtLen(f.face[1] - f.face[0]), planLocked ? 'linked' : 'free', { k: 'len' });
|
|
196
|
-
// heights: blue once entered (the sill: locked), grey while they're defaults / free to drag
|
|
197
|
-
tag(S(T, f.face[1], 0), S(T, f.face[1], f.H), 34, 0, fmtLen(f.H), sk.lines[f.lid].height != null ? 'drive' : 'free', { k: 'wallH' });
|
|
198
|
-
for (const o of ops) {
|
|
199
|
-
const off = 24;
|
|
200
|
-
const op = sk.openings[o.id];
|
|
201
|
-
if (o.sill > 0.01 || o.type === 'window') tag(S(T, o.u1, 0), S(T, o.u1, o.sill), off, 0, fmtLen(o.sill), op.sillLocked ? 'drive' : 'free', { k: 'sill', op: o.id }, true);
|
|
202
|
-
tag(S(T, o.u1, o.sill), S(T, o.u1, o.head), off, 0, fmtLen(o.head - o.sill), op.h != null ? 'drive' : 'free', { k: 'h', op: o.id });
|
|
203
|
-
}
|
|
204
|
-
return hits;
|
|
205
206
|
}
|
|
206
207
|
|
|
207
|
-
/** Rows of dimensions below the floor, pixels: the
|
|
208
|
+
/** Rows of dimensions below the floor, pixels: the strings, then extra rows, then the overall length. */
|
|
208
209
|
const ROW0 = 30, ROW = 34;
|
|
209
210
|
|
|
210
211
|
type PosAct = Extract<ElevAct, { k: 'pos' }>;
|
|
211
|
-
|
|
212
|
+
/** Something in a string along the floor: a door or window, or a cabinet (its left and right points as seen). */
|
|
213
|
+
type Item = { u0: number; u1: number; op?: ElevOpening; cab?: { id: string; l: string; r: string } };
|
|
214
|
+
interface Seg { u0: number; u1: number; act: ElevAct; look: Look; /** an object's own width: always shown */ width?: true }
|
|
215
|
+
|
|
216
|
+
/** One rule for every chip: blue once someone entered it; grey while it's a standard size (a new cabinet's) or only measured. */
|
|
217
|
+
const faceLook = (c: Constraint, id: string): Look => (ed.highlight.has(id) || ed.ana.bad.has(id) || ed.badFaceCons.has(id) ? 'bad' : c.driving === false ? 'ref' : c.auto ? 'free' : 'drive');
|
|
218
|
+
|
|
219
|
+
/** The face dimension across the wall between two points, if there is one. */
|
|
220
|
+
function dimAcross(face: WallFace, a: string, b: string): string | null {
|
|
221
|
+
for (const [id, c] of Object.entries(face.cons)) if (c.type === 'dist' && c.kind === 'h' && ((c.a === a && c.b === b) || (c.a === b && c.b === a))) return id;
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** The face dimension of a cabinet's height above the floor, if there is one. */
|
|
226
|
+
export function cabLiftDim(face: WallFace, id: string): string | null {
|
|
227
|
+
const cb = face.cabs[id];
|
|
228
|
+
for (const [cid, k] of Object.entries(face.cons)) {
|
|
229
|
+
const ends = [k.a, k.b];
|
|
230
|
+
if (k.type === 'dist' && k.kind === 'v' && ends.some(v => !!v && !!face.points[v]?.anchor && face.points[v].y === 0) && ends.some(v => v === cb.bl || v === cb.br)) return cid;
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
212
234
|
|
|
213
|
-
/**
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
235
|
+
/**
|
|
236
|
+
* The strings along the floor, corner to corner, left to right: the doors and windows and the cabinets that fit
|
|
237
|
+
* between them in the first; cabinets that overlap those (hung above a counter, say) in the next ones.
|
|
238
|
+
*/
|
|
239
|
+
function strings(sk: Sketch, f: ElevFrame, face: WallFace): Item[][] {
|
|
240
|
+
const ops: Item[] = elevOpenings(sk, f).map(o => ({ u0: o.u0, u1: o.u1, op: o }));
|
|
241
|
+
const cabs: Item[] = Object.keys(face.cabs ?? {}).flatMap(id => {
|
|
242
|
+
const b = cabBox(face, id);
|
|
243
|
+
if (!b) return [];
|
|
244
|
+
const [l, r] = cabSides(face, id);
|
|
245
|
+
return [{ u0: b.u0, u1: b.u1, cab: { id, l, r } }];
|
|
246
|
+
});
|
|
247
|
+
const rows: Item[][] = [];
|
|
248
|
+
const inside = (it: Item) => it.u0 >= f.face[0] - 0.01 && it.u1 <= f.face[1] + 0.01;
|
|
249
|
+
const fits = (row: Item[], it: Item) => inside(it) && !row.some(x => it.u0 < x.u1 - 0.01 && it.u1 > x.u0 + 0.01);
|
|
250
|
+
// doors and windows first, then cabinets: each in the first string it fits, overlapping ones in strings of their own
|
|
251
|
+
for (const it of [...ops.sort((p, q) => p.u0 - q.u0), ...cabs.sort((p, q) => p.u0 - q.u0)]) {
|
|
252
|
+
if (!inside(it)) continue;
|
|
253
|
+
const row = rows.find(r => fits(r, it));
|
|
254
|
+
if (row) row.push(it); else rows.push([it]);
|
|
221
255
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
256
|
+
return rows.map(r => r.sort((p, q) => p.u0 - q.u0));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** One string's dimensions: each gap between corners, doors, windows and cabinets, and each one's width. */
|
|
260
|
+
function stringSegs(sk: Sketch, f: ElevFrame, face: WallFace, row: Item[]): Seg[] {
|
|
261
|
+
const other: 'a' | 'b' = f.leftEnd === 'a' ? 'b' : 'a';
|
|
262
|
+
type Node = { u: number; pt: string; end?: 'a' | 'b'; it?: Item; edge?: OpeningEdge };
|
|
263
|
+
const nodes: Node[] = [{ u: f.face[0], pt: cornerAnchor(f.leftEnd), end: f.leftEnd }];
|
|
264
|
+
for (const it of row) {
|
|
265
|
+
if (it.op) nodes.push({ u: it.u0, pt: openingAnchor(it.op.id, it.op.e0), it, edge: it.op.e0 }, { u: it.u1, pt: openingAnchor(it.op.id, it.op.e1), it, edge: it.op.e1 });
|
|
266
|
+
else nodes.push({ u: it.u0, pt: it.cab!.l, it }, { u: it.u1, pt: it.cab!.r, it });
|
|
267
|
+
}
|
|
268
|
+
nodes.push({ u: f.face[1], pt: cornerAnchor(other), end: other });
|
|
269
|
+
if (nodes.length <= 2) return [];
|
|
270
|
+
const faceRef = (end: 'a' | 'b'): OpeningRef => ({ face: end, line: f.lid, side: f.side as 1 | -1 });
|
|
225
271
|
const segs: Seg[] = [];
|
|
226
272
|
for (let i = 1; i < nodes.length; i++) {
|
|
227
273
|
const n1 = nodes[i - 1], n2 = nodes[i];
|
|
228
|
-
if (n1.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
274
|
+
if (n1.it && n1.it === n2.it) {
|
|
275
|
+
// a door's or window's width (its own), or a cabinet's (a face dimension)
|
|
276
|
+
if (n1.it.op) segs.push({ u0: n1.u, u1: n2.u, act: { k: 'w', op: n1.it.op.id }, look: widthEntered(sk.openings[n1.it.op.id]) ? 'drive' : 'free', width: true });
|
|
277
|
+
else {
|
|
278
|
+
const id = face.cabs[n1.it.cab!.id].w;
|
|
279
|
+
segs.push(id && face.cons[id]
|
|
280
|
+
? { u0: n1.u, u1: n2.u, act: { k: 'fdim', id }, look: faceLook(face.cons[id], id), width: true }
|
|
281
|
+
: { u0: n1.u, u1: n2.u, act: { k: 'gap', a: n1.pt, b: n2.pt, kind: 'h', title: 'Cabinet width' }, look: 'free', width: true });
|
|
282
|
+
}
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (!n1.it?.cab && !n2.it?.cab) {
|
|
286
|
+
if (!n1.it && !n2.it) continue;
|
|
287
|
+
// between corners, doors and windows: the plan's door/window dimensions
|
|
288
|
+
let act: PosAct;
|
|
289
|
+
if (!n1.it) act = { k: 'pos', op: n2.it!.op!.id, edge: n2.edge!, ref: faceRef(n1.end!), title: 'Distance from the corner' };
|
|
290
|
+
else if (!n2.it) act = { k: 'pos', op: n1.it.op!.id, edge: n1.edge!, ref: faceRef(n2.end!), title: 'Distance from the corner' };
|
|
291
|
+
else act = { k: 'pos', ...pickBetween(sk, n1.it.op!.id, n1.edge!, n2.it.op!.id, n2.edge!), title: 'Space between' };
|
|
292
|
+
const d = (sk.openings[act.op].dims || []).find(x => x.edge === act.edge && sameRef(x.ref, act.ref));
|
|
293
|
+
segs.push({ u0: n1.u, u1: n2.u, act, look: !d ? 'free' : ed.badOpeningDims.has(d.id) ? 'bad' : d.driving ? 'drive' : 'ref' });
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
// to or from a cabinet: a face dimension once entered
|
|
297
|
+
const id = dimAcross(face, n1.pt, n2.pt);
|
|
298
|
+
const title = n1.end || n2.end ? 'Distance from the corner' : 'Space between';
|
|
299
|
+
segs.push(id ? { u0: n1.u, u1: n2.u, act: { k: 'fdim', id }, look: faceLook(face.cons[id], id) } : { u0: n1.u, u1: n2.u, act: { k: 'gap', a: n1.pt, b: n2.pt, kind: 'h', title }, look: 'free' });
|
|
236
300
|
}
|
|
237
|
-
return
|
|
301
|
+
return segs;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** The doors, windows and cabinets a wall-view dimension measures from or to (by the points it runs between). */
|
|
305
|
+
function owners(face: WallFace, act: ElevAct): string[] {
|
|
306
|
+
const out: string[] = [];
|
|
307
|
+
const of = (pid: string | undefined) => {
|
|
308
|
+
if (!pid) return;
|
|
309
|
+
const m = /^@o:([^:]+):/.exec(pid);
|
|
310
|
+
if (m) { out.push(m[1]); return; }
|
|
311
|
+
for (const [id, cb] of Object.entries(face.cabs ?? {})) if ([cb.bl, cb.br, cb.tr, cb.tl].includes(pid)) out.push(id);
|
|
312
|
+
};
|
|
313
|
+
if ('op' in act) out.push(act.op);
|
|
314
|
+
if (act.k === 'pos' && 'op' in act.ref) out.push(act.ref.op);
|
|
315
|
+
if (act.k === 'depth') out.push(act.cab);
|
|
316
|
+
if (act.k === 'gap') { of(act.a); of(act.b); }
|
|
317
|
+
if (act.k === 'fdim') { const c = face.cons[act.id]; of(c?.a); of(c?.b); }
|
|
318
|
+
// the wall's own length and height: the wall's
|
|
319
|
+
if (act.k === 'len' || act.k === 'wallH') out.push(WALL);
|
|
320
|
+
return out;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* The doors, windows and cabinets whose reference dimensions show (as the plan shows a door's or window's distances
|
|
325
|
+
* only while it's selected): the selected and hovered ones, and the one a selected dimension belongs to.
|
|
326
|
+
*/
|
|
327
|
+
/** The wall itself, among what a wall view's dimensions belong to: live while one of its edges or corners is picked. */
|
|
328
|
+
const WALL = '@wall';
|
|
329
|
+
/** One of the wall's own edges or corners (the face's anchors that aren't a door's or window's, nor a shape's). */
|
|
330
|
+
const wallPart = (id: string): boolean => id.startsWith('@') && !id.startsWith('@o:') && !id.startsWith('@k:');
|
|
331
|
+
|
|
332
|
+
function liveObjects(face: WallFace): Set<string> {
|
|
333
|
+
const live = new Set<string>();
|
|
334
|
+
const add = (type: string, id: string) => {
|
|
335
|
+
if (type === 'opening' || type === 'cab') live.add(id);
|
|
336
|
+
else if ((type === 'line' || type === 'point') && wallPart(id)) live.add(WALL);
|
|
337
|
+
else if (type === 'elev') { try { for (const o of owners(face, JSON.parse(id) as ElevAct)) live.add(o); } catch { /* not a chip */ } }
|
|
338
|
+
else if (type === 'dim' && face.cons[id]) for (const o of owners(face, { k: 'fdim', id })) live.add(o);
|
|
339
|
+
};
|
|
340
|
+
for (const s of ed.sel) add(s.type, s.id);
|
|
341
|
+
if (ed.hover) add(ed.hover.type, ed.hover.id);
|
|
342
|
+
return live;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* The wall view's dimensions, over the face: the strings along the floor, door/window dimensions that aren't a step of
|
|
347
|
+
* one, face dimensions entered from a chip that no longer are, the wall's length (set in the plan) and height, and
|
|
348
|
+
* beside each door, window and cabinet its height off the floor and its own height. `on`: whether a chip is selected.
|
|
349
|
+
* As in the plan, what's been entered (blue) always shows; the rest (grey: just measured, a standard size or a
|
|
350
|
+
* reference) only while what it belongs to — a door, window or cabinet, or the wall itself (an edge or corner of it) —
|
|
351
|
+
* is selected or hovered. Returns the chips, to tap.
|
|
352
|
+
*/
|
|
353
|
+
export function drawWallChips(c: C2D, T: Xform, sk: Sketch, f: ElevFrame, face: WallFace, on: (act: ElevAct) => boolean): ElevHit[] {
|
|
354
|
+
const hits: ElevHit[] = [], shown = new Set<string>(), live = liveObjects(face);
|
|
355
|
+
/** entered by someone, so it shows in blue (or red, when it can't be met) — not just measured, a standard size or a reference */
|
|
356
|
+
const entered = (act: ElevAct, look: Look) => (look === 'drive' || look === 'bad' || look === 'linked') && !(act.k === 'fdim' && face.cons[act.id]?.auto);
|
|
357
|
+
/** an object's own size (a door's, window's or cabinet's width or height; the wall's length or height), not a distance */
|
|
358
|
+
const ownSize = (act: ElevAct): boolean => act.k === 'w' || act.k === 'h' || act.k === 'len' || act.k === 'wallH'
|
|
359
|
+
|| (act.k === 'gap' && (act.title === 'Cabinet width' || act.title === 'Cabinet height'))
|
|
360
|
+
|| (act.k === 'fdim' && Object.values(face.cabs ?? {}).some(cb => cb.w === act.id || cb.h === act.id));
|
|
361
|
+
// a dimension shows once it's entered, and otherwise only while what it belongs to is selected or hovered
|
|
362
|
+
const tag = (p: [number, number], q: [number, number], nx: number, ny: number, label: string, look: Look, act: ElevAct, force = false) => {
|
|
363
|
+
if (!entered(act, look) && !on(act) && !owners(face, act).some(o => live.has(o))) return;
|
|
364
|
+
// as in the plan, a number that holds nothing is dashed and in parentheses: a reference, or one only measured —
|
|
365
|
+
// a distance, or the wall's length when the plan doesn't lock it (a standard size, which holds, stays solid)
|
|
366
|
+
const dashed = look === 'ref' || (look === 'free' && !(ownSize(act) && act.k !== 'len'));
|
|
367
|
+
const box = dim(c, p, q, nx, ny, label, look, force, on(act), dashed);
|
|
368
|
+
if (box) hits.push({ ...box, act, look });
|
|
369
|
+
if (act.k === 'fdim') shown.add(act.id);
|
|
370
|
+
};
|
|
371
|
+
/** the chip's value: a driving face dimension's own, otherwise what's measured */
|
|
372
|
+
const val = (act: ElevAct, measured: number) => (act.k === 'fdim' && face.cons[act.id]?.driving !== false ? face.cons[act.id].value! : measured);
|
|
373
|
+
const rows = strings(sk, f, face);
|
|
374
|
+
rows.forEach((row, r) => {
|
|
375
|
+
for (const g of stringSegs(sk, f, face, row)) tag(S(T, g.u0, 0), S(T, g.u1, 0), 0, ROW0 + ROW * r, fmtLen(val(g.act, g.u1 - g.u0)), g.look, g.act);
|
|
376
|
+
});
|
|
377
|
+
let next = Math.max(rows.length, 1);
|
|
378
|
+
// door/window dimensions that aren't a step of a string, packed into rows of their own
|
|
379
|
+
const extra = extraDims(sk, f, rows);
|
|
380
|
+
for (const x of extra) tag(S(T, x.u0, 0), S(T, x.u1, 0), 0, ROW0 + ROW * (next + x.row), fmtLen(x.u1 - x.u0), ed.badOpeningDims.has(x.d.id) ? 'bad' : x.d.driving ? 'drive' : 'ref', x.act);
|
|
381
|
+
next += extra.reduce((m, x) => Math.max(m, x.row + 1), 0);
|
|
382
|
+
// heights beside doors, windows and cabinets
|
|
383
|
+
for (const o of elevOpenings(sk, f)) {
|
|
384
|
+
const op = sk.openings[o.id];
|
|
385
|
+
if (o.sill > 0.01 || o.type === 'window') tag(S(T, o.u1, 0), S(T, o.u1, o.sill), 24, 0, fmtLen(o.sill), op.sillLocked ? 'drive' : 'free', { k: 'sill', op: o.id }, true);
|
|
386
|
+
tag(S(T, o.u1, o.sill), S(T, o.u1, o.head), 24, 0, fmtLen(o.head - o.sill), op.h != null ? 'drive' : 'free', { k: 'h', op: o.id });
|
|
387
|
+
}
|
|
388
|
+
// beside a cabinet: on its right like a window's, unless a cabinet or opening is right there (a run of cabinets),
|
|
389
|
+
// then on its left, else just inside it
|
|
390
|
+
const boxes = [...Object.keys(face.cabs ?? {}).map(id => ({ id, b: cabBox(face, id) })).filter(x => x.b).map(x => ({ id: x.id, ...x.b! })),
|
|
391
|
+
...elevOpenings(sk, f).map(o => ({ id: o.id, u0: o.u0, u1: o.u1, z0: o.sill, z1: o.head }))];
|
|
392
|
+
const room = 64 / T.s;
|
|
393
|
+
const blocked = (id: string, b: { z0: number; z1: number }, lo: number, hi: number) =>
|
|
394
|
+
boxes.some(o => o.id !== id && o.u1 > lo + 0.01 && o.u0 < hi - 0.01 && o.z1 > b.z0 - 0.01 && o.z0 < b.z1 + 0.01);
|
|
395
|
+
for (const [id, cb] of Object.entries(face.cabs ?? {})) {
|
|
396
|
+
const b = cabBox(face, id);
|
|
397
|
+
if (!b) continue;
|
|
398
|
+
const [l, r] = cabSides(face, id), zid = cabLiftDim(face, id);
|
|
399
|
+
const right = !blocked(id, b, b.u1, b.u1 + room), left = !right && !blocked(id, b, b.u0 - room, b.u0);
|
|
400
|
+
const u = right ? b.u1 : b.u0, off = right ? 24 : left ? -24 : 24, at = right || left ? u : b.u1 - 48 / T.s;
|
|
401
|
+
// its height off the floor: when it's off the floor, set, or selected (a cabinet on the floor needs no 0")
|
|
402
|
+
if (b.z0 > 0.01 || zid || live.has(id)) {
|
|
403
|
+
const zAct: ElevAct = zid ? { k: 'fdim', id: zid } : { k: 'gap', a: cornerAnchor(f.leftEnd), b: l, kind: 'v', title: 'Cabinet off the floor' };
|
|
404
|
+
tag(S(T, at, 0), S(T, at, b.z0), off, 0, fmtLen(val(zAct, b.z0)), zid ? faceLook(face.cons[zid], zid) : 'free', zAct, true);
|
|
405
|
+
}
|
|
406
|
+
// its depth, on its door near the bottom: a chip like the others (grey until it's entered, and then only while
|
|
407
|
+
// the cabinet is selected or hovered), on the cabinet itself
|
|
408
|
+
if (cb.depthSet || live.has(id) || on({ k: 'depth', cab: id })) {
|
|
409
|
+
const dAct: ElevAct = { k: 'depth', cab: id }, look: Look = cb.depthSet ? 'drive' : 'free', st = LOOK(look);
|
|
410
|
+
const [cx] = S(T, (b.u0 + b.u1) / 2, 0), [, by] = S(T, 0, b.z0), [, ty] = S(T, 0, b.z1);
|
|
411
|
+
if (by - ty > 40) {
|
|
412
|
+
const y = by - Math.min(16, (by - ty) * 0.15), text = 'D ' + fmtLen(cb.depth);
|
|
413
|
+
const w = chip(c, text, cx, y, { bg: st.bg, fg: st.fg, font: '600 11.5px', h: 20, pad: 6, border: on(dAct) ? [TH.yellow, 2] : null });
|
|
414
|
+
hits.push({ x: cx - w / 2 - 4, y: y - 14, w: w + 8, h: 28, act: dAct, look });
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
const top = r === cb.br ? cb.tr : cb.tl;
|
|
418
|
+
const hAct: ElevAct = cb.h && face.cons[cb.h] ? { k: 'fdim', id: cb.h } : { k: 'gap', a: r, b: top, kind: 'v', title: 'Cabinet height' };
|
|
419
|
+
tag(S(T, at, b.z0), S(T, at, b.z1), off, 0, fmtLen(val(hAct, b.z1 - b.z0)), hAct.k === 'fdim' ? faceLook(face.cons[hAct.id], hAct.id) : 'free', hAct);
|
|
420
|
+
}
|
|
421
|
+
// face dimensions entered from a chip that isn't drawn now (what they measure has moved): across in rows of their own, up beside their point
|
|
422
|
+
const lostRows: [number, number][][] = [];
|
|
423
|
+
for (const [id, k] of Object.entries(face.cons)) {
|
|
424
|
+
if (!k.elev || k.anchor || k.type !== 'dist' || shown.has(id) || !k.a || !k.b || !face.points[k.a] || !face.points[k.b]) continue;
|
|
425
|
+
if (Object.values(face.cabs ?? {}).some(cb => cb.w === id || cb.h === id)) continue;
|
|
426
|
+
const A = face.points[k.a], B = face.points[k.b], act: ElevAct = { k: 'fdim', id }, text = fmtLen(k.driving === false ? measure(face, k) : k.value!);
|
|
427
|
+
if (k.kind === 'v') { tag(S(T, B.x, A.y), S(T, B.x, B.y), -24, 0, text, faceLook(k, id), act, true); continue; }
|
|
428
|
+
const u0 = Math.min(A.x, B.x), u1 = Math.max(A.x, B.x);
|
|
429
|
+
let row = 0;
|
|
430
|
+
while ((lostRows[row] || []).some(([x, y]) => u0 < y + 6 && u1 > x - 6)) row++;
|
|
431
|
+
(lostRows[row] = lostRows[row] || []).push([u0, u1]);
|
|
432
|
+
tag(S(T, A.x, 0), S(T, B.x, 0), 0, ROW0 + ROW * (next + row), text, faceLook(k, id), act);
|
|
433
|
+
}
|
|
434
|
+
next += lostRows.length;
|
|
435
|
+
// the wall's length comes from the floor plan: linked (outlined blue) when a dimension locks it there, grey when free
|
|
436
|
+
const planLocked = Object.values(sk.cons).some(cn => cn.type === 'dist' && cn.line === f.lid && cn.driving !== false && cn.layer !== 'print');
|
|
437
|
+
const anyRow = rows.some(r => r.length) || next > 1;
|
|
438
|
+
tag(S(T, f.face[0], 0), S(T, f.face[1], 0), 0, anyRow ? ROW0 + ROW * next : ROW0, fmtLen(f.face[1] - f.face[0]), planLocked ? 'linked' : 'free', { k: 'len' });
|
|
439
|
+
// the wall's height: blue once entered, grey while it's the default
|
|
440
|
+
tag(S(T, f.face[1], 0), S(T, f.face[1], f.H), 34, 0, fmtLen(f.H), sk.lines[f.lid].height != null ? 'drive' : 'free', { k: 'wallH' });
|
|
441
|
+
return hits;
|
|
238
442
|
}
|
|
239
443
|
|
|
240
444
|
export interface ExtraDim { u0: number; u1: number; d: OpeningDim; act: PosAct; row: number }
|
|
241
445
|
|
|
242
|
-
/** Saved door/window dimensions on this wall that the
|
|
243
|
-
export function extraDims(sk: Sketch, f: ElevFrame): ExtraDim[] {
|
|
244
|
-
const shown = new Set(
|
|
446
|
+
/** Saved door/window dimensions on this wall that the strings (`rows`) don't show, packed into rows. */
|
|
447
|
+
export function extraDims(sk: Sketch, f: ElevFrame, strs?: Item[][]): ExtraDim[] {
|
|
448
|
+
const shown = new Set<string>();
|
|
449
|
+
for (const row of strs ?? strings(sk, f, emptyFace())) for (const g of stringSegs(sk, f, emptyFace(), row.filter(it => it.op))) {
|
|
450
|
+
if (g.act.k !== 'pos') continue;
|
|
451
|
+
const a = g.act, d = (sk.openings[a.op].dims || []).find(x => x.edge === a.edge && sameRef(x.ref, a.ref));
|
|
452
|
+
if (d) shown.add(d.id);
|
|
453
|
+
}
|
|
245
454
|
const out: ExtraDim[] = [], rows: [number, number][][] = [];
|
|
246
455
|
for (const o of elevOpenings(sk, f)) {
|
|
247
456
|
const op = sk.openings[o.id], g = openingSpan(sk, op);
|
|
@@ -251,20 +460,17 @@ export function extraDims(sk: Sketch, f: ElevFrame): ExtraDim[] {
|
|
|
251
460
|
const r = refS(sk, op, d.ref);
|
|
252
461
|
if (r == null) continue;
|
|
253
462
|
const a = toU(f, r), b = toU(f, edgeS(op, g.w, d.edge)), u0 = Math.min(a, b), u1 = Math.max(a, b);
|
|
254
|
-
let
|
|
255
|
-
while ((rows[
|
|
256
|
-
(rows[
|
|
463
|
+
let k = 0;
|
|
464
|
+
while ((rows[k] || []).some(([x, y]) => u0 < y + 6 && u1 > x - 6)) k++;
|
|
465
|
+
(rows[k] = rows[k] || []).push([u0, u1]);
|
|
257
466
|
const title = 'op' in d.ref ? 'Space between' : 'face' in d.ref ? 'Distance from the corner' : 'Distance from the wall end';
|
|
258
|
-
out.push({ u0, u1, d, row, act: { k: 'pos', op: o.id, edge: d.edge, ref: d.ref, title } });
|
|
467
|
+
out.push({ u0, u1, d, row: k, act: { k: 'pos', op: o.id, edge: d.edge, ref: d.ref, title } });
|
|
259
468
|
}
|
|
260
469
|
}
|
|
261
470
|
return out;
|
|
262
471
|
}
|
|
263
472
|
|
|
264
|
-
/**
|
|
265
|
-
export const extraRowsPx = (sk: Sketch, f: ElevFrame): number => ROW * extraDims(sk, f).reduce((m, x) => Math.max(m, x.row + 1), 0);
|
|
266
|
-
|
|
267
|
-
/** A chip for previews (line length while drawing). */
|
|
473
|
+
/** A chip for previews (a ghost's size, a height while placing). */
|
|
268
474
|
export const previewChip = (c: C2D, text: string, x: number, y: number) => chip(c, text, x, y, { bg: TH.chip, fg: TH.ink, font: '600 11.5px', h: 20, pad: 6, border: [TH.purple, 1.5] });
|
|
269
475
|
|
|
270
476
|
/** A small plan of the page in the corner: the wall in yellow and an arrow showing where you're looking from. */
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Icons drawn in a Figma frame, kept exactly as Figma exports them (their own size, viewBox and colours) rather than
|
|
3
|
+
redrawn on the rail's 24 grid: the Pages panel's Delete and + Page (Figma 2548:101989).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Delete page: a trash can, white, for the red pill. */
|
|
7
|
+
export const TRASH_SVG = `<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="22.7032" height="25.25" viewBox="0 0 22.7032 25.25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
8
|
+
<g id="Group 1707480533">
|
|
9
|
+
<path id="Vector" d="M8.79826 11.3535V19.0063" stroke="white" stroke-width="2.29583" stroke-linecap="round" stroke-linejoin="round"/>
|
|
10
|
+
<path id="Vector_2" d="M13.9033 11.3535V19.0063" stroke="white" stroke-width="2.29583" stroke-linecap="round" stroke-linejoin="round"/>
|
|
11
|
+
<path id="Vector_3" d="M1.14792 6.24557H21.5553" stroke="white" stroke-width="2.29583" stroke-linecap="round" stroke-linejoin="round"/>
|
|
12
|
+
<path id="Vector_4" d="M3.69674 6.24557H11.3495H19.0023V20.2757C19.0023 22.389 17.2892 24.1021 15.1759 24.1021H7.52313C5.40988 24.1021 3.69674 22.389 3.69674 20.2757V6.24557Z" stroke="white" stroke-width="2.29583" stroke-linecap="round" stroke-linejoin="round"/>
|
|
13
|
+
<path id="Vector_5" d="M7.52578 3.69884C7.52578 2.29 8.66787 1.14792 10.0767 1.14792H12.6276C14.0365 1.14792 15.1786 2.29 15.1786 3.69884V6.24977H7.52578V3.69884Z" stroke="white" stroke-width="2.29583" stroke-linecap="round" stroke-linejoin="round"/>
|
|
14
|
+
</g>
|
|
15
|
+
</svg>`;
|
|
16
|
+
export const TRASH_SIZE = { width: 22.7032, height: 25.25 };
|
|
17
|
+
|
|
18
|
+
/** + Page: a plus, dark grey, for the yellow pill. */
|
|
19
|
+
export const PLUS_SVG = `<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18.5" height="18.5" viewBox="0 0 18.5 18.5" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
20
|
+
<path id="Vector" d="M9.25 0.125C9.58152 0.125 9.89937 0.256791 10.1338 0.491211C10.3682 0.725631 10.5 1.04348 10.5 1.375V8H17.125C17.4565 8 17.7744 8.13179 18.0088 8.36621C18.2432 8.60063 18.375 8.91848 18.375 9.25C18.375 9.58152 18.2432 9.89937 18.0088 10.1338C17.7744 10.3682 17.4565 10.5 17.125 10.5H10.5V17.125C10.5 17.4565 10.3682 17.7744 10.1338 18.0088C9.89937 18.2432 9.58152 18.375 9.25 18.375C8.91848 18.375 8.60063 18.2432 8.36621 18.0088C8.13179 17.7744 8 17.4565 8 17.125V10.5H1.375C1.04348 10.5 0.725631 10.3682 0.491211 10.1338C0.256791 9.89937 0.125 9.58152 0.125 9.25C0.125 8.91848 0.256791 8.60063 0.491211 8.36621C0.725631 8.13179 1.04348 8 1.375 8H8V1.375C8 1.04348 8.13179 0.725631 8.36621 0.491211C8.60063 0.256791 8.91848 0.125 9.25 0.125Z" fill="#181A1C" stroke="#181A1C" stroke-width="0.25"/>
|
|
21
|
+
</svg>`;
|
|
22
|
+
export const PLUS_SIZE = { width: 18.5, height: 18.5 };
|