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
@@ -615,6 +615,7 @@ var WorldOrbit = (() => {
615
615
  system,
616
616
  groups: [],
617
617
  relations: [],
618
+ events: [],
618
619
  objects
619
620
  };
620
621
  }
@@ -1072,8 +1073,10 @@ var WorldOrbit = (() => {
1072
1073
  const scaleModel = resolveScaleModel(layoutPreset, options.scaleModel);
1073
1074
  const spacingFactor = layoutPresetSpacing(layoutPreset);
1074
1075
  const systemId = document.system?.id ?? null;
1075
- const objectMap = new Map(document.objects.map((object) => [object.id, object]));
1076
- const relationships = buildSceneRelationships(document.objects, objectMap);
1076
+ const activeEventId = options.activeEventId ?? null;
1077
+ const effectiveObjects = createEffectiveObjects(document.objects, document.events ?? [], activeEventId);
1078
+ const objectMap = new Map(effectiveObjects.map((object) => [object.id, object]));
1079
+ const relationships = buildSceneRelationships(effectiveObjects, objectMap);
1077
1080
  const positions = /* @__PURE__ */ new Map();
1078
1081
  const orbitDrafts = [];
1079
1082
  const leaderDrafts = [];
@@ -1082,7 +1085,7 @@ var WorldOrbit = (() => {
1082
1085
  const atObjects = [];
1083
1086
  const surfaceChildren = /* @__PURE__ */ new Map();
1084
1087
  const orbitChildren = /* @__PURE__ */ new Map();
1085
- for (const object of document.objects) {
1088
+ for (const object of effectiveObjects) {
1086
1089
  const placement = object.placement;
1087
1090
  if (!placement) {
1088
1091
  rootObjects.push(object);
@@ -1177,13 +1180,14 @@ var WorldOrbit = (() => {
1177
1180
  const objects = [...positions.values()].map((position) => createSceneObject(position, scaleModel, relationships));
1178
1181
  const orbitVisuals = orbitDrafts.map((draft) => createOrbitVisual(draft, relationships.groupIds.get(draft.object.id) ?? null));
1179
1182
  const leaders = leaderDrafts.map((draft) => createLeaderLine(draft));
1180
- const labels = createSceneLabels(objects, height, scaleModel.labelMultiplier);
1183
+ const labels = createSceneLabels(objects, width, height, scaleModel.labelMultiplier);
1181
1184
  const relations = createSceneRelations(document, objects);
1182
- const layers = createSceneLayers(orbitVisuals, relations, leaders, objects, labels);
1183
- const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships);
1185
+ const events = createSceneEvents(document.events ?? [], objects, activeEventId);
1186
+ const layers = createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels);
1187
+ const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, scaleModel.labelMultiplier);
1184
1188
  const semanticGroups = createSceneSemanticGroups(document, objects);
1185
1189
  const viewpoints = createSceneViewpoints(document, projection, frame.preset, relationships, objectMap);
1186
- const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels);
1190
+ const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, scaleModel.labelMultiplier);
1187
1191
  return {
1188
1192
  width,
1189
1193
  height,
@@ -1209,6 +1213,8 @@ var WorldOrbit = (() => {
1209
1213
  groups,
1210
1214
  semanticGroups,
1211
1215
  viewpoints,
1216
+ events,
1217
+ activeEventId,
1212
1218
  objects,
1213
1219
  orbitVisuals,
1214
1220
  relations,
@@ -1227,6 +1233,35 @@ var WorldOrbit = (() => {
1227
1233
  y: center.y + dx * sin + dy * cos
1228
1234
  };
1229
1235
  }
1236
+ function createEffectiveObjects(objects, events, activeEventId) {
1237
+ const cloned = objects.map((object) => structuredClone(object));
1238
+ if (!activeEventId) {
1239
+ return cloned;
1240
+ }
1241
+ const activeEvent = events.find((event) => event.id === activeEventId);
1242
+ if (!activeEvent) {
1243
+ return cloned;
1244
+ }
1245
+ const objectMap = new Map(cloned.map((object) => [object.id, object]));
1246
+ for (const pose of activeEvent.positions) {
1247
+ const object = objectMap.get(pose.objectId);
1248
+ if (!object) {
1249
+ continue;
1250
+ }
1251
+ object.placement = pose.placement ? structuredClone(pose.placement) : null;
1252
+ if (pose.inner) {
1253
+ object.properties.inner = { ...pose.inner };
1254
+ } else {
1255
+ delete object.properties.inner;
1256
+ }
1257
+ if (pose.outer) {
1258
+ object.properties.outer = { ...pose.outer };
1259
+ } else {
1260
+ delete object.properties.outer;
1261
+ }
1262
+ }
1263
+ return cloned;
1264
+ }
1230
1265
  function resolveLayoutPreset(document) {
1231
1266
  const rawScale = String(document.system?.properties.scale ?? "balanced").toLowerCase();
1232
1267
  switch (rawScale) {
@@ -1382,24 +1417,14 @@ var WorldOrbit = (() => {
1382
1417
  hidden: draft.object.properties.hidden === true
1383
1418
  };
1384
1419
  }
1385
- function createSceneLabels(objects, sceneHeight, labelMultiplier) {
1420
+ function createSceneLabels(objects, sceneWidth, sceneHeight, labelMultiplier) {
1386
1421
  const labels = [];
1387
1422
  const occupied = [];
1388
- const visibleObjects = [...objects].filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false).sort((left, right) => left.sortKey - right.sortKey);
1423
+ const objectMap = new Map(objects.map((object) => [object.objectId, object]));
1424
+ const visibleObjects = [...objects].filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false).sort(compareLabelPlacementOrder);
1389
1425
  for (const object of visibleObjects) {
1390
- const direction = object.y > sceneHeight * 0.62 ? -1 : 1;
1391
- const labelHalfWidth = estimateLabelHalfWidth(object, labelMultiplier);
1392
- let labelY = object.y + direction * (object.radius + 18 * labelMultiplier);
1393
- let secondaryY = labelY + direction * (16 * labelMultiplier);
1394
- let bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
1395
- let attempts = 0;
1396
- while (occupied.some((entry) => rectsOverlap(entry, bounds)) && attempts < 10) {
1397
- labelY += direction * 14 * labelMultiplier;
1398
- secondaryY += direction * 14 * labelMultiplier;
1399
- bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
1400
- attempts += 1;
1401
- }
1402
- occupied.push(bounds);
1426
+ const placement = selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) ?? createLabelPlacement(object, defaultVerticalDirection(object, objectMap.get(object.parentId ?? "") ?? null, sceneHeight), 0, labelMultiplier);
1427
+ occupied.push(createLabelRect(object, placement, labelMultiplier));
1403
1428
  labels.push({
1404
1429
  renderId: `${object.renderId}-label`,
1405
1430
  objectId: object.objectId,
@@ -1408,17 +1433,128 @@ var WorldOrbit = (() => {
1408
1433
  semanticGroupIds: [...object.semanticGroupIds],
1409
1434
  label: object.label,
1410
1435
  secondaryLabel: object.secondaryLabel,
1411
- x: object.x,
1412
- y: labelY,
1413
- secondaryY,
1414
- textAnchor: "middle",
1415
- direction: direction < 0 ? "above" : "below",
1436
+ x: placement.x,
1437
+ y: placement.labelY,
1438
+ secondaryY: placement.secondaryY,
1439
+ textAnchor: placement.textAnchor,
1440
+ direction: placement.direction,
1416
1441
  hidden: object.hidden
1417
1442
  });
1418
1443
  }
1419
1444
  return labels;
1420
1445
  }
1421
- function createSceneLayers(orbitVisuals, relations, leaders, objects, labels) {
1446
+ function compareLabelPlacementOrder(left, right) {
1447
+ const priorityDiff = labelPlacementPriority(left) - labelPlacementPriority(right);
1448
+ if (priorityDiff !== 0) {
1449
+ return priorityDiff;
1450
+ }
1451
+ const renderPriorityDiff = (right.object.renderHints?.renderPriority ?? 0) - (left.object.renderHints?.renderPriority ?? 0);
1452
+ if (renderPriorityDiff !== 0) {
1453
+ return renderPriorityDiff;
1454
+ }
1455
+ return left.sortKey - right.sortKey;
1456
+ }
1457
+ function labelPlacementPriority(object) {
1458
+ switch (object.object.type) {
1459
+ case "star":
1460
+ return 0;
1461
+ case "planet":
1462
+ return 1;
1463
+ case "moon":
1464
+ return 2;
1465
+ case "belt":
1466
+ case "ring":
1467
+ return 3;
1468
+ case "asteroid":
1469
+ case "comet":
1470
+ return 4;
1471
+ case "structure":
1472
+ case "phenomenon":
1473
+ return 5;
1474
+ }
1475
+ }
1476
+ function selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) {
1477
+ for (const direction of preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight)) {
1478
+ const maxAttempts = direction === "left" || direction === "right" ? 4 : 6;
1479
+ for (let attempt = 0; attempt <= maxAttempts; attempt += 1) {
1480
+ const placement = createLabelPlacement(object, direction, attempt, labelMultiplier);
1481
+ const rect = createLabelRect(object, placement, labelMultiplier);
1482
+ if (!occupied.some((entry) => rectsOverlap(entry, rect))) {
1483
+ return placement;
1484
+ }
1485
+ }
1486
+ }
1487
+ return null;
1488
+ }
1489
+ function preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight) {
1490
+ const parent = object.parentId ? objectMap.get(object.parentId) ?? null : null;
1491
+ const vertical = defaultVerticalDirection(object, parent, sceneHeight);
1492
+ const oppositeVertical = vertical === "below" ? "above" : "below";
1493
+ const horizontal = defaultHorizontalDirection(object, parent, sceneWidth);
1494
+ const oppositeHorizontal = horizontal === "right" ? "left" : "right";
1495
+ 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";
1496
+ return preferHorizontal ? [horizontal, vertical, oppositeHorizontal, oppositeVertical] : [vertical, horizontal, oppositeVertical, oppositeHorizontal];
1497
+ }
1498
+ function defaultVerticalDirection(object, parent, sceneHeight) {
1499
+ if (parent && Math.abs(object.y - parent.y) > 6) {
1500
+ return object.y >= parent.y ? "below" : "above";
1501
+ }
1502
+ return object.y > sceneHeight * 0.62 ? "above" : "below";
1503
+ }
1504
+ function defaultHorizontalDirection(object, parent, sceneWidth) {
1505
+ if (parent && Math.abs(object.x - parent.x) > 6) {
1506
+ return object.x >= parent.x ? "right" : "left";
1507
+ }
1508
+ return object.x >= sceneWidth / 2 ? "right" : "left";
1509
+ }
1510
+ function createLabelPlacement(object, direction, attempt, labelMultiplier) {
1511
+ const step = 14 * labelMultiplier;
1512
+ switch (direction) {
1513
+ case "above": {
1514
+ const labelY = object.y - (object.radius + 18 * labelMultiplier + attempt * step);
1515
+ return {
1516
+ x: object.x,
1517
+ labelY,
1518
+ secondaryY: labelY - 16 * labelMultiplier,
1519
+ textAnchor: "middle",
1520
+ direction
1521
+ };
1522
+ }
1523
+ case "below": {
1524
+ const labelY = object.y + object.radius + 18 * labelMultiplier + attempt * step;
1525
+ return {
1526
+ x: object.x,
1527
+ labelY,
1528
+ secondaryY: labelY + 16 * labelMultiplier,
1529
+ textAnchor: "middle",
1530
+ direction
1531
+ };
1532
+ }
1533
+ case "left": {
1534
+ const x = object.x - (object.visualRadius + 16 * labelMultiplier + attempt * step);
1535
+ const labelY = object.y - 4 * labelMultiplier;
1536
+ return {
1537
+ x,
1538
+ labelY,
1539
+ secondaryY: labelY + 16 * labelMultiplier,
1540
+ textAnchor: "end",
1541
+ direction
1542
+ };
1543
+ }
1544
+ case "right": {
1545
+ const x = object.x + object.visualRadius + 16 * labelMultiplier + attempt * step;
1546
+ const labelY = object.y - 4 * labelMultiplier;
1547
+ return {
1548
+ x,
1549
+ labelY,
1550
+ secondaryY: labelY + 16 * labelMultiplier,
1551
+ textAnchor: "start",
1552
+ direction
1553
+ };
1554
+ }
1555
+ }
1556
+ }
1557
+ function createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels) {
1422
1558
  const backOrbitIds = orbitVisuals.filter((visual) => !visual.hidden && Boolean(visual.backArcPath)).map((visual) => visual.renderId);
1423
1559
  const frontOrbitIds = orbitVisuals.filter((visual) => !visual.hidden).map((visual) => visual.renderId);
1424
1560
  return [
@@ -1433,6 +1569,10 @@ var WorldOrbit = (() => {
1433
1569
  id: "relations",
1434
1570
  renderIds: relations.filter((relation) => !relation.hidden).map((relation) => relation.renderId)
1435
1571
  },
1572
+ {
1573
+ id: "events",
1574
+ renderIds: events.filter((event) => !event.hidden).map((event) => event.renderId)
1575
+ },
1436
1576
  {
1437
1577
  id: "objects",
1438
1578
  renderIds: objects.filter((object) => !object.hidden).map((object) => object.renderId)
@@ -1444,7 +1584,7 @@ var WorldOrbit = (() => {
1444
1584
  { id: "metadata", renderIds: ["wo-title", "wo-subtitle", "wo-meta"] }
1445
1585
  ];
1446
1586
  }
1447
- function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships) {
1587
+ function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, labelMultiplier) {
1448
1588
  const groups = /* @__PURE__ */ new Map();
1449
1589
  const ensureGroup = (groupId) => {
1450
1590
  if (!groupId) {
@@ -1493,7 +1633,7 @@ var WorldOrbit = (() => {
1493
1633
  }
1494
1634
  }
1495
1635
  for (const group of groups.values()) {
1496
- group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels);
1636
+ group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier);
1497
1637
  }
1498
1638
  return [...groups.values()].sort((left, right) => left.label.localeCompare(right.label));
1499
1639
  }
@@ -1527,6 +1667,29 @@ var WorldOrbit = (() => {
1527
1667
  };
1528
1668
  }).sort((left, right) => left.relation.id.localeCompare(right.relation.id));
1529
1669
  }
1670
+ function createSceneEvents(events, objects, activeEventId) {
1671
+ const objectMap = new Map(objects.map((object) => [object.objectId, object]));
1672
+ return events.map((event) => {
1673
+ const objectIds = [.../* @__PURE__ */ new Set([
1674
+ ...event.targetObjectId ? [event.targetObjectId] : [],
1675
+ ...event.participantObjectIds
1676
+ ])];
1677
+ const positions = objectIds.map((objectId) => objectMap.get(objectId)).filter(Boolean);
1678
+ const centroidX = positions.length > 0 ? positions.reduce((sum, object) => sum + object.x, 0) / positions.length : 0;
1679
+ const centroidY = positions.length > 0 ? positions.reduce((sum, object) => sum + object.y, 0) / positions.length : 0;
1680
+ return {
1681
+ renderId: `${createRenderId(event.id)}-event`,
1682
+ eventId: event.id,
1683
+ event,
1684
+ objectIds,
1685
+ participantIds: [...event.participantObjectIds],
1686
+ targetObjectId: event.targetObjectId,
1687
+ x: centroidX,
1688
+ y: centroidY,
1689
+ hidden: event.hidden || positions.length === 0 || positions.every((object) => object.hidden) || activeEventId !== null && event.id !== activeEventId
1690
+ };
1691
+ }).sort((left, right) => left.event.id.localeCompare(right.event.id));
1692
+ }
1530
1693
  function createSceneViewpoints(document, projection, preset, relationships, objectMap) {
1531
1694
  const generatedOverview = createGeneratedOverviewViewpoint(document, projection, preset);
1532
1695
  const drafts = /* @__PURE__ */ new Map();
@@ -1580,6 +1743,7 @@ var WorldOrbit = (() => {
1580
1743
  summary: "Fit the whole system with the current atlas defaults.",
1581
1744
  objectId: null,
1582
1745
  selectedObjectId: null,
1746
+ eventIds: [],
1583
1747
  projection,
1584
1748
  preset,
1585
1749
  rotationDeg: 0,
@@ -1616,6 +1780,9 @@ var WorldOrbit = (() => {
1616
1780
  draft.select = normalizedValue;
1617
1781
  }
1618
1782
  return;
1783
+ case "events":
1784
+ draft.eventIds = splitListValue(normalizedValue);
1785
+ return;
1619
1786
  case "projection":
1620
1787
  case "view":
1621
1788
  draft.projection = parseViewProjection(normalizedValue) ?? projection;
@@ -1672,6 +1839,7 @@ var WorldOrbit = (() => {
1672
1839
  summary: draft.summary?.trim() || createViewpointSummary(label, objectId, filter),
1673
1840
  objectId,
1674
1841
  selectedObjectId,
1842
+ eventIds: [...new Set(draft.eventIds ?? [])],
1675
1843
  projection: draft.projection ?? projection,
1676
1844
  preset: draft.preset ?? preset,
1677
1845
  rotationDeg: draft.rotationDeg ?? 0,
@@ -1729,7 +1897,7 @@ var WorldOrbit = (() => {
1729
1897
  next["orbits-front"] = enabled;
1730
1898
  continue;
1731
1899
  }
1732
- if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "relations" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
1900
+ if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "relations" || rawLayer === "events" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
1733
1901
  next[rawLayer] = enabled;
1734
1902
  }
1735
1903
  }
@@ -1777,7 +1945,7 @@ var WorldOrbit = (() => {
1777
1945
  }
1778
1946
  return parts.join(" - ");
1779
1947
  }
1780
- function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels) {
1948
+ function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, labelMultiplier) {
1781
1949
  let minX = Number.POSITIVE_INFINITY;
1782
1950
  let minY = Number.POSITIVE_INFINITY;
1783
1951
  let maxX = Number.NEGATIVE_INFINITY;
@@ -1807,7 +1975,7 @@ var WorldOrbit = (() => {
1807
1975
  for (const label of labels) {
1808
1976
  if (label.hidden)
1809
1977
  continue;
1810
- includeLabelBounds(label, include);
1978
+ includeLabelBounds(label, include, labelMultiplier);
1811
1979
  }
1812
1980
  if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
1813
1981
  return createBounds(0, 0, width, height);
@@ -1845,13 +2013,10 @@ var WorldOrbit = (() => {
1845
2013
  include(object.x - object.visualRadius - 24, object.y - object.visualRadius - 16);
1846
2014
  include(object.x + object.visualRadius + 24, object.y + object.visualRadius + 36);
1847
2015
  }
1848
- function includeLabelBounds(label, include) {
1849
- const labelScale = 1;
1850
- const labelHalfWidth = estimateLabelHalfWidthFromText(label.label, label.secondaryLabel, labelScale);
1851
- include(label.x - labelHalfWidth, label.y - 18);
1852
- include(label.x + labelHalfWidth, label.y + 8);
1853
- include(label.x - labelHalfWidth, label.secondaryY - 14);
1854
- include(label.x + labelHalfWidth, label.secondaryY + 8);
2016
+ function includeLabelBounds(label, include, labelMultiplier) {
2017
+ const bounds = createLabelRectFromText(label.x, label.y, label.secondaryY, label.textAnchor, label.direction, label.label, label.secondaryLabel, labelMultiplier);
2018
+ include(bounds.left, bounds.top);
2019
+ include(bounds.right, bounds.bottom);
1855
2020
  }
1856
2021
  function placeObject(object, x, y, depth, positions, orbitDrafts, leaderDrafts, context) {
1857
2022
  if (positions.has(object.id)) {
@@ -2241,7 +2406,7 @@ var WorldOrbit = (() => {
2241
2406
  return null;
2242
2407
  }
2243
2408
  }
2244
- function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels) {
2409
+ function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier) {
2245
2410
  let minX = Number.POSITIVE_INFINITY;
2246
2411
  let minY = Number.POSITIVE_INFINITY;
2247
2412
  let maxX = Number.NEGATIVE_INFINITY;
@@ -2270,7 +2435,7 @@ var WorldOrbit = (() => {
2270
2435
  }
2271
2436
  for (const label of labels) {
2272
2437
  if (!label.hidden && group.labelIds.includes(label.objectId)) {
2273
- includeLabelBounds(label, include);
2438
+ includeLabelBounds(label, include, labelMultiplier);
2274
2439
  }
2275
2440
  }
2276
2441
  if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
@@ -2295,12 +2460,28 @@ var WorldOrbit = (() => {
2295
2460
  }
2296
2461
  return current.id;
2297
2462
  }
2298
- function createLabelRect(x, labelY, secondaryY, labelHalfWidth, direction) {
2463
+ function createLabelRect(object, placement, labelMultiplier) {
2464
+ return createLabelRectFromText(placement.x, placement.labelY, placement.secondaryY, placement.textAnchor, placement.direction, object.label, object.secondaryLabel, labelMultiplier);
2465
+ }
2466
+ function createLabelRectFromText(x, labelY, secondaryY, textAnchor, direction, label, secondaryLabel, labelMultiplier) {
2467
+ const labelHalfWidth = estimateLabelHalfWidthFromText(label, secondaryLabel, labelMultiplier);
2468
+ const labelWidth = labelHalfWidth * 2;
2469
+ const topPadding = direction === "above" ? 18 : 12;
2470
+ const bottomPadding = direction === "above" ? 8 : 12;
2471
+ let left = x - labelHalfWidth;
2472
+ let right = x + labelHalfWidth;
2473
+ if (textAnchor === "start") {
2474
+ left = x;
2475
+ right = x + labelWidth;
2476
+ } else if (textAnchor === "end") {
2477
+ left = x - labelWidth;
2478
+ right = x;
2479
+ }
2299
2480
  return {
2300
- left: x - labelHalfWidth,
2301
- right: x + labelHalfWidth,
2302
- top: Math.min(labelY, secondaryY) - (direction < 0 ? 18 : 12),
2303
- bottom: Math.max(labelY, secondaryY) + (direction < 0 ? 8 : 12)
2481
+ left,
2482
+ right,
2483
+ top: Math.min(labelY, secondaryY) - topPadding,
2484
+ bottom: Math.max(labelY, secondaryY) + bottomPadding
2304
2485
  };
2305
2486
  }
2306
2487
  function rectsOverlap(left, right) {
@@ -2487,11 +2668,6 @@ var WorldOrbit = (() => {
2487
2668
  function customColorFor(value) {
2488
2669
  return typeof value === "string" && value.trim() ? value : void 0;
2489
2670
  }
2490
- function estimateLabelHalfWidth(object, labelMultiplier) {
2491
- const primaryWidth = object.label.length * 4.6 * labelMultiplier + 18;
2492
- const secondaryWidth = object.secondaryLabel.length * 3.9 * labelMultiplier + 18;
2493
- return Math.max(primaryWidth, secondaryWidth, object.visualRadius + 18);
2494
- }
2495
2671
  function estimateLabelHalfWidthFromText(label, secondaryLabel, labelMultiplier) {
2496
2672
  const primaryWidth = label.length * 4.6 * labelMultiplier + 18;
2497
2673
  const secondaryWidth = secondaryLabel.length * 3.9 * labelMultiplier + 18;
@@ -2531,6 +2707,7 @@ var WorldOrbit = (() => {
2531
2707
  system,
2532
2708
  groups: structuredClone(document.groups ?? []),
2533
2709
  relations: structuredClone(document.relations ?? []),
2710
+ events: structuredClone(document.events ?? []),
2534
2711
  objects: document.objects.map(cloneWorldOrbitObject),
2535
2712
  diagnostics
2536
2713
  };
@@ -2538,7 +2715,7 @@ var WorldOrbit = (() => {
2538
2715
  function upgradeDocumentToDraftV2(document, options = {}) {
2539
2716
  return convertAtlasDocumentToLegacyDraft(upgradeDocumentToV2(document, options));
2540
2717
  }
2541
- function materializeAtlasDocument(document) {
2718
+ function materializeAtlasDocument(document, options = {}) {
2542
2719
  const system = document.system ? {
2543
2720
  type: "system",
2544
2721
  id: document.system.id,
@@ -2549,6 +2726,8 @@ var WorldOrbit = (() => {
2549
2726
  properties: materializeDraftSystemProperties(document.system),
2550
2727
  info: materializeDraftSystemInfo(document.system)
2551
2728
  } : null;
2729
+ const objects = document.objects.map(cloneWorldOrbitObject);
2730
+ applyEventPoseOverrides(objects, document.events ?? [], options.activeEventId ?? null);
2552
2731
  return {
2553
2732
  format: "worldorbit",
2554
2733
  version: "1.0",
@@ -2556,7 +2735,8 @@ var WorldOrbit = (() => {
2556
2735
  system,
2557
2736
  groups: structuredClone(document.groups ?? []),
2558
2737
  relations: structuredClone(document.relations ?? []),
2559
- objects: document.objects.map(cloneWorldOrbitObject)
2738
+ events: document.events.map(cloneWorldOrbitEvent),
2739
+ objects
2560
2740
  };
2561
2741
  }
2562
2742
  function materializeDraftDocument(document) {
@@ -2684,6 +2864,7 @@ var WorldOrbit = (() => {
2684
2864
  summary: viewpoint.summary,
2685
2865
  focusObjectId: viewpoint.objectId,
2686
2866
  selectedObjectId: viewpoint.selectedObjectId,
2867
+ events: [...viewpoint.eventIds],
2687
2868
  projection: viewpoint.projection,
2688
2869
  preset: viewpoint.preset,
2689
2870
  zoom: viewpoint.scale,
@@ -2716,6 +2897,52 @@ var WorldOrbit = (() => {
2716
2897
  info: { ...object.info }
2717
2898
  };
2718
2899
  }
2900
+ function cloneWorldOrbitEvent(event) {
2901
+ return {
2902
+ ...event,
2903
+ participantObjectIds: [...event.participantObjectIds],
2904
+ tags: [...event.tags],
2905
+ positions: event.positions.map(cloneWorldOrbitEventPose)
2906
+ };
2907
+ }
2908
+ function cloneWorldOrbitEventPose(pose) {
2909
+ return {
2910
+ objectId: pose.objectId,
2911
+ placement: clonePlacement(pose.placement),
2912
+ inner: pose.inner ? { ...pose.inner } : void 0,
2913
+ outer: pose.outer ? { ...pose.outer } : void 0
2914
+ };
2915
+ }
2916
+ function clonePlacement(placement) {
2917
+ return placement ? structuredClone(placement) : null;
2918
+ }
2919
+ function applyEventPoseOverrides(objects, events, activeEventId) {
2920
+ if (!activeEventId) {
2921
+ return;
2922
+ }
2923
+ const event = events.find((entry) => entry.id === activeEventId);
2924
+ if (!event) {
2925
+ return;
2926
+ }
2927
+ const objectMap = new Map(objects.map((object) => [object.id, object]));
2928
+ for (const pose of event.positions) {
2929
+ const object = objectMap.get(pose.objectId);
2930
+ if (!object) {
2931
+ continue;
2932
+ }
2933
+ object.placement = clonePlacement(pose.placement);
2934
+ if (pose.inner) {
2935
+ object.properties.inner = { ...pose.inner };
2936
+ } else {
2937
+ delete object.properties.inner;
2938
+ }
2939
+ if (pose.outer) {
2940
+ object.properties.outer = { ...pose.outer };
2941
+ } else {
2942
+ delete object.properties.outer;
2943
+ }
2944
+ }
2945
+ }
2719
2946
  function cloneProperties(properties) {
2720
2947
  const next = {};
2721
2948
  for (const [key, value] of Object.entries(properties)) {
@@ -2813,6 +3040,9 @@ var WorldOrbit = (() => {
2813
3040
  if ((viewpoint.filter?.groupIds.length ?? 0) > 0) {
2814
3041
  info2[`${prefix}.groups`] = viewpoint.filter?.groupIds.join(" ") ?? "";
2815
3042
  }
3043
+ if (viewpoint.events.length > 0) {
3044
+ info2[`${prefix}.events`] = viewpoint.events.join(" ");
3045
+ }
2816
3046
  }
2817
3047
  for (const annotation of system.annotations) {
2818
3048
  const prefix = `annotation.${annotation.id}`;
@@ -2837,7 +3067,7 @@ var WorldOrbit = (() => {
2837
3067
  if (orbitFront !== void 0 || orbitBack !== void 0) {
2838
3068
  tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
2839
3069
  }
2840
- for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
3070
+ for (const key of ["background", "guides", "relations", "events", "objects", "labels", "metadata"]) {
2841
3071
  if (layers[key] !== void 0) {
2842
3072
  tokens.push(layers[key] ? key : `-${key}`);
2843
3073
  }
@@ -2941,6 +3171,10 @@ var WorldOrbit = (() => {
2941
3171
  lines.push("");
2942
3172
  lines.push(...formatAtlasRelation(relation));
2943
3173
  }
3174
+ for (const event of [...document.events].sort(compareIdLike)) {
3175
+ lines.push("");
3176
+ lines.push(...formatAtlasEvent(event));
3177
+ }
2944
3178
  const sortedObjects = [...document.objects].sort(compareObjects);
2945
3179
  if (sortedObjects.length > 0 && lines.at(-1) !== "") {
2946
3180
  lines.push("");
@@ -2971,6 +3205,10 @@ var WorldOrbit = (() => {
2971
3205
  lines.push("");
2972
3206
  lines.push(...formatAtlasRelation(relation));
2973
3207
  }
3208
+ for (const event of [...legacy.events].sort(compareIdLike)) {
3209
+ lines.push("");
3210
+ lines.push(...formatAtlasEvent(event));
3211
+ }
2974
3212
  const sortedObjects = [...legacy.objects].sort(compareObjects);
2975
3213
  if (sortedObjects.length > 0 && lines.at(-1) !== "") {
2976
3214
  lines.push("");
@@ -3182,6 +3420,9 @@ var WorldOrbit = (() => {
3182
3420
  if (layerTokens.length > 0) {
3183
3421
  lines.push(` layers ${layerTokens.join(" ")}`);
3184
3422
  }
3423
+ if (viewpoint.events.length > 0) {
3424
+ lines.push(` events ${viewpoint.events.join(" ")}`);
3425
+ }
3185
3426
  if (viewpoint.filter) {
3186
3427
  lines.push(" filter");
3187
3428
  if (viewpoint.filter.query) {
@@ -3254,6 +3495,54 @@ var WorldOrbit = (() => {
3254
3495
  }
3255
3496
  return lines;
3256
3497
  }
3498
+ function formatAtlasEvent(event) {
3499
+ const lines = [`event ${event.id}`, ` kind ${quoteIfNeeded(event.kind)}`];
3500
+ if (event.label) {
3501
+ lines.push(` label ${quoteIfNeeded(event.label)}`);
3502
+ }
3503
+ if (event.summary) {
3504
+ lines.push(` summary ${quoteIfNeeded(event.summary)}`);
3505
+ }
3506
+ if (event.targetObjectId) {
3507
+ lines.push(` target ${event.targetObjectId}`);
3508
+ }
3509
+ if (event.participantObjectIds.length > 0) {
3510
+ lines.push(` participants ${event.participantObjectIds.join(" ")}`);
3511
+ }
3512
+ if (event.timing) {
3513
+ lines.push(` timing ${quoteIfNeeded(event.timing)}`);
3514
+ }
3515
+ if (event.visibility) {
3516
+ lines.push(` visibility ${quoteIfNeeded(event.visibility)}`);
3517
+ }
3518
+ if (event.tags.length > 0) {
3519
+ lines.push(` tags ${event.tags.map(quoteIfNeeded).join(" ")}`);
3520
+ }
3521
+ if (event.color) {
3522
+ lines.push(` color ${quoteIfNeeded(event.color)}`);
3523
+ }
3524
+ if (event.hidden) {
3525
+ lines.push(" hidden true");
3526
+ }
3527
+ if (event.positions.length > 0) {
3528
+ lines.push("");
3529
+ lines.push(" positions");
3530
+ for (const pose of [...event.positions].sort(comparePoseObjectId)) {
3531
+ lines.push(` pose ${pose.objectId}`);
3532
+ for (const fieldLine of formatEventPoseFields(pose)) {
3533
+ lines.push(` ${fieldLine}`);
3534
+ }
3535
+ }
3536
+ }
3537
+ return lines;
3538
+ }
3539
+ function formatEventPoseFields(pose) {
3540
+ return [
3541
+ ...formatPlacement(pose.placement),
3542
+ ...formatOptionalUnit("inner", pose.inner),
3543
+ ...formatOptionalUnit("outer", pose.outer)
3544
+ ];
3545
+ }
3257
3546
  function formatValue(value) {
3258
3547
  if (Array.isArray(value)) {
3259
3548
  return value.map((item) => quoteIfNeeded(item)).join(" ");
@@ -3295,7 +3584,7 @@ var WorldOrbit = (() => {
3295
3584
  if (orbitFront !== void 0 || orbitBack !== void 0) {
3296
3585
  tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
3297
3586
  }
3298
- for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
3587
+ for (const key of ["background", "guides", "relations", "events", "objects", "labels", "metadata"]) {
3299
3588
  if (layers[key] !== void 0) {
3300
3589
  tokens.push(layers[key] ? key : `-${key}`);
3301
3590
  }
@@ -3323,6 +3612,9 @@ var WorldOrbit = (() => {
3323
3612
  function compareIdLike(left, right) {
3324
3613
  return left.id.localeCompare(right.id);
3325
3614
  }
3615
+ function comparePoseObjectId(left, right) {
3616
+ return left.objectId.localeCompare(right.objectId);
3617
+ }
3326
3618
  function objectTypeIndex(objectType) {
3327
3619
  switch (objectType) {
3328
3620
  case "star":
@@ -3518,6 +3810,7 @@ var WorldOrbit = (() => {
3518
3810
  const diagnostics = [];
3519
3811
  const objectMap = new Map(document.objects.map((object) => [object.id, object]));
3520
3812
  const groupIds = new Set(document.groups.map((group) => group.id));
3813
+ const eventIds = new Set(document.events.map((event) => event.id));
3521
3814
  if (!document.system) {
3522
3815
  diagnostics.push(error("validate.system.required", "Atlas documents must declare exactly one system."));
3523
3816
  }
@@ -3527,6 +3820,7 @@ var WorldOrbit = (() => {
3527
3820
  ["viewpoint", document.system?.viewpoints.map((viewpoint) => viewpoint.id) ?? []],
3528
3821
  ["annotation", document.system?.annotations.map((annotation) => annotation.id) ?? []],
3529
3822
  ["relation", document.relations.map((relation) => relation.id)],
3823
+ ["event", document.events.map((event) => event.id)],
3530
3824
  ["object", document.objects.map((object) => object.id)]
3531
3825
  ]) {
3532
3826
  for (const id of ids) {
@@ -3542,11 +3836,14 @@ var WorldOrbit = (() => {
3542
3836
  validateRelation(relation, objectMap, diagnostics);
3543
3837
  }
3544
3838
  for (const viewpoint of document.system?.viewpoints ?? []) {
3545
- validateViewpointFilter(viewpoint.filter, groupIds, sourceSchemaVersion, diagnostics, viewpoint.id);
3839
+ validateViewpoint(viewpoint.filter, viewpoint.events ?? [], groupIds, eventIds, sourceSchemaVersion, diagnostics, viewpoint.id);
3546
3840
  }
3547
3841
  for (const object of document.objects) {
3548
3842
  validateObject(object, document.system, objectMap, groupIds, diagnostics);
3549
3843
  }
3844
+ for (const event of document.events) {
3845
+ validateEvent(event, objectMap, diagnostics);
3846
+ }
3550
3847
  return diagnostics;
3551
3848
  }
3552
3849
  function validateRelation(relation, objectMap, diagnostics) {
@@ -3564,13 +3861,19 @@ var WorldOrbit = (() => {
3564
3861
  diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
3565
3862
  }
3566
3863
  }
3567
- function validateViewpointFilter(filter, groupIds, sourceSchemaVersion, diagnostics, viewpointId) {
3568
- if (!filter || sourceSchemaVersion !== "2.1") {
3569
- return;
3570
- }
3571
- for (const groupId of filter.groupIds) {
3572
- if (!groupIds.has(groupId)) {
3573
- diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpointId}".`));
3864
+ function validateViewpoint(filter, eventRefs, groupIds, eventIds, sourceSchemaVersion, diagnostics, viewpointId) {
3865
+ if (sourceSchemaVersion === "2.1") {
3866
+ if (filter) {
3867
+ for (const groupId of filter.groupIds) {
3868
+ if (!groupIds.has(groupId)) {
3869
+ diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpointId}".`, void 0, `viewpoint.${viewpointId}.groups`));
3870
+ }
3871
+ }
3872
+ }
3873
+ for (const eventId of eventRefs) {
3874
+ if (!eventIds.has(eventId)) {
3875
+ diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${viewpointId}".`, void 0, `viewpoint.${viewpointId}.events`));
3876
+ }
3574
3877
  }
3575
3878
  }
3576
3879
  }
@@ -3656,6 +3959,103 @@ var WorldOrbit = (() => {
3656
3959
  }
3657
3960
  }
3658
3961
  }
3962
+ function validateEvent(event, objectMap, diagnostics) {
3963
+ const fieldPrefix = `event.${event.id}`;
3964
+ const referencedIds = /* @__PURE__ */ new Set();
3965
+ if (!event.kind.trim()) {
3966
+ diagnostics.push(error("validate.event.kind.required", `Event "${event.id}" is missing a "kind" value.`, void 0, `${fieldPrefix}.kind`));
3967
+ }
3968
+ if (!event.targetObjectId && event.participantObjectIds.length === 0) {
3969
+ diagnostics.push(error("validate.event.references.required", `Event "${event.id}" must define a "target" or at least one participant.`, void 0, `${fieldPrefix}.participants`));
3970
+ }
3971
+ if (event.targetObjectId) {
3972
+ referencedIds.add(event.targetObjectId);
3973
+ if (!objectMap.has(event.targetObjectId)) {
3974
+ diagnostics.push(error("validate.event.target.unknown", `Unknown event target "${event.targetObjectId}" on "${event.id}".`, void 0, `${fieldPrefix}.target`));
3975
+ }
3976
+ }
3977
+ const seenParticipants = /* @__PURE__ */ new Set();
3978
+ for (const participantId of event.participantObjectIds) {
3979
+ referencedIds.add(participantId);
3980
+ if (seenParticipants.has(participantId)) {
3981
+ diagnostics.push(warn("validate.event.participants.duplicate", `Event "${event.id}" repeats participant "${participantId}".`, void 0, `${fieldPrefix}.participants`));
3982
+ continue;
3983
+ }
3984
+ seenParticipants.add(participantId);
3985
+ if (!objectMap.has(participantId)) {
3986
+ diagnostics.push(error("validate.event.participants.unknown", `Unknown event participant "${participantId}" on "${event.id}".`, void 0, `${fieldPrefix}.participants`));
3987
+ }
3988
+ }
3989
+ if (event.targetObjectId && event.participantObjectIds.length > 0 && !event.participantObjectIds.includes(event.targetObjectId)) {
3990
+ diagnostics.push(warn("validate.event.target.notParticipant", `Event "${event.id}" defines a target outside its participants list.`, void 0, `${fieldPrefix}.target`));
3991
+ }
3992
+ if (event.positions.length === 0) {
3993
+ diagnostics.push(warn("validate.event.positions.missing", `Event "${event.id}" has no positions block and cannot drive a scene snapshot.`, void 0, `${fieldPrefix}.positions`));
3994
+ }
3995
+ if (/(?:^|[-_])(solar-eclipse|lunar-eclipse|transit|occultation)(?:$|[-_])/.test(event.kind) && referencedIds.size < 3) {
3996
+ 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`));
3997
+ }
3998
+ const poseIds = /* @__PURE__ */ new Set();
3999
+ for (const pose of event.positions) {
4000
+ const poseFieldPrefix = `${fieldPrefix}.pose.${pose.objectId}`;
4001
+ if (poseIds.has(pose.objectId)) {
4002
+ diagnostics.push(error("validate.event.pose.duplicate", `Event "${event.id}" defines "${pose.objectId}" more than once in positions.`, void 0, poseFieldPrefix));
4003
+ continue;
4004
+ }
4005
+ poseIds.add(pose.objectId);
4006
+ const object = objectMap.get(pose.objectId);
4007
+ if (!object) {
4008
+ diagnostics.push(error("validate.event.pose.object.unknown", `Unknown event pose object "${pose.objectId}" on "${event.id}".`, void 0, poseFieldPrefix));
4009
+ continue;
4010
+ }
4011
+ if (!referencedIds.has(pose.objectId)) {
4012
+ diagnostics.push(warn("validate.event.pose.unreferenced", `Event pose "${pose.objectId}" on "${event.id}" is not listed in target/participants.`, void 0, poseFieldPrefix));
4013
+ }
4014
+ validateEventPose(pose, object, objectMap, diagnostics, poseFieldPrefix, event.id);
4015
+ }
4016
+ }
4017
+ function validateEventPose(pose, object, objectMap, diagnostics, fieldPrefix, eventId) {
4018
+ const placement = pose.placement;
4019
+ if (!placement) {
4020
+ diagnostics.push(error("validate.event.pose.placement.required", `Event "${eventId}" pose "${pose.objectId}" is missing a placement mode.`, void 0, fieldPrefix));
4021
+ return;
4022
+ }
4023
+ if (placement.mode === "orbit") {
4024
+ if (!objectMap.has(placement.target)) {
4025
+ diagnostics.push(error("validate.event.pose.orbit.target.unknown", `Unknown event orbit target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.orbit`));
4026
+ }
4027
+ if (placement.distance && placement.semiMajor) {
4028
+ diagnostics.push(error("validate.event.pose.orbit.distanceConflict", `Event "${eventId}" pose "${pose.objectId}" cannot declare both "distance" and "semiMajor".`, void 0, `${fieldPrefix}.distance`));
4029
+ }
4030
+ return;
4031
+ }
4032
+ if (placement.mode === "surface") {
4033
+ const target = objectMap.get(placement.target);
4034
+ if (!target) {
4035
+ diagnostics.push(error("validate.event.pose.surface.target.unknown", `Unknown event surface target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.surface`));
4036
+ } else if (!SURFACE_TARGET_TYPES2.has(target.type)) {
4037
+ 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`));
4038
+ }
4039
+ return;
4040
+ }
4041
+ if (placement.mode === "at") {
4042
+ if (object.type !== "structure" && object.type !== "phenomenon") {
4043
+ 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`));
4044
+ }
4045
+ const reference = placement.reference;
4046
+ if (reference.kind === "named" && !objectMap.has(reference.name)) {
4047
+ diagnostics.push(error("validate.event.pose.at.target.unknown", `Unknown event at-reference target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
4048
+ } else if (reference.kind === "anchor" && !objectMap.has(reference.objectId)) {
4049
+ diagnostics.push(error("validate.event.pose.anchor.target.unknown", `Unknown event anchor target "${reference.objectId}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
4050
+ } else if (reference.kind === "lagrange") {
4051
+ if (!objectMap.has(reference.primary)) {
4052
+ diagnostics.push(error("validate.event.pose.lagrange.primary.unknown", `Unknown event Lagrange target "${reference.primary}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
4053
+ } else if (reference.secondary && !objectMap.has(reference.secondary)) {
4054
+ diagnostics.push(error("validate.event.pose.lagrange.secondary.unknown", `Unknown event Lagrange target "${reference.secondary}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
4055
+ }
4056
+ }
4057
+ }
4058
+ }
3659
4059
  function validateAtTarget(object, objectMap, diagnostics) {
3660
4060
  const reference = object.placement?.mode === "at" ? object.placement.reference : null;
3661
4061
  if (!reference) {
@@ -3856,6 +4256,21 @@ var WorldOrbit = (() => {
3856
4256
  });
3857
4257
  }
3858
4258
  var DRAFT_OBJECT_FIELD_KEYS = new Set(DRAFT_OBJECT_FIELD_SPECS.keys());
4259
+ var EVENT_POSE_FIELD_KEYS = /* @__PURE__ */ new Set([
4260
+ "orbit",
4261
+ "distance",
4262
+ "semiMajor",
4263
+ "eccentricity",
4264
+ "period",
4265
+ "angle",
4266
+ "inclination",
4267
+ "phase",
4268
+ "at",
4269
+ "surface",
4270
+ "free",
4271
+ "inner",
4272
+ "outer"
4273
+ ]);
3859
4274
  function parseWorldOrbitAtlas(source) {
3860
4275
  return parseAtlasSource(source);
3861
4276
  }
@@ -3873,12 +4288,15 @@ var WorldOrbit = (() => {
3873
4288
  const objectNodes = [];
3874
4289
  const groups = [];
3875
4290
  const relations = [];
4291
+ const events = [];
4292
+ const eventPoseNodes = /* @__PURE__ */ new Map();
3876
4293
  let sawDefaults = false;
3877
4294
  let sawAtlas = false;
3878
4295
  const viewpointIds = /* @__PURE__ */ new Set();
3879
4296
  const annotationIds = /* @__PURE__ */ new Set();
3880
4297
  const groupIds = /* @__PURE__ */ new Set();
3881
4298
  const relationIds = /* @__PURE__ */ new Set();
4299
+ const eventIds = /* @__PURE__ */ new Set();
3882
4300
  for (let index = 0; index < lines.length; index++) {
3883
4301
  const rawLine = lines[index];
3884
4302
  const lineNumber = index + 1;
@@ -3909,7 +4327,7 @@ var WorldOrbit = (() => {
3909
4327
  continue;
3910
4328
  }
3911
4329
  if (indent === 0) {
3912
- section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, { sawDefaults, sawAtlas });
4330
+ section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, { sawDefaults, sawAtlas });
3913
4331
  if (section.kind === "system") {
3914
4332
  system = section.system;
3915
4333
  } else if (section.kind === "defaults") {
@@ -3928,6 +4346,7 @@ var WorldOrbit = (() => {
3928
4346
  throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
3929
4347
  }
3930
4348
  const objects = objectNodes.map((node) => normalizeDraftObject(node, sourceSchemaVersion, diagnostics));
4349
+ const normalizedEvents = events.map((event) => normalizeDraftEvent(event, eventPoseNodes.get(event.id) ?? []));
3931
4350
  const outputVersion = forcedOutputVersion ?? (sourceSchemaVersion === "2.0-draft" ? "2.0" : sourceSchemaVersion);
3932
4351
  const baseDocument = {
3933
4352
  format: "worldorbit",
@@ -3935,6 +4354,7 @@ var WorldOrbit = (() => {
3935
4354
  system,
3936
4355
  groups,
3937
4356
  relations,
4357
+ events: normalizedEvents,
3938
4358
  objects,
3939
4359
  diagnostics
3940
4360
  };
@@ -3970,7 +4390,7 @@ var WorldOrbit = (() => {
3970
4390
  const version = tokens[1].value.toLowerCase();
3971
4391
  return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
3972
4392
  }
3973
- function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, flags) {
4393
+ function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, flags) {
3974
4394
  const keyword = tokens[0]?.value.toLowerCase();
3975
4395
  switch (keyword) {
3976
4396
  case "system":
@@ -4007,7 +4427,7 @@ var WorldOrbit = (() => {
4007
4427
  if (!system) {
4008
4428
  throw new WorldOrbitError('Atlas section "viewpoint" requires a preceding system declaration', line, tokens[0].column);
4009
4429
  }
4010
- return startViewpointSection(tokens, line, system, viewpointIds);
4430
+ return startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaVersion, diagnostics);
4011
4431
  case "annotation":
4012
4432
  if (!system) {
4013
4433
  throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
@@ -4019,6 +4439,9 @@ var WorldOrbit = (() => {
4019
4439
  case "relation":
4020
4440
  warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "relation", { line, column: tokens[0].column });
4021
4441
  return startRelationSection(tokens, line, relations, relationIds);
4442
+ case "event":
4443
+ warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "event", { line, column: tokens[0].column });
4444
+ return startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourceSchemaVersion, diagnostics);
4022
4445
  case "object":
4023
4446
  return startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes);
4024
4447
  default:
@@ -4055,7 +4478,7 @@ var WorldOrbit = (() => {
4055
4478
  seenFields: /* @__PURE__ */ new Set()
4056
4479
  };
4057
4480
  }
4058
- function startViewpointSection(tokens, line, system, viewpointIds) {
4481
+ function startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaVersion, diagnostics) {
4059
4482
  if (tokens.length !== 2) {
4060
4483
  throw new WorldOrbitError("Invalid viewpoint declaration", line, tokens[0]?.column ?? 1);
4061
4484
  }
@@ -4072,6 +4495,7 @@ var WorldOrbit = (() => {
4072
4495
  summary: "",
4073
4496
  focusObjectId: null,
4074
4497
  selectedObjectId: null,
4498
+ events: [],
4075
4499
  projection: system.defaults.view,
4076
4500
  preset: system.defaults.preset,
4077
4501
  zoom: null,
@@ -4084,6 +4508,8 @@ var WorldOrbit = (() => {
4084
4508
  return {
4085
4509
  kind: "viewpoint",
4086
4510
  viewpoint,
4511
+ sourceSchemaVersion,
4512
+ diagnostics,
4087
4513
  seenFields: /* @__PURE__ */ new Set(),
4088
4514
  inFilter: false,
4089
4515
  filterIndent: null,
@@ -4174,6 +4600,49 @@ var WorldOrbit = (() => {
4174
4600
  seenFields: /* @__PURE__ */ new Set()
4175
4601
  };
4176
4602
  }
4603
+ function startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourceSchemaVersion, diagnostics) {
4604
+ if (tokens.length !== 2) {
4605
+ throw new WorldOrbitError("Invalid event declaration", line, tokens[0]?.column ?? 1);
4606
+ }
4607
+ const id = normalizeIdentifier2(tokens[1].value);
4608
+ if (!id) {
4609
+ throw new WorldOrbitError("Event id must not be empty", line, tokens[1].column);
4610
+ }
4611
+ if (eventIds.has(id)) {
4612
+ throw new WorldOrbitError(`Duplicate event id "${id}"`, line, tokens[1].column);
4613
+ }
4614
+ const event = {
4615
+ id,
4616
+ kind: "",
4617
+ label: humanizeIdentifier3(id),
4618
+ summary: null,
4619
+ targetObjectId: null,
4620
+ participantObjectIds: [],
4621
+ timing: null,
4622
+ visibility: null,
4623
+ tags: [],
4624
+ color: null,
4625
+ hidden: false,
4626
+ positions: []
4627
+ };
4628
+ const rawPoses = [];
4629
+ events.push(event);
4630
+ eventPoseNodes.set(id, rawPoses);
4631
+ eventIds.add(id);
4632
+ return {
4633
+ kind: "event",
4634
+ event,
4635
+ sourceSchemaVersion,
4636
+ diagnostics,
4637
+ seenFields: /* @__PURE__ */ new Set(),
4638
+ rawPoses,
4639
+ inPositions: false,
4640
+ positionsIndent: null,
4641
+ activePose: null,
4642
+ poseIndent: null,
4643
+ activePoseSeenFields: /* @__PURE__ */ new Set()
4644
+ };
4645
+ }
4177
4646
  function startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes) {
4178
4647
  if (tokens.length < 3) {
4179
4648
  throw new WorldOrbitError("Invalid atlas object declaration", line, tokens[0]?.column ?? 1);
@@ -4230,6 +4699,9 @@ var WorldOrbit = (() => {
4230
4699
  case "relation":
4231
4700
  applyRelationField(section, tokens, line);
4232
4701
  return;
4702
+ case "event":
4703
+ applyEventField(section, indent, tokens, line);
4704
+ return;
4233
4705
  case "object":
4234
4706
  applyObjectField(section, indent, tokens, line);
4235
4707
  return;
@@ -4356,7 +4828,14 @@ var WorldOrbit = (() => {
4356
4828
  section.viewpoint.rotationDeg = parseFiniteNumber2(value, line, tokens[0].column, "rotation");
4357
4829
  return;
4358
4830
  case "layers":
4359
- section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line);
4831
+ section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line, section.sourceSchemaVersion, section.diagnostics);
4832
+ return;
4833
+ case "events":
4834
+ warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.events", {
4835
+ line,
4836
+ column: tokens[0].column
4837
+ });
4838
+ section.viewpoint.events = parseTokenList(tokens.slice(1), line, "events");
4360
4839
  return;
4361
4840
  default:
4362
4841
  throw new WorldOrbitError(`Unknown viewpoint field "${tokens[0].value}"`, line, tokens[0].column);
@@ -4461,6 +4940,106 @@ var WorldOrbit = (() => {
4461
4940
  throw new WorldOrbitError(`Unknown relation field "${tokens[0].value}"`, line, tokens[0].column);
4462
4941
  }
4463
4942
  }
4943
+ function applyEventField(section, indent, tokens, line) {
4944
+ if (section.activePose && indent <= (section.poseIndent ?? 0)) {
4945
+ section.activePose = null;
4946
+ section.poseIndent = null;
4947
+ section.activePoseSeenFields.clear();
4948
+ }
4949
+ if (!section.activePose && section.inPositions && indent <= (section.positionsIndent ?? 0)) {
4950
+ section.inPositions = false;
4951
+ section.positionsIndent = null;
4952
+ }
4953
+ if (section.activePose) {
4954
+ section.activePose.fields.push(parseEventPoseField(tokens, line, section.activePoseSeenFields));
4955
+ return;
4956
+ }
4957
+ if (section.inPositions) {
4958
+ if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "pose") {
4959
+ throw new WorldOrbitError(`Unknown event positions field "${tokens[0].value}"`, line, tokens[0]?.column ?? 1);
4960
+ }
4961
+ const objectId = tokens[1].value;
4962
+ if (!objectId.trim()) {
4963
+ throw new WorldOrbitError("Event pose object id must not be empty", line, tokens[1].column);
4964
+ }
4965
+ const rawPose = {
4966
+ objectId,
4967
+ fields: [],
4968
+ location: { line, column: tokens[0].column }
4969
+ };
4970
+ section.rawPoses.push(rawPose);
4971
+ section.activePose = rawPose;
4972
+ section.poseIndent = indent;
4973
+ section.activePoseSeenFields = /* @__PURE__ */ new Set();
4974
+ return;
4975
+ }
4976
+ if (tokens.length === 1 && tokens[0].value.toLowerCase() === "positions") {
4977
+ if (section.seenFields.has("positions")) {
4978
+ throw new WorldOrbitError('Duplicate event field "positions"', line, tokens[0].column);
4979
+ }
4980
+ section.seenFields.add("positions");
4981
+ section.inPositions = true;
4982
+ section.positionsIndent = indent;
4983
+ return;
4984
+ }
4985
+ const key = requireUniqueField(tokens, section.seenFields, line);
4986
+ switch (key) {
4987
+ case "kind":
4988
+ section.event.kind = joinFieldValue(tokens, line);
4989
+ return;
4990
+ case "label":
4991
+ section.event.label = joinFieldValue(tokens, line);
4992
+ return;
4993
+ case "summary":
4994
+ section.event.summary = joinFieldValue(tokens, line);
4995
+ return;
4996
+ case "target":
4997
+ section.event.targetObjectId = joinFieldValue(tokens, line);
4998
+ return;
4999
+ case "participants":
5000
+ section.event.participantObjectIds = parseTokenList(tokens.slice(1), line, "participants");
5001
+ return;
5002
+ case "timing":
5003
+ section.event.timing = joinFieldValue(tokens, line);
5004
+ return;
5005
+ case "visibility":
5006
+ section.event.visibility = joinFieldValue(tokens, line);
5007
+ return;
5008
+ case "tags":
5009
+ section.event.tags = parseTokenList(tokens.slice(1), line, "tags");
5010
+ return;
5011
+ case "color":
5012
+ section.event.color = joinFieldValue(tokens, line);
5013
+ return;
5014
+ case "hidden":
5015
+ section.event.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
5016
+ line,
5017
+ column: tokens[0].column
5018
+ });
5019
+ return;
5020
+ default:
5021
+ throw new WorldOrbitError(`Unknown event field "${tokens[0].value}"`, line, tokens[0].column);
5022
+ }
5023
+ }
5024
+ function parseEventPoseField(tokens, line, seenFields) {
5025
+ if (tokens.length < 2) {
5026
+ throw new WorldOrbitError("Invalid event pose field line", line, tokens[0]?.column ?? 1);
5027
+ }
5028
+ const key = tokens[0].value;
5029
+ if (!EVENT_POSE_FIELD_KEYS.has(key)) {
5030
+ throw new WorldOrbitError(`Unknown event pose field "${key}"`, line, tokens[0].column);
5031
+ }
5032
+ if (seenFields.has(key)) {
5033
+ throw new WorldOrbitError(`Duplicate event pose field "${key}"`, line, tokens[0].column);
5034
+ }
5035
+ seenFields.add(key);
5036
+ return {
5037
+ type: "field",
5038
+ key,
5039
+ values: tokens.slice(1).map((token) => token.value),
5040
+ location: { line, column: tokens[0].column }
5041
+ };
5042
+ }
4464
5043
  function applyObjectField(section, indent, tokens, line) {
4465
5044
  if (section.activeBlock && indent <= (section.blockIndent ?? 0)) {
4466
5045
  section.activeBlock = null;
@@ -4519,7 +5098,7 @@ var WorldOrbit = (() => {
4519
5098
  function parseObjectTypeTokens(tokens, line) {
4520
5099
  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");
4521
5100
  }
4522
- function parseLayerTokens(tokens, line) {
5101
+ function parseLayerTokens(tokens, line, sourceSchemaVersion, diagnostics) {
4523
5102
  const layers = {};
4524
5103
  for (const token of parseTokenList(tokens, line, "layers")) {
4525
5104
  const enabled = !token.startsWith("-") && !token.startsWith("!");
@@ -4529,7 +5108,13 @@ var WorldOrbit = (() => {
4529
5108
  layers["orbits-front"] = enabled;
4530
5109
  continue;
4531
5110
  }
4532
- if (raw === "background" || raw === "guides" || raw === "orbits-back" || raw === "orbits-front" || raw === "relations" || raw === "objects" || raw === "labels" || raw === "metadata") {
5111
+ if (raw === "background" || raw === "guides" || raw === "orbits-back" || raw === "orbits-front" || raw === "relations" || raw === "events" || raw === "objects" || raw === "labels" || raw === "metadata") {
5112
+ if (raw === "events" && sourceSchemaVersion && diagnostics) {
5113
+ warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "layers.events", {
5114
+ line,
5115
+ column: tokens[0]?.column ?? 1
5116
+ });
5117
+ }
4533
5118
  layers[raw] = enabled;
4534
5119
  }
4535
5120
  }
@@ -4668,7 +5253,7 @@ var WorldOrbit = (() => {
4668
5253
  }
4669
5254
  function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
4670
5255
  const fieldMap = collectDraftFields(node.fields);
4671
- const placement = extractDraftPlacement(node.objectType, fieldMap);
5256
+ const placement = extractPlacementFromFieldMap(fieldMap);
4672
5257
  const properties = normalizeDraftProperties(node.objectType, fieldMap);
4673
5258
  const groups = parseOptionalTokenList(fieldMap.get("groups")?.[0]);
4674
5259
  const epoch = parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]);
@@ -4720,6 +5305,24 @@ var WorldOrbit = (() => {
4720
5305
  }
4721
5306
  return object;
4722
5307
  }
5308
+ function normalizeDraftEvent(event, rawPoses) {
5309
+ return {
5310
+ ...event,
5311
+ participantObjectIds: [...new Set(event.participantObjectIds)],
5312
+ tags: [...new Set(event.tags)],
5313
+ positions: rawPoses.map((pose) => normalizeDraftEventPose(pose))
5314
+ };
5315
+ }
5316
+ function normalizeDraftEventPose(rawPose) {
5317
+ const fieldMap = collectDraftFields(rawPose.fields);
5318
+ const placement = extractPlacementFromFieldMap(fieldMap);
5319
+ return {
5320
+ objectId: rawPose.objectId,
5321
+ placement,
5322
+ inner: parseOptionalUnitField(fieldMap.get("inner")?.[0], "inner"),
5323
+ outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer")
5324
+ };
5325
+ }
4723
5326
  function collectDraftFields(fields) {
4724
5327
  const grouped = /* @__PURE__ */ new Map();
4725
5328
  for (const field of fields) {
@@ -4736,7 +5339,7 @@ var WorldOrbit = (() => {
4736
5339
  }
4737
5340
  return grouped;
4738
5341
  }
4739
- function extractDraftPlacement(objectType, fieldMap) {
5342
+ function extractPlacementFromFieldMap(fieldMap) {
4740
5343
  const orbitField = fieldMap.get("orbit")?.[0];
4741
5344
  const atField = fieldMap.get("at")?.[0];
4742
5345
  const surfaceField = fieldMap.get("surface")?.[0];
@@ -5029,6 +5632,7 @@ var WorldOrbit = (() => {
5029
5632
  },
5030
5633
  groups: [],
5031
5634
  relations: [],
5635
+ events: [],
5032
5636
  objects: [],
5033
5637
  diagnostics: []
5034
5638
  };
@@ -5055,6 +5659,12 @@ var WorldOrbit = (() => {
5055
5659
  for (const relation of [...document.relations].sort(compareIdLike2)) {
5056
5660
  paths.push({ kind: "relation", id: relation.id });
5057
5661
  }
5662
+ for (const event of [...document.events].sort(compareIdLike2)) {
5663
+ paths.push({ kind: "event", id: event.id });
5664
+ for (const pose of [...event.positions].sort(comparePoseObjectId2)) {
5665
+ paths.push({ kind: "event-pose", id: event.id, key: pose.objectId });
5666
+ }
5667
+ }
5058
5668
  for (const object of [...document.objects].sort(compareIdLike2)) {
5059
5669
  paths.push({ kind: "object", id: object.id });
5060
5670
  }
@@ -5070,6 +5680,10 @@ var WorldOrbit = (() => {
5070
5680
  return path.key ? document.system?.atlasMetadata[path.key] ?? null : null;
5071
5681
  case "group":
5072
5682
  return path.id ? findGroup(document, path.id) : null;
5683
+ case "event":
5684
+ return path.id ? findEvent(document, path.id) : null;
5685
+ case "event-pose":
5686
+ return path.id && path.key ? findEventPose(document, path.id, path.key) : null;
5073
5687
  case "object":
5074
5688
  return path.id ? findObject(document, path.id) : null;
5075
5689
  case "viewpoint":
@@ -5109,6 +5723,18 @@ var WorldOrbit = (() => {
5109
5723
  }
5110
5724
  upsertById(next.groups, value);
5111
5725
  return next;
5726
+ case "event":
5727
+ if (!path.id) {
5728
+ throw new Error('Event updates require an "id" value.');
5729
+ }
5730
+ upsertById(next.events, value);
5731
+ return next;
5732
+ case "event-pose":
5733
+ if (!path.id || !path.key) {
5734
+ throw new Error('Event pose updates require an event "id" and pose "key" value.');
5735
+ }
5736
+ upsertEventPose(next.events, path.id, value);
5737
+ return next;
5112
5738
  case "object":
5113
5739
  if (!path.id) {
5114
5740
  throw new Error('Object updates require an "id" value.');
@@ -5157,6 +5783,19 @@ var WorldOrbit = (() => {
5157
5783
  next.groups = next.groups.filter((group) => group.id !== path.id);
5158
5784
  }
5159
5785
  return next;
5786
+ case "event":
5787
+ if (path.id) {
5788
+ next.events = next.events.filter((event) => event.id !== path.id);
5789
+ }
5790
+ return next;
5791
+ case "event-pose":
5792
+ if (path.id && path.key) {
5793
+ const event = findEvent(next, path.id);
5794
+ if (event) {
5795
+ event.positions = event.positions.filter((pose) => pose.objectId !== path.key);
5796
+ }
5797
+ }
5798
+ return next;
5160
5799
  case "viewpoint":
5161
5800
  if (path.id) {
5162
5801
  system.viewpoints = system.viewpoints.filter((viewpoint) => viewpoint.id !== path.id);
@@ -5225,6 +5864,22 @@ var WorldOrbit = (() => {
5225
5864
  };
5226
5865
  }
5227
5866
  }
5867
+ if (diagnostic.field?.startsWith("event.")) {
5868
+ const parts = diagnostic.field.split(".");
5869
+ if (parts[1] && findEvent(document, parts[1])) {
5870
+ if (parts[2] === "pose" && parts[3] && findEventPose(document, parts[1], parts[3])) {
5871
+ return {
5872
+ kind: "event-pose",
5873
+ id: parts[1],
5874
+ key: parts[3]
5875
+ };
5876
+ }
5877
+ return {
5878
+ kind: "event",
5879
+ id: parts[1]
5880
+ };
5881
+ }
5882
+ }
5228
5883
  if (diagnostic.field && diagnostic.field in ensureSystem(document).atlasMetadata) {
5229
5884
  return {
5230
5885
  kind: "metadata",
@@ -5256,6 +5911,12 @@ var WorldOrbit = (() => {
5256
5911
  function findRelation(document, relationId) {
5257
5912
  return document.relations.find((relation) => relation.id === relationId) ?? null;
5258
5913
  }
5914
+ function findEvent(document, eventId) {
5915
+ return document.events.find((event) => event.id === eventId) ?? null;
5916
+ }
5917
+ function findEventPose(document, eventId, objectId) {
5918
+ return findEvent(document, eventId)?.positions.find((pose) => pose.objectId === objectId) ?? null;
5919
+ }
5259
5920
  function findViewpoint(system, viewpointId) {
5260
5921
  return system?.viewpoints.find((viewpoint) => viewpoint.id === viewpointId) ?? null;
5261
5922
  }
@@ -5271,9 +5932,25 @@ var WorldOrbit = (() => {
5271
5932
  }
5272
5933
  items[index] = value;
5273
5934
  }
5935
+ function upsertEventPose(events, eventId, value) {
5936
+ const event = events.find((entry) => entry.id === eventId);
5937
+ if (!event) {
5938
+ throw new Error(`Unknown event "${eventId}" for pose update.`);
5939
+ }
5940
+ const index = event.positions.findIndex((entry) => entry.objectId === value.objectId);
5941
+ if (index === -1) {
5942
+ event.positions.push(value);
5943
+ event.positions.sort(comparePoseObjectId2);
5944
+ return;
5945
+ }
5946
+ event.positions[index] = value;
5947
+ }
5274
5948
  function compareIdLike2(left, right) {
5275
5949
  return left.id.localeCompare(right.id);
5276
5950
  }
5951
+ function comparePoseObjectId2(left, right) {
5952
+ return left.objectId.localeCompare(right.objectId);
5953
+ }
5277
5954
 
5278
5955
  // packages/core/dist/load.js
5279
5956
  var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;