@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
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import type { WebGLRenderer } from 'three';
|
|
2
|
+
import { ed } from '../app/state';
|
|
3
|
+
import { interacting, toast } from '../editor/ports';
|
|
4
|
+
import { pick3d, v3, type Command3, type Pick3 } from '../editor/view3dState';
|
|
5
|
+
import { settings } from '../model/settings';
|
|
6
|
+
import { planSK, store } from '../model/store';
|
|
7
|
+
import { buildScene, CUT_H, loweredBy, type Cut3, type Scene3 } from '../view3d/build';
|
|
8
|
+
import { between, eye, fit, home, orbitBy, panBy, topDown, zoomAt, zoomLimits, type Orbit, type Window3 } from '../view3d/camera';
|
|
9
|
+
import type { V3 } from '../view3d/mesh';
|
|
10
|
+
import { Viewer3D, type Theme3 } from '../view3d/viewer';
|
|
11
|
+
import { reducedMotion } from './motion';
|
|
12
|
+
import { PHONE_MAX } from './theme';
|
|
13
|
+
import { topLayout } from './topRow';
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
What the 3D view does, whichever screen it's on (a browser's canvas, a device's GL view — see View3D.web.tsx and
|
|
17
|
+
View3D.native.tsx, which only make the renderer and pass the fingers on): the model made again when the plan
|
|
18
|
+
changes, the walls in front cut away as the camera turns, the camera moved by gestures and by the rail's views, and
|
|
19
|
+
a tap told to the editor. It draws only when something changed, at most once a frame.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** Looking down steeper than this, nothing's in the way: no walls are cut away. */
|
|
23
|
+
const CUT_EL = 1.2;
|
|
24
|
+
|
|
25
|
+
/** A plan with nothing to stand up yet: the camera frames a room-sized space. */
|
|
26
|
+
const EMPTY: { min: V3; max: V3 } = { min: [-120, -120, 0], max: [120, 120, 96] };
|
|
27
|
+
|
|
28
|
+
/** A second tap this soon after the first (ms), and this near it (points), is a double tap. */
|
|
29
|
+
export const DOUBLE_MS = 320, DOUBLE_NEAR = 24;
|
|
30
|
+
|
|
31
|
+
/** How long the rail's views and a double tap glide for (ms): long enough to see where the camera went. */
|
|
32
|
+
const GLIDE_MS = 360;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Cutaway models kept, by which walls they lower (all that tells one from another): turning back and forth finds them
|
|
36
|
+
* made. One that took longer than SLOW_MS to make waits while the camera moves, until it rests for REST_MS — a big
|
|
37
|
+
* plan on a phone's JavaScript thread would stall the turn each time another wall came in front.
|
|
38
|
+
*/
|
|
39
|
+
const CUT_KEEP = 6, SLOW_MS = 16, REST_MS = 150;
|
|
40
|
+
|
|
41
|
+
const now = (): number => (typeof performance !== 'undefined' ? performance.now() : Date.now());
|
|
42
|
+
|
|
43
|
+
/** The view showing now (for tests and the harness). */
|
|
44
|
+
let active: View3DHost | null = null;
|
|
45
|
+
|
|
46
|
+
export class View3DHost {
|
|
47
|
+
private readonly viewer: Viewer3D;
|
|
48
|
+
/** the model with every wall at full height (what the camera frames), and the one showing (cut away or not) */
|
|
49
|
+
private full: Scene3 | null = null;
|
|
50
|
+
private shown: Scene3 | null = null;
|
|
51
|
+
private modelKey = '';
|
|
52
|
+
private cutKey = '';
|
|
53
|
+
private cmdSeq: number;
|
|
54
|
+
private theme: Theme3;
|
|
55
|
+
private w = 1;
|
|
56
|
+
private h = 1;
|
|
57
|
+
private frame = 0;
|
|
58
|
+
private failed = false;
|
|
59
|
+
/** cutaway models made for this model, by what they lower (the most recently shown last) */
|
|
60
|
+
private readonly cuts = new Map<string, Scene3>();
|
|
61
|
+
/** how long the last model took to make (ms) */
|
|
62
|
+
private buildMs = 0;
|
|
63
|
+
/** a gesture is moving the camera; when it last moved */
|
|
64
|
+
private held = false;
|
|
65
|
+
private movedAt = 0;
|
|
66
|
+
private rest: ReturnType<typeof setTimeout> | undefined;
|
|
67
|
+
/** the camera gliding to a view */
|
|
68
|
+
private glide: { from: Orbit; to: Orbit; t0: number } | null = null;
|
|
69
|
+
/** frames still to draw, changed or not, since the GL view was made or resized (see settle) */
|
|
70
|
+
private settling = 0;
|
|
71
|
+
/** on screen (the tab showing): hidden, it's kept as it is but draws nothing (see setVisible); and frames drawn */
|
|
72
|
+
private visible = true;
|
|
73
|
+
private frames = 0;
|
|
74
|
+
private lastTap: { t: number; x: number; y: number } | null = null;
|
|
75
|
+
|
|
76
|
+
/** `present`: shows what was drawn, where the GL view needs telling (expo-gl's endFrameEXP). */
|
|
77
|
+
constructor(renderer: WebGLRenderer, theme: Theme3, private readonly present: () => void = () => {}) {
|
|
78
|
+
this.theme = theme;
|
|
79
|
+
this.viewer = new Viewer3D(renderer, theme);
|
|
80
|
+
// (a camera move asked for before this view opened isn't for it)
|
|
81
|
+
this.cmdSeq = v3.cmd.seq;
|
|
82
|
+
active = this;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private get aspect(): number {
|
|
86
|
+
return this.w / this.h;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Where the model is framed: clear of the chrome over the view — the top bar (and the tabs under it, where they don't
|
|
91
|
+
* fit beside it: a phone, an upright tablet) and the bar along the bottom. (The 3D tab has no rail.)
|
|
92
|
+
*/
|
|
93
|
+
private win(): Window3 {
|
|
94
|
+
const top = topLayout(this.w, this.h, this.w < PHONE_MAX).below ? 150 : 84, bottom = 96, left = 20, right = 20;
|
|
95
|
+
if (this.w < left + right + 120 || this.h < top + bottom + 120) return { x0: -1, x1: 1, y0: -1, y1: 1 };
|
|
96
|
+
return { x0: -1 + (2 * left) / this.w, x1: 1 - (2 * right) / this.w, y0: -1 + (2 * bottom) / this.h, y1: 1 - (2 * top) / this.h };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** What the camera frames: everything at full height. */
|
|
100
|
+
private box(): { min: V3; max: V3 } {
|
|
101
|
+
return this.full && this.full.parts.length ? this.full : EMPTY;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Drawing went wrong: said once (a 3D view that stays empty says why), and logged in full. */
|
|
105
|
+
private fail(e: unknown): void {
|
|
106
|
+
console.error('3D view:', e);
|
|
107
|
+
if (this.failed) return;
|
|
108
|
+
this.failed = true;
|
|
109
|
+
toast('The 3D view couldn’t be drawn (' + (e instanceof Error ? e.message : String(e)) + '). Leave the tab and open it again to retry.', { warn: true });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** The editor changed, or the theme: the model, a camera move asked for, what's tapped. */
|
|
113
|
+
sync(theme: Theme3): void {
|
|
114
|
+
try { this.syncNow(theme); } catch (e) { this.fail(e); }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private syncNow(theme: Theme3): void {
|
|
118
|
+
if (theme !== this.theme) { this.theme = theme; this.viewer.setTheme(theme); }
|
|
119
|
+
const key = [store.curPageId, ed.editSeq, settings.wallH].join('|');
|
|
120
|
+
if (key !== this.modelKey) {
|
|
121
|
+
this.modelKey = key;
|
|
122
|
+
const t = now();
|
|
123
|
+
this.full = buildScene(planSK());
|
|
124
|
+
this.buildMs = now() - t;
|
|
125
|
+
this.cuts.clear();
|
|
126
|
+
this.cutKey = '#'; // (made again for the new model, cut away or not)
|
|
127
|
+
if (!v3.orbit || v3.orbitPage !== store.curPageId) {
|
|
128
|
+
this.glide = null;
|
|
129
|
+
v3.orbit = this.view('reset');
|
|
130
|
+
v3.orbitPage = store.curPageId;
|
|
131
|
+
v3.framed = 'reset';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (v3.cmd.seq !== this.cmdSeq) {
|
|
135
|
+
this.cmdSeq = v3.cmd.seq;
|
|
136
|
+
this.glideTo(v3.cmd.what);
|
|
137
|
+
}
|
|
138
|
+
this.viewer.setPicked(v3.pick ? v3.pick.kind + ':' + v3.pick.id : null);
|
|
139
|
+
this.requestDraw();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Where view `what` puts the camera, on the screen as it is now ('fit': everything, from where it looks now). */
|
|
143
|
+
private view(what: Command3): Orbit {
|
|
144
|
+
const { min, max } = this.box(), a = this.aspect, w = this.win();
|
|
145
|
+
return what === 'top' ? topDown(min, max, a, w) : what === 'reset' || !v3.orbit ? home(min, max, a, w) : fit(v3.orbit, min, max, a, w);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** The camera to view `what`: gliding there from where it is (straight there when motion is reduced). */
|
|
149
|
+
private glideTo(what: Command3): void {
|
|
150
|
+
const from = v3.orbit, to = this.view(what);
|
|
151
|
+
v3.framed = what;
|
|
152
|
+
this.glide = from && !reducedMotion() ? { from, to, t0: now() } : null;
|
|
153
|
+
if (!this.glide) v3.orbit = to;
|
|
154
|
+
this.requestDraw();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** The camera was moved by hand: a glide stops where it got to, and it's no longer at a view. */
|
|
158
|
+
private moved(): void {
|
|
159
|
+
this.glide = null;
|
|
160
|
+
v3.framed = null;
|
|
161
|
+
this.movedAt = now();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Where the walls in front are cut away from, as the camera is now (null: none are). */
|
|
165
|
+
private cut(): Cut3 | null {
|
|
166
|
+
const o = v3.orbit;
|
|
167
|
+
if (!v3.cutaway || !o || !this.full || !this.full.parts.length || o.el > CUT_EL) return null;
|
|
168
|
+
const { min, max } = this.full, c = { x: (min[0] + max[0]) / 2, y: (min[1] + max[1]) / 2 };
|
|
169
|
+
const e = eye(o), dx = e[0] - c.x, dy = e[1] - c.y, L = Math.hypot(dx, dy);
|
|
170
|
+
return L < 1e-6 ? null : { c, d: { x: dx / L, y: dy / L }, h: CUT_H };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* The model to show from here: another only when what's cut away changes — one made before if it's kept, or made
|
|
175
|
+
* now (or, if they're slow to make, once the camera rests).
|
|
176
|
+
*/
|
|
177
|
+
private showNow(): void {
|
|
178
|
+
const cut = this.cut(), key = cut ? loweredBy(planSK(), cut).join(',') : '';
|
|
179
|
+
if (key === this.cutKey && this.shown) return;
|
|
180
|
+
let next = key ? this.cuts.get(key) : this.full;
|
|
181
|
+
if (next) {
|
|
182
|
+
// (the most recently shown last: the first is the one to let go)
|
|
183
|
+
if (key) { this.cuts.delete(key); this.cuts.set(key, next); }
|
|
184
|
+
} else {
|
|
185
|
+
const t = now();
|
|
186
|
+
if (this.shown && this.buildMs > SLOW_MS && (this.held || this.glide) && t - this.movedAt < REST_MS) {
|
|
187
|
+
clearTimeout(this.rest);
|
|
188
|
+
this.rest = setTimeout(() => this.requestDraw(), REST_MS - (t - this.movedAt) + 5);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
next = buildScene(planSK(), { cut });
|
|
192
|
+
this.buildMs = now() - t;
|
|
193
|
+
this.cuts.set(key, next);
|
|
194
|
+
if (this.cuts.size > CUT_KEEP) this.cuts.delete(this.cuts.keys().next().value!);
|
|
195
|
+
}
|
|
196
|
+
this.cutKey = key;
|
|
197
|
+
this.shown = next;
|
|
198
|
+
if (next) this.viewer.setModel(next);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private requestDraw(): void {
|
|
202
|
+
if (this.frame || !this.visible) return;
|
|
203
|
+
this.frame = requestAnimationFrame(() => { this.frame = 0; this.draw(); });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private draw(): void {
|
|
207
|
+
if (!this.full || !v3.orbit) return;
|
|
208
|
+
try {
|
|
209
|
+
const g = this.glide;
|
|
210
|
+
if (g) {
|
|
211
|
+
const t = (now() - g.t0) / GLIDE_MS;
|
|
212
|
+
v3.orbit = between(g.from, g.to, t);
|
|
213
|
+
this.movedAt = now();
|
|
214
|
+
if (t >= 1) this.glide = null;
|
|
215
|
+
else this.requestDraw();
|
|
216
|
+
}
|
|
217
|
+
this.showNow();
|
|
218
|
+
this.viewer.setOrbit(v3.orbit);
|
|
219
|
+
this.viewer.render();
|
|
220
|
+
this.present();
|
|
221
|
+
this.frames++;
|
|
222
|
+
if (this.settling > 0) { this.settling--; this.requestDraw(); }
|
|
223
|
+
} catch (e) { this.fail(e); }
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** The view's size on the screen, in points. A camera still at a view is framed again for the new shape. */
|
|
227
|
+
resize(w: number, h: number): void {
|
|
228
|
+
const was = { w: this.w, h: this.h };
|
|
229
|
+
this.w = Math.max(1, w);
|
|
230
|
+
this.h = Math.max(1, h);
|
|
231
|
+
this.viewer.setSize(this.w, this.h);
|
|
232
|
+
if (v3.framed && v3.orbit && this.full && (Math.abs(was.w - this.w) > 0.5 || Math.abs(was.h - this.h) > 0.5)) {
|
|
233
|
+
const to = this.view(v3.framed);
|
|
234
|
+
if (this.glide) this.glide.to = to;
|
|
235
|
+
else v3.orbit = to;
|
|
236
|
+
}
|
|
237
|
+
this.requestDraw();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Draw the next few frames too, whether or not anything changes: a GL view just made or resized can let its first
|
|
242
|
+
* frames go while it makes its buffers (expo-gl), and this view draws only when something changes.
|
|
243
|
+
*/
|
|
244
|
+
settle(frames = 4): void {
|
|
245
|
+
this.settling = Math.max(this.settling, frames);
|
|
246
|
+
this.requestDraw();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* The tab is showing, or not. Left, the view is kept as it is — its GL context, its compiled shaders, its model — so
|
|
251
|
+
* coming back draws at once, where making them all again was a pause each time the tab opened; meanwhile it draws
|
|
252
|
+
* nothing, and the tests and harness see no 3D view.
|
|
253
|
+
*/
|
|
254
|
+
setVisible(on: boolean): void {
|
|
255
|
+
if (on === this.visible) return;
|
|
256
|
+
this.visible = on;
|
|
257
|
+
if (on) { active = this; this.requestDraw(); } else {
|
|
258
|
+
if (active === this) active = null;
|
|
259
|
+
if (this.frame) cancelAnimationFrame(this.frame);
|
|
260
|
+
this.frame = 0;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Draw again (the GL context was lost and is back: what was on the GPU went with it, and goes up again as it draws). */
|
|
265
|
+
redraw(): void {
|
|
266
|
+
this.requestDraw();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** A drag (screen points): the model turns with it. */
|
|
270
|
+
orbit(dx: number, dy: number): void {
|
|
271
|
+
if (!v3.orbit) return;
|
|
272
|
+
this.moved();
|
|
273
|
+
v3.orbit = orbitBy(v3.orbit, dx, dy, this.h);
|
|
274
|
+
this.requestDraw();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** A two-finger (or right-button) drag: the model slides with it. */
|
|
278
|
+
pan(dx: number, dy: number): void {
|
|
279
|
+
if (!v3.orbit) return;
|
|
280
|
+
this.moved();
|
|
281
|
+
v3.orbit = panBy(v3.orbit, dx, dy, this.h);
|
|
282
|
+
this.requestDraw();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Closer by `f` (under 1: in) toward screen point x, y. */
|
|
286
|
+
zoom(f: number, x: number, y: number): void {
|
|
287
|
+
if (!v3.orbit || !isFinite(f) || f <= 0) return;
|
|
288
|
+
this.moved();
|
|
289
|
+
const { min, max } = this.box(), [lo, hi] = zoomLimits(min, max);
|
|
290
|
+
v3.orbit = zoomAt(v3.orbit, f, (x / this.w) * 2 - 1, 1 - (y / this.h) * 2, this.aspect, lo, hi);
|
|
291
|
+
this.requestDraw();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* A tap (screen points) — on either screen: what it landed on is picked at once (its sizes go in the bar; empty space
|
|
296
|
+
* lets go), and a second soon after and near it frames everything. Not waiting to see whether a second comes: a
|
|
297
|
+
* device's double-tap gesture held every single tap back half a second.
|
|
298
|
+
*/
|
|
299
|
+
tapAt(x: number, y: number): void {
|
|
300
|
+
const t = now(), l = this.lastTap;
|
|
301
|
+
if (l && t - l.t < DOUBLE_MS && Math.hypot(x - l.x, y - l.y) < DOUBLE_NEAR) {
|
|
302
|
+
this.lastTap = null;
|
|
303
|
+
this.doubleTap();
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
this.lastTap = { t, x, y };
|
|
307
|
+
this.tap(x, y);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** What a tap at x, y landed on is picked (its sizes go in the bar); empty space lets go. */
|
|
311
|
+
private tap(x: number, y: number): void {
|
|
312
|
+
const hit = this.viewer.pick(x, y);
|
|
313
|
+
let p: Pick3 | null = hit ? { kind: hit.kind, id: hit.id } : null;
|
|
314
|
+
if (p && hit && p.kind === 'wall' && Math.abs(hit.n[2]) < 0.5) {
|
|
315
|
+
// (a wall's side: which face of it, as the wall view takes it)
|
|
316
|
+
const sk = planSK(), l = sk.lines[p.id], A = l && sk.points[l.a], B = l && sk.points[l.b];
|
|
317
|
+
if (A && B) p = { ...p, side: hit.n[0] * -(B.y - A.y) + hit.n[1] * (B.x - A.x) >= 0 ? 1 : -1 };
|
|
318
|
+
}
|
|
319
|
+
pick3d(p);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** A double tap: everything in view again, from where it's looked at. */
|
|
323
|
+
private doubleTap(): void {
|
|
324
|
+
if (v3.orbit) this.glideTo('fit');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* A gesture started moving the camera, or ended: on a phone the chrome steps aside meanwhile, and a cutaway put off
|
|
329
|
+
* while it moved is made as it ends.
|
|
330
|
+
*/
|
|
331
|
+
moving(on: boolean): void {
|
|
332
|
+
this.held = on;
|
|
333
|
+
interacting(on);
|
|
334
|
+
if (!on) this.requestDraw();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
dispose(): void {
|
|
338
|
+
if (this.frame) cancelAnimationFrame(this.frame);
|
|
339
|
+
this.frame = 0;
|
|
340
|
+
clearTimeout(this.rest);
|
|
341
|
+
this.viewer.dispose();
|
|
342
|
+
if (active === this) active = null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** For tests: what's showing, and where plan points land on the screen. */
|
|
346
|
+
debug() {
|
|
347
|
+
return {
|
|
348
|
+
parts: this.shown?.parts.map(p => p.kind + ':' + p.id) ?? [],
|
|
349
|
+
lowered: this.shown?.lowered ?? [],
|
|
350
|
+
orbit: v3.orbit,
|
|
351
|
+
size: [this.w, this.h],
|
|
352
|
+
/** the last frame's draws and triangles */
|
|
353
|
+
drawn: this.viewer.stats(),
|
|
354
|
+
near: this.viewer.camera.near,
|
|
355
|
+
far: this.viewer.camera.far,
|
|
356
|
+
/** cutaway models kept; whether the camera is gliding to a view; frames drawn since it was made */
|
|
357
|
+
cutsKept: this.cuts.size,
|
|
358
|
+
gliding: !!this.glide,
|
|
359
|
+
frames: this.frames,
|
|
360
|
+
project: (x: number, y: number, z: number) => this.viewer.project(x, y, z),
|
|
361
|
+
pickAt: (x: number, y: number) => this.viewer.pick(x, y),
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** The 3D view showing now, for tests and the harness (null when the tab isn't). */
|
|
367
|
+
export const view3dDebug = () => active?.debug() ?? null;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Platform, StyleSheet } from 'react-native';
|
|
2
|
+
import { CORNER_SMOOTHING } from '../render/squircle';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
Every rounded corner in the editor's chrome smoothed as Figma's 60% ("iOS") — the panels, cards, fields, keys and
|
|
6
|
+
menus. Pills and circles are left as they are: a corner at half the shorter side has no straight side to ease into
|
|
7
|
+
(Figma leaves them round too).
|
|
8
|
+
|
|
9
|
+
On a device that's iOS's own continuous corner (`borderCurve`), which is what Figma's "iOS" preset copies; Android
|
|
10
|
+
has no such corner and keeps round ones. In a browser that draws CSS `corner-shape`, a superellipse stands in for it:
|
|
11
|
+
Figma's corner runs (1 + smoothing)·r along each side but keeps its middle on the circle of radius r, so the corner
|
|
12
|
+
is drawn that much wider, with the exponent that puts its middle back where the round corner's was. A browser
|
|
13
|
+
without `corner-shape` keeps round corners as they were (and its radii untouched).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const RADII = ['borderRadius', 'borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomLeftRadius', 'borderBottomRightRadius'] as const;
|
|
17
|
+
|
|
18
|
+
/** The superellipse exponent (as CSS's log₂ form) whose middle sits where a round corner's does, `1 + smoothing` wider. */
|
|
19
|
+
function superellipseK(smoothing: number): number {
|
|
20
|
+
// a round corner's middle is (1 − 1/√2)·r in from the corner on each side; a superellipse |x|ⁿ + |y|ⁿ = 1 drawn
|
|
21
|
+
// (1 + s)·r wide has its middle at (1 + s)·r·(1 − 2^(−1/n)) — equal them and solve for n, then K = log₂ n
|
|
22
|
+
const t = 1 - (1 - Math.SQRT1_2) / (1 + smoothing);
|
|
23
|
+
const n = -1 / Math.log2(t);
|
|
24
|
+
return Math.log2(n);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const webCorners = Platform.OS === 'web' && typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('corner-shape', 'superellipse(2)');
|
|
28
|
+
const CORNER_SHAPE = 'superellipse(' + superellipseK(CORNER_SMOOTHING).toFixed(2) + ')';
|
|
29
|
+
|
|
30
|
+
type Style = Record<string, unknown>;
|
|
31
|
+
|
|
32
|
+
/** One style with its corners smoothed (unchanged when it has no rounded corner, or is a pill or a circle). */
|
|
33
|
+
export function smoothCorners<T extends Style>(st: T): T {
|
|
34
|
+
const radii = RADII.filter(k => typeof st[k] === 'number' && (st[k] as number) > 0);
|
|
35
|
+
if (!radii.length) return st;
|
|
36
|
+
const dims = [st.width, st.height, st.minHeight].filter((v): v is number => typeof v === 'number');
|
|
37
|
+
const half = dims.length ? Math.min(...dims) / 2 : Infinity;
|
|
38
|
+
// a pill (an "as round as it goes" 100, or half its known size) or a circle
|
|
39
|
+
if (radii.some(k => (st[k] as number) >= 100 || (st[k] as number) >= half - 0.5)) return st;
|
|
40
|
+
if (Platform.OS === 'ios') return { ...st, borderCurve: 'continuous' };
|
|
41
|
+
if (!webCorners) return st;
|
|
42
|
+
const out: Style = { ...st, cornerShape: CORNER_SHAPE };
|
|
43
|
+
for (const k of radii) out[k] = Math.min((st[k] as number) * (1 + CORNER_SMOOTHING), half);
|
|
44
|
+
return out as T;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** StyleSheet.create, every style's corners smoothed (smoothCorners). */
|
|
48
|
+
export function createSheet<T extends StyleSheet.NamedStyles<T>>(styles: T & StyleSheet.NamedStyles<unknown>): T {
|
|
49
|
+
const out = {} as Record<string, unknown>;
|
|
50
|
+
for (const k of Object.keys(styles)) out[k] = smoothCorners((styles as Record<string, Style>)[k]);
|
|
51
|
+
return StyleSheet.create(out as T);
|
|
52
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Easing, Platform } from 'react-native';
|
|
2
|
+
|
|
3
|
+
/** The chrome's curve and pace (see motion.tsx): off at once, settling softly at the end, over 150 ms. */
|
|
4
|
+
export const EASE = Easing.bezier(0.2, 0, 0, 1);
|
|
5
|
+
export const DURATION = 150;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Animations of opacity, transforms and colours run on the native driver on a device: the UI thread plays them from
|
|
9
|
+
* start to end, so they stay smooth while the JavaScript thread works (a browser has no such driver).
|
|
10
|
+
*/
|
|
11
|
+
export const NATIVE = Platform.OS !== 'web';
|
|
12
|
+
|
|
13
|
+
/** A box TweenView eases to (see Tween.tsx). */
|
|
14
|
+
export type TweenTo = { left?: number; top?: number; width?: number; height?: number };
|
package/src/react/host.ts
CHANGED
|
@@ -18,10 +18,15 @@ export function setLaserConnected(on: boolean): void {
|
|
|
18
18
|
notify();
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/**
|
|
22
|
+
* A reading in inches. False when no drawing is open to take it. `capture`: the host's own record of the reading (its
|
|
23
|
+
* device, its number, its flags — anything JSON), kept with the dimension it sets: handed back when that's saved to
|
|
24
|
+
* the group (CadLinks.saveMeasurement), or with the new value for one linked to a measurement of the job
|
|
25
|
+
* (CadLinks.update). A value typed over it isn't the reading's.
|
|
26
|
+
*/
|
|
27
|
+
export function laserReading(inches: number, capture?: unknown): boolean {
|
|
23
28
|
if (!editorHost.open || !store.project || !Number.isFinite(inches) || inches <= 0) return false;
|
|
24
|
-
applyReading(inches);
|
|
29
|
+
applyReading(inches, capture);
|
|
25
30
|
notify();
|
|
26
31
|
return true;
|
|
27
32
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { MeasurementKind } from '../editor/links';
|
|
3
|
+
import type { LinkTarget } from '../model/types';
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
The host's own keypad, used in place of the editor's: on a phone BOLDR asks for lengths and angles with the
|
|
7
|
+
measurement keypads people already know from the rest of the app. The editor hands over what it's asking for — what
|
|
8
|
+
the value is, the extra choices beside the keys (a wall's edge, a direction) — and the host hands back the value, or
|
|
9
|
+
that it was closed without one. Tablets and browsers keep the editor's keypad (react/KeypadSheet.tsx), whose page of
|
|
10
|
+
the job's measurements the host can still draw itself (MeasurementList, below).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** An extra choice beside the keys: a wall's edge, a direction. */
|
|
14
|
+
export interface HostKeypadExtra { label: string; ariaLabel?: string; on?: boolean; danger?: boolean; press(): void }
|
|
15
|
+
|
|
16
|
+
/** What the editor is asking the host's keypad for. */
|
|
17
|
+
export interface HostKeypadRequest {
|
|
18
|
+
title: string;
|
|
19
|
+
sub?: string;
|
|
20
|
+
/** a length (handed back in inches) or an angle (in degrees) */
|
|
21
|
+
kind: 'length' | 'angle';
|
|
22
|
+
/** the value it has now, written as the editor shows it */
|
|
23
|
+
current?: string;
|
|
24
|
+
/** the extra choices, read again after each press (their state can change) */
|
|
25
|
+
extras(): HostKeypadExtra[];
|
|
26
|
+
/** the value entered: inches for a length, degrees for an angle */
|
|
27
|
+
apply(value: number): void;
|
|
28
|
+
/**
|
|
29
|
+
* A line's length or a dimension's angle, with a host that links (CadEditorProps.links): what the job's
|
|
30
|
+
* measurements are offered for — show them with the keys (a page swiped to, as BOLDR's keypads have it), those of
|
|
31
|
+
* this kind pickable and the rest greyed out. Absent: offer none.
|
|
32
|
+
*/
|
|
33
|
+
measurementKind?: 'length' | 'angle';
|
|
34
|
+
/** The measurement picked from that list: the value takes it, linked to it, and the editor closes the keypad. */
|
|
35
|
+
pickMeasurement?(target: LinkTarget): void;
|
|
36
|
+
/** closed without a value */
|
|
37
|
+
cancel(): void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Shown with a request; hidden with null — the editor closed it itself (an extra, a laser reading, another tool). */
|
|
41
|
+
export type HostKeypad = (request: HostKeypadRequest | null) => void;
|
|
42
|
+
|
|
43
|
+
/** The editor keypad's page of the job's measurements, as a host that draws it is given it (MeasurementList). */
|
|
44
|
+
export interface MeasurementListPage {
|
|
45
|
+
/** What the field takes: a length or an angle. List the other kind greyed out. */
|
|
46
|
+
kind: MeasurementKind;
|
|
47
|
+
/** The measurement picked: the value takes it, linked to it, and the editor closes the keypad. */
|
|
48
|
+
pick(target: LinkTarget): void;
|
|
49
|
+
/** The page's size, which the list fills: the keypad's width, and the keys' height. */
|
|
50
|
+
width: number;
|
|
51
|
+
height: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The host's own list of the job's measurements, drawn on the editor keypad's page of them in place of the editor's
|
|
56
|
+
* grid (CadEditorProps.measurementList): BOLDR's tablets show the group headers and measurement tiles of its phones.
|
|
57
|
+
* It's rendered as a component of its own, from when the page is first shown and again when what it's given changes.
|
|
58
|
+
* Return the list's element, and keep what it fetches inside that.
|
|
59
|
+
*/
|
|
60
|
+
export type MeasurementList = (page: MeasurementListPage) => ReactNode;
|
|
61
|
+
|
|
62
|
+
let host: HostKeypad | null = null;
|
|
63
|
+
|
|
64
|
+
/** The keypad to use from now on (CadEditor: a phone's host's), or null for the editor's own. */
|
|
65
|
+
export function setHostKeypad(fn: HostKeypad | null): void { host = fn; }
|
|
66
|
+
export const hostKeypad = (): HostKeypad | null => host;
|