worldorbit 3.2.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +543 -543
  2. package/dist/browser/core/dist/atlas-edit.js +146 -1
  3. package/dist/browser/core/dist/atlas-validate.js +105 -10
  4. package/dist/browser/core/dist/draft-parse.js +461 -17
  5. package/dist/browser/core/dist/draft.d.ts +2 -1
  6. package/dist/browser/core/dist/draft.js +26 -4
  7. package/dist/browser/core/dist/format.js +126 -5
  8. package/dist/browser/core/dist/index.d.ts +1 -0
  9. package/dist/browser/core/dist/index.js +1 -0
  10. package/dist/browser/core/dist/load.js +12 -2
  11. package/dist/browser/core/dist/normalize.js +1 -0
  12. package/dist/browser/core/dist/scene.js +226 -5
  13. package/dist/browser/core/dist/schema.js +11 -1
  14. package/dist/browser/core/dist/solver.d.ts +33 -0
  15. package/dist/browser/core/dist/solver.js +99 -0
  16. package/dist/browser/core/dist/spatial-scene.js +56 -0
  17. package/dist/browser/core/dist/types.d.ts +130 -4
  18. package/dist/browser/editor/dist/editor.js +844 -719
  19. package/dist/browser/editor/dist/types.d.ts +2 -1
  20. package/dist/browser/viewer/dist/minimap.js +9 -7
  21. package/dist/browser/viewer/dist/render.js +78 -18
  22. package/dist/browser/viewer/dist/runtime-3d.js +2 -0
  23. package/dist/browser/viewer/dist/theme.js +1 -0
  24. package/dist/browser/viewer/dist/types.d.ts +7 -0
  25. package/dist/browser/viewer/dist/viewer.js +34 -3
  26. package/dist/obsidian-plugin/README.md +141 -124
  27. package/dist/obsidian-plugin/main.js +82 -68
  28. package/dist/unpkg/core/dist/atlas-edit.js +146 -1
  29. package/dist/unpkg/core/dist/atlas-validate.js +105 -10
  30. package/dist/unpkg/core/dist/draft-parse.js +461 -17
  31. package/dist/unpkg/core/dist/draft.d.ts +2 -1
  32. package/dist/unpkg/core/dist/draft.js +26 -4
  33. package/dist/unpkg/core/dist/format.js +126 -5
  34. package/dist/unpkg/core/dist/index.d.ts +1 -0
  35. package/dist/unpkg/core/dist/index.js +1 -0
  36. package/dist/unpkg/core/dist/load.js +12 -2
  37. package/dist/unpkg/core/dist/normalize.js +1 -0
  38. package/dist/unpkg/core/dist/scene.js +226 -5
  39. package/dist/unpkg/core/dist/schema.js +11 -1
  40. package/dist/unpkg/core/dist/solver.d.ts +33 -0
  41. package/dist/unpkg/core/dist/solver.js +99 -0
  42. package/dist/unpkg/core/dist/spatial-scene.js +56 -0
  43. package/dist/unpkg/core/dist/types.d.ts +130 -4
  44. package/dist/unpkg/editor/dist/editor.js +844 -719
  45. package/dist/unpkg/editor/dist/types.d.ts +2 -1
  46. package/dist/unpkg/viewer/dist/minimap.js +9 -7
  47. package/dist/unpkg/viewer/dist/render.js +78 -18
  48. package/dist/unpkg/viewer/dist/runtime-3d.js +2 -0
  49. package/dist/unpkg/viewer/dist/theme.js +1 -0
  50. package/dist/unpkg/viewer/dist/types.d.ts +7 -0
  51. package/dist/unpkg/viewer/dist/viewer.js +34 -3
  52. package/dist/unpkg/worldorbit-core.min.js +12 -12
  53. package/dist/unpkg/worldorbit-editor.min.js +381 -340
  54. package/dist/unpkg/worldorbit-markdown.min.js +47 -33
  55. package/dist/unpkg/worldorbit-viewer.min.js +238 -224
  56. package/dist/unpkg/worldorbit.js +1218 -46
  57. package/dist/unpkg/worldorbit.min.js +242 -228
  58. package/package.json +5 -1
  59. package/packages/core/dist/atlas-edit.js +146 -1
  60. package/packages/core/dist/atlas-validate.js +105 -10
  61. package/packages/core/dist/draft-parse.js +461 -17
  62. package/packages/core/dist/draft.d.ts +2 -1
  63. package/packages/core/dist/draft.js +26 -4
  64. package/packages/core/dist/format.js +126 -5
  65. package/packages/core/dist/index.d.ts +1 -0
  66. package/packages/core/dist/index.js +1 -0
  67. package/packages/core/dist/load.js +12 -2
  68. package/packages/core/dist/normalize.js +1 -0
  69. package/packages/core/dist/scene.js +226 -5
  70. package/packages/core/dist/schema.js +11 -1
  71. package/packages/core/dist/solver.d.ts +33 -0
  72. package/packages/core/dist/solver.js +99 -0
  73. package/packages/core/dist/spatial-scene.js +56 -0
  74. package/packages/core/dist/types.d.ts +130 -4
  75. package/packages/editor/dist/editor.js +844 -719
  76. package/packages/editor/dist/types.d.ts +2 -1
  77. package/packages/viewer/dist/minimap.js +9 -7
  78. package/packages/viewer/dist/render.js +78 -18
  79. package/packages/viewer/dist/runtime-3d.js +2 -0
  80. package/packages/viewer/dist/theme.js +1 -0
  81. package/packages/viewer/dist/types.d.ts +7 -0
  82. package/packages/viewer/dist/viewer.js +34 -3
