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.
Files changed (203) hide show
  1. package/README.md +91 -18
  2. package/dist/browser/core/dist/atlas-edit.d.ts +11 -0
  3. package/dist/browser/core/dist/atlas-edit.js +347 -0
  4. package/dist/browser/core/dist/atlas-utils.d.ts +22 -0
  5. package/dist/browser/core/dist/atlas-utils.js +189 -0
  6. package/dist/browser/core/dist/atlas-validate.d.ts +2 -0
  7. package/dist/browser/core/dist/atlas-validate.js +488 -0
  8. package/dist/browser/core/dist/diagnostics.d.ts +10 -0
  9. package/dist/browser/core/dist/diagnostics.js +109 -0
  10. package/dist/browser/core/dist/draft-parse.d.ts +3 -0
  11. package/dist/browser/core/dist/draft-parse.js +1654 -0
  12. package/dist/browser/core/dist/draft.d.ts +21 -0
  13. package/dist/browser/core/dist/draft.js +482 -0
  14. package/dist/browser/core/dist/errors.d.ts +7 -0
  15. package/dist/browser/core/dist/errors.js +16 -0
  16. package/dist/browser/core/dist/format.d.ts +4 -0
  17. package/dist/browser/core/dist/format.js +613 -0
  18. package/dist/browser/core/dist/index.d.ts +29 -0
  19. package/dist/browser/core/dist/index.js +35 -6101
  20. package/dist/browser/core/dist/load.d.ts +4 -0
  21. package/dist/browser/core/dist/load.js +182 -0
  22. package/dist/browser/core/dist/markdown.d.ts +2 -0
  23. package/dist/browser/core/dist/markdown.js +37 -0
  24. package/dist/browser/core/dist/normalize.d.ts +2 -0
  25. package/dist/browser/core/dist/normalize.js +312 -0
  26. package/dist/browser/core/dist/parse.d.ts +2 -0
  27. package/dist/browser/core/dist/parse.js +133 -0
  28. package/dist/browser/core/dist/scene.d.ts +3 -0
  29. package/dist/browser/core/dist/scene.js +1901 -0
  30. package/dist/browser/core/dist/schema.d.ts +8 -0
  31. package/dist/browser/core/dist/schema.js +298 -0
  32. package/dist/browser/core/dist/spatial-scene.d.ts +3 -0
  33. package/dist/browser/core/dist/spatial-scene.js +420 -0
  34. package/dist/browser/core/dist/tokenize.d.ts +4 -0
  35. package/dist/browser/core/dist/tokenize.js +68 -0
  36. package/dist/browser/core/dist/types.d.ts +637 -0
  37. package/dist/browser/core/dist/types.js +1 -0
  38. package/dist/browser/core/dist/validate.d.ts +2 -0
  39. package/dist/browser/core/dist/validate.js +56 -0
  40. package/dist/browser/editor/dist/editor.d.ts +2 -0
  41. package/dist/browser/editor/dist/editor.js +3700 -0
  42. package/dist/browser/editor/dist/index.d.ts +2 -0
  43. package/dist/browser/editor/dist/index.js +1 -11702
  44. package/dist/browser/editor/dist/types.d.ts +59 -0
  45. package/dist/browser/editor/dist/types.js +1 -0
  46. package/dist/browser/markdown/dist/html.d.ts +3 -0
  47. package/dist/browser/markdown/dist/html.js +64 -0
  48. package/dist/browser/markdown/dist/index.d.ts +4 -0
  49. package/dist/browser/markdown/dist/index.js +3 -5766
  50. package/dist/browser/markdown/dist/rehype.d.ts +10 -0
  51. package/dist/browser/markdown/dist/rehype.js +49 -0
  52. package/dist/browser/markdown/dist/remark.d.ts +9 -0
  53. package/dist/browser/markdown/dist/remark.js +28 -0
  54. package/dist/browser/markdown/dist/types.d.ts +11 -0
  55. package/dist/browser/markdown/dist/types.js +1 -0
  56. package/dist/browser/viewer/dist/atlas-state.d.ts +12 -0
  57. package/dist/browser/viewer/dist/atlas-state.js +269 -0
  58. package/dist/browser/viewer/dist/atlas-viewer.d.ts +2 -0
  59. package/dist/browser/viewer/dist/atlas-viewer.js +495 -0
  60. package/dist/browser/viewer/dist/custom-element.d.ts +1 -0
  61. package/dist/browser/viewer/dist/custom-element.js +78 -0
  62. package/dist/browser/viewer/dist/embed.d.ts +24 -0
  63. package/dist/browser/viewer/dist/embed.js +172 -0
  64. package/dist/browser/viewer/dist/errors.d.ts +6 -0
  65. package/dist/browser/viewer/dist/errors.js +12 -0
  66. package/dist/browser/viewer/dist/index.d.ts +10 -0
  67. package/dist/browser/viewer/dist/index.js +9 -7901
  68. package/dist/browser/viewer/dist/minimap.d.ts +3 -0
  69. package/dist/browser/viewer/dist/minimap.js +63 -0
  70. package/dist/browser/viewer/dist/render.d.ts +6 -0
  71. package/dist/browser/viewer/dist/render.js +670 -0
  72. package/dist/browser/viewer/dist/runtime-3d.d.ts +19 -0
  73. package/dist/browser/viewer/dist/runtime-3d.js +494 -0
  74. package/dist/browser/viewer/dist/theme.d.ts +4 -0
  75. package/dist/browser/viewer/dist/theme.js +103 -0
  76. package/dist/browser/viewer/dist/tooltip.d.ts +3 -0
  77. package/dist/browser/viewer/dist/tooltip.js +198 -0
  78. package/dist/browser/viewer/dist/types.d.ts +292 -0
  79. package/dist/browser/viewer/dist/types.js +1 -0
  80. package/dist/browser/viewer/dist/vendor/three.module.js +53032 -0
  81. package/dist/browser/viewer/dist/viewer-state.d.ts +19 -0
  82. package/dist/browser/viewer/dist/viewer-state.js +162 -0
  83. package/dist/browser/viewer/dist/viewer.d.ts +2 -0
  84. package/dist/browser/viewer/dist/viewer.js +1662 -0
  85. package/dist/unpkg/core/dist/atlas-edit.d.ts +11 -0
  86. package/dist/unpkg/core/dist/atlas-edit.js +347 -0
  87. package/dist/unpkg/core/dist/atlas-utils.d.ts +22 -0
  88. package/dist/unpkg/core/dist/atlas-utils.js +189 -0
  89. package/dist/unpkg/core/dist/atlas-validate.d.ts +2 -0
  90. package/dist/unpkg/core/dist/atlas-validate.js +488 -0
  91. package/dist/unpkg/core/dist/diagnostics.d.ts +10 -0
  92. package/dist/unpkg/core/dist/diagnostics.js +109 -0
  93. package/dist/unpkg/core/dist/draft-parse.d.ts +3 -0
  94. package/dist/unpkg/core/dist/draft-parse.js +1654 -0
  95. package/dist/unpkg/core/dist/draft.d.ts +21 -0
  96. package/dist/unpkg/core/dist/draft.js +482 -0
  97. package/dist/unpkg/core/dist/errors.d.ts +7 -0
  98. package/dist/unpkg/core/dist/errors.js +16 -0
  99. package/dist/unpkg/core/dist/format.d.ts +4 -0
  100. package/dist/unpkg/core/dist/format.js +613 -0
  101. package/dist/unpkg/core/dist/index.d.ts +29 -0
  102. package/dist/unpkg/core/dist/index.js +35 -6173
  103. package/dist/unpkg/core/dist/load.d.ts +4 -0
  104. package/dist/unpkg/core/dist/load.js +182 -0
  105. package/dist/unpkg/core/dist/markdown.d.ts +2 -0
  106. package/dist/unpkg/core/dist/markdown.js +37 -0
  107. package/dist/unpkg/core/dist/normalize.d.ts +2 -0
  108. package/dist/unpkg/core/dist/normalize.js +312 -0
  109. package/dist/unpkg/core/dist/parse.d.ts +2 -0
  110. package/dist/unpkg/core/dist/parse.js +133 -0
  111. package/dist/unpkg/core/dist/scene.d.ts +3 -0
  112. package/dist/unpkg/core/dist/scene.js +1901 -0
  113. package/dist/unpkg/core/dist/schema.d.ts +8 -0
  114. package/dist/unpkg/core/dist/schema.js +298 -0
  115. package/dist/unpkg/core/dist/spatial-scene.d.ts +3 -0
  116. package/dist/unpkg/core/dist/spatial-scene.js +420 -0
  117. package/dist/unpkg/core/dist/tokenize.d.ts +4 -0
  118. package/dist/unpkg/core/dist/tokenize.js +68 -0
  119. package/dist/unpkg/core/dist/types.d.ts +637 -0
  120. package/dist/unpkg/core/dist/types.js +1 -0
  121. package/dist/unpkg/core/dist/validate.d.ts +2 -0
  122. package/dist/unpkg/core/dist/validate.js +56 -0
  123. package/dist/unpkg/editor/dist/editor.d.ts +2 -0
  124. package/dist/unpkg/editor/dist/editor.js +3700 -0
  125. package/dist/unpkg/editor/dist/index.d.ts +2 -0
  126. package/dist/unpkg/editor/dist/index.js +1 -11727
  127. package/dist/unpkg/editor/dist/types.d.ts +59 -0
  128. package/dist/unpkg/editor/dist/types.js +1 -0
  129. package/dist/unpkg/markdown/dist/html.d.ts +3 -0
  130. package/dist/unpkg/markdown/dist/html.js +64 -0
  131. package/dist/unpkg/markdown/dist/index.d.ts +4 -0
  132. package/dist/unpkg/markdown/dist/index.js +3 -5794
  133. package/dist/unpkg/markdown/dist/rehype.d.ts +10 -0
  134. package/dist/unpkg/markdown/dist/rehype.js +49 -0
  135. package/dist/unpkg/markdown/dist/remark.d.ts +9 -0
  136. package/dist/unpkg/markdown/dist/remark.js +28 -0
  137. package/dist/unpkg/markdown/dist/types.d.ts +11 -0
  138. package/dist/unpkg/markdown/dist/types.js +1 -0
  139. package/dist/unpkg/viewer/dist/atlas-state.d.ts +12 -0
  140. package/dist/unpkg/viewer/dist/atlas-state.js +269 -0
  141. package/dist/unpkg/viewer/dist/atlas-viewer.d.ts +2 -0
  142. package/dist/unpkg/viewer/dist/atlas-viewer.js +495 -0
  143. package/dist/unpkg/viewer/dist/custom-element.d.ts +1 -0
  144. package/dist/unpkg/viewer/dist/custom-element.js +78 -0
  145. package/dist/unpkg/viewer/dist/embed.d.ts +24 -0
  146. package/dist/unpkg/viewer/dist/embed.js +172 -0
  147. package/dist/unpkg/viewer/dist/errors.d.ts +6 -0
  148. package/dist/unpkg/viewer/dist/errors.js +12 -0
  149. package/dist/unpkg/viewer/dist/index.d.ts +10 -0
  150. package/dist/unpkg/viewer/dist/index.js +9 -7958
  151. package/dist/unpkg/viewer/dist/minimap.d.ts +3 -0
  152. package/dist/unpkg/viewer/dist/minimap.js +63 -0
  153. package/dist/unpkg/viewer/dist/render.d.ts +6 -0
  154. package/dist/unpkg/viewer/dist/render.js +670 -0
  155. package/dist/unpkg/viewer/dist/runtime-3d.d.ts +19 -0
  156. package/dist/unpkg/viewer/dist/runtime-3d.js +494 -0
  157. package/dist/unpkg/viewer/dist/theme.d.ts +4 -0
  158. package/dist/unpkg/viewer/dist/theme.js +103 -0
  159. package/dist/unpkg/viewer/dist/tooltip.d.ts +3 -0
  160. package/dist/unpkg/viewer/dist/tooltip.js +198 -0
  161. package/dist/unpkg/viewer/dist/types.d.ts +292 -0
  162. package/dist/unpkg/viewer/dist/types.js +1 -0
  163. package/dist/unpkg/viewer/dist/vendor/three.module.js +53032 -0
  164. package/dist/unpkg/viewer/dist/viewer-state.d.ts +19 -0
  165. package/dist/unpkg/viewer/dist/viewer-state.js +162 -0
  166. package/dist/unpkg/viewer/dist/viewer.d.ts +2 -0
  167. package/dist/unpkg/viewer/dist/viewer.js +1662 -0
  168. package/dist/unpkg/worldorbit-core.min.js +1 -12
  169. package/dist/unpkg/worldorbit-editor.min.js +1 -894
  170. package/dist/unpkg/worldorbit-markdown.min.js +1 -103
  171. package/dist/unpkg/worldorbit-viewer.min.js +1 -259
  172. package/dist/unpkg/worldorbit.js +2 -9243
  173. package/dist/unpkg/worldorbit.min.js +2 -263
  174. package/package.json +1 -1
  175. package/packages/core/dist/atlas-edit.js +1 -1
  176. package/packages/core/dist/atlas-validate.js +99 -10
  177. package/packages/core/dist/draft-parse.js +190 -15
  178. package/packages/core/dist/draft.js +50 -11
  179. package/packages/core/dist/format.js +36 -5
  180. package/packages/core/dist/index.d.ts +1 -0
  181. package/packages/core/dist/index.js +1 -0
  182. package/packages/core/dist/load.js +9 -2
  183. package/packages/core/dist/scene.js +158 -24
  184. package/packages/core/dist/spatial-scene.d.ts +3 -0
  185. package/packages/core/dist/spatial-scene.js +420 -0
  186. package/packages/core/dist/types.d.ts +124 -2
  187. package/packages/editor/dist/editor.js +130 -8
  188. package/packages/editor/dist/types.d.ts +4 -0
  189. package/packages/markdown/dist/html.js +10 -3
  190. package/packages/viewer/dist/atlas-state.js +8 -2
  191. package/packages/viewer/dist/atlas-viewer.js +20 -8
  192. package/packages/viewer/dist/custom-element.js +18 -4
  193. package/packages/viewer/dist/embed.d.ts +5 -1
  194. package/packages/viewer/dist/embed.js +58 -24
  195. package/packages/viewer/dist/errors.d.ts +6 -0
  196. package/packages/viewer/dist/errors.js +12 -0
  197. package/packages/viewer/dist/index.d.ts +1 -0
  198. package/packages/viewer/dist/index.js +1 -0
  199. package/packages/viewer/dist/runtime-3d.d.ts +19 -0
  200. package/packages/viewer/dist/runtime-3d.js +494 -0
  201. package/packages/viewer/dist/types.d.ts +25 -3
  202. package/packages/viewer/dist/vendor/three.module.js +53032 -0
  203. package/packages/viewer/dist/viewer.js +517 -41
