worldorbit 2.5.17 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -18
- 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 -6101
- 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 -11702
- 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 -5766
- 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 -7901
- 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 -6173
- 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 -11727
- 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 -5794
- 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 -7958
- 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 +1 -12
- package/dist/unpkg/worldorbit-editor.min.js +1 -894
- package/dist/unpkg/worldorbit-markdown.min.js +1 -103
- package/dist/unpkg/worldorbit-viewer.min.js +1 -259
- package/dist/unpkg/worldorbit.js +2 -9243
- package/dist/unpkg/worldorbit.min.js +2 -263
- package/package.json +1 -1
- package/packages/core/dist/atlas-edit.js +1 -1
- package/packages/core/dist/atlas-validate.js +99 -10
- package/packages/core/dist/draft-parse.js +190 -15
- package/packages/core/dist/draft.js +50 -11
- package/packages/core/dist/format.js +36 -5
- package/packages/core/dist/index.d.ts +1 -0
- package/packages/core/dist/index.js +1 -0
- package/packages/core/dist/load.js +9 -2
- package/packages/core/dist/scene.js +158 -24
- 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 +124 -2
- package/packages/editor/dist/editor.js +130 -8
- package/packages/editor/dist/types.d.ts +4 -0
- package/packages/markdown/dist/html.js +10 -3
- package/packages/viewer/dist/atlas-state.js +8 -2
- package/packages/viewer/dist/atlas-viewer.js +20 -8
- 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 +25 -3
- package/packages/viewer/dist/vendor/three.module.js +53032 -0
- package/packages/viewer/dist/viewer.js +517 -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,8 @@ 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",
|
|
48
|
+
camera: options.camera ? { ...options.camera } : null,
|
|
46
49
|
scaleModel: options.scaleModel ? { ...options.scaleModel } : undefined,
|
|
47
50
|
theme: options.theme,
|
|
48
51
|
layers: options.layers,
|
|
@@ -54,9 +57,14 @@ export function createInteractiveViewer(container, options) {
|
|
|
54
57
|
const previousPosition = container.style.position;
|
|
55
58
|
let currentInput = resolveInitialInput(options);
|
|
56
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;
|
|
57
64
|
let state = { ...DEFAULT_VIEWER_STATE };
|
|
58
65
|
let svgElement = null;
|
|
59
66
|
let cameraRoot = null;
|
|
67
|
+
let runtime3d = null;
|
|
60
68
|
let minimapRoot = null;
|
|
61
69
|
let tooltipRoot = null;
|
|
62
70
|
let suppressClick = false;
|
|
@@ -71,6 +79,14 @@ export function createInteractiveViewer(container, options) {
|
|
|
71
79
|
let activeTooltipObjectId = null;
|
|
72
80
|
let activeTooltipDetails = null;
|
|
73
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
|
+
};
|
|
74
90
|
if (previousTabIndex === null) {
|
|
75
91
|
container.tabIndex = 0;
|
|
76
92
|
}
|
|
@@ -80,12 +96,18 @@ export function createInteractiveViewer(container, options) {
|
|
|
80
96
|
if (!container.style.position) {
|
|
81
97
|
container.style.position = "relative";
|
|
82
98
|
}
|
|
99
|
+
syncAnimationFrozenState();
|
|
83
100
|
const handleWheel = (event) => {
|
|
84
101
|
if (!behavior.pointer || destroyed) {
|
|
85
102
|
return;
|
|
86
103
|
}
|
|
87
104
|
event.preventDefault();
|
|
88
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
|
+
}
|
|
89
111
|
const anchor = getWorldPointFromClient(event.clientX, event.clientY);
|
|
90
112
|
const factor = clampValue(Math.exp(-event.deltaY * 0.002), 0.6, 1.6);
|
|
91
113
|
updateState(zoomViewerStateAt(scene, state, factor, anchor, constraints));
|
|
@@ -98,7 +120,7 @@ export function createInteractiveViewer(container, options) {
|
|
|
98
120
|
if ((isTouch && !behavior.touch) || (!isTouch && !behavior.pointer)) {
|
|
99
121
|
return;
|
|
100
122
|
}
|
|
101
|
-
if (!isTouch && event.button !== 0) {
|
|
123
|
+
if (!isTouch && event.button !== 0 && !is3DView()) {
|
|
102
124
|
return;
|
|
103
125
|
}
|
|
104
126
|
container.focus();
|
|
@@ -126,6 +148,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
126
148
|
}
|
|
127
149
|
const isTouch = event.pointerType === "touch";
|
|
128
150
|
if (isTouch) {
|
|
151
|
+
if (is3DView()) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
129
154
|
if (!behavior.touch || !touchPoints.has(event.pointerId)) {
|
|
130
155
|
return;
|
|
131
156
|
}
|
|
@@ -154,6 +179,29 @@ export function createInteractiveViewer(container, options) {
|
|
|
154
179
|
}
|
|
155
180
|
return;
|
|
156
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
|
+
}
|
|
157
205
|
if (!behavior.pointer || activePointerId !== event.pointerId || !lastPointerPoint) {
|
|
158
206
|
return;
|
|
159
207
|
}
|
|
@@ -188,7 +236,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
188
236
|
suppressClick = false;
|
|
189
237
|
return;
|
|
190
238
|
}
|
|
191
|
-
const objectId =
|
|
239
|
+
const objectId = is3DView()
|
|
240
|
+
? runtime3d?.hitTest(event.clientX, event.clientY) ?? null
|
|
241
|
+
: getClosestObjectId(event.target);
|
|
192
242
|
applySelection(objectId);
|
|
193
243
|
if (behavior.tooltipMode === "pinned") {
|
|
194
244
|
pinnedTooltipObjectId = objectId;
|
|
@@ -196,6 +246,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
196
246
|
}
|
|
197
247
|
};
|
|
198
248
|
const handleMouseOver = (event) => {
|
|
249
|
+
if (is3DView()) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
199
252
|
const objectId = getClosestObjectId(event.target);
|
|
200
253
|
applyHover(objectId);
|
|
201
254
|
};
|
|
@@ -203,6 +256,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
203
256
|
applyHover(null);
|
|
204
257
|
};
|
|
205
258
|
const handleFocusIn = (event) => {
|
|
259
|
+
if (is3DView()) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
206
262
|
const objectId = getClosestObjectId(event.target);
|
|
207
263
|
if (!objectId) {
|
|
208
264
|
return;
|
|
@@ -216,7 +272,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
216
272
|
if (!behavior.keyboard || destroyed) {
|
|
217
273
|
return;
|
|
218
274
|
}
|
|
219
|
-
const objectId =
|
|
275
|
+
const objectId = is3DView()
|
|
276
|
+
? state.selectedObjectId
|
|
277
|
+
: getClosestObjectId(event.target);
|
|
220
278
|
if ((event.key === "Enter" || event.key === " ") && objectId) {
|
|
221
279
|
event.preventDefault();
|
|
222
280
|
applySelection(objectId);
|
|
@@ -293,18 +351,36 @@ export function createInteractiveViewer(container, options) {
|
|
|
293
351
|
setSource(source) {
|
|
294
352
|
currentInput = { kind: "source", value: source };
|
|
295
353
|
scene = renderSceneFromInput(currentInput, renderOptions);
|
|
354
|
+
providedSpatialScene = null;
|
|
355
|
+
spatialScene =
|
|
356
|
+
renderOptions.viewMode === "3d"
|
|
357
|
+
? renderSpatialSceneFromInput(currentInput, renderOptions, null)
|
|
358
|
+
: null;
|
|
359
|
+
syncAnimationFrozenState();
|
|
296
360
|
activeViewpointId = null;
|
|
297
361
|
rerenderScene(true);
|
|
298
362
|
},
|
|
299
363
|
setDocument(document) {
|
|
300
364
|
currentInput = { kind: "document", value: document };
|
|
301
365
|
scene = renderSceneFromInput(currentInput, renderOptions);
|
|
366
|
+
providedSpatialScene = null;
|
|
367
|
+
spatialScene =
|
|
368
|
+
renderOptions.viewMode === "3d"
|
|
369
|
+
? renderSpatialSceneFromInput(currentInput, renderOptions, null)
|
|
370
|
+
: null;
|
|
371
|
+
syncAnimationFrozenState();
|
|
302
372
|
activeViewpointId = null;
|
|
303
373
|
rerenderScene(true);
|
|
304
374
|
},
|
|
305
375
|
setScene(nextScene) {
|
|
306
376
|
currentInput = { kind: "scene", value: nextScene };
|
|
307
377
|
scene = nextScene;
|
|
378
|
+
providedSpatialScene = null;
|
|
379
|
+
spatialScene =
|
|
380
|
+
renderOptions.viewMode === "3d"
|
|
381
|
+
? renderSpatialSceneFromInput(currentInput, renderOptions, null)
|
|
382
|
+
: null;
|
|
383
|
+
syncAnimationFrozenState();
|
|
308
384
|
activeViewpointId = null;
|
|
309
385
|
rerenderScene(true);
|
|
310
386
|
},
|
|
@@ -314,6 +390,30 @@ export function createInteractiveViewer(container, options) {
|
|
|
314
390
|
getRenderOptions() {
|
|
315
391
|
return cloneRenderOptions(renderOptions);
|
|
316
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
|
+
},
|
|
317
417
|
listViewpoints() {
|
|
318
418
|
return scene.viewpoints.slice();
|
|
319
419
|
},
|
|
@@ -333,6 +433,12 @@ export function createInteractiveViewer(container, options) {
|
|
|
333
433
|
if (currentInput.kind !== "scene" && viewpoint.projection !== scene.projection) {
|
|
334
434
|
nextRenderOptions.projection = viewpoint.projection;
|
|
335
435
|
}
|
|
436
|
+
if (viewpoint.camera) {
|
|
437
|
+
nextRenderOptions.camera = { ...viewpoint.camera };
|
|
438
|
+
}
|
|
439
|
+
else if (renderOptions.camera) {
|
|
440
|
+
nextRenderOptions.camera = null;
|
|
441
|
+
}
|
|
336
442
|
if (viewpointLayers) {
|
|
337
443
|
nextRenderOptions.layers = viewpointLayers;
|
|
338
444
|
}
|
|
@@ -361,6 +467,53 @@ export function createInteractiveViewer(container, options) {
|
|
|
361
467
|
setActiveEvent(id) {
|
|
362
468
|
api.setRenderOptions({ activeEventId: id });
|
|
363
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
|
+
},
|
|
364
517
|
search(query, limit = 12) {
|
|
365
518
|
return searchSceneObjects(scene, query, limit);
|
|
366
519
|
},
|
|
@@ -420,11 +573,32 @@ export function createInteractiveViewer(container, options) {
|
|
|
420
573
|
},
|
|
421
574
|
setRenderOptions(options) {
|
|
422
575
|
const sceneAffecting = hasSceneAffectingRenderOptions(options);
|
|
423
|
-
|
|
576
|
+
const previousRenderOptions = renderOptions;
|
|
577
|
+
const previousScene = scene;
|
|
578
|
+
const previousSpatialScene = spatialScene;
|
|
579
|
+
const nextRenderOptions = mergeRenderOptions(renderOptions, options);
|
|
580
|
+
let nextScene = scene;
|
|
424
581
|
if (currentInput.kind !== "scene" && sceneAffecting) {
|
|
425
|
-
|
|
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;
|
|
426
601
|
}
|
|
427
|
-
rerenderScene(sceneAffecting);
|
|
428
602
|
},
|
|
429
603
|
getState() {
|
|
430
604
|
return { ...state };
|
|
@@ -442,11 +616,15 @@ export function createInteractiveViewer(container, options) {
|
|
|
442
616
|
updateState(rotateViewerState(state, deg));
|
|
443
617
|
},
|
|
444
618
|
fitToSystem() {
|
|
445
|
-
updateState(
|
|
619
|
+
updateState(is3DView()
|
|
620
|
+
? { ...DEFAULT_VIEWER_STATE, selectedObjectId: state.selectedObjectId }
|
|
621
|
+
: fitViewerState(scene, state, constraints));
|
|
446
622
|
},
|
|
447
623
|
focusObject(id) {
|
|
448
624
|
activeViewpointId = null;
|
|
449
|
-
updateState(
|
|
625
|
+
updateState(is3DView()
|
|
626
|
+
? create3DFocusState(id)
|
|
627
|
+
: focusViewerState(scene, state, id, constraints));
|
|
450
628
|
applySelection(id);
|
|
451
629
|
if (behavior.tooltipMode === "pinned") {
|
|
452
630
|
pinnedTooltipObjectId = getObjectById(id)?.objectId ?? null;
|
|
@@ -458,7 +636,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
458
636
|
updateTooltip();
|
|
459
637
|
},
|
|
460
638
|
resetView() {
|
|
461
|
-
const resetState =
|
|
639
|
+
const resetState = is3DView()
|
|
640
|
+
? { ...DEFAULT_VIEWER_STATE }
|
|
641
|
+
: fitViewerState(scene, { ...DEFAULT_VIEWER_STATE }, constraints);
|
|
462
642
|
activeViewpointId = null;
|
|
463
643
|
updateState(resetState);
|
|
464
644
|
applySelection(null);
|
|
@@ -488,6 +668,9 @@ export function createInteractiveViewer(container, options) {
|
|
|
488
668
|
container.removeEventListener("focusin", handleFocusIn);
|
|
489
669
|
container.removeEventListener("focusout", handleFocusOut);
|
|
490
670
|
container.removeEventListener("keydown", handleKeyDown);
|
|
671
|
+
stopAnimationLoop();
|
|
672
|
+
runtime3d?.destroy();
|
|
673
|
+
runtime3d = null;
|
|
491
674
|
tooltipRoot?.remove();
|
|
492
675
|
tooltipRoot = null;
|
|
493
676
|
minimapRoot?.remove();
|
|
@@ -515,15 +698,26 @@ export function createInteractiveViewer(container, options) {
|
|
|
515
698
|
}
|
|
516
699
|
return api;
|
|
517
700
|
function rerenderScene(resetView) {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
svgElement = container.querySelector('[data-worldorbit-svg="true"]');
|
|
524
|
-
cameraRoot = container.querySelector("#worldorbit-camera-root");
|
|
701
|
+
runtime3d?.destroy();
|
|
702
|
+
runtime3d = null;
|
|
703
|
+
container.innerHTML = "";
|
|
704
|
+
svgElement = null;
|
|
705
|
+
cameraRoot = null;
|
|
525
706
|
minimapRoot = null;
|
|
526
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
|
+
}
|
|
527
721
|
if (behavior.minimap) {
|
|
528
722
|
minimapRoot = document.createElement("div");
|
|
529
723
|
minimapRoot.dataset.worldorbitMinimapRoot = "true";
|
|
@@ -537,11 +731,13 @@ export function createInteractiveViewer(container, options) {
|
|
|
537
731
|
tooltipRoot.addEventListener("click", handleTooltipClick);
|
|
538
732
|
container.append(tooltipRoot);
|
|
539
733
|
}
|
|
540
|
-
if (!svgElement || !cameraRoot) {
|
|
734
|
+
if (!is3DView() && (!svgElement || !cameraRoot)) {
|
|
541
735
|
throw new Error("Interactive viewer could not locate the rendered SVG camera root.");
|
|
542
736
|
}
|
|
543
737
|
state = resetView
|
|
544
|
-
?
|
|
738
|
+
? is3DView()
|
|
739
|
+
? { ...DEFAULT_VIEWER_STATE }
|
|
740
|
+
: fitViewerState(scene, { ...DEFAULT_VIEWER_STATE }, constraints)
|
|
545
741
|
: sanitizeState(state);
|
|
546
742
|
applySelection(state.selectedObjectId &&
|
|
547
743
|
getObjectById(state.selectedObjectId)
|
|
@@ -555,7 +751,7 @@ export function createInteractiveViewer(container, options) {
|
|
|
555
751
|
pinnedTooltipObjectId && getObjectById(pinnedTooltipObjectId)
|
|
556
752
|
? pinnedTooltipObjectId
|
|
557
753
|
: null;
|
|
558
|
-
|
|
754
|
+
syncRuntimePresentation();
|
|
559
755
|
notifyFilterChange();
|
|
560
756
|
notifyViewpointChange();
|
|
561
757
|
options.onViewChange?.({ ...state });
|
|
@@ -563,7 +759,7 @@ export function createInteractiveViewer(container, options) {
|
|
|
563
759
|
}
|
|
564
760
|
function updateState(nextState) {
|
|
565
761
|
state = sanitizeState(nextState);
|
|
566
|
-
|
|
762
|
+
syncRuntimePresentation();
|
|
567
763
|
options.onViewChange?.({ ...state });
|
|
568
764
|
emitAtlasStateChange();
|
|
569
765
|
}
|
|
@@ -579,6 +775,10 @@ export function createInteractiveViewer(container, options) {
|
|
|
579
775
|
};
|
|
580
776
|
}
|
|
581
777
|
function updateCameraTransform() {
|
|
778
|
+
if (is3DView()) {
|
|
779
|
+
sync3DView();
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
582
782
|
if (!cameraRoot) {
|
|
583
783
|
return;
|
|
584
784
|
}
|
|
@@ -587,7 +787,7 @@ export function createInteractiveViewer(container, options) {
|
|
|
587
787
|
updateTooltip();
|
|
588
788
|
}
|
|
589
789
|
function applySelection(objectId, emitCallback = true) {
|
|
590
|
-
if (state.selectedObjectId) {
|
|
790
|
+
if (!is3DView() && state.selectedObjectId) {
|
|
591
791
|
container
|
|
592
792
|
.querySelector(`[data-object-id="${cssEscape(state.selectedObjectId)}"]`)
|
|
593
793
|
?.classList.remove("wo-object-selected");
|
|
@@ -598,7 +798,7 @@ export function createInteractiveViewer(container, options) {
|
|
|
598
798
|
? objectId
|
|
599
799
|
: null,
|
|
600
800
|
};
|
|
601
|
-
if (state.selectedObjectId) {
|
|
801
|
+
if (!is3DView() && state.selectedObjectId) {
|
|
602
802
|
container
|
|
603
803
|
.querySelector(`[data-object-id="${cssEscape(state.selectedObjectId)}"]`)
|
|
604
804
|
?.classList.add("wo-object-selected");
|
|
@@ -671,6 +871,10 @@ export function createInteractiveViewer(container, options) {
|
|
|
671
871
|
};
|
|
672
872
|
}
|
|
673
873
|
function syncAtlasHighlights() {
|
|
874
|
+
if (is3DView()) {
|
|
875
|
+
sync3DView();
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
674
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")) {
|
|
675
879
|
element.classList.remove("wo-chain-selected", "wo-chain-hover", "wo-ancestor-selected", "wo-ancestor-hover", "wo-orbit-related-selected", "wo-orbit-related-hover");
|
|
676
880
|
}
|
|
@@ -712,6 +916,19 @@ export function createInteractiveViewer(container, options) {
|
|
|
712
916
|
}
|
|
713
917
|
}
|
|
714
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
|
+
}
|
|
715
932
|
if (!svgElement) {
|
|
716
933
|
return {
|
|
717
934
|
x: scene.width / 2,
|
|
@@ -757,6 +974,19 @@ export function createInteractiveViewer(container, options) {
|
|
|
757
974
|
const scale = viewpoint.scale !== null && viewpoint.scale !== undefined
|
|
758
975
|
? clampValue(viewpoint.scale, constraints.minScale, constraints.maxScale)
|
|
759
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
|
+
}
|
|
760
990
|
const targetObject = viewpoint.objectId &&
|
|
761
991
|
scene.objects.find((object) => object.objectId === viewpoint.objectId && !object.hidden);
|
|
762
992
|
if (targetObject) {
|
|
@@ -901,32 +1131,24 @@ export function createInteractiveViewer(container, options) {
|
|
|
901
1131
|
}
|
|
902
1132
|
}
|
|
903
1133
|
function positionTooltip(element, renderObject) {
|
|
904
|
-
|
|
1134
|
+
const point = is3DView()
|
|
1135
|
+
? runtime3d?.projectObjectToContainer(renderObject.objectId) ?? null
|
|
1136
|
+
: project2DTooltipPoint(renderObject);
|
|
1137
|
+
if (!point) {
|
|
905
1138
|
return;
|
|
906
1139
|
}
|
|
907
|
-
const anchor = {
|
|
908
|
-
x: renderObject.anchorX ?? renderObject.x,
|
|
909
|
-
y: renderObject.anchorY ??
|
|
910
|
-
renderObject.y - Math.max(renderObject.visualRadius, renderObject.radius),
|
|
911
|
-
};
|
|
912
|
-
const viewportPoint = projectWorldPoint(anchor);
|
|
913
|
-
const svgRect = svgElement.getBoundingClientRect();
|
|
914
|
-
const containerRect = container.getBoundingClientRect();
|
|
915
|
-
const pointX = svgRect.left -
|
|
916
|
-
containerRect.left +
|
|
917
|
-
(viewportPoint.x / Math.max(scene.width, 1)) * svgRect.width;
|
|
918
|
-
const pointY = svgRect.top -
|
|
919
|
-
containerRect.top +
|
|
920
|
-
(viewportPoint.y / Math.max(scene.height, 1)) * svgRect.height;
|
|
921
1140
|
const maxLeft = Math.max(container.clientWidth - element.offsetWidth - 12, 12);
|
|
922
1141
|
const maxTop = Math.max(container.clientHeight - element.offsetHeight - 12, 12);
|
|
923
|
-
const preferAbove =
|
|
924
|
-
const nextLeft = clampValue(
|
|
925
|
-
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);
|
|
926
1145
|
element.style.left = `${nextLeft}px`;
|
|
927
1146
|
element.style.top = `${nextTop}px`;
|
|
928
1147
|
}
|
|
929
1148
|
function projectWorldPoint(point) {
|
|
1149
|
+
if (is3DView()) {
|
|
1150
|
+
return point;
|
|
1151
|
+
}
|
|
930
1152
|
const center = {
|
|
931
1153
|
x: scene.width / 2,
|
|
932
1154
|
y: scene.height / 2,
|
|
@@ -937,6 +1159,27 @@ export function createInteractiveViewer(container, options) {
|
|
|
937
1159
|
y: center.y + (rotated.y - center.y) * state.scale + state.translateY,
|
|
938
1160
|
};
|
|
939
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
|
+
}
|
|
940
1183
|
function handleTooltipClick(event) {
|
|
941
1184
|
const target = event.target?.closest("[data-tooltip-action]");
|
|
942
1185
|
if (!target) {
|
|
@@ -964,6 +1207,92 @@ export function createInteractiveViewer(container, options) {
|
|
|
964
1207
|
options.onTooltipChange?.(details);
|
|
965
1208
|
}
|
|
966
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
|
+
}
|
|
967
1296
|
}
|
|
968
1297
|
function resolveInitialInput(options) {
|
|
969
1298
|
if (options.scene) {
|
|
@@ -989,9 +1318,25 @@ function renderSceneFromInput(input, renderOptions) {
|
|
|
989
1318
|
}
|
|
990
1319
|
}
|
|
991
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
|
+
}
|
|
992
1336
|
function cloneRenderOptions(renderOptions) {
|
|
993
1337
|
return {
|
|
994
1338
|
...renderOptions,
|
|
1339
|
+
camera: renderOptions.camera ? { ...renderOptions.camera } : null,
|
|
995
1340
|
filter: renderOptions.filter ? { ...renderOptions.filter } : undefined,
|
|
996
1341
|
scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : undefined,
|
|
997
1342
|
layers: renderOptions.layers ? { ...renderOptions.layers } : undefined,
|
|
@@ -999,12 +1344,20 @@ function cloneRenderOptions(renderOptions) {
|
|
|
999
1344
|
? { ...renderOptions.theme }
|
|
1000
1345
|
: renderOptions.theme,
|
|
1001
1346
|
activeEventId: renderOptions.activeEventId ?? null,
|
|
1347
|
+
viewMode: renderOptions.viewMode ?? "2d",
|
|
1002
1348
|
};
|
|
1003
1349
|
}
|
|
1004
1350
|
function mergeRenderOptions(current, next) {
|
|
1005
1351
|
return {
|
|
1006
1352
|
...current,
|
|
1007
1353
|
...next,
|
|
1354
|
+
camera: next.camera !== undefined
|
|
1355
|
+
? next.camera
|
|
1356
|
+
? { ...next.camera }
|
|
1357
|
+
: null
|
|
1358
|
+
: current.camera
|
|
1359
|
+
? { ...current.camera }
|
|
1360
|
+
: null,
|
|
1008
1361
|
filter: next.filter !== undefined
|
|
1009
1362
|
? normalizeViewerFilter(next.filter)
|
|
1010
1363
|
: current.filter
|
|
@@ -1029,6 +1382,7 @@ function mergeRenderOptions(current, next) {
|
|
|
1029
1382
|
theme: next.theme && typeof next.theme === "object"
|
|
1030
1383
|
? { ...next.theme }
|
|
1031
1384
|
: next.theme ?? current.theme,
|
|
1385
|
+
viewMode: next.viewMode ?? current.viewMode ?? "2d",
|
|
1032
1386
|
};
|
|
1033
1387
|
}
|
|
1034
1388
|
function hasSceneAffectingRenderOptions(options) {
|
|
@@ -1037,6 +1391,7 @@ function hasSceneAffectingRenderOptions(options) {
|
|
|
1037
1391
|
options.padding !== undefined ||
|
|
1038
1392
|
options.preset !== undefined ||
|
|
1039
1393
|
options.projection !== undefined ||
|
|
1394
|
+
options.camera !== undefined ||
|
|
1040
1395
|
options.scaleModel !== undefined ||
|
|
1041
1396
|
options.activeEventId !== undefined);
|
|
1042
1397
|
}
|
|
@@ -1050,6 +1405,99 @@ function resolveSourceRenderOptions(loaded, renderOptions) {
|
|
|
1050
1405
|
preset: atlasDocument.system.defaults.preset,
|
|
1051
1406
|
};
|
|
1052
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
|
+
}
|
|
1053
1501
|
function createTouchGestureState(scene, state, touchPoints) {
|
|
1054
1502
|
const { center, distance } = getTouchCenterAndDistance(touchPoints);
|
|
1055
1503
|
return {
|
|
@@ -1116,6 +1564,34 @@ function installViewerTooltipStyles() {
|
|
|
1116
1564
|
const style = document.createElement("style");
|
|
1117
1565
|
style.id = TOOLTIP_STYLE_ID;
|
|
1118
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
|
+
}
|
|
1119
1595
|
.wo-viewer-tooltip-root {
|
|
1120
1596
|
position: absolute;
|
|
1121
1597
|
z-index: 12;
|