worldorbit 2.5.16 → 2.5.17

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 (34) hide show
  1. package/README.md +1 -1
  2. package/dist/browser/core/dist/index.js +750 -73
  3. package/dist/browser/editor/dist/index.js +1303 -135
  4. package/dist/browser/markdown/dist/index.js +631 -72
  5. package/dist/browser/viewer/dist/index.js +658 -77
  6. package/dist/unpkg/core/dist/index.js +750 -73
  7. package/dist/unpkg/editor/dist/index.js +1303 -135
  8. package/dist/unpkg/markdown/dist/index.js +631 -72
  9. package/dist/unpkg/viewer/dist/index.js +658 -77
  10. package/dist/unpkg/worldorbit-core.min.js +12 -12
  11. package/dist/unpkg/worldorbit-editor.min.js +284 -202
  12. package/dist/unpkg/worldorbit-markdown.min.js +66 -58
  13. package/dist/unpkg/worldorbit-viewer.min.js +76 -68
  14. package/dist/unpkg/worldorbit.js +797 -78
  15. package/dist/unpkg/worldorbit.min.js +80 -72
  16. package/package.json +1 -1
  17. package/packages/core/dist/atlas-edit.js +74 -0
  18. package/packages/core/dist/atlas-validate.js +122 -8
  19. package/packages/core/dist/draft-parse.js +212 -8
  20. package/packages/core/dist/draft.d.ts +5 -2
  21. package/packages/core/dist/draft.js +59 -3
  22. package/packages/core/dist/format.js +63 -1
  23. package/packages/core/dist/normalize.js +1 -0
  24. package/packages/core/dist/scene.js +248 -46
  25. package/packages/core/dist/types.d.ts +41 -2
  26. package/packages/editor/dist/editor.js +597 -61
  27. package/packages/editor/dist/types.d.ts +3 -1
  28. package/packages/viewer/dist/atlas-state.js +6 -0
  29. package/packages/viewer/dist/atlas-viewer.js +1 -0
  30. package/packages/viewer/dist/render.js +31 -2
  31. package/packages/viewer/dist/theme.js +1 -0
  32. package/packages/viewer/dist/tooltip.js +9 -0
  33. package/packages/viewer/dist/types.d.ts +8 -1
  34. package/packages/viewer/dist/viewer.js +12 -1
@@ -5,6 +5,7 @@
5
5
  background: true,
6
6
  guides: true,
7
7
  relations: true,
8
+ events: true,
8
9
  orbits: true,
9
10
  objects: true,
10
11
  labels: true,
@@ -156,12 +157,14 @@
156
157
  return {
157
158
  version: "2.0",
158
159
  viewpointId,
160
+ activeEventId: renderOptions.activeEventId ?? null,
159
161
  viewerState: { ...viewerState },
160
162
  renderOptions: {
161
163
  preset: renderOptions.preset,
162
164
  projection: renderOptions.projection,
163
165
  layers: renderOptions.layers ? { ...renderOptions.layers } : void 0,
164
- scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : void 0
166
+ scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : void 0,
167
+ activeEventId: renderOptions.activeEventId ?? null
165
168
  },
166
169
  filter: normalizeViewerFilter(filter)
167
170
  };
@@ -174,6 +177,7 @@
174
177
  return {
175
178
  version: "2.0",
176
179
  viewpointId: raw.viewpointId ?? null,
180
+ activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null,
177
181
  viewerState: {
178
182
  scale: raw.viewerState?.scale ?? 1,
179
183
  rotationDeg: raw.viewerState?.rotationDeg ?? 0,
@@ -185,7 +189,8 @@
185
189
  preset: raw.renderOptions?.preset,
186
190
  projection: raw.renderOptions?.projection,
187
191
  layers: raw.renderOptions?.layers ? { ...raw.renderOptions.layers } : void 0,
188
- scaleModel: raw.renderOptions?.scaleModel ? { ...raw.renderOptions.scaleModel } : void 0
192
+ scaleModel: raw.renderOptions?.scaleModel ? { ...raw.renderOptions.scaleModel } : void 0,
193
+ activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null
189
194
  },
190
195
  filter: normalizeViewerFilter(raw.filter ?? null)
191
196
  };
@@ -201,7 +206,8 @@
201
206
  renderOptions: {
202
207
  ...atlasState.renderOptions,
203
208
  layers: atlasState.renderOptions.layers ? { ...atlasState.renderOptions.layers } : void 0,
204
- scaleModel: atlasState.renderOptions.scaleModel ? { ...atlasState.renderOptions.scaleModel } : void 0
209
+ scaleModel: atlasState.renderOptions.scaleModel ? { ...atlasState.renderOptions.scaleModel } : void 0,
210
+ activeEventId: atlasState.renderOptions.activeEventId ?? null
205
211
  },
206
212
  filter: atlasState.filter ? { ...atlasState.filter } : null
207
213
  }
@@ -219,6 +225,7 @@
219
225
  background: viewpoint.layers.background,
220
226
  guides: viewpoint.layers.guides,
221
227
  relations: viewpoint.layers.relations,
228
+ events: viewpoint.layers.events,
222
229
  orbits: viewpoint.layers["orbits-front"] === void 0 && viewpoint.layers["orbits-back"] === void 0 ? void 0 : viewpoint.layers["orbits-front"] !== false || viewpoint.layers["orbits-back"] !== false,
223
230
  objects: viewpoint.layers.objects,
224
231
  labels: viewpoint.layers.labels,
@@ -866,6 +873,7 @@
866
873
  system,
867
874
  groups: [],
868
875
  relations: [],
876
+ events: [],
869
877
  objects
870
878
  };
871
879
  }
@@ -1249,8 +1257,10 @@
1249
1257
  const scaleModel = resolveScaleModel(layoutPreset, options.scaleModel);
1250
1258
  const spacingFactor = layoutPresetSpacing(layoutPreset);
1251
1259
  const systemId = document2.system?.id ?? null;
1252
- const objectMap = new Map(document2.objects.map((object) => [object.id, object]));
1253
- const relationships = buildSceneRelationships(document2.objects, objectMap);
1260
+ const activeEventId = options.activeEventId ?? null;
1261
+ const effectiveObjects = createEffectiveObjects(document2.objects, document2.events ?? [], activeEventId);
1262
+ const objectMap = new Map(effectiveObjects.map((object) => [object.id, object]));
1263
+ const relationships = buildSceneRelationships(effectiveObjects, objectMap);
1254
1264
  const positions = /* @__PURE__ */ new Map();
1255
1265
  const orbitDrafts = [];
1256
1266
  const leaderDrafts = [];
@@ -1259,7 +1269,7 @@
1259
1269
  const atObjects = [];
1260
1270
  const surfaceChildren = /* @__PURE__ */ new Map();
1261
1271
  const orbitChildren = /* @__PURE__ */ new Map();
1262
- for (const object of document2.objects) {
1272
+ for (const object of effectiveObjects) {
1263
1273
  const placement = object.placement;
1264
1274
  if (!placement) {
1265
1275
  rootObjects.push(object);
@@ -1354,13 +1364,14 @@
1354
1364
  const objects = [...positions.values()].map((position) => createSceneObject(position, scaleModel, relationships));
1355
1365
  const orbitVisuals = orbitDrafts.map((draft) => createOrbitVisual(draft, relationships.groupIds.get(draft.object.id) ?? null));
1356
1366
  const leaders = leaderDrafts.map((draft) => createLeaderLine(draft));
1357
- const labels = createSceneLabels(objects, height, scaleModel.labelMultiplier);
1367
+ const labels = createSceneLabels(objects, width, height, scaleModel.labelMultiplier);
1358
1368
  const relations = createSceneRelations(document2, objects);
1359
- const layers = createSceneLayers(orbitVisuals, relations, leaders, objects, labels);
1360
- const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships);
1369
+ const events = createSceneEvents(document2.events ?? [], objects, activeEventId);
1370
+ const layers = createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels);
1371
+ const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, scaleModel.labelMultiplier);
1361
1372
  const semanticGroups = createSceneSemanticGroups(document2, objects);
1362
1373
  const viewpoints = createSceneViewpoints(document2, projection, frame.preset, relationships, objectMap);
1363
- const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels);
1374
+ const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, scaleModel.labelMultiplier);
1364
1375
  return {
1365
1376
  width,
1366
1377
  height,
@@ -1386,6 +1397,8 @@
1386
1397
  groups,
1387
1398
  semanticGroups,
1388
1399
  viewpoints,
1400
+ events,
1401
+ activeEventId,
1389
1402
  objects,
1390
1403
  orbitVisuals,
1391
1404
  relations,
@@ -1404,6 +1417,35 @@
1404
1417
  y: center.y + dx * sin + dy * cos
1405
1418
  };
1406
1419
  }
