@pascal-app/viewer 1.1.1 → 1.1.3
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 +24 -0
- package/dist/ui/components/viewer-camera-controls.d.ts +7 -0
- package/dist/ui/components/viewer-camera-controls.d.ts.map +1 -0
- package/dist/ui/components/viewer-camera-controls.js +162 -0
- package/dist/ui/components/viewer-opening-camera.d.ts +14 -0
- package/dist/ui/components/viewer-opening-camera.d.ts.map +1 -0
- package/dist/ui/components/viewer-opening-camera.js +32 -0
- package/dist/ui/index.d.ts +3 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +3 -0
- package/dist/ui/lib/opening-camera.d.ts +22 -0
- package/dist/ui/lib/opening-camera.d.ts.map +1 -0
- package/dist/ui/lib/opening-camera.js +42 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -353,6 +353,7 @@ import { LiveWalkthroughController, useViewer, Viewer } from '@pascal-app/viewer
|
|
|
353
353
|
import {
|
|
354
354
|
ParametricViewerRooms,
|
|
355
355
|
SceneLoader,
|
|
356
|
+
ViewerCameraControls,
|
|
356
357
|
ViewerControlsBar,
|
|
357
358
|
ViewerSceneHeader,
|
|
358
359
|
ViewerSelectionManager,
|
|
@@ -367,6 +368,7 @@ function ProjectViewer({ ready, name }: { ready: boolean; name: string }) {
|
|
|
367
368
|
<div className="relative h-full w-full">
|
|
368
369
|
<ViewerStage>
|
|
369
370
|
<Viewer selectionManager="custom">
|
|
371
|
+
<ViewerCameraControls />
|
|
370
372
|
<LiveWalkthroughController />
|
|
371
373
|
<ViewerSelectionManager />
|
|
372
374
|
<ParametricViewerRooms />
|
|
@@ -399,6 +401,28 @@ mounted, zones draw no tinted volume: a room shows through its pill and highligh
|
|
|
399
401
|
`LiveWalkthroughController` moves the camera. Read `useFirstPersonHud` for the HUD's room and
|
|
400
402
|
interaction props, as in the live walkthrough example above.
|
|
401
403
|
|
|
404
|
+
### Camera controls and opening poses
|
|
405
|
+
|
|
406
|
+
Mount `ViewerCameraControls` (`ViewerCameraControlsProps`) inside `Viewer` for right-drag orbit,
|
|
407
|
+
middle-drag or Space + left-drag pan, wheel dolly (orthographic zoom), touch controls and the
|
|
408
|
+
controls bar's frame, top-view and 90° orbit events. It synchronizes navigation with the floor
|
|
409
|
+
plan, opens live scenes on the site's saved camera and unmounts its controls during walkthrough.
|
|
410
|
+
Both optional props default to `false`: `glbActive` lets `GlbScene` frame the baked scene, and
|
|
411
|
+
`holdOpening` leaves the opening view to an explicit pose. Distance limits remain 5–100 metres.
|
|
412
|
+
|
|
413
|
+
`parseOpeningCamera(raw: string | null)` reads `px,py,pz,tx,ty,tz[,fov]` or returns `'saved'`;
|
|
414
|
+
invalid input returns `null`. Coordinates are bounded to ±1000 metres, position and target must
|
|
415
|
+
be at least 0.01 metres apart, and optional FOV is 10–120 degrees. `savedSceneCamera(nodes)`
|
|
416
|
+
resolves the site's saved camera, then the building's, returning `OpeningCameraPose | null`.
|
|
417
|
+
`OpeningCameraPose` has `position` and `target` tuples and an optional `fov`.
|
|
418
|
+
|
|
419
|
+
For an explicit pose, pass `holdOpening={true}` to the controls and mount `ViewerOpeningCamera`
|
|
420
|
+
(`ViewerOpeningCameraProps`) inside `Viewer` with `pose`, `ready` and `onApplied`. Once the scene
|
|
421
|
+
is ready and controls exist, it applies the pose on the next animation frame without a transition,
|
|
422
|
+
updates perspective FOV when supplied, then calls `onApplied`. It applies once per mount; keep
|
|
423
|
+
the loader visible until the callback fires. Resolve `'saved'` through `savedSceneCamera` before
|
|
424
|
+
passing a pose. These components and helpers all export from `@pascal-app/viewer/ui`.
|
|
425
|
+
|
|
402
426
|
## Asset CDN Helpers
|
|
403
427
|
|
|
404
428
|
```typescript
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type ViewerCameraControlsProps = {
|
|
2
|
+
glbActive?: boolean;
|
|
3
|
+
/** A `?camera=` pose owns the opening view. */
|
|
4
|
+
holdOpening?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare const ViewerCameraControls: ({ glbActive, holdOpening, }?: ViewerCameraControlsProps) => import("react").JSX.Element | null;
|
|
7
|
+
//# sourceMappingURL=viewer-camera-controls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewer-camera-controls.d.ts","sourceRoot":"","sources":["../../../src/ui/components/viewer-camera-controls.tsx"],"names":[],"mappings":"AAUA,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,eAAO,MAAM,oBAAoB,iCAG9B,yBAAyB,uCAyM3B,CAAA"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { emitter, useScene } from '@pascal-app/core';
|
|
4
|
+
import { CameraControls, CameraControlsImpl } from '@react-three/drei';
|
|
5
|
+
import { useThree } from '@react-three/fiber';
|
|
6
|
+
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
7
|
+
import useViewer from '../../store/use-viewer';
|
|
8
|
+
import { frameViewerCamera } from '../lib/viewer-selection';
|
|
9
|
+
import { useViewerCameraNavigationSync } from './use-viewer-camera-navigation-sync';
|
|
10
|
+
export const ViewerCameraControls = ({ glbActive = false, holdOpening = false, } = {}) => {
|
|
11
|
+
const controls = useRef(null);
|
|
12
|
+
const site = useScene((s) => Object.values(s.nodes).find((node) => node.type === 'site'));
|
|
13
|
+
const cameraMode = useViewer((s) => s.cameraMode);
|
|
14
|
+
const walkthroughMode = useViewer((s) => s.walkthroughMode);
|
|
15
|
+
const firstLoad = useRef(true);
|
|
16
|
+
const opened = useRef(false);
|
|
17
|
+
const camera = useThree((state) => state.camera);
|
|
18
|
+
const publishNavigationPose = useViewerCameraNavigationSync(controls);
|
|
19
|
+
// Configure mouse buttons - same as editor
|
|
20
|
+
const mouseButtons = useMemo(() => {
|
|
21
|
+
const wheelAction = cameraMode === 'orthographic'
|
|
22
|
+
? CameraControlsImpl.ACTION.ZOOM
|
|
23
|
+
: CameraControlsImpl.ACTION.DOLLY;
|
|
24
|
+
return {
|
|
25
|
+
left: CameraControlsImpl.ACTION.NONE,
|
|
26
|
+
middle: CameraControlsImpl.ACTION.SCREEN_PAN,
|
|
27
|
+
right: CameraControlsImpl.ACTION.ROTATE,
|
|
28
|
+
wheel: wheelAction,
|
|
29
|
+
};
|
|
30
|
+
}, [cameraMode]);
|
|
31
|
+
const touches = useMemo(() => ({
|
|
32
|
+
one: CameraControlsImpl.ACTION.TOUCH_ROTATE,
|
|
33
|
+
three: CameraControlsImpl.ACTION.TOUCH_ROTATE,
|
|
34
|
+
two: cameraMode === 'orthographic'
|
|
35
|
+
? CameraControlsImpl.ACTION.TOUCH_ZOOM_TRUCK
|
|
36
|
+
: CameraControlsImpl.ACTION.TOUCH_DOLLY_TRUCK,
|
|
37
|
+
}), [cameraMode]);
|
|
38
|
+
// The parametric scene opens on the site's saved view (the baked one frames
|
|
39
|
+
// itself in `GlbScene`). After that only the navigation moves the camera.
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!controls.current || glbActive)
|
|
42
|
+
return;
|
|
43
|
+
// A default pose first, unless an opening pose places the camera: the saved
|
|
44
|
+
// view then animates from the pose instead of snapping to the default first.
|
|
45
|
+
if (firstLoad.current) {
|
|
46
|
+
firstLoad.current = false;
|
|
47
|
+
if (!holdOpening)
|
|
48
|
+
controls.current.setLookAt(30, 30, 30, 0, 0, 0, false);
|
|
49
|
+
}
|
|
50
|
+
if (opened.current || !site)
|
|
51
|
+
return;
|
|
52
|
+
opened.current = true;
|
|
53
|
+
if (holdOpening || !site.camera)
|
|
54
|
+
return;
|
|
55
|
+
const { position, target } = site.camera;
|
|
56
|
+
requestAnimationFrame(() => {
|
|
57
|
+
controls.current?.setLookAt(position[0], position[1], position[2], target[0], target[1], target[2], true);
|
|
58
|
+
});
|
|
59
|
+
}, [site, glbActive, holdOpening]);
|
|
60
|
+
// The bar, the floor list and the sidebar frame what they select.
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
const onFrame = (event) => {
|
|
63
|
+
const control = controls.current;
|
|
64
|
+
if (!control)
|
|
65
|
+
return;
|
|
66
|
+
frameViewerCamera(control, camera, event);
|
|
67
|
+
};
|
|
68
|
+
emitter.on('camera-controls:frame', onFrame);
|
|
69
|
+
return () => emitter.off('camera-controls:frame', onFrame);
|
|
70
|
+
}, [camera]);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
const handleTopView = () => {
|
|
73
|
+
if (!controls.current)
|
|
74
|
+
return;
|
|
75
|
+
const currentPolarAngle = controls.current.polarAngle;
|
|
76
|
+
// Toggle: if already near top view (< 0.1 radians ≈ 5.7°), go back to 45°
|
|
77
|
+
// Otherwise, go to top view (0°)
|
|
78
|
+
const targetAngle = currentPolarAngle < 0.1 ? Math.PI / 4 : 0;
|
|
79
|
+
controls.current.rotatePolarTo(targetAngle, true);
|
|
80
|
+
};
|
|
81
|
+
const handleOrbitCW = () => {
|
|
82
|
+
if (!controls.current)
|
|
83
|
+
return;
|
|
84
|
+
const currentAzimuth = controls.current.azimuthAngle;
|
|
85
|
+
const currentPolar = controls.current.polarAngle;
|
|
86
|
+
// Round to nearest 90° increment, then rotate 90° clockwise
|
|
87
|
+
const rounded = Math.round(currentAzimuth / (Math.PI / 2)) * (Math.PI / 2);
|
|
88
|
+
const target = rounded - Math.PI / 2;
|
|
89
|
+
controls.current.rotateTo(target, currentPolar, true);
|
|
90
|
+
};
|
|
91
|
+
const handleOrbitCCW = () => {
|
|
92
|
+
if (!controls.current)
|
|
93
|
+
return;
|
|
94
|
+
const currentAzimuth = controls.current.azimuthAngle;
|
|
95
|
+
const currentPolar = controls.current.polarAngle;
|
|
96
|
+
// Round to nearest 90° increment, then rotate 90° counter-clockwise
|
|
97
|
+
const rounded = Math.round(currentAzimuth / (Math.PI / 2)) * (Math.PI / 2);
|
|
98
|
+
const target = rounded + Math.PI / 2;
|
|
99
|
+
controls.current.rotateTo(target, currentPolar, true);
|
|
100
|
+
};
|
|
101
|
+
emitter.on('camera-controls:top-view', handleTopView);
|
|
102
|
+
emitter.on('camera-controls:orbit-cw', handleOrbitCW);
|
|
103
|
+
emitter.on('camera-controls:orbit-ccw', handleOrbitCCW);
|
|
104
|
+
return () => {
|
|
105
|
+
emitter.off('camera-controls:top-view', handleTopView);
|
|
106
|
+
emitter.off('camera-controls:orbit-cw', handleOrbitCW);
|
|
107
|
+
emitter.off('camera-controls:orbit-ccw', handleOrbitCCW);
|
|
108
|
+
};
|
|
109
|
+
}, []);
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
const keyState = {
|
|
112
|
+
space: false,
|
|
113
|
+
};
|
|
114
|
+
const updateConfig = () => {
|
|
115
|
+
if (!controls.current)
|
|
116
|
+
return;
|
|
117
|
+
const wheelAction = cameraMode === 'orthographic'
|
|
118
|
+
? CameraControlsImpl.ACTION.ZOOM
|
|
119
|
+
: CameraControlsImpl.ACTION.DOLLY;
|
|
120
|
+
controls.current.mouseButtons.wheel = wheelAction;
|
|
121
|
+
controls.current.mouseButtons.middle = CameraControlsImpl.ACTION.SCREEN_PAN;
|
|
122
|
+
controls.current.mouseButtons.right = CameraControlsImpl.ACTION.ROTATE;
|
|
123
|
+
controls.current.mouseButtons.left = keyState.space
|
|
124
|
+
? CameraControlsImpl.ACTION.SCREEN_PAN
|
|
125
|
+
: CameraControlsImpl.ACTION.NONE;
|
|
126
|
+
};
|
|
127
|
+
const onKeyDown = (event) => {
|
|
128
|
+
if (event.code !== 'Space')
|
|
129
|
+
return;
|
|
130
|
+
keyState.space = true;
|
|
131
|
+
document.body.style.cursor = 'grab';
|
|
132
|
+
updateConfig();
|
|
133
|
+
};
|
|
134
|
+
const onKeyUp = (event) => {
|
|
135
|
+
if (event.code !== 'Space')
|
|
136
|
+
return;
|
|
137
|
+
keyState.space = false;
|
|
138
|
+
document.body.style.cursor = '';
|
|
139
|
+
updateConfig();
|
|
140
|
+
};
|
|
141
|
+
document.addEventListener('keydown', onKeyDown);
|
|
142
|
+
document.addEventListener('keyup', onKeyUp);
|
|
143
|
+
updateConfig();
|
|
144
|
+
return () => {
|
|
145
|
+
document.removeEventListener('keydown', onKeyDown);
|
|
146
|
+
document.removeEventListener('keyup', onKeyUp);
|
|
147
|
+
if (document.body.style.cursor === 'grab') {
|
|
148
|
+
document.body.style.cursor = '';
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}, [cameraMode]);
|
|
152
|
+
const onTransitionStart = useCallback(() => {
|
|
153
|
+
useViewer.getState().setCameraDragging(true);
|
|
154
|
+
}, []);
|
|
155
|
+
const onRest = useCallback(() => {
|
|
156
|
+
useViewer.getState().setCameraDragging(false);
|
|
157
|
+
}, []);
|
|
158
|
+
if (walkthroughMode) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
return (_jsx(CameraControls, { makeDefault: true, maxDistance: 100, maxPolarAngle: Math.PI / 2 - 0.1, minDistance: 5, minPolarAngle: 0, mouseButtons: mouseButtons, onRest: onRest, onSleep: onRest, onTransitionStart: onTransitionStart, onUpdate: publishNavigationPose, ref: controls, restThreshold: 0.01, touches: touches }));
|
|
162
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { OpeningCameraPose } from '../lib/opening-camera';
|
|
2
|
+
export type ViewerOpeningCameraProps = {
|
|
3
|
+
onApplied: () => void;
|
|
4
|
+
pose: OpeningCameraPose;
|
|
5
|
+
ready: boolean;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Puts the camera on the opening pose once the scene is on screen. Both
|
|
9
|
+
* framing paths move the camera first (`ViewerCameraControls` to the site's
|
|
10
|
+
* bookmark, `GlbScene` to the building), so the pose lands a frame after
|
|
11
|
+
* readiness and without a transition: the last move is the one that sticks.
|
|
12
|
+
*/
|
|
13
|
+
export declare function ViewerOpeningCamera({ onApplied, pose, ready }: ViewerOpeningCameraProps): null;
|
|
14
|
+
//# sourceMappingURL=viewer-opening-camera.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewer-opening-camera.d.ts","sourceRoot":"","sources":["../../../src/ui/components/viewer-opening-camera.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAc9D,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,IAAI,EAAE,iBAAiB,CAAA;IACvB,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,wBAAwB,QAuBvF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useThree } from '@react-three/fiber';
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
/**
|
|
5
|
+
* Puts the camera on the opening pose once the scene is on screen. Both
|
|
6
|
+
* framing paths move the camera first (`ViewerCameraControls` to the site's
|
|
7
|
+
* bookmark, `GlbScene` to the building), so the pose lands a frame after
|
|
8
|
+
* readiness and without a transition: the last move is the one that sticks.
|
|
9
|
+
*/
|
|
10
|
+
export function ViewerOpeningCamera({ onApplied, pose, ready }) {
|
|
11
|
+
const controls = useThree((state) => state.controls);
|
|
12
|
+
const camera = useThree((state) => state.camera);
|
|
13
|
+
const applied = useRef(false);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (applied.current || !(ready && controls))
|
|
16
|
+
return;
|
|
17
|
+
const frame = requestAnimationFrame(() => {
|
|
18
|
+
applied.current = true;
|
|
19
|
+
const perspective = camera;
|
|
20
|
+
if (pose.fov !== undefined && perspective.isPerspectiveCamera) {
|
|
21
|
+
perspective.fov = pose.fov;
|
|
22
|
+
perspective.updateProjectionMatrix();
|
|
23
|
+
}
|
|
24
|
+
const [px, py, pz] = pose.position;
|
|
25
|
+
const [tx, ty, tz] = pose.target;
|
|
26
|
+
controls.setLookAt(px, py, pz, tx, ty, tz, false);
|
|
27
|
+
onApplied();
|
|
28
|
+
});
|
|
29
|
+
return () => cancelAnimationFrame(frame);
|
|
30
|
+
}, [camera, controls, onApplied, pose, ready]);
|
|
31
|
+
return null;
|
|
32
|
+
}
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -11,8 +11,10 @@ export { ParametricViewerRooms, ViewerRooms } from './components/rooms/viewer-ro
|
|
|
11
11
|
export { SceneLoader, SceneLoadFailed } from './components/scene-loader';
|
|
12
12
|
export { openSkyPanel, SKY_DOCK_HEIGHT, SkyControl, useSkyPanel } from './components/sky-control';
|
|
13
13
|
export { useViewerCameraNavigationSync } from './components/use-viewer-camera-navigation-sync';
|
|
14
|
+
export { ViewerCameraControls, type ViewerCameraControlsProps, } from './components/viewer-camera-controls';
|
|
14
15
|
export { ViewerControlsBar, type ViewerControlsBarProps } from './components/viewer-controls-bar';
|
|
15
16
|
export { ViewerHoverLabel, viewerHoverLabel } from './components/viewer-hover-label';
|
|
17
|
+
export { ViewerOpeningCamera, type ViewerOpeningCameraProps, } from './components/viewer-opening-camera';
|
|
16
18
|
export { type ViewerLinkProps, ViewerSceneHeader, type ViewerSceneHeaderProps, } from './components/viewer-scene-header';
|
|
17
19
|
export { ViewerSelectionManager } from './components/viewer-selection-manager';
|
|
18
20
|
export { ViewerStage, type ViewerStageProps } from './components/viewer-stage';
|
|
@@ -30,6 +32,7 @@ export { belowGradeShown, followBelowGrade, followGroundWindowBox, gradeStanding
|
|
|
30
32
|
export { type CameraPoseApplicationPlan, normalizeCameraPose, planCameraPoseApplication, publishInitialCameraPose, releaseCameraPoseEventSuppression, stepCameraPoseInterpolation, withCameraPoseDistance, } from './lib/camera-pose';
|
|
31
33
|
export { authoredLevelName, localizedLevelName } from './lib/level-name';
|
|
32
34
|
export { nodeDisplayLabel } from './lib/node-display-label';
|
|
35
|
+
export { type OpeningCameraPose, parseOpeningCamera, savedSceneCamera, } from './lib/opening-camera';
|
|
33
36
|
export { formatRoomArea } from './lib/room-area';
|
|
34
37
|
export { type RoomAssemblyHeights, resolveRoomAssemblyHeights } from './lib/room-assembly-overlay';
|
|
35
38
|
export { type DescriptorTranslator, displayRoomName, editedRoomName, ROOM_NAME_MESSAGES, } from './lib/room-name-messages';
|
package/dist/ui/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"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"}
|
package/dist/ui/index.js
CHANGED
|
@@ -11,8 +11,10 @@ export { ParametricViewerRooms, ViewerRooms } from './components/rooms/viewer-ro
|
|
|
11
11
|
export { SceneLoader, SceneLoadFailed } from './components/scene-loader';
|
|
12
12
|
export { openSkyPanel, SKY_DOCK_HEIGHT, SkyControl, useSkyPanel } from './components/sky-control';
|
|
13
13
|
export { useViewerCameraNavigationSync } from './components/use-viewer-camera-navigation-sync';
|
|
14
|
+
export { ViewerCameraControls, } from './components/viewer-camera-controls';
|
|
14
15
|
export { ViewerControlsBar } from './components/viewer-controls-bar';
|
|
15
16
|
export { ViewerHoverLabel, viewerHoverLabel } from './components/viewer-hover-label';
|
|
17
|
+
export { ViewerOpeningCamera, } from './components/viewer-opening-camera';
|
|
16
18
|
export { ViewerSceneHeader, } from './components/viewer-scene-header';
|
|
17
19
|
export { ViewerSelectionManager } from './components/viewer-selection-manager';
|
|
18
20
|
export { ViewerStage } from './components/viewer-stage';
|
|
@@ -30,6 +32,7 @@ export { belowGradeShown, followBelowGrade, followGroundWindowBox, gradeStanding
|
|
|
30
32
|
export { normalizeCameraPose, planCameraPoseApplication, publishInitialCameraPose, releaseCameraPoseEventSuppression, stepCameraPoseInterpolation, withCameraPoseDistance, } from './lib/camera-pose';
|
|
31
33
|
export { authoredLevelName, localizedLevelName } from './lib/level-name';
|
|
32
34
|
export { nodeDisplayLabel } from './lib/node-display-label';
|
|
35
|
+
export { parseOpeningCamera, savedSceneCamera, } from './lib/opening-camera';
|
|
33
36
|
export { formatRoomArea } from './lib/room-area';
|
|
34
37
|
export { resolveRoomAssemblyHeights } from './lib/room-assembly-overlay';
|
|
35
38
|
export { displayRoomName, editedRoomName, ROOM_NAME_MESSAGES, } from './lib/room-name-messages';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AnyNode } from '@pascal-app/core';
|
|
2
|
+
type Vec3 = [number, number, number];
|
|
3
|
+
export type OpeningCameraPose = {
|
|
4
|
+
position: Vec3;
|
|
5
|
+
target: Vec3;
|
|
6
|
+
fov?: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Reads `?camera=`: `px,py,pz,tx,ty,tz[,fov]` in scene metres and degrees, or
|
|
10
|
+
* `saved` for the view saved on the scene. Anything else is null, so a
|
|
11
|
+
* malformed link opens on the default view instead of a broken one.
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseOpeningCamera(raw: string | null): OpeningCameraPose | 'saved' | null;
|
|
14
|
+
/**
|
|
15
|
+
* The view saved on the scene: the camera captured on the site in the editor,
|
|
16
|
+
* else the one on the building. Both viewer paths already open near it by
|
|
17
|
+
* default, but they disagree on which node wins (the parametric viewer reads
|
|
18
|
+
* the site, the baked one the building); `camera=saved` pins one rule.
|
|
19
|
+
*/
|
|
20
|
+
export declare function savedSceneCamera(nodes: Readonly<Record<string, AnyNode>>): OpeningCameraPose | null;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=opening-camera.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opening-camera.d.ts","sourceRoot":"","sources":["../../../src/ui/lib/opening-camera.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAE/C,KAAK,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAEpC,MAAM,MAAM,iBAAiB,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,IAAI,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAO9E;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB,GAAG,OAAO,GAAG,IAAI,CAqBzF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,iBAAiB,GAAG,IAAI,CAM1B"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// The viewer camera's far plane is 1000 m; a pose beyond it frames nothing.
|
|
2
|
+
const MAX_COORDINATE = 1000;
|
|
3
|
+
const MIN_FOV = 10;
|
|
4
|
+
const MAX_FOV = 120;
|
|
5
|
+
/**
|
|
6
|
+
* Reads `?camera=`: `px,py,pz,tx,ty,tz[,fov]` in scene metres and degrees, or
|
|
7
|
+
* `saved` for the view saved on the scene. Anything else is null, so a
|
|
8
|
+
* malformed link opens on the default view instead of a broken one.
|
|
9
|
+
*/
|
|
10
|
+
export function parseOpeningCamera(raw) {
|
|
11
|
+
if (raw === 'saved')
|
|
12
|
+
return 'saved';
|
|
13
|
+
const parts = raw?.split(',') ?? [];
|
|
14
|
+
if (parts.length !== 6 && parts.length !== 7)
|
|
15
|
+
return null;
|
|
16
|
+
if (parts.some((part) => part.trim() === ''))
|
|
17
|
+
return null;
|
|
18
|
+
const values = parts.map(Number);
|
|
19
|
+
const [px, py, pz, tx, ty, tz, fov] = values;
|
|
20
|
+
if (values.slice(0, 6).some((value) => !(Math.abs(value) <= MAX_COORDINATE)))
|
|
21
|
+
return null;
|
|
22
|
+
if (Math.hypot(px - tx, py - ty, pz - tz) < 0.01)
|
|
23
|
+
return null;
|
|
24
|
+
const pose = { position: [px, py, pz], target: [tx, ty, tz] };
|
|
25
|
+
if (fov === undefined)
|
|
26
|
+
return pose;
|
|
27
|
+
if (!(fov >= MIN_FOV && fov <= MAX_FOV))
|
|
28
|
+
return null;
|
|
29
|
+
return { ...pose, fov };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The view saved on the scene: the camera captured on the site in the editor,
|
|
33
|
+
* else the one on the building. Both viewer paths already open near it by
|
|
34
|
+
* default, but they disagree on which node wins (the parametric viewer reads
|
|
35
|
+
* the site, the baked one the building); `camera=saved` pins one rule.
|
|
36
|
+
*/
|
|
37
|
+
export function savedSceneCamera(nodes) {
|
|
38
|
+
const all = Object.values(nodes);
|
|
39
|
+
const saved = all.find((node) => node.type === 'site')?.camera ??
|
|
40
|
+
all.find((node) => node.type === 'building')?.camera;
|
|
41
|
+
return saved ? { position: saved.position, target: saved.target } : null;
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pascal-app/viewer",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "3D viewer component for Pascal building editor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"prepublishOnly": "npm run build"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@pascal-app/core": "^1.1.
|
|
51
|
+
"@pascal-app/core": "^1.1.3",
|
|
52
52
|
"@react-three/drei": "^10",
|
|
53
53
|
"@react-three/fiber": "^9",
|
|
54
54
|
"react": "^19",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"zustand": "^5"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
|
-
"@pascal-app/core": "^1.1.
|
|
75
|
+
"@pascal-app/core": "^1.1.3",
|
|
76
76
|
"@pascal/typescript-config": "*",
|
|
77
77
|
"@react-three/test-renderer": "^9.1.1",
|
|
78
78
|
"@types/howler": "^2.2.13",
|