@@ -0,0 +1,420 @@
1
+ import { renderDocumentToScene } from "./scene.js";
2
+ const DAY_IN_SECONDS = 86_400;
3
+ const YEAR_IN_SECONDS = DAY_IN_SECONDS * 365.25;
4
+ const FASTEST_VISIBLE_ORBIT_SECONDS = 18;
5
+ const SLOWEST_VISIBLE_ORBIT_SECONDS = 180;
6
+ export function renderDocumentToSpatialScene(document, options = {}) {
7
+ const renderOptions = {
8
+ width: options.width,
9
+ height: options.height,
10
+ padding: options.padding,
11
+ preset: options.preset,
12
+ projection: options.projection,
13
+ camera: options.camera,
14
+ scaleModel: options.scaleModel,
15
+ activeEventId: options.activeEventId,
16
+ };
17
+ const scene = renderDocumentToScene(document, renderOptions);
18
+ const scaleModel = resolveSpatialScaleModel(scene.layoutPreset, options.spatialScaleModel);
19
+ const sceneCenter = {
20
+ x: scene.contentBounds.centerX,
21
+ y: scene.contentBounds.centerY,
22
+ };
23
+ const objectMap = new Map(scene.objects.map((object) => [object.objectId, object]));
24
+ const orbitMap = new Map(scene.orbitVisuals.map((orbit) => [orbit.objectId, orbit]));
25
+ const rawMotionMetrics = new Map();
26
+ for (const object of scene.objects) {
27
+ const placement = object.object.placement;
28
+ if (!placement || placement.mode !== "orbit") {
29
+ continue;
30
+ }
31
+ rawMotionMetrics.set(object.objectId, resolveRawOrbitMetric(placement));
32
+ }
33
+ const minimumMotionMetric = Math.min(...[...rawMotionMetrics.values()].filter((value) => Number.isFinite(value) && value > 0)) || 1;
34
+ const positionCache = new Map();
35
+ const spatialObjects = scene.objects.map((entry) => createSpatialObject(entry, scene, sceneCenter, objectMap, orbitMap, scaleModel, positionCache, minimumMotionMetric));
36
+ const spatialObjectMap = new Map(spatialObjects.map((object) => [object.objectId, object]));
37
+ const spatialOrbits = scene.orbitVisuals.map((orbit) => createSpatialOrbit(orbit, spatialObjectMap, minimumMotionMetric, scene.activeEventId !== null));
38
+ const focusTargets = spatialObjects.map((object) => ({
39
+ objectId: object.objectId,
40
+ center: { ...object.position },
41
+ radius: object.visualRadius + scaleModel.focusPadding,
42
+ }));
43
+ return {
44
+ width: scene.width,
45
+ height: scene.height,
46
+ padding: scene.padding,
47
+ renderPreset: scene.renderPreset,
48
+ projection: scene.projection,
49
+ camera: scene.camera,
50
+ scaleModel,
51
+ title: scene.title,
52
+ subtitle: scene.subtitle,
53
+ systemId: scene.systemId,
54
+ viewMode: "3d",
55
+ layoutPreset: scene.layoutPreset,
56
+ metadata: {
57
+ ...scene.metadata,
58
+ "viewer.mode": "3d",
59
+ },
60
+ contentBounds: calculateSpatialBounds(spatialObjects),
61
+ semanticGroups: scene.semanticGroups,
62
+ viewpoints: scene.viewpoints,
63
+ activeEventId: scene.activeEventId,
64
+ timeFrozen: scene.activeEventId !== null,
65
+ objects: spatialObjects,
66
+ orbits: spatialOrbits,
67
+ focusTargets,
68
+ };
69
+ }
70
+ function createSpatialObject(entry, scene, sceneCenter, objectMap, orbitMap, scaleModel, positionCache, minimumMotionMetric) {
71
+ const position = resolveSpatialObjectPosition(entry, scene, sceneCenter, objectMap, orbitMap, positionCache);
72
+ const motion = createMotionModel(entry.object, orbitMap.get(entry.objectId), minimumMotionMetric, scene.activeEventId !== null);
73
+ return {
74
+ objectId: entry.objectId,
75
+ object: entry.object,
76
+ parentId: entry.parentId,
77
+ ancestorIds: entry.ancestorIds.slice(),
78
+ childIds: entry.childIds.slice(),
79
+ groupId: entry.groupId,
80
+ semanticGroupIds: entry.semanticGroupIds.slice(),
81
+ position,
82
+ radius: clampNumber(entry.radius * scaleModel.bodyRadiusMultiplier, scaleModel.minBodyRadius, scaleModel.maxBodyRadius),
83
+ visualRadius: clampNumber(entry.visualRadius * scaleModel.bodyRadiusMultiplier, scaleModel.minBodyRadius, scaleModel.maxBodyRadius + 24),
84
+ label: entry.label,
85
+ secondaryLabel: entry.secondaryLabel,
86
+ fillColor: entry.fillColor,
87
+ imageHref: entry.imageHref,
88
+ hidden: entry.hidden,
89
+ motion,
90
+ };
91
+ }
92
+ function createSpatialOrbit(orbit, objectMap, minimumMotionMetric, frozen) {
93
+ const owner = objectMap.get(orbit.objectId);
94
+ const parent = objectMap.get(orbit.parentId);
95
+ return {
96
+ objectId: orbit.objectId,
97
+ object: orbit.object,
98
+ parentId: orbit.parentId,
99
+ groupId: orbit.groupId,
100
+ semanticGroupIds: orbit.semanticGroupIds.slice(),
101
+ center: parent?.position ?? { x: 0, y: 0, z: 0 },
102
+ kind: orbit.kind,
103
+ radius: orbit.radius,
104
+ semiMajor: orbit.radius ?? orbit.rx ?? 0,
105
+ semiMinor: orbit.radius ?? orbit.ry ?? 0,
106
+ rotationDeg: orbit.rotationDeg,
107
+ inclinationDeg: owner?.motion?.inclinationDeg ??
108
+ unitValueToDegrees(orbit.object.placement?.mode === "orbit"
109
+ ? orbit.object.placement.inclination
110
+ : undefined) ??
111
+ 0,
112
+ band: orbit.band,
113
+ bandThickness: orbit.bandThickness,
114
+ hidden: orbit.hidden,
115
+ motion: owner?.motion ??
116
+ createMotionModel(orbit.object, orbit, minimumMotionMetric, frozen),
117
+ };
118
+ }
119
+ function resolveSpatialObjectPosition(entry, scene, sceneCenter, objectMap, orbitMap, cache) {
120
+ const cached = cache.get(entry.objectId);
121
+ if (cached) {
122
+ return cached;
123
+ }
124
+ const placement = entry.object.placement;
125
+ let position;
126
+ if (placement?.mode === "orbit" && entry.parentId) {
127
+ const parent = objectMap.get(entry.parentId);
128
+ const parentPosition = parent
129
+ ? resolveSpatialObjectPosition(parent, scene, sceneCenter, objectMap, orbitMap, cache)
130
+ : { x: 0, y: 0, z: 0 };
131
+ const orbit = orbitMap.get(entry.objectId);
132
+ const motion = createMotionModel(entry.object, orbit, 1, scene.activeEventId !== null);
133
+ const local = motion
134
+ ? computeOrbitPosition(motion, 0)
135
+ : {
136
+ x: (entry.x - sceneCenter.x) * 0.8,
137
+ y: 0,
138
+ z: (entry.y - sceneCenter.y) * 0.8,
139
+ };
140
+ position = addPoint3D(parentPosition, local);
141
+ }
142
+ else if (placement?.mode === "surface" && entry.parentId) {
143
+ const parent = objectMap.get(entry.parentId);
144
+ const parentPosition = parent
145
+ ? resolveSpatialObjectPosition(parent, scene, sceneCenter, objectMap, orbitMap, cache)
146
+ : { x: 0, y: 0, z: 0 };
147
+ const parentRadius = parent?.visualRadius ?? 16;
148
+ const seed = hashUnit(entry.objectId);
149
+ const angle = seed * Math.PI * 2;
150
+ position = {
151
+ x: parentPosition.x + Math.cos(angle) * (parentRadius + entry.visualRadius * 0.9),
152
+ y: parentPosition.y + Math.sin(seed * Math.PI) * Math.max(entry.visualRadius * 0.2, 2),
153
+ z: parentPosition.z + Math.sin(angle) * (parentRadius + entry.visualRadius * 0.9),
154
+ };
155
+ }
156
+ else if (placement?.mode === "at" && entry.parentId) {
157
+ const parent = objectMap.get(entry.parentId);
158
+ const parentPosition = parent
159
+ ? resolveSpatialObjectPosition(parent, scene, sceneCenter, objectMap, orbitMap, cache)
160
+ : { x: 0, y: 0, z: 0 };
161
+ const anchorX = entry.anchorX ?? parent?.x ?? sceneCenter.x;
162
+ const anchorY = entry.anchorY ?? parent?.y ?? sceneCenter.y;
163
+ position = {
164
+ x: parentPosition.x + (entry.x - anchorX),
165
+ y: parentPosition.y,
166
+ z: parentPosition.z + (entry.y - anchorY),
167
+ };
168
+ }
169
+ else {
170
+ position = {
171
+ x: (entry.x - sceneCenter.x) * 0.8,
172
+ y: 0,
173
+ z: (entry.y - sceneCenter.y) * 0.8,
174
+ };
175
+ }
176
+ cache.set(entry.objectId, position);
177
+ return position;
178
+ }
179
+ export function evaluateSpatialSceneAtTime(scene, timeSeconds) {
180
+ const objectMap = new Map(scene.objects.map((object) => [object.objectId, object]));
181
+ const cache = new Map();
182
+ const resolvePosition = (objectId) => {
183
+ const cached = cache.get(objectId);
184
+ if (cached) {
185
+ return cached;
186
+ }
187
+ const object = objectMap.get(objectId);
188
+ if (!object) {
189
+ return { x: 0, y: 0, z: 0 };
190
+ }
191
+ let next = { ...object.position };
192
+ if (object.motion && object.parentId) {
193
+ const parentPosition = resolvePosition(object.parentId);
194
+ const local = computeOrbitPosition(object.motion, scene.timeFrozen ? 0 : timeSeconds);
195
+ next = addPoint3D(parentPosition, local);
196
+ }
197
+ cache.set(objectId, next);
198
+ return next;
199
+ };
200
+ for (const object of scene.objects) {
201
+ resolvePosition(object.objectId);
202
+ }
203
+ return cache;
204
+ }
205
+ function computeOrbitPosition(motion, timeSeconds) {
206
+ const angleDeg = motion.phase0Deg + motion.angularVelocityDegPerSecond * timeSeconds;
207
+ const angle = degreesToRadians(angleDeg);
208
+ const rotation = degreesToRadians(motion.rotationDeg);
209
+ const inclination = degreesToRadians(motion.inclinationDeg);
210
+ const localX = Math.cos(angle) * motion.semiMajor;
211
+ const localZ = Math.sin(angle) * motion.semiMinor;
212
+ const rotatedX = localX * Math.cos(rotation) - localZ * Math.sin(rotation);
213
+ const rotatedZ = localX * Math.sin(rotation) + localZ * Math.cos(rotation);
214
+ return {
215
+ x: rotatedX,
216
+ y: rotatedZ * Math.sin(inclination),
217
+ z: rotatedZ * Math.cos(inclination),
218
+ };
219
+ }
220
+ function createMotionModel(object, orbit, minimumMotionMetric, frozen) {
221
+ const placement = object.placement;
222
+ if (!placement || placement.mode !== "orbit") {
223
+ return null;
224
+ }
225
+ const semiMajor = orbit?.radius ??
226
+ orbit?.rx ??
227
+ clampNumber(resolveRawOrbitMetric(placement) * 48, 24, 1_200);
228
+ const semiMinor = orbit?.radius ?? orbit?.ry ?? semiMajor;
229
+ const periodSeconds = unitValueToDurationSeconds(placement.period);
230
+ const rawMetric = resolveRawOrbitMetric(placement);
231
+ const ratio = clampNumber(rawMetric / Math.max(minimumMotionMetric, 0.0001), 1, 20);
232
+ const visualDurationSeconds = periodSeconds
233
+ ? clampNumber(FASTEST_VISIBLE_ORBIT_SECONDS * ratio, FASTEST_VISIBLE_ORBIT_SECONDS, SLOWEST_VISIBLE_ORBIT_SECONDS)
234
+ : clampNumber(FASTEST_VISIBLE_ORBIT_SECONDS * Math.pow(ratio, 0.75), FASTEST_VISIBLE_ORBIT_SECONDS, SLOWEST_VISIBLE_ORBIT_SECONDS);
235
+ return {
236
+ phase0Deg: unitValueToDegrees(placement.phase) ?? 0,
237
+ rotationDeg: unitValueToDegrees(placement.angle) ??
238
+ orbit?.rotationDeg ??
239
+ 0,
240
+ inclinationDeg: unitValueToDegrees(placement.inclination) ?? 0,
241
+ semiMajor,
242
+ semiMinor,
243
+ eccentricity: placement.eccentricity ?? 0,
244
+ periodSeconds,
245
+ angularVelocityDegPerSecond: 360 / Math.max(visualDurationSeconds, 0.001),
246
+ heuristic: periodSeconds === null,
247
+ frozen,
248
+ };
249
+ }
250
+ function resolveRawOrbitMetric(placement) {
251
+ const distance = unitValueToDistanceMetric(placement.semiMajor) ??
252
+ unitValueToDistanceMetric(placement.distance) ??
253
+ 1;
254
+ return Math.max(distance, 0.01);
255
+ }
256
+ function unitValueToDistanceMetric(value) {
257
+ if (!value) {
258
+ return null;
259
+ }
260
+ switch (value.unit) {
261
+ case "au":
262
+ return value.value;
263
+ case "km":
264
+ return value.value / 149_597_870.7;
265
+ case "m":
266
+ return value.value / 149_597_870_700;
267
+ case "re":
268
+ return (value.value * 6_371) / 149_597_870.7;
269
+ case "rj":
270
+ return (value.value * 71_492) / 149_597_870.7;
271
+ case "sol":
272
+ return (value.value * 695_700) / 149_597_870.7;
273
+ case "ly":
274
+ return value.value * 63_241.077;
275
+ case "pc":
276
+ return value.value * 206_264.806;
277
+ case "kpc":
278
+ return value.value * 206_264_806;
279
+ default:
280
+ return value.value;
281
+ }
282
+ }
283
+ function unitValueToDurationSeconds(value) {
284
+ if (!value) {
285
+ return null;
286
+ }
287
+ switch (value.unit) {
288
+ case "s":
289
+ return value.value;
290
+ case "min":
291
+ return value.value * 60;
292
+ case "h":
293
+ return value.value * 3_600;
294
+ case "d":
295
+ return value.value * DAY_IN_SECONDS;
296
+ case "y":
297
+ return value.value * YEAR_IN_SECONDS;
298
+ case "ky":
299
+ return value.value * YEAR_IN_SECONDS * 1_000;
300
+ case "my":
301
+ return value.value * YEAR_IN_SECONDS * 1_000_000;
302
+ case "gy":
303
+ return value.value * YEAR_IN_SECONDS * 1_000_000_000;
304
+ default:
305
+ return null;
306
+ }
307
+ }
308
+ function unitValueToDegrees(value) {
309
+ if (!value) {
310
+ return null;
311
+ }
312
+ return value.unit === "deg" || value.unit === null ? value.value : null;
313
+ }
314
+ function resolveSpatialScaleModel(layoutPreset, overrides) {
315
+ const defaults = defaultSpatialScaleModel(layoutPreset);
316
+ return {
317
+ ...defaults,
318
+ ...overrides,
319
+ };
320
+ }
321
+ function defaultSpatialScaleModel(layoutPreset) {
322
+ switch (layoutPreset) {
323
+ case "compact":
324
+ return {
325
+ orbitDistanceMultiplier: 0.92,
326
+ bodyRadiusMultiplier: 0.92,
327
+ markerSizeMultiplier: 0.92,
328
+ ringThicknessMultiplier: 0.9,
329
+ focusPadding: 10,
330
+ minBodyRadius: 4,
331
+ maxBodyRadius: 34,
332
+ };
333
+ case "presentation":
334
+ return {
335
+ orbitDistanceMultiplier: 1.15,
336
+ bodyRadiusMultiplier: 1.12,
337
+ markerSizeMultiplier: 1.08,
338
+ ringThicknessMultiplier: 1.14,
339
+ focusPadding: 16,
340
+ minBodyRadius: 5,
341
+ maxBodyRadius: 44,
342
+ };
343
+ default:
344
+ return {
345
+ orbitDistanceMultiplier: 1,
346
+ bodyRadiusMultiplier: 1,
347
+ markerSizeMultiplier: 1,
348
+ ringThicknessMultiplier: 1,
349
+ focusPadding: 12,
350
+ minBodyRadius: 4,
351
+ maxBodyRadius: 40,
352
+ };
353
+ }
354
+ }
355
+ function calculateSpatialBounds(objects) {
356
+ if (objects.length === 0) {
357
+ return {
358
+ minX: 0,
359
+ minY: 0,
360
+ minZ: 0,
361
+ maxX: 0,
362
+ maxY: 0,
363
+ maxZ: 0,
364
+ width: 0,
365
+ height: 0,
366
+ depth: 0,
367
+ center: { x: 0, y: 0, z: 0 },
368
+ };
369
+ }
370
+ let minX = Number.POSITIVE_INFINITY;
371
+ let minY = Number.POSITIVE_INFINITY;
372
+ let minZ = Number.POSITIVE_INFINITY;
373
+ let maxX = Number.NEGATIVE_INFINITY;
374
+ let maxY = Number.NEGATIVE_INFINITY;
375
+ let maxZ = Number.NEGATIVE_INFINITY;
376
+ for (const object of objects) {
377
+ minX = Math.min(minX, object.position.x - object.visualRadius);
378
+ minY = Math.min(minY, object.position.y - object.visualRadius);
379
+ minZ = Math.min(minZ, object.position.z - object.visualRadius);
380
+ maxX = Math.max(maxX, object.position.x + object.visualRadius);
381
+ maxY = Math.max(maxY, object.position.y + object.visualRadius);
382
+ maxZ = Math.max(maxZ, object.position.z + object.visualRadius);
383
+ }
384
+ return {
385
+ minX,
386
+ minY,
387
+ minZ,
388
+ maxX,
389
+ maxY,
390
+ maxZ,
391
+ width: maxX - minX,
392
+ height: maxY - minY,
393
+ depth: maxZ - minZ,
394
+ center: {
395
+ x: (minX + maxX) / 2,
396
+ y: (minY + maxY) / 2,
397
+ z: (minZ + maxZ) / 2,
398
+ },
399
+ };
400
+ }
401
+ function addPoint3D(left, right) {
402
+ return {
403
+ x: left.x + right.x,
404
+ y: left.y + right.y,
405
+ z: left.z + right.z,
406
+ };
407
+ }
408
+ function hashUnit(value) {
409
+ let hash = 0;
410
+ for (let index = 0; index < value.length; index += 1) {
411
+ hash = (hash * 31 + value.charCodeAt(index)) >>> 0;
412
+ }
413
+ return (hash % 10_000) / 10_000;
414
+ }
415
+ function clampNumber(value, min, max) {
416
+ return Math.min(Math.max(value, min), max);
417
+ }
418
+ function degreesToRadians(value) {
419
+ return (value * Math.PI) / 180;
420
+ }
@@ -2,15 +2,21 @@ export type WorldOrbitObjectType = "system" | "star" | "planet" | "moon" | "belt
2
2
  export type PlacementMode = "orbit" | "at" | "surface" | "free";