1420
+ function createEffectiveObjects(objects, events, activeEventId) {
1421
+ const cloned = objects.map((object) => structuredClone(object));
1422
+ if (!activeEventId) {
1423
+ return cloned;
1424
+ }
1425
+ const activeEvent = events.find((event) => event.id === activeEventId);
1426
+ if (!activeEvent) {
1427
+ return cloned;
1428
+ }
1429
+ const objectMap = new Map(cloned.map((object) => [object.id, object]));
1430
+ for (const pose of activeEvent.positions) {
1431
+ const object = objectMap.get(pose.objectId);
1432
+ if (!object) {
1433
+ continue;
1434
+ }
1435
+ object.placement = pose.placement ? structuredClone(pose.placement) : null;
1436
+ if (pose.inner) {
1437
+ object.properties.inner = { ...pose.inner };
1438
+ } else {
1439
+ delete object.properties.inner;
1440
+ }
1441
+ if (pose.outer) {
1442
+ object.properties.outer = { ...pose.outer };
1443
+ } else {
1444
+ delete object.properties.outer;
1445
+ }
1446
+ }
1447
+ return cloned;
1448
+ }
1407
1449
  function resolveLayoutPreset(document2) {
1408
1450
  const rawScale = String(document2.system?.properties.scale ?? "balanced").toLowerCase();
1409
1451
  switch (rawScale) {
@@ -1559,24 +1601,14 @@
1559
1601
  hidden: draft.object.properties.hidden === true
1560
1602
  };
1561
1603
  }
1562
- function createSceneLabels(objects, sceneHeight, labelMultiplier) {
1604
+ function createSceneLabels(objects, sceneWidth, sceneHeight, labelMultiplier) {
1563
1605
  const labels = [];
1564
1606
  const occupied = [];
1565
- const visibleObjects = [...objects].filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false).sort((left, right) => left.sortKey - right.sortKey);
1607
+ const objectMap = new Map(objects.map((object) => [object.objectId, object]));
1608
+ const visibleObjects = [...objects].filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false).sort(compareLabelPlacementOrder);
1566
1609
  for (const object of visibleObjects) {
1567
- const direction = object.y > sceneHeight * 0.62 ? -1 : 1;
1568
- const labelHalfWidth = estimateLabelHalfWidth(object, labelMultiplier);
1569
- let labelY = object.y + direction * (object.radius + 18 * labelMultiplier);
1570
- let secondaryY = labelY + direction * (16 * labelMultiplier);
1571
- let bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
1572
- let attempts = 0;
1573
- while (occupied.some((entry) => rectsOverlap(entry, bounds)) && attempts < 10) {
1574
- labelY += direction * 14 * labelMultiplier;
1575
- secondaryY += direction * 14 * labelMultiplier;
1576
- bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
1577
- attempts += 1;
1578
- }
1579
- occupied.push(bounds);
1610
+ const placement = selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) ?? createLabelPlacement(object, defaultVerticalDirection(object, objectMap.get(object.parentId ?? "") ?? null, sceneHeight), 0, labelMultiplier);
1611
+ occupied.push(createLabelRect(object, placement, labelMultiplier));
1580
1612
  labels.push({
1581
1613
  renderId: `${object.renderId}-label`,
1582
1614
  objectId: object.objectId,
@@ -1585,17 +1617,128 @@
1585
1617
  semanticGroupIds: [...object.semanticGroupIds],
1586
1618
  label: object.label,
1587
1619
  secondaryLabel: object.secondaryLabel,
1588
- x: object.x,
1589
- y: labelY,
1590
- secondaryY,
1591
- textAnchor: "middle",
1592
- direction: direction < 0 ? "above" : "below",
1620
+ x: placement.x,
1621
+ y: placement.labelY,
1622
+ secondaryY: placement.secondaryY,
1623
+ textAnchor: placement.textAnchor,
1624
+ direction: placement.direction,
1593
1625
  hidden: object.hidden
1594
1626
  });
1595
1627
  }
1596
1628
  return labels;
1597
1629
  }
1598
- function createSceneLayers(orbitVisuals, relations, leaders, objects, labels) {
1630
+ function compareLabelPlacementOrder(left, right) {
1631
+ const priorityDiff = labelPlacementPriority(left) - labelPlacementPriority(right);
1632
+ if (priorityDiff !== 0) {
1633
+ return priorityDiff;
1634
+ }
1635
+ const renderPriorityDiff = (right.object.renderHints?.renderPriority ?? 0) - (left.object.renderHints?.renderPriority ?? 0);
1636
+ if (renderPriorityDiff !== 0) {
1637
+ return renderPriorityDiff;
1638
+ }
1639
+ return left.sortKey - right.sortKey;
1640
+ }
1641
+ function labelPlacementPriority(object) {
1642
+ switch (object.object.type) {
1643
+ case "star":
1644
+ return 0;
1645
+ case "planet":
1646
+ return 1;
1647
+ case "moon":
1648
+ return 2;
1649
+ case "belt":
1650
+ case "ring":
1651
+ return 3;
1652
+ case "asteroid":
1653
+ case "comet":
1654
+ return 4;
1655
+ case "structure":
1656
+ case "phenomenon":
1657
+ return 5;
1658
+ }
1659
+ }
1660
+ function selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) {
1661
+ for (const direction of preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight)) {
1662
+ const maxAttempts = direction === "left" || direction === "right" ? 4 : 6;
1663
+ for (let attempt = 0; attempt <= maxAttempts; attempt += 1) {
1664
+ const placement = createLabelPlacement(object, direction, attempt, labelMultiplier);
1665
+ const rect = createLabelRect(object, placement, labelMultiplier);
1666
+ if (!occupied.some((entry) => rectsOverlap(entry, rect))) {
1667
+ return placement;
1668
+ }
1669
+ }
1670
+ }
1671
+ return null;
1672
+ }
1673
+ function preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight) {
1674
+ const parent = object.parentId ? objectMap.get(object.parentId) ?? null : null;
1675
+ const vertical = defaultVerticalDirection(object, parent, sceneHeight);
1676
+ const oppositeVertical = vertical === "below" ? "above" : "below";
1677
+ const horizontal = defaultHorizontalDirection(object, parent, sceneWidth);
1678
+ const oppositeHorizontal = horizontal === "right" ? "left" : "right";
1679
+ const preferHorizontal = object.object.type === "structure" || object.object.type === "phenomenon" || object.object.placement?.mode === "at" || object.object.placement?.mode === "surface" || object.object.placement?.mode === "free";
1680
+ return preferHorizontal ? [horizontal, vertical, oppositeHorizontal, oppositeVertical] : [vertical, horizontal, oppositeVertical, oppositeHorizontal];
1681
+ }
1682
+ function defaultVerticalDirection(object, parent, sceneHeight) {
1683
+ if (parent && Math.abs(object.y - parent.y) > 6) {
1684
+ return object.y >= parent.y ? "below" : "above";
1685
+ }
1686
+ return object.y > sceneHeight * 0.62 ? "above" : "below";
1687
+ }
1688
+ function defaultHorizontalDirection(object, parent, sceneWidth) {
1689
+ if (parent && Math.abs(object.x - parent.x) > 6) {
1690
+ return object.x >= parent.x ? "right" : "left";
1691
+ }
1692
+ return object.x >= sceneWidth / 2 ? "right" : "left";
1693
+ }
1694
+ function createLabelPlacement(object, direction, attempt, labelMultiplier) {
1695
+ const step = 14 * labelMultiplier;
1696
+ switch (direction) {
1697
+ case "above": {
1698
+ const labelY = object.y - (object.radius + 18 * labelMultiplier + attempt * step);
1699
+ return {
1700
+ x: object.x,
1701
+ labelY,
1702
+ secondaryY: labelY - 16 * labelMultiplier,
1703
+ textAnchor: "middle",
1704
+ direction
1705
+ };
1706
+ }
1707
+ case "below": {
1708
+ const labelY = object.y + object.radius + 18 * labelMultiplier + attempt * step;
1709
+ return {
1710
+ x: object.x,
1711
+ labelY,
1712
+ secondaryY: labelY + 16 * labelMultiplier,
1713
+ textAnchor: "middle",
1714
+ direction
1715
+ };
1716
+ }
1717
+ case "left": {
1718
+ const x = object.x - (object.visualRadius + 16 * labelMultiplier + attempt * step);
1719
+ const labelY = object.y - 4 * labelMultiplier;
1720
+ return {
1721
+ x,
1722
+ labelY,
1723
+ secondaryY: labelY + 16 * labelMultiplier,
1724
+ textAnchor: "end",
1725
+ direction
1726
+ };
1727
+ }
1728
+ case "right": {
1729
+ const x = object.x + object.visualRadius + 16 * labelMultiplier + attempt * step;
1730
+ const labelY = object.y - 4 * labelMultiplier;
1731
+ return {
1732
+ x,
1733
+ labelY,
1734
+ secondaryY: labelY + 16 * labelMultiplier,
1735
+ textAnchor: "start",
1736
+ direction
1737
+ };
1738
+ }
1739
+ }
1740
+ }
1741
+ function createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels) {
1599
1742
  const backOrbitIds = orbitVisuals.filter((visual) => !visual.hidden && Boolean(visual.backArcPath)).map((visual) => visual.renderId);
1600
1743
  const frontOrbitIds = orbitVisuals.filter((visual) => !visual.hidden).map((visual) => visual.renderId);
1601
1744
  return [
@@ -1610,6 +1753,10 @@
1610
1753
  id: "relations",
1611
1754
  renderIds: relations.filter((relation) => !relation.hidden).map((relation) => relation.renderId)
1612
1755
  },
1756
+ {
1757
+ id: "events",
1758
+ renderIds: events.filter((event) => !event.hidden).map((event) => event.renderId)
1759
+ },
1613
1760
  {
1614
1761
  id: "objects",
1615
1762
  renderIds: objects.filter((object) => !object.hidden).map((object) => object.renderId)
@@ -1621,7 +1768,7 @@
1621
1768
  { id: "metadata", renderIds: ["wo-title", "wo-subtitle", "wo-meta"] }
1622
1769
  ];
1623
1770
  }
