@pascal-app/editor 1.0.1 → 1.0.4
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/package.json +16 -9
- package/src/components/editor/action-menu-button.tsx +83 -0
- package/src/components/editor/bake-thumbnail.tsx +315 -4
- package/src/components/editor/capture-level-picker.tsx +72 -0
- package/src/components/editor/custom-camera-controls.tsx +221 -128
- package/src/components/editor/delete-confirmation-dialog.tsx +297 -7
- package/src/components/editor/duplicate-node.ts +244 -0
- package/src/components/editor/editor-layout-mobile.tsx +37 -25
- package/src/components/editor/editor-layout-v2.tsx +131 -58
- package/src/components/editor/export-manager.tsx +34 -8
- package/src/components/editor/first-person/build-collider-world.test.ts +51 -0
- package/src/components/editor/first-person/build-collider-world.ts +6 -2
- package/src/components/editor/first-person/mechanism-targets.test.ts +75 -0
- package/src/components/editor/first-person/mechanism-targets.ts +36 -0
- package/src/components/editor/first-person-controls.tsx +205 -2
- package/src/components/editor/floating-action-menu.tsx +63 -207
- package/src/components/editor/floor-aware-camera-controls.test.ts +442 -0
- package/src/components/editor/floor-aware-camera-controls.ts +324 -0
- package/src/components/editor/floor-openings-overlay.tsx +243 -0
- package/src/components/editor/floor-region-controls.tsx +846 -0
- package/src/components/editor/floor-region-webgpu-materials.test.ts +12 -0
- package/src/components/editor/floor-surface.test.ts +165 -0
- package/src/components/editor/floor-surface.ts +182 -0
- package/src/components/editor/floorplan-background-selection.test.ts +11 -2
- package/src/components/editor/floorplan-background-selection.ts +1 -3
- package/src/components/editor/floorplan-panel.tsx +536 -487
- package/src/components/editor/footprint-height-handle.tsx +204 -0
- package/src/components/editor/grid.tsx +31 -7
- package/src/components/editor/group-actions.ts +59 -11
- package/src/components/editor/group-floating-action-menu.tsx +19 -9
- package/src/components/editor/group-move-3d.ts +4 -6
- package/src/components/editor/group-rotate-handle.tsx +81 -35
- package/src/components/editor/group-selection-box-3d.tsx +25 -17
- package/src/components/editor/group-transform-shared.test.ts +126 -1
- package/src/components/editor/group-transform-shared.ts +79 -73
- package/src/components/editor/handles/handle-arrow.tsx +72 -2
- package/src/components/editor/handles/handle-hit-priority.test.tsx +63 -0
- package/src/components/editor/handles/handle-hit-priority.tsx +37 -0
- package/src/components/editor/handles/swallow-next-click.test.ts +28 -0
- package/src/components/editor/handles/use-handle-drag.ts +272 -131
- package/src/components/editor/index.tsx +316 -176
- package/src/components/editor/join-walls-pill.tsx +44 -0
- package/src/components/editor/node-action-menu.tsx +70 -70
- package/src/components/editor/node-arrow-handles.tsx +167 -14
- package/src/components/editor/open-wall-ends-3d-layer.tsx +273 -0
- package/src/components/editor/open-wall-ends-hint.tsx +89 -0
- package/src/components/editor/quick-measurement-hud.tsx +3 -2
- package/src/components/editor/registry-action-contributions.test.tsx +63 -0
- package/src/components/editor/registry-action-contributions.tsx +132 -0
- package/src/components/editor/room-controls.tsx +847 -0
- package/src/components/editor/room-floor-highlight.tsx +128 -0
- package/src/components/editor/room-handles-2d.tsx +354 -0
- package/src/components/editor/room-handles.tsx +675 -0
- package/src/components/editor/room-highlight.tsx +225 -0
- package/src/components/editor/room-labels.test.ts +357 -0
- package/src/components/editor/room-labels.tsx +990 -0
- package/src/components/editor/room-transform-ghost.tsx +256 -0
- package/src/components/editor/room-webgpu-materials.test.ts +27 -0
- package/src/components/editor/selection-manager.tsx +451 -317
- package/src/components/editor/slab-hole-highlights.tsx +3 -0
- package/src/components/editor/snapshot-capture-overlay.tsx +4 -0
- package/src/components/editor/snapshot-capture.test.ts +52 -4
- package/src/components/editor/snapshot-capture.ts +30 -5
- package/src/components/editor/thumbnail-generator.test.ts +308 -0
- package/src/components/editor/thumbnail-generator.tsx +1035 -229
- package/src/components/editor/use-floorplan-background-placement.ts +31 -11
- package/src/components/editor/vector-edge-extractor.tsx +121 -0
- package/src/components/editor/views/host-view.tsx +61 -0
- package/src/components/editor/views/plugins-view.tsx +670 -0
- package/src/components/editor/views/scene-region.tsx +59 -0
- package/src/components/editor/views/use-editor-views.ts +57 -0
- package/src/components/editor/views/view-bar.tsx +220 -0
- package/src/components/editor/wall-justification.test.ts +140 -0
- package/src/components/editor/wall-move-side-handles.tsx +114 -163
- package/src/components/editor/wall-opening-highlights.tsx +2 -3
- package/src/components/editor/wall-push-arrow.tsx +92 -0
- package/src/components/editor/wall-region-handles.tsx +509 -0
- package/src/components/editor/wall-snap-beacon-layer.tsx +3 -0
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +8 -4
- package/src/components/editor-2d/floorplan-group-move.tsx +17 -24
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.test.tsx +86 -0
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +290 -0
- package/src/components/editor-2d/floorplan-registered-tool-layer.tsx +33 -12
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +11 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +242 -126
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.test.ts +156 -0
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +278 -0
- package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +77 -0
- package/src/components/editor-2d/renderers/floorplan-draft-measurement.tsx +166 -0
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +4 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +87 -8
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +355 -245
- package/src/components/editor-2d/svg-paths.ts +12 -15
- package/src/components/procedural-items/inspector.tsx +40 -15
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.test.ts +97 -14
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +18 -18
- package/src/components/systems/zone/zone-system.test.ts +47 -0
- package/src/components/systems/zone/zone-system.tsx +31 -66
- package/src/components/tools/__fixtures__/hosting.ts +10 -0
- package/src/components/tools/item/item-surface-placement.test.ts +3 -9
- package/src/components/tools/item/item-surface-strategy-resolver.test.ts +3 -9
- package/src/components/tools/item/placement-history.test.tsx +474 -0
- package/src/components/tools/item/placement-strategies.ts +63 -47
- package/src/components/tools/item/shelf-surface-strategy-resolver.test.ts +3 -3
- package/src/components/tools/item/use-draft-node.test.tsx +432 -1
- package/src/components/tools/item/use-draft-node.ts +207 -97
- package/src/components/tools/item/use-placement-coordinator.tsx +251 -111
- package/src/components/tools/paint-region/use-paint-tint.ts +37 -0
- package/src/components/tools/paint-region/wall-paint-region-tool.tsx +466 -0
- package/src/components/tools/registry/item-surface-move.test.ts +10 -2
- package/src/components/tools/registry/item-surface-move.ts +46 -24
- package/src/components/tools/registry/item-surface-resolver.test.ts +2 -2
- package/src/components/tools/registry/move-registry-node-tool.tsx +26 -10
- package/src/components/tools/select/box-select-tool.tsx +2 -12
- package/src/components/tools/select/plane-box-select-tool.test.tsx +79 -0
- package/src/components/tools/select/plane-box-select-tool.tsx +35 -22
- package/src/components/tools/select/select-candidates.test.ts +37 -4
- package/src/components/tools/select/select-candidates.ts +15 -11
- package/src/components/tools/shared/affordance-dispatch.test.ts +24 -0
- package/src/components/tools/shared/affordance-dispatch.ts +13 -0
- package/src/components/tools/shared/cursor-sphere.tsx +36 -12
- package/src/components/tools/shared/draft-measurement-label.tsx +38 -0
- package/src/components/tools/shared/pointer-support-cap.test.ts +27 -0
- package/src/components/tools/shared/pointer-support-cap.ts +17 -5
- package/src/components/tools/shared/surface-rejection.test.tsx +5 -4
- package/src/components/tools/shared/surface-rejection.tsx +14 -0
- package/src/components/tools/site/site-boundary-editor.tsx +1 -2
- package/src/components/tools/stair/stair-defaults.ts +0 -3
- package/src/components/tools/stair/stair-tool.tsx +90 -42
- package/src/components/tools/tool-manager.tsx +82 -23
- package/src/components/tools/wall/wall-drafting.test.ts +19 -4
- package/src/components/tools/zone/zone-tool.tsx +4 -12
- package/src/components/ui/action-menu/action-button.tsx +3 -0
- package/src/components/ui/action-menu/camera-actions.tsx +2 -1
- package/src/components/ui/action-menu/control-modes.tsx +2 -35
- package/src/components/ui/action-menu/measurement-control.tsx +3 -10
- package/src/components/ui/build-panel/build-panel.tsx +235 -0
- package/src/components/ui/command-palette/editor-commands.tsx +4 -30
- package/src/components/ui/command-palette/index.tsx +5 -24
- package/src/components/ui/controls/finish-swatch.tsx +110 -0
- package/src/components/ui/controls/material-paint-panel.tsx +101 -91
- package/src/components/ui/controls/material-picker.tsx +8 -67
- package/src/components/ui/controls/scene-material-list.tsx +4 -32
- package/src/components/ui/controls/select-control.tsx +65 -0
- package/src/components/ui/controls/slider-control.tsx +76 -31
- package/src/components/ui/floating-level-selector.tsx +9 -14
- package/src/components/ui/helpers/building-helper.tsx +4 -1
- package/src/components/ui/helpers/contextual-helper-panel.tsx +154 -70
- package/src/components/ui/helpers/helper-manager.test.tsx +69 -0
- package/src/components/ui/helpers/helper-manager.tsx +348 -19
- package/src/components/ui/helpers/item-helper.tsx +8 -0
- package/src/components/ui/helpers/registered-tool-helper.tsx +10 -33
- package/src/components/ui/icon-ref.tsx +45 -1
- package/src/components/ui/item-catalog/item-catalog.tsx +5 -30
- package/src/components/ui/level-duplicate-dialog.tsx +1 -1
- package/src/components/ui/node-icon-image.tsx +16 -0
- package/src/components/ui/panels/collections/collections-popover.tsx +20 -14
- package/src/components/ui/panels/floor-foundation-panel.tsx +551 -0
- package/src/components/ui/panels/floor-opening-panel.tsx +143 -0
- package/src/components/ui/panels/mobile-panel-sheet.tsx +2 -2
- package/src/components/ui/panels/mobile-selection-bar.tsx +2 -2
- package/src/components/ui/panels/multi-field-value.test.ts +26 -3
- package/src/components/ui/panels/multi-field-value.ts +6 -3
- package/src/components/ui/panels/multi-height-mode.tsx +27 -21
- package/src/components/ui/panels/multi-parametric-inspector.tsx +52 -2
- package/src/components/ui/panels/multi-selection-panel.tsx +43 -4
- package/src/components/ui/panels/node-display.ts +8 -3
- package/src/components/ui/panels/panel-manager.tsx +103 -68
- package/src/components/ui/panels/panel-wrapper.tsx +78 -36
- package/src/components/ui/panels/parametric-field-control.tsx +1 -1
- package/src/components/ui/panels/parametric-inspector.tsx +7 -19
- package/src/components/ui/panels/reference-panel.tsx +2 -3
- package/src/components/ui/panels/room-construction-rows.tsx +774 -0
- package/src/components/ui/panels/room-name-options.test.ts +55 -0
- package/src/components/ui/panels/room-name-options.ts +47 -0
- package/src/components/ui/panels/room-openings-field.tsx +110 -0
- package/src/components/ui/panels/room-panel.tsx +725 -0
- package/src/components/ui/panels/shape-choice.tsx +58 -0
- package/src/components/ui/panels/wall-region-list.tsx +109 -0
- package/src/components/ui/primitives/color-dot.tsx +36 -24
- package/src/components/ui/primitives/shortcut-token.tsx +4 -0
- package/src/components/ui/primitives/sidebar.tsx +2 -0
- package/src/components/ui/right-stack.test.ts +29 -0
- package/src/components/ui/right-stack.tsx +145 -0
- package/src/components/ui/sidebar/icon-rail.tsx +5 -0
- package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +2 -2
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +615 -372
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +7 -7
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -4
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/collection-tree-node.tsx +197 -0
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +24 -11
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +176 -211
- package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +30 -8
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +4 -1
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +1 -4
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +21 -13
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +0 -27
- package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +2 -3
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +9 -4
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +0 -2
- package/src/components/ui/sidebar/tab-bar.tsx +10 -0
- package/src/components/ui/sidebar/use-plugin-panels.tsx +15 -5
- package/src/components/viewer/floorplan-compass-button.tsx +9 -1
- package/src/components/viewer/floorplan-preview-visibility.test.ts +35 -0
- package/src/components/viewer/floorplan-preview-visibility.ts +19 -0
- package/src/components/viewer/floorplan-preview.tsx +6 -18
- package/src/components/viewer/viewer-hover-label.tsx +34 -0
- package/src/components/viewer/viewer-rooms.tsx +175 -0
- package/src/components/viewer/viewer-scene-header.tsx +181 -145
- package/src/components/viewer/viewer-selection-manager.tsx +124 -0
- package/src/components/viewer/viewer-stage.tsx +2 -2
- package/src/components/viewer-zone-system.tsx +9 -9
- package/src/hooks/use-auto-save.integration.test.tsx +311 -0
- package/src/hooks/use-auto-save.ts +55 -14
- package/src/hooks/use-ceiling-events.test.ts +1 -0
- package/src/hooks/use-ceiling-events.ts +2 -0
- package/src/hooks/use-grid-events.test.ts +2 -0
- package/src/hooks/use-grid-events.ts +16 -11
- package/src/hooks/use-keyboard.test.ts +182 -0
- package/src/hooks/use-keyboard.ts +122 -91
- package/src/hooks/use-open-wall-ends.test.ts +99 -0
- package/src/hooks/use-open-wall-ends.ts +238 -0
- package/src/hooks/use-placement-notice.test.ts +48 -0
- package/src/hooks/use-placement-notice.ts +42 -0
- package/src/hooks/use-selected-room.ts +143 -0
- package/src/index.tsx +306 -39
- package/src/lib/catalog-panel-model.ts +39 -0
- package/src/lib/catalog-selection.test.ts +27 -0
- package/src/lib/catalog-selection.ts +8 -0
- package/src/lib/ceiling-edit-session.test.ts +321 -0
- package/src/lib/ceiling-edit-session.ts +206 -0
- package/src/lib/ceiling-region-session.test.ts +179 -0
- package/src/lib/contextual-help.test.ts +27 -1
- package/src/lib/contextual-help.ts +16 -9
- package/src/lib/continuation.test.ts +19 -1
- package/src/lib/continuation.ts +25 -4
- package/src/lib/door-interaction.ts +14 -0
- package/src/lib/editor-level-display.test.ts +96 -0
- package/src/lib/editor-level-display.ts +48 -0
- package/src/lib/editor-views.ts +144 -0
- package/src/lib/export-geometry-wait.test.ts +90 -0
- package/src/lib/floor-edit-session.ts +153 -0
- package/src/lib/floor-footprints.test.ts +563 -0
- package/src/lib/floor-footprints.ts +395 -0
- package/src/lib/floor-foundation-support.test.ts +283 -0
- package/src/lib/floor-opening-draft.ts +218 -0
- package/src/lib/floor-openings.test.ts +216 -0
- package/src/lib/floor-region-geometry.ts +191 -0
- package/src/lib/floor-region-session.test.ts +485 -0
- package/src/lib/floor-region-session.ts +589 -0
- package/src/lib/floor-region-snap.ts +101 -0
- package/src/lib/floor-step-hover.test.ts +32 -0
- package/src/lib/floorplan/annotation-visibility.ts +17 -0
- package/src/lib/floorplan/dimension-drive.test.ts +360 -0
- package/src/lib/floorplan/dimension-drive.ts +479 -0
- package/src/lib/floorplan/floorplan-export.test.ts +15 -9
- package/src/lib/floorplan/floorplan-export.tsx +32 -8
- package/src/lib/floorplan/floorplan-extension.ts +48 -0
- package/src/lib/floorplan/floorplan-mode.test.ts +4 -0
- package/src/lib/floorplan/floorplan-mode.ts +2 -0
- package/src/lib/floorplan/floorplan-pdfkit-document.ts +339 -18
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +138 -8
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +56 -25
- package/src/lib/floorplan/floorplan-plugin-install.test.tsx +138 -0
- package/src/lib/floorplan/floorplan-readonly.ts +6 -0
- package/src/lib/floorplan/geometry.ts +13 -6
- package/src/lib/floorplan/index.ts +0 -9
- package/src/lib/floorplan/items.ts +5 -2
- package/src/lib/floorplan/marks.test.ts +213 -0
- package/src/lib/floorplan/marks.ts +238 -0
- package/src/lib/floorplan/open-wall-ends.test.ts +268 -0
- package/src/lib/floorplan/open-wall-ends.ts +162 -0
- package/src/lib/floorplan/pdf-fonts/GeistMono-Bold.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/GeistMono-Regular.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/LICENSE-GeistMono.txt +92 -0
- package/src/lib/floorplan/pdf-fonts/LICENSE-LiberationSans.txt +102 -0
- package/src/lib/floorplan/pdf-fonts/LiberationSans-Bold.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/LiberationSans-Regular.ttf +0 -0
- package/src/lib/floorplan/schedules.test.ts +189 -0
- package/src/lib/floorplan/schedules.ts +339 -0
- package/src/lib/floorplan/sheet-export.test.ts +100 -0
- package/src/lib/floorplan/sheet-export.ts +318 -0
- package/src/lib/floorplan/site-plan/build-site-plan-drawing.ts +779 -0
- package/src/lib/floorplan/site-plan/contours.test.ts +43 -0
- package/src/lib/floorplan/site-plan/contributors.ts +97 -0
- package/src/lib/floorplan/site-plan/coverage.ts +232 -0
- package/src/lib/floorplan/site-plan/drawing-type-switch.test.ts +15 -0
- package/src/lib/floorplan/site-plan/drawing-type-switch.tsx +86 -0
- package/src/lib/floorplan/site-plan/front-edge.test.ts +103 -0
- package/src/lib/floorplan/site-plan/front-edge.ts +156 -0
- package/src/lib/floorplan/site-plan/geometry.test.ts +275 -0
- package/src/lib/floorplan/site-plan/geometry.ts +557 -0
- package/src/lib/floorplan/site-plan/index.ts +71 -0
- package/src/lib/floorplan/site-plan/site-annotations.test.ts +366 -0
- package/src/lib/floorplan/site-plan/site-annotations.ts +1083 -0
- package/src/lib/floorplan/site-plan/site-parts.ts +379 -0
- package/src/lib/floorplan/site-plan/site-plan-layer.tsx +138 -0
- package/src/lib/floorplan/site-plan/street-name.test.ts +69 -0
- package/src/lib/floorplan/site-plan/street-name.ts +302 -0
- package/src/lib/floorplan/site-plan/yards.test.ts +69 -0
- package/src/lib/floorplan/types.ts +1 -27
- package/src/lib/fresh-planar-placement.test.ts +36 -0
- package/src/lib/fresh-planar-placement.ts +191 -36
- package/src/lib/geometry-script/author.ts +152 -0
- package/src/lib/geometry-script/client.ts +152 -0
- package/src/lib/geometry-script/protocol.ts +12 -0
- package/src/lib/gesture-lifecycle.test.ts +595 -0
- package/src/lib/gesture-lifecycle.ts +290 -0
- package/src/lib/glb-export-zone-holes.test.ts +41 -0
- package/src/lib/glb-export.test.ts +544 -16
- package/src/lib/glb-export.ts +256 -125
- package/src/lib/grid-frame.test.ts +19 -0
- package/src/lib/grid-frame.ts +18 -0
- package/src/lib/history.test.ts +6 -0
- package/src/lib/history.ts +7 -0
- package/src/lib/hud-title.ts +109 -0
- package/src/lib/hydration-gated-storage.ts +49 -0
- package/src/lib/ifc-export.test.ts +174 -0
- package/src/lib/ifc-export.ts +201 -0
- package/src/lib/ifc-parts.ts +15 -0
- package/src/lib/inspector-expanded.test.ts +44 -0
- package/src/lib/inspector-expanded.ts +36 -0
- package/src/lib/interaction/scope.ts +27 -1
- package/src/lib/keyboard-pan.test.ts +184 -0
- package/src/lib/keyboard-pan.ts +238 -0
- package/src/lib/live-gesture-round-trip.test.ts +360 -0
- package/src/lib/lot/__fixtures__/map-dossier-miami-shores.json +1 -0
- package/src/lib/lot/__fixtures__/map-dossier-stpete.json +1 -0
- package/src/lib/lot/clean-ring.test.ts +186 -0
- package/src/lib/lot/clean-ring.ts +217 -0
- package/src/lib/lot/dossier.test.ts +202 -0
- package/src/lib/lot/dossier.ts +498 -0
- package/src/lib/lot/drop-in.test.ts +326 -0
- package/src/lib/lot/drop-in.ts +407 -0
- package/src/lib/lot/index.ts +43 -0
- package/src/lib/lot/lot-patch.test.ts +340 -0
- package/src/lib/lot/lot-patch.ts +394 -0
- package/src/lib/lot/parcel-provider.ts +43 -0
- package/src/lib/lot/terrain.test.ts +161 -0
- package/src/lib/lot/terrain.ts +266 -0
- package/src/lib/material-catalog-model.ts +74 -0
- package/src/lib/material-paint-panel-model.ts +80 -0
- package/src/lib/material-paint.ts +4 -0
- package/src/lib/mezzanine-actions.test.ts +342 -0
- package/src/lib/mezzanine-draft.test.ts +501 -0
- package/src/lib/mezzanine-draft.ts +278 -0
- package/src/lib/mezzanine-messages.ts +26 -0
- package/src/lib/mezzanine-selection.test.ts +207 -0
- package/src/lib/mezzanine-stairs.ts +46 -0
- package/src/lib/model-export.ts +3 -1
- package/src/lib/node-action-movement.ts +17 -6
- package/src/lib/node-display-label.ts +37 -0
- package/src/lib/orbit-floor-clearance.test.ts +165 -0
- package/src/lib/orbit-floor-clearance.ts +106 -0
- package/src/lib/overlay-transform.test.ts +28 -0
- package/src/lib/overlay-transform.ts +12 -0
- package/src/lib/paint-commit-route.test.ts +37 -0
- package/src/lib/paint-commit-route.ts +17 -0
- package/src/lib/paint-eyedropper.test.tsx +165 -0
- package/src/lib/paint-eyedropper.ts +99 -0
- package/src/lib/paint-pass-through.test.ts +111 -0
- package/src/lib/paint-pass-through.ts +76 -0
- package/src/lib/paint-region-hover.ts +25 -0
- package/src/lib/paint-region-mode.ts +154 -0
- package/src/lib/paint-regions.test.ts +166 -0
- package/src/lib/paint-regions.ts +192 -0
- package/src/lib/paint-scope.test.ts +317 -35
- package/src/lib/paint-scope.ts +266 -25
- package/src/lib/panel-rows.ts +96 -0
- package/src/lib/panel-tool-options.test.ts +34 -0
- package/src/lib/panel-tool-options.ts +46 -0
- package/src/lib/parametric-node-update.ts +37 -0
- package/src/lib/plate-paint-affected.test.ts +300 -0
- package/src/lib/plate-paint-affected.ts +177 -0
- package/src/lib/plugin-chat.ts +50 -0
- package/src/lib/plugin-panels.test.ts +25 -0
- package/src/lib/plugin-panels.ts +94 -0
- package/src/lib/print-shell-compiler.ts +1 -0
- package/src/lib/registered-draft-snap.ts +12 -0
- package/src/lib/roof-duplication.ts +10 -2
- package/src/lib/room-assembly-overlay.test.ts +275 -0
- package/src/lib/room-assembly-overlay.ts +266 -0
- package/src/lib/room-built-on.test.ts +379 -0
- package/src/lib/room-built-on.ts +138 -0
- package/src/lib/room-construction-commands.ts +712 -0
- package/src/lib/room-construction.test.ts +746 -0
- package/src/lib/room-construction.ts +385 -0
- package/src/lib/room-controls-phase6.test.ts +630 -0
- package/src/lib/room-divide-session.ts +523 -0
- package/src/lib/room-handle-drag.ts +1068 -0
- package/src/lib/room-name-catalog.test.ts +90 -0
- package/src/lib/room-name-catalog.ts +168 -0
- package/src/lib/room-push-dimensions.test.ts +311 -0
- package/src/lib/room-push-dimensions.ts +251 -0
- package/src/lib/room-selection-commands.ts +62 -0
- package/src/lib/room-selection.test.ts +973 -0
- package/src/lib/room-selection.ts +417 -0
- package/src/lib/room-structure-commands.ts +94 -0
- package/src/lib/room-structure.test.ts +921 -0
- package/src/lib/room-transform-session.ts +570 -0
- package/src/lib/room-zone-routing.test.ts +294 -0
- package/src/lib/room-zone-routing.ts +197 -0
- package/src/lib/scene-clipboard.test.ts +364 -0
- package/src/lib/scene-clipboard.ts +229 -48
- package/src/lib/scene-material-refs.test.ts +79 -0
- package/src/lib/scene-material-refs.ts +79 -0
- package/src/lib/scene.test.ts +87 -0
- package/src/lib/scene.ts +39 -45
- package/src/lib/selection-routing.test.ts +129 -45
- package/src/lib/selection-routing.ts +53 -74
- package/src/lib/session-writes.test.ts +120 -0
- package/src/lib/session-writes.ts +205 -0
- package/src/lib/sidebar-panel.ts +62 -0
- package/src/lib/site-boundary.test.ts +4 -6
- package/src/lib/site-boundary.ts +1 -1
- package/src/lib/snapping-mode.test.ts +17 -2
- package/src/lib/snapping-mode.ts +19 -7
- package/src/lib/spatial-drag-plane.test.ts +53 -0
- package/src/lib/spatial-drag-plane.ts +19 -0
- package/src/lib/spatial-pointer-input.test.ts +72 -0
- package/src/lib/spatial-pointer-input.ts +62 -0
- package/src/lib/spatial-snap.test.ts +47 -0
- package/src/lib/terrace-draft.test.ts +145 -0
- package/src/lib/terrace-draft.ts +213 -0
- package/src/lib/terrain-panel-model.ts +117 -0
- package/src/lib/tool-cursor-icon.test.ts +16 -0
- package/src/lib/tool-cursor-icon.ts +11 -0
- package/src/lib/typing-target.ts +9 -0
- package/src/lib/units.ts +77 -39
- package/src/lib/vector-edges.test.ts +58 -0
- package/src/lib/vector-edges.ts +400 -0
- package/src/lib/viewer-selection.test.ts +577 -0
- package/src/lib/viewer-selection.ts +569 -0
- package/src/lib/wall-draw-variant.test.ts +71 -0
- package/src/lib/wall-draw-variant.ts +91 -0
- package/src/lib/wall-paint-region-session.ts +246 -0
- package/src/lib/wall-paint-region.test.ts +373 -0
- package/src/lib/wall-polygon-draft.test.ts +262 -0
- package/src/lib/wall-polygon-draft.ts +286 -0
- package/src/lib/wall-push.test.ts +453 -0
- package/src/lib/wall-region-face.ts +352 -0
- package/src/lib/wall-region-handles.test.ts +304 -0
- package/src/lib/wall-region-handles.ts +368 -0
- package/src/lib/wall-region-list.test.ts +119 -0
- package/src/lib/wall-region-list.ts +136 -0
- package/src/lib/wall-region-snap.ts +110 -0
- package/src/lib/window-interaction.ts +14 -0
- package/src/lib/world-grid-snap.test.ts +48 -0
- package/src/lib/world-grid-snap.ts +9 -5
- package/src/lib/zone-content.ts +4 -1
- package/src/lib/zone-name.test.ts +17 -0
- package/src/lib/zone-name.ts +17 -0
- package/src/store/opens-in-select-mode.test.ts +223 -0
- package/src/store/paint-mode.test.ts +188 -0
- package/src/store/rotation-snapping.test.ts +53 -0
- package/src/store/terrain-sculpt-mode.test.ts +19 -21
- package/src/store/tool-mode.test.ts +137 -6
- package/src/store/use-delete-confirmation.ts +28 -1
- package/src/store/use-drawing-view.test.ts +24 -3
- package/src/store/use-drawing-view.ts +29 -1
- package/src/store/use-editor.tsx +442 -323
- package/src/store/use-floorplan-draft-preview.ts +23 -1
- package/src/store/use-floorplan-marquee.ts +12 -2
- package/src/store/use-interaction-scope.ts +155 -6
- package/src/store/use-quick-measurement-hud.ts +3 -3
- package/src/store/use-stair-build-preview.ts +13 -5
- package/src/store/use-wall-move-ghosts.ts +4 -2
- package/src/store/use-wall-region-handles-selection.test.ts +222 -0
- package/src/store/use-wall-region-handles-selection.ts +139 -0
- package/src/store/view-layout.test.ts +175 -0
- package/src/store/view-layout.ts +196 -0
- package/src/test-utils/immediate-animation-frames.ts +16 -0
- package/src/test-utils/live-gesture-round-trip.ts +224 -0
- package/src/test-utils/selection-harness.ts +82 -0
- package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +0 -499
- package/src/components/systems/zone/zone-label-editor-system.tsx +0 -333
- package/src/components/tools/fence/fence-drafting.test.ts +0 -100
- package/src/components/tools/fence/fence-drafting.ts +0 -266
- package/src/components/ui/sidebar/panels/plugins-panel.tsx +0 -218
- package/src/components/viewer/viewer-units-panel.tsx +0 -116
- package/src/lib/floorplan/stairs.ts +0 -489
- package/src/lib/level-duplication.test.ts +0 -144
- package/src/lib/level-duplication.ts +0 -195
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
type AnyNode,
|
|
4
5
|
type AnyNodeId,
|
|
5
6
|
type CameraControlEvent,
|
|
6
7
|
type CameraControlFitSceneEvent,
|
|
8
|
+
type CameraControlFrameEvent,
|
|
7
9
|
type CameraPose,
|
|
8
10
|
emitter,
|
|
9
11
|
sceneRegistry,
|
|
10
12
|
useScene,
|
|
11
13
|
} from '@pascal-app/core'
|
|
12
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
GRID_LAYER,
|
|
16
|
+
getLevelPresentationY,
|
|
17
|
+
useSceneGroundReplacement,
|
|
18
|
+
useViewer,
|
|
19
|
+
ZONE_LAYER,
|
|
20
|
+
} from '@pascal-app/viewer'
|
|
13
21
|
import { CameraControls, CameraControlsImpl } from '@react-three/drei'
|
|
14
22
|
import { useFrame, useThree } from '@react-three/fiber'
|
|
15
23
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react'
|
|
@@ -31,15 +39,37 @@ import {
|
|
|
31
39
|
withCameraPoseDistance,
|
|
32
40
|
} from '../../lib/camera-pose'
|
|
33
41
|
import { EDITOR_LAYER } from '../../lib/constants'
|
|
42
|
+
import {
|
|
43
|
+
acceptsKeyboardPan,
|
|
44
|
+
advanceKeyboardPanMotion,
|
|
45
|
+
clearKeyboardPanKeys,
|
|
46
|
+
createKeyboardPanMotion,
|
|
47
|
+
hasKeyboardPanInput,
|
|
48
|
+
isEditableKeyboardTarget,
|
|
49
|
+
isKeyboardPanKey,
|
|
50
|
+
type KeyboardPanState,
|
|
51
|
+
keyboardPanSpeed,
|
|
52
|
+
setKeyboardPanKey,
|
|
53
|
+
syncKeyboardPanMotion,
|
|
54
|
+
} from '../../lib/keyboard-pan'
|
|
55
|
+
import {
|
|
56
|
+
MAX_ORBIT_POLAR_ANGLE,
|
|
57
|
+
ORBIT_FLOOR_CLEARANCE,
|
|
58
|
+
ORBIT_TARGET_HEIGHT,
|
|
59
|
+
} from '../../lib/orbit-floor-clearance'
|
|
34
60
|
import { editorOwnsOneFingerDrag } from '../../lib/touch-gesture-priority'
|
|
61
|
+
import { frameViewerCamera } from '../../lib/viewer-selection'
|
|
35
62
|
import { publishCameraPose } from '../../store/camera-pose-store'
|
|
36
|
-
import useEditor from '../../store/use-editor'
|
|
63
|
+
import useEditor, { takePreviewCameraRestore } from '../../store/use-editor'
|
|
37
64
|
import {
|
|
38
65
|
useActiveHandleDrag,
|
|
39
66
|
useEndpointReshape,
|
|
40
67
|
useMovingNode,
|
|
41
68
|
} from '../../store/use-interaction-scope'
|
|
69
|
+
import { visibleScene } from '../../store/view-layout'
|
|
42
70
|
import { createCameraDraggingLifecycle } from './camera-dragging-lifecycle'
|
|
71
|
+
import { FloorAwareCameraControls } from './floor-aware-camera-controls'
|
|
72
|
+
import { FloorSurface } from './floor-surface'
|
|
43
73
|
|
|
44
74
|
const currentTarget = new Vector3()
|
|
45
75
|
const tempBox = new Box3()
|
|
@@ -51,11 +81,37 @@ const tempTarget = new Vector3()
|
|
|
51
81
|
const transitionFreezePosition = new Vector3()
|
|
52
82
|
const transitionFreezeTarget = new Vector3()
|
|
53
83
|
const keyboardPanSpherical = new Spherical()
|
|
84
|
+
/** Mouse/touch orbit speed (camera-controls default 1): slower for precise looks. */
|
|
85
|
+
const ORBIT_ROTATE_SPEED = 0.6
|
|
86
|
+
// In 2D-only view the canvas is paused, so the floor plan drives WASD, orbit and
|
|
87
|
+
// top view itself (`floorplan-panel.tsx`) and the camera stands down.
|
|
88
|
+
const planOwnsNavigation = () => visibleScene(useEditor.getState()) === '2d'
|
|
54
89
|
const DEFAULT_MAX_POLAR_ANGLE = Math.PI / 2 - 0.1
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
90
|
+
// Focusing one of these aims at its floor (raised to the orbit's pivot
|
|
91
|
+
// height), not at the middle of its bounding box.
|
|
92
|
+
const GROUND_FOCUS_TYPES = new Set(['site', 'building', 'level', 'zone', 'slab'])
|
|
93
|
+
|
|
94
|
+
type LevelMode = ReturnType<typeof useViewer.getState>['levelMode']
|
|
95
|
+
|
|
96
|
+
/** Where the orbit pivot rests on a level: its floor plus the pivot height. */
|
|
97
|
+
function orbitGroundY(
|
|
98
|
+
levelId: string | null | undefined,
|
|
99
|
+
levelMode: LevelMode = useViewer.getState().levelMode,
|
|
100
|
+
) {
|
|
101
|
+
const floorY = levelId ? getLevelPresentationY(levelId, useScene.getState().nodes, levelMode) : 0
|
|
102
|
+
return floorY + ORBIT_TARGET_HEIGHT
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function levelOf(nodeId: string): string | null {
|
|
106
|
+
const nodes = useScene.getState().nodes
|
|
107
|
+
let node: AnyNode | undefined = nodes[nodeId as AnyNodeId]
|
|
108
|
+
while (node) {
|
|
109
|
+
if (node.type === 'level') return node.id
|
|
110
|
+
node = node.parentId ? nodes[node.parentId as AnyNodeId] : undefined
|
|
111
|
+
}
|
|
112
|
+
return null
|
|
113
|
+
}
|
|
114
|
+
|
|
59
115
|
type CameraMode = ReturnType<typeof useViewer.getState>['cameraMode']
|
|
60
116
|
type CameraPoseSnapshot = {
|
|
61
117
|
mode: CameraMode
|
|
@@ -114,54 +170,6 @@ function freezeCameraControlTransition(control: CameraControlsImpl) {
|
|
|
114
170
|
)
|
|
115
171
|
}
|
|
116
172
|
|
|
117
|
-
function isEditableKeyboardTarget(target: EventTarget | null) {
|
|
118
|
-
return (
|
|
119
|
-
target instanceof HTMLInputElement ||
|
|
120
|
-
target instanceof HTMLTextAreaElement ||
|
|
121
|
-
target instanceof HTMLSelectElement ||
|
|
122
|
-
(target instanceof HTMLElement && target.isContentEditable)
|
|
123
|
-
)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
type KeyboardPanState = {
|
|
127
|
-
forward: boolean
|
|
128
|
-
backward: boolean
|
|
129
|
-
left: boolean
|
|
130
|
-
right: boolean
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function setKeyboardPanKey(state: KeyboardPanState, code: string, pressed: boolean): boolean {
|
|
134
|
-
if (code === 'KeyW') {
|
|
135
|
-
const changed = state.forward !== pressed
|
|
136
|
-
state.forward = pressed
|
|
137
|
-
return changed
|
|
138
|
-
}
|
|
139
|
-
if (code === 'KeyS') {
|
|
140
|
-
const changed = state.backward !== pressed
|
|
141
|
-
state.backward = pressed
|
|
142
|
-
return changed
|
|
143
|
-
}
|
|
144
|
-
if (code === 'KeyA') {
|
|
145
|
-
const changed = state.left !== pressed
|
|
146
|
-
state.left = pressed
|
|
147
|
-
return changed
|
|
148
|
-
}
|
|
149
|
-
if (code === 'KeyD') {
|
|
150
|
-
const changed = state.right !== pressed
|
|
151
|
-
state.right = pressed
|
|
152
|
-
return changed
|
|
153
|
-
}
|
|
154
|
-
return false
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function isKeyboardPanKey(code: string): boolean {
|
|
158
|
-
return code === 'KeyW' || code === 'KeyA' || code === 'KeyS' || code === 'KeyD'
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function hasKeyboardPanInput(state: KeyboardPanState): boolean {
|
|
162
|
-
return state.forward || state.backward || state.left || state.right
|
|
163
|
-
}
|
|
164
|
-
|
|
165
173
|
type CameraViewportSize = {
|
|
166
174
|
width: number
|
|
167
175
|
height: number
|
|
@@ -348,7 +356,7 @@ function useFirstPersonCameraPoseRestore(
|
|
|
348
356
|
}
|
|
349
357
|
|
|
350
358
|
export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) => {
|
|
351
|
-
const controls = useRef<
|
|
359
|
+
const controls = useRef<FloorAwareCameraControls | null>(null)
|
|
352
360
|
const pendingAppliedPose = useRef<CameraPoseApplicationPlan | null>(null)
|
|
353
361
|
const activePoseInterpolation = useRef<{
|
|
354
362
|
camera: Camera
|
|
@@ -362,6 +370,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
362
370
|
left: false,
|
|
363
371
|
right: false,
|
|
364
372
|
})
|
|
373
|
+
const keyboardPanMotion = useRef(createKeyboardPanMotion())
|
|
365
374
|
const isPreviewMode = useEditor((s) => s.isPreviewMode)
|
|
366
375
|
const isFirstPersonMode = useEditor((s) => s.isFirstPersonMode)
|
|
367
376
|
const allowUndergroundCamera = useEditor((s) => s.allowUndergroundCamera)
|
|
@@ -374,12 +383,24 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
374
383
|
isFirstPersonMode,
|
|
375
384
|
cameraMode,
|
|
376
385
|
)
|
|
377
|
-
|
|
386
|
+
// Selecting the building or site clears the level; the camera keeps
|
|
387
|
+
// standing on the last level rather than dropping to the ground floor.
|
|
388
|
+
const lastLevelId = useRef<string | null>(null)
|
|
389
|
+
if (selection.levelId) lastLevelId.current = selection.levelId
|
|
390
|
+
const currentLevelId = lastLevelId.current
|
|
378
391
|
const firstLoad = useRef(true)
|
|
392
|
+
const floorY = useScene((state) =>
|
|
393
|
+
currentLevelId ? getLevelPresentationY(currentLevelId, state.nodes, levelMode) : 0,
|
|
394
|
+
)
|
|
395
|
+
const allowUnderground = !isPreviewMode && allowUndergroundCamera
|
|
396
|
+
const floorMinY = allowUnderground ? null : floorY + ORBIT_FLOOR_CLEARANCE
|
|
379
397
|
const maxPolarAngle =
|
|
380
|
-
|
|
398
|
+
allowUnderground || cameraMode === 'perspective'
|
|
399
|
+
? MAX_ORBIT_POLAR_ANGLE
|
|
400
|
+
: DEFAULT_MAX_POLAR_ANGLE
|
|
381
401
|
|
|
382
402
|
const camera = useThree((state) => state.camera)
|
|
403
|
+
const scene = useThree((state) => state.scene)
|
|
383
404
|
const gl = useThree((state) => state.gl)
|
|
384
405
|
const raycaster = useThree((state) => state.raycaster)
|
|
385
406
|
const viewportSize = useThree((state) => state.size)
|
|
@@ -418,7 +439,10 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
418
439
|
const beginLocalCameraInteraction = useCallback(
|
|
419
440
|
({ dragging = true }: { dragging?: boolean } = {}) => {
|
|
420
441
|
cancelPoseApplication()
|
|
421
|
-
if (dragging)
|
|
442
|
+
if (dragging) {
|
|
443
|
+
controls.current?.releaseWrittenPose()
|
|
444
|
+
cameraDraggingLifecycle.begin()
|
|
445
|
+
}
|
|
422
446
|
emitter.emit('camera-controls:interaction-start', undefined)
|
|
423
447
|
},
|
|
424
448
|
[cameraDraggingLifecycle, cancelPoseApplication],
|
|
@@ -506,11 +530,21 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
506
530
|
|
|
507
531
|
emitter.on('camera-controls:apply-pose', handleAppliedPose)
|
|
508
532
|
emitter.on('camera-controls:cancel-pose', cancelPoseApplication)
|
|
533
|
+
if (!isPreviewMode) {
|
|
534
|
+
const restore = takePreviewCameraRestore()
|
|
535
|
+
if (restore) handleAppliedPose(restore)
|
|
536
|
+
}
|
|
509
537
|
return () => {
|
|
510
538
|
emitter.off('camera-controls:apply-pose', handleAppliedPose)
|
|
511
539
|
emitter.off('camera-controls:cancel-pose', cancelPoseApplication)
|
|
512
540
|
}
|
|
513
|
-
}, [
|
|
541
|
+
}, [
|
|
542
|
+
applyPendingPose,
|
|
543
|
+
cancelPoseApplication,
|
|
544
|
+
freezeActivePoseInterpolation,
|
|
545
|
+
isFirstPersonMode,
|
|
546
|
+
isPreviewMode,
|
|
547
|
+
])
|
|
514
548
|
|
|
515
549
|
useEffect(() => cancelPoseApplication, [cancelPoseApplication])
|
|
516
550
|
|
|
@@ -521,33 +555,60 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
521
555
|
if (process.env.NODE_ENV !== 'development') return
|
|
522
556
|
const w = window as typeof window & {
|
|
523
557
|
__pascalCameraControls?: (() => CameraControlsImpl | null) | null
|
|
558
|
+
__pascalScene?: (() => unknown) | null
|
|
559
|
+
__pascalCapture?: (() => Promise<string>) | null
|
|
524
560
|
}
|
|
525
561
|
w.__pascalCameraControls = () => controls.current
|
|
562
|
+
// the live three.js scene, for the same tooling (a probe can walk the
|
|
563
|
+
// meshes for an empty geometry the WebGPU validator complains about)
|
|
564
|
+
w.__pascalScene = () => scene
|
|
565
|
+
// a fresh frame read back as a PNG data URL: the canvas is cleared after
|
|
566
|
+
// each presented frame, so a capture has to render and read in one task
|
|
567
|
+
w.__pascalCapture = async () => {
|
|
568
|
+
const r = gl as unknown as {
|
|
569
|
+
renderAsync?: (s: unknown, c: unknown) => Promise<void>
|
|
570
|
+
render: (s: unknown, c: unknown) => void
|
|
571
|
+
domElement: HTMLCanvasElement
|
|
572
|
+
}
|
|
573
|
+
if (r.renderAsync) await r.renderAsync(scene, camera)
|
|
574
|
+
else r.render(scene, camera)
|
|
575
|
+
return r.domElement.toDataURL('image/png')
|
|
576
|
+
}
|
|
526
577
|
return () => {
|
|
527
578
|
w.__pascalCameraControls = null
|
|
579
|
+
w.__pascalScene = null
|
|
580
|
+
w.__pascalCapture = null
|
|
528
581
|
}
|
|
529
|
-
}, [])
|
|
582
|
+
}, [scene, gl, camera])
|
|
530
583
|
|
|
584
|
+
// `camera` too: drei builds a fresh controls instance (pivot at the origin)
|
|
585
|
+
// when the default camera mounts or changes; it is rested on the floor too.
|
|
586
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: camera re-runs this on a new instance
|
|
531
587
|
useEffect(() => {
|
|
532
588
|
if (isPreviewMode || isFirstPersonMode || isRestoringFirstPersonPose()) return
|
|
533
589
|
// Analytic destination, not `sceneRegistry` mesh position: a level created
|
|
534
590
|
// this frame still sits at y=0 (LevelSystem lerps it later), and a mode
|
|
535
591
|
// switch leaves every level mid-lerp — the camera must pan to where the
|
|
536
592
|
// level will settle, in the CURRENT presentation mode.
|
|
537
|
-
const targetY = currentLevelId
|
|
538
|
-
? getLevelPresentationY(currentLevelId, useScene.getState().nodes, levelMode)
|
|
539
|
-
: 0
|
|
593
|
+
const targetY = orbitGroundY(currentLevelId, levelMode)
|
|
540
594
|
if (!controls.current) return
|
|
541
595
|
if (firstLoad.current) {
|
|
542
596
|
firstLoad.current = false
|
|
543
|
-
controls.current.setLookAt(20, 20, 20, 0,
|
|
597
|
+
controls.current.setLookAt(20, 20 + targetY, 20, 0, targetY, 0, true)
|
|
544
598
|
}
|
|
545
|
-
controls.current.
|
|
599
|
+
controls.current.getOrbitTarget(currentTarget)
|
|
546
600
|
// Idempotence guard: skip when already there — also swallows the thumbnail
|
|
547
601
|
// generator's synchronous stacked→restore levelMode round-trip.
|
|
548
602
|
if (Math.abs(currentTarget.y - targetY) < 1e-3) return
|
|
549
603
|
controls.current.moveTo(currentTarget.x, targetY, currentTarget.z, true)
|
|
550
|
-
}, [
|
|
604
|
+
}, [
|
|
605
|
+
camera,
|
|
606
|
+
currentLevelId,
|
|
607
|
+
levelMode,
|
|
608
|
+
isPreviewMode,
|
|
609
|
+
isFirstPersonMode,
|
|
610
|
+
isRestoringFirstPersonPose,
|
|
611
|
+
])
|
|
551
612
|
|
|
552
613
|
useEffect(() => {
|
|
553
614
|
if (isFirstPersonMode || !controls.current) return
|
|
@@ -560,6 +621,15 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
560
621
|
}
|
|
561
622
|
}, [isFirstPersonMode, maxPolarAngle])
|
|
562
623
|
|
|
624
|
+
const floorSurface = useMemo(() => new FloorSurface(), [])
|
|
625
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: drei builds a new controls instance per camera
|
|
626
|
+
useLayoutEffect(() => {
|
|
627
|
+
floorSurface.levelId = currentLevelId ?? null
|
|
628
|
+
if (!controls.current) return
|
|
629
|
+
controls.current.floorMinY = floorMinY
|
|
630
|
+
controls.current.floorSurface = floorSurface
|
|
631
|
+
}, [camera, currentLevelId, floorMinY, floorSurface, isFirstPersonMode])
|
|
632
|
+
|
|
563
633
|
const focusNode = useCallback(
|
|
564
634
|
(nodeId: string) => {
|
|
565
635
|
if (isPreviewMode || isFirstPersonMode || !controls.current) return
|
|
@@ -567,17 +637,34 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
567
637
|
const object3D = sceneRegistry.nodes.get(nodeId)
|
|
568
638
|
if (!object3D) return
|
|
569
639
|
|
|
640
|
+
const node = useScene.getState().nodes[nodeId as AnyNodeId]
|
|
641
|
+
const groundFocus = node !== undefined && GROUND_FOCUS_TYPES.has(node.type)
|
|
570
642
|
tempBox.setFromObject(object3D)
|
|
571
|
-
if (tempBox.isEmpty())
|
|
572
|
-
|
|
573
|
-
|
|
643
|
+
if (!tempBox.isEmpty()) {
|
|
644
|
+
tempBox.getCenter(tempCenter)
|
|
645
|
+
} else if (groundFocus && node.type === 'zone' && node.polygon.length > 0) {
|
|
646
|
+
// A room draws nothing measurable in some display modes: aim at the
|
|
647
|
+
// middle of its outline instead.
|
|
648
|
+
tempBox.makeEmpty()
|
|
649
|
+
for (const [x, z] of node.polygon) tempBox.expandByPoint(tempCenter.set(x, 0, z))
|
|
650
|
+
tempBox.getCenter(tempCenter)
|
|
651
|
+
object3D.updateWorldMatrix(true, false)
|
|
652
|
+
tempCenter.applyMatrix4(object3D.matrixWorld)
|
|
653
|
+
} else {
|
|
654
|
+
return
|
|
655
|
+
}
|
|
656
|
+
if (groundFocus) {
|
|
657
|
+
tempCenter.y = orbitGroundY(levelOf(nodeId) ?? lastLevelId.current)
|
|
658
|
+
}
|
|
574
659
|
controls.current.getPosition(tempPosition)
|
|
575
660
|
controls.current.getTarget(tempTarget)
|
|
576
661
|
tempDelta.copy(tempCenter).sub(tempTarget)
|
|
577
662
|
|
|
663
|
+
const focusedY = tempPosition.y + tempDelta.y
|
|
664
|
+
const floorMinY = isPerspectiveCamera(camera) ? controls.current.floorLimitY : null
|
|
578
665
|
controls.current.setLookAt(
|
|
579
666
|
tempPosition.x + tempDelta.x,
|
|
580
|
-
|
|
667
|
+
floorMinY === null ? focusedY : Math.max(focusedY, floorMinY),
|
|
581
668
|
tempPosition.z + tempDelta.z,
|
|
582
669
|
tempCenter.x,
|
|
583
670
|
tempCenter.y,
|
|
@@ -585,7 +672,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
585
672
|
true,
|
|
586
673
|
)
|
|
587
674
|
},
|
|
588
|
-
[isPreviewMode, isFirstPersonMode],
|
|
675
|
+
[camera, isPreviewMode, isFirstPersonMode],
|
|
589
676
|
)
|
|
590
677
|
|
|
591
678
|
const publishCurrentPose = useCallback(() => {
|
|
@@ -667,23 +754,18 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
667
754
|
}
|
|
668
755
|
}
|
|
669
756
|
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
const vertical = (panKeys.forward ? 1 : 0) - (panKeys.backward ? 1 : 0)
|
|
673
|
-
if (horizontal === 0 && vertical === 0) return
|
|
757
|
+
const motion = keyboardPanMotion.current
|
|
758
|
+
if (!advanceKeyboardPanMotion(motion, keyboardPanKeys.current, performance.now())) return
|
|
674
759
|
|
|
675
760
|
const control = controls.current
|
|
676
761
|
|
|
677
762
|
control.getSpherical(keyboardPanSpherical, false)
|
|
678
763
|
const viewWidth = getCameraViewWidth(camera, keyboardPanSpherical.radius, viewportSize)
|
|
679
|
-
const speed =
|
|
680
|
-
Math.max(viewWidth * KEYBOARD_PAN_VIEW_WIDTH_PER_SECOND, KEYBOARD_PAN_MIN_SPEED),
|
|
681
|
-
KEYBOARD_PAN_MAX_SPEED,
|
|
682
|
-
)
|
|
683
|
-
const step = (speed * Math.min(delta, 0.05)) / Math.hypot(horizontal, vertical)
|
|
764
|
+
const speed = keyboardPanSpeed(viewWidth)
|
|
684
765
|
|
|
685
|
-
|
|
686
|
-
if (
|
|
766
|
+
// The motion already eases; the controls' own smoothing would add a glide on top.
|
|
767
|
+
if (motion.stepX !== 0) control.truck(motion.stepX * speed, 0, false)
|
|
768
|
+
if (motion.stepY !== 0) control.forward(motion.stepY * speed, false)
|
|
687
769
|
}, 0)
|
|
688
770
|
|
|
689
771
|
// Configure mouse buttons based on control mode and camera mode
|
|
@@ -694,13 +776,14 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
694
776
|
? CameraControlsImpl.ACTION.ZOOM
|
|
695
777
|
: CameraControlsImpl.ACTION.DOLLY
|
|
696
778
|
|
|
779
|
+
// Left click selects, in Preview as in the published viewer.
|
|
697
780
|
return {
|
|
698
|
-
left:
|
|
781
|
+
left: CameraControlsImpl.ACTION.NONE,
|
|
699
782
|
middle: CameraControlsImpl.ACTION.SCREEN_PAN,
|
|
700
783
|
right: CameraControlsImpl.ACTION.ROTATE,
|
|
701
784
|
wheel: wheelAction,
|
|
702
785
|
}
|
|
703
|
-
}, [cameraMode
|
|
786
|
+
}, [cameraMode])
|
|
704
787
|
|
|
705
788
|
// Touch gestures (mobile / trackpad).
|
|
706
789
|
// - One finger drag → rotate by default (much easier on a phone), but
|
|
@@ -766,13 +849,6 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
766
849
|
let panPointerId: number | null = null
|
|
767
850
|
let panPointerButton: number | null = null
|
|
768
851
|
|
|
769
|
-
const clearKeyboardPanKeys = () => {
|
|
770
|
-
keyboardPanKeys.current.forward = false
|
|
771
|
-
keyboardPanKeys.current.backward = false
|
|
772
|
-
keyboardPanKeys.current.left = false
|
|
773
|
-
keyboardPanKeys.current.right = false
|
|
774
|
-
}
|
|
775
|
-
|
|
776
852
|
const setNavigationCursor = (cursor: 'grab' | 'grabbing') => {
|
|
777
853
|
document.body.style.cursor = cursor
|
|
778
854
|
gl.domElement.style.cursor = cursor
|
|
@@ -823,10 +899,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
823
899
|
controls.current.mouseButtons.wheel = wheelAction
|
|
824
900
|
controls.current.mouseButtons.middle = CameraControlsImpl.ACTION.SCREEN_PAN
|
|
825
901
|
controls.current.mouseButtons.right = CameraControlsImpl.ACTION.ROTATE
|
|
826
|
-
if (
|
|
827
|
-
// In preview mode, left-click is always pan (viewer-style)
|
|
828
|
-
controls.current.mouseButtons.left = CameraControlsImpl.ACTION.SCREEN_PAN
|
|
829
|
-
} else if (space) {
|
|
902
|
+
if (space) {
|
|
830
903
|
controls.current.mouseButtons.left = CameraControlsImpl.ACTION.SCREEN_PAN
|
|
831
904
|
} else {
|
|
832
905
|
controls.current.mouseButtons.left = CameraControlsImpl.ACTION.NONE
|
|
@@ -835,10 +908,12 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
835
908
|
|
|
836
909
|
const onKeyDown = (event: KeyboardEvent) => {
|
|
837
910
|
if (isKeyboardPanKey(event.code)) {
|
|
838
|
-
if (
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
911
|
+
if (acceptsKeyboardPan(event) && !planOwnsNavigation()) {
|
|
912
|
+
syncKeyboardPanMotion(
|
|
913
|
+
keyboardPanMotion.current,
|
|
914
|
+
keyboardPanKeys.current,
|
|
915
|
+
performance.now(),
|
|
916
|
+
)
|
|
842
917
|
const changed = setKeyboardPanKey(keyboardPanKeys.current, event.code, true)
|
|
843
918
|
if (changed) beginLocalCameraInteraction()
|
|
844
919
|
event.preventDefault()
|
|
@@ -870,6 +945,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
870
945
|
|
|
871
946
|
const onKeyUp = (event: KeyboardEvent) => {
|
|
872
947
|
if (isKeyboardPanKey(event.code)) {
|
|
948
|
+
syncKeyboardPanMotion(keyboardPanMotion.current, keyboardPanKeys.current, performance.now())
|
|
873
949
|
const changed = setKeyboardPanKey(keyboardPanKeys.current, event.code, false)
|
|
874
950
|
if (changed) {
|
|
875
951
|
if (!hasKeyboardPanInput(keyboardPanKeys.current)) {
|
|
@@ -930,7 +1006,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
930
1006
|
|
|
931
1007
|
const onBlur = () => {
|
|
932
1008
|
keyState.space = false
|
|
933
|
-
clearKeyboardPanKeys()
|
|
1009
|
+
clearKeyboardPanKeys(keyboardPanKeys.current)
|
|
934
1010
|
panPointerId = null
|
|
935
1011
|
panPointerButton = null
|
|
936
1012
|
clearNavigationCursor()
|
|
@@ -955,18 +1031,11 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
955
1031
|
window.removeEventListener('pointercancel', onPointerUp, true)
|
|
956
1032
|
window.removeEventListener('blur', onBlur)
|
|
957
1033
|
gl.domElement.removeEventListener('wheel', onWheel, true)
|
|
958
|
-
clearKeyboardPanKeys()
|
|
1034
|
+
clearKeyboardPanKeys(keyboardPanKeys.current)
|
|
959
1035
|
clearNavigationCursor()
|
|
960
1036
|
cameraDraggingLifecycle.end()
|
|
961
1037
|
}
|
|
962
|
-
}, [
|
|
963
|
-
beginLocalCameraInteraction,
|
|
964
|
-
cameraDraggingLifecycle,
|
|
965
|
-
cameraMode,
|
|
966
|
-
gl,
|
|
967
|
-
isPreviewMode,
|
|
968
|
-
isFirstPersonMode,
|
|
969
|
-
])
|
|
1038
|
+
}, [beginLocalCameraInteraction, cameraDraggingLifecycle, cameraMode, gl, isFirstPersonMode])
|
|
970
1039
|
|
|
971
1040
|
// `controlstart` fires only for user pointer interactions. Pointerdowns
|
|
972
1041
|
// mapped to ACTION.NONE must not flag the camera as dragging because no
|
|
@@ -979,18 +1048,18 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
979
1048
|
})
|
|
980
1049
|
}, [beginLocalCameraInteraction])
|
|
981
1050
|
|
|
982
|
-
// Preview
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
: null
|
|
986
|
-
|
|
1051
|
+
// Preview opens on the editor's floor (or building, or site) framed the
|
|
1052
|
+
// viewer's way; afterwards only the viewer navigation moves the camera
|
|
1053
|
+
// (`camera-controls:frame`), never a canvas selection.
|
|
987
1054
|
useEffect(() => {
|
|
988
1055
|
if (!(isPreviewMode && controls.current) || isFirstPersonMode) return
|
|
989
1056
|
|
|
990
1057
|
const nodes = useScene.getState().nodes
|
|
991
|
-
|
|
1058
|
+
const { selection: opening } = useViewer.getState()
|
|
1059
|
+
const openingNodeId = opening.levelId ?? opening.buildingId
|
|
1060
|
+
let node = openingNodeId ? nodes[openingNodeId as AnyNodeId] : null
|
|
992
1061
|
|
|
993
|
-
if (!
|
|
1062
|
+
if (!openingNodeId) {
|
|
994
1063
|
const site = Object.values(nodes).find((n) => n.type === 'site')
|
|
995
1064
|
node = site || null
|
|
996
1065
|
}
|
|
@@ -1023,10 +1092,10 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1023
1092
|
return
|
|
1024
1093
|
}
|
|
1025
1094
|
|
|
1026
|
-
if (!
|
|
1095
|
+
if (!openingNodeId) return
|
|
1027
1096
|
|
|
1028
1097
|
// Calculate camera position from bounding box
|
|
1029
|
-
const object3D = sceneRegistry.nodes.get(
|
|
1098
|
+
const object3D = sceneRegistry.nodes.get(openingNodeId)
|
|
1030
1099
|
if (!object3D) return
|
|
1031
1100
|
|
|
1032
1101
|
tempBox.setFromObject(object3D)
|
|
@@ -1045,7 +1114,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1045
1114
|
tempCenter.z,
|
|
1046
1115
|
true,
|
|
1047
1116
|
)
|
|
1048
|
-
}, [isPreviewMode, isFirstPersonMode
|
|
1117
|
+
}, [isPreviewMode, isFirstPersonMode])
|
|
1049
1118
|
|
|
1050
1119
|
// Preset capture auto-framing — when `setCaptureMode({ mode: 'preset',
|
|
1051
1120
|
// isolated })` fires, fly the camera to a pose that fits the union
|
|
@@ -1196,7 +1265,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1196
1265
|
}
|
|
1197
1266
|
|
|
1198
1267
|
const handleTopView = () => {
|
|
1199
|
-
if (isFirstPersonMode || !controls.current) return
|
|
1268
|
+
if (isFirstPersonMode || !controls.current || planOwnsNavigation()) return
|
|
1200
1269
|
|
|
1201
1270
|
const currentPolarAngle = controls.current.polarAngle
|
|
1202
1271
|
|
|
@@ -1208,10 +1277,10 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1208
1277
|
}
|
|
1209
1278
|
|
|
1210
1279
|
const handleOrbitCW = () => {
|
|
1211
|
-
if (isFirstPersonMode || !controls.current) return
|
|
1280
|
+
if (isFirstPersonMode || !controls.current || planOwnsNavigation()) return
|
|
1212
1281
|
|
|
1213
1282
|
const currentAzimuth = controls.current.azimuthAngle
|
|
1214
|
-
const currentPolar = controls.current.
|
|
1283
|
+
const currentPolar = controls.current.orbitPolarAngle
|
|
1215
1284
|
// Round to nearest 90° increment, then rotate 90° clockwise
|
|
1216
1285
|
const rounded = Math.round(currentAzimuth / (Math.PI / 2)) * (Math.PI / 2)
|
|
1217
1286
|
const target = rounded - Math.PI / 2
|
|
@@ -1220,10 +1289,10 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1220
1289
|
}
|
|
1221
1290
|
|
|
1222
1291
|
const handleOrbitCCW = () => {
|
|
1223
|
-
if (isFirstPersonMode || !controls.current) return
|
|
1292
|
+
if (isFirstPersonMode || !controls.current || planOwnsNavigation()) return
|
|
1224
1293
|
|
|
1225
1294
|
const currentAzimuth = controls.current.azimuthAngle
|
|
1226
|
-
const currentPolar = controls.current.
|
|
1295
|
+
const currentPolar = controls.current.orbitPolarAngle
|
|
1227
1296
|
// Round to nearest 90° increment, then rotate 90° counter-clockwise
|
|
1228
1297
|
const rounded = Math.round(currentAzimuth / (Math.PI / 2)) * (Math.PI / 2)
|
|
1229
1298
|
const target = rounded + Math.PI / 2
|
|
@@ -1235,11 +1304,18 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1235
1304
|
focusNode(nodeId)
|
|
1236
1305
|
}
|
|
1237
1306
|
|
|
1307
|
+
const handleFrame = (event: CameraControlFrameEvent) => {
|
|
1308
|
+
if (isFirstPersonMode || !controls.current) return
|
|
1309
|
+
cancelPoseApplication()
|
|
1310
|
+
frameViewerCamera(controls.current, camera, event)
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1238
1313
|
const handleFitScene = ({ bounds }: CameraControlFitSceneEvent) => {
|
|
1239
1314
|
if (isFirstPersonMode || !controls.current || isPreviewMode) return
|
|
1315
|
+
const groundY = orbitGroundY(lastLevelId.current)
|
|
1240
1316
|
if (!bounds) {
|
|
1241
1317
|
// Restore default framing pose when no bounds were computed.
|
|
1242
|
-
controls.current.setLookAt(20, 20, 20, 0,
|
|
1318
|
+
controls.current.setLookAt(20, 20 + groundY, 20, 0, groundY, 0, true)
|
|
1243
1319
|
return
|
|
1244
1320
|
}
|
|
1245
1321
|
const [cx, cz] = bounds.center
|
|
@@ -1249,11 +1325,20 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1249
1325
|
const maxExtent = Math.max(w, d)
|
|
1250
1326
|
const distance = Math.max(maxExtent * 1.4, 15)
|
|
1251
1327
|
const height = Math.max(maxExtent * 0.8, 10)
|
|
1252
|
-
controls.current.setLookAt(
|
|
1328
|
+
controls.current.setLookAt(
|
|
1329
|
+
cx + distance * 0.7,
|
|
1330
|
+
height + groundY,
|
|
1331
|
+
cz + distance * 0.7,
|
|
1332
|
+
cx,
|
|
1333
|
+
groundY,
|
|
1334
|
+
cz,
|
|
1335
|
+
true,
|
|
1336
|
+
)
|
|
1253
1337
|
}
|
|
1254
1338
|
|
|
1255
1339
|
emitter.on('camera-controls:capture', handleNodeCapture)
|
|
1256
1340
|
emitter.on('camera-controls:focus', handleNodeFocus)
|
|
1341
|
+
emitter.on('camera-controls:frame', handleFrame)
|
|
1257
1342
|
emitter.on('camera-controls:view', handleNodeView)
|
|
1258
1343
|
emitter.on('camera-controls:top-view', handleTopView)
|
|
1259
1344
|
emitter.on('camera-controls:orbit-cw', handleOrbitCW)
|
|
@@ -1263,13 +1348,14 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1263
1348
|
return () => {
|
|
1264
1349
|
emitter.off('camera-controls:capture', handleNodeCapture)
|
|
1265
1350
|
emitter.off('camera-controls:focus', handleNodeFocus)
|
|
1351
|
+
emitter.off('camera-controls:frame', handleFrame)
|
|
1266
1352
|
emitter.off('camera-controls:view', handleNodeView)
|
|
1267
1353
|
emitter.off('camera-controls:top-view', handleTopView)
|
|
1268
1354
|
emitter.off('camera-controls:orbit-cw', handleOrbitCW)
|
|
1269
1355
|
emitter.off('camera-controls:orbit-ccw', handleOrbitCCW)
|
|
1270
1356
|
emitter.off('camera-controls:fit-scene', handleFitScene)
|
|
1271
1357
|
}
|
|
1272
|
-
}, [focusNode, isPreviewMode, isFirstPersonMode])
|
|
1358
|
+
}, [camera, cancelPoseApplication, focusNode, isPreviewMode, isFirstPersonMode])
|
|
1273
1359
|
|
|
1274
1360
|
const onTransitionStart = useCallback(() => {
|
|
1275
1361
|
cameraDraggingLifecycle.begin()
|
|
@@ -1300,6 +1386,10 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1300
1386
|
// navigation guardrails.
|
|
1301
1387
|
const isPresetCapture = captureMode.mode === 'preset'
|
|
1302
1388
|
const minDistance = isPresetCapture ? 0.5 : 2
|
|
1389
|
+
// Over modeled or mapped surroundings (a neighbourhood instead of the
|
|
1390
|
+
// horizon disc) the camera may pull back far enough to take it all in.
|
|
1391
|
+
const groundReplaced = useSceneGroundReplacement()
|
|
1392
|
+
const maxDistance = groundReplaced ? 1500 : 100
|
|
1303
1393
|
|
|
1304
1394
|
if (isFirstPersonMode) {
|
|
1305
1395
|
return null
|
|
@@ -1307,8 +1397,10 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1307
1397
|
|
|
1308
1398
|
return (
|
|
1309
1399
|
<CameraControls
|
|
1400
|
+
azimuthRotateSpeed={ORBIT_ROTATE_SPEED}
|
|
1310
1401
|
makeDefault
|
|
1311
|
-
maxDistance={
|
|
1402
|
+
maxDistance={maxDistance}
|
|
1403
|
+
impl={FloorAwareCameraControls}
|
|
1312
1404
|
maxPolarAngle={maxPolarAngle}
|
|
1313
1405
|
minDistance={minDistance}
|
|
1314
1406
|
minPolarAngle={0}
|
|
@@ -1317,6 +1409,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1317
1409
|
onControlStart={handleControlStart}
|
|
1318
1410
|
onUpdate={handleCameraUpdate}
|
|
1319
1411
|
onRest={onRest}
|
|
1412
|
+
polarRotateSpeed={ORBIT_ROTATE_SPEED}
|
|
1320
1413
|
onSleep={onRest}
|
|
1321
1414
|
onTransitionStart={onTransitionStart}
|
|
1322
1415
|
ref={controls}
|