@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
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Figma's corner smoothing, at the 60% it calls "iOS" (Apple's continuous corners): a corner keeps its circular arc of
|
|
3
|
+
radius r in the middle, but shorter, and eases into each straight side over a longer run — (1 + smoothing) · r of
|
|
4
|
+
the side instead of r — so the curvature has no sudden step where the side ends. The construction is Figma's own
|
|
5
|
+
(as published in its "Desperately seeking squircles", and the MIT figma-squircle package).
|
|
6
|
+
*/
|
|
7
|
+
/** How much every rounded corner in the editor is smoothed: Figma's 60%, "iOS". */
|
|
8
|
+
export const CORNER_SMOOTHING = 0.6;
|
|
9
|
+
const rad = (deg) => (deg * Math.PI) / 180;
|
|
10
|
+
/** One corner's measures: the run along each side (p), the two Béziers' (a, b, c, d), the arc's half-sweep (alpha). */
|
|
11
|
+
function cornerParams(r, smoothing, budget) {
|
|
12
|
+
// a corner can't run further along its sides than half the shorter side: less smoothing where it can't fit
|
|
13
|
+
const s = Math.max(0, Math.min(smoothing, budget / r - 1));
|
|
14
|
+
const p = Math.min((1 + s) * r, budget);
|
|
15
|
+
const arcMeasure = 90 * (1 - s);
|
|
16
|
+
const arcSection = Math.sin(rad(arcMeasure / 2)) * r * Math.SQRT2;
|
|
17
|
+
const alpha = (90 - arcMeasure) / 2;
|
|
18
|
+
const p3ToP4 = r * Math.tan(rad(alpha / 2));
|
|
19
|
+
const beta = 45 * s;
|
|
20
|
+
const c = p3ToP4 * Math.cos(rad(beta));
|
|
21
|
+
const d = c * Math.tan(rad(beta));
|
|
22
|
+
const b = (p - arcSection - c - d) / 3;
|
|
23
|
+
return { p, a: 2 * b, b, c, d, alpha: rad(alpha) };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A rounded rectangle's path with smoothed corners (x, y its top left; r each corner's radius). A pill or a circle —
|
|
27
|
+
* r at least half the shorter side — is left round: smoothing has nothing to ease into there, as in Figma.
|
|
28
|
+
*/
|
|
29
|
+
export function smoothRectPath(c, x, y, w, h, r, smoothing = CORNER_SMOOTHING) {
|
|
30
|
+
const budget = Math.min(w, h) / 2;
|
|
31
|
+
r = Math.max(0, Math.min(r, budget));
|
|
32
|
+
c.beginPath();
|
|
33
|
+
if (r <= 0 || smoothing <= 0 || r >= budget - 1e-6) {
|
|
34
|
+
c.moveTo(x + r, y);
|
|
35
|
+
c.arcTo(x + w, y, x + w, y + h, r);
|
|
36
|
+
c.arcTo(x + w, y + h, x, y + h, r);
|
|
37
|
+
c.arcTo(x, y + h, x, y, r);
|
|
38
|
+
c.arcTo(x, y, x + w, y, r);
|
|
39
|
+
c.closePath();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const k = cornerParams(r, smoothing, budget);
|
|
43
|
+
// each corner drawn in its own frame: u along the side coming into it, v along the side leaving it, the corner's
|
|
44
|
+
// vertex at the origin — then turned to its place, clockwise from the top right
|
|
45
|
+
const corners = [
|
|
46
|
+
{ ox: x + w, oy: y, ux: [1, 0], vx: [0, 1], turn: 0 },
|
|
47
|
+
{ ox: x + w, oy: y + h, ux: [0, 1], vx: [-1, 0], turn: Math.PI / 2 },
|
|
48
|
+
{ ox: x, oy: y + h, ux: [-1, 0], vx: [0, -1], turn: Math.PI },
|
|
49
|
+
{ ox: x, oy: y, ux: [0, -1], vx: [1, 0], turn: (3 * Math.PI) / 2 },
|
|
50
|
+
];
|
|
51
|
+
const { p, a, b, c: cc, d, alpha } = k;
|
|
52
|
+
corners.forEach((q, i) => {
|
|
53
|
+
const at = (u, v) => [q.ox + q.ux[0] * u + q.vx[0] * v, q.oy + q.ux[1] * u + q.vx[1] * v];
|
|
54
|
+
const [sx, sy] = at(-p, 0);
|
|
55
|
+
if (i === 0)
|
|
56
|
+
c.moveTo(sx, sy);
|
|
57
|
+
else
|
|
58
|
+
c.lineTo(sx, sy);
|
|
59
|
+
// into the arc, along the side
|
|
60
|
+
const [c1x, c1y] = at(-p + a, 0), [c2x, c2y] = at(-p + a + b, 0), [e1x, e1y] = at(-p + a + b + cc, d);
|
|
61
|
+
c.bezierCurveTo(c1x, c1y, c2x, c2y, e1x, e1y);
|
|
62
|
+
// the arc: radius r about (−r, r), its middle (90° less twice alpha) — angles turned with the corner
|
|
63
|
+
const [cx, cy] = at(-r, r);
|
|
64
|
+
c.arc(cx, cy, r, -Math.PI / 2 + alpha + q.turn, -alpha + q.turn, false);
|
|
65
|
+
// out of the arc onto the next side (the first Bézier mirrored across the corner's diagonal)
|
|
66
|
+
const [c3x, c3y] = at(0, p - a - b), [c4x, c4y] = at(0, p - a), [ex, ey] = at(0, p);
|
|
67
|
+
c.bezierCurveTo(c3x, c3y, c4x, c4y, ex, ey);
|
|
68
|
+
});
|
|
69
|
+
c.closePath();
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=squircle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"squircle.js","sourceRoot":"","sources":["../../src/render/squircle.ts"],"names":[],"mappings":"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"}
|
package/dist/render/view.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export declare const cvs: {
|
|
|
13
13
|
dpr: number;
|
|
14
14
|
};
|
|
15
15
|
export declare function initCanvas(el: HTMLCanvasElement): void;
|
|
16
|
+
/** The view of what's being edited: the page's plan, or the wall face open. */
|
|
16
17
|
export declare function V(): View;
|
|
17
18
|
export declare function mainT(): Xform;
|
|
18
19
|
export declare const S: (T: Xform, x: number, y: number) => [number, number];
|
|
@@ -25,15 +26,49 @@ export declare function sketchBounds(sk: Sketch, inclOrigin?: boolean): {
|
|
|
25
26
|
x1: number;
|
|
26
27
|
y1: number;
|
|
27
28
|
} | null;
|
|
29
|
+
/** The open wall's face, for the wall view to fit (registered by editor/elevation.ts; null on the plan). */
|
|
30
|
+
declare let wallBox: () => {
|
|
31
|
+
x0: number;
|
|
32
|
+
y0: number;
|
|
33
|
+
x1: number;
|
|
34
|
+
y1: number;
|
|
35
|
+
} | null;
|
|
36
|
+
export declare function setWallBox(fn: typeof wallBox): void;
|
|
28
37
|
export declare function fitView(): void;
|
|
38
|
+
/** Size of the wall view's key plan (top right), pixels. */
|
|
39
|
+
export declare const keyPlanSize: (W: number) => number;
|
|
29
40
|
/** Height of an image underlay in inches: its own, or from the picture's aspect ratio. */
|
|
30
41
|
export declare function underlayH(u: Underlay): number;
|
|
31
42
|
export type HandlePart = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w';
|
|
32
|
-
/**
|
|
43
|
+
/** An image's middle, inches. */
|
|
44
|
+
export declare function underlayMid(u: Underlay): {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
};
|
|
48
|
+
/** The point `lx` across and `ly` up from an image's middle in its own (unturned) frame, as it sits turned (inches). */
|
|
49
|
+
export declare function underlayAt(u: Underlay, lx: number, ly: number): {
|
|
50
|
+
x: number;
|
|
51
|
+
y: number;
|
|
52
|
+
};
|
|
53
|
+
/** A point (inches) as across and up from an image's middle, in its own unturned frame. */
|
|
54
|
+
export declare function underlayLocal(u: Underlay, x: number, y: number): {
|
|
55
|
+
x: number;
|
|
56
|
+
y: number;
|
|
57
|
+
};
|
|
58
|
+
/** An image's corners as it sits turned: top left, top right, bottom right, bottom left (inches). */
|
|
59
|
+
export declare function underlayCorners(u: Underlay): {
|
|
60
|
+
x: number;
|
|
61
|
+
y: number;
|
|
62
|
+
}[];
|
|
63
|
+
/**
|
|
64
|
+
* Screen positions of an underlay's resize handles: corners scale, side handles stretch. `ang`: how far the image is
|
|
65
|
+
* turned on screen (radians clockwise), for the side handles' bars to lie along its sides.
|
|
66
|
+
*/
|
|
33
67
|
export declare function underlayHandles(T: Xform, u: Underlay): {
|
|
34
68
|
part: HandlePart;
|
|
35
69
|
x: number;
|
|
36
70
|
y: number;
|
|
71
|
+
ang: number;
|
|
37
72
|
}[];
|
|
38
73
|
/** A world-space box (y up: top > bottom). */
|
|
39
74
|
export interface Box {
|
|
@@ -50,7 +85,62 @@ export declare function boxHandles(T: Xform, b: Box): {
|
|
|
50
85
|
}[];
|
|
51
86
|
/** Bounding box of a pen stroke or revision cloud. */
|
|
52
87
|
export declare function noteBox(n: Note): Box | null;
|
|
53
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* A note's text is set at one size (NOTE_TEXT_SIZE px) and the whole note scaled to its size on the drawing: its
|
|
90
|
+
* letters are `ts` inches tall (NOTE_TEXT_IN unless set, from NOTE_TEXT_MIN_IN to NOTE_TEXT_MAX_IN), so it zooms with
|
|
91
|
+
* the drawing rather than staying one size on screen. (Set at one size, the text is measured — and on a device its
|
|
92
|
+
* font made — once, not at every step of a zoom.)
|
|
93
|
+
*/
|
|
94
|
+
export { NOTE_TEXT_SIZE, NOTE_TEXT_IN, NOTE_TEXT_MIN_IN, NOTE_TEXT_MAX_IN, noteTextIn } from '../model/markup';
|
|
95
|
+
export declare const noteFont: (size: number) => string;
|
|
96
|
+
/** What measures text: a drawing context, or anything that sets a font and measures with it. */
|
|
97
|
+
export interface TextMeasure {
|
|
98
|
+
font: string;
|
|
99
|
+
measureText(s: string): {
|
|
100
|
+
width: number;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export declare function setTextMeasure(m: TextMeasure | null): void;
|
|
104
|
+
export declare const textMeasure: () => TextMeasure;
|
|
105
|
+
/**
|
|
106
|
+
* A note's box on screen at its size: left / top corner, width, height; `mid` is the text's middle line, `k` its scale
|
|
107
|
+
* from the unscaled size (padding and corners grow with it). Measured with `c` (the drawing's context when drawing).
|
|
108
|
+
*/
|
|
109
|
+
export declare function noteTextBox(T: Xform, n: Note, c?: TextMeasure): {
|
|
110
|
+
x: number;
|
|
111
|
+
y: number;
|
|
112
|
+
w: number;
|
|
113
|
+
h: number;
|
|
114
|
+
mid: number;
|
|
115
|
+
k: number;
|
|
116
|
+
size: number;
|
|
117
|
+
};
|
|
118
|
+
/** How much a link's tile shows on the screen: all of it, its picture or icon alone, or a pin. */
|
|
119
|
+
export type LinkLod = 'full' | 'compact' | 'pin';
|
|
120
|
+
/** A tile on the screen: its square (top left, side), its middle, how much it shows, and its scale from the layout. */
|
|
121
|
+
export interface LinkBox {
|
|
122
|
+
x: number;
|
|
123
|
+
y: number;
|
|
124
|
+
s: number;
|
|
125
|
+
cx: number;
|
|
126
|
+
cy: number;
|
|
127
|
+
lod: LinkLod;
|
|
128
|
+
k: number;
|
|
129
|
+
}
|
|
130
|
+
export declare function linkBox(T: Xform, n: Note): LinkBox;
|
|
131
|
+
/** Where a leader leaves the tile toward screen point tx, ty: the edge of its square (null: the point's under it). */
|
|
132
|
+
export declare function leaderStart(b: LinkBox, tx: number, ty: number): [number, number] | null;
|
|
133
|
+
/** A tile's leader on the screen, from the tile's edge to its tip (null: none, or its tip is under the tile). */
|
|
134
|
+
export declare function leaderPath(T: Xform, n: Note, b?: LinkBox): [[number, number], [number, number]] | null;
|
|
135
|
+
/** A tile's handle: its leader's tip (tiles are sized together, by the Tile size slider, not one by one). */
|
|
136
|
+
export declare function linkHandles(T: Xform, n: Note): {
|
|
137
|
+
part: string;
|
|
138
|
+
x: number;
|
|
139
|
+
y: number;
|
|
140
|
+
}[];
|
|
141
|
+
/** A tile's box and leader tip on the screen, for fitting and box selection: left, top, right, bottom. */
|
|
142
|
+
export declare function linkExtent(T: Xform, n: Note): [number, number, number, number];
|
|
143
|
+
/** Handles for adjusting markup: a line's or arrow's ends ('p0', 'p1'), the box around a cloud, shape or pen stroke, a note's corners. */
|
|
54
144
|
export declare function noteHandles(T: Xform, n: Note): {
|
|
55
145
|
part: string;
|
|
56
146
|
x: number;
|
|
@@ -70,10 +160,33 @@ export declare function setImageResolver(fn: ((ref: string) => Promise<string>)
|
|
|
70
160
|
export declare const isStoredRef: (src: string) => boolean;
|
|
71
161
|
/** `to` is the same picture as `from` (an upload's reference for the local copy): reuse what's loaded, no flicker. */
|
|
72
162
|
export declare function aliasImage(from: string, to: string): void;
|
|
163
|
+
/** A new frame is being drawn: let go of the least recently used past those kept, if the last frame didn't draw them. */
|
|
164
|
+
export declare function nextThumbFrame(): void;
|
|
165
|
+
export declare function linkThumb(src: string): ImageLike;
|
|
73
166
|
export declare function imgCache(src: string | undefined): ImageLike | null;
|
|
74
167
|
export declare function setFrameRenderer(fn: (() => void) | null): void;
|
|
168
|
+
/** Whether the drawing (not only its moving parts) has changed since the host last kept it; the host then marks it kept. */
|
|
169
|
+
export declare const drawingChanged: () => boolean;
|
|
170
|
+
export declare function markDrawingKept(): void;
|
|
171
|
+
/** Whether the photos and plans under the drawing have changed since the host last kept them; it then marks them kept. */
|
|
172
|
+
export declare const imagesChanged: () => boolean;
|
|
173
|
+
export declare function markImagesKept(): void;
|
|
174
|
+
/**
|
|
175
|
+
* Paint a frame that's waiting, now. A press is tested against the tap targets as last painted (the chips), so one that
|
|
176
|
+
* comes between an edit and the next frame — a quick tap, a slow frame — would land on where a chip was, not where it is.
|
|
177
|
+
*/
|
|
178
|
+
export declare function paintPending(): void;
|
|
179
|
+
/**
|
|
180
|
+
* The view as the host moved it itself — a device pans and zooms on its UI thread, at the screen's rate, and tells
|
|
181
|
+
* this thread where it got to: take it, and draw the plan there on the next frame.
|
|
182
|
+
*/
|
|
183
|
+
export declare function setViewFromHost(s: number, cx: number, cy: number): void;
|
|
75
184
|
/** Repaint on the next animation frame (several requests in one frame paint once). */
|
|
76
185
|
export declare function requestDraw(): void;
|
|
186
|
+
/** Only the page's photos and plans changed (one turned, faded, moved or sized): the drawing over them can be reused. */
|
|
187
|
+
export declare function requestImageDraw(): void;
|
|
188
|
+
/** Only the moving parts changed (a tool's preview, the snap marker, the selection box): the rest can be reused. */
|
|
189
|
+
export declare function requestOverlayDraw(): void;
|
|
77
190
|
/** The plan's size in display points, where no browser canvas measures it (a device). */
|
|
78
191
|
export declare function setViewSize(W: number, H: number): void;
|
|
79
192
|
export declare function resize(): void;
|
package/dist/render/view.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { clamp } from '../model/geom';
|
|
2
|
+
import { faceEdit } from '../model/faces';
|
|
2
3
|
import { page, SK } from '../model/store';
|
|
4
|
+
import { CLOSED_SHAPES, isSegment, NOTE_TEXT_SIZE, noteTextIn } from '../model/markup';
|
|
5
|
+
import { LINK_FULL_PX, LINK_PIN_PX, LINK_TILE_PX, linkSide } from '../model/links';
|
|
3
6
|
import { draw } from './draw';
|
|
4
7
|
import { onSessionReset, sessionState } from '../model/session';
|
|
5
8
|
import { refreshDrawerSoon } from '../editor/ports';
|
|
9
|
+
import { FONT } from './theme';
|
|
10
|
+
import { ZOOM_MAX, ZOOM_MIN } from './navigate';
|
|
6
11
|
export const cvs = sessionState(() => ({
|
|
7
12
|
cv: null,
|
|
8
13
|
ctx: null,
|
|
@@ -14,8 +19,13 @@ export function initCanvas(el) {
|
|
|
14
19
|
cvs.cv = el;
|
|
15
20
|
cvs.ctx = el.getContext('2d');
|
|
16
21
|
}
|
|
22
|
+
/** The pan/zoom of each wall face that's been open, by page and face. */
|
|
23
|
+
const faceViews = sessionState(() => ({}));
|
|
24
|
+
/** The view of what's being edited: the page's plan, or the wall face open. */
|
|
17
25
|
export function V() {
|
|
18
26
|
const pg = page();
|
|
27
|
+
if (faceEdit.key)
|
|
28
|
+
return (faceViews[pg.id + '|' + faceEdit.key] ??= { cx: 0, cy: 48, s: 2 });
|
|
19
29
|
if (!pg.view)
|
|
20
30
|
pg.view = { cx: 84, cy: 120, s: 2 };
|
|
21
31
|
return pg.view;
|
|
@@ -32,16 +42,16 @@ export function s2w(sx, sy) {
|
|
|
32
42
|
export function zoomAt(sx, sy, f) {
|
|
33
43
|
const v = V();
|
|
34
44
|
const [wx, wy] = s2w(sx, sy);
|
|
35
|
-
v.s = clamp(v.s * f,
|
|
45
|
+
v.s = clamp(v.s * f, ZOOM_MIN, ZOOM_MAX);
|
|
36
46
|
v.cx = wx - (sx - cvs.W / 2) / v.s;
|
|
37
47
|
v.cy = wy + (sy - cvs.H / 2) / v.s;
|
|
38
|
-
|
|
48
|
+
requestViewDraw();
|
|
39
49
|
}
|
|
40
50
|
export function panBy(dx, dy) {
|
|
41
51
|
const v = V();
|
|
42
52
|
v.cx -= dx / v.s;
|
|
43
53
|
v.cy += dy / v.s;
|
|
44
|
-
|
|
54
|
+
requestViewDraw();
|
|
45
55
|
}
|
|
46
56
|
export function sketchBounds(sk, inclOrigin = true) {
|
|
47
57
|
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
@@ -62,6 +72,14 @@ export function sketchBounds(sk, inclOrigin = true) {
|
|
|
62
72
|
for (const n of sk.notes) {
|
|
63
73
|
if (n.pts)
|
|
64
74
|
n.pts.forEach(p => add(p[0], p[1]));
|
|
75
|
+
else if (n.type === 'link') {
|
|
76
|
+
// its tile, and what its leader points at
|
|
77
|
+
const h = linkSide(n) / 2, x = n.x ?? 0, y = n.y ?? 0;
|
|
78
|
+
add(x - h, y - h);
|
|
79
|
+
add(x + h, y + h);
|
|
80
|
+
if (n.leader)
|
|
81
|
+
add(n.leader[0], n.leader[1]);
|
|
82
|
+
}
|
|
65
83
|
else
|
|
66
84
|
add(n.x, n.y);
|
|
67
85
|
}
|
|
@@ -69,14 +87,23 @@ export function sketchBounds(sk, inclOrigin = true) {
|
|
|
69
87
|
return null;
|
|
70
88
|
return { x0, y0, x1, y1 };
|
|
71
89
|
}
|
|
90
|
+
/** The open wall's face, for the wall view to fit (registered by editor/elevation.ts; null on the plan). */
|
|
91
|
+
let wallBox = () => null;
|
|
92
|
+
export function setWallBox(fn) { wallBox = fn; }
|
|
72
93
|
export function fitView() {
|
|
73
|
-
const sk = SK(), pg = page();
|
|
94
|
+
const sk = SK(), pg = page(), wallFace = wallBox();
|
|
74
95
|
let b = sketchBounds(sk);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
b = b ? { x0: Math.min(b.x0,
|
|
79
|
-
|
|
96
|
+
// the wall view fits the wall itself (and what's drawn on it); the page's photos and plans are under the plan, not
|
|
97
|
+
// on the wall — counting them zoomed a wall out to take in a plan nobody could see there
|
|
98
|
+
if (faceEdit.key && wallFace)
|
|
99
|
+
b = b ? { x0: Math.min(b.x0, wallFace.x0), y0: Math.min(b.y0, wallFace.y0), x1: Math.max(b.x1, wallFace.x1), y1: Math.max(b.y1, wallFace.y1) } : wallFace;
|
|
100
|
+
if (!faceEdit.key)
|
|
101
|
+
for (const u of pg.underlays) {
|
|
102
|
+
// (as it sits, turned)
|
|
103
|
+
const cs = underlayCorners(u), xs = cs.map(q => q.x), ys = cs.map(q => q.y);
|
|
104
|
+
const bb = { x0: Math.min(...xs), y0: Math.min(...ys), x1: Math.max(...xs), y1: Math.max(...ys) };
|
|
105
|
+
b = b ? { x0: Math.min(b.x0, bb.x0), y0: Math.min(b.y0, bb.y0), x1: Math.max(b.x1, bb.x1), y1: Math.max(b.y1, bb.y1) } : bb;
|
|
106
|
+
}
|
|
80
107
|
const v = V();
|
|
81
108
|
if (!b || (b.x1 - b.x0 < 1 && b.y1 - b.y0 < 1)) {
|
|
82
109
|
v.cx = 0;
|
|
@@ -86,8 +113,10 @@ export function fitView() {
|
|
|
86
113
|
return;
|
|
87
114
|
}
|
|
88
115
|
const { W, H } = cvs;
|
|
89
|
-
const phone = W < 760;
|
|
90
|
-
|
|
116
|
+
const phone = W < 760, wall = faceEdit.key != null;
|
|
117
|
+
// a wall face: room for the dimensions along the floor and beside it, and for the key plan (top right)
|
|
118
|
+
const L = phone ? 78 : 110, R = wall ? (phone ? 64 : Math.max(110, keyPlanSize(W) + 40)) : phone ? 28 : 110;
|
|
119
|
+
const Tp = phone ? 200 : wall ? 110 : 150, Bt = wall ? (phone ? 210 : 190) : 120;
|
|
91
120
|
const bw = Math.max(b.x1 - b.x0, 24), bh = Math.max(b.y1 - b.y0, 24);
|
|
92
121
|
v.s = clamp(Math.min((W - L - R) / bw, (H - Tp - Bt) / bh), 0.08, 60);
|
|
93
122
|
const scx = (L + W - R) / 2, scy = (Tp + H - Bt) / 2;
|
|
@@ -95,6 +124,8 @@ export function fitView() {
|
|
|
95
124
|
v.cy = (b.y0 + b.y1) / 2 + (scy - H / 2) / v.s;
|
|
96
125
|
requestDraw();
|
|
97
126
|
}
|
|
127
|
+
/** Size of the wall view's key plan (top right), pixels. */
|
|
128
|
+
export const keyPlanSize = (W) => Math.min(150, Math.max(96, W * 0.16));
|
|
98
129
|
/** Height of an image underlay in inches: its own, or from the picture's aspect ratio. */
|
|
99
130
|
export function underlayH(u) {
|
|
100
131
|
if (u.h)
|
|
@@ -102,9 +133,39 @@ export function underlayH(u) {
|
|
|
102
133
|
const img = imgCache(u.src);
|
|
103
134
|
return img && img.naturalWidth ? (u.w * img.naturalHeight) / img.naturalWidth : u.w * 0.7;
|
|
104
135
|
}
|
|
105
|
-
|
|
136
|
+
/*
|
|
137
|
+
An image turned (its `rot`) turns about its middle: x, y, w and h are its box before it's turned. These give its
|
|
138
|
+
points as they sit turned. The world's y runs up, so a turn clockwise as it's seen is the other way round in it.
|
|
139
|
+
*/
|
|
140
|
+
const turnOf = (u) => (-(u.rot ?? 0) * Math.PI) / 180;
|
|
141
|
+
/** An image's middle, inches. */
|
|
142
|
+
export function underlayMid(u) {
|
|
143
|
+
return { x: u.x + u.w / 2, y: u.y - underlayH(u) / 2 };
|
|
144
|
+
}
|
|
145
|
+
/** The point `lx` across and `ly` up from an image's middle in its own (unturned) frame, as it sits turned (inches). */
|
|
146
|
+
export function underlayAt(u, lx, ly) {
|
|
147
|
+
const m = underlayMid(u), a = turnOf(u), c = Math.cos(a), s = Math.sin(a);
|
|
148
|
+
return { x: m.x + lx * c - ly * s, y: m.y + lx * s + ly * c };
|
|
149
|
+
}
|
|
150
|
+
/** A point (inches) as across and up from an image's middle, in its own unturned frame. */
|
|
151
|
+
export function underlayLocal(u, x, y) {
|
|
152
|
+
const m = underlayMid(u), a = turnOf(u), c = Math.cos(a), s = Math.sin(a), dx = x - m.x, dy = y - m.y;
|
|
153
|
+
return { x: dx * c + dy * s, y: -dx * s + dy * c };
|
|
154
|
+
}
|
|
155
|
+
/** Where an image's handles sit in its own frame (as fractions of its half width and half height), in boxHandles' order. */
|
|
156
|
+
const HANDLES = [['nw', -1, 1], ['n', 0, 1], ['ne', 1, 1], ['e', 1, 0], ['se', 1, -1], ['s', 0, -1], ['sw', -1, -1], ['w', -1, 0]];
|
|
157
|
+
/** An image's corners as it sits turned: top left, top right, bottom right, bottom left (inches). */
|
|
158
|
+
export function underlayCorners(u) {
|
|
159
|
+
const hw = u.w / 2, hh = underlayH(u) / 2;
|
|
160
|
+
return [[-1, 1], [1, 1], [1, -1], [-1, -1]].map(([i, j]) => underlayAt(u, i * hw, j * hh));
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Screen positions of an underlay's resize handles: corners scale, side handles stretch. `ang`: how far the image is
|
|
164
|
+
* turned on screen (radians clockwise), for the side handles' bars to lie along its sides.
|
|
165
|
+
*/
|
|
106
166
|
export function underlayHandles(T, u) {
|
|
107
|
-
|
|
167
|
+
const hw = u.w / 2, hh = underlayH(u) / 2, ang = ((u.rot ?? 0) * Math.PI) / 180;
|
|
168
|
+
return HANDLES.map(([part, i, j]) => { const p = underlayAt(u, i * hw, j * hh), [x, y] = S(T, p.x, p.y); return { part, x, y, ang }; });
|
|
108
169
|
}
|
|
109
170
|
/** Screen positions of the 4 corner + 4 side handles of a box. */
|
|
110
171
|
export function boxHandles(T, b) {
|
|
@@ -122,16 +183,96 @@ export function noteBox(n) {
|
|
|
122
183
|
const xs = n.pts.map(p => p[0]), ys = n.pts.map(p => p[1]);
|
|
123
184
|
return { left: Math.min(...xs), right: Math.max(...xs), top: Math.max(...ys), bottom: Math.min(...ys) };
|
|
124
185
|
}
|
|
125
|
-
/**
|
|
186
|
+
/**
|
|
187
|
+
* A note's text is set at one size (NOTE_TEXT_SIZE px) and the whole note scaled to its size on the drawing: its
|
|
188
|
+
* letters are `ts` inches tall (NOTE_TEXT_IN unless set, from NOTE_TEXT_MIN_IN to NOTE_TEXT_MAX_IN), so it zooms with
|
|
189
|
+
* the drawing rather than staying one size on screen. (Set at one size, the text is measured — and on a device its
|
|
190
|
+
* font made — once, not at every step of a zoom.)
|
|
191
|
+
*/
|
|
192
|
+
export { NOTE_TEXT_SIZE, NOTE_TEXT_IN, NOTE_TEXT_MIN_IN, NOTE_TEXT_MAX_IN, noteTextIn } from '../model/markup';
|
|
193
|
+
export const noteFont = (size) => '600 ' + size + 'px ' + FONT;
|
|
194
|
+
/**
|
|
195
|
+
* What measures text outside drawing a frame (picking a note, its handles, a box select): the browser's canvas, or on
|
|
196
|
+
* a device — which has no browser canvas — the host's (Skia, with the drawing's own fonts: skia/host.ts), so a note
|
|
197
|
+
* is picked by the same box it's drawn in. Failing both, an estimate rather than a crash.
|
|
198
|
+
*/
|
|
199
|
+
let hostMeasure = null;
|
|
200
|
+
export function setTextMeasure(m) { hostMeasure = m; }
|
|
201
|
+
const estimate = {
|
|
202
|
+
font: '',
|
|
203
|
+
measureText(s) { const size = Number(/([\d.]+)px/.exec(this.font)?.[1] ?? 14); return { width: s.length * size * 0.6 }; },
|
|
204
|
+
};
|
|
205
|
+
export const textMeasure = () => cvs.ctx ?? hostMeasure ?? estimate;
|
|
206
|
+
/**
|
|
207
|
+
* A note's box on screen at its size: left / top corner, width, height; `mid` is the text's middle line, `k` its scale
|
|
208
|
+
* from the unscaled size (padding and corners grow with it). Measured with `c` (the drawing's context when drawing).
|
|
209
|
+
*/
|
|
210
|
+
export function noteTextBox(T, n, c = textMeasure()) {
|
|
211
|
+
const size = noteTextIn(n) * T.s, k = size / NOTE_TEXT_SIZE, [x, mid] = S(T, n.x, n.y);
|
|
212
|
+
c.font = noteFont(NOTE_TEXT_SIZE);
|
|
213
|
+
const w = (c.measureText(n.text).width + 16) * k;
|
|
214
|
+
return { x, y: mid - 14 * k, w, h: 28 * k, mid, k, size };
|
|
215
|
+
}
|
|
216
|
+
export function linkBox(T, n) {
|
|
217
|
+
const [cx, cy] = S(T, n.x ?? 0, n.y ?? 0), side = linkSide(n) * T.s;
|
|
218
|
+
const lod = side >= LINK_FULL_PX ? 'full' : side >= LINK_PIN_PX ? 'compact' : 'pin';
|
|
219
|
+
const s = Math.max(LINK_PIN_PX, side);
|
|
220
|
+
return { x: cx - s / 2, y: cy - s / 2, s, cx, cy, lod, k: s / LINK_TILE_PX };
|
|
221
|
+
}
|
|
222
|
+
/** Where a leader leaves the tile toward screen point tx, ty: the edge of its square (null: the point's under it). */
|
|
223
|
+
export function leaderStart(b, tx, ty) {
|
|
224
|
+
const dx = tx - b.cx, dy = ty - b.cy, h = b.s / 2, m = Math.max(Math.abs(dx), Math.abs(dy));
|
|
225
|
+
if (m <= h)
|
|
226
|
+
return null;
|
|
227
|
+
return [b.cx + (dx * h) / m, b.cy + (dy * h) / m];
|
|
228
|
+
}
|
|
229
|
+
/** A tile's leader on the screen, from the tile's edge to its tip (null: none, or its tip is under the tile). */
|
|
230
|
+
export function leaderPath(T, n, b = linkBox(T, n)) {
|
|
231
|
+
if (!n.leader)
|
|
232
|
+
return null;
|
|
233
|
+
const tip = S(T, n.leader[0], n.leader[1]), start = leaderStart(b, tip[0], tip[1]);
|
|
234
|
+
return start ? [start, tip] : null;
|
|
235
|
+
}
|
|
236
|
+
/** A tile's handle: its leader's tip (tiles are sized together, by the Tile size slider, not one by one). */
|
|
237
|
+
export function linkHandles(T, n) {
|
|
238
|
+
if (!n.leader)
|
|
239
|
+
return [];
|
|
240
|
+
const [x, y] = S(T, n.leader[0], n.leader[1]);
|
|
241
|
+
return [{ part: 'tip', x, y }];
|
|
242
|
+
}
|
|
243
|
+
/** A tile's box and leader tip on the screen, for fitting and box selection: left, top, right, bottom. */
|
|
244
|
+
export function linkExtent(T, n) {
|
|
245
|
+
const b = linkBox(T, n);
|
|
246
|
+
let l = b.x, t = b.y, r = b.x + b.s, bt = b.y + b.s;
|
|
247
|
+
if (n.leader) {
|
|
248
|
+
const [x, y] = S(T, n.leader[0], n.leader[1]);
|
|
249
|
+
l = Math.min(l, x);
|
|
250
|
+
r = Math.max(r, x);
|
|
251
|
+
t = Math.min(t, y);
|
|
252
|
+
bt = Math.max(bt, y);
|
|
253
|
+
}
|
|
254
|
+
return [l, t, r, bt];
|
|
255
|
+
}
|
|
256
|
+
/** Handles for adjusting markup: a line's or arrow's ends ('p0', 'p1'), the box around a cloud, shape or pen stroke, a note's corners. */
|
|
126
257
|
export function noteHandles(T, n) {
|
|
127
|
-
if (n.type === '
|
|
258
|
+
if (n.type === 'link')
|
|
259
|
+
return linkHandles(T, n);
|
|
260
|
+
if (isSegment(n) && n.pts && n.pts.length >= 2) {
|
|
128
261
|
const last = n.pts.length - 1;
|
|
129
262
|
return [{ part: 'p0', p: n.pts[0] }, { part: 'p' + last, p: n.pts[last] }].map(({ part, p }) => { const [x, y] = S(T, p[0], p[1]); return { part, x, y }; });
|
|
130
263
|
}
|
|
131
|
-
if (n.type === 'cloud' || n.type === 'pen') {
|
|
264
|
+
if (n.type === 'cloud' || n.type === 'pen' || CLOSED_SHAPES.has(n.type)) {
|
|
132
265
|
const b = noteBox(n);
|
|
133
266
|
return b ? boxHandles(T, b) : [];
|
|
134
267
|
}
|
|
268
|
+
if (n.type === 'text' && n.text) {
|
|
269
|
+
// a note scales as a whole (its text can't stretch): corners only
|
|
270
|
+
const b = noteTextBox(T, n);
|
|
271
|
+
return [
|
|
272
|
+
{ part: 'nw', x: b.x, y: b.y }, { part: 'ne', x: b.x + b.w, y: b.y },
|
|
273
|
+
{ part: 'se', x: b.x + b.w, y: b.y + b.h }, { part: 'sw', x: b.x, y: b.y + b.h },
|
|
274
|
+
];
|
|
275
|
+
}
|
|
135
276
|
return [];
|
|
136
277
|
}
|
|
137
278
|
const browserImage = (src, onLoad) => {
|
|
@@ -142,7 +283,7 @@ const browserImage = (src, onLoad) => {
|
|
|
142
283
|
};
|
|
143
284
|
let loadImage = browserImage;
|
|
144
285
|
/** Load pictures some other way from now on (null: the browser's). */
|
|
145
|
-
export function setImageLoader(fn) { loadImage = fn || browserImage; imgs.clear(); }
|
|
286
|
+
export function setImageLoader(fn) { loadImage = fn || browserImage; imgs.clear(); thumbs.clear(); }
|
|
146
287
|
/** Turns a stored reference (`stored:…`, kept by a drawing in place of a URL) into a URL to load. */
|
|
147
288
|
let resolveStored = null;
|
|
148
289
|
export function setImageResolver(fn) { resolveStored = fn; imgs.clear(); }
|
|
@@ -154,6 +295,33 @@ export function aliasImage(from, to) {
|
|
|
154
295
|
if (im && !imgs.has(to))
|
|
155
296
|
imgs.set(to, im);
|
|
156
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Small pictures of linked files (a link tile's), by address: kept apart from the page's photos and plans, and only the
|
|
300
|
+
* last few dozen used — a drawing can link many — each drawn again as it arrives. What's let go goes between frames,
|
|
301
|
+
* and never what the last frame drew (so many on the screen at once don't load each other out, again and again).
|
|
302
|
+
*/
|
|
303
|
+
const thumbs = new Map(), THUMBS_KEPT = 64;
|
|
304
|
+
let thumbFrame = 0;
|
|
305
|
+
/** A new frame is being drawn: let go of the least recently used past those kept, if the last frame didn't draw them. */
|
|
306
|
+
export function nextThumbFrame() {
|
|
307
|
+
for (const [k, o] of thumbs) {
|
|
308
|
+
if (thumbs.size <= THUMBS_KEPT || o.used === thumbFrame)
|
|
309
|
+
break;
|
|
310
|
+
thumbs.delete(k);
|
|
311
|
+
}
|
|
312
|
+
thumbFrame++;
|
|
313
|
+
}
|
|
314
|
+
export function linkThumb(src) {
|
|
315
|
+
let t = thumbs.get(src);
|
|
316
|
+
// (the most recently used last: the first is the one to let go)
|
|
317
|
+
if (t)
|
|
318
|
+
thumbs.delete(src);
|
|
319
|
+
else
|
|
320
|
+
t = { im: loadImage(src, () => requestDraw()), used: 0 };
|
|
321
|
+
t.used = thumbFrame;
|
|
322
|
+
thumbs.set(src, t);
|
|
323
|
+
return t.im;
|
|
324
|
+
}
|
|
157
325
|
export function imgCache(src) {
|
|
158
326
|
if (!src)
|
|
159
327
|
return null;
|
|
@@ -179,17 +347,74 @@ let drawFrame = 0;
|
|
|
179
347
|
/** What paints a frame: the browser canvas by default; a host can draw elsewhere (e.g. Skia on a device). */
|
|
180
348
|
let paint = () => draw();
|
|
181
349
|
export function setFrameRenderer(fn) { paint = fn || (() => draw()); }
|
|
182
|
-
|
|
183
|
-
|
|
350
|
+
/*
|
|
351
|
+
What the next frame has to redraw, for a host that keeps the drawing as a picture (see PlanCanvas.native): the
|
|
352
|
+
drawing itself only when something in it changed (requestDraw), or when the view moved (the host compares the view
|
|
353
|
+
it last drew at); otherwise just the moving parts, as a tool follows the pointer (requestOverlayDraw). The page's
|
|
354
|
+
photos and plans are kept apart, under it: turning, fading, moving or sizing one redraws only them
|
|
355
|
+
(requestImageDraw), not every wall and dimension over them. A host that redraws everything each frame (the
|
|
356
|
+
browser) ignores this.
|
|
357
|
+
*/
|
|
358
|
+
const frame = { drawingChanged: true, imagesChanged: true };
|
|
359
|
+
/** Whether the drawing (not only its moving parts) has changed since the host last kept it; the host then marks it kept. */
|
|
360
|
+
export const drawingChanged = () => frame.drawingChanged;
|
|
361
|
+
export function markDrawingKept() { frame.drawingChanged = false; }
|
|
362
|
+
/** Whether the photos and plans under the drawing have changed since the host last kept them; it then marks them kept. */
|
|
363
|
+
export const imagesChanged = () => frame.imagesChanged;
|
|
364
|
+
export function markImagesKept() { frame.imagesChanged = false; }
|
|
365
|
+
function schedule() {
|
|
184
366
|
if (drawFrame)
|
|
185
367
|
return;
|
|
186
368
|
drawFrame = requestAnimationFrame(() => { drawFrame = 0; paint(); });
|
|
187
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* Paint a frame that's waiting, now. A press is tested against the tap targets as last painted (the chips), so one that
|
|
372
|
+
* comes between an edit and the next frame — a quick tap, a slow frame — would land on where a chip was, not where it is.
|
|
373
|
+
*/
|
|
374
|
+
export function paintPending() {
|
|
375
|
+
if (!drawFrame)
|
|
376
|
+
return;
|
|
377
|
+
cancelAnimationFrame(drawFrame);
|
|
378
|
+
drawFrame = 0;
|
|
379
|
+
paint();
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* The view as the host moved it itself — a device pans and zooms on its UI thread, at the screen's rate, and tells
|
|
383
|
+
* this thread where it got to: take it, and draw the plan there on the next frame.
|
|
384
|
+
*/
|
|
385
|
+
export function setViewFromHost(s, cx, cy) {
|
|
386
|
+
const v = V();
|
|
387
|
+
v.s = s;
|
|
388
|
+
v.cx = cx;
|
|
389
|
+
v.cy = cy;
|
|
390
|
+
schedule();
|
|
391
|
+
}
|
|
392
|
+
/** Repaint on the next animation frame (several requests in one frame paint once). */
|
|
393
|
+
export function requestDraw() {
|
|
394
|
+
frame.drawingChanged = true;
|
|
395
|
+
frame.imagesChanged = true;
|
|
396
|
+
schedule();
|
|
397
|
+
}
|
|
398
|
+
/** Only the page's photos and plans changed (one turned, faded, moved or sized): the drawing over them can be reused. */
|
|
399
|
+
export function requestImageDraw() {
|
|
400
|
+
frame.imagesChanged = true;
|
|
401
|
+
schedule();
|
|
402
|
+
}
|
|
403
|
+
/** Only the moving parts changed (a tool's preview, the snap marker, the selection box): the rest can be reused. */
|
|
404
|
+
export function requestOverlayDraw() { schedule(); }
|
|
405
|
+
/**
|
|
406
|
+
* The view panned or zoomed: draw the next frame there. The drawing itself didn't change — a host that keeps it sees
|
|
407
|
+
* the view moved, and draws it again at the new one, with nothing waiting for the view to settle first.
|
|
408
|
+
*/
|
|
409
|
+
function requestViewDraw() { schedule(); }
|
|
188
410
|
onSessionReset(() => {
|
|
189
411
|
if (drawFrame)
|
|
190
412
|
cancelAnimationFrame(drawFrame);
|
|
191
413
|
drawFrame = 0;
|
|
414
|
+
frame.drawingChanged = true;
|
|
415
|
+
frame.imagesChanged = true;
|
|
192
416
|
imgs.clear();
|
|
417
|
+
thumbs.clear();
|
|
193
418
|
});
|
|
194
419
|
/** The plan's size in display points, where no browser canvas measures it (a device). */
|
|
195
420
|
export function setViewSize(W, H) {
|