1624
- function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships) {
1771
+ function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, labelMultiplier) {
1625
1772
  const groups = /* @__PURE__ */ new Map();
1626
1773
  const ensureGroup = (groupId) => {
1627
1774
  if (!groupId) {
@@ -1670,7 +1817,7 @@
1670
1817
  }
1671
1818
  }
1672
1819
  for (const group of groups.values()) {
1673
- group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels);
1820
+ group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier);
1674
1821
  }
1675
1822
  return [...groups.values()].sort((left, right) => left.label.localeCompare(right.label));
1676
1823
  }
@@ -1704,6 +1851,29 @@
1704
1851
  };
1705
1852
  }).sort((left, right) => left.relation.id.localeCompare(right.relation.id));
1706
1853
  }
1854
+ function createSceneEvents(events, objects, activeEventId) {
1855
+ const objectMap = new Map(objects.map((object) => [object.objectId, object]));
1856
+ return events.map((event) => {
1857
+ const objectIds = [.../* @__PURE__ */ new Set([
1858
+ ...event.targetObjectId ? [event.targetObjectId] : [],
1859
+ ...event.participantObjectIds
1860
+ ])];
1861
+ const positions = objectIds.map((objectId) => objectMap.get(objectId)).filter(Boolean);
1862
+ const centroidX = positions.length > 0 ? positions.reduce((sum, object) => sum + object.x, 0) / positions.length : 0;
1863
+ const centroidY = positions.length > 0 ? positions.reduce((sum, object) => sum + object.y, 0) / positions.length : 0;
1864
+ return {
1865
+ renderId: `${createRenderId(event.id)}-event`,
1866
+ eventId: event.id,
1867
+ event,
1868
+ objectIds,
1869
+ participantIds: [...event.participantObjectIds],
1870
+ targetObjectId: event.targetObjectId,
1871
+ x: centroidX,
1872
+ y: centroidY,
1873
+ hidden: event.hidden || positions.length === 0 || positions.every((object) => object.hidden) || activeEventId !== null && event.id !== activeEventId
1874
+ };
1875
+ }).sort((left, right) => left.event.id.localeCompare(right.event.id));
1876
+ }
1707
1877
  function createSceneViewpoints(document2, projection, preset, relationships, objectMap) {
1708
1878
  const generatedOverview = createGeneratedOverviewViewpoint(document2, projection, preset);
1709
1879
  const drafts = /* @__PURE__ */ new Map();
@@ -1757,6 +1927,7 @@
1757
1927
  summary: "Fit the whole system with the current atlas defaults.",
1758
1928
  objectId: null,
1759
1929
  selectedObjectId: null,
1930
+ eventIds: [],
1760
1931
  projection,
1761
1932
  preset,
1762
1933
  rotationDeg: 0,
@@ -1793,6 +1964,9 @@
1793
1964
  draft.select = normalizedValue;
1794
1965
  }
1795
1966
  return;
1967
+ case "events":
1968
+ draft.eventIds = splitListValue(normalizedValue);
1969
+ return;
1796
1970
  case "projection":
1797
1971
  case "view":
1798
1972
  draft.projection = parseViewProjection(normalizedValue) ?? projection;
@@ -1849,6 +2023,7 @@
1849
2023
  summary: draft.summary?.trim() || createViewpointSummary(label, objectId, filter),
1850
2024
  objectId,
1851
2025
  selectedObjectId,
2026
+ eventIds: [...new Set(draft.eventIds ?? [])],
1852
2027
  projection: draft.projection ?? projection,
1853
2028
  preset: draft.preset ?? preset,
1854
2029
  rotationDeg: draft.rotationDeg ?? 0,
@@ -1906,7 +2081,7 @@
1906
2081
  next["orbits-front"] = enabled;
1907
2082
  continue;
1908
2083
  }
1909
- if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "relations" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
2084
+ if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "relations" || rawLayer === "events" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
1910
2085
  next[rawLayer] = enabled;
1911
2086
  }
1912
2087
  }
@@ -1954,7 +2129,7 @@
1954
2129
  }
1955
2130
  return parts.join(" - ");
1956
2131
  }
1957
- function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels) {
2132
+ function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, labelMultiplier) {
1958
2133
  let minX = Number.POSITIVE_INFINITY;
1959
2134
  let minY = Number.POSITIVE_INFINITY;
1960
2135
  let maxX = Number.NEGATIVE_INFINITY;
@@ -1984,7 +2159,7 @@
1984
2159
  for (const label of labels) {
1985
2160
  if (label.hidden)
1986
2161
  continue;
1987
- includeLabelBounds(label, include);
2162
+ includeLabelBounds(label, include, labelMultiplier);
1988
2163
  }
1989
2164
  if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
1990
2165
  return createBounds(0, 0, width, height);
@@ -2022,13 +2197,10 @@
2022
2197
  include(object.x - object.visualRadius - 24, object.y - object.visualRadius - 16);
2023
2198
  include(object.x + object.visualRadius + 24, object.y + object.visualRadius + 36);
2024
2199
  }
2025
- function includeLabelBounds(label, include) {
2026
- const labelScale = 1;
2027
- const labelHalfWidth = estimateLabelHalfWidthFromText(label.label, label.secondaryLabel, labelScale);
2028
- include(label.x - labelHalfWidth, label.y - 18);
2029
- include(label.x + labelHalfWidth, label.y + 8);
2030
- include(label.x - labelHalfWidth, label.secondaryY - 14);
2031
- include(label.x + labelHalfWidth, label.secondaryY + 8);
2200
+ function includeLabelBounds(label, include, labelMultiplier) {
2201
+ const bounds = createLabelRectFromText(label.x, label.y, label.secondaryY, label.textAnchor, label.direction, label.label, label.secondaryLabel, labelMultiplier);
2202
+ include(bounds.left, bounds.top);
2203
+ include(bounds.right, bounds.bottom);
2032
2204
  }
2033
2205
  function placeObject(object, x, y, depth, positions, orbitDrafts, leaderDrafts, context) {
2034
2206
  if (positions.has(object.id)) {
@@ -2418,7 +2590,7 @@
2418
2590
  return null;
2419
2591
  }
2420
2592
  }
2421
- function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels) {
2593
+ function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier) {
2422
2594
  let minX = Number.POSITIVE_INFINITY;
2423
2595
  let minY = Number.POSITIVE_INFINITY;
2424
2596
  let maxX = Number.NEGATIVE_INFINITY;
@@ -2447,7 +2619,7 @@
2447
2619
  }
2448
2620
  for (const label of labels) {
2449
2621
  if (!label.hidden && group.labelIds.includes(label.objectId)) {
2450
- includeLabelBounds(label, include);
2622
+ includeLabelBounds(label, include, labelMultiplier);
2451
2623
  }
2452
2624
  }
2453
2625
  if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
@@ -2472,12 +2644,28 @@
2472
2644
  }
2473
2645
  return current.id;
2474
2646
  }
