worldorbit 2.6.0 → 3.0.1
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 +20 -9
- package/dist/browser/core/dist/atlas-edit.d.ts +11 -0
- package/dist/browser/core/dist/atlas-edit.js +347 -0
- package/dist/browser/core/dist/atlas-utils.d.ts +22 -0
- package/dist/browser/core/dist/atlas-utils.js +189 -0
- package/dist/browser/core/dist/atlas-validate.d.ts +2 -0
- package/dist/browser/core/dist/atlas-validate.js +488 -0
- package/dist/browser/core/dist/diagnostics.d.ts +10 -0
- package/dist/browser/core/dist/diagnostics.js +109 -0
- package/dist/browser/core/dist/draft-parse.d.ts +3 -0
- package/dist/browser/core/dist/draft-parse.js +1654 -0
- package/dist/browser/core/dist/draft.d.ts +21 -0
- package/dist/browser/core/dist/draft.js +482 -0
- package/dist/browser/core/dist/errors.d.ts +7 -0
- package/dist/browser/core/dist/errors.js +16 -0
- package/dist/browser/core/dist/format.d.ts +4 -0
- package/dist/browser/core/dist/format.js +613 -0
- package/dist/browser/core/dist/index.d.ts +29 -0
- package/dist/browser/core/dist/index.js +35 -6542
- package/dist/browser/core/dist/load.d.ts +4 -0
- package/dist/browser/core/dist/load.js +182 -0
- package/dist/browser/core/dist/markdown.d.ts +2 -0
- package/dist/browser/core/dist/markdown.js +37 -0
- package/dist/browser/core/dist/normalize.d.ts +2 -0
- package/dist/browser/core/dist/normalize.js +312 -0
- package/dist/browser/core/dist/parse.d.ts +2 -0
- package/dist/browser/core/dist/parse.js +133 -0
- package/dist/browser/core/dist/scene.d.ts +3 -0
- package/dist/browser/core/dist/scene.js +1901 -0
- package/dist/browser/core/dist/schema.d.ts +8 -0
- package/dist/browser/core/dist/schema.js +298 -0
- package/dist/browser/core/dist/spatial-scene.d.ts +3 -0
- package/dist/browser/core/dist/spatial-scene.js +420 -0
- package/dist/browser/core/dist/tokenize.d.ts +4 -0
- package/dist/browser/core/dist/tokenize.js +68 -0
- package/dist/browser/core/dist/types.d.ts +637 -0
- package/dist/browser/core/dist/types.js +1 -0
- package/dist/browser/core/dist/validate.d.ts +2 -0
- package/dist/browser/core/dist/validate.js +56 -0
- package/dist/browser/editor/dist/editor.d.ts +2 -0
- package/dist/browser/editor/dist/editor.js +3700 -0
- package/dist/browser/editor/dist/index.d.ts +2 -0
- package/dist/browser/editor/dist/index.js +1 -12250
- package/dist/browser/editor/dist/types.d.ts +59 -0
- package/dist/browser/editor/dist/types.js +1 -0
- package/dist/browser/markdown/dist/html.d.ts +3 -0
- package/dist/browser/markdown/dist/html.js +64 -0
- package/dist/browser/markdown/dist/index.d.ts +4 -0
- package/dist/browser/markdown/dist/index.js +3 -6179
- package/dist/browser/markdown/dist/rehype.d.ts +10 -0
- package/dist/browser/markdown/dist/rehype.js +49 -0
- package/dist/browser/markdown/dist/remark.d.ts +9 -0
- package/dist/browser/markdown/dist/remark.js +28 -0
- package/dist/browser/markdown/dist/types.d.ts +11 -0
- package/dist/browser/markdown/dist/types.js +1 -0
- package/dist/browser/viewer/dist/atlas-state.d.ts +12 -0
- package/dist/browser/viewer/dist/atlas-state.js +269 -0
- package/dist/browser/viewer/dist/atlas-viewer.d.ts +2 -0
- package/dist/browser/viewer/dist/atlas-viewer.js +495 -0
- package/dist/browser/viewer/dist/custom-element.d.ts +1 -0
- package/dist/browser/viewer/dist/custom-element.js +78 -0
- package/dist/browser/viewer/dist/embed.d.ts +24 -0
- package/dist/browser/viewer/dist/embed.js +172 -0
- package/dist/browser/viewer/dist/errors.d.ts +6 -0
- package/dist/browser/viewer/dist/errors.js +12 -0
- package/dist/browser/viewer/dist/index.d.ts +10 -0
- package/dist/browser/viewer/dist/index.js +9 -8334
- package/dist/browser/viewer/dist/minimap.d.ts +3 -0
- package/dist/browser/viewer/dist/minimap.js +63 -0
- package/dist/browser/viewer/dist/render.d.ts +6 -0
- package/dist/browser/viewer/dist/render.js +670 -0
- package/dist/browser/viewer/dist/runtime-3d.d.ts +19 -0
- package/dist/browser/viewer/dist/runtime-3d.js +494 -0
- package/dist/browser/viewer/dist/theme.d.ts +4 -0
- package/dist/browser/viewer/dist/theme.js +103 -0
- package/dist/browser/viewer/dist/tooltip.d.ts +3 -0
- package/dist/browser/viewer/dist/tooltip.js +198 -0
- package/dist/browser/viewer/dist/types.d.ts +292 -0
- package/dist/browser/viewer/dist/types.js +1 -0
- package/dist/browser/viewer/dist/vendor/three.module.js +53032 -0
- package/dist/browser/viewer/dist/viewer-state.d.ts +19 -0
- package/dist/browser/viewer/dist/viewer-state.js +162 -0
- package/dist/browser/viewer/dist/viewer.d.ts +2 -0
- package/dist/browser/viewer/dist/viewer.js +1662 -0
- package/dist/unpkg/core/dist/atlas-edit.d.ts +11 -0
- package/dist/unpkg/core/dist/atlas-edit.js +347 -0
- package/dist/unpkg/core/dist/atlas-utils.d.ts +22 -0
- package/dist/unpkg/core/dist/atlas-utils.js +189 -0
- package/dist/unpkg/core/dist/atlas-validate.d.ts +2 -0
- package/dist/unpkg/core/dist/atlas-validate.js +488 -0
- package/dist/unpkg/core/dist/diagnostics.d.ts +10 -0
- package/dist/unpkg/core/dist/diagnostics.js +109 -0
- package/dist/unpkg/core/dist/draft-parse.d.ts +3 -0
- package/dist/unpkg/core/dist/draft-parse.js +1654 -0
- package/dist/unpkg/core/dist/draft.d.ts +21 -0
- package/dist/unpkg/core/dist/draft.js +482 -0
- package/dist/unpkg/core/dist/errors.d.ts +7 -0
- package/dist/unpkg/core/dist/errors.js +16 -0
- package/dist/unpkg/core/dist/format.d.ts +4 -0
- package/dist/unpkg/core/dist/format.js +613 -0
- package/dist/unpkg/core/dist/index.d.ts +29 -0
- package/dist/unpkg/core/dist/index.js +35 -6614
- package/dist/unpkg/core/dist/load.d.ts +4 -0
- package/dist/unpkg/core/dist/load.js +182 -0
- package/dist/unpkg/core/dist/markdown.d.ts +2 -0
- package/dist/unpkg/core/dist/markdown.js +37 -0
- package/dist/unpkg/core/dist/normalize.d.ts +2 -0
- package/dist/unpkg/core/dist/normalize.js +312 -0
- package/dist/unpkg/core/dist/parse.d.ts +2 -0
- package/dist/unpkg/core/dist/parse.js +133 -0
- package/dist/unpkg/core/dist/scene.d.ts +3 -0
- package/dist/unpkg/core/dist/scene.js +1901 -0
- package/dist/unpkg/core/dist/schema.d.ts +8 -0
- package/dist/unpkg/core/dist/schema.js +298 -0
- package/dist/unpkg/core/dist/spatial-scene.d.ts +3 -0
- package/dist/unpkg/core/dist/spatial-scene.js +420 -0
- package/dist/unpkg/core/dist/tokenize.d.ts +4 -0
- package/dist/unpkg/core/dist/tokenize.js +68 -0
- package/dist/unpkg/core/dist/types.d.ts +637 -0
- package/dist/unpkg/core/dist/types.js +1 -0
- package/dist/unpkg/core/dist/validate.d.ts +2 -0
- package/dist/unpkg/core/dist/validate.js +56 -0
- package/dist/unpkg/editor/dist/editor.d.ts +2 -0
- package/dist/unpkg/editor/dist/editor.js +3700 -0
- package/dist/unpkg/editor/dist/index.d.ts +2 -0
- package/dist/unpkg/editor/dist/index.js +1 -12275
- package/dist/unpkg/editor/dist/types.d.ts +59 -0
- package/dist/unpkg/editor/dist/types.js +1 -0
- package/dist/unpkg/markdown/dist/html.d.ts +3 -0
- package/dist/unpkg/markdown/dist/html.js +64 -0
- package/dist/unpkg/markdown/dist/index.d.ts +4 -0
- package/dist/unpkg/markdown/dist/index.js +3 -6207
- package/dist/unpkg/markdown/dist/rehype.d.ts +10 -0
- package/dist/unpkg/markdown/dist/rehype.js +49 -0
- package/dist/unpkg/markdown/dist/remark.d.ts +9 -0
- package/dist/unpkg/markdown/dist/remark.js +28 -0
- package/dist/unpkg/markdown/dist/types.d.ts +11 -0
- package/dist/unpkg/markdown/dist/types.js +1 -0
- package/dist/unpkg/viewer/dist/atlas-state.d.ts +12 -0
- package/dist/unpkg/viewer/dist/atlas-state.js +269 -0
- package/dist/unpkg/viewer/dist/atlas-viewer.d.ts +2 -0
- package/dist/unpkg/viewer/dist/atlas-viewer.js +495 -0
- package/dist/unpkg/viewer/dist/custom-element.d.ts +1 -0
- package/dist/unpkg/viewer/dist/custom-element.js +78 -0
- package/dist/unpkg/viewer/dist/embed.d.ts +24 -0
- package/dist/unpkg/viewer/dist/embed.js +172 -0
- package/dist/unpkg/viewer/dist/errors.d.ts +6 -0
- package/dist/unpkg/viewer/dist/errors.js +12 -0
- package/dist/unpkg/viewer/dist/index.d.ts +10 -0
- package/dist/unpkg/viewer/dist/index.js +9 -8391
- package/dist/unpkg/viewer/dist/minimap.d.ts +3 -0
- package/dist/unpkg/viewer/dist/minimap.js +63 -0
- package/dist/unpkg/viewer/dist/render.d.ts +6 -0
- package/dist/unpkg/viewer/dist/render.js +670 -0
- package/dist/unpkg/viewer/dist/runtime-3d.d.ts +19 -0
- package/dist/unpkg/viewer/dist/runtime-3d.js +494 -0
- package/dist/unpkg/viewer/dist/theme.d.ts +4 -0
- package/dist/unpkg/viewer/dist/theme.js +103 -0
- package/dist/unpkg/viewer/dist/tooltip.d.ts +3 -0
- package/dist/unpkg/viewer/dist/tooltip.js +198 -0
- package/dist/unpkg/viewer/dist/types.d.ts +292 -0
- package/dist/unpkg/viewer/dist/types.js +1 -0
- package/dist/unpkg/viewer/dist/vendor/three.module.js +53032 -0
- package/dist/unpkg/viewer/dist/viewer-state.d.ts +19 -0
- package/dist/unpkg/viewer/dist/viewer-state.js +162 -0
- package/dist/unpkg/viewer/dist/viewer.d.ts +2 -0
- package/dist/unpkg/viewer/dist/viewer.js +1662 -0
- package/dist/unpkg/worldorbit-core.min.js +10 -10
- package/dist/unpkg/worldorbit-editor.min.js +4109 -256
- package/dist/unpkg/worldorbit-markdown.min.js +26 -26
- package/dist/unpkg/worldorbit-viewer.min.js +3945 -92
- package/dist/unpkg/worldorbit.js +32219 -199
- package/dist/unpkg/worldorbit.min.js +3949 -96
- package/package.json +1 -1
- package/packages/core/dist/index.d.ts +1 -0
- package/packages/core/dist/index.js +1 -0
- package/packages/core/dist/spatial-scene.d.ts +3 -0
- package/packages/core/dist/spatial-scene.js +420 -0
- package/packages/core/dist/types.d.ts +105 -0
- package/packages/editor/dist/editor.js +25 -4
- package/packages/editor/dist/types.d.ts +4 -0
- package/packages/markdown/dist/html.js +10 -3
- package/packages/viewer/dist/atlas-state.js +3 -0
- package/packages/viewer/dist/atlas-viewer.js +1 -0
- package/packages/viewer/dist/custom-element.js +18 -4
- package/packages/viewer/dist/embed.d.ts +5 -1
- package/packages/viewer/dist/embed.js +58 -24
- package/packages/viewer/dist/errors.d.ts +6 -0
- package/packages/viewer/dist/errors.js +12 -0
- package/packages/viewer/dist/index.d.ts +1 -0
- package/packages/viewer/dist/index.js +1 -0
- package/packages/viewer/dist/runtime-3d.d.ts +19 -0
- package/packages/viewer/dist/runtime-3d.js +494 -0
- package/packages/viewer/dist/types.d.ts +21 -2
- package/packages/viewer/dist/vendor/three.module.js +53032 -0
- package/packages/viewer/dist/viewer.js +501 -41
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { loadWorldOrbitSource, renderDocumentToScene, rotatePoint, } from "@worldorbit/core";
|
|
1
|
+
import { loadWorldOrbitSource, renderDocumentToScene, renderDocumentToSpatialScene, rotatePoint, } from "@worldorbit/core";
|
|
2
2
|
import { computeVisibleObjectIds, createAtlasStateSnapshot, createViewerBookmark, deserializeViewerAtlasState, normalizeViewerFilter, sceneViewpointToLayerOptions, searchSceneObjects, serializeViewerAtlasState, viewpointToViewerFilter, } from "./atlas-state.js";
|
|
3
3
|
import { renderViewerMinimap } from "./minimap.js";
|
|
4
4
|
import { renderSceneToSvg } from "./render.js";
|
|
5
|
+
import { createViewer3DRuntime, } from "./runtime-3d.js";
|
|
5
6
|
import { buildViewerTooltipDetails, renderDefaultTooltipContent, } from "./tooltip.js";
|
|
6
7
|
import { DEFAULT_VIEWER_STATE, composeViewerTransform, fitViewerState, focusViewerState, invertViewerPoint, panViewerState, rotateViewerState, zoomViewerStateAt, } from "./viewer-state.js";
|
|
7
8
|
const DEFAULT_VIEWER_LIMITS = {
|
|
@@ -43,6 +44,7 @@ export function createInteractiveViewer(container, options) {
|
|
|
43
44
|
padding: options.padding,
|
|
44
45
|
preset: options.preset,
|
|
45
46
|
projection: options.projection,
|
|
47
|
+
viewMode: options.viewMode ?? "2d",
|
|
46
48
|
camera: options.camera ? { ...options.camera } : null,
|
|
47
49
|
scaleModel: options.scaleModel ? { ...options.scaleModel } : undefined,
|
|
48
50
|
theme: options.theme,
|
|
@@ -55,9 +57,14 @@ export function createInteractiveViewer(container, options) {
|
|
|
55
57
|
const previousPosition = container.style.position;
|
|
56
58
|
let currentInput = resolveInitialInput(options);
|
|
57
59
|
let scene = renderSceneFromInput(currentInput, renderOptions);
|
|
60
|
+
let providedSpatialScene = options.spatialScene ?? null;
|
|
61
|
+
let spatialScene = renderOptions.viewMode === "3d"
|
|
62
|
+
? renderSpatialSceneFromInput(currentInput, renderOptions, providedSpatialScene)
|
|
63
|
+
: null;
|
|
58
64
|
let state = { ...DEFAULT_VIEWER_STATE };
|
|
59
65
|
let svgElement = null;
|
|
60
66
|
let cameraRoot = null;
|
|
67
|
+
let runtime3d = null;
|
|
61
68
|
let minimapRoot = null;
|
|
62
69
|
let tooltipRoot = null;
|
|
63
70
|
let suppressClick = false;
|
|
@@ -72,6 +79,14 @@ export function createInteractiveViewer(container, options) {
|
|
|
72
79
|
let activeTooltipObjectId = null;
|
|
73
80
|
let activeTooltipDetails = null;
|
|
74
81
|
let activeViewpointId = null;
|
|
82
|
+
let animationFrameId = null;
|
|
83
|
+
let lastAnimationTimestamp = null;
|
|
84
|
+
let animationState = {
|
|
85
|
+
playing: false,
|
|
86
|
+
speed: 1,
|
|
87
|
+
timeSeconds: 0,
|
|
88
|
+
frozenByEvent: spatialScene?.timeFrozen ?? false,
|
|
89
|
+
};
|
|
75
90
|
if (previousTabIndex === null) {
|
|
76
91
|
container.tabIndex = 0;
|
|
77
92
|
}
|
|
@@ -81,12 +96,18 @@ export function createInteractiveViewer(container, options) {
|
|
|
81
96
|
if (!container.style.position) {
|
|
82
97
|
container.style.position = "relative";
|
|
83
98
|
}
|
|
99
|
+
syncAnimationFrozenState();
|
|
84
100
|
const handleWheel = (event) => {
|
|
85
101
|
if (!behavior.pointer || destroyed) {
|
|
86
102
|
return;
|
|
87
103
|
}
|
|
88
104
|
event.preventDefault();
|
|
89
105
|
container.focus();
|
|
106
|
+
if (is3DView()) {
|
|
107
|
+
const factor = clampValue(Math.exp(-event.deltaY * 0.002), 0.6, 1.6);
|
|
108
|
+
api.zoomBy(factor);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
90
111
|
const anchor = getWorldPointFromClient(event.clientX, event.clientY);
|
|
91
112
|
const factor = clampValue(Math.exp(-event.deltaY * 0.002), 0.6, 1.6);
|
|
92
113
|
updateState(zoomViewerStateAt(scene, state, factor, anchor, constraints));
|
|
@@ -99,7 +120,7 @@ export function createInteractiveViewer(container, options) {
|
|
|
99
120
|
if ((isTouch && !behavior.touch) || (!isTouch && !behavior.pointer)) {
|
|
100
121
|
return;
|
|
101
122
|
}
|
|
102
|
-
if (!isTouch && event.button !== 0) {
|
|
123
|
+
if (!isTouch && event.button !== 0 && !is3DView()) {
|
|
103
124
|
return;
|
|
104
125
|
}
|
|
105
126
|
container.focus();
|
|
@@ -127,6 +148,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
127
148
|
}
|
|
128
149
|
const isTouch = event.pointerType === "touch";
|
|
129
150
|
if (isTouch) {
|
|
151
|
+
if (is3DView()) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
130
154
|
if (!behavior.touch || !touchPoints.has(event.pointerId)) {
|
|
131
155
|
return;
|
|
132
156
|
}
|
|
@@ -155,6 +179,29 @@ export function createInteractiveViewer(container, options) {
|
|
|
155
179
|
}
|
|
156
180
|
return;
|
|
157
181
|
}
|
|
182
|
+
if (is3DView() && behavior.pointer && activePointerId === null) {
|
|
183
|
+
applyHover(runtime3d?.hitTest(event.clientX, event.clientY) ?? null);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (is3DView() && behavior.pointer && activePointerId === event.pointerId && lastPointerPoint) {
|
|
187
|
+
const nextPoint = getViewportPointFromClient(event.clientX, event.clientY);
|
|
188
|
+
const deltaX = nextPoint.x - lastPointerPoint.x;
|
|
189
|
+
const deltaY = nextPoint.y - lastPointerPoint.y;
|
|
190
|
+
dragDistance += Math.abs(deltaX) + Math.abs(deltaY);
|
|
191
|
+
lastPointerPoint = nextPoint;
|
|
192
|
+
if (dragDistance > 2) {
|
|
193
|
+
suppressClick = true;
|
|
194
|
+
}
|
|
195
|
+
if (event.shiftKey || event.buttons === 2) {
|
|
196
|
+
api.panBy(deltaX, deltaY);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
api.rotateBy(deltaX * 0.35);
|
|
200
|
+
api.panBy(0, deltaY * 0.35);
|
|
201
|
+
}
|
|
202
|
+
applyHover(runtime3d?.hitTest(event.clientX, event.clientY) ?? null);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
158
205
|
if (!behavior.pointer || activePointerId !== event.pointerId || !lastPointerPoint) {
|
|
159
206
|
return;
|
|
160
207
|
}
|
|
@@ -189,7 +236,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
189
236
|
suppressClick = false;
|
|
190
237
|
return;
|
|
191
238
|
}
|
|
192
|
-
const objectId =
|
|
239
|
+
const objectId = is3DView()
|
|
240
|
+
? runtime3d?.hitTest(event.clientX, event.clientY) ?? null
|
|
241
|
+
: getClosestObjectId(event.target);
|
|
193
242
|
applySelection(objectId);
|
|
194
243
|
if (behavior.tooltipMode === "pinned") {
|
|
195
244
|
pinnedTooltipObjectId = objectId;
|
|
@@ -197,6 +246,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
197
246
|
}
|
|
198
247
|
};
|
|
199
248
|
const handleMouseOver = (event) => {
|
|
249
|
+
if (is3DView()) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
200
252
|
const objectId = getClosestObjectId(event.target);
|
|
201
253
|
applyHover(objectId);
|
|
202
254
|
};
|
|
@@ -204,6 +256,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
204
256
|
applyHover(null);
|
|
205
257
|
};
|
|
206
258
|
const handleFocusIn = (event) => {
|
|
259
|
+
if (is3DView()) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
207
262
|
const objectId = getClosestObjectId(event.target);
|
|
208
263
|
if (!objectId) {
|
|
209
264
|
return;
|
|
@@ -217,7 +272,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
217
272
|
if (!behavior.keyboard || destroyed) {
|
|
218
273
|
return;
|
|
219
274
|
}
|
|
220
|
-
const objectId =
|
|
275
|
+
const objectId = is3DView()
|
|
276
|
+
? state.selectedObjectId
|
|
277
|
+
: getClosestObjectId(event.target);
|
|
221
278
|
if ((event.key === "Enter" || event.key === " ") && objectId) {
|
|
222
279
|
event.preventDefault();
|
|
223
280
|
applySelection(objectId);
|
|
@@ -294,18 +351,36 @@ export function createInteractiveViewer(container, options) {
|
|
|
294
351
|
setSource(source) {
|
|
295
352
|
currentInput = { kind: "source", value: source };
|
|
296
353
|
scene = renderSceneFromInput(currentInput, renderOptions);
|
|
354
|
+
providedSpatialScene = null;
|
|
355
|
+
spatialScene =
|
|
356
|
+
renderOptions.viewMode === "3d"
|
|
357
|
+
? renderSpatialSceneFromInput(currentInput, renderOptions, null)
|
|
358
|
+
: null;
|
|
359
|
+
syncAnimationFrozenState();
|
|
297
360
|
activeViewpointId = null;
|
|
298
361
|
rerenderScene(true);
|
|
299
362
|
},
|
|
300
363
|
setDocument(document) {
|
|
301
364
|
currentInput = { kind: "document", value: document };
|
|
302
365
|
scene = renderSceneFromInput(currentInput, renderOptions);
|
|
366
|
+
providedSpatialScene = null;
|
|
367
|
+
spatialScene =
|
|
368
|
+
renderOptions.viewMode === "3d"
|
|
369
|
+
? renderSpatialSceneFromInput(currentInput, renderOptions, null)
|
|
370
|
+
: null;
|
|
371
|
+
syncAnimationFrozenState();
|
|
303
372
|
activeViewpointId = null;
|
|
304
373
|
rerenderScene(true);
|
|
305
374
|
},
|
|
306
375
|
setScene(nextScene) {
|
|
307
376
|
currentInput = { kind: "scene", value: nextScene };
|
|
308
377
|
scene = nextScene;
|
|
378
|
+
providedSpatialScene = null;
|
|
379
|
+
spatialScene =
|
|
380
|
+
renderOptions.viewMode === "3d"
|
|
381
|
+
? renderSpatialSceneFromInput(currentInput, renderOptions, null)
|
|
382
|
+
: null;
|
|
383
|
+
syncAnimationFrozenState();
|
|
309
384
|
activeViewpointId = null;
|
|
310
385
|
rerenderScene(true);
|
|
311
386
|
},
|
|
@@ -315,6 +390,30 @@ export function createInteractiveViewer(container, options) {
|
|
|
315
390
|
getRenderOptions() {
|
|
316
391
|
return cloneRenderOptions(renderOptions);
|
|
317
392
|
},
|
|
393
|
+
getViewMode() {
|
|
394
|
+
return renderOptions.viewMode ?? "2d";
|
|
395
|
+
},
|
|
396
|
+
setViewMode(mode) {
|
|
397
|
+
const previousRenderOptions = renderOptions;
|
|
398
|
+
const previousSpatialScene = spatialScene;
|
|
399
|
+
const nextRenderOptions = mergeRenderOptions(renderOptions, { viewMode: mode });
|
|
400
|
+
const nextSpatialScene = mode === "3d"
|
|
401
|
+
? renderSpatialSceneFromInput(currentInput, nextRenderOptions, providedSpatialScene)
|
|
402
|
+
: null;
|
|
403
|
+
renderOptions = nextRenderOptions;
|
|
404
|
+
spatialScene = nextSpatialScene;
|
|
405
|
+
syncAnimationFrozenState();
|
|
406
|
+
try {
|
|
407
|
+
rerenderScene(false);
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
renderOptions = previousRenderOptions;
|
|
411
|
+
spatialScene = previousSpatialScene;
|
|
412
|
+
syncAnimationFrozenState();
|
|
413
|
+
rerenderScene(false);
|
|
414
|
+
throw error;
|
|
415
|
+
}
|
|
416
|
+
},
|
|
318
417
|
listViewpoints() {
|
|
319
418
|
return scene.viewpoints.slice();
|
|
320
419
|
},
|
|
@@ -368,6 +467,53 @@ export function createInteractiveViewer(container, options) {
|
|
|
368
467
|
setActiveEvent(id) {
|
|
369
468
|
api.setRenderOptions({ activeEventId: id });
|
|
370
469
|
},
|
|
470
|
+
playAnimation() {
|
|
471
|
+
if (!is3DView()) {
|
|
472
|
+
animationState = {
|
|
473
|
+
...animationState,
|
|
474
|
+
playing: false,
|
|
475
|
+
};
|
|
476
|
+
stopAnimationLoop();
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
if (animationState.frozenByEvent) {
|
|
480
|
+
animationState = {
|
|
481
|
+
...animationState,
|
|
482
|
+
playing: false,
|
|
483
|
+
};
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
animationState = {
|
|
487
|
+
...animationState,
|
|
488
|
+
playing: true,
|
|
489
|
+
};
|
|
490
|
+
ensureAnimationFrame();
|
|
491
|
+
},
|
|
492
|
+
pauseAnimation() {
|
|
493
|
+
animationState = {
|
|
494
|
+
...animationState,
|
|
495
|
+
playing: false,
|
|
496
|
+
};
|
|
497
|
+
stopAnimationLoop();
|
|
498
|
+
},
|
|
499
|
+
resetAnimation() {
|
|
500
|
+
animationState = {
|
|
501
|
+
...animationState,
|
|
502
|
+
playing: false,
|
|
503
|
+
timeSeconds: 0,
|
|
504
|
+
};
|
|
505
|
+
stopAnimationLoop();
|
|
506
|
+
syncRuntimePresentation();
|
|
507
|
+
},
|
|
508
|
+
setAnimationSpeed(multiplier) {
|
|
509
|
+
animationState = {
|
|
510
|
+
...animationState,
|
|
511
|
+
speed: clampValue(multiplier, 0.1, 64),
|
|
512
|
+
};
|
|
513
|
+
},
|
|
514
|
+
getAnimationState() {
|
|
515
|
+
return { ...animationState };
|
|
516
|
+
},
|
|
371
517
|
search(query, limit = 12) {
|
|
372
518
|
return searchSceneObjects(scene, query, limit);
|
|
373
519
|
},
|
|
@@ -427,11 +573,32 @@ export function createInteractiveViewer(container, options) {
|
|
|
427
573
|
},
|
|
428
574
|
setRenderOptions(options) {
|
|
429
575
|
const sceneAffecting = hasSceneAffectingRenderOptions(options);
|
|
430
|
-
|
|
576
|
+
const previousRenderOptions = renderOptions;
|
|
577
|
+
const previousScene = scene;
|
|
578
|
+
const previousSpatialScene = spatialScene;
|
|
579
|
+
const nextRenderOptions = mergeRenderOptions(renderOptions, options);
|
|
580
|
+
let nextScene = scene;
|
|
431
581
|
if (currentInput.kind !== "scene" && sceneAffecting) {
|
|
432
|
-
|
|
582
|
+
nextScene = renderSceneFromInput(currentInput, nextRenderOptions);
|
|
583
|
+
}
|
|
584
|
+
const nextSpatialScene = nextRenderOptions.viewMode === "3d"
|
|
585
|
+
? renderSpatialSceneFromInput(currentInput, nextRenderOptions, providedSpatialScene)
|
|
586
|
+
: null;
|
|
587
|
+
renderOptions = nextRenderOptions;
|
|
588
|
+
scene = nextScene;
|
|
589
|
+
spatialScene = nextSpatialScene;
|
|
590
|
+
syncAnimationFrozenState();
|
|
591
|
+
try {
|
|
592
|
+
rerenderScene(sceneAffecting);
|
|
593
|
+
}
|
|
594
|
+
catch (error) {
|
|
595
|
+
renderOptions = previousRenderOptions;
|
|
596
|
+
scene = previousScene;
|
|
597
|
+
spatialScene = previousSpatialScene;
|
|
598
|
+
syncAnimationFrozenState();
|
|
599
|
+
rerenderScene(sceneAffecting);
|
|
600
|
+
throw error;
|
|
433
601
|
}
|
|
434
|
-
rerenderScene(sceneAffecting);
|
|
435
602
|
},
|
|
436
603
|
getState() {
|
|
437
604
|
return { ...state };
|
|
@@ -449,11 +616,15 @@ export function createInteractiveViewer(container, options) {
|
|
|
449
616
|
updateState(rotateViewerState(state, deg));
|
|
450
617
|
},
|
|
451
618
|
fitToSystem() {
|
|
452
|
-
updateState(
|
|
619
|
+
updateState(is3DView()
|
|
620
|
+
? { ...DEFAULT_VIEWER_STATE, selectedObjectId: state.selectedObjectId }
|
|
621
|
+
: fitViewerState(scene, state, constraints));
|
|
453
622
|
},
|
|
454
623
|
focusObject(id) {
|
|
455
624
|
activeViewpointId = null;
|
|
456
|
-
updateState(
|
|
625
|
+
updateState(is3DView()
|
|
626
|
+
? create3DFocusState(id)
|
|
627
|
+
: focusViewerState(scene, state, id, constraints));
|
|
457
628
|
applySelection(id);
|
|
458
629
|
if (behavior.tooltipMode === "pinned") {
|
|
459
630
|
pinnedTooltipObjectId = getObjectById(id)?.objectId ?? null;
|
|
@@ -465,7 +636,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
465
636
|
updateTooltip();
|
|
466
637
|
},
|
|
467
638
|
resetView() {
|
|
468
|
-
const resetState =
|
|
639
|
+
const resetState = is3DView()
|
|
640
|
+
? { ...DEFAULT_VIEWER_STATE }
|
|
641
|
+
: fitViewerState(scene, { ...DEFAULT_VIEWER_STATE }, constraints);
|
|
469
642
|
activeViewpointId = null;
|
|
470
643
|
updateState(resetState);
|
|
471
644
|
applySelection(null);
|
|
@@ -495,6 +668,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
495
668
|
container.removeEventListener("focusin", handleFocusIn);
|
|
496
669
|
container.removeEventListener("focusout", handleFocusOut);
|
|
497
670
|
container.removeEventListener("keydown", handleKeyDown);
|
|
671
|
+
stopAnimationLoop();
|
|
672
|
+
runtime3d?.destroy();
|
|
673
|
+
runtime3d = null;
|
|
498
674
|
tooltipRoot?.remove();
|
|
499
675
|
tooltipRoot = null;
|
|
500
676
|
minimapRoot?.remove();
|
|
@@ -522,15 +698,26 @@ export function createInteractiveViewer(container, options) {
|
|
|
522
698
|
}
|
|
523
699
|
return api;
|
|
524
700
|
function rerenderScene(resetView) {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
svgElement = container.querySelector('[data-worldorbit-svg="true"]');
|
|
531
|
-
cameraRoot = container.querySelector("#worldorbit-camera-root");
|
|
701
|
+
runtime3d?.destroy();
|
|
702
|
+
runtime3d = null;
|
|
703
|
+
container.innerHTML = "";
|
|
704
|
+
svgElement = null;
|
|
705
|
+
cameraRoot = null;
|
|
532
706
|
minimapRoot = null;
|
|
533
707
|
tooltipRoot = null;
|
|
708
|
+
if (is3DView()) {
|
|
709
|
+
spatialScene = spatialScene ?? renderSpatialSceneFromInput(currentInput, renderOptions, providedSpatialScene);
|
|
710
|
+
runtime3d = createViewer3DRuntime(container);
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
container.innerHTML = renderSceneToSvg(scene, {
|
|
714
|
+
...renderOptions,
|
|
715
|
+
filter: renderOptions.filter ?? null,
|
|
716
|
+
selectedObjectId: state.selectedObjectId,
|
|
717
|
+
});
|
|
718
|
+
svgElement = container.querySelector('[data-worldorbit-svg="true"]');
|
|
719
|
+
cameraRoot = container.querySelector("#worldorbit-camera-root");
|
|
720
|
+
}
|
|
534
721
|
if (behavior.minimap) {
|
|
535
722
|
minimapRoot = document.createElement("div");
|
|
536
723
|
minimapRoot.dataset.worldorbitMinimapRoot = "true";
|
|
@@ -544,11 +731,13 @@ export function createInteractiveViewer(container, options) {
|
|
|
544
731
|
tooltipRoot.addEventListener("click", handleTooltipClick);
|
|
545
732
|
container.append(tooltipRoot);
|
|
546
733
|
}
|
|
547
|
-
if (!svgElement || !cameraRoot) {
|
|
734
|
+
if (!is3DView() && (!svgElement || !cameraRoot)) {
|
|
548
735
|
throw new Error("Interactive viewer could not locate the rendered SVG camera root.");
|
|
549
736
|
}
|
|
550
737
|
state = resetView
|
|
551
|
-
?
|
|
738
|
+
? is3DView()
|
|
739
|
+
? { ...DEFAULT_VIEWER_STATE }
|
|
740
|
+
: fitViewerState(scene, { ...DEFAULT_VIEWER_STATE }, constraints)
|
|
552
741
|
: sanitizeState(state);
|
|
553
742
|
applySelection(state.selectedObjectId &&
|
|
554
743
|
getObjectById(state.selectedObjectId)
|
|
@@ -562,7 +751,7 @@ export function createInteractiveViewer(container, options) {
|
|
|
562
751
|
pinnedTooltipObjectId && getObjectById(pinnedTooltipObjectId)
|
|
563
752
|
? pinnedTooltipObjectId
|
|
564
753
|
: null;
|
|
565
|
-
|
|
754
|
+
syncRuntimePresentation();
|
|
566
755
|
notifyFilterChange();
|
|
567
756
|
notifyViewpointChange();
|
|
568
757
|
options.onViewChange?.({ ...state });
|
|
@@ -570,7 +759,7 @@ export function createInteractiveViewer(container, options) {
|
|
|
570
759
|
}
|
|
571
760
|
function updateState(nextState) {
|
|
572
761
|
state = sanitizeState(nextState);
|
|
573
|
-
|
|
762
|
+
syncRuntimePresentation();
|
|
574
763
|
options.onViewChange?.({ ...state });
|
|
575
764
|
emitAtlasStateChange();
|
|
576
765
|
}
|
|
@@ -586,6 +775,10 @@ export function createInteractiveViewer(container, options) {
|
|
|
586
775
|
};
|
|
587
776
|
}
|
|
588
777
|
function updateCameraTransform() {
|
|
778
|
+
if (is3DView()) {
|
|
779
|
+
sync3DView();
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
589
782
|
if (!cameraRoot) {
|
|
590
783
|
return;
|
|
591
784
|
}
|
|
@@ -594,7 +787,7 @@ export function createInteractiveViewer(container, options) {
|
|
|
594
787
|
updateTooltip();
|
|
595
788
|
}
|
|
596
789
|
function applySelection(objectId, emitCallback = true) {
|
|
597
|
-
if (state.selectedObjectId) {
|
|
790
|
+
if (!is3DView() && state.selectedObjectId) {
|
|
598
791
|
container
|
|
599
792
|
.querySelector(`[data-object-id="${cssEscape(state.selectedObjectId)}"]`)
|
|
600
793
|
?.classList.remove("wo-object-selected");
|
|
@@ -605,7 +798,7 @@ export function createInteractiveViewer(container, options) {
|
|
|
605
798
|
? objectId
|
|
606
799
|
: null,
|
|
607
800
|
};
|
|
608
|
-
if (state.selectedObjectId) {
|
|
801
|
+
if (!is3DView() && state.selectedObjectId) {
|
|
609
802
|
container
|
|
610
803
|
.querySelector(`[data-object-id="${cssEscape(state.selectedObjectId)}"]`)
|
|
611
804
|
?.classList.add("wo-object-selected");
|
|
@@ -678,6 +871,10 @@ export function createInteractiveViewer(container, options) {
|
|
|
678
871
|
};
|
|
679
872
|
}
|
|
680
873
|
function syncAtlasHighlights() {
|
|
874
|
+
if (is3DView()) {
|
|
875
|
+
sync3DView();
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
681
878
|
for (const element of container.querySelectorAll(".wo-chain-selected, .wo-chain-hover, .wo-ancestor-selected, .wo-ancestor-hover, .wo-orbit-related-selected, .wo-orbit-related-hover")) {
|
|
682
879
|
element.classList.remove("wo-chain-selected", "wo-chain-hover", "wo-ancestor-selected", "wo-ancestor-hover", "wo-orbit-related-selected", "wo-orbit-related-hover");
|
|
683
880
|
}
|
|
@@ -719,6 +916,19 @@ export function createInteractiveViewer(container, options) {
|
|
|
719
916
|
}
|
|
720
917
|
}
|
|
721
918
|
function getViewportPointFromClient(clientX, clientY) {
|
|
919
|
+
if (is3DView()) {
|
|
920
|
+
const rect = container.getBoundingClientRect();
|
|
921
|
+
if (!rect.width || !rect.height) {
|
|
922
|
+
return {
|
|
923
|
+
x: scene.width / 2,
|
|
924
|
+
y: scene.height / 2,
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
return {
|
|
928
|
+
x: clientX - rect.left,
|
|
929
|
+
y: clientY - rect.top,
|
|
930
|
+
};
|
|
931
|
+
}
|
|
722
932
|
if (!svgElement) {
|
|
723
933
|
return {
|
|
724
934
|
x: scene.width / 2,
|
|
@@ -764,6 +974,19 @@ export function createInteractiveViewer(container, options) {
|
|
|
764
974
|
const scale = viewpoint.scale !== null && viewpoint.scale !== undefined
|
|
765
975
|
? clampValue(viewpoint.scale, constraints.minScale, constraints.maxScale)
|
|
766
976
|
: null;
|
|
977
|
+
if (is3DView()) {
|
|
978
|
+
const focusId = viewpoint.objectId ?? viewpoint.selectedObjectId ?? null;
|
|
979
|
+
const target = focusId
|
|
980
|
+
? spatialScene?.focusTargets.find((entry) => entry.objectId === focusId)
|
|
981
|
+
: null;
|
|
982
|
+
return {
|
|
983
|
+
scale: scale ?? 1.6,
|
|
984
|
+
rotationDeg,
|
|
985
|
+
translateX: target ? -target.center.x : 0,
|
|
986
|
+
translateY: target ? -target.center.z : 0,
|
|
987
|
+
selectedObjectId: viewpoint.selectedObjectId ?? viewpoint.objectId ?? null,
|
|
988
|
+
};
|
|
989
|
+
}
|
|
767
990
|
const targetObject = viewpoint.objectId &&
|
|
768
991
|
scene.objects.find((object) => object.objectId === viewpoint.objectId && !object.hidden);
|
|
769
992
|
if (targetObject) {
|
|
@@ -908,32 +1131,24 @@ export function createInteractiveViewer(container, options) {
|
|
|
908
1131
|
}
|
|
909
1132
|
}
|
|
910
1133
|
function positionTooltip(element, renderObject) {
|
|
911
|
-
|
|
1134
|
+
const point = is3DView()
|
|
1135
|
+
? runtime3d?.projectObjectToContainer(renderObject.objectId) ?? null
|
|
1136
|
+
: project2DTooltipPoint(renderObject);
|
|
1137
|
+
if (!point) {
|
|
912
1138
|
return;
|
|
913
1139
|
}
|
|
914
|
-
const anchor = {
|
|
915
|
-
x: renderObject.anchorX ?? renderObject.x,
|
|
916
|
-
y: renderObject.anchorY ??
|
|
917
|
-
renderObject.y - Math.max(renderObject.visualRadius, renderObject.radius),
|
|
918
|
-
};
|
|
919
|
-
const viewportPoint = projectWorldPoint(anchor);
|
|
920
|
-
const svgRect = svgElement.getBoundingClientRect();
|
|
921
|
-
const containerRect = container.getBoundingClientRect();
|
|
922
|
-
const pointX = svgRect.left -
|
|
923
|
-
containerRect.left +
|
|
924
|
-
(viewportPoint.x / Math.max(scene.width, 1)) * svgRect.width;
|
|
925
|
-
const pointY = svgRect.top -
|
|
926
|
-
containerRect.top +
|
|
927
|
-
(viewportPoint.y / Math.max(scene.height, 1)) * svgRect.height;
|
|
928
1140
|
const maxLeft = Math.max(container.clientWidth - element.offsetWidth - 12, 12);
|
|
929
1141
|
const maxTop = Math.max(container.clientHeight - element.offsetHeight - 12, 12);
|
|
930
|
-
const preferAbove =
|
|
931
|
-
const nextLeft = clampValue(
|
|
932
|
-
const nextTop = clampValue(preferAbove ?
|
|
1142
|
+
const preferAbove = point.y > container.clientHeight * 0.48;
|
|
1143
|
+
const nextLeft = clampValue(point.x + 18, 12, maxLeft);
|
|
1144
|
+
const nextTop = clampValue(preferAbove ? point.y - element.offsetHeight - 18 : point.y + 18, 12, maxTop);
|
|
933
1145
|
element.style.left = `${nextLeft}px`;
|
|
934
1146
|
element.style.top = `${nextTop}px`;
|
|
935
1147
|
}
|
|
936
1148
|
function projectWorldPoint(point) {
|
|
1149
|
+
if (is3DView()) {
|
|
1150
|
+
return point;
|
|
1151
|
+
}
|
|
937
1152
|
const center = {
|
|
938
1153
|
x: scene.width / 2,
|
|
939
1154
|
y: scene.height / 2,
|
|
@@ -944,6 +1159,27 @@ export function createInteractiveViewer(container, options) {
|
|
|
944
1159
|
y: center.y + (rotated.y - center.y) * state.scale + state.translateY,
|
|
945
1160
|
};
|
|
946
1161
|
}
|
|
1162
|
+
function project2DTooltipPoint(renderObject) {
|
|
1163
|
+
if (!svgElement) {
|
|
1164
|
+
return null;
|
|
1165
|
+
}
|
|
1166
|
+
const anchor = {
|
|
1167
|
+
x: renderObject.anchorX ?? renderObject.x,
|
|
1168
|
+
y: renderObject.anchorY ??
|
|
1169
|
+
renderObject.y - Math.max(renderObject.visualRadius, renderObject.radius),
|
|
1170
|
+
};
|
|
1171
|
+
const viewportPoint = projectWorldPoint(anchor);
|
|
1172
|
+
const svgRect = svgElement.getBoundingClientRect();
|
|
1173
|
+
const containerRect = container.getBoundingClientRect();
|
|
1174
|
+
return {
|
|
1175
|
+
x: svgRect.left -
|
|
1176
|
+
containerRect.left +
|
|
1177
|
+
(viewportPoint.x / Math.max(scene.width, 1)) * svgRect.width,
|
|
1178
|
+
y: svgRect.top -
|
|
1179
|
+
containerRect.top +
|
|
1180
|
+
(viewportPoint.y / Math.max(scene.height, 1)) * svgRect.height,
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
947
1183
|
function handleTooltipClick(event) {
|
|
948
1184
|
const target = event.target?.closest("[data-tooltip-action]");
|
|
949
1185
|
if (!target) {
|
|
@@ -971,6 +1207,92 @@ export function createInteractiveViewer(container, options) {
|
|
|
971
1207
|
options.onTooltipChange?.(details);
|
|
972
1208
|
}
|
|
973
1209
|
}
|
|
1210
|
+
function is3DView() {
|
|
1211
|
+
return renderOptions.viewMode === "3d";
|
|
1212
|
+
}
|
|
1213
|
+
function syncAnimationFrozenState() {
|
|
1214
|
+
animationState = {
|
|
1215
|
+
...animationState,
|
|
1216
|
+
frozenByEvent: spatialScene?.timeFrozen ?? false,
|
|
1217
|
+
};
|
|
1218
|
+
if (animationState.frozenByEvent) {
|
|
1219
|
+
animationState = {
|
|
1220
|
+
...animationState,
|
|
1221
|
+
playing: false,
|
|
1222
|
+
};
|
|
1223
|
+
stopAnimationLoop();
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
function ensureAnimationFrame() {
|
|
1227
|
+
if (animationFrameId !== null || !animationState.playing || destroyed) {
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
animationFrameId = window.requestAnimationFrame(renderAnimationFrame);
|
|
1231
|
+
}
|
|
1232
|
+
function stopAnimationLoop() {
|
|
1233
|
+
if (animationFrameId !== null) {
|
|
1234
|
+
window.cancelAnimationFrame(animationFrameId);
|
|
1235
|
+
animationFrameId = null;
|
|
1236
|
+
}
|
|
1237
|
+
lastAnimationTimestamp = null;
|
|
1238
|
+
}
|
|
1239
|
+
function renderAnimationFrame(timestamp) {
|
|
1240
|
+
animationFrameId = null;
|
|
1241
|
+
if (!animationState.playing || destroyed) {
|
|
1242
|
+
lastAnimationTimestamp = null;
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
const previousTimestamp = lastAnimationTimestamp ?? timestamp;
|
|
1246
|
+
const deltaSeconds = Math.max((timestamp - previousTimestamp) / 1_000, 0);
|
|
1247
|
+
lastAnimationTimestamp = timestamp;
|
|
1248
|
+
animationState = {
|
|
1249
|
+
...animationState,
|
|
1250
|
+
timeSeconds: animationState.timeSeconds + deltaSeconds * animationState.speed,
|
|
1251
|
+
};
|
|
1252
|
+
sync3DView();
|
|
1253
|
+
ensureAnimationFrame();
|
|
1254
|
+
}
|
|
1255
|
+
function syncRuntimePresentation() {
|
|
1256
|
+
updateCameraTransform();
|
|
1257
|
+
if (is3DView() && animationState.playing) {
|
|
1258
|
+
ensureAnimationFrame();
|
|
1259
|
+
}
|
|
1260
|
+
else if (!animationState.playing || !is3DView()) {
|
|
1261
|
+
stopAnimationLoop();
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
function sync3DView() {
|
|
1265
|
+
if (!is3DView() || !runtime3d || !spatialScene) {
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
runtime3d.update({
|
|
1269
|
+
spatialScene,
|
|
1270
|
+
renderOptions,
|
|
1271
|
+
visibleObjectIds: getVisibleObjectIds(),
|
|
1272
|
+
selectedObjectId: state.selectedObjectId,
|
|
1273
|
+
hoveredObjectId,
|
|
1274
|
+
state,
|
|
1275
|
+
timeSeconds: animationState.timeSeconds,
|
|
1276
|
+
});
|
|
1277
|
+
updateMinimap();
|
|
1278
|
+
updateTooltip();
|
|
1279
|
+
}
|
|
1280
|
+
function create3DFocusState(objectId) {
|
|
1281
|
+
const target = spatialScene?.focusTargets.find((entry) => entry.objectId === objectId);
|
|
1282
|
+
if (!target) {
|
|
1283
|
+
return {
|
|
1284
|
+
...DEFAULT_VIEWER_STATE,
|
|
1285
|
+
selectedObjectId: objectId,
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
return {
|
|
1289
|
+
scale: 1.8,
|
|
1290
|
+
rotationDeg: state.rotationDeg,
|
|
1291
|
+
translateX: -target.center.x,
|
|
1292
|
+
translateY: -target.center.z,
|
|
1293
|
+
selectedObjectId: objectId,
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
974
1296
|
}
|
|
975
1297
|
function resolveInitialInput(options) {
|
|
976
1298
|
if (options.scene) {
|
|
@@ -996,6 +1318,21 @@ function renderSceneFromInput(input, renderOptions) {
|
|
|
996
1318
|
}
|
|
997
1319
|
}
|
|
998
1320
|
}
|
|
1321
|
+
function renderSpatialSceneFromInput(input, renderOptions, providedSpatialScene) {
|
|
1322
|
+
if (providedSpatialScene) {
|
|
1323
|
+
return providedSpatialScene;
|
|
1324
|
+
}
|
|
1325
|
+
switch (input.kind) {
|
|
1326
|
+
case "scene":
|
|
1327
|
+
return fallbackSpatialSceneFromRenderScene(input.value);
|
|
1328
|
+
case "document":
|
|
1329
|
+
return renderDocumentToSpatialScene(input.value, renderOptions);
|
|
1330
|
+
case "source": {
|
|
1331
|
+
const loaded = loadWorldOrbitSource(input.value);
|
|
1332
|
+
return renderDocumentToSpatialScene(loaded.document, resolveSourceRenderOptions(loaded, renderOptions));
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
999
1336
|
function cloneRenderOptions(renderOptions) {
|
|
1000
1337
|
return {
|
|
1001
1338
|
...renderOptions,
|
|
@@ -1007,6 +1344,7 @@ function cloneRenderOptions(renderOptions) {
|
|
|
1007
1344
|
? { ...renderOptions.theme }
|
|
1008
1345
|
: renderOptions.theme,
|
|
1009
1346
|
activeEventId: renderOptions.activeEventId ?? null,
|
|
1347
|
+
viewMode: renderOptions.viewMode ?? "2d",
|
|
1010
1348
|
};
|
|
1011
1349
|
}
|
|
1012
1350
|
function mergeRenderOptions(current, next) {
|
|
@@ -1044,6 +1382,7 @@ function mergeRenderOptions(current, next) {
|
|
|
1044
1382
|
theme: next.theme && typeof next.theme === "object"
|
|
1045
1383
|
? { ...next.theme }
|
|
1046
1384
|
: next.theme ?? current.theme,
|
|
1385
|
+
viewMode: next.viewMode ?? current.viewMode ?? "2d",
|
|
1047
1386
|
};
|
|
1048
1387
|
}
|
|
1049
1388
|
function hasSceneAffectingRenderOptions(options) {
|
|
@@ -1066,6 +1405,99 @@ function resolveSourceRenderOptions(loaded, renderOptions) {
|
|
|
1066
1405
|
preset: atlasDocument.system.defaults.preset,
|
|
1067
1406
|
};
|
|
1068
1407
|
}
|
|
1408
|
+
function fallbackSpatialSceneFromRenderScene(scene) {
|
|
1409
|
+
return {
|
|
1410
|
+
width: scene.width,
|
|
1411
|
+
height: scene.height,
|
|
1412
|
+
padding: scene.padding,
|
|
1413
|
+
renderPreset: scene.renderPreset,
|
|
1414
|
+
projection: scene.projection,
|
|
1415
|
+
camera: scene.camera,
|
|
1416
|
+
scaleModel: {
|
|
1417
|
+
orbitDistanceMultiplier: 1,
|
|
1418
|
+
bodyRadiusMultiplier: 1,
|
|
1419
|
+
markerSizeMultiplier: 1,
|
|
1420
|
+
ringThicknessMultiplier: 1,
|
|
1421
|
+
focusPadding: 12,
|
|
1422
|
+
minBodyRadius: 4,
|
|
1423
|
+
maxBodyRadius: 40,
|
|
1424
|
+
},
|
|
1425
|
+
title: scene.title,
|
|
1426
|
+
subtitle: scene.subtitle,
|
|
1427
|
+
systemId: scene.systemId,
|
|
1428
|
+
viewMode: "3d",
|
|
1429
|
+
layoutPreset: scene.layoutPreset,
|
|
1430
|
+
metadata: {
|
|
1431
|
+
...scene.metadata,
|
|
1432
|
+
"viewer.mode": "3d-fallback",
|
|
1433
|
+
},
|
|
1434
|
+
contentBounds: {
|
|
1435
|
+
minX: scene.contentBounds.minX - scene.contentBounds.centerX,
|
|
1436
|
+
minY: -40,
|
|
1437
|
+
minZ: scene.contentBounds.minY - scene.contentBounds.centerY,
|
|
1438
|
+
maxX: scene.contentBounds.maxX - scene.contentBounds.centerX,
|
|
1439
|
+
maxY: 40,
|
|
1440
|
+
maxZ: scene.contentBounds.maxY - scene.contentBounds.centerY,
|
|
1441
|
+
width: scene.contentBounds.width,
|
|
1442
|
+
height: 80,
|
|
1443
|
+
depth: scene.contentBounds.height,
|
|
1444
|
+
center: { x: 0, y: 0, z: 0 },
|
|
1445
|
+
},
|
|
1446
|
+
semanticGroups: scene.semanticGroups,
|
|
1447
|
+
viewpoints: scene.viewpoints,
|
|
1448
|
+
activeEventId: scene.activeEventId,
|
|
1449
|
+
timeFrozen: scene.activeEventId !== null,
|
|
1450
|
+
objects: scene.objects.map((object) => ({
|
|
1451
|
+
objectId: object.objectId,
|
|
1452
|
+
object: object.object,
|
|
1453
|
+
parentId: object.parentId,
|
|
1454
|
+
ancestorIds: object.ancestorIds.slice(),
|
|
1455
|
+
childIds: object.childIds.slice(),
|
|
1456
|
+
groupId: object.groupId,
|
|
1457
|
+
semanticGroupIds: object.semanticGroupIds.slice(),
|
|
1458
|
+
position: {
|
|
1459
|
+
x: object.x - scene.contentBounds.centerX,
|
|
1460
|
+
y: 0,
|
|
1461
|
+
z: object.y - scene.contentBounds.centerY,
|
|
1462
|
+
},
|
|
1463
|
+
radius: object.radius,
|
|
1464
|
+
visualRadius: object.visualRadius,
|
|
1465
|
+
label: object.label,
|
|
1466
|
+
secondaryLabel: object.secondaryLabel,
|
|
1467
|
+
fillColor: object.fillColor,
|
|
1468
|
+
imageHref: object.imageHref,
|
|
1469
|
+
hidden: object.hidden,
|
|
1470
|
+
motion: null,
|
|
1471
|
+
})),
|
|
1472
|
+
orbits: scene.orbitVisuals.map((orbit) => ({
|
|
1473
|
+
objectId: orbit.objectId,
|
|
1474
|
+
object: orbit.object,
|
|
1475
|
+
parentId: orbit.parentId,
|
|
1476
|
+
groupId: orbit.groupId,
|
|
1477
|
+
semanticGroupIds: orbit.semanticGroupIds.slice(),
|
|
1478
|
+
center: { x: 0, y: 0, z: 0 },
|
|
1479
|
+
kind: orbit.kind,
|
|
1480
|
+
radius: orbit.radius,
|
|
1481
|
+
semiMajor: orbit.radius ?? orbit.rx ?? 0,
|
|
1482
|
+
semiMinor: orbit.radius ?? orbit.ry ?? 0,
|
|
1483
|
+
rotationDeg: orbit.rotationDeg,
|
|
1484
|
+
inclinationDeg: 0,
|
|
1485
|
+
band: orbit.band,
|
|
1486
|
+
bandThickness: orbit.bandThickness,
|
|
1487
|
+
hidden: orbit.hidden,
|
|
1488
|
+
motion: null,
|
|
1489
|
+
})),
|
|
1490
|
+
focusTargets: scene.objects.map((object) => ({
|
|
1491
|
+
objectId: object.objectId,
|
|
1492
|
+
center: {
|
|
1493
|
+
x: object.x - scene.contentBounds.centerX,
|
|
1494
|
+
y: 0,
|
|
1495
|
+
z: object.y - scene.contentBounds.centerY,
|
|
1496
|
+
},
|
|
1497
|
+
radius: object.visualRadius + 12,
|
|
1498
|
+
})),
|
|
1499
|
+
};
|
|
1500
|
+
}
|
|
1069
1501
|
function createTouchGestureState(scene, state, touchPoints) {
|
|
1070
1502
|
const { center, distance } = getTouchCenterAndDistance(touchPoints);
|
|
1071
1503
|
return {
|
|
@@ -1132,6 +1564,34 @@ function installViewerTooltipStyles() {
|
|
|
1132
1564
|
const style = document.createElement("style");
|
|
1133
1565
|
style.id = TOOLTIP_STYLE_ID;
|
|
1134
1566
|
style.textContent = `
|
|
1567
|
+
.wo-viewer-3d-root {
|
|
1568
|
+
position: relative;
|
|
1569
|
+
min-height: 320px;
|
|
1570
|
+
width: 100%;
|
|
1571
|
+
border-radius: 22px;
|
|
1572
|
+
overflow: hidden;
|
|
1573
|
+
background:
|
|
1574
|
+
radial-gradient(circle at top left, rgba(240, 180, 100, 0.08), transparent 24%),
|
|
1575
|
+
linear-gradient(180deg, rgba(255,255,255,0.02), transparent);
|
|
1576
|
+
}
|
|
1577
|
+
.wo-viewer-3d-loading {
|
|
1578
|
+
display: grid;
|
|
1579
|
+
place-items: center;
|
|
1580
|
+
min-height: 320px;
|
|
1581
|
+
padding: 24px;
|
|
1582
|
+
color: rgba(237, 246, 255, 0.76);
|
|
1583
|
+
font: 600 14px/1.5 "Segoe UI Variable", "Segoe UI", sans-serif;
|
|
1584
|
+
text-align: center;
|
|
1585
|
+
}
|
|
1586
|
+
.wo-viewer-3d-loading.is-error {
|
|
1587
|
+
color: #ffb2b2;
|
|
1588
|
+
}
|
|
1589
|
+
.wo-viewer-3d-canvas {
|
|
1590
|
+
display: block;
|
|
1591
|
+
width: 100%;
|
|
1592
|
+
height: 100%;
|
|
1593
|
+
min-height: 320px;
|
|
1594
|
+
}
|
|
1135
1595
|
.wo-viewer-tooltip-root {
|
|
1136
1596
|
position: absolute;
|
|
1137
1597
|
z-index: 12;
|