@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
|
@@ -2,7 +2,7 @@ import { isDim, measure, relatedCons } from '../model/constraints';
|
|
|
2
2
|
import { addCons, restorePos, snapshotPos } from '../model/sketch';
|
|
3
3
|
import type { Analysis, Constraint, Sketch, XY } from '../model/types';
|
|
4
4
|
import { buildSys, getX, type System, type Vec } from './equations';
|
|
5
|
-
import { jac, rref, solveDense, sq, type Row } from './linalg';
|
|
5
|
+
import { blocks, compactRows, envAt, envelopeOf, jac, rref, rrefTol, solveDense, solveEnvelope, sq, type Envelope, type Row } from './linalg';
|
|
6
6
|
import type { AddResult, SolveResult, Solver } from './types';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -13,24 +13,32 @@ export function gnSolve(sys: System, X: Vec, W: Float64Array, maxIt = 60): { X:
|
|
|
13
13
|
const m = sys.eqs.length, n = sys.n;
|
|
14
14
|
const evalR = (Y: Vec) => sys.eqs.map(e => e.f(Y));
|
|
15
15
|
let r = evalR(X), nr = sq(r);
|
|
16
|
-
|
|
16
|
+
let env: Envelope | null = null;
|
|
17
|
+
// (solved to 1e-10 — far finer than the 1e-6 a solve must reach: past that, steps only chase rounding error)
|
|
18
|
+
for (let it = 0; it < maxIt && nr > 1e-20; it++) {
|
|
17
19
|
const J = jac(sys, X);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
// A = J W⁻¹ Jᵀ, summed variable by variable: only the equations that share a variable meet in A, and each variable
|
|
21
|
+
// is in a handful of them — so it costs about as much as J has entries, not every pair of equations (which, on a
|
|
22
|
+
// plan of a few dozen rooms, made each step of a drag take longer than a frame)
|
|
23
|
+
const cols: { row: number[]; val: number[] }[] = Array.from({ length: n }, () => ({ row: [], val: [] }));
|
|
24
|
+
for (let i = 0; i < m; i++) for (const [v, a] of J[i]) { cols[v].row.push(i); cols[v].val.push(a); }
|
|
25
|
+
// kept in the envelope of its band (see envelopeOf) and factored there: solving A densely took time growing with
|
|
26
|
+
// the cube of the equations — most of each step of a drag on a big plan. Dense if the band's factor breaks down.
|
|
27
|
+
env ??= envelopeOf(sys.eqs.map(e => e.deps), n);
|
|
28
|
+
const E = new Float64Array(env.size), at = (i: number, k: number) => (i >= k ? envAt(env!, i, k) : envAt(env!, k, i));
|
|
29
|
+
for (let v = 0; v < n; v++) {
|
|
30
|
+
const { row, val } = cols[v], w = W[v];
|
|
31
|
+
for (let p = 0; p < row.length; p++) {
|
|
32
|
+
const i = env.pos[row[p]], ai = val[p] / w;
|
|
33
|
+
E[at(i, i)] += ai * val[p];
|
|
34
|
+
for (let q = 0; q < p; q++) E[at(i, env.pos[row[q]])] += ai * val[q];
|
|
27
35
|
}
|
|
28
36
|
}
|
|
29
37
|
let md = 0;
|
|
30
|
-
for (let i = 0; i < m; i++) md = Math.max(md,
|
|
38
|
+
for (let i = 0; i < m; i++) md = Math.max(md, E[envAt(env, i, i)]);
|
|
31
39
|
const mu = 1e-8 * (md || 1) + 1e-14;
|
|
32
|
-
for (let i = 0; i < m; i++)
|
|
33
|
-
const y = solveDense(
|
|
40
|
+
for (let i = 0; i < m; i++) E[envAt(env, i, i)] += mu;
|
|
41
|
+
const y = solveEnvelope(env, E, r) ?? solveDense(denseNormal(J, W, m, n, mu), Float64Array.from(r), m);
|
|
34
42
|
if (!y) break;
|
|
35
43
|
const dx = new Float64Array(n);
|
|
36
44
|
for (let i = 0; i < m; i++) {
|
|
@@ -43,7 +51,7 @@ export function gnSolve(sys: System, X: Vec, W: Float64Array, maxIt = 60): { X:
|
|
|
43
51
|
const Xn = new Float64Array(n);
|
|
44
52
|
for (let j = 0; j < n; j++) Xn[j] = X[j] + step * dx[j];
|
|
45
53
|
const rn = evalR(Xn), nn = sq(rn);
|
|
46
|
-
if (isFinite(nn) && (nn < nr || nn < 1e-
|
|
54
|
+
if (isFinite(nn) && (nn < nr || nn < 1e-20)) { X = Xn; r = rn; nr = nn; ok = true; break; }
|
|
47
55
|
step *= 0.5;
|
|
48
56
|
}
|
|
49
57
|
if (!ok) break;
|
|
@@ -52,6 +60,23 @@ export function gnSolve(sys: System, X: Vec, W: Float64Array, maxIt = 60): { X:
|
|
|
52
60
|
return { X, ok: maxr < 1e-6, res: maxr, r };
|
|
53
61
|
}
|
|
54
62
|
|
|
63
|
+
/** J W⁻¹ Jᵀ + μI as a dense m × m matrix (for solveDense, when the banded factor breaks down). */
|
|
64
|
+
function denseNormal(J: Row[], W: Float64Array, m: number, n: number, mu: number): Float64Array {
|
|
65
|
+
const cols: { row: number[]; val: number[] }[] = Array.from({ length: n }, () => ({ row: [], val: [] }));
|
|
66
|
+
for (let i = 0; i < m; i++) for (const [v, a] of J[i]) { cols[v].row.push(i); cols[v].val.push(a); }
|
|
67
|
+
const A = new Float64Array(m * m);
|
|
68
|
+
for (let v = 0; v < n; v++) {
|
|
69
|
+
const { row, val } = cols[v], w = W[v];
|
|
70
|
+
for (let p = 0; p < row.length; p++) {
|
|
71
|
+
const i = row[p], ai = val[p] / w;
|
|
72
|
+
A[i * m + i] += ai * val[p];
|
|
73
|
+
for (let q = 0; q < p; q++) { const k = row[q], s = ai * val[q]; A[i * m + k] += s; A[k * m + i] += s; }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (let i = 0; i < m; i++) A[i * m + i] += mu;
|
|
77
|
+
return A;
|
|
78
|
+
}
|
|
79
|
+
|
|
55
80
|
export function writeX(sk: Sketch, sys: System, X: Vec): void {
|
|
56
81
|
for (const [k, i] of sys.idx) {
|
|
57
82
|
if (k.startsWith('r:')) { const c = sk.circles[k.slice(2)]; if (c) c.r = X[i]; }
|
|
@@ -87,11 +112,51 @@ export function culpritsFor(sys0: System, newRows: Row[]): string[] {
|
|
|
87
112
|
return [...out];
|
|
88
113
|
}
|
|
89
114
|
|
|
115
|
+
/** The drawing's equations with the new constraint's added ('__new'), and the new ones' Jacobian rows and residuals. */
|
|
90
116
|
function newRowsFor(sk: Sketch, c: Constraint) {
|
|
91
|
-
const sys1 = buildSys(sk, { extra: c }), X = getX(sys1)
|
|
92
|
-
const rows
|
|
93
|
-
sys1.eqs.
|
|
94
|
-
return { sys1,
|
|
117
|
+
const sys1 = buildSys(sk, { extra: c }), X = getX(sys1);
|
|
118
|
+
const rows = jac({ ...sys1, eqs: sys1.eqs.filter(e => e.cid === '__new') }, X);
|
|
119
|
+
const res = sys1.eqs.filter(e => e.cid === '__new').map(e => Math.abs(e.f(X)));
|
|
120
|
+
return { sys1, X, rows, res };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* How much a new constraint's equations (cid '__new' in `sys1`, rows of `J1`) add to the rank of the others: 0 when
|
|
125
|
+
* they follow from them (a redundant or a conflicting constraint). One that holds a variable no other equation touches
|
|
126
|
+
* adds 1 outright — as most of a new shape's constraints do (a rectangle's corners are new points). Otherwise only the
|
|
127
|
+
* block of the system it joins is reduced: the rest has the same rank with it or without it. Both used to reduce the
|
|
128
|
+
* whole drawing, twice for each constraint — most of the time it took to finish a shape on a big plan.
|
|
129
|
+
*/
|
|
130
|
+
export function rankGain(sys1: System, X: Vec, newRows?: Row[]): number {
|
|
131
|
+
const fresh: number[] = [], olds: number[] = [];
|
|
132
|
+
sys1.eqs.forEach((e, i) => (e.cid === '__new' ? fresh : olds).push(i));
|
|
133
|
+
if (fresh.length === 1) {
|
|
134
|
+
const row = newRows?.[0] ?? jac({ ...sys1, eqs: [sys1.eqs[fresh[0]]] }, X)[0];
|
|
135
|
+
// its variables it clearly moves (not a derivative lost in rounding, which the full reduction judges against the
|
|
136
|
+
// rest): one that no other equation moves at all here — a new point's, or a y only level walls hold — adds 1.
|
|
137
|
+
// (Only the equations on those variables are differentiated; a level wall depends on its points' x's but doesn't
|
|
138
|
+
// move with them.)
|
|
139
|
+
const strong = [...row].filter(([, v]) => Math.abs(v) >= 1e-3).map(([k]) => k);
|
|
140
|
+
if (strong.length) {
|
|
141
|
+
const want = new Set(strong), touching = olds.filter(i => sys1.eqs[i].deps.some(d => want.has(d)));
|
|
142
|
+
const rows = jac({ ...sys1, eqs: touching.map(i => sys1.eqs[i]) }, X);
|
|
143
|
+
if (strong.some(k => rows.every(r => !r.has(k)))) return 1;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return rankGainOf(sys1, jac(sys1, X), fresh, olds);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function rankGainOf(sys1: System, J1: Row[], fresh: number[], olds: number[]): number {
|
|
150
|
+
const tol0 = rrefTol(olds.map(i => J1[i])), tol1 = rrefTol(J1);
|
|
151
|
+
const isNew = new Set(fresh);
|
|
152
|
+
let r0 = 0, r1 = 0;
|
|
153
|
+
for (const b of blocks(J1, sys1.n)) {
|
|
154
|
+
if (!b.rows.some(i => isNew.has(i))) continue;
|
|
155
|
+
const before = b.rows.filter(i => !isNew.has(i));
|
|
156
|
+
r0 += rref(compactRows(before.map(i => J1[i]), b.cols), b.cols.length, false, tol0).rank;
|
|
157
|
+
r1 += rref(compactRows(b.rows.map(i => J1[i]), b.cols), b.cols.length, false, tol1).rank;
|
|
158
|
+
}
|
|
159
|
+
return r1 - r0;
|
|
95
160
|
}
|
|
96
161
|
|
|
97
162
|
export class GaussNewtonSolver implements Solver {
|
|
@@ -118,8 +183,34 @@ export class GaussNewtonSolver implements Solver {
|
|
|
118
183
|
const snap = snapshotPos(sk);
|
|
119
184
|
ids.forEach((id, i) => { sk.points[id].x = targets[i].x; sk.points[id].y = targets[i].y; });
|
|
120
185
|
const s = this.solve(sk, ids);
|
|
121
|
-
if (!s.ok) restorePos(sk, snap);
|
|
122
|
-
|
|
186
|
+
if (!s.ok) { restorePos(sk, snap); return false; }
|
|
187
|
+
this.settle(sk, ids, snap);
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
solveHolding(sk: Sketch, held: string[]): SolveResult {
|
|
192
|
+
const snap = snapshotPos(sk), ids = held.filter(id => sk.points[id]);
|
|
193
|
+
const s = this.solve(sk, ids);
|
|
194
|
+
if (s.ok && ids.length) this.settle(sk, ids, snap);
|
|
195
|
+
return s;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* After a solve that pulled `ids` (held heavy) into line: they stay where they got to, everything else goes back to
|
|
200
|
+
* where it was (`before`) and is solved once more — so the rest moves only as far as the constraints need. Pulling
|
|
201
|
+
* the held points into line, the solve can use any free motion to ease the strain (a cabinet's sideways slack; a
|
|
202
|
+
* cabinet free to slide up its wall, edge-to-edge collinear with another), and that would otherwise stay behind —
|
|
203
|
+
* a little more at each step of a drag: things tied to what's dragged gliding when they shouldn't move at all.
|
|
204
|
+
*/
|
|
205
|
+
private settle(sk: Sketch, ids: string[], before: ReturnType<typeof snapshotPos>): void {
|
|
206
|
+
const got = ids.map(id => ({ x: sk.points[id].x, y: sk.points[id].y }));
|
|
207
|
+
const solved = snapshotPos(sk);
|
|
208
|
+
restorePos(sk, before);
|
|
209
|
+
const pts = ids.map(id => sk.points[id]), had = pts.map(p => p.fixed);
|
|
210
|
+
pts.forEach((p, i) => { p.x = got[i].x; p.y = got[i].y; p.fixed = true; });
|
|
211
|
+
const settled = this.solve(sk);
|
|
212
|
+
pts.forEach((p, i) => { if (had[i]) p.fixed = true; else delete p.fixed; });
|
|
213
|
+
if (!settled.ok) restorePos(sk, solved);
|
|
123
214
|
}
|
|
124
215
|
|
|
125
216
|
analyzeDOF(sk: Sketch): Analysis {
|
|
@@ -128,11 +219,18 @@ export class GaussNewtonSolver implements Solver {
|
|
|
128
219
|
for (const id in sk.points) if (sk.points[id].fixed) det.add(id);
|
|
129
220
|
let dof = sys.n, ok = true;
|
|
130
221
|
if (sys.eqs.length) {
|
|
131
|
-
|
|
132
|
-
|
|
222
|
+
// reduced block by block (see blocks): the same rank and determined variables as the whole at once
|
|
223
|
+
const J = jac(sys, X), tol = rrefTol(J), detV = new Uint8Array(sys.n);
|
|
224
|
+
let rank = 0;
|
|
225
|
+
for (const b of blocks(J, sys.n)) {
|
|
226
|
+
const R = rref(compactRows(b.rows.map(i => J[i]), b.cols), b.cols.length, true, tol);
|
|
227
|
+
rank += R.rank;
|
|
228
|
+
b.cols.forEach((c, k) => { if (R.det![k]) detV[c] = 1; });
|
|
229
|
+
}
|
|
230
|
+
dof = sys.n - rank;
|
|
133
231
|
for (const [k, i] of sys.idx) {
|
|
134
|
-
if (k.startsWith('r:')) { if (
|
|
135
|
-
else if (
|
|
232
|
+
if (k.startsWith('r:')) { if (detV[i]) detR.add(k.slice(2)); }
|
|
233
|
+
else if (detV[i] && detV[i + 1]) det.add(k);
|
|
136
234
|
}
|
|
137
235
|
sys.eqs.forEach(e => { if (Math.abs(e.f(X)) > 1e-5) { bad.add(e.cid); ok = false; } });
|
|
138
236
|
}
|
|
@@ -140,10 +238,9 @@ export class GaussNewtonSolver implements Solver {
|
|
|
140
238
|
}
|
|
141
239
|
|
|
142
240
|
addConstraint(sk: Sketch, c: Constraint, { explain = false }: { explain?: boolean } = {}): AddResult {
|
|
143
|
-
const sys0 = buildSys(sk), r0 = rref(jac(sys0, getX(sys0)), sys0.n, false).rank;
|
|
144
241
|
const N = newRowsFor(sk, c);
|
|
145
242
|
if (!N.rows.length) return { ok: false };
|
|
146
|
-
const gain =
|
|
243
|
+
const gain = rankGain(N.sys1, N.X, N.rows);
|
|
147
244
|
const resid = Math.max(...N.res);
|
|
148
245
|
if (gain === 0) {
|
|
149
246
|
if (isDim(c)) {
|
|
@@ -155,7 +252,7 @@ export class GaussNewtonSolver implements Solver {
|
|
|
155
252
|
}
|
|
156
253
|
if (resid < 1e-6) return { ok: false, redundant: true };
|
|
157
254
|
let culprits: string[] = [];
|
|
158
|
-
if (explain) { culprits = culpritsFor(
|
|
255
|
+
if (explain) { culprits = culpritsFor(buildSys(sk), N.rows); if (!culprits.length) culprits = relatedCons(sk, c); }
|
|
159
256
|
return { ok: false, conflict: 'dependent', culprits };
|
|
160
257
|
}
|
|
161
258
|
const snap = snapshotPos(sk);
|
package/src/solver/linalg.ts
CHANGED
|
@@ -54,13 +54,20 @@ export const sq = (r: ArrayLike<number>): number => {
|
|
|
54
54
|
return s;
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
/**
|
|
58
|
-
export function
|
|
57
|
+
/** What rref counts as zero for these rows: a billionth of their largest entry (or of 1). */
|
|
58
|
+
export function rrefTol(rows: Row[]): number {
|
|
59
|
+
let maxAbs = 0;
|
|
60
|
+
for (const r of rows) for (const v of r.values()) { const a = Math.abs(v); if (a > maxAbs) maxAbs = a; }
|
|
61
|
+
return 1e-9 * Math.max(1, maxAbs);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Reduced row echelon form: rank, pivot columns and (optionally) which variables are fully determined. `tol`: what
|
|
66
|
+
* counts as zero (by default rrefTol of these rows — pass the whole system's when reducing it block by block).
|
|
67
|
+
*/
|
|
68
|
+
export function rref(rows: Row[], n: number, needDet: boolean, tol = rrefTol(rows)): { rank: number; piv: number[]; det: Uint8Array | null } {
|
|
59
69
|
const m = rows.length;
|
|
60
70
|
const M = rows.map(r => { const a = new Float64Array(n); r.forEach((v, k) => (a[k] = v)); return a; });
|
|
61
|
-
let maxAbs = 0;
|
|
62
|
-
for (const a of M) for (let k = 0; k < n; k++) { const v = Math.abs(a[k]); if (v > maxAbs) maxAbs = v; }
|
|
63
|
-
const tol = 1e-9 * Math.max(1, maxAbs);
|
|
64
71
|
const piv: number[] = [];
|
|
65
72
|
let r = 0;
|
|
66
73
|
for (let col = 0; col < n && r < m; col++) {
|
|
@@ -94,3 +101,132 @@ export function rref(rows: Row[], n: number, needDet: boolean): { rank: number;
|
|
|
94
101
|
}
|
|
95
102
|
return { rank: r, piv, det };
|
|
96
103
|
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The system's independent blocks: groups of rows that share no variable with any other group (directly or through
|
|
107
|
+
* other rows), each with its variables (ascending). A plan's level and plumb walls set its x's apart from its y's, and
|
|
108
|
+
* separate drawings on a page are separate blocks: a block's rank and reduced form don't depend on the rest, so the
|
|
109
|
+
* system is reduced block by block, at a fraction of the cost of reducing it whole. Rows with no entries are left out.
|
|
110
|
+
*/
|
|
111
|
+
export function blocks(rows: Row[], n: number): { rows: number[]; cols: number[] }[] {
|
|
112
|
+
const parent = new Int32Array(n);
|
|
113
|
+
for (let i = 0; i < n; i++) parent[i] = i;
|
|
114
|
+
const find = (x: number): number => { while (parent[x] !== x) { parent[x] = parent[parent[x]]; x = parent[x]; } return x; };
|
|
115
|
+
for (const r of rows) {
|
|
116
|
+
let root = -1;
|
|
117
|
+
for (const k of r.keys()) { const f = find(k); if (root < 0) root = f; else if (f !== root) parent[f] = root; }
|
|
118
|
+
}
|
|
119
|
+
const byRoot = new Map<number, { rows: number[]; cols: number[] }>();
|
|
120
|
+
rows.forEach((r, i) => {
|
|
121
|
+
for (const k of r.keys()) {
|
|
122
|
+
const root = find(k);
|
|
123
|
+
let b = byRoot.get(root);
|
|
124
|
+
if (!b) byRoot.set(root, (b = { rows: [], cols: [] }));
|
|
125
|
+
b.rows.push(i);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
for (let c = 0; c < n; c++) { const b = byRoot.get(find(c)); if (b) b.cols.push(c); }
|
|
130
|
+
return [...byRoot.values()];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Rows renumbered onto `cols` (ascending): variable cols[k] becomes k. */
|
|
134
|
+
export function compactRows(rows: Row[], cols: number[]): Row[] {
|
|
135
|
+
const at = new Map<number, number>();
|
|
136
|
+
cols.forEach((c, k) => at.set(c, k));
|
|
137
|
+
return rows.map(r => {
|
|
138
|
+
const o: Row = new Map();
|
|
139
|
+
r.forEach((v, c) => { const k = at.get(c); if (k !== undefined) o.set(k, v); });
|
|
140
|
+
return o;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Where the nonzeros of J W⁻¹ Jᵀ can be — equations that share a variable — with the equations reordered (reverse
|
|
146
|
+
* Cuthill–McKee) so they sit close to the diagonal. Its LDLᵀ factor fills in only within each row's envelope (from the
|
|
147
|
+
* row's first nonzero to the diagonal): a narrow band for a drawing's equations, which mostly tie a point to its
|
|
148
|
+
* neighbours, with equations that share nothing in separate blocks of it. Worked out once a solve, from the variables
|
|
149
|
+
* each equation depends on (the same at every step).
|
|
150
|
+
*/
|
|
151
|
+
export interface Envelope {
|
|
152
|
+
m: number;
|
|
153
|
+
/** pos[equation] = its place in the new order; ord[place] = the equation there */
|
|
154
|
+
pos: Int32Array; ord: Int32Array;
|
|
155
|
+
/** each row's (in the new order) first column, and where its entries (first … diagonal) start in the values */
|
|
156
|
+
first: Int32Array; start: Int32Array;
|
|
157
|
+
size: number;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Where entry (i, k) of the envelope (new order, k ≤ i) is kept in its values. */
|
|
161
|
+
export const envAt = (env: Envelope, i: number, k: number): number => env.start[i] + (k - env.first[i]);
|
|
162
|
+
|
|
163
|
+
export function envelopeOf(deps: number[][], n: number): Envelope {
|
|
164
|
+
const m = deps.length;
|
|
165
|
+
const byVar: number[][] = Array.from({ length: n }, () => []);
|
|
166
|
+
deps.forEach((ds, i) => { for (const v of ds) byVar[v].push(i); });
|
|
167
|
+
const near: Set<number>[] = Array.from({ length: m }, () => new Set<number>());
|
|
168
|
+
for (const eqs of byVar) for (let p = 0; p < eqs.length; p++) for (let q = p + 1; q < eqs.length; q++) { near[eqs[p]].add(eqs[q]); near[eqs[q]].add(eqs[p]); }
|
|
169
|
+
const adj = near.map(s => [...s]), deg = adj.map(a => a.length);
|
|
170
|
+
// Cuthill–McKee: breadth first from a least-connected equation of each block, fewest neighbours first; then reversed
|
|
171
|
+
const order: number[] = [], seen = new Uint8Array(m);
|
|
172
|
+
const starts = Array.from({ length: m }, (_, i) => i).sort((a, b) => deg[a] - deg[b] || a - b);
|
|
173
|
+
for (const s of starts) {
|
|
174
|
+
if (seen[s]) continue;
|
|
175
|
+
seen[s] = 1;
|
|
176
|
+
const q = [s];
|
|
177
|
+
for (let h = 0; h < q.length; h++) {
|
|
178
|
+
const next = adj[q[h]].filter(v => !seen[v]).sort((a, b) => deg[a] - deg[b] || a - b);
|
|
179
|
+
for (const v of next) { seen[v] = 1; q.push(v); }
|
|
180
|
+
}
|
|
181
|
+
for (const e of q) order.push(e);
|
|
182
|
+
}
|
|
183
|
+
order.reverse();
|
|
184
|
+
const ord = Int32Array.from(order), pos = new Int32Array(m);
|
|
185
|
+
ord.forEach((e, i) => { pos[e] = i; });
|
|
186
|
+
const first = new Int32Array(m), start = new Int32Array(m + 1);
|
|
187
|
+
for (let i = 0; i < m; i++) {
|
|
188
|
+
let f = i;
|
|
189
|
+
for (const e of adj[ord[i]]) { const j = pos[e]; if (j < f) f = j; }
|
|
190
|
+
first[i] = f;
|
|
191
|
+
start[i + 1] = start[i] + (i - f + 1);
|
|
192
|
+
}
|
|
193
|
+
return { m, pos, ord, first, start, size: start[m] };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Solve A y = b for a symmetric positive definite A kept in `env`'s envelope (its lower triangle, `val`, row by row in
|
|
198
|
+
* the new order — overwritten by the LDLᵀ factor); b and y in the equations' own order. Null if a pivot isn't positive
|
|
199
|
+
* (A isn't numerically positive definite), for the caller to solve it another way.
|
|
200
|
+
*/
|
|
201
|
+
export function solveEnvelope(env: Envelope, val: Float64Array, b: ArrayLike<number>): Float64Array | null {
|
|
202
|
+
const { m, first, start, ord } = env, D = new Float64Array(m);
|
|
203
|
+
for (let i = 0; i < m; i++) {
|
|
204
|
+
const fi = first[i], si = start[i] - fi;
|
|
205
|
+
for (let j = fi; j < i; j++) {
|
|
206
|
+
const fj = first[j], sj = start[j] - fj;
|
|
207
|
+
let s = val[si + j];
|
|
208
|
+
for (let k = fi > fj ? fi : fj; k < j; k++) s -= val[si + k] * val[sj + k] * D[k];
|
|
209
|
+
val[si + j] = s / D[j];
|
|
210
|
+
}
|
|
211
|
+
let d = val[si + i];
|
|
212
|
+
for (let k = fi; k < i; k++) { const l = val[si + k]; d -= l * l * D[k]; }
|
|
213
|
+
if (!(d > 0) || !Number.isFinite(d)) return null;
|
|
214
|
+
D[i] = d;
|
|
215
|
+
}
|
|
216
|
+
// L z = b, then D, then Lᵀ y = z (all in the new order)
|
|
217
|
+
const z = new Float64Array(m);
|
|
218
|
+
for (let i = 0; i < m; i++) {
|
|
219
|
+
const fi = first[i], si = start[i] - fi;
|
|
220
|
+
let s = b[ord[i]];
|
|
221
|
+
for (let k = fi; k < i; k++) s -= val[si + k] * z[k];
|
|
222
|
+
z[i] = s;
|
|
223
|
+
}
|
|
224
|
+
for (let i = 0; i < m; i++) z[i] /= D[i];
|
|
225
|
+
for (let i = m - 1; i >= 0; i--) {
|
|
226
|
+
const fi = first[i], si = start[i] - fi, zi = z[i];
|
|
227
|
+
for (let k = fi; k < i; k++) z[k] -= val[si + k] * zi;
|
|
228
|
+
}
|
|
229
|
+
const y = new Float64Array(m);
|
|
230
|
+
for (let i = 0; i < m; i++) y[ord[i]] = z[i];
|
|
231
|
+
return y;
|
|
232
|
+
}
|
package/src/solver/types.ts
CHANGED
|
@@ -35,6 +35,11 @@ export interface Solver {
|
|
|
35
35
|
solve(sk: Sketch, heavy?: string[]): SolveResult;
|
|
36
36
|
/** Move points toward targets and re-solve; reverts and returns false if that can't be satisfied. */
|
|
37
37
|
drag(sk: Sketch, ids: string[], targets: XY[]): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Solve with the points `held` kept where they are as far as the constraints allow (heavy), and the rest moved only
|
|
40
|
+
* as far as the constraints need: nothing free drifts while the held ones are put back in line.
|
|
41
|
+
*/
|
|
42
|
+
solveHolding(sk: Sketch, held: string[]): SolveResult;
|
|
38
43
|
analyzeDOF(sk: Sketch): Analysis;
|
|
39
44
|
/**
|
|
40
45
|
* Add a constraint safely: independent → add + solve; redundant dimension → reference dimension;
|
package/src/styles.css
CHANGED
|
@@ -29,9 +29,18 @@ html{scroll-padding-top:env(safe-area-inset-top,0px)}
|
|
|
29
29
|
*,*::before,*::after{box-sizing:inherit}
|
|
30
30
|
html,body{height:100%;margin:0}
|
|
31
31
|
body{background:var(--cv-bg);font-family:var(--font);color:#fff;overflow:hidden;-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent;user-select:none;-webkit-user-select:none;overscroll-behavior:none}
|
|
32
|
-
|
|
32
|
+
/* clip, not just hidden: nothing may scroll the editor itself — the closed drawer and the sheets waiting off-screen
|
|
33
|
+
made it scrollable, and scrolling one into view (a test's click, the browser revealing a focused field) shifted the
|
|
34
|
+
whole page sideways under the pointer */
|
|
35
|
+
#app{position:relative;height:100%;overflow:hidden;overflow:clip}
|
|
33
36
|
#cv{position:absolute;inset:0;width:100%;height:100%;touch-action:none;display:block;outline:none}
|
|
34
37
|
button{font:inherit;color:inherit;border:0;background:none;padding:0;cursor:pointer;-webkit-tap-highlight-color:transparent}
|
|
38
|
+
/* how the chrome moves (as react/motion.tsx): changes of state move over 150 ms; a press sinks a touch in 70 ms and
|
|
39
|
+
comes back with the same easing; wide rows only dim, switches don't sink */
|
|
40
|
+
button{transition:transform .15s cubic-bezier(.2,0,0,1),opacity .15s cubic-bezier(.2,0,0,1),background-color .15s cubic-bezier(.2,0,0,1),color .15s cubic-bezier(.2,0,0,1)}
|
|
41
|
+
button:active:not([disabled]){transform:scale(.96);opacity:.82;transition-duration:.07s}
|
|
42
|
+
.mlink:active:not([disabled]),.cf-item:active{transform:none;opacity:.7}
|
|
43
|
+
.switch:active:not([disabled]){transform:none;opacity:1}
|
|
35
44
|
button:focus-visible,input:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--yellow);outline-offset:2px}
|
|
36
45
|
svg{display:block;flex-shrink:0}
|
|
37
46
|
input{font:inherit}
|
|
@@ -41,6 +50,8 @@ input{font:inherit}
|
|
|
41
50
|
.top>*{pointer-events:auto}
|
|
42
51
|
.tl,.tr{display:flex;align-items:center;gap:8px;min-width:0}
|
|
43
52
|
.cbtn{width:46px;height:46px;border-radius:50%;background:var(--g3);border:1px solid rgba(255,255,255,.16);display:flex;align-items:center;justify-content:center;flex-shrink:0;box-shadow:0 2px 10px rgba(0,0,0,.16);color:#fff;position:relative;transition:transform .12s}
|
|
53
|
+
/* a round button whose menu is open (•••, Layers): its icon in yellow, as the React editor shows it */
|
|
54
|
+
.cbtn[aria-expanded="true"]{color:var(--yellow)}
|
|
44
55
|
.cbtn:active{transform:scale(.93)}
|
|
45
56
|
.cbtn.bt{background:var(--blue);border-color:transparent}
|
|
46
57
|
.cbtn.off{color:var(--lg1)}
|
|
@@ -70,7 +81,7 @@ input{font:inherit}
|
|
|
70
81
|
.hint{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
|
|
71
82
|
|
|
72
83
|
/* left rail */
|
|
73
|
-
.rail{position:absolute;left:12px;top:112px;background:var(--g2);border-radius:
|
|
84
|
+
.rail{position:absolute;left:12px;top:112px;background:var(--g2);border-radius:26px;padding:6px;display:flex;flex-direction:column;align-items:center;gap:0;z-index:22;max-height:calc(100% - 196px);overflow-y:auto;scrollbar-width:none;box-shadow:0 6px 22px rgba(0,0,0,.26)}
|
|
74
85
|
@media (max-height:760px){.rail button{width:38px;height:38px}.rail .div{margin:3px 0}}
|
|
75
86
|
.rail::-webkit-scrollbar{display:none}
|
|
76
87
|
.rail button{width:40px;height:40px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff;transition:background .12s}
|
|
@@ -85,7 +96,7 @@ input{font:inherit}
|
|
|
85
96
|
|
|
86
97
|
/* bottom contextual bar */
|
|
87
98
|
.bottom{position:absolute;left:50%;bottom:14px;transform:translateX(-50%);display:flex;align-items:center;gap:12px;z-index:24;max-width:calc(100% - 180px)}
|
|
88
|
-
.ctx{display:flex;align-items:center;gap:2px;background:var(--g2);border-radius:
|
|
99
|
+
.ctx{display:flex;align-items:center;gap:2px;background:var(--g2);border-radius:33px;padding:6px;overflow-x:auto;scrollbar-width:none;box-shadow:0 6px 22px rgba(0,0,0,.26);max-width:100%}
|
|
89
100
|
.ctx::-webkit-scrollbar{display:none}
|
|
90
101
|
.cb{min-width:58px;height:54px;border-radius:27px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;color:#fff;font-size:10.5px;font-weight:500;padding:0 8px;flex-shrink:0;white-space:nowrap;transition:background .12s}
|
|
91
102
|
.cb:hover{background:rgba(255,255,255,.08)}
|
|
@@ -95,7 +106,7 @@ input{font:inherit}
|
|
|
95
106
|
.cb.locked{color:var(--red)}
|
|
96
107
|
.ctx .sep{width:1px;height:32px;background:var(--g3);margin:0 4px;flex-shrink:0}
|
|
97
108
|
.ctx .lbl{font-size:12px;color:var(--lg1);padding:0 10px;white-space:nowrap;flex-shrink:0}
|
|
98
|
-
.addbtn{width:48px;height:48px;border-radius:50%;background:var(--yellow);color:var(--dark);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-left:4px}
|
|
109
|
+
.addbtn{width:48px;height:48px;border-radius:50%;background:var(--yellow);color:var(--dark);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-left:4px;margin-right:3px}
|
|
99
110
|
/* the + stays pinned at the end of the bar while the other actions scroll under it (phone) */
|
|
100
111
|
.ctx .addbtn{position:sticky;right:0;z-index:1;box-shadow:0 0 0 6px var(--g2),-16px 0 10px -2px var(--g2)}
|
|
101
112
|
.ctx:has(.addbtn){padding-right:6px}
|
|
@@ -123,12 +134,16 @@ input{font:inherit}
|
|
|
123
134
|
.welcome .ypill{height:38px;font-size:14px}
|
|
124
135
|
|
|
125
136
|
/* drawer */
|
|
126
|
-
.drawer{position:absolute;top:0;right:0;bottom:0;width:350px;max-width:100%;background:var(--g1);z-index:50;transform:translateX(
|
|
137
|
+
.drawer{position:absolute;top:0;right:0;bottom:0;width:350px;max-width:100%;background:var(--g1);border-top:1px solid var(--g2);border-left:1px solid var(--g2);z-index:50;transform:translateX(calc(100% + 40px));transition:transform .22s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;box-shadow:-8px 0 30px rgba(0,0,0,.3);color:#fff}
|
|
127
138
|
.drawer.show{transform:none}
|
|
139
|
+
/* (sliding out it takes no taps: a tap on the drawing as it goes lands on the drawing, as with the backdrop) */
|
|
140
|
+
.drawer:not(.show){pointer-events:none}
|
|
128
141
|
.dh{display:flex;align-items:center;justify-content:space-between;padding:16px 14px 10px 16px;gap:10px}
|
|
129
142
|
.tabs{display:flex;background:var(--dark);border-radius:20px;padding:3px}
|
|
143
|
+
.tabs[hidden]{display:none}
|
|
130
144
|
.tabs button{padding:8px 14px;border-radius:17px;font-size:13px;font-weight:600;color:var(--lg1)}
|
|
131
145
|
.tabs button[aria-selected="true"]{background:#fff;color:var(--dark)}
|
|
146
|
+
.dtitle{font-size:17px;font-weight:700;color:#fff;padding-left:2px}
|
|
132
147
|
.dclose{width:38px;height:38px;border-radius:50%;background:var(--g2);display:flex;align-items:center;justify-content:center}
|
|
133
148
|
.dbody{flex:1;overflow-y:auto;padding:6px 16px 30px;scrollbar-width:thin}
|
|
134
149
|
.field{width:100%;background:transparent;border:1px solid transparent;border-radius:8px;color:#fff;padding:6px 8px;margin-left:-8px;font-weight:600}
|
|
@@ -148,10 +163,14 @@ input{font:inherit}
|
|
|
148
163
|
.sec{font-size:13px;font-weight:600;color:var(--g4);margin:18px 0 8px}
|
|
149
164
|
.row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 0;border-bottom:1px solid var(--g2);font-size:14px}
|
|
150
165
|
.row small{display:block;color:var(--g4);font-size:12px;margin-top:2px}
|
|
151
|
-
|
|
152
|
-
.switch
|
|
153
|
-
.switch
|
|
154
|
-
.switch[aria-checked="true"]
|
|
166
|
+
/* BOLDR's toggle: grey with the knob left when off, green with it right when on (the Figma's 69 × 36, scaled) */
|
|
167
|
+
.switch{width:48px;height:25px;border-radius:100px;background:var(--g4);position:relative;flex-shrink:0;transition:background-color .26s cubic-bezier(.4,0,.2,1)}
|
|
168
|
+
.switch::after{content:"";position:absolute;top:2px;left:2px;width:21px;height:21px;border-radius:11px;background:#fff;box-shadow:0 2px 4px rgba(0,0,0,.2);transition:transform .26s cubic-bezier(.4,0,.2,1),width .26s cubic-bezier(.4,0,.2,1)}
|
|
169
|
+
.switch[aria-checked="true"]{background:#58A942}
|
|
170
|
+
.switch[aria-checked="true"]::after{transform:translateX(23px)}
|
|
171
|
+
/* pressed in, as Apple's: the knob stretches toward the side it's about to go, and rounds up again as it lets go */
|
|
172
|
+
.switch:active:not([disabled])::after{width:27px;transition-duration:.12s}
|
|
173
|
+
.switch[aria-checked="true"]:active:not([disabled])::after{transform:translateX(17px)}
|
|
155
174
|
input[type=range]{width:120px;accent-color:var(--yellow)}
|
|
156
175
|
.roomrow{background:var(--g2);border-radius:10px;padding:8px 10px;margin-bottom:8px}
|
|
157
176
|
.roomrow .field{font-size:14px}
|
|
@@ -166,32 +185,34 @@ input[type=range]{width:120px;accent-color:var(--yellow)}
|
|
|
166
185
|
.sheet.show{transform:none;visibility:visible;transition:transform .28s cubic-bezier(.2,.8,.2,1),visibility 0s}
|
|
167
186
|
.sheet .handle{width:50px;height:5px;border-radius:3px;background:var(--g4);margin:0 auto 14px}
|
|
168
187
|
@media (min-width:900px){
|
|
169
|
-
.sheet{left:auto;right:16px;bottom:16px;width:
|
|
188
|
+
.sheet{left:auto;right:16px;bottom:16px;width:360px;border-radius:20px;border-bottom:1px solid var(--g3);padding-top:16px}
|
|
170
189
|
.sheet .handle{display:none}
|
|
171
190
|
.backdrop{background:transparent}
|
|
172
191
|
}
|
|
173
|
-
.kp
|
|
174
|
-
.kp
|
|
175
|
-
.kp-
|
|
176
|
-
.kp-display
|
|
177
|
-
.kp-
|
|
178
|
-
.kp-
|
|
179
|
-
.kp-
|
|
192
|
+
.kp{--key:#4B6074;--key2:#67819A;--mred:#DF6358}
|
|
193
|
+
.sheet.kp,.sheet.cf{border:1px solid var(--g3);border-bottom:0;padding-left:16px;padding-right:16px;box-shadow:0 5px 15px rgba(0,0,0,.75)}
|
|
194
|
+
@media (min-width:900px){.sheet.kp,.sheet.cf{border-bottom:1px solid var(--g3)}}
|
|
195
|
+
.kp-title{display:flex;align-items:center;gap:12px;font-size:16px;font-weight:600;margin-bottom:12px}
|
|
196
|
+
.kp-title small{flex:1;text-align:right;font-size:12px;color:var(--lg1);font-weight:500}
|
|
197
|
+
.kp-x{width:20px;height:20px;display:flex;align-items:center;justify-content:center;color:#fff}
|
|
198
|
+
.kp-display{background:var(--dark);border-radius:12px;min-height:80px;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:8px 12px;margin-bottom:12px}
|
|
199
|
+
.kp-display input{width:100%;background:transparent;border:0;color:#fff;font-size:34px;font-weight:700;text-align:center;letter-spacing:-.3px;outline:none;caret-color:var(--yellow)}
|
|
200
|
+
.kp-display input::placeholder{color:rgba(255,255,255,.4)}
|
|
201
|
+
.kp-prev{font-size:13px;color:var(--lg1);min-height:16px;font-weight:500;margin-top:2px}
|
|
202
|
+
.kp-prev.bad{color:#FFB4AE}
|
|
180
203
|
.kp-extra{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px}
|
|
181
204
|
.kp-extra:empty{display:none}
|
|
182
|
-
.kp-extra button{height:
|
|
183
|
-
.kp-extra button.on{background:#fff;color:var(--dark)}
|
|
184
|
-
.kp-extra button.danger{color
|
|
185
|
-
.kp-fr{display:grid;grid-template-columns:repeat(8,1fr);gap:5px;margin-bottom:
|
|
186
|
-
.kp-fr button{height:
|
|
205
|
+
.kp-extra button{height:30px;padding:0 10px;border-radius:7px;background:var(--dark);border:.5px solid var(--g3);font-size:13px;font-weight:500;color:#fff;display:flex;align-items:center;gap:6px}
|
|
206
|
+
.kp-extra button.on{background:#fff;border-color:#fff;color:var(--dark)}
|
|
207
|
+
.kp-extra button.danger{color:var(--mred)}
|
|
208
|
+
.kp-fr{display:grid;grid-template-columns:repeat(8,1fr);gap:5px;margin-bottom:7px}
|
|
209
|
+
.kp-fr button{height:32px;border-radius:7px;background:var(--key2);font-size:12px;font-weight:600}
|
|
187
210
|
.keys{display:grid;grid-template-columns:repeat(4,1fr);gap:7px;margin-bottom:12px}
|
|
188
|
-
.key{height:
|
|
189
|
-
.key
|
|
190
|
-
.key.
|
|
191
|
-
.key.
|
|
192
|
-
.
|
|
193
|
-
.kp-actions{display:flex;gap:10px}
|
|
194
|
-
.kp-actions .ypill{flex:1}
|
|
211
|
+
.key{height:50px;border-radius:8px;background:var(--key);font-size:22px;font-weight:700;color:#fff;display:flex;align-items:center;justify-content:center}
|
|
212
|
+
.key.alt{background:var(--key2)}
|
|
213
|
+
.key.wide{grid-column:span 3}
|
|
214
|
+
.key.del{background:var(--mred);font-size:17px;font-weight:500}
|
|
215
|
+
.kp-apply{width:100%;height:46px;border-radius:12px;background:var(--yellow);color:var(--dark);font-size:16px;font-weight:600}
|
|
195
216
|
.gbtn{height:46px;padding:0 18px;border-radius:24px;background:var(--g2);color:#fff;font-weight:600;font-size:15px}
|
|
196
217
|
.cf h3{margin:0 0 6px;font-size:18px;font-weight:600}
|
|
197
218
|
.cf p{margin:0 0 12px;color:var(--lg1);font-size:14px;line-height:1.45}
|
|
@@ -204,6 +225,18 @@ input[type=range]{width:120px;accent-color:var(--yellow)}
|
|
|
204
225
|
.menu{position:absolute;top:66px;right:12px;width:300px;background:var(--g1);border-radius:15px;padding:12px 14px;z-index:58;box-shadow:0 10px 30px rgba(0,0,0,.35);display:none;color:#fff;max-height:calc(100% - 90px);overflow-y:auto}
|
|
205
226
|
.menu.show{display:block}
|
|
206
227
|
.menu .mrow{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 0;font-size:14px}
|
|
228
|
+
.menu.layers{width:330px}
|
|
229
|
+
.menu .mrow small{display:block;color:var(--g4);font-size:12px;margin-top:2px}
|
|
230
|
+
.menu .thispage{margin:2px 0 8px}
|
|
231
|
+
/* the selected image's opacity slider, just above the bar */
|
|
232
|
+
.opacity-pop{position:absolute;z-index:57;width:340px;display:none;align-items:center;gap:10px;background:var(--g2);border-radius:24px;padding:8px 16px;min-height:48px;box-sizing:border-box;box-shadow:0 8px 24px rgba(0,0,0,.3);color:var(--lg2);font-size:12.5px;font-weight:600}
|
|
233
|
+
.opacity-pop.show{display:flex}
|
|
234
|
+
.opacity-pop input{flex:1;accent-color:#fff}
|
|
235
|
+
.opacity-pop b{width:48px;text-align:right;color:#fff}
|
|
236
|
+
/* Reset sits concentric in the slider: the same 8 above, below and beside it, its radius the slider's less that */
|
|
237
|
+
#rotatePop{padding-right:8px}
|
|
238
|
+
.opacity-pop .pop-reset{height:32px;padding:0 16px;border-radius:16px;background:var(--g3);color:#fff;font-size:12.5px;font-weight:600}
|
|
239
|
+
.opacity-pop .pop-reset:disabled{opacity:.4}
|
|
207
240
|
.seg{display:flex;background:var(--dark);border-radius:16px;padding:3px}
|
|
208
241
|
.seg button{padding:6px 10px;border-radius:13px;font-size:12.5px;font-weight:600;color:var(--lg1)}
|
|
209
242
|
.seg button[aria-pressed="true"]{background:#fff;color:var(--dark)}
|
|
@@ -213,10 +246,34 @@ input[type=range]{width:120px;accent-color:var(--yellow)}
|
|
|
213
246
|
.keys-list{display:none;font-size:12.5px;color:var(--lg1);line-height:1.7;padding-bottom:6px}
|
|
214
247
|
.keys-list.show{display:block}
|
|
215
248
|
.keys-list kbd{font-family:inherit;color:var(--yellow);font-weight:600;display:inline-block;min-width:64px}
|
|
216
|
-
.palette{position:absolute;z-index:58;background:var(--g1);border-radius:
|
|
249
|
+
.palette{position:absolute;z-index:58;width:264px;max-height:calc(100% - 16px);overflow-y:auto;background:var(--g1);border:1px solid var(--g2);border-radius:20px;padding:12px;display:none;flex-direction:column;gap:8px;box-shadow:0 8px 24px rgba(0,0,0,.3)}
|
|
217
250
|
.palette.show{display:flex}
|
|
218
|
-
.
|
|
219
|
-
.
|
|
251
|
+
.pl-label{font-size:11px;font-weight:600;color:var(--g4);text-transform:uppercase;letter-spacing:.5px;margin-top:2px}
|
|
252
|
+
.pl-swatches{display:flex;flex-wrap:wrap;gap:10px}
|
|
253
|
+
.pl-swatch{width:40px;height:32px;border-radius:16px;border:2px solid rgba(255,255,255,.18)}
|
|
254
|
+
.pl-swatch.on{border:3px solid #fff}
|
|
255
|
+
.pl-row{display:flex;gap:8px;align-items:center}
|
|
256
|
+
.pl-opt{flex:1;height:40px;border-radius:12px;border:1px solid rgba(255,255,255,.18);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;color:#fff;font-size:10px;font-weight:500}
|
|
257
|
+
.pl-opt.tall{height:48px}
|
|
258
|
+
.pl-opt.on{border-color:#fff;background:var(--blue)}
|
|
259
|
+
.pl-opt i{display:block;background:#fff}
|
|
260
|
+
.pv-dot{border-radius:10px}
|
|
261
|
+
.pv-bar{width:24px;height:5px}
|
|
262
|
+
.pv-row{display:flex;align-items:center;gap:3px}
|
|
263
|
+
.pv-dash{width:6px;height:5px}
|
|
264
|
+
.pv-row .pv-dot{width:5px;height:5px;border-radius:3px}
|
|
265
|
+
.pv-cap{width:22px;height:7px}
|
|
266
|
+
.pv-cap.round{border-radius:4px}
|
|
267
|
+
.pv-arrow{gap:0}
|
|
268
|
+
.pv-arrow.start{flex-direction:row-reverse}
|
|
269
|
+
.pv-shaft{width:13px;height:5px}
|
|
270
|
+
.pl-opt i.pv-head{width:0;height:0;background:none;border-top:6px solid transparent;border-bottom:6px solid transparent;border-left:9px solid #fff}
|
|
271
|
+
.pl-opt .pv-arrow.start i.pv-head{border-left:0;border-right:9px solid #fff}
|
|
272
|
+
|
|
273
|
+
/* what a tool just picked does: a line under the tabs that goes by itself */
|
|
274
|
+
.tooltip-note{position:absolute;top:66px;left:50%;transform:translate(-50%,-4px);max-width:min(460px,calc(100% - 24px));background:var(--g1);border:1px solid var(--g2);color:var(--lg2);border-radius:16px;padding:8px 14px;font-size:13px;text-align:center;z-index:29;opacity:0;pointer-events:none;transition:opacity .24s cubic-bezier(.4,0,.2,1),transform .24s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 11px rgba(0,0,0,.22)}
|
|
275
|
+
.tooltip-note.show{opacity:1;transform:translate(-50%,0);transition-duration:.16s}
|
|
276
|
+
@media (max-width:760px){.tooltip-note{top:120px}}
|
|
220
277
|
|
|
221
278
|
/* toast */
|
|
222
279
|
.toast{position:absolute;left:50%;bottom:96px;transform:translate(-50%,12px);background:var(--dark);border:1px solid var(--g2);color:#fff;border-radius:12px;padding:11px 14px;font-size:14px;line-height:1.35;z-index:70;opacity:0;pointer-events:none;transition:opacity .2s,transform .2s;display:flex;align-items:center;gap:14px;max-width:min(520px,calc(100% - 32px));box-shadow:0 8px 24px rgba(0,0,0,.3)}
|
|
@@ -252,7 +309,8 @@ input[type=range]{width:120px;accent-color:var(--yellow)}
|
|
|
252
309
|
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}
|
|
253
310
|
|
|
254
311
|
/* ---------- wall elevation sheet ---------- */
|
|
255
|
-
.elev{position:absolute;
|
|
312
|
+
.elev{position:absolute;top:0;left:0;right:0;z-index:54;display:none;flex-direction:column;pointer-events:none}
|
|
313
|
+
.elev>*{pointer-events:auto}
|
|
256
314
|
.elev.show{display:flex;animation:elevIn .22s cubic-bezier(.2,.8,.2,1)}
|
|
257
315
|
@keyframes elevIn{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}
|
|
258
316
|
.elev-top{display:flex;align-items:center;gap:10px;padding:12px;padding-top:max(12px,env(safe-area-inset-top));flex-shrink:0}
|
|
@@ -260,12 +318,3 @@ input[type=range]{width:120px;accent-color:var(--yellow)}
|
|
|
260
318
|
.elev-title b{font-size:14px;color:#fff;white-space:nowrap}
|
|
261
319
|
.elev-title small{font-size:12px;color:var(--lg1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
262
320
|
.elev-flip{color:#fff;font-size:14px;font-weight:600;gap:8px}
|
|
263
|
-
.elev-body{position:relative;flex:1;min-height:0}
|
|
264
|
-
.elev-body canvas{position:absolute;inset:0;width:100%;height:100%;touch-action:none;display:block}
|
|
265
|
-
.elev-bar{position:absolute;left:50%;bottom:max(16px,env(safe-area-inset-bottom));transform:translateX(-50%);display:flex;align-items:center;gap:6px;background:var(--g2);border-radius:34px;padding:6px 6px 6px 20px;box-shadow:0 6px 22px rgba(0,0,0,.26);color:#fff;font-size:14px;font-weight:600;white-space:nowrap}
|
|
266
|
-
.elev-bar[hidden]{display:none}
|
|
267
|
-
.elev .trash{position:absolute;right:16px;bottom:max(16px,env(safe-area-inset-bottom))}
|
|
268
|
-
.elev-acts{display:flex;align-items:center;gap:2px;margin-left:6px}
|
|
269
|
-
.elev-acts:empty{display:none}
|
|
270
|
-
.elev-rail{top:12px;max-height:calc(100% - 24px)}
|
|
271
|
-
.elev-bar .addbtn[hidden]{display:none}
|