2475
- function createLabelRect(x, labelY, secondaryY, labelHalfWidth, direction) {
2647
+ function createLabelRect(object, placement, labelMultiplier) {
2648
+ return createLabelRectFromText(placement.x, placement.labelY, placement.secondaryY, placement.textAnchor, placement.direction, object.label, object.secondaryLabel, labelMultiplier);
2649
+ }
2650
+ function createLabelRectFromText(x, labelY, secondaryY, textAnchor, direction, label, secondaryLabel, labelMultiplier) {
2651
+ const labelHalfWidth = estimateLabelHalfWidthFromText(label, secondaryLabel, labelMultiplier);
2652
+ const labelWidth = labelHalfWidth * 2;
2653
+ const topPadding = direction === "above" ? 18 : 12;
2654
+ const bottomPadding = direction === "above" ? 8 : 12;
2655
+ let left = x - labelHalfWidth;
2656
+ let right = x + labelHalfWidth;
2657
+ if (textAnchor === "start") {
2658
+ left = x;
2659
+ right = x + labelWidth;
2660
+ } else if (textAnchor === "end") {
2661
+ left = x - labelWidth;
2662
+ right = x;
2663
+ }
2476
2664
  return {
2477
- left: x - labelHalfWidth,
2478
- right: x + labelHalfWidth,
2479
- top: Math.min(labelY, secondaryY) - (direction < 0 ? 18 : 12),
2480
- bottom: Math.max(labelY, secondaryY) + (direction < 0 ? 8 : 12)
2665
+ left,
2666
+ right,
2667
+ top: Math.min(labelY, secondaryY) - topPadding,
2668
+ bottom: Math.max(labelY, secondaryY) + bottomPadding
2481
2669
  };
2482
2670
  }
2483
2671
  function rectsOverlap(left, right) {
@@ -2664,11 +2852,6 @@
2664
2852
  function customColorFor(value) {
2665
2853
  return typeof value === "string" && value.trim() ? value : void 0;
2666
2854
  }
2667
- function estimateLabelHalfWidth(object, labelMultiplier) {
2668
- const primaryWidth = object.label.length * 4.6 * labelMultiplier + 18;
2669
- const secondaryWidth = object.secondaryLabel.length * 3.9 * labelMultiplier + 18;
2670
- return Math.max(primaryWidth, secondaryWidth, object.visualRadius + 18);
2671
- }
2672
2855
  function estimateLabelHalfWidthFromText(label, secondaryLabel, labelMultiplier) {
2673
2856
  const primaryWidth = label.length * 4.6 * labelMultiplier + 18;
2674
2857
  const secondaryWidth = secondaryLabel.length * 3.9 * labelMultiplier + 18;
@@ -2685,7 +2868,7 @@
2685
2868
  }
2686
2869
 
2687
2870
  // packages/core/dist/draft.js
2688
- function materializeAtlasDocument(document2) {
2871
+ function materializeAtlasDocument(document2, options = {}) {
2689
2872
  const system = document2.system ? {
2690
2873
  type: "system",
2691
2874
  id: document2.system.id,
@@ -2696,6 +2879,8 @@
2696
2879
  properties: materializeDraftSystemProperties(document2.system),
2697
2880
  info: materializeDraftSystemInfo(document2.system)
2698
2881
  } : null;
2882
+ const objects = document2.objects.map(cloneWorldOrbitObject);
2883
+ applyEventPoseOverrides(objects, document2.events ?? [], options.activeEventId ?? null);
2699
2884
  return {
2700
2885
  format: "worldorbit",
2701
2886
  version: "1.0",
@@ -2703,7 +2888,8 @@
2703
2888
  system,
2704
2889
  groups: structuredClone(document2.groups ?? []),
2705
2890
  relations: structuredClone(document2.relations ?? []),
2706
- objects: document2.objects.map(cloneWorldOrbitObject)
2891
+ events: document2.events.map(cloneWorldOrbitEvent),
2892
+ objects
2707
2893
  };
2708
2894
  }
2709
2895
  function cloneWorldOrbitObject(object) {
@@ -2725,6 +2911,52 @@
2725
2911
  info: { ...object.info }
2726
2912
  };
2727
2913
  }
2914
+ function cloneWorldOrbitEvent(event) {
2915
+ return {
2916
+ ...event,
2917
+ participantObjectIds: [...event.participantObjectIds],
2918
+ tags: [...event.tags],
2919
+ positions: event.positions.map(cloneWorldOrbitEventPose)
2920
+ };
2921
+ }
2922
+ function cloneWorldOrbitEventPose(pose) {
2923
+ return {
2924
+ objectId: pose.objectId,
2925
+ placement: clonePlacement(pose.placement),
2926
+ inner: pose.inner ? { ...pose.inner } : void 0,
2927
+ outer: pose.outer ? { ...pose.outer } : void 0
2928
+ };
2929
+ }
2930
+ function clonePlacement(placement) {
2931
+ return placement ? structuredClone(placement) : null;
2932
+ }
2933
+ function applyEventPoseOverrides(objects, events, activeEventId) {
2934
+ if (!activeEventId) {
2935
+ return;
2936
+ }
2937
+ const event = events.find((entry) => entry.id === activeEventId);
2938
+ if (!event) {
2939
+ return;
2940
+ }
2941
+ const objectMap = new Map(objects.map((object) => [object.id, object]));
2942
+ for (const pose of event.positions) {
2943
+ const object = objectMap.get(pose.objectId);
2944
+ if (!object) {
2945
+ continue;
2946
+ }
2947
+ object.placement = clonePlacement(pose.placement);
2948
+ if (pose.inner) {
2949
+ object.properties.inner = { ...pose.inner };
2950
+ } else {
2951
+ delete object.properties.inner;
2952
+ }
2953
+ if (pose.outer) {
2954
+ object.properties.outer = { ...pose.outer };
2955
+ } else {
2956
+ delete object.properties.outer;
2957
+ }
2958
+ }
2959
+ }
2728
2960
  function cloneProperties(properties) {
2729
2961
  const next = {};
2730
2962
  for (const [key, value] of Object.entries(properties)) {
@@ -2813,6 +3045,9 @@
2813
3045
  if ((viewpoint.filter?.groupIds.length ?? 0) > 0) {
2814
3046
  info2[`${prefix}.groups`] = viewpoint.filter?.groupIds.join(" ") ?? "";
2815
3047
  }
3048
+ if (viewpoint.events.length > 0) {
3049
+ info2[`${prefix}.events`] = viewpoint.events.join(" ");
3050
+ }
2816
3051
  }
2817
3052
  for (const annotation of system.annotations) {
2818
3053
  const prefix = `annotation.${annotation.id}`;
@@ -2837,7 +3072,7 @@
2837
3072
  if (orbitFront !== void 0 || orbitBack !== void 0) {
2838
3073
  tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
2839
3074
  }
2840
- for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
3075
+ for (const key of ["background", "guides", "relations", "events", "objects", "labels", "metadata"]) {
2841
3076
  if (layers[key] !== void 0) {
2842
3077
  tokens.push(layers[key] ? key : `-${key}`);
2843
3078
  }
@@ -3011,6 +3246,7 @@
3011
3246
  const diagnostics = [];
3012
3247
  const objectMap = new Map(document2.objects.map((object) => [object.id, object]));
3013
3248
  const groupIds = new Set(document2.groups.map((group) => group.id));
3249
+ const eventIds = new Set(document2.events.map((event) => event.id));
3014
3250
  if (!document2.system) {
3015
3251
  diagnostics.push(error("validate.system.required", "Atlas documents must declare exactly one system."));
3016
3252
  }
@@ -3020,6 +3256,7 @@
3020
3256
  ["viewpoint", document2.system?.viewpoints.map((viewpoint) => viewpoint.id) ?? []],
3021
3257
  ["annotation", document2.system?.annotations.map((annotation) => annotation.id) ?? []],
3022
3258
  ["relation", document2.relations.map((relation) => relation.id)],
3259
+ ["event", document2.events.map((event) => event.id)],
3023
3260
  ["object", document2.objects.map((object) => object.id)]
3024
3261
  ]) {
3025
3262
  for (const id of ids) {
@@ -3035,11 +3272,14 @@
3035
3272
  validateRelation(relation, objectMap, diagnostics);
3036
3273
  }
3037
3274
  for (const viewpoint of document2.system?.viewpoints ?? []) {
3038
- validateViewpointFilter(viewpoint.filter, groupIds, sourceSchemaVersion, diagnostics, viewpoint.id);
3275
+ validateViewpoint(viewpoint.filter, viewpoint.events ?? [], groupIds, eventIds, sourceSchemaVersion, diagnostics, viewpoint.id);
3039
3276
  }
3040
3277
  for (const object of document2.objects) {
3041
3278
  validateObject(object, document2.system, objectMap, groupIds, diagnostics);
3042
3279
  }
3280
+ for (const event of document2.events) {
3281
+ validateEvent(event, objectMap, diagnostics);
3282
+ }
3043
3283
  return diagnostics;
3044
3284
  }
3045
3285
  function validateRelation(relation, objectMap, diagnostics) {
@@ -3057,13 +3297,19 @@
3057
3297
  diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
3058
3298
  }
3059
3299
  }
3060
- function validateViewpointFilter(filter, groupIds, sourceSchemaVersion, diagnostics, viewpointId) {
3061
- if (!filter || sourceSchemaVersion !== "2.1") {
3062
- return;
3063
- }
3064
- for (const groupId of filter.groupIds) {
3065
- if (!groupIds.has(groupId)) {
3066
- diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpointId}".`));
3300
+ function validateViewpoint(filter, eventRefs, groupIds, eventIds, sourceSchemaVersion, diagnostics, viewpointId) {
3301
+ if (sourceSchemaVersion === "2.1") {
3302
+ if (filter) {
3303
+ for (const groupId of filter.groupIds) {
3304
+ if (!groupIds.has(groupId)) {
3305
+ diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpointId}".`, void 0, `viewpoint.${viewpointId}.groups`));
3306
+ }
3307
+ }
3308
+ }
3309
+ for (const eventId of eventRefs) {
3310
+ if (!eventIds.has(eventId)) {
3311
+ diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${viewpointId}".`, void 0, `viewpoint.${viewpointId}.events`));
3312
+ }
3067
3313
  }
3068
3314
  }
3069
3315
  }
@@ -3149,6 +3395,103 @@
3149
3395
  }
3150
3396
  }
3151
3397
  }