@@ -8,6 +8,14 @@ const STRUCTURED_TYPED_BLOCKS = new Set([
8
8
  "habitability",
9
9
  "settlement",
10
10
  ]);
11
+ const TRAJECTORY_SEGMENT_KINDS = new Set([
12
+ "departure",
13
+ "transfer",
14
+ "flyby",
15
+ "capture",
16
+ "stationkeeping",
17
+ "escape",
18
+ ]);
11
19
  const DRAFT_OBJECT_FIELD_SPECS = new Map();
12
20
  for (const key of [
13
21
  "orbit",
@@ -40,6 +48,7 @@ for (const key of [
40
48
  "on",
41
49
  "source",
42
50
  "cycle",
51
+ "trajectory",
43
52
  ]) {
44
53
  const schema = getFieldSchema(key);
45
54
  if (schema) {
@@ -65,10 +74,11 @@ for (const spec of [
65
74
  { key: "validate", inlineMode: "single", allowRepeat: true },
66
75
  { key: "locked", inlineMode: "multiple", allowRepeat: false },
67
76
  { key: "tolerance", inlineMode: "pair", allowRepeat: true },
77
+ { key: "trajectory", inlineMode: "single", allowRepeat: false },
68
78
  ]) {
69
79
  DRAFT_OBJECT_FIELD_SPECS.set(spec.key, {
70
80
  key: spec.key,
71
- version: "2.1",
81
+ version: spec.key === "trajectory" ? "3.0" : "2.1",
72
82
  inlineMode: spec.inlineMode,
73
83
  allowRepeat: spec.allowRepeat,
74
84
  });
@@ -90,6 +100,8 @@ const EVENT_POSE_FIELD_KEYS = new Set([
90
100
  "outer",
91
101
  "epoch",
92
102
  "referencePlane",
103
+ "segment",
104
+ "maneuver",
93
105
  ]);
94
106
  export function parseWorldOrbitAtlas(source) {
95
107
  return parseAtlasSource(source);
@@ -109,6 +121,7 @@ function parseAtlasSource(source, forcedOutputVersion) {
109
121
  const groups = [];
110
122
  const relations = [];
111
123
  const events = [];
124
+ const trajectories = [];
112
125
  const eventPoseNodes = new Map();
113
126
  let sawDefaults = false;
114
127
  let sawAtlas = false;
@@ -117,6 +130,7 @@ function parseAtlasSource(source, forcedOutputVersion) {
117
130
  const groupIds = new Set();
118
131
  const relationIds = new Set();
119
132
  const eventIds = new Set();
133
+ const trajectoryIds = new Set();
120
134
  for (let index = 0; index < lines.length; index++) {
121
135
  const rawLine = lines[index];
122
136
  const lineNumber = index + 1;
@@ -147,7 +161,7 @@ function parseAtlasSource(source, forcedOutputVersion) {
147
161
  continue;
148
162
  }
149
163
  if (indent === 0) {
150
- section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, { sawDefaults, sawAtlas });
164
+ section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, trajectories, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, trajectoryIds, { sawDefaults, sawAtlas });
151
165
  if (section.kind === "system") {
152
166
  system = section.system;
153
167
  }
@@ -165,7 +179,7 @@ function parseAtlasSource(source, forcedOutputVersion) {
165
179
  handleSectionLine(section, indent, tokens, lineNumber);
166
180
  }
167
181
  if (!sawSchemaHeader) {
168
- throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
182
+ throw new WorldOrbitError('Missing required atlas schema header "schema 2.0", "schema 3.0", or "schema 3.1"');
169
183
  }
170
184
  const objects = objectNodes.map((node) => normalizeDraftObject(node, sourceSchemaVersion, diagnostics));
171
185
  const normalizedEvents = events.map((event) => normalizeDraftEvent(event, eventPoseNodes.get(event.id) ?? []));
@@ -179,6 +193,7 @@ function parseAtlasSource(source, forcedOutputVersion) {
179
193
  groups,
180
194
  relations,
181
195
  events: normalizedEvents,
196
+ trajectories,
182
197
  objects,
183
198
  diagnostics,
184
199
  };
@@ -210,21 +225,25 @@ function parseAtlasSource(source, forcedOutputVersion) {
210
225
  function assertDraftSchemaHeader(tokens, line) {
211
226
  if (tokens.length !== 2 ||
212
227
  tokens[0].value.toLowerCase() !== "schema" ||
213
- !["2.0-draft", "2.0", "2.1", "2.5", "2.6"].includes(tokens[1].value.toLowerCase())) {
214
- throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", "schema 2.5", "schema 2.6", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
228
+ !["2.0-draft", "2.0", "2.1", "2.5", "2.6", "3.0", "3.1"].includes(tokens[1].value.toLowerCase())) {
229
+ throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", "schema 2.5", "schema 2.6", "schema 3.0", "schema 3.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
215
230
  }
216
231
  const version = tokens[1].value.toLowerCase();
217
232
  return version === "2.6"
218
233
  ? "2.6"
219
- : version === "2.5"
220
- ? "2.5"
221
- : version === "2.1"
222
- ? "2.1"
223
- : version === "2.0-draft"
224
- ? "2.0-draft"
225
- : "2.0";
226
- }
227
- function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, flags) {
234
+ : version === "3.0"
235
+ ? "3.0"
236
+ : version === "3.1"
237
+ ? "3.1"
238
+ : version === "2.5"
239
+ ? "2.5"
240
+ : version === "2.1"
241
+ ? "2.1"
242
+ : version === "2.0-draft"
243
+ ? "2.0-draft"
244
+ : "2.0";
245
+ }
246
+ function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, trajectories, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, trajectoryIds, flags) {
228
247
  const keyword = tokens[0]?.value.toLowerCase();
229
248
  switch (keyword) {
230
249
  case "system":
@@ -278,6 +297,9 @@ function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, sy
278
297
  case "event":
279
298
  warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "event", { line, column: tokens[0].column });
280
299
  return startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourceSchemaVersion, diagnostics);
300
+ case "trajectory":
301
+ warnIfSchema30Feature(sourceSchemaVersion, diagnostics, "trajectory", { line, column: tokens[0].column });
302
+ return startTrajectorySection(tokens, line, trajectories, trajectoryIds, sourceSchemaVersion, diagnostics);
281
303
  case "object":
282
304
  return startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes);
283
305
  default:
@@ -485,6 +507,51 @@ function startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourc
485
507
  activePoseSeenFields: new Set(),
486
508
  };
487
509
  }
510
+ function startTrajectorySection(tokens, line, trajectories, trajectoryIds, sourceSchemaVersion, diagnostics) {
511
+ if (tokens.length !== 2) {
512
+ throw new WorldOrbitError("Invalid trajectory declaration", line, tokens[0]?.column ?? 1);
513
+ }
514
+ const id = normalizeIdentifier(tokens[1].value);
515
+ if (!id) {
516
+ throw new WorldOrbitError("Trajectory id must not be empty", line, tokens[1].column);
517
+ }
518
+ if (trajectoryIds.has(id)) {
519
+ throw new WorldOrbitError(`Duplicate trajectory id "${id}"`, line, tokens[1].column);
520
+ }
521
+ const trajectory = {
522
+ id,
523
+ label: humanizeIdentifier(id),
524
+ summary: null,
525
+ craftObjectId: null,
526
+ tags: [],
527
+ color: null,
528
+ renderMode: null,
529
+ stroke: null,
530
+ strokeWidth: null,
531
+ marker: null,
532
+ labelMode: null,
533
+ showWaypoints: null,
534
+ hidden: false,
535
+ segments: [],
536
+ };
537
+ trajectories.push(trajectory);
538
+ trajectoryIds.add(id);
539
+ return {
540
+ kind: "trajectory",
541
+ trajectory,
542
+ sourceSchemaVersion,
543
+ diagnostics,
544
+ seenFields: new Set(),
545
+ inSegment: false,
546
+ segmentIndent: null,
547
+ activeSegment: null,
548
+ activeSegmentSeenFields: new Set(),
549
+ inManeuver: false,
550
+ maneuverIndent: null,
551
+ activeManeuver: null,
552
+ activeManeuverSeenFields: new Set(),
553
+ };
554
+ }
488
555
  function startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes) {
489
556
  if (tokens.length < 3) {
490
557
  throw new WorldOrbitError("Invalid atlas object declaration", line, tokens[0]?.column ?? 1);
@@ -544,6 +611,9 @@ function handleSectionLine(section, indent, tokens, line) {
544
611
  case "event":
545
612
  applyEventField(section, indent, tokens, line);
546
613
  return;
614
+ case "trajectory":
615
+ applyTrajectoryField(section, indent, tokens, line);
616
+ return;
547
617
  case "object":
548
618
  applyObjectField(section, indent, tokens, line);
549
619
  return;
@@ -863,6 +933,13 @@ function applyEventField(section, indent, tokens, line) {
863
933
  column: tokens[0]?.column ?? 1,
864
934
  });
865
935
  }
936
+ if (tokens[0]?.value === "segment" ||
937
+ tokens[0]?.value === "maneuver") {
938
+ warnIfSchema30Feature(section.sourceSchemaVersion, section.diagnostics, `pose.${tokens[0].value}`, {
939
+ line,
940
+ column: tokens[0]?.column ?? 1,
941
+ });
942
+ }
866
943
  section.activePose.fields.push(parseEventPoseField(tokens, line, section.activePoseSeenFields));
867
944
  return;
868
945
  }
@@ -905,6 +982,13 @@ function applyEventField(section, indent, tokens, line) {
905
982
  case "summary":
906
983
  section.event.summary = joinFieldValue(tokens, line);
907
984
  return;
985
+ case "trajectory":
986
+ warnIfSchema30Feature(section.sourceSchemaVersion, section.diagnostics, "event.trajectory", {
987
+ line,
988
+ column: tokens[0].column,
989
+ });
990
+ section.event.trajectoryId = joinFieldValue(tokens, line);
991
+ return;
908
992
  case "target":
909
993
  section.event.targetObjectId = joinFieldValue(tokens, line);
910
994
  return;
@@ -947,6 +1031,302 @@ function applyEventField(section, indent, tokens, line) {
947
1031
  throw new WorldOrbitError(`Unknown event field "${tokens[0].value}"`, line, tokens[0].column);
948
1032
  }
949
1033
  }
1034
+ function applyTrajectoryField(section, indent, tokens, line) {
1035
+ if (section.activeManeuver && indent <= (section.maneuverIndent ?? 0)) {
1036
+ section.activeManeuver = null;
1037
+ section.maneuverIndent = null;
1038
+ section.activeManeuverSeenFields.clear();
1039
+ section.inManeuver = false;
1040
+ }
1041
+ if (section.activeSegment && indent <= (section.segmentIndent ?? 0)) {
1042
+ section.activeSegment = null;
1043
+ section.segmentIndent = null;
1044
+ section.activeSegmentSeenFields.clear();
1045
+ section.inSegment = false;
1046
+ }
1047
+ if (section.activeManeuver) {
1048
+ applyTrajectoryManeuverField(section, tokens, line);
1049
+ return;
1050
+ }
1051
+ if (section.activeSegment) {
1052
+ if (tokens[0]?.value.toLowerCase() === "maneuver") {
1053
+ if (tokens.length !== 2) {
1054
+ throw new WorldOrbitError("Invalid trajectory maneuver declaration", line, tokens[0]?.column ?? 1);
1055
+ }
1056
+ const id = normalizeIdentifier(tokens[1].value);
1057
+ if (!id) {
1058
+ throw new WorldOrbitError("Trajectory maneuver id must not be empty", line, tokens[1].column);
1059
+ }
1060
+ if (section.activeSegment.maneuvers.some((maneuver) => maneuver.id === id)) {
1061
+ throw new WorldOrbitError(`Duplicate trajectory maneuver id "${id}"`, line, tokens[1].column);
1062
+ }
1063
+ const maneuver = {
1064
+ id,
1065
+ kind: "burn",
1066
+ label: null,
1067
+ epoch: null,
1068
+ notes: [],
1069
+ };
1070
+ section.activeSegment.maneuvers.push(maneuver);
1071
+ section.activeManeuver = maneuver;
1072
+ section.inManeuver = true;
1073
+ section.maneuverIndent = indent;
1074
+ section.activeManeuverSeenFields = new Set();
1075
+ return;
1076
+ }
1077
+ applyTrajectorySegmentField(section, tokens, line);
1078
+ return;
1079
+ }
1080
+ if (tokens[0]?.value.toLowerCase() === "segment") {
1081
+ if (tokens.length !== 2) {
1082
+ throw new WorldOrbitError("Invalid trajectory segment declaration", line, tokens[0]?.column ?? 1);
1083
+ }
1084
+ const id = normalizeIdentifier(tokens[1].value);
1085
+ if (!id) {
1086
+ throw new WorldOrbitError("Trajectory segment id must not be empty", line, tokens[1].column);
1087
+ }
1088
+ if (section.trajectory.segments.some((segment) => segment.id === id)) {
1089
+ throw new WorldOrbitError(`Duplicate trajectory segment id "${id}"`, line, tokens[1].column);
1090
+ }
1091
+ const segment = {
1092
+ id,
1093
+ kind: "transfer",
1094
+ label: null,
1095
+ summary: null,
1096
+ fromObjectId: null,
1097
+ toObjectId: null,
1098
+ aroundObjectId: null,
1099
+ assist: null,
1100
+ epoch: null,
1101
+ waypointLabel: null,
1102
+ waypointDate: null,
1103
+ renderHidden: null,
1104
+ sampleDensity: null,
1105
+ notes: [],
1106
+ maneuvers: [],
1107
+ };
1108
+ section.trajectory.segments.push(segment);
1109
+ section.activeSegment = {
1110
+ id,
1111
+ fields: [],
1112
+ maneuvers: segment.maneuvers,
1113
+ assist: null,
1114
+ location: { line, column: tokens[0].column },
1115
+ };
1116
+ section.inSegment = true;
1117
+ section.segmentIndent = indent;
1118
+ section.activeSegmentSeenFields = new Set();
1119
+ return;
1120
+ }
1121
+ const key = requireUniqueField(tokens, section.seenFields, line);
1122
+ const value = joinFieldValue(tokens, line);
1123
+ switch (key) {
1124
+ case "label":
1125
+ section.trajectory.label = value;
1126
+ return;
1127
+ case "summary":
1128
+ section.trajectory.summary = value;
1129
+ return;
1130
+ case "craft":
1131
+ section.trajectory.craftObjectId = value;
1132
+ return;
1133
+ case "tags":
1134
+ section.trajectory.tags = parseTokenList(tokens.slice(1), line, "tags");
1135
+ return;
1136
+ case "color":
1137
+ section.trajectory.color = value;
1138
+ return;
1139
+ case "rendermode":
1140
+ warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "trajectory.renderMode", {
1141
+ line,
1142
+ column: tokens[0].column,
1143
+ });
1144
+ if (value !== "illustrative" && value !== "solver" && value !== "auto") {
1145
+ throw new WorldOrbitError(`Unknown trajectory render mode "${value}"`, line, tokens[0].column);
1146
+ }
1147
+ section.trajectory.renderMode = value;
1148
+ return;
1149
+ case "stroke":
1150
+ warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "trajectory.stroke", {
1151
+ line,
1152
+ column: tokens[0].column,
1153
+ });
1154
+ section.trajectory.stroke = value;
1155
+ return;
1156
+ case "strokewidth":
1157
+ warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "trajectory.strokeWidth", {
1158
+ line,
1159
+ column: tokens[0].column,
1160
+ });
1161
+ section.trajectory.strokeWidth = parsePositiveNumber(value, line, tokens[0].column, "strokeWidth");
1162
+ return;
1163
+ case "marker":
1164
+ warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "trajectory.marker", {
1165
+ line,
1166
+ column: tokens[0].column,
1167
+ });
1168
+ section.trajectory.marker = value;
1169
+ return;
1170
+ case "labelmode":
1171
+ warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "trajectory.labelMode", {
1172
+ line,
1173
+ column: tokens[0].column,
1174
+ });
1175
+ section.trajectory.labelMode = value;
1176
+ return;
1177
+ case "showwaypoints":
1178
+ warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "trajectory.showWaypoints", {
1179
+ line,
1180
+ column: tokens[0].column,
1181
+ });
1182
+ section.trajectory.showWaypoints = parseAtlasBoolean(value, "showWaypoints", {
1183
+ line,
1184
+ column: tokens[0].column,
1185
+ });
1186
+ return;
1187
+ case "hidden":
1188
+ section.trajectory.hidden = parseAtlasBoolean(value, "hidden", {
1189
+ line,
1190
+ column: tokens[0].column,
1191
+ });
1192
+ return;
1193
+ default:
1194
+ throw new WorldOrbitError(`Unknown trajectory field "${tokens[0].value}"`, line, tokens[0].column);
1195
+ }
1196
+ }
1197
+ function applyTrajectorySegmentField(section, tokens, line) {
1198
+ const segment = section.activeSegment;
1199
+ if (!segment) {
1200
+ return;
1201
+ }
1202
+ const key = requireUniqueField(tokens, section.activeSegmentSeenFields, line);
1203
+ const value = joinFieldValue(tokens, line);
1204
+ const target = section.trajectory.segments.find((entry) => entry.id === segment.id);
1205
+ switch (key) {
1206
+ case "kind": {
1207
+ const normalized = value.toLowerCase();
1208
+ if (!TRAJECTORY_SEGMENT_KINDS.has(normalized)) {
1209
+ throw new WorldOrbitError(`Unknown trajectory segment kind "${value}"`, line, tokens[0].column);
1210
+ }
1211
+ target.kind = normalized;
1212
+ return;
1213
+ }
1214
+ case "label":
1215
+ target.label = value;
1216
+ return;
1217
+ case "summary":
1218
+ target.summary = value;
1219
+ return;
1220
+ case "from":
1221
+ target.fromObjectId = value;
1222
+ return;
1223
+ case "to":
1224
+ target.toObjectId = value;
1225
+ return;
1226
+ case "around":
1227
+ target.aroundObjectId = value;
1228
+ return;
1229
+ case "assist":
1230
+ target.assist = {
1231
+ objectId: value,
1232
+ notes: [],
1233
+ };
1234
+ return;
1235
+ case "epoch":
1236
+ target.epoch = value;
1237
+ return;
1238
+ case "periapsis":
1239
+ target.periapsis = parseAtlasUnitValue(value, { line, column: tokens[0].column }, "periapsis");
1240
+ return;
1241
+ case "apoapsis":
1242
+ target.apoapsis = parseAtlasUnitValue(value, { line, column: tokens[0].column }, "apoapsis");
1243
+ return;
1244
+ case "inclination":
1245
+ target.inclination = parseAtlasUnitValue(value, { line, column: tokens[0].column }, "inclination");
1246
+ return;
1247
+ case "duration":
1248
+ target.duration = parseAtlasUnitValue(value, { line, column: tokens[0].column }, "duration");
1249
+ return;
1250
+ case "deltav":
1251
+ target.deltaV = parseAtlasUnitValue(value, { line, column: tokens[0].column });
1252
+ return;
1253
+ case "phaseangle":
1254
+ target.phaseAngle = parseAtlasUnitValue(value, { line, column: tokens[0].column }, "phaseAngle");
1255
+ return;
1256
+ case "turnangle":
1257
+ target.turnAngle = parseAtlasUnitValue(value, { line, column: tokens[0].column }, "turnAngle");
1258
+ return;
1259
+ case "energy":
1260
+ target.energy = parseAtlasUnitValue(value, { line, column: tokens[0].column });
1261
+ return;
1262
+ case "waypointlabel":
1263
+ warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "segment.waypointLabel", {
1264
+ line,
1265
+ column: tokens[0].column,
1266
+ });
1267
+ target.waypointLabel = value;
1268
+ return;
1269
+ case "waypointdate":
1270
+ warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "segment.waypointDate", {
1271
+ line,
1272
+ column: tokens[0].column,
1273
+ });
1274
+ target.waypointDate = value;
1275
+ return;
1276
+ case "renderhidden":
1277
+ warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "segment.renderHidden", {
1278
+ line,
1279
+ column: tokens[0].column,
1280
+ });
1281
+ target.renderHidden = parseAtlasBoolean(value, "renderHidden", {
1282
+ line,
1283
+ column: tokens[0].column,
1284
+ });
1285
+ return;
1286
+ case "sampledensity":
1287
+ warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "segment.sampleDensity", {
1288
+ line,
1289
+ column: tokens[0].column,
1290
+ });
1291
+ target.sampleDensity = parsePositiveNumber(value, line, tokens[0].column, "sampleDensity");
1292
+ return;
1293
+ case "notes":
1294
+ target.notes = parseTokenList(tokens.slice(1), line, "notes");
1295
+ return;
1296
+ default:
1297
+ throw new WorldOrbitError(`Unknown trajectory segment field "${tokens[0].value}"`, line, tokens[0].column);
1298
+ }
1299
+ }
1300
+ function applyTrajectoryManeuverField(section, tokens, line) {
1301
+ const maneuver = section.activeManeuver;
1302
+ if (!maneuver) {
1303
+ return;
1304
+ }
1305
+ const key = requireUniqueField(tokens, section.activeManeuverSeenFields, line);
1306
+ const value = joinFieldValue(tokens, line);
1307
+ switch (key) {
1308
+ case "kind":
1309
+ maneuver.kind = value;
1310
+ return;
1311
+ case "label":
1312
+ maneuver.label = value;
1313
+ return;
1314
+ case "epoch":
1315
+ maneuver.epoch = value;
1316
+ return;
1317
+ case "deltav":
1318
+ maneuver.deltaV = parseAtlasUnitValue(value, { line, column: tokens[0].column });
1319
+ return;
1320
+ case "duration":
1321
+ maneuver.duration = parseAtlasUnitValue(value, { line, column: tokens[0].column }, "duration");
1322
+ return;
1323
+ case "notes":
1324
+ maneuver.notes = parseTokenList(tokens.slice(1), line, "notes");
1325
+ return;
1326
+ default:
1327
+ throw new WorldOrbitError(`Unknown trajectory maneuver field "${tokens[0].value}"`, line, tokens[0].column);
1328
+ }
1329
+ }
950
1330
  function parseEventPoseField(tokens, line, seenFields) {
951
1331
  if (tokens.length < 2) {
952
1332
  throw new WorldOrbitError("Invalid event pose field line", line, tokens[0]?.column ?? 1);
@@ -1056,7 +1436,8 @@ function parseLayerTokens(tokens, line, sourceSchemaVersion, diagnostics) {
1056
1436
  raw === "events" ||
1057
1437
  raw === "objects" ||
1058
1438
  raw === "labels" ||
1059
- raw === "metadata") {
1439
+ raw === "metadata" ||
1440
+ raw === "trajectories") {
1060
1441
  if (raw === "events" && sourceSchemaVersion && diagnostics) {
1061
1442
  warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "layers.events", {
1062
1443
  line,
@@ -1181,6 +1562,18 @@ function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion,
1181
1562
  column: keyToken.column,
1182
1563
  });
1183
1564
  }
1565
+ else if (spec.version === "3.0") {
1566
+ warnIfSchema30Feature(sourceSchemaVersion, diagnostics, keyToken.value, {
1567
+ line,
1568
+ column: keyToken.column,
1569
+ });
1570
+ }
1571
+ else if (spec.version === "3.1") {
1572
+ warnIfSchema31Feature(sourceSchemaVersion, diagnostics, keyToken.value, {
1573
+ line,
1574
+ column: keyToken.column,
1575
+ });
1576
+ }
1184
1577
  index++;
1185
1578
  const valueTokens = [];
1186
1579
  if (spec.inlineMode === "single") {
@@ -1233,6 +1626,18 @@ function parseObjectField(tokens, line, objectType, sourceSchemaVersion, diagnos
1233
1626
  column: tokens[0].column,
1234
1627
  });
1235
1628
  }
1629
+ else if (spec.version === "3.0") {
1630
+ warnIfSchema30Feature(sourceSchemaVersion, diagnostics, tokens[0].value, {
1631
+ line,
1632
+ column: tokens[0].column,
1633
+ });
1634
+ }
1635
+ else if (spec.version === "3.1") {
1636
+ warnIfSchema31Feature(sourceSchemaVersion, diagnostics, tokens[0].value, {
1637
+ line,
1638
+ column: tokens[0].column,
1639
+ });
1640
+ }
1236
1641
  const field = {
1237
1642
  type: "field",
1238
1643
  key: tokens[0].value,
@@ -1277,6 +1682,7 @@ function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
1277
1682
  const tolerances = fieldMap.get("tolerance")?.map((field) => parseToleranceField(field));
1278
1683
  const typedBlocks = normalizeTypedBlocks(node.typedBlockEntries);
1279
1684
  const info = normalizeInfoEntries(node.infoEntries, "info");
1685
+ const trajectoryId = parseOptionalJoinedValue(fieldMap.get("trajectory")?.[0]);
1280
1686
  const object = {
1281
1687
  type: node.objectType,
1282
1688
  id: node.id,
@@ -1306,6 +1712,8 @@ function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
1306
1712
  object.tolerances = tolerances;
1307
1713
  if (typedBlocks && Object.keys(typedBlocks).length > 0)
1308
1714
  object.typedBlocks = typedBlocks;
1715
+ if (trajectoryId)
1716
+ object.trajectoryId = trajectoryId;
1309
1717
  if (isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
1310
1718
  if (object.groups ||
1311
1719
  object.epoch ||
@@ -1317,10 +1725,14 @@ function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
1317
1725
  object.validationRules?.length ||
1318
1726
  object.lockedFields?.length ||
1319
1727
  object.tolerances?.length ||
1320
- object.typedBlocks) {
1728
+ object.typedBlocks ||
1729
+ object.trajectoryId) {
1321
1730
  warnIfSchema21Feature(sourceSchemaVersion, diagnostics, node.id, node.location);
1322
1731
  }
1323
1732
  }
1733
+ if (object.trajectoryId) {
1734
+ warnIfSchema30Feature(sourceSchemaVersion, diagnostics, `${node.id}.trajectory`, node.location);
1735
+ }
1324
1736
  return object;
1325
1737
  }
1326
1738
  function normalizeDraftEvent(event, rawPoses) {
@@ -1337,6 +1749,8 @@ function normalizeDraftEventPose(rawPose) {
1337
1749
  return {
1338
1750
  objectId: rawPose.objectId,
1339
1751
  placement,
1752
+ trajectorySegmentId: parseOptionalJoinedValue(fieldMap.get("segment")?.[0]),
1753
+ trajectoryManeuverId: parseOptionalJoinedValue(fieldMap.get("maneuver")?.[0]),
1340
1754
  inner: parseOptionalUnitField(fieldMap.get("inner")?.[0], "inner"),
1341
1755
  outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer"),
1342
1756
  epoch: parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]),
@@ -1553,6 +1967,32 @@ function warnIfSchema25Feature(sourceSchemaVersion, diagnostics, featureName, lo
1553
1967
  column: location.column,
1554
1968
  });
1555
1969
  }
1970
+ function warnIfSchema30Feature(sourceSchemaVersion, diagnostics, featureName, location) {
1971
+ if (!isSchemaOlderThan(sourceSchemaVersion, "3.0")) {
1972
+ return;
1973
+ }
1974
+ diagnostics.push({
1975
+ code: "parse.schema30.featureCompatibility",
1976
+ severity: "warning",
1977
+ source: "parse",
1978
+ message: `Feature "${featureName}" requires schema 3.0; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
1979
+ line: location.line,
1980
+ column: location.column,
1981
+ });
1982
+ }
1983
+ function warnIfSchema31Feature(sourceSchemaVersion, diagnostics, featureName, location) {
1984
+ if (!isSchemaOlderThan(sourceSchemaVersion, "3.1")) {
1985
+ return;
1986
+ }
1987
+ diagnostics.push({
1988
+ code: "parse.schema31.featureCompatibility",
1989
+ severity: "warning",
1990
+ source: "parse",
1991
+ message: `Feature "${featureName}" requires schema 3.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
1992
+ line: location.line,
1993
+ column: location.column,
1994
+ });
1995
+ }
1556
1996
  function isSchemaOlderThan(sourceSchemaVersion, requiredVersion) {
1557
1997
  return schemaVersionRank(sourceSchemaVersion) < schemaVersionRank(requiredVersion);
1558
1998
  }
@@ -1568,8 +2008,12 @@ function schemaVersionRank(version) {
1568
2008
  return 3;
1569
2009
  case "2.6":
1570
2010
  return 4;
1571
- default:
2011
+ case "3.0":
1572
2012
  return 5;
2013
+ case "3.1":
2014
+ return 6;
2015
+ default:
2016
+ return 7;
1573
2017
  }
1574
2018
  }
