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
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import { evaluateSpatialSceneAtTime, } from "@worldorbit/core";
|
|
2
|
+
import { WorldOrbit3DUnavailableError } from "./errors.js";
|
|
3
|
+
import { resolveTheme } from "./theme.js";
|
|
4
|
+
const VIEW_ROOT_CLASS = "wo-viewer-3d-root";
|
|
5
|
+
let threeModulePromise = null;
|
|
6
|
+
export function createViewer3DRuntime(container) {
|
|
7
|
+
ensureWebGLSupport();
|
|
8
|
+
const root = document.createElement("div");
|
|
9
|
+
root.className = VIEW_ROOT_CLASS;
|
|
10
|
+
root.dataset.worldorbit3d = "true";
|
|
11
|
+
root.innerHTML = `<div class="wo-viewer-3d-loading">Loading 3D view...</div>`;
|
|
12
|
+
container.innerHTML = "";
|
|
13
|
+
container.append(root);
|
|
14
|
+
let runtime = null;
|
|
15
|
+
let currentScene = null;
|
|
16
|
+
let currentRenderOptions = null;
|
|
17
|
+
let currentState = null;
|
|
18
|
+
let currentVisibleObjectIds = new Set();
|
|
19
|
+
let currentSelectedObjectId = null;
|
|
20
|
+
let currentHoveredObjectId = null;
|
|
21
|
+
let currentTimeSeconds = 0;
|
|
22
|
+
let currentPositions = new Map();
|
|
23
|
+
let pendingUpdate = null;
|
|
24
|
+
let destroyed = false;
|
|
25
|
+
const objectVisuals = new Map();
|
|
26
|
+
const orbitVisuals = new Map();
|
|
27
|
+
const raycastTargets = [];
|
|
28
|
+
void loadThreeModule()
|
|
29
|
+
.then((THREE) => {
|
|
30
|
+
if (destroyed) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const scene3d = new THREE.Scene();
|
|
34
|
+
const camera = new THREE.PerspectiveCamera(52, 1, 0.1, 20_000);
|
|
35
|
+
const renderer = new THREE.WebGLRenderer({
|
|
36
|
+
antialias: true,
|
|
37
|
+
alpha: true,
|
|
38
|
+
preserveDrawingBuffer: true,
|
|
39
|
+
});
|
|
40
|
+
renderer.domElement.classList.add("wo-viewer-3d-canvas");
|
|
41
|
+
renderer.domElement.dataset.worldorbit3dCanvas = "true";
|
|
42
|
+
root.innerHTML = "";
|
|
43
|
+
root.append(renderer.domElement);
|
|
44
|
+
const ambientLight = new THREE.AmbientLight(0xffffff, 1.2);
|
|
45
|
+
const keyLight = new THREE.PointLight(0xffffff, 1.35, 0, 2);
|
|
46
|
+
scene3d.add(ambientLight);
|
|
47
|
+
scene3d.add(keyLight);
|
|
48
|
+
const orbitLayer = new THREE.Group();
|
|
49
|
+
const objectLayer = new THREE.Group();
|
|
50
|
+
scene3d.add(orbitLayer);
|
|
51
|
+
scene3d.add(objectLayer);
|
|
52
|
+
const raycaster = new THREE.Raycaster();
|
|
53
|
+
raycaster.params.Line = { threshold: 10 };
|
|
54
|
+
runtime = {
|
|
55
|
+
THREE,
|
|
56
|
+
scene3d,
|
|
57
|
+
camera,
|
|
58
|
+
renderer,
|
|
59
|
+
keyLight,
|
|
60
|
+
orbitLayer,
|
|
61
|
+
objectLayer,
|
|
62
|
+
raycaster,
|
|
63
|
+
pointer: new THREE.Vector2(),
|
|
64
|
+
};
|
|
65
|
+
if (pendingUpdate) {
|
|
66
|
+
applyUpdate(pendingUpdate);
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
.catch((error) => {
|
|
70
|
+
if (destroyed) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
root.innerHTML = `<div class="wo-viewer-3d-loading is-error">${escapeHtml(error instanceof Error ? error.message : "WorldOrbit 3D could not be initialized.")}</div>`;
|
|
74
|
+
});
|
|
75
|
+
return {
|
|
76
|
+
update(next) {
|
|
77
|
+
pendingUpdate = next;
|
|
78
|
+
applyUpdate(next);
|
|
79
|
+
},
|
|
80
|
+
hitTest(clientX, clientY) {
|
|
81
|
+
if (!runtime || !currentScene) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
const rect = runtime.renderer.domElement.getBoundingClientRect();
|
|
85
|
+
if (!rect.width || !rect.height) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
runtime.pointer.x = ((clientX - rect.left) / rect.width) * 2 - 1;
|
|
89
|
+
runtime.pointer.y = -((clientY - rect.top) / rect.height) * 2 + 1;
|
|
90
|
+
runtime.raycaster.setFromCamera(runtime.pointer, runtime.camera);
|
|
91
|
+
const intersections = runtime.raycaster.intersectObjects(raycastTargets, true);
|
|
92
|
+
for (const hit of intersections) {
|
|
93
|
+
const objectId = resolveUserDataObjectId(hit.object);
|
|
94
|
+
if (objectId) {
|
|
95
|
+
return objectId;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
},
|
|
100
|
+
projectObjectToContainer(objectId) {
|
|
101
|
+
if (!runtime) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
const position = currentPositions.get(objectId);
|
|
105
|
+
if (!position) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const vector = new runtime.THREE.Vector3(position.x, position.y, position.z);
|
|
109
|
+
vector.project(runtime.camera);
|
|
110
|
+
if (vector.z > 1) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const rect = runtime.renderer.domElement.getBoundingClientRect();
|
|
114
|
+
const containerRect = container.getBoundingClientRect();
|
|
115
|
+
return {
|
|
116
|
+
x: rect.left -
|
|
117
|
+
containerRect.left +
|
|
118
|
+
((vector.x + 1) / 2) * rect.width,
|
|
119
|
+
y: rect.top -
|
|
120
|
+
containerRect.top +
|
|
121
|
+
((1 - vector.y) / 2) * rect.height,
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
destroy() {
|
|
125
|
+
destroyed = true;
|
|
126
|
+
pendingUpdate = null;
|
|
127
|
+
runtime?.renderer.dispose();
|
|
128
|
+
root.remove();
|
|
129
|
+
objectVisuals.clear();
|
|
130
|
+
orbitVisuals.clear();
|
|
131
|
+
raycastTargets.length = 0;
|
|
132
|
+
runtime = null;
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
function applyUpdate(next) {
|
|
136
|
+
if (!runtime) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const sceneChanged = currentScene !== next.spatialScene;
|
|
140
|
+
currentScene = next.spatialScene;
|
|
141
|
+
currentRenderOptions = next.renderOptions;
|
|
142
|
+
currentState = next.state;
|
|
143
|
+
currentVisibleObjectIds = next.visibleObjectIds;
|
|
144
|
+
currentSelectedObjectId = next.selectedObjectId;
|
|
145
|
+
currentHoveredObjectId = next.hoveredObjectId;
|
|
146
|
+
currentTimeSeconds = next.timeSeconds;
|
|
147
|
+
if (sceneChanged) {
|
|
148
|
+
rebuildScene(next.spatialScene);
|
|
149
|
+
}
|
|
150
|
+
resizeRenderer(next.spatialScene);
|
|
151
|
+
currentPositions = evaluateSpatialSceneAtTime(next.spatialScene, next.timeSeconds);
|
|
152
|
+
updateObjectTransforms();
|
|
153
|
+
updateOrbitTransforms();
|
|
154
|
+
updateVisibility();
|
|
155
|
+
updateInteractionState();
|
|
156
|
+
updateCamera();
|
|
157
|
+
renderNow();
|
|
158
|
+
}
|
|
159
|
+
function rebuildScene(spatialScene) {
|
|
160
|
+
if (!runtime) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
clearGroup(runtime.orbitLayer);
|
|
164
|
+
clearGroup(runtime.objectLayer);
|
|
165
|
+
objectVisuals.clear();
|
|
166
|
+
orbitVisuals.clear();
|
|
167
|
+
raycastTargets.length = 0;
|
|
168
|
+
const theme = resolveTheme(currentRenderOptions?.theme);
|
|
169
|
+
runtime.scene3d.background = new runtime.THREE.Color(theme.backgroundStart);
|
|
170
|
+
for (const orbit of spatialScene.orbits) {
|
|
171
|
+
const visual = createOrbitVisual(runtime.THREE, orbit, theme);
|
|
172
|
+
runtime.orbitLayer.add(visual.root);
|
|
173
|
+
orbitVisuals.set(orbit.objectId, visual);
|
|
174
|
+
raycastTargets.push(visual.root);
|
|
175
|
+
}
|
|
176
|
+
for (const object of spatialScene.objects) {
|
|
177
|
+
const visual = createObjectVisual(runtime.THREE, object, theme);
|
|
178
|
+
runtime.objectLayer.add(visual.root);
|
|
179
|
+
objectVisuals.set(object.objectId, visual);
|
|
180
|
+
raycastTargets.push(visual.root);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function updateObjectTransforms() {
|
|
184
|
+
for (const object of currentScene?.objects ?? []) {
|
|
185
|
+
const visual = objectVisuals.get(object.objectId);
|
|
186
|
+
const position = currentPositions.get(object.objectId);
|
|
187
|
+
if (!visual || !position) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
visual.root.position.set(position.x, position.y, position.z);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function updateOrbitTransforms() {
|
|
194
|
+
for (const orbit of currentScene?.orbits ?? []) {
|
|
195
|
+
const visual = orbitVisuals.get(orbit.objectId);
|
|
196
|
+
if (!visual) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const parentPosition = currentPositions.get(orbit.parentId);
|
|
200
|
+
visual.root.position.set(parentPosition?.x ?? orbit.center.x, parentPosition?.y ?? orbit.center.y, parentPosition?.z ?? orbit.center.z);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function updateVisibility() {
|
|
204
|
+
const layers = currentRenderOptions?.layers ?? {};
|
|
205
|
+
for (const object of currentScene?.objects ?? []) {
|
|
206
|
+
const visual = objectVisuals.get(object.objectId);
|
|
207
|
+
if (!visual) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const hideStructure = layers.structures === false &&
|
|
211
|
+
(object.object.type === "structure" || object.object.type === "phenomenon");
|
|
212
|
+
const hideObjects = layers.objects === false;
|
|
213
|
+
visual.root.visible =
|
|
214
|
+
!object.hidden &&
|
|
215
|
+
currentVisibleObjectIds.has(object.objectId) &&
|
|
216
|
+
!hideStructure &&
|
|
217
|
+
!hideObjects;
|
|
218
|
+
}
|
|
219
|
+
for (const orbit of currentScene?.orbits ?? []) {
|
|
220
|
+
const visual = orbitVisuals.get(orbit.objectId);
|
|
221
|
+
if (!visual) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
const hideStructure = layers.structures === false &&
|
|
225
|
+
(orbit.object.type === "structure" || orbit.object.type === "phenomenon");
|
|
226
|
+
visual.root.visible =
|
|
227
|
+
!orbit.hidden &&
|
|
228
|
+
currentVisibleObjectIds.has(orbit.objectId) &&
|
|
229
|
+
layers.orbits !== false &&
|
|
230
|
+
!hideStructure;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function updateInteractionState() {
|
|
234
|
+
if (!runtime) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
for (const visual of objectVisuals.values()) {
|
|
238
|
+
applyVisualState(runtime.THREE, visual.materials, visual.baseColor, currentSelectedObjectId === visual.objectId, currentHoveredObjectId === visual.objectId);
|
|
239
|
+
const scale = currentSelectedObjectId === visual.objectId
|
|
240
|
+
? 1.2
|
|
241
|
+
: currentHoveredObjectId === visual.objectId
|
|
242
|
+
? 1.1
|
|
243
|
+
: 1;
|
|
244
|
+
visual.root.scale.set(scale, scale, scale);
|
|
245
|
+
}
|
|
246
|
+
for (const visual of orbitVisuals.values()) {
|
|
247
|
+
applyVisualState(runtime.THREE, visual.materials, visual.baseColor, currentSelectedObjectId === visual.objectId, currentHoveredObjectId === visual.objectId);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function updateCamera() {
|
|
251
|
+
if (!runtime || !currentScene || !currentState) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const sceneCamera = currentRenderOptions?.camera ?? currentScene.camera;
|
|
255
|
+
const bounds = currentScene.contentBounds;
|
|
256
|
+
const size = Math.max(bounds.width, bounds.depth, bounds.height, 160);
|
|
257
|
+
const yaw = degreesToRadians((sceneCamera?.azimuth ?? 34) + currentState.rotationDeg);
|
|
258
|
+
const pitch = degreesToRadians(clampValue(sceneCamera?.elevation ?? 24, -75, 75));
|
|
259
|
+
const zoomDistanceFactor = clampValue(2.4 / Math.max(currentState.scale, 0.1), 0.35, 8);
|
|
260
|
+
const semanticDistance = clampValue(sceneCamera?.distance ?? 6, 2, 24);
|
|
261
|
+
const distance = clampValue(size * zoomDistanceFactor * (semanticDistance / 6), 28, 8_000);
|
|
262
|
+
const panFactor = Math.max(size / 900, 0.12);
|
|
263
|
+
const target = new runtime.THREE.Vector3(bounds.center.x - currentState.translateX * panFactor, bounds.center.y, bounds.center.z - currentState.translateY * panFactor);
|
|
264
|
+
runtime.camera.position.set(target.x + distance * Math.cos(pitch) * Math.sin(yaw), target.y + distance * Math.sin(pitch), target.z + distance * Math.cos(pitch) * Math.cos(yaw));
|
|
265
|
+
runtime.camera.lookAt(target);
|
|
266
|
+
if (sceneCamera?.roll) {
|
|
267
|
+
runtime.camera.rotation.z = degreesToRadians(sceneCamera.roll);
|
|
268
|
+
}
|
|
269
|
+
runtime.keyLight.position.copy(runtime.camera.position);
|
|
270
|
+
}
|
|
271
|
+
function resizeRenderer(spatialScene) {
|
|
272
|
+
if (!runtime) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const width = Math.max(1, Math.round(container.clientWidth || spatialScene.width || 960));
|
|
276
|
+
const height = Math.max(1, Math.round(container.clientHeight || spatialScene.height || 560));
|
|
277
|
+
runtime.renderer.setSize(width, height, false);
|
|
278
|
+
runtime.camera.aspect = width / height;
|
|
279
|
+
runtime.camera.updateProjectionMatrix();
|
|
280
|
+
}
|
|
281
|
+
function renderNow() {
|
|
282
|
+
if (!runtime) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
runtime.renderer.render(runtime.scene3d, runtime.camera);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function createObjectVisual(THREE, object, theme) {
|
|
289
|
+
const root = new THREE.Group();
|
|
290
|
+
root.userData.objectId = object.objectId;
|
|
291
|
+
const baseColor = object.fillColor ?? colorForObject(object);
|
|
292
|
+
const material = new THREE.MeshPhongMaterial({
|
|
293
|
+
color: baseColor,
|
|
294
|
+
emissive: object.object.type === "star"
|
|
295
|
+
? new THREE.Color(theme.starGlow)
|
|
296
|
+
: new THREE.Color(0x000000),
|
|
297
|
+
emissiveIntensity: object.object.type === "star" ? 0.6 : 0.08,
|
|
298
|
+
transparent: true,
|
|
299
|
+
opacity: object.object.type === "phenomenon" ? 0.7 : 1,
|
|
300
|
+
});
|
|
301
|
+
const geometry = geometryForObject(THREE, object);
|
|
302
|
+
const body = new THREE.Mesh(geometry, material);
|
|
303
|
+
body.userData.objectId = object.objectId;
|
|
304
|
+
root.add(body);
|
|
305
|
+
return {
|
|
306
|
+
objectId: object.objectId,
|
|
307
|
+
root,
|
|
308
|
+
body,
|
|
309
|
+
materials: [material],
|
|
310
|
+
baseColor,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function createOrbitVisual(THREE, orbit, theme) {
|
|
314
|
+
const root = new THREE.Group();
|
|
315
|
+
root.userData.objectId = orbit.objectId;
|
|
316
|
+
root.rotation.y = degreesToRadians(orbit.rotationDeg);
|
|
317
|
+
root.rotation.x = degreesToRadians(orbit.inclinationDeg);
|
|
318
|
+
const baseColor = orbit.object.properties.color ?? theme.orbit;
|
|
319
|
+
const materials = [];
|
|
320
|
+
if (orbit.band) {
|
|
321
|
+
const material = new THREE.MeshBasicMaterial({
|
|
322
|
+
color: baseColor,
|
|
323
|
+
transparent: true,
|
|
324
|
+
opacity: 0.42,
|
|
325
|
+
side: 2,
|
|
326
|
+
});
|
|
327
|
+
const geometry = bandGeometryForOrbit(THREE, orbit);
|
|
328
|
+
const mesh = new THREE.Mesh(geometry, material);
|
|
329
|
+
mesh.userData.objectId = orbit.objectId;
|
|
330
|
+
root.add(mesh);
|
|
331
|
+
materials.push(material);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
const material = new THREE.LineBasicMaterial({
|
|
335
|
+
color: baseColor,
|
|
336
|
+
transparent: true,
|
|
337
|
+
opacity: 0.55,
|
|
338
|
+
});
|
|
339
|
+
const points = sampleOrbitPoints(THREE, orbit);
|
|
340
|
+
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
|
341
|
+
const line = new THREE.LineLoop(geometry, material);
|
|
342
|
+
line.userData.objectId = orbit.objectId;
|
|
343
|
+
root.add(line);
|
|
344
|
+
materials.push(material);
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
objectId: orbit.objectId,
|
|
348
|
+
root,
|
|
349
|
+
materials,
|
|
350
|
+
baseColor,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
function geometryForObject(THREE, object) {
|
|
354
|
+
const radius = Math.max(object.visualRadius, 2);
|
|
355
|
+
switch (object.object.type) {
|
|
356
|
+
case "star":
|
|
357
|
+
return new THREE.SphereGeometry(radius * 1.12, 28, 20);
|
|
358
|
+
case "structure":
|
|
359
|
+
return new THREE.BoxGeometry(radius * 1.5, radius * 1.5, radius * 1.5);
|
|
360
|
+
case "phenomenon":
|
|
361
|
+
return new THREE.OctahedronGeometry(radius * 1.25, 0);
|
|
362
|
+
case "belt":
|
|
363
|
+
case "ring":
|
|
364
|
+
return new THREE.OctahedronGeometry(Math.max(radius * 0.85, 3), 0);
|
|
365
|
+
default:
|
|
366
|
+
return new THREE.SphereGeometry(radius, 20, 14);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function bandGeometryForOrbit(THREE, orbit) {
|
|
370
|
+
const thickness = Math.max(orbit.bandThickness ?? 8, 3);
|
|
371
|
+
const points = sampleOrbitPoints(THREE, orbit, 72);
|
|
372
|
+
const curve = new THREE.CatmullRomCurve3(points, true);
|
|
373
|
+
return new THREE.TubeGeometry(curve, 128, thickness * 0.28, 10, true);
|
|
374
|
+
}
|
|
375
|
+
function sampleOrbitPoints(THREE, orbit, segments = 96) {
|
|
376
|
+
const points = [];
|
|
377
|
+
const semiMajor = Math.max(orbit.semiMajor, orbit.radius ?? 1, 1);
|
|
378
|
+
const semiMinor = Math.max(orbit.semiMinor, orbit.radius ?? 1, 1);
|
|
379
|
+
for (let index = 0; index < segments; index += 1) {
|
|
380
|
+
const angle = (index / segments) * Math.PI * 2;
|
|
381
|
+
points.push(new THREE.Vector3(Math.cos(angle) * semiMajor, 0, Math.sin(angle) * semiMinor));
|
|
382
|
+
}
|
|
383
|
+
return points;
|
|
384
|
+
}
|
|
385
|
+
function colorForObject(object) {
|
|
386
|
+
switch (object.object.type) {
|
|
387
|
+
case "star":
|
|
388
|
+
return "#ffd36a";
|
|
389
|
+
case "planet":
|
|
390
|
+
return "#73b6ff";
|
|
391
|
+
case "moon":
|
|
392
|
+
return "#d8dde8";
|
|
393
|
+
case "belt":
|
|
394
|
+
return "#b8926a";
|
|
395
|
+
case "ring":
|
|
396
|
+
return "#cdbf9a";
|
|
397
|
+
case "structure":
|
|
398
|
+
return "#ffce8a";
|
|
399
|
+
case "phenomenon":
|
|
400
|
+
return "#7ce6ff";
|
|
401
|
+
case "asteroid":
|
|
402
|
+
return "#cdb5a1";
|
|
403
|
+
case "comet":
|
|
404
|
+
return "#b8f2ff";
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function applyVisualState(THREE, materials, baseColor, selected, hovered) {
|
|
408
|
+
const color = new THREE.Color(baseColor);
|
|
409
|
+
if (selected) {
|
|
410
|
+
color.offsetHSL(0, 0, 0.16);
|
|
411
|
+
}
|
|
412
|
+
else if (hovered) {
|
|
413
|
+
color.offsetHSL(0, 0, 0.08);
|
|
414
|
+
}
|
|
415
|
+
for (const material of materials) {
|
|
416
|
+
if (!material) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
material.color?.set?.(color);
|
|
420
|
+
if (typeof material.opacity === "number") {
|
|
421
|
+
material.opacity = selected ? 0.85 : hovered ? 0.72 : material.transparent ? 0.55 : 1;
|
|
422
|
+
}
|
|
423
|
+
material.emissive?.set?.(selected ? new THREE.Color("#ffdda9") : hovered ? new THREE.Color("#cfe9ff") : new THREE.Color(0x000000));
|
|
424
|
+
material.emissiveIntensity = selected ? 0.28 : hovered ? 0.14 : material.emissiveIntensity ?? 0.08;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
function clearGroup(group) {
|
|
428
|
+
while (group.children.length > 0) {
|
|
429
|
+
const child = group.children[0];
|
|
430
|
+
group.remove(child);
|
|
431
|
+
child.geometry?.dispose?.();
|
|
432
|
+
if (Array.isArray(child.material)) {
|
|
433
|
+
child.material.forEach((entry) => entry?.dispose?.());
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
child.material?.dispose?.();
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function ensureWebGLSupport() {
|
|
441
|
+
if (typeof document === "undefined") {
|
|
442
|
+
throw new WorldOrbit3DUnavailableError();
|
|
443
|
+
}
|
|
444
|
+
const navigatorLike = document.defaultView?.navigator ??
|
|
445
|
+
globalThis.window?.navigator ??
|
|
446
|
+
(typeof navigator !== "undefined" ? navigator : undefined);
|
|
447
|
+
if (/jsdom/i.test(navigatorLike?.userAgent ?? "")) {
|
|
448
|
+
throw new WorldOrbit3DUnavailableError("WorldOrbit 3D needs WebGL support, but this environment did not provide it.");
|
|
449
|
+
}
|
|
450
|
+
const canvas = document.createElement("canvas");
|
|
451
|
+
const tryGetContext = (kind) => {
|
|
452
|
+
try {
|
|
453
|
+
return canvas.getContext?.(kind) ?? null;
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
const context = tryGetContext("webgl2") ??
|
|
460
|
+
tryGetContext("webgl") ??
|
|
461
|
+
tryGetContext("experimental-webgl");
|
|
462
|
+
if (!context) {
|
|
463
|
+
throw new WorldOrbit3DUnavailableError("WorldOrbit 3D needs WebGL support, but this environment did not provide it.");
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
function resolveUserDataObjectId(target) {
|
|
467
|
+
let cursor = target;
|
|
468
|
+
while (cursor) {
|
|
469
|
+
if (typeof cursor.userData?.objectId === "string") {
|
|
470
|
+
return cursor.userData.objectId;
|
|
471
|
+
}
|
|
472
|
+
cursor = cursor.parent;
|
|
473
|
+
}
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
function clampValue(value, min, max) {
|
|
477
|
+
return Math.min(Math.max(value, min), max);
|
|
478
|
+
}
|
|
479
|
+
function degreesToRadians(value) {
|
|
480
|
+
return (value * Math.PI) / 180;
|
|
481
|
+
}
|
|
482
|
+
function escapeHtml(value) {
|
|
483
|
+
return value
|
|
484
|
+
.replaceAll("&", "&")
|
|
485
|
+
.replaceAll("<", "<")
|
|
486
|
+
.replaceAll(">", ">")
|
|
487
|
+
.replaceAll('"', """);
|
|
488
|
+
}
|
|
489
|
+
function loadThreeModule() {
|
|
490
|
+
if (!threeModulePromise) {
|
|
491
|
+
threeModulePromise = import("./vendor/three.module.js");
|
|
492
|
+
}
|
|
493
|
+
return threeModulePromise;
|
|
494
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { CoordinatePoint, RenderOrbitVisual, RenderProjectionFallback, RenderPresetName, RenderSceneEvent, RenderSceneGroup, RenderSceneLabel, RenderScaleModel, RenderScene, RenderSceneObject, RenderSceneViewpoint, SceneRenderOptions, ViewProjection, WorldOrbitObject, WorldOrbitDocument, WorldOrbitViewCamera } from "@worldorbit/core";
|
|
1
|
+
import type { CoordinatePoint, RenderOrbitVisual, RenderProjectionFallback, RenderPresetName, RenderSceneEvent, RenderSceneGroup, RenderSceneLabel, RenderScaleModel, RenderScene, RenderSceneObject, RenderSceneViewpoint, SceneRenderOptions, SpatialScene, ViewProjection, WorldOrbitObject, WorldOrbitDocument, WorldOrbitViewCamera } from "@worldorbit/core";
|
|
2
2
|
export type WorldOrbitThemeName = "atlas" | "nightglass" | "ember";
|
|
3
|
-
export type
|
|
3
|
+
export type WorldOrbitViewMode = "2d" | "3d";
|
|
4
|
+
export type WorldOrbitEmbedMode = "static" | "interactive" | "interactive-2d" | "interactive-3d";
|
|
4
5
|
export type TooltipMode = "hover" | "pinned" | "disabled";
|
|
5
6
|
export interface WorldOrbitTheme {
|
|
6
7
|
name: string;
|
|
@@ -62,6 +63,7 @@ export interface SvgRenderOptions extends SceneRenderOptions {
|
|
|
62
63
|
export interface ViewerRenderOptions extends Omit<SvgRenderOptions, "selectedObjectId"> {
|
|
63
64
|
projection?: "document" | ViewProjection;
|
|
64
65
|
scaleModel?: Partial<RenderScaleModel>;
|
|
66
|
+
viewMode?: WorldOrbitViewMode;
|
|
65
67
|
}
|
|
66
68
|
export interface ViewerState {
|
|
67
69
|
scale: number;
|
|
@@ -115,9 +117,16 @@ export interface ViewerAtlasState {
|
|
|
115
117
|
layers?: ViewerLayerOptions;
|
|
116
118
|
scaleModel?: Partial<RenderScaleModel>;
|
|
117
119
|
activeEventId?: string | null;
|
|
120
|
+
viewMode?: WorldOrbitViewMode;
|
|
118
121
|
};
|
|
119
122
|
filter: ViewerFilter | null;
|
|
120
123
|
}
|
|
124
|
+
export interface ViewerAnimationState {
|
|
125
|
+
playing: boolean;
|
|
126
|
+
speed: number;
|
|
127
|
+
timeSeconds: number;
|
|
128
|
+
frozenByEvent: boolean;
|
|
129
|
+
}
|
|
121
130
|
export interface ViewerBookmark {
|
|
122
131
|
id: string;
|
|
123
132
|
label: string;
|
|
@@ -154,6 +163,7 @@ export interface InteractiveViewerOptions extends ViewerRenderOptions {
|
|
|
154
163
|
source?: string;
|
|
155
164
|
document?: WorldOrbitDocument;
|
|
156
165
|
scene?: RenderScene;
|
|
166
|
+
spatialScene?: SpatialScene;
|
|
157
167
|
initialViewpointId?: string;
|
|
158
168
|
initialSelectionObjectId?: string;
|
|
159
169
|
initialFilter?: ViewerFilter | null;
|
|
@@ -194,11 +204,18 @@ export interface WorldOrbitViewer {
|
|
|
194
204
|
getScene(): RenderScene;
|
|
195
205
|
getRenderOptions(): ViewerRenderOptions;
|
|
196
206
|
setRenderOptions(options: Partial<ViewerRenderOptions>): void;
|
|
207
|
+
getViewMode(): WorldOrbitViewMode;
|
|
208
|
+
setViewMode(mode: WorldOrbitViewMode): void;
|
|
197
209
|
listViewpoints(): RenderSceneViewpoint[];
|
|
198
210
|
getActiveViewpoint(): RenderSceneViewpoint | null;
|
|
199
211
|
goToViewpoint(id: string): boolean;
|
|
200
212
|
getActiveEventId(): string | null;
|
|
201
213
|
setActiveEvent(id: string | null): void;
|
|
214
|
+
playAnimation(): void;
|
|
215
|
+
pauseAnimation(): void;
|
|
216
|
+
resetAnimation(): void;
|
|
217
|
+
setAnimationSpeed(multiplier: number): void;
|
|
218
|
+
getAnimationState(): ViewerAnimationState;
|
|
202
219
|
search(query: string, limit?: number): ViewerSearchResult[];
|
|
203
220
|
getFilter(): ViewerFilter | null;
|
|
204
221
|
setFilter(filter: ViewerFilter | null): void;
|
|
@@ -250,11 +267,13 @@ export interface WorldOrbitEmbedPayload {
|
|
|
250
267
|
version: "2.0";
|
|
251
268
|
mode: WorldOrbitEmbedMode;
|
|
252
269
|
scene: RenderScene;
|
|
270
|
+
spatialScene?: SpatialScene;
|
|
253
271
|
options?: {
|
|
254
272
|
theme?: WorldOrbitTheme | WorldOrbitThemeName;
|
|
255
273
|
layers?: ViewerLayerOptions;
|
|
256
274
|
subtitle?: string;
|
|
257
275
|
preset?: SceneRenderOptions["preset"];
|
|
276
|
+
viewMode?: WorldOrbitViewMode;
|
|
258
277
|
initialViewpointId?: string;
|
|
259
278
|
initialSelectionObjectId?: string;
|
|
260
279
|
initialFilter?: ViewerFilter | null;
|