3398
+ function validateEvent(event, objectMap, diagnostics) {
3399
+ const fieldPrefix = `event.${event.id}`;
3400
+ const referencedIds = /* @__PURE__ */ new Set();
3401
+ if (!event.kind.trim()) {
3402
+ diagnostics.push(error("validate.event.kind.required", `Event "${event.id}" is missing a "kind" value.`, void 0, `${fieldPrefix}.kind`));
3403
+ }
3404
+ if (!event.targetObjectId && event.participantObjectIds.length === 0) {
3405
+ diagnostics.push(error("validate.event.references.required", `Event "${event.id}" must define a "target" or at least one participant.`, void 0, `${fieldPrefix}.participants`));
3406
+ }
3407
+ if (event.targetObjectId) {
3408
+ referencedIds.add(event.targetObjectId);
3409
+ if (!objectMap.has(event.targetObjectId)) {
3410
+ diagnostics.push(error("validate.event.target.unknown", `Unknown event target "${event.targetObjectId}" on "${event.id}".`, void 0, `${fieldPrefix}.target`));
3411
+ }
3412
+ }
3413
+ const seenParticipants = /* @__PURE__ */ new Set();
3414
+ for (const participantId of event.participantObjectIds) {
3415
+ referencedIds.add(participantId);
3416
+ if (seenParticipants.has(participantId)) {
3417
+ diagnostics.push(warn("validate.event.participants.duplicate", `Event "${event.id}" repeats participant "${participantId}".`, void 0, `${fieldPrefix}.participants`));
3418
+ continue;
3419
+ }
3420
+ seenParticipants.add(participantId);
3421
+ if (!objectMap.has(participantId)) {
3422
+ diagnostics.push(error("validate.event.participants.unknown", `Unknown event participant "${participantId}" on "${event.id}".`, void 0, `${fieldPrefix}.participants`));
3423
+ }
3424
+ }
3425
+ if (event.targetObjectId && event.participantObjectIds.length > 0 && !event.participantObjectIds.includes(event.targetObjectId)) {
3426
+ diagnostics.push(warn("validate.event.target.notParticipant", `Event "${event.id}" defines a target outside its participants list.`, void 0, `${fieldPrefix}.target`));
3427
+ }
3428
+ if (event.positions.length === 0) {
3429
+ diagnostics.push(warn("validate.event.positions.missing", `Event "${event.id}" has no positions block and cannot drive a scene snapshot.`, void 0, `${fieldPrefix}.positions`));
3430
+ }
3431
+ if (/(?:^|[-_])(solar-eclipse|lunar-eclipse|transit|occultation)(?:$|[-_])/.test(event.kind) && referencedIds.size < 3) {
3432
+ diagnostics.push(warn("validate.event.kind.participants", `Event "${event.id}" looks like an eclipse or transit but references fewer than three bodies.`, void 0, `${fieldPrefix}.participants`));
3433
+ }
3434
+ const poseIds = /* @__PURE__ */ new Set();
3435
+ for (const pose of event.positions) {
3436
+ const poseFieldPrefix = `${fieldPrefix}.pose.${pose.objectId}`;
3437
+ if (poseIds.has(pose.objectId)) {
3438
+ diagnostics.push(error("validate.event.pose.duplicate", `Event "${event.id}" defines "${pose.objectId}" more than once in positions.`, void 0, poseFieldPrefix));
3439
+ continue;
3440
+ }
3441
+ poseIds.add(pose.objectId);
3442
+ const object = objectMap.get(pose.objectId);
3443
+ if (!object) {
3444
+ diagnostics.push(error("validate.event.pose.object.unknown", `Unknown event pose object "${pose.objectId}" on "${event.id}".`, void 0, poseFieldPrefix));
3445
+ continue;
3446
+ }
3447
+ if (!referencedIds.has(pose.objectId)) {
3448
+ diagnostics.push(warn("validate.event.pose.unreferenced", `Event pose "${pose.objectId}" on "${event.id}" is not listed in target/participants.`, void 0, poseFieldPrefix));
3449
+ }
3450
+ validateEventPose(pose, object, objectMap, diagnostics, poseFieldPrefix, event.id);
3451
+ }
3452
+ }
3453
+ function validateEventPose(pose, object, objectMap, diagnostics, fieldPrefix, eventId) {
3454
+ const placement = pose.placement;
3455
+ if (!placement) {
3456
+ diagnostics.push(error("validate.event.pose.placement.required", `Event "${eventId}" pose "${pose.objectId}" is missing a placement mode.`, void 0, fieldPrefix));
3457
+ return;
3458
+ }
3459
+ if (placement.mode === "orbit") {
3460
+ if (!objectMap.has(placement.target)) {
3461
+ diagnostics.push(error("validate.event.pose.orbit.target.unknown", `Unknown event orbit target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.orbit`));
3462
+ }
3463
+ if (placement.distance && placement.semiMajor) {
3464
+ diagnostics.push(error("validate.event.pose.orbit.distanceConflict", `Event "${eventId}" pose "${pose.objectId}" cannot declare both "distance" and "semiMajor".`, void 0, `${fieldPrefix}.distance`));
3465
+ }
3466
+ return;
3467
+ }
3468
+ if (placement.mode === "surface") {
3469
+ const target = objectMap.get(placement.target);
3470
+ if (!target) {
3471
+ diagnostics.push(error("validate.event.pose.surface.target.unknown", `Unknown event surface target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.surface`));
3472
+ } else if (!SURFACE_TARGET_TYPES2.has(target.type)) {
3473
+ diagnostics.push(error("validate.event.pose.surface.target.invalid", `Event surface target "${placement.target}" on "${eventId}:${pose.objectId}" is not surface-capable.`, void 0, `${fieldPrefix}.surface`));
3474
+ }
3475
+ return;
3476
+ }
3477
+ if (placement.mode === "at") {
3478
+ if (object.type !== "structure" && object.type !== "phenomenon") {
3479
+ diagnostics.push(error("validate.event.pose.at.objectType", `Only structures and phenomena may use "at" placement in events; found "${object.type}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
3480
+ }
3481
+ const reference = placement.reference;
3482
+ if (reference.kind === "named" && !objectMap.has(reference.name)) {
3483
+ diagnostics.push(error("validate.event.pose.at.target.unknown", `Unknown event at-reference target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
3484
+ } else if (reference.kind === "anchor" && !objectMap.has(reference.objectId)) {
3485
+ diagnostics.push(error("validate.event.pose.anchor.target.unknown", `Unknown event anchor target "${reference.objectId}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
3486
+ } else if (reference.kind === "lagrange") {
3487
+ if (!objectMap.has(reference.primary)) {
3488
+ diagnostics.push(error("validate.event.pose.lagrange.primary.unknown", `Unknown event Lagrange target "${reference.primary}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
3489
+ } else if (reference.secondary && !objectMap.has(reference.secondary)) {
3490
+ diagnostics.push(error("validate.event.pose.lagrange.secondary.unknown", `Unknown event Lagrange target "${reference.secondary}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
3491
+ }
3492
+ }
3493
+ }
3494
+ }
3152
3495
  function validateAtTarget(object, objectMap, diagnostics) {
3153
3496
  const reference = object.placement?.mode === "at" ? object.placement.reference : null;
3154
3497
  if (!reference) {
@@ -3349,6 +3692,21 @@
3349
3692
  });
3350
3693
  }
3351
3694
  var DRAFT_OBJECT_FIELD_KEYS = new Set(DRAFT_OBJECT_FIELD_SPECS.keys());
3695
+ var EVENT_POSE_FIELD_KEYS = /* @__PURE__ */ new Set([
3696
+ "orbit",
3697
+ "distance",
3698
+ "semiMajor",
3699
+ "eccentricity",
3700
+ "period",
3701
+ "angle",
3702
+ "inclination",
3703
+ "phase",
3704
+ "at",
3705
+ "surface",
3706
+ "free",
3707
+ "inner",
3708
+ "outer"
3709
+ ]);
3352
3710
  function parseWorldOrbitAtlas(source) {
3353
3711
  return parseAtlasSource(source);
3354
3712
  }
@@ -3363,12 +3721,15 @@
3363
3721
  const objectNodes = [];
3364
3722
  const groups = [];
3365
3723
  const relations = [];
3724
+ const events = [];
3725
+ const eventPoseNodes = /* @__PURE__ */ new Map();
3366
3726
  let sawDefaults = false;
3367
3727
  let sawAtlas = false;
3368
3728
  const viewpointIds = /* @__PURE__ */ new Set();
3369
3729
  const annotationIds = /* @__PURE__ */ new Set();
3370
3730
  const groupIds = /* @__PURE__ */ new Set();
3371
3731
  const relationIds = /* @__PURE__ */ new Set();
3732
+ const eventIds = /* @__PURE__ */ new Set();
3372
3733
  for (let index = 0; index < lines.length; index++) {
3373
3734
  const rawLine = lines[index];
3374
3735
  const lineNumber = index + 1;
@@ -3399,7 +3760,7 @@
3399
3760
  continue;
3400
3761
  }
3401
3762
  if (indent === 0) {
3402
- section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, { sawDefaults, sawAtlas });
3763
+ section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, { sawDefaults, sawAtlas });
3403
3764
  if (section.kind === "system") {
3404
3765
  system = section.system;
3405
3766
  } else if (section.kind === "defaults") {
@@ -3418,6 +3779,7 @@
3418
3779
  throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
3419
3780
  }
3420
3781
  const objects = objectNodes.map((node) => normalizeDraftObject(node, sourceSchemaVersion, diagnostics));
3782
+ const normalizedEvents = events.map((event) => normalizeDraftEvent(event, eventPoseNodes.get(event.id) ?? []));
3421
3783
  const outputVersion = forcedOutputVersion ?? (sourceSchemaVersion === "2.0-draft" ? "2.0" : sourceSchemaVersion);
3422
3784
  const baseDocument = {
3423
3785
  format: "worldorbit",
@@ -3425,6 +3787,7 @@
3425
3787
  system,
3426
3788
  groups,
3427
3789
  relations,
3790
+ events: normalizedEvents,
3428
3791
  objects,
3429
3792
  diagnostics
3430
3793
  };
@@ -3460,7 +3823,7 @@
3460
3823
  const version = tokens[1].value.toLowerCase();
3461
3824
  return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
3462
3825
  }
3463
- function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, flags) {
3826
+ function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, flags) {
3464
3827
  const keyword = tokens[0]?.value.toLowerCase();
3465
3828
  switch (keyword) {
3466
3829
  case "system":
@@ -3497,7 +3860,7 @@
3497
3860
  if (!system) {
3498
3861
  throw new WorldOrbitError('Atlas section "viewpoint" requires a preceding system declaration', line, tokens[0].column);
3499
3862
  }
3500
- return startViewpointSection(tokens, line, system, viewpointIds);
3863
+ return startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaVersion, diagnostics);
3501
3864
  case "annotation":
3502
3865
  if (!system) {
3503
3866
  throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
@@ -3509,6 +3872,9 @@
3509
3872
  case "relation":
3510
3873
  warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "relation", { line, column: tokens[0].column });
3511
3874
  return startRelationSection(tokens, line, relations, relationIds);
3875
+ case "event":
3876
+ warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "event", { line, column: tokens[0].column });
3877
+ return startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourceSchemaVersion, diagnostics);
3512
3878
  case "object":
3513
3879
  return startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes);
3514
3880
  default:
@@ -3545,7 +3911,7 @@
3545
3911
  seenFields: /* @__PURE__ */ new Set()
3546
3912
  };
3547
3913
  }
3548
- function startViewpointSection(tokens, line, system, viewpointIds) {
3914
+ function startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaVersion, diagnostics) {
3549
3915
  if (tokens.length !== 2) {
3550
3916
  throw new WorldOrbitError("Invalid viewpoint declaration", line, tokens[0]?.column ?? 1);
3551
3917
  }
@@ -3562,6 +3928,7 @@
3562
3928
  summary: "",
3563
3929
  focusObjectId: null,
3564
3930
  selectedObjectId: null,
3931
+ events: [],
3565
3932
  projection: system.defaults.view,
3566
3933
  preset: system.defaults.preset,
3567
3934
  zoom: null,
@@ -3574,6 +3941,8 @@
3574
3941
  return {
3575
3942
  kind: "viewpoint",
3576
3943
  viewpoint,
3944
+ sourceSchemaVersion,
3945
+ diagnostics,
3577
3946
  seenFields: /* @__PURE__ */ new Set(),
3578
3947
  inFilter: false,
3579
3948
  filterIndent: null,
@@ -3664,6 +4033,49 @@
3664
4033
  seenFields: /* @__PURE__ */ new Set()
3665
4034
  };
3666
4035
  }
4036
+ function startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourceSchemaVersion, diagnostics) {
4037
+ if (tokens.length !== 2) {
4038
+ throw new WorldOrbitError("Invalid event declaration", line, tokens[0]?.column ?? 1);
4039
+ }
4040
+ const id = normalizeIdentifier(tokens[1].value);
4041
+ if (!id) {
4042
+ throw new WorldOrbitError("Event id must not be empty", line, tokens[1].column);
4043
+ }
4044
+ if (eventIds.has(id)) {
4045
+ throw new WorldOrbitError(`Duplicate event id "${id}"`, line, tokens[1].column);
4046
+ }
4047
+ const event = {
4048
+ id,
4049
+ kind: "",
4050
+ label: humanizeIdentifier2(id),
4051
+ summary: null,
4052
+ targetObjectId: null,
4053
+ participantObjectIds: [],
4054
+ timing: null,
4055
+ visibility: null,
4056
+ tags: [],
4057
+ color: null,
4058
+ hidden: false,
4059
+ positions: []
4060
+ };
4061
+ const rawPoses = [];
4062
+ events.push(event);
4063
+ eventPoseNodes.set(id, rawPoses);
4064
+ eventIds.add(id);
4065
+ return {
4066
+ kind: "event",
4067
+ event,
4068
+ sourceSchemaVersion,
4069
+ diagnostics,
4070
+ seenFields: /* @__PURE__ */ new Set(),
4071
+ rawPoses,
4072
+ inPositions: false,
4073
+ positionsIndent: null,
4074
+ activePose: null,
4075
+ poseIndent: null,
4076
+ activePoseSeenFields: /* @__PURE__ */ new Set()
4077
+ };
4078
+ }
3667
4079
  function startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes) {
3668
4080
  if (tokens.length < 3) {
3669
4081
  throw new WorldOrbitError("Invalid atlas object declaration", line, tokens[0]?.column ?? 1);
@@ -3720,6 +4132,9 @@
3720
4132
  case "relation":
3721
4133
  applyRelationField(section, tokens, line);
3722
4134
  return;
4135
+ case "event":
4136
+ applyEventField(section, indent, tokens, line);
4137
+ return;
3723
4138
  case "object":
3724
4139
  applyObjectField(section, indent, tokens, line);
3725
4140
  return;
@@ -3846,7 +4261,14 @@
3846
4261
  section.viewpoint.rotationDeg = parseFiniteNumber2(value, line, tokens[0].column, "rotation");
3847
4262
  return;
3848
4263
  case "layers":
3849
- section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line);
4264
+ section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line, section.sourceSchemaVersion, section.diagnostics);
4265
+ return;
4266
+ case "events":
4267
+ warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.events", {
4268
+ line,
4269
+ column: tokens[0].column
4270
+ });
4271
+ section.viewpoint.events = parseTokenList(tokens.slice(1), line, "events");
3850
4272
  return;
3851
4273
  default:
3852
4274
  throw new WorldOrbitError(`Unknown viewpoint field "${tokens[0].value}"`, line, tokens[0].column);
@@ -3951,6 +4373,106 @@
3951
4373
  throw new WorldOrbitError(`Unknown relation field "${tokens[0].value}"`, line, tokens[0].column);
3952
4374
  }
3953
4375
  }
4376
+ function applyEventField(section, indent, tokens, line) {
4377
+ if (section.activePose && indent <= (section.poseIndent ?? 0)) {
4378
+ section.activePose = null;
4379
+ section.poseIndent = null;
4380
+ section.activePoseSeenFields.clear();
4381
+ }
4382
+ if (!section.activePose && section.inPositions && indent <= (section.positionsIndent ?? 0)) {
4383
+ section.inPositions = false;
4384
+ section.positionsIndent = null;
4385
+ }
4386
+ if (section.activePose) {
4387
+ section.activePose.fields.push(parseEventPoseField(tokens, line, section.activePoseSeenFields));
4388
+ return;
4389
+ }
4390
+ if (section.inPositions) {
4391
+ if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "pose") {
4392
+ throw new WorldOrbitError(`Unknown event positions field "${tokens[0].value}"`, line, tokens[0]?.column ?? 1);
4393
+ }
4394
+ const objectId = tokens[1].value;
4395
+ if (!objectId.trim()) {
4396
+ throw new WorldOrbitError("Event pose object id must not be empty", line, tokens[1].column);
4397
+ }
4398
+ const rawPose = {
4399
+ objectId,
4400
+ fields: [],
4401
+ location: { line, column: tokens[0].column }
4402
+ };
4403
+ section.rawPoses.push(rawPose);
4404
+ section.activePose = rawPose;
4405
+ section.poseIndent = indent;
4406
+ section.activePoseSeenFields = /* @__PURE__ */ new Set();
4407
+ return;
4408
+ }
4409
+ if (tokens.length === 1 && tokens[0].value.toLowerCase() === "positions") {
4410
+ if (section.seenFields.has("positions")) {
4411
+ throw new WorldOrbitError('Duplicate event field "positions"', line, tokens[0].column);
4412
+ }
4413
+ section.seenFields.add("positions");
4414
+ section.inPositions = true;
4415
+ section.positionsIndent = indent;
4416
+ return;
4417
+ }
4418
+ const key = requireUniqueField(tokens, section.seenFields, line);
4419
+ switch (key) {
4420
+ case "kind":
4421
+ section.event.kind = joinFieldValue(tokens, line);
4422
+ return;
4423
+ case "label":
4424
+ section.event.label = joinFieldValue(tokens, line);
4425
+ return;
4426
+ case "summary":
4427
+ section.event.summary = joinFieldValue(tokens, line);
4428
+ return;
4429
+ case "target":
4430
+ section.event.targetObjectId = joinFieldValue(tokens, line);
4431
+ return;
4432
+ case "participants":
4433
+ section.event.participantObjectIds = parseTokenList(tokens.slice(1), line, "participants");
4434
+ return;
4435
+ case "timing":
4436
+ section.event.timing = joinFieldValue(tokens, line);
4437
+ return;
4438
+ case "visibility":
4439
+ section.event.visibility = joinFieldValue(tokens, line);
4440
+ return;
4441
+ case "tags":
4442
+ section.event.tags = parseTokenList(tokens.slice(1), line, "tags");
4443
+ return;
4444
+ case "color":
4445
+ section.event.color = joinFieldValue(tokens, line);
4446
+ return;
4447
+ case "hidden":
4448
+ section.event.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
4449
+ line,
4450
+ column: tokens[0].column
4451
+ });
4452
+ return;
4453
+ default:
4454
+ throw new WorldOrbitError(`Unknown event field "${tokens[0].value}"`, line, tokens[0].column);
4455
+ }
4456
+ }
4457
+ function parseEventPoseField(tokens, line, seenFields) {
4458
+ if (tokens.length < 2) {
4459
+ throw new WorldOrbitError("Invalid event pose field line", line, tokens[0]?.column ?? 1);
4460
+ }
4461
+ const key = tokens[0].value;
4462
+ if (!EVENT_POSE_FIELD_KEYS.has(key)) {
4463
+ throw new WorldOrbitError(`Unknown event pose field "${key}"`, line, tokens[0].column);
4464
+ }
4465
+ if (seenFields.has(key)) {
4466
+ throw new WorldOrbitError(`Duplicate event pose field "${key}"`, line, tokens[0].column);
4467
+ }
4468
+ seenFields.add(key);
4469
+ return {
4470
+ type: "field",
4471
+ key,
4472
+ values: tokens.slice(1).map((token) => token.value),
4473
+ location: { line, column: tokens[0].column }
4474
+ };
4475
+ }
3954
4476
  function applyObjectField(section, indent, tokens, line) {
3955
4477
  if (section.activeBlock && indent <= (section.blockIndent ?? 0)) {
3956
4478
  section.activeBlock = null;
@@ -4009,7 +4531,7 @@
4009
4531
  function parseObjectTypeTokens(tokens, line) {
4010
4532
  return parseTokenList(tokens, line, "objectTypes").filter((value) => value === "star" || value === "planet" || value === "moon" || value === "belt" || value === "asteroid" || value === "comet" || value === "ring" || value === "structure" || value === "phenomenon");
4011
4533
  }
4012
- function parseLayerTokens(tokens, line) {
4534
+ function parseLayerTokens(tokens, line, sourceSchemaVersion, diagnostics) {
4013
4535
  const layers = {};
4014
4536
  for (const token of parseTokenList(tokens, line, "layers")) {
4015
4537
  const enabled = !token.startsWith("-") && !token.startsWith("!");
@@ -4019,7 +4541,13 @@
4019
4541
  layers["orbits-front"] = enabled;
4020
4542
  continue;
4021
4543
  }
4022
- if (raw === "background" || raw === "guides" || raw === "orbits-back" || raw === "orbits-front" || raw === "relations" || raw === "objects" || raw === "labels" || raw === "metadata") {
4544
+ if (raw === "background" || raw === "guides" || raw === "orbits-back" || raw === "orbits-front" || raw === "relations" || raw === "events" || raw === "objects" || raw === "labels" || raw === "metadata") {
4545
+ if (raw === "events" && sourceSchemaVersion && diagnostics) {
4546
+ warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "layers.events", {
4547
+ line,
4548
+ column: tokens[0]?.column ?? 1
4549
+ });
4550
+ }
4023
4551
  layers[raw] = enabled;
4024
4552
  }
4025
4553
  }
@@ -4158,7 +4686,7 @@
4158
4686
  }
4159
4687
  function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
4160
4688
  const fieldMap = collectDraftFields(node.fields);
4161
- const placement = extractDraftPlacement(node.objectType, fieldMap);
4689
+ const placement = extractPlacementFromFieldMap(fieldMap);
4162
4690
  const properties = normalizeDraftProperties(node.objectType, fieldMap);
4163
4691
  const groups = parseOptionalTokenList(fieldMap.get("groups")?.[0]);
4164
4692
  const epoch = parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]);
@@ -4210,6 +4738,24 @@
4210
4738
  }
4211
4739
  return object;
4212
4740
  }
4741
+ function normalizeDraftEvent(event, rawPoses) {
4742
+ return {
4743
+ ...event,
4744
+ participantObjectIds: [...new Set(event.participantObjectIds)],
4745
+ tags: [...new Set(event.tags)],
4746
+ positions: rawPoses.map((pose) => normalizeDraftEventPose(pose))
4747
+ };
4748
+ }
4749
+ function normalizeDraftEventPose(rawPose) {
4750
+ const fieldMap = collectDraftFields(rawPose.fields);
4751
+ const placement = extractPlacementFromFieldMap(fieldMap);
4752
+ return {
4753
+ objectId: rawPose.objectId,
4754
+ placement,
4755
+ inner: parseOptionalUnitField(fieldMap.get("inner")?.[0], "inner"),
4756
+ outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer")
4757
+ };
4758
+ }
4213
4759
  function collectDraftFields(fields) {
4214
4760
  const grouped = /* @__PURE__ */ new Map();
4215
4761
  for (const field of fields) {
@@ -4226,7 +4772,7 @@
4226
4772
  }
4227
4773
  return grouped;
4228
4774
  }
4229
- function extractDraftPlacement(objectType, fieldMap) {
4775
+ function extractPlacementFromFieldMap(fieldMap) {
4230
4776
  const orbitField = fieldMap.get("orbit")?.[0];
4231
4777
  const atField = fieldMap.get("at")?.[0];
4232
4778
  const surfaceField = fieldMap.get("surface")?.[0];
@@ -4837,6 +5383,7 @@
4837
5383
  const orbitMarkup = layers.orbits ? renderOrbitLayer(scene, visibleObjectIds, layers.structures) : { back: "", front: "" };
4838
5384
  const leaderMarkup = layers.guides ? scene.leaders.filter((leader) => !leader.hidden).filter((leader) => visibleObjectIds.has(leader.objectId)).filter((leader) => layers.structures || !isStructureLike(leader.object)).map((leader) => `<line class="wo-leader wo-leader-${leader.mode}" x1="${leader.x1}" y1="${leader.y1}" x2="${leader.x2}" y2="${leader.y2}" data-render-id="${escapeXml(leader.renderId)}" data-group-id="${escapeAttribute(leader.groupId ?? "")}" />`).join("") : "";
4839
5385
  const relationMarkup = layers.relations ? scene.relations.filter((relation) => !relation.hidden).filter((relation) => visibleObjectIds.has(relation.fromObjectId) && visibleObjectIds.has(relation.toObjectId)).map((relation) => `<line class="wo-relation" x1="${relation.x1}" y1="${relation.y1}" x2="${relation.x2}" y2="${relation.y2}" data-render-id="${escapeXml(relation.renderId)}" data-relation-id="${escapeAttribute(relation.relationId)}" />`).join("") : "";
5386
+ const eventMarkup = layers.events ? scene.events.filter((event) => !event.hidden).map((event) => renderSceneEventOverlay(scene, event, visibleObjectIds, theme)).join("") : "";
4840
5387
  const objectMarkup = layers.objects ? visibleObjects.map((object) => renderSceneObject(object, options.selectedObjectId ?? null, theme)).join("") : "";
4841
5388
  const labelMarkup = layers.labels ? visibleLabels.map((label) => renderSceneLabel(scene, label, options.selectedObjectId ?? null)).join("") : "";
4842
5389
  const metadataMarkup = layers.metadata ? `<text class="wo-title" x="56" y="64">${escapeXml(scene.title)}</text>
@@ -4872,6 +5419,9 @@
4872
5419
  .wo-orbit-front { opacity: 0.9; }
4873
5420
  .wo-orbit-band { stroke: ${theme.orbitBand}; stroke-linecap: round; }
4874
5421
  .wo-relation { stroke: ${theme.relation}; stroke-width: 2; stroke-dasharray: 10 6; }
5422
+ .wo-event-line { stroke: ${theme.accent}; stroke-width: 1.6; stroke-dasharray: 5 5; opacity: 0.72; }
5423
+ .wo-event-node { fill: ${theme.accent}; stroke: ${theme.selected}; stroke-width: 1.4; opacity: 0.92; }
5424
+ .wo-event-label { fill: ${theme.accent}; font-family: ${theme.fontFamily}; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
4875
5425
  .wo-leader { stroke: ${theme.leader}; stroke-width: 1.5; stroke-dasharray: 6 5; }
4876
5426
  .wo-label { fill: ${theme.ink}; font-family: ${theme.fontFamily}; font-weight: 600; letter-spacing: 0.02em; }
4877
5427
  .wo-label-secondary { fill: ${theme.muted}; font-family: ${theme.fontFamily}; font-weight: 500; }
@@ -4906,6 +5456,7 @@
4906
5456
  ${layers.orbits ? `<g data-layer-id="orbits-back">${orbitMarkup.back}</g>` : ""}
4907
5457
  ${layers.guides ? `<g data-layer-id="guides">${leaderMarkup}</g>` : ""}
4908
5458
  ${layers.relations ? `<g data-layer-id="relations">${relationMarkup}</g>` : ""}
5459
+ ${layers.events ? `<g data-layer-id="events">${eventMarkup}</g>` : ""}
4909
5460
  ${layers.objects ? `<g data-layer-id="objects">${objectMarkup}</g>` : ""}
4910
5461
  ${layers.orbits ? `<g data-layer-id="orbits-front">${orbitMarkup.front}</g>` : ""}
4911
5462
  ${layers.labels ? `<g data-layer-id="labels">${labelMarkup}</g>` : ""}
@@ -4913,6 +5464,20 @@
4913
5464
  </g>
4914
5465
  </g>
4915
5466
  </svg>`;
5467
+ }
5468
+ function renderSceneEventOverlay(scene, event, visibleObjectIds, theme) {
5469
+ const participants = event.objectIds.filter((objectId) => visibleObjectIds.has(objectId)).map((objectId) => scene.objects.find((object) => object.objectId === objectId && !object.hidden)).filter(Boolean);
5470
+ if (participants.length === 0) {
5471
+ return "";
5472
+ }
5473
+ const stroke = event.event.color || theme.accent;
5474
+ const label = event.event.label || event.event.id;
5475
+ const lineMarkup = participants.map((object) => `<line class="wo-event-line" x1="${event.x}" y1="${event.y}" x2="${object.x}" y2="${object.y}" stroke="${escapeAttribute(stroke)}" data-event-id="${escapeAttribute(event.eventId)}" data-object-id="${escapeAttribute(object.objectId)}" />`).join("");
5476
+ return `<g class="wo-event" data-render-id="${escapeXml(event.renderId)}" data-event-id="${escapeAttribute(event.eventId)}">
5477
+ ${lineMarkup}
5478
+ <circle class="wo-event-node" cx="${event.x}" cy="${event.y}" r="5" fill="${escapeAttribute(stroke)}" />
5479
+ <text class="wo-event-label" x="${event.x}" y="${event.y - 10}" text-anchor="middle" font-size="10">${escapeXml(label)}</text>
5480
+ </g>`;
4916
5481
  }
4917
5482
  function renderDocumentToSvg(document2, options = {}) {
4918
5483
  return renderSceneToSvg(renderDocumentToScene(document2, options), options);
@@ -5512,6 +6077,13 @@
5512
6077
  value: `${details.object.resonance.targetObjectId} ${details.object.resonance.ratio}`
5513
6078
  });
5514
6079
  }
6080
+ if (details.relatedEvents.length > 0) {
6081
+ fields.set("events", {
6082
+ key: "events",
6083
+ label: "Events",
6084
+ value: details.relatedEvents.map((event) => event.event.label || event.event.id).join(", ")
6085
+ });
6086
+ }
5515
6087
  if (placement?.mode === "at") {
5516
6088
  fields.set("placement", {
5517
6089
  key: "placement",
@@ -5926,6 +6498,12 @@
5926
6498
  emitAtlasStateChange();
5927
6499
  return true;
5928
6500
  },
6501
+ getActiveEventId() {
6502
+ return renderOptions.activeEventId ?? null;
6503
+ },
6504
+ setActiveEvent(id) {
6505
+ api.setRenderOptions({ activeEventId: id });
6506
+ },
5929
6507
  search(query, limit = 12) {
5930
6508
  return searchSceneObjects(scene, query, limit);
5931
6509
  },
@@ -6190,6 +6768,7 @@
6190
6768
  orbit: scene.orbitVisuals.find((orbit) => orbit.objectId === renderObject.objectId && !orbit.hidden) ?? null,
6191
6769
  relatedOrbits: scene.orbitVisuals.filter((orbit) => !orbit.hidden && (orbit.objectId === renderObject.objectId || renderObject.ancestorIds.includes(orbit.objectId) || renderObject.childIds.includes(orbit.objectId))),
6192
6770
  relations: scene.relations.filter((relation) => !relation.hidden && (relation.fromObjectId === renderObject.objectId || relation.toObjectId === renderObject.objectId)),
6771
+ relatedEvents: scene.events.filter((event) => !event.hidden && (event.targetObjectId === renderObject.objectId || event.objectIds.includes(renderObject.objectId))),
6193
6772
  parent: getObjectById(renderObject.parentId),
6194
6773
  children: renderObject.childIds.map((childId) => getObjectById(childId)).filter(Boolean),
6195
6774
  ancestors: renderObject.ancestorIds.map((ancestorId) => getObjectById(ancestorId)).filter(Boolean),
@@ -6506,7 +7085,8 @@
6506
7085
  filter: renderOptions.filter ? { ...renderOptions.filter } : void 0,
6507
7086
  scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : void 0,
6508
7087
  layers: renderOptions.layers ? { ...renderOptions.layers } : void 0,
6509
- theme: renderOptions.theme && typeof renderOptions.theme === "object" ? { ...renderOptions.theme } : renderOptions.theme
7088
+ theme: renderOptions.theme && typeof renderOptions.theme === "object" ? { ...renderOptions.theme } : renderOptions.theme,
7089
+ activeEventId: renderOptions.activeEventId ?? null
6510
7090
  };
6511
7091
  }
6512
7092
  function mergeRenderOptions(current, next) {
@@ -6526,7 +7106,7 @@
6526
7106
  };
6527
7107
  }
6528
7108
  function hasSceneAffectingRenderOptions(options) {
6529
- return options.width !== void 0 || options.height !== void 0 || options.padding !== void 0 || options.preset !== void 0 || options.projection !== void 0 || options.scaleModel !== void 0;
7109
+ return options.width !== void 0 || options.height !== void 0 || options.padding !== void 0 || options.preset !== void 0 || options.projection !== void 0 || options.scaleModel !== void 0 || options.activeEventId !== void 0;
6530
7110
  }
6531
7111
  function resolveSourceRenderOptions2(loaded, renderOptions) {
6532
7112
  const atlasDocument = loaded.atlasDocument ?? loaded.draftDocument;
@@ -7136,6 +7716,7 @@
7136
7716
  groupCount: activeViewer.getScene().groups.length,
7137
7717
  semanticGroupCount: activeViewer.getScene().semanticGroups.length,
7138
7718
  relationCount: activeViewer.getScene().relations.length,
7719
+ eventCount: activeViewer.getScene().events.length,
7139
7720
  viewpointCount: activeViewer.getScene().viewpoints.length
7140
7721
  }
7141
7722
  };