1575
2019
  function preprocessAtlasSource(source) {
@@ -1,4 +1,4 @@
1
- import type { SceneRenderOptions, WorldOrbitAtlasDocument, WorldOrbitEvent, WorldOrbitAtlasSystem, WorldOrbitDiagnostic, WorldOrbitDocument, WorldOrbitObject } from "./types.js";
1
+ import type { SceneRenderOptions, WorldOrbitAtlasDocument, WorldOrbitEvent, WorldOrbitAtlasSystem, WorldOrbitDiagnostic, WorldOrbitDocument, WorldOrbitObject, WorldOrbitTrajectory } from "./types.js";
2
2
  interface UpgradeOptions extends Pick<SceneRenderOptions, "preset" | "projection"> {
3
3
  }
4
4
  export declare function upgradeDocumentToV2(document: WorldOrbitDocument, options?: UpgradeOptions): WorldOrbitAtlasDocument;
@@ -12,6 +12,7 @@ export declare function upgradeDocumentToDraftV2(document: WorldOrbitDocument, o
12
12
  groups: import("./types.js").WorldOrbitGroup[];
13
13
  relations: import("./types.js").WorldOrbitRelation[];
14
14
  events: WorldOrbitEvent[];
15
+ trajectories: WorldOrbitTrajectory[];
15
16
  objects: WorldOrbitObject[];
16
17
  diagnostics: WorldOrbitDiagnostic[];
17
18
  };