3
3
  export type Unit = "au" | "km" | "m" | "ly" | "pc" | "kpc" | "re" | "rj" | "sol" | "me" | "mj" | "s" | "min" | "h" | "d" | "y" | "ky" | "my" | "gy" | "K" | "deg";
4
4
  export type WorldOrbitDocumentVersion = "1.0";
5
- export type WorldOrbitAtlasDocumentVersion = "2.0" | "2.1";
5
+ export type WorldOrbitAtlasDocumentVersion = "2.0" | "2.1" | "2.5";
6
6
  export type WorldOrbitDraftDocumentVersion = "2.0-draft";
7
7
  export type WorldOrbitAnyDocumentVersion = WorldOrbitDocumentVersion | WorldOrbitAtlasDocumentVersion | WorldOrbitDraftDocumentVersion;
8
- export type ViewProjection = "topdown" | "isometric";
8
+ export type ViewProjection = "topdown" | "isometric" | "orthographic" | "perspective";
9
+ export type RenderProjectionFallback = "topdown" | "isometric";
9
10
  export type RenderPresetName = "diagram" | "presentation" | "atlas-card" | "markdown";
10
11
  export interface CoordinatePoint {
11
12
  x: number;
12
13
  y: number;
13
14
  }
15
+ export interface CoordinatePoint3D {
16
+ x: number;
17
+ y: number;
18
+ z: number;
19
+ }
14
20
  export interface UnitValue {
15
21
  value: number;
16
22
  unit: Unit | null;
@@ -144,6 +150,8 @@ export interface WorldOrbitEvent {
144
150
  participantObjectIds: string[];
145
151
  timing: string | null;
146
152
  visibility: string | null;
153
+ epoch?: string | null;
154
+ referencePlane?: string | null;
147
155
  tags: string[];
148
156
  color: string | null;
149
157
  hidden: boolean;
@@ -154,6 +162,8 @@ export interface WorldOrbitEventPose {
154
162
  placement: Placement | null;
155
163
  inner?: UnitValue;
156
164
  outer?: UnitValue;
165
+ epoch?: string | null;
166
+ referencePlane?: string | null;
157
167
  }
158
168
  export interface WorldOrbitResonance {
159
169
  targetObjectId: string;
@@ -175,6 +185,12 @@ export interface WorldOrbitToleranceRule {
175
185
  field: string;
176
186
  value: NormalizedValue;
177
187
  }
188
+ export interface WorldOrbitViewCamera {
189
+ azimuth: number | null;
190
+ elevation: number | null;
191
+ roll: number | null;
192
+ distance: number | null;
193
+ }
178
194
  export type NormalizedValue = string | number | boolean | string[] | UnitValue;
179
195
  export type Placement = OrbitPlacement | AtPlacement | SurfacePlacement | FreePlacement;
180
196
  export interface OrbitPlacement {
@@ -234,9 +250,22 @@ export interface SceneRenderOptions {
234
250
  padding?: number;
235
251
  preset?: RenderPresetName;
236
252
  projection?: "document" | ViewProjection;
253
+ camera?: WorldOrbitViewCamera | null;
237
254
  scaleModel?: Partial<RenderScaleModel>;
238
255
  activeEventId?: string | null;
239
256
  }
257
+ export interface SpatialScaleModel {
258
+ orbitDistanceMultiplier: number;
259
+ bodyRadiusMultiplier: number;
260
+ markerSizeMultiplier: number;
261
+ ringThicknessMultiplier: number;
262
+ focusPadding: number;
263
+ minBodyRadius: number;
264
+ maxBodyRadius: number;
265
+ }
266
+ export interface SpatialSceneRenderOptions extends SceneRenderOptions {
267
+ spatialScaleModel?: Partial<SpatialScaleModel>;
268
+ }
240
269
  export interface RenderBounds {
241
270
  minX: number;
242
271
  minY: number;
@@ -344,8 +373,10 @@ export interface RenderSceneViewpoint {
344
373
  selectedObjectId: string | null;
345
374
  eventIds: string[];
346
375
  projection: ViewProjection;
376
+ renderProjection: RenderProjectionFallback;
347
377
  preset: RenderPresetName | null;
348
378
  rotationDeg: number;
379
+ camera: WorldOrbitViewCamera | null;
349
380
  scale: number | null;
350
381
  layers: Partial<Record<SceneLayerId, boolean>>;
351
382
  filter: RenderSceneViewpointFilter | null;
@@ -386,12 +417,79 @@ export interface RenderSceneRelation {
386
417
  y2: number;
387
418
  hidden: boolean;
388
419
  }
420
+ export interface SpatialBounds {
421
+ minX: number;
422
+ minY: number;
423
+ minZ: number;
424
+ maxX: number;
425
+ maxY: number;
426
+ maxZ: number;
427
+ width: number;
428
+ height: number;
429
+ depth: number;
430
+ center: CoordinatePoint3D;
431
+ }
432
+ export interface OrbitalMotionModel {
433
+ phase0Deg: number;
434
+ rotationDeg: number;
435
+ inclinationDeg: number;
436
+ semiMajor: number;
437
+ semiMinor: number;
438
+ eccentricity: number;
439
+ periodSeconds: number | null;
440
+ angularVelocityDegPerSecond: number;
441
+ heuristic: boolean;
442
+ frozen: boolean;
443
+ }
444
+ export interface SpatialSceneObject {
445
+ objectId: string;
446
+ object: WorldOrbitObject;
447
+ parentId: string | null;
448
+ ancestorIds: string[];
449
+ childIds: string[];
450
+ groupId: string | null;
451
+ semanticGroupIds: string[];
452
+ position: CoordinatePoint3D;
453
+ radius: number;
454
+ visualRadius: number;
455
+ label: string;
456
+ secondaryLabel: string;
457
+ fillColor?: string;
458
+ imageHref?: string;
459
+ hidden: boolean;
460
+ motion: OrbitalMotionModel | null;
461
+ }
462
+ export interface SpatialOrbit {
463
+ objectId: string;
464
+ object: WorldOrbitObject;
465
+ parentId: string;
466
+ groupId: string | null;
467
+ semanticGroupIds: string[];
468
+ center: CoordinatePoint3D;
469
+ kind: "circle" | "ellipse";
470
+ radius?: number;
471
+ semiMajor: number;
472
+ semiMinor: number;
473
+ rotationDeg: number;
474
+ inclinationDeg: number;
475
+ band: boolean;
476
+ bandThickness?: number;
477
+ hidden: boolean;
478
+ motion: OrbitalMotionModel | null;
479
+ }
480
+ export interface SpatialFocusTarget {
481
+ objectId: string;
482
+ center: CoordinatePoint3D;
483
+ radius: number;
484
+ }
389
485
  export interface RenderScene {
390
486
  width: number;
391
487
  height: number;
392
488
  padding: number;
393
489
  renderPreset: RenderPresetName | null;
394
490
  projection: ViewProjection;
491
+ renderProjection: RenderProjectionFallback;
492
+ camera: WorldOrbitViewCamera | null;
395
493
  scaleModel: RenderScaleModel;
396
494
  title: string;
397
495
  subtitle: string;
@@ -412,6 +510,29 @@ export interface RenderScene {
412
510
  leaders: RenderLeaderLine[];
413
511
  labels: RenderSceneLabel[];
414
512
  }
513
+ export interface SpatialScene {
514
+ width: number;
515
+ height: number;
516
+ padding: number;
517
+ renderPreset: RenderPresetName | null;
518
+ projection: ViewProjection;
519
+ camera: WorldOrbitViewCamera | null;
520
+ scaleModel: SpatialScaleModel;
521
+ title: string;
522
+ subtitle: string;
523
+ systemId: string | null;
524
+ viewMode: "3d";
525
+ layoutPreset: SceneLayoutPreset;
526
+ metadata: Record<string, string>;
527
+ contentBounds: SpatialBounds;
528
+ semanticGroups: RenderSceneSemanticGroup[];
529
+ viewpoints: RenderSceneViewpoint[];
530
+ activeEventId: string | null;
531
+ timeFrozen: boolean;
532
+ objects: SpatialSceneObject[];
533
+ orbits: SpatialOrbit[];
534
+ focusTargets: SpatialFocusTarget[];
535
+ }
415
536
  export type SceneLayoutPreset = "compact" | "balanced" | "presentation";
416
537
  export type FieldValueKind = "string" | "list" | "boolean" | "number" | "unit";
417
538
  export type UnitFamily = "distance" | "radius" | "mass" | "duration" | "angle" | "generic";
@@ -464,6 +585,7 @@ export interface WorldOrbitAtlasViewpoint {
464
585
  preset: RenderPresetName | null;
465
586
  zoom: number | null;
466
587
  rotationDeg: number;
588
+ camera: WorldOrbitViewCamera | null;
467
589
  layers: Partial<Record<SceneLayerId, boolean>>;
468
590
  filter: RenderSceneViewpointFilter | null;
469
591
  }