@reekon-tools/boldr-cad 0.1.0 → 1.1.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 +99 -16
- 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 +9 -1
- package/dist/data/memory.js +13 -8
- package/dist/data/memory.js.map +1 -1
- package/dist/data/provider.d.ts +30 -10
- package/dist/data/sync.d.ts +14 -3
- package/dist/data/sync.js +133 -66
- 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 +20 -5
- package/dist/editor/keypad.js +62 -15
- 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 +38 -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 +170 -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 +7 -1
- 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 +54 -4
- package/dist/react/CadEditor.js +79 -21
- 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 +582 -97
- package/dist/tools/dim.js.map +1 -1
- package/dist/tools/elevation.js +5 -5
- 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 +25 -18
- 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 +11 -3
- package/dist/units/format.js +27 -6
- package/dist/units/format.js.map +1 -1
- package/dist/units/parse.d.ts +2 -1
- package/dist/units/parse.js +5 -4
- package/dist/units/parse.js.map +1 -1
- package/dist/units/prefs.d.ts +9 -1
- package/dist/units/prefs.js +6 -0
- package/dist/units/prefs.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 +15 -10
- package/src/data/provider.ts +25 -10
- package/src/data/sync.ts +107 -53
- 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 +67 -18
- 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 +39 -6
- package/src/editor/project.ts +157 -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 +7 -1
- 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 +142 -34
- 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 +525 -93
- package/src/tools/elevation.ts +5 -5
- 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 +11 -16
- 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 +24 -6
- package/src/units/parse.ts +4 -3
- package/src/units/prefs.ts +13 -1
- 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/render/elevation.js
CHANGED
|
@@ -1,51 +1,24 @@
|
|
|
1
|
-
import { ed } from '../app/state';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { ed, isSel } from '../app/state';
|
|
2
|
+
import { measure } from '../model/constraints';
|
|
3
|
+
import { elevOpenings, toU } from '../model/elevation';
|
|
4
|
+
import { cabPoints, cornerAnchor, emptyFace, openingAnchor } from '../model/faces';
|
|
5
|
+
import { edgeS, openingSpan, pickBetween, refS, sameRef, widthEntered } from '../model/openings';
|
|
4
6
|
import { fmtLen } from '../units';
|
|
5
|
-
import { chip } from './draw';
|
|
7
|
+
import { chip, linkBlue, linkMark } from './draw';
|
|
6
8
|
import { FONT, TH } from './theme';
|
|
7
9
|
import { S } from './view';
|
|
8
|
-
export const elevT = (v, W, H) => ({ s: v.s, ox: W / 2 - v.cu * v.s, oy: H / 2 + v.cz * v.s });
|
|
9
|
-
/** Screen room taken by dimensions around the wall, pixels: below the floor, right of the face. */
|
|
10
|
-
const DIM_BELOW = 92, DIM_RIGHT = 70, PAD = 36;
|
|
11
|
-
/**
|
|
12
|
-
* Fit the wall to a W × H view, leaving pixel margins for the dimensions and for anything overlaid
|
|
13
|
-
* (`keep`: space taken at the top or the right, e.g. the key plan).
|
|
14
|
-
*/
|
|
15
|
-
export function elevFit(f, W, H, keep = {}) {
|
|
16
|
-
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]));
|
|
17
|
-
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);
|
|
18
|
-
const s = Math.max(0.05, Math.min((W - 2 * PAD - right - left) / (w || 1), (H - 2 * PAD - below - top) / (h || 1)));
|
|
19
|
-
// centre the drawing in the space left after the margins
|
|
20
|
-
return { s, cu: (u0 + u1) / 2 + (right - left) / 2 / s, cz: h / 2 - (below - top) / 2 / s };
|
|
21
|
-
}
|
|
22
10
|
const LOOK = (look) => look === 'drive' ? { bg: TH.blue, fg: '#fff', line: TH.blue } : look === 'bad' ? { bg: TH.red, fg: '#fff', line: TH.red }
|
|
23
11
|
: look === 'linked' ? { bg: TH.bg, fg: linkBlue(), line: linkBlue() }
|
|
24
12
|
: look === 'ref' ? { bg: TH.ref, fg: TH.reftext, line: TH.chipline } : { bg: TH.chip, fg: TH.ink, line: TH.ink2 };
|
|
25
|
-
/** Blue for outlined (linked) chips: the lighter blue on a dark canvas, so the text stays readable. */
|
|
26
|
-
function linkBlue() {
|
|
27
|
-
const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i.exec(TH.bg);
|
|
28
|
-
const dark = !!m && (parseInt(m[1], 16) * 299 + parseInt(m[2], 16) * 587 + parseInt(m[3], 16) * 114) / 1000 < 128;
|
|
29
|
-
return dark ? TH.lblue : TH.blue;
|
|
30
|
-
}
|
|
31
|
-
/** A small chain-link mark centred on (x, y). */
|
|
32
|
-
function linkMark(c, x, y, color) {
|
|
33
|
-
c.save();
|
|
34
|
-
c.translate(x, y);
|
|
35
|
-
c.rotate(-Math.PI / 4);
|
|
36
|
-
c.strokeStyle = color;
|
|
37
|
-
c.lineWidth = 1.4;
|
|
38
|
-
c.beginPath();
|
|
39
|
-
c.roundRect(-6.5, -2.25, 7.5, 4.5, 2.25);
|
|
40
|
-
c.roundRect(-1, -2.25, 7.5, 4.5, 2.25);
|
|
41
|
-
c.stroke();
|
|
42
|
-
c.restore();
|
|
43
|
-
}
|
|
44
13
|
/**
|
|
45
14
|
* Linear dimension between screen points p and q, its line offset by (nx, ny) pixels: ticks, extension lines and
|
|
46
15
|
* the value chip. Returns the chip's box (for taps). `force` draws the chip even for a zero-length dimension.
|
|
47
16
|
*/
|
|
48
|
-
|
|
17
|
+
/**
|
|
18
|
+
* A dimension: extension lines, the dimension line with 45° ticks, the chip. `dashed`: a number that holds nothing (only
|
|
19
|
+
* measured, or a reference) — its dimension line dashed and its value in parentheses, as in the plan.
|
|
20
|
+
*/
|
|
21
|
+
function dim(c, p, q, nx, ny, label, look = 'free', force = false, selected = false, dashed = false) {
|
|
49
22
|
const len = Math.hypot(q[0] - p[0], q[1] - p[1]);
|
|
50
23
|
if (len < 2 && !force)
|
|
51
24
|
return null;
|
|
@@ -59,8 +32,20 @@ function dim(c, p, q, nx, ny, label, look = 'free', force = false, selected = fa
|
|
|
59
32
|
c.lineTo(e[0] + (nx / L) * 5, e[1] + (ny / L) * 5);
|
|
60
33
|
}
|
|
61
34
|
if (len >= 2) {
|
|
62
|
-
|
|
63
|
-
|
|
35
|
+
if (dashed) {
|
|
36
|
+
c.stroke();
|
|
37
|
+
c.beginPath();
|
|
38
|
+
c.setLineDash([3, 3]);
|
|
39
|
+
c.moveTo(a[0], a[1]);
|
|
40
|
+
c.lineTo(b[0], b[1]);
|
|
41
|
+
c.stroke();
|
|
42
|
+
c.beginPath();
|
|
43
|
+
c.setLineDash([]);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
c.moveTo(a[0], a[1]);
|
|
47
|
+
c.lineTo(b[0], b[1]);
|
|
48
|
+
}
|
|
64
49
|
// 45° architectural ticks
|
|
65
50
|
const ux = (b[0] - a[0]) / len, uy = (b[1] - a[1]) / len;
|
|
66
51
|
const tx = (ux + nx / L) * 4, ty = (uy + ny / L) * 4;
|
|
@@ -72,7 +57,8 @@ function dim(c, p, q, nx, ny, label, look = 'free', force = false, selected = fa
|
|
|
72
57
|
c.stroke();
|
|
73
58
|
c.restore();
|
|
74
59
|
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;
|
|
75
|
-
|
|
60
|
+
// a number that holds nothing (dashed) reads in parentheses, as in the plan
|
|
61
|
+
const linked = look === 'linked', text = dashed ? '(' + label + ')' : linked ? ' ' + label : label; // (figure spaces: room for the link mark)
|
|
76
62
|
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 });
|
|
77
63
|
if (linked)
|
|
78
64
|
linkMark(c, cx - w / 2 + 11, cy, st.fg);
|
|
@@ -131,50 +117,96 @@ function drawOpening(c, T, o) {
|
|
|
131
117
|
c.fillText(o.type === 'door' ? 'Door' : 'Window', (x0 + x1) / 2, o.type === 'door' ? yTop + h * 0.3 : (yTop + yBot) / 2);
|
|
132
118
|
c.restore();
|
|
133
119
|
}
|
|
134
|
-
/**
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
120
|
+
/** A cabinet's tint and panel lines: neutral grey (no yellow — that's the selection's), against a window's blue glass. */
|
|
121
|
+
const WOOD = 'rgba(128,128,128,.14)', WOOD_LINE = 'rgba(128,128,128,.9)';
|
|
122
|
+
/**
|
|
123
|
+
* A cabinet, as cabinetry is drawn in elevation: a wood-tinted box, a drawer across its top with a pull, and a door
|
|
124
|
+
* 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.
|
|
125
|
+
*/
|
|
126
|
+
function drawCabinet(c, T, x, y, w, h, name) {
|
|
127
|
+
const pad = Math.min(1.5 * T.s, w / 10, h / 10), drawer = Math.min(6 * T.s, h * 0.24);
|
|
128
|
+
c.save();
|
|
138
129
|
c.fillStyle = TH.bg;
|
|
139
|
-
c.fillRect(
|
|
130
|
+
c.fillRect(x, y, w, h);
|
|
131
|
+
c.fillStyle = WOOD;
|
|
132
|
+
c.fillRect(x, y, w, h);
|
|
133
|
+
c.strokeStyle = TH.ink;
|
|
134
|
+
c.lineWidth = 1.5;
|
|
135
|
+
c.strokeRect(x, y, w, h);
|
|
136
|
+
// the drawer front and the door, inset in the box
|
|
137
|
+
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;
|
|
138
|
+
c.strokeStyle = WOOD_LINE;
|
|
139
|
+
c.lineWidth = 1;
|
|
140
|
+
if (dh > 3)
|
|
141
|
+
c.strokeRect(ix, dy, iw, dh);
|
|
142
|
+
if (oh > 6)
|
|
143
|
+
c.strokeRect(ix, oy, iw, oh);
|
|
144
|
+
// a pull on the drawer, a handle at the top of the door on its right
|
|
145
|
+
c.strokeStyle = TH.ink;
|
|
146
|
+
c.lineWidth = 2;
|
|
147
|
+
c.lineCap = 'round';
|
|
148
|
+
c.beginPath();
|
|
149
|
+
if (dh > 6 && iw > 16) {
|
|
150
|
+
const pw = Math.min(iw * 0.3, 4 * T.s);
|
|
151
|
+
c.moveTo(ix + iw / 2 - pw / 2, dy + dh / 2);
|
|
152
|
+
c.lineTo(ix + iw / 2 + pw / 2, dy + dh / 2);
|
|
153
|
+
}
|
|
154
|
+
if (oh > 24 && iw > 16) {
|
|
155
|
+
const hx = ix + iw - Math.max(5, pad * 2), hl = Math.min(oh * 0.18, 4 * T.s);
|
|
156
|
+
c.moveTo(hx, oy + Math.max(6, pad * 1.5));
|
|
157
|
+
c.lineTo(hx, oy + Math.max(6, pad * 1.5) + hl);
|
|
158
|
+
}
|
|
159
|
+
c.stroke();
|
|
160
|
+
// its name, on the door (its sizes are in the bar when it's picked: on the drawing they'd be noise)
|
|
161
|
+
c.fillStyle = TH.ink2;
|
|
162
|
+
c.textAlign = 'center';
|
|
163
|
+
c.textBaseline = 'middle';
|
|
164
|
+
const cx = x + w / 2, cy = oy + oh / 2;
|
|
165
|
+
if (w > 44 && oh > 22) {
|
|
166
|
+
c.font = '600 11px ' + FONT;
|
|
167
|
+
c.fillText(name, cx, cy);
|
|
168
|
+
}
|
|
169
|
+
c.restore();
|
|
170
|
+
}
|
|
171
|
+
/** A cabinet's extent on the face. */
|
|
172
|
+
export function cabBox(face, id) {
|
|
173
|
+
const cb = face.cabs?.[id], ps = cb ? cabPoints(cb).map(p => face.points[p]) : [];
|
|
174
|
+
if (!cb || ps.some(p => !p))
|
|
175
|
+
return null;
|
|
176
|
+
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)) };
|
|
177
|
+
}
|
|
178
|
+
/** A cabinet's left and right bottom corners as seen. */
|
|
179
|
+
const cabSides = (face, id) => {
|
|
180
|
+
const cb = face.cabs[id];
|
|
181
|
+
return face.points[cb.bl].x <= face.points[cb.br].x ? [cb.bl, cb.br] : [cb.br, cb.bl];
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Under what's drawn on the face: the walls cut at its corners, the face, its doors and windows, its cabinets (a door
|
|
185
|
+
* panel and their name and size; their edges are the face's lines, drawn over this), the face's outline and the floor.
|
|
186
|
+
* (The selected door, window or cabinet's outline: drawWallSelection, over all of it.)
|
|
187
|
+
*/
|
|
188
|
+
export function drawWallBackdrop(c, T, sk, f, face) {
|
|
140
189
|
const [fx0, fyTop] = S(T, f.face[0], f.H), [fx1, fyBot] = S(T, f.face[1], 0);
|
|
141
|
-
// walls cut at the corners, then the face
|
|
142
190
|
for (const [a, b] of f.strips) {
|
|
143
191
|
const [x0] = S(T, a, 0), [x1] = S(T, b, 0);
|
|
144
192
|
hatch(c, x0, fyTop, x1 - x0, fyBot - fyTop);
|
|
145
193
|
}
|
|
146
194
|
c.fillStyle = TH.room;
|
|
147
195
|
c.fillRect(fx0, fyTop, fx1 - fx0, fyBot - fyTop);
|
|
148
|
-
const
|
|
149
|
-
const hits = [];
|
|
150
|
-
// the face's end edges, as tap targets for the corners (first, so openings and chips on top win)
|
|
151
|
-
for (const [u, end] of [[f.face[0], f.leftEnd], [f.face[1], f.leftEnd === 'a' ? 'b' : 'a']]) {
|
|
152
|
-
const [x, yTop] = S(T, u, f.H), [, yBot] = S(T, u, 0);
|
|
153
|
-
hits.push({ x: x - 10, y: yTop, w: 20, h: yBot - yTop, act: { k: 'corner', end } });
|
|
154
|
-
}
|
|
155
|
-
for (const o of ops) {
|
|
196
|
+
for (const o of elevOpenings(sk, f))
|
|
156
197
|
drawOpening(c, T, o);
|
|
157
|
-
|
|
158
|
-
|
|
198
|
+
for (const [id, cb] of Object.entries(face.cabs ?? {})) {
|
|
199
|
+
const b = cabBox(face, id);
|
|
200
|
+
if (!b)
|
|
201
|
+
continue;
|
|
202
|
+
const [x0, y0] = S(T, b.u0, b.z1), [x1, y1] = S(T, b.u1, b.z0);
|
|
203
|
+
drawCabinet(c, T, x0, y0, x1 - x0, y1 - y0, cb.label || 'Cabinet');
|
|
159
204
|
}
|
|
160
|
-
// lines drawn on this face: solid, or dashed guides
|
|
161
205
|
c.save();
|
|
162
|
-
c.lineCap = 'round';
|
|
163
|
-
for (const ln of elevLines(sk, f)) {
|
|
164
|
-
const [a, b] = elevLineUZ(f, ln), p = S(T, a[0], a[1]), q = S(T, b[0], b[1]);
|
|
165
|
-
c.strokeStyle = ln.guide ? TH.constr : TH.ink;
|
|
166
|
-
c.lineWidth = ln.guide ? 1.5 : 2;
|
|
167
|
-
c.setLineDash(ln.guide ? [6, 5] : []);
|
|
168
|
-
c.beginPath();
|
|
169
|
-
c.moveTo(p[0], p[1]);
|
|
170
|
-
c.lineTo(q[0], q[1]);
|
|
171
|
-
c.stroke();
|
|
172
|
-
}
|
|
173
|
-
c.restore();
|
|
174
206
|
c.strokeStyle = TH.ink;
|
|
175
207
|
c.lineWidth = 2;
|
|
176
208
|
c.strokeRect(fx0, fyTop, fx1 - fx0, fyBot - fyTop);
|
|
177
|
-
// floor line across
|
|
209
|
+
// the floor line, across the cut walls too
|
|
178
210
|
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]));
|
|
179
211
|
const [gx0] = S(T, u0, 0), [gx1] = S(T, u1, 0);
|
|
180
212
|
c.lineWidth = 3;
|
|
@@ -182,80 +214,324 @@ export function drawElevation(c, W, H, v, sk, f, selected = null) {
|
|
|
182
214
|
c.moveTo(gx0 - 14, fyBot);
|
|
183
215
|
c.lineTo(gx1 + 14, fyBot);
|
|
184
216
|
c.stroke();
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
217
|
+
c.restore();
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The selected door, window or cabinet (and the one under the pointer, fainter): a yellow outline right on its edges,
|
|
221
|
+
* drawn over everything else on the wall — its own edges and its neighbours' — so the whole of it shows, even where
|
|
222
|
+
* it shares a side with another cabinet, or lines up with a window.
|
|
223
|
+
*/
|
|
224
|
+
export function drawWallSelection(c, T, sk, f, face) {
|
|
225
|
+
const lit = (type, id) => isSel(type, id) || (ed.hover?.type === type && ed.hover.id === id);
|
|
226
|
+
const ring = (u0, z1, u1, z0, selected) => {
|
|
227
|
+
const [x0, y0] = S(T, u0, z1), [x1, y1] = S(T, u1, z0);
|
|
228
|
+
c.save();
|
|
229
|
+
c.strokeStyle = TH.yellow;
|
|
230
|
+
c.globalAlpha = selected ? 1 : 0.5;
|
|
231
|
+
c.lineWidth = 3;
|
|
232
|
+
c.lineJoin = 'miter';
|
|
233
|
+
c.strokeRect(x0, y0, x1 - x0, y1 - y0);
|
|
234
|
+
c.restore();
|
|
192
235
|
};
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
// plan wall point): each on its own row below it, so it can be seen, selected and deleted
|
|
203
|
-
const extra = extraDims(sk, f);
|
|
204
|
-
for (const x of extra)
|
|
205
|
-
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);
|
|
206
|
-
const rows = extra.reduce((m, x) => Math.max(m, x.row + 1), 0);
|
|
207
|
-
// the wall's length comes from the floor plan: linked (outlined blue) when a dimension locks it there, grey when free
|
|
208
|
-
const planLocked = Object.values(sk.cons).some(cn => cn.type === 'dist' && cn.line === f.lid && cn.driving !== false && cn.layer !== 'print');
|
|
209
|
-
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' });
|
|
210
|
-
// heights: blue once entered (the sill: locked), grey while they're defaults / free to drag
|
|
211
|
-
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' });
|
|
212
|
-
for (const o of ops) {
|
|
213
|
-
const off = 24;
|
|
214
|
-
const op = sk.openings[o.id];
|
|
215
|
-
if (o.sill > 0.01 || o.type === 'window')
|
|
216
|
-
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);
|
|
217
|
-
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 });
|
|
236
|
+
for (const o of elevOpenings(sk, f))
|
|
237
|
+
if (lit('opening', o.id))
|
|
238
|
+
ring(o.u0, o.head, o.u1, o.sill, isSel('opening', o.id));
|
|
239
|
+
for (const id of Object.keys(face.cabs ?? {})) {
|
|
240
|
+
if (!lit('cab', id))
|
|
241
|
+
continue;
|
|
242
|
+
const b = cabBox(face, id);
|
|
243
|
+
if (b)
|
|
244
|
+
ring(b.u0, b.z1, b.u1, b.z0, isSel('cab', id));
|
|
218
245
|
}
|
|
219
|
-
return hits;
|
|
220
246
|
}
|
|
221
|
-
/** Rows of dimensions below the floor, pixels: the
|
|
247
|
+
/** Rows of dimensions below the floor, pixels: the strings, then extra rows, then the overall length. */
|
|
222
248
|
const ROW0 = 30, ROW = 34;
|
|
223
|
-
/**
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
for (const
|
|
228
|
-
if (
|
|
229
|
-
|
|
230
|
-
|
|
249
|
+
/** One rule for every chip: blue once someone entered it; grey while it's a standard size (a new cabinet's) or only measured. */
|
|
250
|
+
const faceLook = (c, id) => (ed.highlight.has(id) || ed.ana.bad.has(id) || ed.badFaceCons.has(id) ? 'bad' : c.driving === false ? 'ref' : c.auto ? 'free' : 'drive');
|
|
251
|
+
/** The face dimension across the wall between two points, if there is one. */
|
|
252
|
+
function dimAcross(face, a, b) {
|
|
253
|
+
for (const [id, c] of Object.entries(face.cons))
|
|
254
|
+
if (c.type === 'dist' && c.kind === 'h' && ((c.a === a && c.b === b) || (c.a === b && c.b === a)))
|
|
255
|
+
return id;
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
/** The face dimension of a cabinet's height above the floor, if there is one. */
|
|
259
|
+
export function cabLiftDim(face, id) {
|
|
260
|
+
const cb = face.cabs[id];
|
|
261
|
+
for (const [cid, k] of Object.entries(face.cons)) {
|
|
262
|
+
const ends = [k.a, k.b];
|
|
263
|
+
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))
|
|
264
|
+
return cid;
|
|
231
265
|
}
|
|
232
|
-
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* The strings along the floor, corner to corner, left to right: the doors and windows and the cabinets that fit
|
|
270
|
+
* between them in the first; cabinets that overlap those (hung above a counter, say) in the next ones.
|
|
271
|
+
*/
|
|
272
|
+
function strings(sk, f, face) {
|
|
273
|
+
const ops = elevOpenings(sk, f).map(o => ({ u0: o.u0, u1: o.u1, op: o }));
|
|
274
|
+
const cabs = Object.keys(face.cabs ?? {}).flatMap(id => {
|
|
275
|
+
const b = cabBox(face, id);
|
|
276
|
+
if (!b)
|
|
277
|
+
return [];
|
|
278
|
+
const [l, r] = cabSides(face, id);
|
|
279
|
+
return [{ u0: b.u0, u1: b.u1, cab: { id, l, r } }];
|
|
280
|
+
});
|
|
281
|
+
const rows = [];
|
|
282
|
+
const inside = (it) => it.u0 >= f.face[0] - 0.01 && it.u1 <= f.face[1] + 0.01;
|
|
283
|
+
const fits = (row, it) => inside(it) && !row.some(x => it.u0 < x.u1 - 0.01 && it.u1 > x.u0 + 0.01);
|
|
284
|
+
// doors and windows first, then cabinets: each in the first string it fits, overlapping ones in strings of their own
|
|
285
|
+
for (const it of [...ops.sort((p, q) => p.u0 - q.u0), ...cabs.sort((p, q) => p.u0 - q.u0)]) {
|
|
286
|
+
if (!inside(it))
|
|
287
|
+
continue;
|
|
288
|
+
const row = rows.find(r => fits(r, it));
|
|
289
|
+
if (row)
|
|
290
|
+
row.push(it);
|
|
291
|
+
else
|
|
292
|
+
rows.push([it]);
|
|
293
|
+
}
|
|
294
|
+
return rows.map(r => r.sort((p, q) => p.u0 - q.u0));
|
|
295
|
+
}
|
|
296
|
+
/** One string's dimensions: each gap between corners, doors, windows and cabinets, and each one's width. */
|
|
297
|
+
function stringSegs(sk, f, face, row) {
|
|
298
|
+
const other = f.leftEnd === 'a' ? 'b' : 'a';
|
|
299
|
+
const nodes = [{ u: f.face[0], pt: cornerAnchor(f.leftEnd), end: f.leftEnd }];
|
|
300
|
+
for (const it of row) {
|
|
301
|
+
if (it.op)
|
|
302
|
+
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 });
|
|
303
|
+
else
|
|
304
|
+
nodes.push({ u: it.u0, pt: it.cab.l, it }, { u: it.u1, pt: it.cab.r, it });
|
|
305
|
+
}
|
|
306
|
+
nodes.push({ u: f.face[1], pt: cornerAnchor(other), end: other });
|
|
233
307
|
if (nodes.length <= 2)
|
|
234
|
-
return
|
|
235
|
-
const
|
|
308
|
+
return [];
|
|
309
|
+
const faceRef = (end) => ({ face: end, line: f.lid, side: f.side });
|
|
236
310
|
const segs = [];
|
|
237
311
|
for (let i = 1; i < nodes.length; i++) {
|
|
238
312
|
const n1 = nodes[i - 1], n2 = nodes[i];
|
|
239
|
-
if (n1.
|
|
240
|
-
|
|
313
|
+
if (n1.it && n1.it === n2.it) {
|
|
314
|
+
// a door's or window's width (its own), or a cabinet's (a face dimension)
|
|
315
|
+
if (n1.it.op)
|
|
316
|
+
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 });
|
|
317
|
+
else {
|
|
318
|
+
const id = face.cabs[n1.it.cab.id].w;
|
|
319
|
+
segs.push(id && face.cons[id]
|
|
320
|
+
? { u0: n1.u, u1: n2.u, act: { k: 'fdim', id }, look: faceLook(face.cons[id], id), width: true }
|
|
321
|
+
: { u0: n1.u, u1: n2.u, act: { k: 'gap', a: n1.pt, b: n2.pt, kind: 'h', title: 'Cabinet width' }, look: 'free', width: true });
|
|
322
|
+
}
|
|
241
323
|
continue;
|
|
242
324
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
act
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
325
|
+
if (!n1.it?.cab && !n2.it?.cab) {
|
|
326
|
+
if (!n1.it && !n2.it)
|
|
327
|
+
continue;
|
|
328
|
+
// between corners, doors and windows: the plan's door/window dimensions
|
|
329
|
+
let act;
|
|
330
|
+
if (!n1.it)
|
|
331
|
+
act = { k: 'pos', op: n2.it.op.id, edge: n2.edge, ref: faceRef(n1.end), title: 'Distance from the corner' };
|
|
332
|
+
else if (!n2.it)
|
|
333
|
+
act = { k: 'pos', op: n1.it.op.id, edge: n1.edge, ref: faceRef(n2.end), title: 'Distance from the corner' };
|
|
334
|
+
else
|
|
335
|
+
act = { k: 'pos', ...pickBetween(sk, n1.it.op.id, n1.edge, n2.it.op.id, n2.edge), title: 'Space between' };
|
|
336
|
+
const d = (sk.openings[act.op].dims || []).find(x => x.edge === act.edge && sameRef(x.ref, act.ref));
|
|
337
|
+
segs.push({ u0: n1.u, u1: n2.u, act, look: !d ? 'free' : ed.badOpeningDims.has(d.id) ? 'bad' : d.driving ? 'drive' : 'ref' });
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
// to or from a cabinet: a face dimension once entered
|
|
341
|
+
const id = dimAcross(face, n1.pt, n2.pt);
|
|
342
|
+
const title = n1.end || n2.end ? 'Distance from the corner' : 'Space between';
|
|
343
|
+
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' });
|
|
344
|
+
}
|
|
345
|
+
return segs;
|
|
346
|
+
}
|
|
347
|
+
/** The doors, windows and cabinets a wall-view dimension measures from or to (by the points it runs between). */
|
|
348
|
+
function owners(face, act) {
|
|
349
|
+
const out = [];
|
|
350
|
+
const of = (pid) => {
|
|
351
|
+
if (!pid)
|
|
352
|
+
return;
|
|
353
|
+
const m = /^@o:([^:]+):/.exec(pid);
|
|
354
|
+
if (m) {
|
|
355
|
+
out.push(m[1]);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
for (const [id, cb] of Object.entries(face.cabs ?? {}))
|
|
359
|
+
if ([cb.bl, cb.br, cb.tr, cb.tl].includes(pid))
|
|
360
|
+
out.push(id);
|
|
361
|
+
};
|
|
362
|
+
if ('op' in act)
|
|
363
|
+
out.push(act.op);
|
|
364
|
+
if (act.k === 'pos' && 'op' in act.ref)
|
|
365
|
+
out.push(act.ref.op);
|
|
366
|
+
if (act.k === 'depth')
|
|
367
|
+
out.push(act.cab);
|
|
368
|
+
if (act.k === 'gap') {
|
|
369
|
+
of(act.a);
|
|
370
|
+
of(act.b);
|
|
371
|
+
}
|
|
372
|
+
if (act.k === 'fdim') {
|
|
373
|
+
const c = face.cons[act.id];
|
|
374
|
+
of(c?.a);
|
|
375
|
+
of(c?.b);
|
|
376
|
+
}
|
|
377
|
+
// the wall's own length and height: the wall's
|
|
378
|
+
if (act.k === 'len' || act.k === 'wallH')
|
|
379
|
+
out.push(WALL);
|
|
380
|
+
return out;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* The doors, windows and cabinets whose reference dimensions show (as the plan shows a door's or window's distances
|
|
384
|
+
* only while it's selected): the selected and hovered ones, and the one a selected dimension belongs to.
|
|
385
|
+
*/
|
|
386
|
+
/** The wall itself, among what a wall view's dimensions belong to: live while one of its edges or corners is picked. */
|
|
387
|
+
const WALL = '@wall';
|
|
388
|
+
/** 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). */
|
|
389
|
+
const wallPart = (id) => id.startsWith('@') && !id.startsWith('@o:') && !id.startsWith('@k:');
|
|
390
|
+
function liveObjects(face) {
|
|
391
|
+
const live = new Set();
|
|
392
|
+
const add = (type, id) => {
|
|
393
|
+
if (type === 'opening' || type === 'cab')
|
|
394
|
+
live.add(id);
|
|
395
|
+
else if ((type === 'line' || type === 'point') && wallPart(id))
|
|
396
|
+
live.add(WALL);
|
|
397
|
+
else if (type === 'elev') {
|
|
398
|
+
try {
|
|
399
|
+
for (const o of owners(face, JSON.parse(id)))
|
|
400
|
+
live.add(o);
|
|
401
|
+
}
|
|
402
|
+
catch { /* not a chip */ }
|
|
403
|
+
}
|
|
404
|
+
else if (type === 'dim' && face.cons[id])
|
|
405
|
+
for (const o of owners(face, { k: 'fdim', id }))
|
|
406
|
+
live.add(o);
|
|
407
|
+
};
|
|
408
|
+
for (const s of ed.sel)
|
|
409
|
+
add(s.type, s.id);
|
|
410
|
+
if (ed.hover)
|
|
411
|
+
add(ed.hover.type, ed.hover.id);
|
|
412
|
+
return live;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* The wall view's dimensions, over the face: the strings along the floor, door/window dimensions that aren't a step of
|
|
416
|
+
* one, face dimensions entered from a chip that no longer are, the wall's length (set in the plan) and height, and
|
|
417
|
+
* beside each door, window and cabinet its height off the floor and its own height. `on`: whether a chip is selected.
|
|
418
|
+
* As in the plan, what's been entered (blue) always shows; the rest (grey: just measured, a standard size or a
|
|
419
|
+
* reference) only while what it belongs to — a door, window or cabinet, or the wall itself (an edge or corner of it) —
|
|
420
|
+
* is selected or hovered. Returns the chips, to tap.
|
|
421
|
+
*/
|
|
422
|
+
export function drawWallChips(c, T, sk, f, face, on) {
|
|
423
|
+
const hits = [], shown = new Set(), live = liveObjects(face);
|
|
424
|
+
/** 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 */
|
|
425
|
+
const entered = (act, look) => (look === 'drive' || look === 'bad' || look === 'linked') && !(act.k === 'fdim' && face.cons[act.id]?.auto);
|
|
426
|
+
/** 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 */
|
|
427
|
+
const ownSize = (act) => act.k === 'w' || act.k === 'h' || act.k === 'len' || act.k === 'wallH'
|
|
428
|
+
|| (act.k === 'gap' && (act.title === 'Cabinet width' || act.title === 'Cabinet height'))
|
|
429
|
+
|| (act.k === 'fdim' && Object.values(face.cabs ?? {}).some(cb => cb.w === act.id || cb.h === act.id));
|
|
430
|
+
// a dimension shows once it's entered, and otherwise only while what it belongs to is selected or hovered
|
|
431
|
+
const tag = (p, q, nx, ny, label, look, act, force = false) => {
|
|
432
|
+
if (!entered(act, look) && !on(act) && !owners(face, act).some(o => live.has(o)))
|
|
433
|
+
return;
|
|
434
|
+
// as in the plan, a number that holds nothing is dashed and in parentheses: a reference, or one only measured —
|
|
435
|
+
// a distance, or the wall's length when the plan doesn't lock it (a standard size, which holds, stays solid)
|
|
436
|
+
const dashed = look === 'ref' || (look === 'free' && !(ownSize(act) && act.k !== 'len'));
|
|
437
|
+
const box = dim(c, p, q, nx, ny, label, look, force, on(act), dashed);
|
|
438
|
+
if (box)
|
|
439
|
+
hits.push({ ...box, act, look });
|
|
440
|
+
if (act.k === 'fdim')
|
|
441
|
+
shown.add(act.id);
|
|
442
|
+
};
|
|
443
|
+
/** the chip's value: a driving face dimension's own, otherwise what's measured */
|
|
444
|
+
const val = (act, measured) => (act.k === 'fdim' && face.cons[act.id]?.driving !== false ? face.cons[act.id].value : measured);
|
|
445
|
+
const rows = strings(sk, f, face);
|
|
446
|
+
rows.forEach((row, r) => {
|
|
447
|
+
for (const g of stringSegs(sk, f, face, row))
|
|
448
|
+
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);
|
|
449
|
+
});
|
|
450
|
+
let next = Math.max(rows.length, 1);
|
|
451
|
+
// door/window dimensions that aren't a step of a string, packed into rows of their own
|
|
452
|
+
const extra = extraDims(sk, f, rows);
|
|
453
|
+
for (const x of extra)
|
|
454
|
+
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);
|
|
455
|
+
next += extra.reduce((m, x) => Math.max(m, x.row + 1), 0);
|
|
456
|
+
// heights beside doors, windows and cabinets
|
|
457
|
+
for (const o of elevOpenings(sk, f)) {
|
|
458
|
+
const op = sk.openings[o.id];
|
|
459
|
+
if (o.sill > 0.01 || o.type === 'window')
|
|
460
|
+
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);
|
|
461
|
+
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 });
|
|
253
462
|
}
|
|
254
|
-
|
|
463
|
+
// beside a cabinet: on its right like a window's, unless a cabinet or opening is right there (a run of cabinets),
|
|
464
|
+
// then on its left, else just inside it
|
|
465
|
+
const boxes = [...Object.keys(face.cabs ?? {}).map(id => ({ id, b: cabBox(face, id) })).filter(x => x.b).map(x => ({ id: x.id, ...x.b })),
|
|
466
|
+
...elevOpenings(sk, f).map(o => ({ id: o.id, u0: o.u0, u1: o.u1, z0: o.sill, z1: o.head }))];
|
|
467
|
+
const room = 64 / T.s;
|
|
468
|
+
const blocked = (id, b, lo, hi) => 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);
|
|
469
|
+
for (const [id, cb] of Object.entries(face.cabs ?? {})) {
|
|
470
|
+
const b = cabBox(face, id);
|
|
471
|
+
if (!b)
|
|
472
|
+
continue;
|
|
473
|
+
const [l, r] = cabSides(face, id), zid = cabLiftDim(face, id);
|
|
474
|
+
const right = !blocked(id, b, b.u1, b.u1 + room), left = !right && !blocked(id, b, b.u0 - room, b.u0);
|
|
475
|
+
const u = right ? b.u1 : b.u0, off = right ? 24 : left ? -24 : 24, at = right || left ? u : b.u1 - 48 / T.s;
|
|
476
|
+
// its height off the floor: when it's off the floor, set, or selected (a cabinet on the floor needs no 0")
|
|
477
|
+
if (b.z0 > 0.01 || zid || live.has(id)) {
|
|
478
|
+
const zAct = zid ? { k: 'fdim', id: zid } : { k: 'gap', a: cornerAnchor(f.leftEnd), b: l, kind: 'v', title: 'Cabinet off the floor' };
|
|
479
|
+
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);
|
|
480
|
+
}
|
|
481
|
+
// its depth, on its door near the bottom: a chip like the others (grey until it's entered, and then only while
|
|
482
|
+
// the cabinet is selected or hovered), on the cabinet itself
|
|
483
|
+
if (cb.depthSet || live.has(id) || on({ k: 'depth', cab: id })) {
|
|
484
|
+
const dAct = { k: 'depth', cab: id }, look = cb.depthSet ? 'drive' : 'free', st = LOOK(look);
|
|
485
|
+
const [cx] = S(T, (b.u0 + b.u1) / 2, 0), [, by] = S(T, 0, b.z0), [, ty] = S(T, 0, b.z1);
|
|
486
|
+
if (by - ty > 40) {
|
|
487
|
+
const y = by - Math.min(16, (by - ty) * 0.15), text = 'D ' + fmtLen(cb.depth);
|
|
488
|
+
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 });
|
|
489
|
+
hits.push({ x: cx - w / 2 - 4, y: y - 14, w: w + 8, h: 28, act: dAct, look });
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const top = r === cb.br ? cb.tr : cb.tl;
|
|
493
|
+
const hAct = cb.h && face.cons[cb.h] ? { k: 'fdim', id: cb.h } : { k: 'gap', a: r, b: top, kind: 'v', title: 'Cabinet height' };
|
|
494
|
+
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);
|
|
495
|
+
}
|
|
496
|
+
// 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
|
|
497
|
+
const lostRows = [];
|
|
498
|
+
for (const [id, k] of Object.entries(face.cons)) {
|
|
499
|
+
if (!k.elev || k.anchor || k.type !== 'dist' || shown.has(id) || !k.a || !k.b || !face.points[k.a] || !face.points[k.b])
|
|
500
|
+
continue;
|
|
501
|
+
if (Object.values(face.cabs ?? {}).some(cb => cb.w === id || cb.h === id))
|
|
502
|
+
continue;
|
|
503
|
+
const A = face.points[k.a], B = face.points[k.b], act = { k: 'fdim', id }, text = fmtLen(k.driving === false ? measure(face, k) : k.value);
|
|
504
|
+
if (k.kind === 'v') {
|
|
505
|
+
tag(S(T, B.x, A.y), S(T, B.x, B.y), -24, 0, text, faceLook(k, id), act, true);
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
const u0 = Math.min(A.x, B.x), u1 = Math.max(A.x, B.x);
|
|
509
|
+
let row = 0;
|
|
510
|
+
while ((lostRows[row] || []).some(([x, y]) => u0 < y + 6 && u1 > x - 6))
|
|
511
|
+
row++;
|
|
512
|
+
(lostRows[row] = lostRows[row] || []).push([u0, u1]);
|
|
513
|
+
tag(S(T, A.x, 0), S(T, B.x, 0), 0, ROW0 + ROW * (next + row), text, faceLook(k, id), act);
|
|
514
|
+
}
|
|
515
|
+
next += lostRows.length;
|
|
516
|
+
// the wall's length comes from the floor plan: linked (outlined blue) when a dimension locks it there, grey when free
|
|
517
|
+
const planLocked = Object.values(sk.cons).some(cn => cn.type === 'dist' && cn.line === f.lid && cn.driving !== false && cn.layer !== 'print');
|
|
518
|
+
const anyRow = rows.some(r => r.length) || next > 1;
|
|
519
|
+
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' });
|
|
520
|
+
// the wall's height: blue once entered, grey while it's the default
|
|
521
|
+
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' });
|
|
522
|
+
return hits;
|
|
255
523
|
}
|
|
256
|
-
/** Saved door/window dimensions on this wall that the
|
|
257
|
-
export function extraDims(sk, f) {
|
|
258
|
-
const shown = new Set(
|
|
524
|
+
/** Saved door/window dimensions on this wall that the strings (`rows`) don't show, packed into rows. */
|
|
525
|
+
export function extraDims(sk, f, strs) {
|
|
526
|
+
const shown = new Set();
|
|
527
|
+
for (const row of strs ?? strings(sk, f, emptyFace()))
|
|
528
|
+
for (const g of stringSegs(sk, f, emptyFace(), row.filter(it => it.op))) {
|
|
529
|
+
if (g.act.k !== 'pos')
|
|
530
|
+
continue;
|
|
531
|
+
const a = g.act, d = (sk.openings[a.op].dims || []).find(x => x.edge === a.edge && sameRef(x.ref, a.ref));
|
|
532
|
+
if (d)
|
|
533
|
+
shown.add(d.id);
|
|
534
|
+
}
|
|
259
535
|
const out = [], rows = [];
|
|
260
536
|
for (const o of elevOpenings(sk, f)) {
|
|
261
537
|
const op = sk.openings[o.id], g = openingSpan(sk, op);
|
|
@@ -268,19 +544,17 @@ export function extraDims(sk, f) {
|
|
|
268
544
|
if (r == null)
|
|
269
545
|
continue;
|
|
270
546
|
const a = toU(f, r), b = toU(f, edgeS(op, g.w, d.edge)), u0 = Math.min(a, b), u1 = Math.max(a, b);
|
|
271
|
-
let
|
|
272
|
-
while ((rows[
|
|
273
|
-
|
|
274
|
-
(rows[
|
|
547
|
+
let k = 0;
|
|
548
|
+
while ((rows[k] || []).some(([x, y]) => u0 < y + 6 && u1 > x - 6))
|
|
549
|
+
k++;
|
|
550
|
+
(rows[k] = rows[k] || []).push([u0, u1]);
|
|
275
551
|
const title = 'op' in d.ref ? 'Space between' : 'face' in d.ref ? 'Distance from the corner' : 'Distance from the wall end';
|
|
276
|
-
out.push({ u0, u1, d, row, act: { k: 'pos', op: o.id, edge: d.edge, ref: d.ref, title } });
|
|
552
|
+
out.push({ u0, u1, d, row: k, act: { k: 'pos', op: o.id, edge: d.edge, ref: d.ref, title } });
|
|
277
553
|
}
|
|
278
554
|
}
|
|
279
555
|
return out;
|
|
280
556
|
}
|
|
281
|
-
/**
|
|
282
|
-
export const extraRowsPx = (sk, f) => ROW * extraDims(sk, f).reduce((m, x) => Math.max(m, x.row + 1), 0);
|
|
283
|
-
/** A chip for previews (line length while drawing). */
|
|
557
|
+
/** A chip for previews (a ghost's size, a height while placing). */
|
|
284
558
|
export const previewChip = (c, text, x, y) => chip(c, text, x, y, { bg: TH.chip, fg: TH.ink, font: '600 11.5px', h: 20, pad: 6, border: [TH.purple, 1.5] });
|
|
285
559
|
/** A small plan of the page in the corner: the wall in yellow and an arrow showing where you're looking from. */
|
|
286
560
|
export function drawKeyPlan(c, x, y, size, sk, f) {
|