worldorbit 2.5.15 → 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.
- package/README.md +1 -1
- package/dist/browser/core/dist/index.js +2444 -342
- package/dist/browser/editor/dist/index.js +11702 -0
- package/dist/browser/markdown/dist/index.js +2207 -392
- package/dist/browser/viewer/dist/index.js +2302 -382
- package/dist/unpkg/core/dist/index.js +2447 -345
- package/dist/unpkg/editor/dist/index.js +11727 -0
- package/dist/unpkg/markdown/dist/index.js +2210 -395
- package/dist/unpkg/viewer/dist/index.js +2305 -385
- package/dist/unpkg/worldorbit-core.min.js +12 -12
- package/dist/unpkg/worldorbit-editor.min.js +894 -0
- package/dist/unpkg/worldorbit-markdown.min.js +66 -58
- package/dist/unpkg/worldorbit-viewer.min.js +76 -68
- package/dist/unpkg/worldorbit.js +797 -78
- package/dist/unpkg/worldorbit.min.js +80 -72
- package/package.json +3 -2
- package/packages/core/dist/atlas-edit.js +74 -0
- package/packages/core/dist/atlas-validate.js +122 -8
- package/packages/core/dist/draft-parse.js +212 -8
- package/packages/core/dist/draft.d.ts +5 -2
- package/packages/core/dist/draft.js +59 -3
- package/packages/core/dist/format.js +63 -1
- package/packages/core/dist/normalize.js +1 -0
- package/packages/core/dist/scene.js +248 -46
- package/packages/core/dist/types.d.ts +41 -2
- package/packages/editor/dist/editor.d.ts +2 -0
- package/packages/editor/dist/editor.js +3578 -0
- package/packages/editor/dist/index.d.ts +2 -0
- package/packages/editor/dist/index.js +1 -0
- package/packages/editor/dist/types.d.ts +55 -0
- package/packages/editor/dist/types.js +1 -0
- package/packages/markdown/dist/html.d.ts +3 -0
- package/packages/markdown/dist/html.js +57 -0
- package/packages/markdown/dist/index.d.ts +4 -0
- package/packages/markdown/dist/index.js +3 -0
- package/packages/markdown/dist/rehype.d.ts +10 -0
- package/packages/markdown/dist/rehype.js +49 -0
- package/packages/markdown/dist/remark.d.ts +9 -0
- package/packages/markdown/dist/remark.js +28 -0
- package/packages/markdown/dist/types.d.ts +11 -0
- package/packages/markdown/dist/types.js +1 -0
- package/packages/viewer/dist/atlas-state.js +6 -0
- package/packages/viewer/dist/atlas-viewer.js +1 -0
- package/packages/viewer/dist/render.js +31 -2
- package/packages/viewer/dist/theme.js +1 -0
- package/packages/viewer/dist/tooltip.js +9 -0
- package/packages/viewer/dist/types.d.ts +8 -1
- package/packages/viewer/dist/viewer.js +12 -1
|
@@ -302,13 +302,13 @@
|
|
|
302
302
|
function unitFamilyAllowsUnit(family, unit) {
|
|
303
303
|
switch (family) {
|
|
304
304
|
case "distance":
|
|
305
|
-
return unit === null || ["au", "km", "re", "sol"].includes(unit);
|
|
305
|
+
return unit === null || ["au", "km", "m", "ly", "pc", "kpc", "re", "sol"].includes(unit);
|
|
306
306
|
case "radius":
|
|
307
|
-
return unit === null || ["km", "re", "sol"].includes(unit);
|
|
307
|
+
return unit === null || ["km", "m", "re", "rj", "sol"].includes(unit);
|
|
308
308
|
case "mass":
|
|
309
|
-
return unit === null || ["me", "sol"].includes(unit);
|
|
309
|
+
return unit === null || ["me", "mj", "sol"].includes(unit);
|
|
310
310
|
case "duration":
|
|
311
|
-
return unit === null || ["h", "d", "y"].includes(unit);
|
|
311
|
+
return unit === null || ["s", "min", "h", "d", "y", "ky", "my", "gy"].includes(unit);
|
|
312
312
|
case "angle":
|
|
313
313
|
return unit === null || unit === "deg";
|
|
314
314
|
case "generic":
|
|
@@ -515,7 +515,7 @@
|
|
|
515
515
|
}
|
|
516
516
|
|
|
517
517
|
// packages/core/dist/normalize.js
|
|
518
|
-
var UNIT_PATTERN = /^(-?\d+(?:\.\d+)?)(au|km|re|sol|
|
|
518
|
+
var UNIT_PATTERN = /^(-?\d+(?:\.\d+)?)(kpc|min|mj|rj|ky|my|gy|au|km|me|re|pc|ly|deg|sol|K|m|s|h|d|y)?$/;
|
|
519
519
|
var BOOLEAN_VALUES = /* @__PURE__ */ new Map([
|
|
520
520
|
["true", true],
|
|
521
521
|
["false", false],
|
|
@@ -540,7 +540,11 @@
|
|
|
540
540
|
return {
|
|
541
541
|
format: "worldorbit",
|
|
542
542
|
version: "1.0",
|
|
543
|
+
schemaVersion: "1.0",
|
|
543
544
|
system,
|
|
545
|
+
groups: [],
|
|
546
|
+
relations: [],
|
|
547
|
+
events: [],
|
|
544
548
|
objects
|
|
545
549
|
};
|
|
546
550
|
}
|
|
@@ -550,13 +554,17 @@
|
|
|
550
554
|
const fieldMap = collectFields(mergedFields);
|
|
551
555
|
const placement = extractPlacement(node.objectType, fieldMap);
|
|
552
556
|
const properties = normalizeProperties(fieldMap);
|
|
553
|
-
const
|
|
557
|
+
const info2 = normalizeInfo(node.infoEntries);
|
|
554
558
|
if (node.objectType === "system") {
|
|
555
559
|
return {
|
|
556
560
|
type: "system",
|
|
557
561
|
id: node.name,
|
|
562
|
+
title: typeof properties.title === "string" ? properties.title : null,
|
|
563
|
+
description: null,
|
|
564
|
+
epoch: null,
|
|
565
|
+
referencePlane: null,
|
|
558
566
|
properties,
|
|
559
|
-
info
|
|
567
|
+
info: info2
|
|
560
568
|
};
|
|
561
569
|
}
|
|
562
570
|
return {
|
|
@@ -564,7 +572,7 @@
|
|
|
564
572
|
id: node.name,
|
|
565
573
|
properties,
|
|
566
574
|
placement,
|
|
567
|
-
info
|
|
575
|
+
info: info2
|
|
568
576
|
};
|
|
569
577
|
}
|
|
570
578
|
function validateFieldCompatibility(objectType, fields) {
|
|
@@ -694,14 +702,14 @@
|
|
|
694
702
|
}
|
|
695
703
|
}
|
|
696
704
|
function normalizeInfo(entries) {
|
|
697
|
-
const
|
|
705
|
+
const info2 = {};
|
|
698
706
|
for (const entry of entries) {
|
|
699
|
-
if (entry.key in
|
|
707
|
+
if (entry.key in info2) {
|
|
700
708
|
throw WorldOrbitError.fromLocation(`Duplicate info key "${entry.key}"`, entry.location);
|
|
701
709
|
}
|
|
702
|
-
|
|
710
|
+
info2[entry.key] = entry.value;
|
|
703
711
|
}
|
|
704
|
-
return
|
|
712
|
+
return info2;
|
|
705
713
|
}
|
|
706
714
|
function parseAtReference(target, location) {
|
|
707
715
|
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
@@ -875,38 +883,38 @@
|
|
|
875
883
|
function createDiagnostic(diagnostic) {
|
|
876
884
|
return { ...diagnostic };
|
|
877
885
|
}
|
|
878
|
-
function diagnosticFromError(
|
|
879
|
-
if (
|
|
886
|
+
function diagnosticFromError(error2, source, code = `${source}.failed`) {
|
|
887
|
+
if (error2 instanceof WorldOrbitError) {
|
|
880
888
|
return {
|
|
881
889
|
code,
|
|
882
890
|
severity: "error",
|
|
883
891
|
source,
|
|
884
|
-
message:
|
|
885
|
-
line:
|
|
886
|
-
column:
|
|
892
|
+
message: error2.message,
|
|
893
|
+
line: error2.line,
|
|
894
|
+
column: error2.column
|
|
887
895
|
};
|
|
888
896
|
}
|
|
889
|
-
if (
|
|
897
|
+
if (error2 instanceof Error) {
|
|
890
898
|
return {
|
|
891
899
|
code,
|
|
892
900
|
severity: "error",
|
|
893
901
|
source,
|
|
894
|
-
message:
|
|
902
|
+
message: error2.message
|
|
895
903
|
};
|
|
896
904
|
}
|
|
897
905
|
return {
|
|
898
906
|
code,
|
|
899
907
|
severity: "error",
|
|
900
908
|
source,
|
|
901
|
-
message: String(
|
|
909
|
+
message: String(error2)
|
|
902
910
|
};
|
|
903
911
|
}
|
|
904
912
|
function parseWithDiagnostics(source) {
|
|
905
913
|
let ast;
|
|
906
914
|
try {
|
|
907
915
|
ast = parseWorldOrbit(source);
|
|
908
|
-
} catch (
|
|
909
|
-
const diagnostic = diagnosticFromError(
|
|
916
|
+
} catch (error2) {
|
|
917
|
+
const diagnostic = diagnosticFromError(error2, "parse");
|
|
910
918
|
return {
|
|
911
919
|
ok: false,
|
|
912
920
|
value: null,
|
|
@@ -916,20 +924,20 @@
|
|
|
916
924
|
let document;
|
|
917
925
|
try {
|
|
918
926
|
document = normalizeDocument(ast);
|
|
919
|
-
} catch (
|
|
927
|
+
} catch (error2) {
|
|
920
928
|
return {
|
|
921
929
|
ok: false,
|
|
922
930
|
value: null,
|
|
923
|
-
diagnostics: [diagnosticFromError(
|
|
931
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
924
932
|
};
|
|
925
933
|
}
|
|
926
934
|
try {
|
|
927
935
|
validateDocument(document);
|
|
928
|
-
} catch (
|
|
936
|
+
} catch (error2) {
|
|
929
937
|
return {
|
|
930
938
|
ok: false,
|
|
931
939
|
value: null,
|
|
932
|
-
diagnostics: [diagnosticFromError(
|
|
940
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
933
941
|
};
|
|
934
942
|
}
|
|
935
943
|
return {
|
|
@@ -948,11 +956,11 @@
|
|
|
948
956
|
value: normalizeDocument(ast),
|
|
949
957
|
diagnostics: []
|
|
950
958
|
};
|
|
951
|
-
} catch (
|
|
959
|
+
} catch (error2) {
|
|
952
960
|
return {
|
|
953
961
|
ok: false,
|
|
954
962
|
value: null,
|
|
955
|
-
diagnostics: [diagnosticFromError(
|
|
963
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
956
964
|
};
|
|
957
965
|
}
|
|
958
966
|
}
|
|
@@ -964,11 +972,11 @@
|
|
|
964
972
|
value: document,
|
|
965
973
|
diagnostics: []
|
|
966
974
|
};
|
|
967
|
-
} catch (
|
|
975
|
+
} catch (error2) {
|
|
968
976
|
return {
|
|
969
977
|
ok: false,
|
|
970
978
|
value: null,
|
|
971
|
-
diagnostics: [diagnosticFromError(
|
|
979
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
972
980
|
};
|
|
973
981
|
}
|
|
974
982
|
}
|
|
@@ -976,7 +984,11 @@
|
|
|
976
984
|
// packages/core/dist/scene.js
|
|
977
985
|
var AU_IN_KM = 1495978707e-1;
|
|
978
986
|
var EARTH_RADIUS_IN_KM = 6371;
|
|
987
|
+
var JUPITER_RADIUS_IN_KM = 71492;
|
|
979
988
|
var SOLAR_RADIUS_IN_KM = 695700;
|
|
989
|
+
var LY_IN_AU = 63241.077;
|
|
990
|
+
var PC_IN_AU = 206264.806;
|
|
991
|
+
var KPC_IN_AU = 206264806;
|
|
980
992
|
var ISO_FLATTENING = 0.68;
|
|
981
993
|
var MIN_ISO_MINOR_SCALE = 0.2;
|
|
982
994
|
var ARC_SAMPLE_COUNT = 28;
|
|
@@ -990,8 +1002,10 @@
|
|
|
990
1002
|
const scaleModel = resolveScaleModel(layoutPreset, options.scaleModel);
|
|
991
1003
|
const spacingFactor = layoutPresetSpacing(layoutPreset);
|
|
992
1004
|
const systemId = document.system?.id ?? null;
|
|
993
|
-
const
|
|
994
|
-
const
|
|
1005
|
+
const activeEventId = options.activeEventId ?? null;
|
|
1006
|
+
const effectiveObjects = createEffectiveObjects(document.objects, document.events ?? [], activeEventId);
|
|
1007
|
+
const objectMap = new Map(effectiveObjects.map((object) => [object.id, object]));
|
|
1008
|
+
const relationships = buildSceneRelationships(effectiveObjects, objectMap);
|
|
995
1009
|
const positions = /* @__PURE__ */ new Map();
|
|
996
1010
|
const orbitDrafts = [];
|
|
997
1011
|
const leaderDrafts = [];
|
|
@@ -1000,7 +1014,7 @@
|
|
|
1000
1014
|
const atObjects = [];
|
|
1001
1015
|
const surfaceChildren = /* @__PURE__ */ new Map();
|
|
1002
1016
|
const orbitChildren = /* @__PURE__ */ new Map();
|
|
1003
|
-
for (const object of
|
|
1017
|
+
for (const object of effectiveObjects) {
|
|
1004
1018
|
const placement = object.placement;
|
|
1005
1019
|
if (!placement) {
|
|
1006
1020
|
rootObjects.push(object);
|
|
@@ -1095,11 +1109,14 @@
|
|
|
1095
1109
|
const objects = [...positions.values()].map((position) => createSceneObject(position, scaleModel, relationships));
|
|
1096
1110
|
const orbitVisuals = orbitDrafts.map((draft) => createOrbitVisual(draft, relationships.groupIds.get(draft.object.id) ?? null));
|
|
1097
1111
|
const leaders = leaderDrafts.map((draft) => createLeaderLine(draft));
|
|
1098
|
-
const labels = createSceneLabels(objects, height, scaleModel.labelMultiplier);
|
|
1099
|
-
const
|
|
1100
|
-
const
|
|
1112
|
+
const labels = createSceneLabels(objects, width, height, scaleModel.labelMultiplier);
|
|
1113
|
+
const relations = createSceneRelations(document, objects);
|
|
1114
|
+
const events = createSceneEvents(document.events ?? [], objects, activeEventId);
|
|
1115
|
+
const layers = createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels);
|
|
1116
|
+
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, scaleModel.labelMultiplier);
|
|
1117
|
+
const semanticGroups = createSceneSemanticGroups(document, objects);
|
|
1101
1118
|
const viewpoints = createSceneViewpoints(document, projection, frame.preset, relationships, objectMap);
|
|
1102
|
-
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels);
|
|
1119
|
+
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, scaleModel.labelMultiplier);
|
|
1103
1120
|
return {
|
|
1104
1121
|
width,
|
|
1105
1122
|
height,
|
|
@@ -1107,7 +1124,7 @@
|
|
|
1107
1124
|
renderPreset: frame.preset,
|
|
1108
1125
|
projection,
|
|
1109
1126
|
scaleModel,
|
|
1110
|
-
title: String(document.system?.properties.title ?? document.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1127
|
+
title: String(document.system?.title ?? document.system?.properties.title ?? document.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1111
1128
|
subtitle: `${capitalizeLabel(projection)} view - ${capitalizeLabel(layoutPreset)} layout`,
|
|
1112
1129
|
systemId,
|
|
1113
1130
|
viewMode: projection,
|
|
@@ -1123,9 +1140,13 @@
|
|
|
1123
1140
|
contentBounds,
|
|
1124
1141
|
layers,
|
|
1125
1142
|
groups,
|
|
1143
|
+
semanticGroups,
|
|
1126
1144
|
viewpoints,
|
|
1145
|
+
events,
|
|
1146
|
+
activeEventId,
|
|
1127
1147
|
objects,
|
|
1128
1148
|
orbitVisuals,
|
|
1149
|
+
relations,
|
|
1129
1150
|
leaders,
|
|
1130
1151
|
labels
|
|
1131
1152
|
};
|
|
@@ -1141,6 +1162,35 @@
|
|
|
1141
1162
|
y: center.y + dx * sin + dy * cos
|
|
1142
1163
|
};
|
|
1143
1164
|
}
|
|
1165
|
+
function createEffectiveObjects(objects, events, activeEventId) {
|
|
1166
|
+
const cloned = objects.map((object) => structuredClone(object));
|
|
1167
|
+
if (!activeEventId) {
|
|
1168
|
+
return cloned;
|
|
1169
|
+
}
|
|
1170
|
+
const activeEvent = events.find((event) => event.id === activeEventId);
|
|
1171
|
+
if (!activeEvent) {
|
|
1172
|
+
return cloned;
|
|
1173
|
+
}
|
|
1174
|
+
const objectMap = new Map(cloned.map((object) => [object.id, object]));
|
|
1175
|
+
for (const pose of activeEvent.positions) {
|
|
1176
|
+
const object = objectMap.get(pose.objectId);
|
|
1177
|
+
if (!object) {
|
|
1178
|
+
continue;
|
|
1179
|
+
}
|
|
1180
|
+
object.placement = pose.placement ? structuredClone(pose.placement) : null;
|
|
1181
|
+
if (pose.inner) {
|
|
1182
|
+
object.properties.inner = { ...pose.inner };
|
|
1183
|
+
} else {
|
|
1184
|
+
delete object.properties.inner;
|
|
1185
|
+
}
|
|
1186
|
+
if (pose.outer) {
|
|
1187
|
+
object.properties.outer = { ...pose.outer };
|
|
1188
|
+
} else {
|
|
1189
|
+
delete object.properties.outer;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
return cloned;
|
|
1193
|
+
}
|
|
1144
1194
|
function resolveLayoutPreset(document) {
|
|
1145
1195
|
const rawScale = String(document.system?.properties.scale ?? "balanced").toLowerCase();
|
|
1146
1196
|
switch (rawScale) {
|
|
@@ -1235,6 +1285,7 @@
|
|
|
1235
1285
|
}
|
|
1236
1286
|
function createSceneObject(position, scaleModel, relationships) {
|
|
1237
1287
|
const { object, x, y, radius, sortKey, anchorX, anchorY } = position;
|
|
1288
|
+
const renderPriority = object.renderHints?.renderPriority ?? 0;
|
|
1238
1289
|
return {
|
|
1239
1290
|
renderId: createRenderId(object.id),
|
|
1240
1291
|
objectId: object.id,
|
|
@@ -1243,11 +1294,12 @@
|
|
|
1243
1294
|
ancestorIds: relationships.ancestorIds.get(object.id) ?? [],
|
|
1244
1295
|
childIds: relationships.childIds.get(object.id) ?? [],
|
|
1245
1296
|
groupId: relationships.groupIds.get(object.id) ?? null,
|
|
1297
|
+
semanticGroupIds: [...object.groups ?? []],
|
|
1246
1298
|
x,
|
|
1247
1299
|
y,
|
|
1248
1300
|
radius,
|
|
1249
1301
|
visualRadius: visualExtentForObject(object, radius, scaleModel),
|
|
1250
|
-
sortKey,
|
|
1302
|
+
sortKey: sortKey + renderPriority * 1e-3,
|
|
1251
1303
|
anchorX,
|
|
1252
1304
|
anchorY,
|
|
1253
1305
|
label: object.id,
|
|
@@ -1264,6 +1316,7 @@
|
|
|
1264
1316
|
object: draft.object,
|
|
1265
1317
|
parentId: draft.parentId,
|
|
1266
1318
|
groupId,
|
|
1319
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1267
1320
|
kind: draft.kind,
|
|
1268
1321
|
cx: draft.cx,
|
|
1269
1322
|
cy: draft.cy,
|
|
@@ -1275,7 +1328,7 @@
|
|
|
1275
1328
|
bandThickness: draft.bandThickness,
|
|
1276
1329
|
frontArcPath: draft.frontArcPath,
|
|
1277
1330
|
backArcPath: draft.backArcPath,
|
|
1278
|
-
hidden: draft.object.properties.hidden === true
|
|
1331
|
+
hidden: draft.object.properties.hidden === true || draft.object.renderHints?.renderOrbit === false
|
|
1279
1332
|
};
|
|
1280
1333
|
}
|
|
1281
1334
|
function createLeaderLine(draft) {
|
|
@@ -1284,6 +1337,7 @@
|
|
|
1284
1337
|
objectId: draft.object.id,
|
|
1285
1338
|
object: draft.object,
|
|
1286
1339
|
groupId: draft.groupId,
|
|
1340
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1287
1341
|
x1: draft.x1,
|
|
1288
1342
|
y1: draft.y1,
|
|
1289
1343
|
x2: draft.x2,
|
|
@@ -1292,42 +1346,144 @@
|
|
|
1292
1346
|
hidden: draft.object.properties.hidden === true
|
|
1293
1347
|
};
|
|
1294
1348
|
}
|
|
1295
|
-
function createSceneLabels(objects, sceneHeight, labelMultiplier) {
|
|
1349
|
+
function createSceneLabels(objects, sceneWidth, sceneHeight, labelMultiplier) {
|
|
1296
1350
|
const labels = [];
|
|
1297
1351
|
const occupied = [];
|
|
1298
|
-
const
|
|
1352
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1353
|
+
const visibleObjects = [...objects].filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false).sort(compareLabelPlacementOrder);
|
|
1299
1354
|
for (const object of visibleObjects) {
|
|
1300
|
-
const
|
|
1301
|
-
|
|
1302
|
-
let labelY = object.y + direction * (object.radius + 18 * labelMultiplier);
|
|
1303
|
-
let secondaryY = labelY + direction * (16 * labelMultiplier);
|
|
1304
|
-
let bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
|
|
1305
|
-
let attempts = 0;
|
|
1306
|
-
while (occupied.some((entry) => rectsOverlap(entry, bounds)) && attempts < 10) {
|
|
1307
|
-
labelY += direction * 14 * labelMultiplier;
|
|
1308
|
-
secondaryY += direction * 14 * labelMultiplier;
|
|
1309
|
-
bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
|
|
1310
|
-
attempts += 1;
|
|
1311
|
-
}
|
|
1312
|
-
occupied.push(bounds);
|
|
1355
|
+
const placement = selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) ?? createLabelPlacement(object, defaultVerticalDirection(object, objectMap.get(object.parentId ?? "") ?? null, sceneHeight), 0, labelMultiplier);
|
|
1356
|
+
occupied.push(createLabelRect(object, placement, labelMultiplier));
|
|
1313
1357
|
labels.push({
|
|
1314
1358
|
renderId: `${object.renderId}-label`,
|
|
1315
1359
|
objectId: object.objectId,
|
|
1316
1360
|
object: object.object,
|
|
1317
1361
|
groupId: object.groupId,
|
|
1362
|
+
semanticGroupIds: [...object.semanticGroupIds],
|
|
1318
1363
|
label: object.label,
|
|
1319
1364
|
secondaryLabel: object.secondaryLabel,
|
|
1320
|
-
x:
|
|
1321
|
-
y: labelY,
|
|
1322
|
-
secondaryY,
|
|
1323
|
-
textAnchor:
|
|
1324
|
-
direction: direction
|
|
1365
|
+
x: placement.x,
|
|
1366
|
+
y: placement.labelY,
|
|
1367
|
+
secondaryY: placement.secondaryY,
|
|
1368
|
+
textAnchor: placement.textAnchor,
|
|
1369
|
+
direction: placement.direction,
|
|
1325
1370
|
hidden: object.hidden
|
|
1326
1371
|
});
|
|
1327
1372
|
}
|
|
1328
1373
|
return labels;
|
|
1329
1374
|
}
|
|
1330
|
-
function
|
|
1375
|
+
function compareLabelPlacementOrder(left, right) {
|
|
1376
|
+
const priorityDiff = labelPlacementPriority(left) - labelPlacementPriority(right);
|
|
1377
|
+
if (priorityDiff !== 0) {
|
|
1378
|
+
return priorityDiff;
|
|
1379
|
+
}
|
|
1380
|
+
const renderPriorityDiff = (right.object.renderHints?.renderPriority ?? 0) - (left.object.renderHints?.renderPriority ?? 0);
|
|
1381
|
+
if (renderPriorityDiff !== 0) {
|
|
1382
|
+
return renderPriorityDiff;
|
|
1383
|
+
}
|
|
1384
|
+
return left.sortKey - right.sortKey;
|
|
1385
|
+
}
|
|
1386
|
+
function labelPlacementPriority(object) {
|
|
1387
|
+
switch (object.object.type) {
|
|
1388
|
+
case "star":
|
|
1389
|
+
return 0;
|
|
1390
|
+
case "planet":
|
|
1391
|
+
return 1;
|
|
1392
|
+
case "moon":
|
|
1393
|
+
return 2;
|
|
1394
|
+
case "belt":
|
|
1395
|
+
case "ring":
|
|
1396
|
+
return 3;
|
|
1397
|
+
case "asteroid":
|
|
1398
|
+
case "comet":
|
|
1399
|
+
return 4;
|
|
1400
|
+
case "structure":
|
|
1401
|
+
case "phenomenon":
|
|
1402
|
+
return 5;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
function selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) {
|
|
1406
|
+
for (const direction of preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight)) {
|
|
1407
|
+
const maxAttempts = direction === "left" || direction === "right" ? 4 : 6;
|
|
1408
|
+
for (let attempt = 0; attempt <= maxAttempts; attempt += 1) {
|
|
1409
|
+
const placement = createLabelPlacement(object, direction, attempt, labelMultiplier);
|
|
1410
|
+
const rect = createLabelRect(object, placement, labelMultiplier);
|
|
1411
|
+
if (!occupied.some((entry) => rectsOverlap(entry, rect))) {
|
|
1412
|
+
return placement;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
return null;
|
|
1417
|
+
}
|
|
1418
|
+
function preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight) {
|
|
1419
|
+
const parent = object.parentId ? objectMap.get(object.parentId) ?? null : null;
|
|
1420
|
+
const vertical = defaultVerticalDirection(object, parent, sceneHeight);
|
|
1421
|
+
const oppositeVertical = vertical === "below" ? "above" : "below";
|
|
1422
|
+
const horizontal = defaultHorizontalDirection(object, parent, sceneWidth);
|
|
1423
|
+
const oppositeHorizontal = horizontal === "right" ? "left" : "right";
|
|
1424
|
+
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";
|
|
1425
|
+
return preferHorizontal ? [horizontal, vertical, oppositeHorizontal, oppositeVertical] : [vertical, horizontal, oppositeVertical, oppositeHorizontal];
|
|
1426
|
+
}
|
|
1427
|
+
function defaultVerticalDirection(object, parent, sceneHeight) {
|
|
1428
|
+
if (parent && Math.abs(object.y - parent.y) > 6) {
|
|
1429
|
+
return object.y >= parent.y ? "below" : "above";
|
|
1430
|
+
}
|
|
1431
|
+
return object.y > sceneHeight * 0.62 ? "above" : "below";
|
|
1432
|
+
}
|
|
1433
|
+
function defaultHorizontalDirection(object, parent, sceneWidth) {
|
|
1434
|
+
if (parent && Math.abs(object.x - parent.x) > 6) {
|
|
1435
|
+
return object.x >= parent.x ? "right" : "left";
|
|
1436
|
+
}
|
|
1437
|
+
return object.x >= sceneWidth / 2 ? "right" : "left";
|
|
1438
|
+
}
|
|
1439
|
+
function createLabelPlacement(object, direction, attempt, labelMultiplier) {
|
|
1440
|
+
const step = 14 * labelMultiplier;
|
|
1441
|
+
switch (direction) {
|
|
1442
|
+
case "above": {
|
|
1443
|
+
const labelY = object.y - (object.radius + 18 * labelMultiplier + attempt * step);
|
|
1444
|
+
return {
|
|
1445
|
+
x: object.x,
|
|
1446
|
+
labelY,
|
|
1447
|
+
secondaryY: labelY - 16 * labelMultiplier,
|
|
1448
|
+
textAnchor: "middle",
|
|
1449
|
+
direction
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
case "below": {
|
|
1453
|
+
const labelY = object.y + object.radius + 18 * labelMultiplier + attempt * step;
|
|
1454
|
+
return {
|
|
1455
|
+
x: object.x,
|
|
1456
|
+
labelY,
|
|
1457
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
1458
|
+
textAnchor: "middle",
|
|
1459
|
+
direction
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
case "left": {
|
|
1463
|
+
const x = object.x - (object.visualRadius + 16 * labelMultiplier + attempt * step);
|
|
1464
|
+
const labelY = object.y - 4 * labelMultiplier;
|
|
1465
|
+
return {
|
|
1466
|
+
x,
|
|
1467
|
+
labelY,
|
|
1468
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
1469
|
+
textAnchor: "end",
|
|
1470
|
+
direction
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
case "right": {
|
|
1474
|
+
const x = object.x + object.visualRadius + 16 * labelMultiplier + attempt * step;
|
|
1475
|
+
const labelY = object.y - 4 * labelMultiplier;
|
|
1476
|
+
return {
|
|
1477
|
+
x,
|
|
1478
|
+
labelY,
|
|
1479
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
1480
|
+
textAnchor: "start",
|
|
1481
|
+
direction
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
function createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels) {
|
|
1331
1487
|
const backOrbitIds = orbitVisuals.filter((visual) => !visual.hidden && Boolean(visual.backArcPath)).map((visual) => visual.renderId);
|
|
1332
1488
|
const frontOrbitIds = orbitVisuals.filter((visual) => !visual.hidden).map((visual) => visual.renderId);
|
|
1333
1489
|
return [
|
|
@@ -1338,6 +1494,14 @@
|
|
|
1338
1494
|
},
|
|
1339
1495
|
{ id: "orbits-back", renderIds: backOrbitIds },
|
|
1340
1496
|
{ id: "orbits-front", renderIds: frontOrbitIds },
|
|
1497
|
+
{
|
|
1498
|
+
id: "relations",
|
|
1499
|
+
renderIds: relations.filter((relation) => !relation.hidden).map((relation) => relation.renderId)
|
|
1500
|
+
},
|
|
1501
|
+
{
|
|
1502
|
+
id: "events",
|
|
1503
|
+
renderIds: events.filter((event) => !event.hidden).map((event) => event.renderId)
|
|
1504
|
+
},
|
|
1341
1505
|
{
|
|
1342
1506
|
id: "objects",
|
|
1343
1507
|
renderIds: objects.filter((object) => !object.hidden).map((object) => object.renderId)
|
|
@@ -1349,7 +1513,7 @@
|
|
|
1349
1513
|
{ id: "metadata", renderIds: ["wo-title", "wo-subtitle", "wo-meta"] }
|
|
1350
1514
|
];
|
|
1351
1515
|
}
|
|
1352
|
-
function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships) {
|
|
1516
|
+
function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, labelMultiplier) {
|
|
1353
1517
|
const groups = /* @__PURE__ */ new Map();
|
|
1354
1518
|
const ensureGroup = (groupId) => {
|
|
1355
1519
|
if (!groupId) {
|
|
@@ -1398,10 +1562,63 @@
|
|
|
1398
1562
|
}
|
|
1399
1563
|
}
|
|
1400
1564
|
for (const group of groups.values()) {
|
|
1401
|
-
group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels);
|
|
1565
|
+
group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier);
|
|
1402
1566
|
}
|
|
1403
1567
|
return [...groups.values()].sort((left, right) => left.label.localeCompare(right.label));
|
|
1404
1568
|
}
|
|
1569
|
+
function createSceneSemanticGroups(document, objects) {
|
|
1570
|
+
return [...document.groups].map((group) => ({
|
|
1571
|
+
id: group.id,
|
|
1572
|
+
label: group.label,
|
|
1573
|
+
summary: group.summary,
|
|
1574
|
+
color: group.color,
|
|
1575
|
+
tags: [...group.tags],
|
|
1576
|
+
hidden: group.hidden,
|
|
1577
|
+
objectIds: objects.filter((object) => !object.hidden && object.semanticGroupIds.includes(group.id)).map((object) => object.objectId)
|
|
1578
|
+
})).sort((left, right) => left.label.localeCompare(right.label));
|
|
1579
|
+
}
|
|
1580
|
+
function createSceneRelations(document, objects) {
|
|
1581
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1582
|
+
return document.relations.map((relation) => {
|
|
1583
|
+
const from = objectMap.get(relation.from);
|
|
1584
|
+
const to = objectMap.get(relation.to);
|
|
1585
|
+
return {
|
|
1586
|
+
renderId: `${createRenderId(relation.id)}-relation`,
|
|
1587
|
+
relationId: relation.id,
|
|
1588
|
+
relation,
|
|
1589
|
+
fromObjectId: relation.from,
|
|
1590
|
+
toObjectId: relation.to,
|
|
1591
|
+
x1: from?.x ?? 0,
|
|
1592
|
+
y1: from?.y ?? 0,
|
|
1593
|
+
x2: to?.x ?? 0,
|
|
1594
|
+
y2: to?.y ?? 0,
|
|
1595
|
+
hidden: relation.hidden || !from || !to || from.hidden || to.hidden
|
|
1596
|
+
};
|
|
1597
|
+
}).sort((left, right) => left.relation.id.localeCompare(right.relation.id));
|
|
1598
|
+
}
|
|
1599
|
+
function createSceneEvents(events, objects, activeEventId) {
|
|
1600
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1601
|
+
return events.map((event) => {
|
|
1602
|
+
const objectIds = [.../* @__PURE__ */ new Set([
|
|
1603
|
+
...event.targetObjectId ? [event.targetObjectId] : [],
|
|
1604
|
+
...event.participantObjectIds
|
|
1605
|
+
])];
|
|
1606
|
+
const positions = objectIds.map((objectId) => objectMap.get(objectId)).filter(Boolean);
|
|
1607
|
+
const centroidX = positions.length > 0 ? positions.reduce((sum, object) => sum + object.x, 0) / positions.length : 0;
|
|
1608
|
+
const centroidY = positions.length > 0 ? positions.reduce((sum, object) => sum + object.y, 0) / positions.length : 0;
|
|
1609
|
+
return {
|
|
1610
|
+
renderId: `${createRenderId(event.id)}-event`,
|
|
1611
|
+
eventId: event.id,
|
|
1612
|
+
event,
|
|
1613
|
+
objectIds,
|
|
1614
|
+
participantIds: [...event.participantObjectIds],
|
|
1615
|
+
targetObjectId: event.targetObjectId,
|
|
1616
|
+
x: centroidX,
|
|
1617
|
+
y: centroidY,
|
|
1618
|
+
hidden: event.hidden || positions.length === 0 || positions.every((object) => object.hidden) || activeEventId !== null && event.id !== activeEventId
|
|
1619
|
+
};
|
|
1620
|
+
}).sort((left, right) => left.event.id.localeCompare(right.event.id));
|
|
1621
|
+
}
|
|
1405
1622
|
function createSceneViewpoints(document, projection, preset, relationships, objectMap) {
|
|
1406
1623
|
const generatedOverview = createGeneratedOverviewViewpoint(document, projection, preset);
|
|
1407
1624
|
const drafts = /* @__PURE__ */ new Map();
|
|
@@ -1419,7 +1636,7 @@
|
|
|
1419
1636
|
}
|
|
1420
1637
|
const field = fieldParts.join(".").toLowerCase();
|
|
1421
1638
|
const draft = drafts.get(id) ?? { id };
|
|
1422
|
-
applyViewpointField(draft, field, value, projection, preset, relationships, objectMap);
|
|
1639
|
+
applyViewpointField(draft, field, value, document, projection, preset, relationships, objectMap);
|
|
1423
1640
|
drafts.set(id, draft);
|
|
1424
1641
|
}
|
|
1425
1642
|
const viewpoints = [...drafts.values()].map((draft) => finalizeViewpointDraft(draft, projection, preset, objectMap)).filter(Boolean);
|
|
@@ -1447,13 +1664,15 @@
|
|
|
1447
1664
|
});
|
|
1448
1665
|
}
|
|
1449
1666
|
function createGeneratedOverviewViewpoint(document, projection, preset) {
|
|
1450
|
-
const
|
|
1667
|
+
const title = document.system?.title ?? document.system?.properties.title;
|
|
1668
|
+
const label = title ? `${String(title)} Overview` : "Overview";
|
|
1451
1669
|
return {
|
|
1452
1670
|
id: "overview",
|
|
1453
1671
|
label,
|
|
1454
1672
|
summary: "Fit the whole system with the current atlas defaults.",
|
|
1455
1673
|
objectId: null,
|
|
1456
1674
|
selectedObjectId: null,
|
|
1675
|
+
eventIds: [],
|
|
1457
1676
|
projection,
|
|
1458
1677
|
preset,
|
|
1459
1678
|
rotationDeg: 0,
|
|
@@ -1463,7 +1682,7 @@
|
|
|
1463
1682
|
generated: true
|
|
1464
1683
|
};
|
|
1465
1684
|
}
|
|
1466
|
-
function applyViewpointField(draft, field, value, projection, preset, relationships, objectMap) {
|
|
1685
|
+
function applyViewpointField(draft, field, value, document, projection, preset, relationships, objectMap) {
|
|
1467
1686
|
const normalizedValue = value.trim();
|
|
1468
1687
|
switch (field) {
|
|
1469
1688
|
case "label":
|
|
@@ -1490,6 +1709,9 @@
|
|
|
1490
1709
|
draft.select = normalizedValue;
|
|
1491
1710
|
}
|
|
1492
1711
|
return;
|
|
1712
|
+
case "events":
|
|
1713
|
+
draft.eventIds = splitListValue(normalizedValue);
|
|
1714
|
+
return;
|
|
1493
1715
|
case "projection":
|
|
1494
1716
|
case "view":
|
|
1495
1717
|
draft.projection = parseViewProjection(normalizedValue) ?? projection;
|
|
@@ -1530,7 +1752,7 @@
|
|
|
1530
1752
|
case "groups":
|
|
1531
1753
|
draft.filter = {
|
|
1532
1754
|
...draft.filter ?? createEmptyViewpointFilter(),
|
|
1533
|
-
groupIds: parseViewpointGroups(normalizedValue, relationships, objectMap)
|
|
1755
|
+
groupIds: parseViewpointGroups(normalizedValue, document, relationships, objectMap)
|
|
1534
1756
|
};
|
|
1535
1757
|
return;
|
|
1536
1758
|
}
|
|
@@ -1546,6 +1768,7 @@
|
|
|
1546
1768
|
summary: draft.summary?.trim() || createViewpointSummary(label, objectId, filter),
|
|
1547
1769
|
objectId,
|
|
1548
1770
|
selectedObjectId,
|
|
1771
|
+
eventIds: [...new Set(draft.eventIds ?? [])],
|
|
1549
1772
|
projection: draft.projection ?? projection,
|
|
1550
1773
|
preset: draft.preset ?? preset,
|
|
1551
1774
|
rotationDeg: draft.rotationDeg ?? 0,
|
|
@@ -1603,7 +1826,7 @@
|
|
|
1603
1826
|
next["orbits-front"] = enabled;
|
|
1604
1827
|
continue;
|
|
1605
1828
|
}
|
|
1606
|
-
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1829
|
+
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "relations" || rawLayer === "events" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1607
1830
|
next[rawLayer] = enabled;
|
|
1608
1831
|
}
|
|
1609
1832
|
}
|
|
@@ -1612,8 +1835,11 @@
|
|
|
1612
1835
|
function parseViewpointObjectTypes(value) {
|
|
1613
1836
|
return splitListValue(value).filter((entry) => entry === "star" || entry === "planet" || entry === "moon" || entry === "belt" || entry === "asteroid" || entry === "comet" || entry === "ring" || entry === "structure" || entry === "phenomenon");
|
|
1614
1837
|
}
|
|
1615
|
-
function parseViewpointGroups(value, relationships, objectMap) {
|
|
1838
|
+
function parseViewpointGroups(value, document, relationships, objectMap) {
|
|
1616
1839
|
return splitListValue(value).map((entry) => {
|
|
1840
|
+
if (document.schemaVersion === "2.1" || document.groups.some((group) => group.id === entry)) {
|
|
1841
|
+
return entry;
|
|
1842
|
+
}
|
|
1617
1843
|
if (entry.startsWith("wo-") && entry.endsWith("-group")) {
|
|
1618
1844
|
return entry;
|
|
1619
1845
|
}
|
|
@@ -1648,7 +1874,7 @@
|
|
|
1648
1874
|
}
|
|
1649
1875
|
return parts.join(" - ");
|
|
1650
1876
|
}
|
|
1651
|
-
function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels) {
|
|
1877
|
+
function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, labelMultiplier) {
|
|
1652
1878
|
let minX = Number.POSITIVE_INFINITY;
|
|
1653
1879
|
let minY = Number.POSITIVE_INFINITY;
|
|
1654
1880
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
@@ -1678,7 +1904,7 @@
|
|
|
1678
1904
|
for (const label of labels) {
|
|
1679
1905
|
if (label.hidden)
|
|
1680
1906
|
continue;
|
|
1681
|
-
includeLabelBounds(label, include);
|
|
1907
|
+
includeLabelBounds(label, include, labelMultiplier);
|
|
1682
1908
|
}
|
|
1683
1909
|
if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
|
|
1684
1910
|
return createBounds(0, 0, width, height);
|
|
@@ -1716,13 +1942,10 @@
|
|
|
1716
1942
|
include(object.x - object.visualRadius - 24, object.y - object.visualRadius - 16);
|
|
1717
1943
|
include(object.x + object.visualRadius + 24, object.y + object.visualRadius + 36);
|
|
1718
1944
|
}
|
|
1719
|
-
function includeLabelBounds(label, include) {
|
|
1720
|
-
const
|
|
1721
|
-
|
|
1722
|
-
include(
|
|
1723
|
-
include(label.x + labelHalfWidth, label.y + 8);
|
|
1724
|
-
include(label.x - labelHalfWidth, label.secondaryY - 14);
|
|
1725
|
-
include(label.x + labelHalfWidth, label.secondaryY + 8);
|
|
1945
|
+
function includeLabelBounds(label, include, labelMultiplier) {
|
|
1946
|
+
const bounds = createLabelRectFromText(label.x, label.y, label.secondaryY, label.textAnchor, label.direction, label.label, label.secondaryLabel, labelMultiplier);
|
|
1947
|
+
include(bounds.left, bounds.top);
|
|
1948
|
+
include(bounds.right, bounds.bottom);
|
|
1726
1949
|
}
|
|
1727
1950
|
function placeObject(object, x, y, depth, positions, orbitDrafts, leaderDrafts, context) {
|
|
1728
1951
|
if (positions.has(object.id)) {
|
|
@@ -1744,8 +1967,9 @@
|
|
|
1744
1967
|
}
|
|
1745
1968
|
const orbiting = [...context.orbitChildren.get(object.id) ?? []].sort(compareOrbiting);
|
|
1746
1969
|
const orbitMetricContext = computeOrbitMetricContext(orbiting, parent.radius, context.spacingFactor, context.scaleModel);
|
|
1970
|
+
const orbitRadiiPx = resolveOrbitRadiiPx(orbiting, orbitMetricContext);
|
|
1747
1971
|
orbiting.forEach((child, index) => {
|
|
1748
|
-
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, context);
|
|
1972
|
+
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, orbitRadiiPx[index] ?? orbitMetricContext.innerPx, context);
|
|
1749
1973
|
orbitDrafts.push({
|
|
1750
1974
|
object: child,
|
|
1751
1975
|
parentId: object.id,
|
|
@@ -1819,7 +2043,8 @@
|
|
|
1819
2043
|
metricSpread: 0,
|
|
1820
2044
|
innerPx,
|
|
1821
2045
|
stepPx,
|
|
1822
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
2046
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
2047
|
+
minimumGapPx: stepPx * 0.42
|
|
1823
2048
|
};
|
|
1824
2049
|
}
|
|
1825
2050
|
const minMetric = Math.min(...presentMetrics);
|
|
@@ -1832,10 +2057,11 @@
|
|
|
1832
2057
|
metricSpread,
|
|
1833
2058
|
innerPx,
|
|
1834
2059
|
stepPx,
|
|
1835
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
2060
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
2061
|
+
minimumGapPx: stepPx * 0.42
|
|
1836
2062
|
};
|
|
1837
2063
|
}
|
|
1838
|
-
function resolveOrbitGeometry(object, index, count, parent, metricContext, context) {
|
|
2064
|
+
function resolveOrbitGeometry(object, index, count, parent, metricContext, orbitRadiusPx, context) {
|
|
1839
2065
|
const placement = object.placement;
|
|
1840
2066
|
const band = object.type === "belt" || object.type === "ring";
|
|
1841
2067
|
if (!placement || placement.mode !== "orbit") {
|
|
@@ -1853,7 +2079,7 @@
|
|
|
1853
2079
|
};
|
|
1854
2080
|
}
|
|
1855
2081
|
const eccentricity = clampNumber(typeof placement.eccentricity === "number" ? placement.eccentricity : 0, 0, 0.92);
|
|
1856
|
-
const semiMajor =
|
|
2082
|
+
const semiMajor = orbitRadiusPx;
|
|
1857
2083
|
const baseMinor = Math.max(semiMajor * Math.sqrt(1 - eccentricity * eccentricity), semiMajor * 0.18);
|
|
1858
2084
|
const inclinationDeg = unitValueToDegrees(placement.inclination) ?? 0;
|
|
1859
2085
|
const inclinationScale = context.projection === "isometric" ? Math.max(MIN_ISO_MINOR_SCALE, Math.cos(degreesToRadians(inclinationDeg))) * ISO_FLATTENING : 1;
|
|
@@ -1883,15 +2109,19 @@
|
|
|
1883
2109
|
objectY: objectPoint.y
|
|
1884
2110
|
};
|
|
1885
2111
|
}
|
|
1886
|
-
function resolveOrbitRadiusPx(
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
2112
|
+
function resolveOrbitRadiusPx(metric, metricContext) {
|
|
2113
|
+
return metricContext.innerPx + metricContext.stepPx * log2(Math.max(metric, 0) + 1);
|
|
2114
|
+
}
|
|
2115
|
+
function resolveOrbitRadiiPx(objects, metricContext) {
|
|
2116
|
+
const radii = [];
|
|
2117
|
+
objects.forEach((object, index) => {
|
|
2118
|
+
const metric = orbitMetric(object);
|
|
2119
|
+
const fallbackRadius = metricContext.innerPx + index * metricContext.stepPx;
|
|
2120
|
+
const baseRadius = metric === null ? fallbackRadius : resolveOrbitRadiusPx(metric, metricContext);
|
|
2121
|
+
const minimumRadius = index === 0 ? metricContext.innerPx : (radii[index - 1] ?? metricContext.innerPx) + metricContext.minimumGapPx;
|
|
2122
|
+
radii.push(Math.max(baseRadius, minimumRadius));
|
|
2123
|
+
});
|
|
2124
|
+
return radii;
|
|
1895
2125
|
}
|
|
1896
2126
|
function orbitMetric(object) {
|
|
1897
2127
|
if (!object.placement || object.placement.mode !== "orbit") {
|
|
@@ -1899,6 +2129,9 @@
|
|
|
1899
2129
|
}
|
|
1900
2130
|
return toDistanceMetric(object.placement.semiMajor ?? object.placement.distance ?? null);
|
|
1901
2131
|
}
|
|
2132
|
+
function log2(value) {
|
|
2133
|
+
return Math.log(value) / Math.log(2);
|
|
2134
|
+
}
|
|
1902
2135
|
function resolveOrbitPhase(phase, index, count) {
|
|
1903
2136
|
const degreeValue = phase ? unitValueToDegrees(phase) : null;
|
|
1904
2137
|
if (degreeValue !== null) {
|
|
@@ -2102,7 +2335,7 @@
|
|
|
2102
2335
|
return null;
|
|
2103
2336
|
}
|
|
2104
2337
|
}
|
|
2105
|
-
function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels) {
|
|
2338
|
+
function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier) {
|
|
2106
2339
|
let minX = Number.POSITIVE_INFINITY;
|
|
2107
2340
|
let minY = Number.POSITIVE_INFINITY;
|
|
2108
2341
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
@@ -2131,7 +2364,7 @@
|
|
|
2131
2364
|
}
|
|
2132
2365
|
for (const label of labels) {
|
|
2133
2366
|
if (!label.hidden && group.labelIds.includes(label.objectId)) {
|
|
2134
|
-
includeLabelBounds(label, include);
|
|
2367
|
+
includeLabelBounds(label, include, labelMultiplier);
|
|
2135
2368
|
}
|
|
2136
2369
|
}
|
|
2137
2370
|
if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
|
|
@@ -2156,12 +2389,28 @@
|
|
|
2156
2389
|
}
|
|
2157
2390
|
return current.id;
|
|
2158
2391
|
}
|
|
2159
|
-
function createLabelRect(
|
|
2392
|
+
function createLabelRect(object, placement, labelMultiplier) {
|
|
2393
|
+
return createLabelRectFromText(placement.x, placement.labelY, placement.secondaryY, placement.textAnchor, placement.direction, object.label, object.secondaryLabel, labelMultiplier);
|
|
2394
|
+
}
|
|
2395
|
+
function createLabelRectFromText(x, labelY, secondaryY, textAnchor, direction, label, secondaryLabel, labelMultiplier) {
|
|
2396
|
+
const labelHalfWidth = estimateLabelHalfWidthFromText(label, secondaryLabel, labelMultiplier);
|
|
2397
|
+
const labelWidth = labelHalfWidth * 2;
|
|
2398
|
+
const topPadding = direction === "above" ? 18 : 12;
|
|
2399
|
+
const bottomPadding = direction === "above" ? 8 : 12;
|
|
2400
|
+
let left = x - labelHalfWidth;
|
|
2401
|
+
let right = x + labelHalfWidth;
|
|
2402
|
+
if (textAnchor === "start") {
|
|
2403
|
+
left = x;
|
|
2404
|
+
right = x + labelWidth;
|
|
2405
|
+
} else if (textAnchor === "end") {
|
|
2406
|
+
left = x - labelWidth;
|
|
2407
|
+
right = x;
|
|
2408
|
+
}
|
|
2160
2409
|
return {
|
|
2161
|
-
left
|
|
2162
|
-
right
|
|
2163
|
-
top: Math.min(labelY, secondaryY) -
|
|
2164
|
-
bottom: Math.max(labelY, secondaryY) +
|
|
2410
|
+
left,
|
|
2411
|
+
right,
|
|
2412
|
+
top: Math.min(labelY, secondaryY) - topPadding,
|
|
2413
|
+
bottom: Math.max(labelY, secondaryY) + bottomPadding
|
|
2165
2414
|
};
|
|
2166
2415
|
}
|
|
2167
2416
|
function rectsOverlap(left, right) {
|
|
@@ -2222,8 +2471,18 @@
|
|
|
2222
2471
|
return value.value;
|
|
2223
2472
|
case "km":
|
|
2224
2473
|
return value.value / AU_IN_KM;
|
|
2474
|
+
case "m":
|
|
2475
|
+
return value.value / 1e3 / AU_IN_KM;
|
|
2476
|
+
case "ly":
|
|
2477
|
+
return value.value * LY_IN_AU;
|
|
2478
|
+
case "pc":
|
|
2479
|
+
return value.value * PC_IN_AU;
|
|
2480
|
+
case "kpc":
|
|
2481
|
+
return value.value * KPC_IN_AU;
|
|
2225
2482
|
case "re":
|
|
2226
2483
|
return value.value * EARTH_RADIUS_IN_KM / AU_IN_KM;
|
|
2484
|
+
case "rj":
|
|
2485
|
+
return value.value * JUPITER_RADIUS_IN_KM / AU_IN_KM;
|
|
2227
2486
|
case "sol":
|
|
2228
2487
|
return value.value * SOLAR_RADIUS_IN_KM / AU_IN_KM;
|
|
2229
2488
|
default:
|
|
@@ -2338,11 +2597,6 @@
|
|
|
2338
2597
|
function customColorFor(value) {
|
|
2339
2598
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
2340
2599
|
}
|
|
2341
|
-
function estimateLabelHalfWidth(object, labelMultiplier) {
|
|
2342
|
-
const primaryWidth = object.label.length * 4.6 * labelMultiplier + 18;
|
|
2343
|
-
const secondaryWidth = object.secondaryLabel.length * 3.9 * labelMultiplier + 18;
|
|
2344
|
-
return Math.max(primaryWidth, secondaryWidth, object.visualRadius + 18);
|
|
2345
|
-
}
|
|
2346
2600
|
function estimateLabelHalfWidthFromText(label, secondaryLabel, labelMultiplier) {
|
|
2347
2601
|
const primaryWidth = label.length * 4.6 * labelMultiplier + 18;
|
|
2348
2602
|
const secondaryWidth = secondaryLabel.length * 3.9 * labelMultiplier + 18;
|
|
@@ -2377,8 +2631,12 @@
|
|
|
2377
2631
|
return {
|
|
2378
2632
|
format: "worldorbit",
|
|
2379
2633
|
version: "2.0",
|
|
2634
|
+
schemaVersion: "2.0",
|
|
2380
2635
|
sourceVersion: document.version,
|
|
2381
2636
|
system,
|
|
2637
|
+
groups: structuredClone(document.groups ?? []),
|
|
2638
|
+
relations: structuredClone(document.relations ?? []),
|
|
2639
|
+
events: structuredClone(document.events ?? []),
|
|
2382
2640
|
objects: document.objects.map(cloneWorldOrbitObject),
|
|
2383
2641
|
diagnostics
|
|
2384
2642
|
};
|
|
@@ -2386,18 +2644,28 @@
|
|
|
2386
2644
|
function upgradeDocumentToDraftV2(document, options = {}) {
|
|
2387
2645
|
return convertAtlasDocumentToLegacyDraft(upgradeDocumentToV2(document, options));
|
|
2388
2646
|
}
|
|
2389
|
-
function materializeAtlasDocument(document) {
|
|
2647
|
+
function materializeAtlasDocument(document, options = {}) {
|
|
2390
2648
|
const system = document.system ? {
|
|
2391
2649
|
type: "system",
|
|
2392
2650
|
id: document.system.id,
|
|
2651
|
+
title: document.system.title,
|
|
2652
|
+
description: document.system.description,
|
|
2653
|
+
epoch: document.system.epoch,
|
|
2654
|
+
referencePlane: document.system.referencePlane,
|
|
2393
2655
|
properties: materializeDraftSystemProperties(document.system),
|
|
2394
2656
|
info: materializeDraftSystemInfo(document.system)
|
|
2395
2657
|
} : null;
|
|
2658
|
+
const objects = document.objects.map(cloneWorldOrbitObject);
|
|
2659
|
+
applyEventPoseOverrides(objects, document.events ?? [], options.activeEventId ?? null);
|
|
2396
2660
|
return {
|
|
2397
2661
|
format: "worldorbit",
|
|
2398
2662
|
version: "1.0",
|
|
2663
|
+
schemaVersion: document.version,
|
|
2399
2664
|
system,
|
|
2400
|
-
|
|
2665
|
+
groups: structuredClone(document.groups ?? []),
|
|
2666
|
+
relations: structuredClone(document.relations ?? []),
|
|
2667
|
+
events: document.events.map(cloneWorldOrbitEvent),
|
|
2668
|
+
objects
|
|
2401
2669
|
};
|
|
2402
2670
|
}
|
|
2403
2671
|
function materializeDraftDocument(document) {
|
|
@@ -2411,7 +2679,10 @@
|
|
|
2411
2679
|
return {
|
|
2412
2680
|
type: "system",
|
|
2413
2681
|
id: document.system?.id ?? "WorldOrbit",
|
|
2414
|
-
title: typeof document.system?.properties.title === "string" ? document.system.properties.title : null,
|
|
2682
|
+
title: document.system?.title ?? (typeof document.system?.properties.title === "string" ? document.system.properties.title : null),
|
|
2683
|
+
description: document.system?.description ?? null,
|
|
2684
|
+
epoch: document.system?.epoch ?? null,
|
|
2685
|
+
referencePlane: document.system?.referencePlane ?? null,
|
|
2415
2686
|
defaults,
|
|
2416
2687
|
atlasMetadata,
|
|
2417
2688
|
viewpoints: scene.viewpoints.map(mapSceneViewpointToDraftViewpoint),
|
|
@@ -2522,6 +2793,7 @@
|
|
|
2522
2793
|
summary: viewpoint.summary,
|
|
2523
2794
|
focusObjectId: viewpoint.objectId,
|
|
2524
2795
|
selectedObjectId: viewpoint.selectedObjectId,
|
|
2796
|
+
events: [...viewpoint.eventIds],
|
|
2525
2797
|
projection: viewpoint.projection,
|
|
2526
2798
|
preset: viewpoint.preset,
|
|
2527
2799
|
zoom: viewpoint.scale,
|
|
@@ -2538,11 +2810,68 @@
|
|
|
2538
2810
|
function cloneWorldOrbitObject(object) {
|
|
2539
2811
|
return {
|
|
2540
2812
|
...object,
|
|
2813
|
+
groups: object.groups ? [...object.groups] : void 0,
|
|
2814
|
+
resonance: object.resonance ? { ...object.resonance } : object.resonance,
|
|
2815
|
+
renderHints: object.renderHints ? { ...object.renderHints } : object.renderHints,
|
|
2816
|
+
deriveRules: object.deriveRules ? object.deriveRules.map((rule) => ({ ...rule })) : void 0,
|
|
2817
|
+
validationRules: object.validationRules ? object.validationRules.map((rule) => ({ ...rule })) : void 0,
|
|
2818
|
+
lockedFields: object.lockedFields ? [...object.lockedFields] : void 0,
|
|
2819
|
+
tolerances: object.tolerances ? object.tolerances.map((entry) => ({
|
|
2820
|
+
field: entry.field,
|
|
2821
|
+
value: entry.value && typeof entry.value === "object" && "value" in entry.value ? { value: entry.value.value, unit: entry.value.unit } : Array.isArray(entry.value) ? [...entry.value] : entry.value
|
|
2822
|
+
})) : void 0,
|
|
2823
|
+
typedBlocks: object.typedBlocks ? Object.fromEntries(Object.entries(object.typedBlocks).map(([key, block]) => [key, { ...block ?? {} }])) : void 0,
|
|
2541
2824
|
properties: cloneProperties(object.properties),
|
|
2542
2825
|
placement: object.placement ? structuredClone(object.placement) : null,
|
|
2543
2826
|
info: { ...object.info }
|
|
2544
2827
|
};
|
|
2545
2828
|
}
|
|
2829
|
+
function cloneWorldOrbitEvent(event) {
|
|
2830
|
+
return {
|
|
2831
|
+
...event,
|
|
2832
|
+
participantObjectIds: [...event.participantObjectIds],
|
|
2833
|
+
tags: [...event.tags],
|
|
2834
|
+
positions: event.positions.map(cloneWorldOrbitEventPose)
|
|
2835
|
+
};
|
|
2836
|
+
}
|
|
2837
|
+
function cloneWorldOrbitEventPose(pose) {
|
|
2838
|
+
return {
|
|
2839
|
+
objectId: pose.objectId,
|
|
2840
|
+
placement: clonePlacement(pose.placement),
|
|
2841
|
+
inner: pose.inner ? { ...pose.inner } : void 0,
|
|
2842
|
+
outer: pose.outer ? { ...pose.outer } : void 0
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
function clonePlacement(placement) {
|
|
2846
|
+
return placement ? structuredClone(placement) : null;
|
|
2847
|
+
}
|
|
2848
|
+
function applyEventPoseOverrides(objects, events, activeEventId) {
|
|
2849
|
+
if (!activeEventId) {
|
|
2850
|
+
return;
|
|
2851
|
+
}
|
|
2852
|
+
const event = events.find((entry) => entry.id === activeEventId);
|
|
2853
|
+
if (!event) {
|
|
2854
|
+
return;
|
|
2855
|
+
}
|
|
2856
|
+
const objectMap = new Map(objects.map((object) => [object.id, object]));
|
|
2857
|
+
for (const pose of event.positions) {
|
|
2858
|
+
const object = objectMap.get(pose.objectId);
|
|
2859
|
+
if (!object) {
|
|
2860
|
+
continue;
|
|
2861
|
+
}
|
|
2862
|
+
object.placement = clonePlacement(pose.placement);
|
|
2863
|
+
if (pose.inner) {
|
|
2864
|
+
object.properties.inner = { ...pose.inner };
|
|
2865
|
+
} else {
|
|
2866
|
+
delete object.properties.inner;
|
|
2867
|
+
}
|
|
2868
|
+
if (pose.outer) {
|
|
2869
|
+
object.properties.outer = { ...pose.outer };
|
|
2870
|
+
} else {
|
|
2871
|
+
delete object.properties.outer;
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2546
2875
|
function cloneProperties(properties) {
|
|
2547
2876
|
const next = {};
|
|
2548
2877
|
for (const [key, value] of Object.entries(properties)) {
|
|
@@ -2582,71 +2911,83 @@
|
|
|
2582
2911
|
if (system.defaults.units) {
|
|
2583
2912
|
properties.units = system.defaults.units;
|
|
2584
2913
|
}
|
|
2914
|
+
if (system.description) {
|
|
2915
|
+
properties.description = system.description;
|
|
2916
|
+
}
|
|
2917
|
+
if (system.epoch) {
|
|
2918
|
+
properties.epoch = system.epoch;
|
|
2919
|
+
}
|
|
2920
|
+
if (system.referencePlane) {
|
|
2921
|
+
properties.referencePlane = system.referencePlane;
|
|
2922
|
+
}
|
|
2585
2923
|
return properties;
|
|
2586
2924
|
}
|
|
2587
2925
|
function materializeDraftSystemInfo(system) {
|
|
2588
|
-
const
|
|
2926
|
+
const info2 = {
|
|
2589
2927
|
...system.atlasMetadata
|
|
2590
2928
|
};
|
|
2591
2929
|
if (system.defaults.theme) {
|
|
2592
|
-
|
|
2930
|
+
info2["atlas.theme"] = system.defaults.theme;
|
|
2593
2931
|
}
|
|
2594
2932
|
for (const viewpoint of system.viewpoints) {
|
|
2595
2933
|
const prefix = `viewpoint.${viewpoint.id}`;
|
|
2596
|
-
|
|
2934
|
+
info2[`${prefix}.label`] = viewpoint.label;
|
|
2597
2935
|
if (viewpoint.summary) {
|
|
2598
|
-
|
|
2936
|
+
info2[`${prefix}.summary`] = viewpoint.summary;
|
|
2599
2937
|
}
|
|
2600
2938
|
if (viewpoint.focusObjectId) {
|
|
2601
|
-
|
|
2939
|
+
info2[`${prefix}.focus`] = viewpoint.focusObjectId;
|
|
2602
2940
|
}
|
|
2603
2941
|
if (viewpoint.selectedObjectId) {
|
|
2604
|
-
|
|
2942
|
+
info2[`${prefix}.select`] = viewpoint.selectedObjectId;
|
|
2605
2943
|
}
|
|
2606
2944
|
if (viewpoint.projection) {
|
|
2607
|
-
|
|
2945
|
+
info2[`${prefix}.projection`] = viewpoint.projection;
|
|
2608
2946
|
}
|
|
2609
2947
|
if (viewpoint.preset) {
|
|
2610
|
-
|
|
2948
|
+
info2[`${prefix}.preset`] = viewpoint.preset;
|
|
2611
2949
|
}
|
|
2612
2950
|
if (viewpoint.zoom !== null) {
|
|
2613
|
-
|
|
2951
|
+
info2[`${prefix}.zoom`] = String(viewpoint.zoom);
|
|
2614
2952
|
}
|
|
2615
2953
|
if (viewpoint.rotationDeg !== 0) {
|
|
2616
|
-
|
|
2954
|
+
info2[`${prefix}.rotation`] = String(viewpoint.rotationDeg);
|
|
2617
2955
|
}
|
|
2618
2956
|
const serializedLayers = serializeViewpointLayers(viewpoint.layers);
|
|
2619
2957
|
if (serializedLayers) {
|
|
2620
|
-
|
|
2958
|
+
info2[`${prefix}.layers`] = serializedLayers;
|
|
2621
2959
|
}
|
|
2622
2960
|
if (viewpoint.filter?.query) {
|
|
2623
|
-
|
|
2961
|
+
info2[`${prefix}.query`] = viewpoint.filter.query;
|
|
2624
2962
|
}
|
|
2625
2963
|
if ((viewpoint.filter?.objectTypes.length ?? 0) > 0) {
|
|
2626
|
-
|
|
2964
|
+
info2[`${prefix}.types`] = viewpoint.filter?.objectTypes.join(" ") ?? "";
|
|
2627
2965
|
}
|
|
2628
2966
|
if ((viewpoint.filter?.tags.length ?? 0) > 0) {
|
|
2629
|
-
|
|
2967
|
+
info2[`${prefix}.tags`] = viewpoint.filter?.tags.join(" ") ?? "";
|
|
2630
2968
|
}
|
|
2631
2969
|
if ((viewpoint.filter?.groupIds.length ?? 0) > 0) {
|
|
2632
|
-
|
|
2970
|
+
info2[`${prefix}.groups`] = viewpoint.filter?.groupIds.join(" ") ?? "";
|
|
2971
|
+
}
|
|
2972
|
+
if (viewpoint.events.length > 0) {
|
|
2973
|
+
info2[`${prefix}.events`] = viewpoint.events.join(" ");
|
|
2633
2974
|
}
|
|
2634
2975
|
}
|
|
2635
2976
|
for (const annotation of system.annotations) {
|
|
2636
2977
|
const prefix = `annotation.${annotation.id}`;
|
|
2637
|
-
|
|
2978
|
+
info2[`${prefix}.label`] = annotation.label;
|
|
2638
2979
|
if (annotation.targetObjectId) {
|
|
2639
|
-
|
|
2980
|
+
info2[`${prefix}.target`] = annotation.targetObjectId;
|
|
2640
2981
|
}
|
|
2641
|
-
|
|
2982
|
+
info2[`${prefix}.body`] = annotation.body;
|
|
2642
2983
|
if (annotation.tags.length > 0) {
|
|
2643
|
-
|
|
2984
|
+
info2[`${prefix}.tags`] = annotation.tags.join(" ");
|
|
2644
2985
|
}
|
|
2645
2986
|
if (annotation.sourceObjectId) {
|
|
2646
|
-
|
|
2987
|
+
info2[`${prefix}.source`] = annotation.sourceObjectId;
|
|
2647
2988
|
}
|
|
2648
2989
|
}
|
|
2649
|
-
return
|
|
2990
|
+
return info2;
|
|
2650
2991
|
}
|
|
2651
2992
|
function serializeViewpointLayers(layers) {
|
|
2652
2993
|
const tokens = [];
|
|
@@ -2655,7 +2996,7 @@
|
|
|
2655
2996
|
if (orbitFront !== void 0 || orbitBack !== void 0) {
|
|
2656
2997
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
2657
2998
|
}
|
|
2658
|
-
for (const key of ["background", "guides", "objects", "labels", "metadata"]) {
|
|
2999
|
+
for (const key of ["background", "guides", "relations", "events", "objects", "labels", "metadata"]) {
|
|
2659
3000
|
if (layers[key] !== void 0) {
|
|
2660
3001
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
2661
3002
|
}
|
|
@@ -2665,7 +3006,8 @@
|
|
|
2665
3006
|
function convertAtlasDocumentToLegacyDraft(document) {
|
|
2666
3007
|
return {
|
|
2667
3008
|
...document,
|
|
2668
|
-
version: "2.0-draft"
|
|
3009
|
+
version: "2.0-draft",
|
|
3010
|
+
schemaVersion: "2.0-draft"
|
|
2669
3011
|
};
|
|
2670
3012
|
}
|
|
2671
3013
|
|
|
@@ -2707,19 +3049,28 @@
|
|
|
2707
3049
|
];
|
|
2708
3050
|
function formatDocument(document, options = {}) {
|
|
2709
3051
|
const schema = options.schema ?? "auto";
|
|
2710
|
-
const useDraft = schema === "2.0" || schema === "2.0-draft" || document.version === "2.0" || document.version === "2.0-draft";
|
|
3052
|
+
const useDraft = schema === "2.0" || schema === "2.1" || schema === "2.0-draft" || document.version === "2.0" || document.version === "2.1" || document.version === "2.0-draft";
|
|
2711
3053
|
if (useDraft) {
|
|
2712
3054
|
if (schema === "2.0-draft") {
|
|
2713
|
-
const legacyDraftDocument = document.version === "2.0-draft" ? document : document.version === "2.0" ? {
|
|
3055
|
+
const legacyDraftDocument = document.version === "2.0-draft" ? document : document.version === "2.0" || document.version === "2.1" ? {
|
|
2714
3056
|
...document,
|
|
2715
|
-
version: "2.0-draft"
|
|
3057
|
+
version: "2.0-draft",
|
|
3058
|
+
schemaVersion: "2.0-draft"
|
|
2716
3059
|
} : upgradeDocumentToDraftV2(document);
|
|
2717
3060
|
return formatDraftDocument(legacyDraftDocument);
|
|
2718
3061
|
}
|
|
2719
|
-
const atlasDocument = document.version === "2.0" ? document : document.version === "2.0-draft" ? {
|
|
3062
|
+
const atlasDocument = document.version === "2.0" || document.version === "2.1" ? document : document.version === "2.0-draft" ? {
|
|
2720
3063
|
...document,
|
|
2721
|
-
version: "2.0"
|
|
3064
|
+
version: "2.0",
|
|
3065
|
+
schemaVersion: "2.0"
|
|
2722
3066
|
} : upgradeDocumentToV2(document);
|
|
3067
|
+
if (schema === "2.1" && atlasDocument.version !== "2.1") {
|
|
3068
|
+
return formatAtlasDocument({
|
|
3069
|
+
...atlasDocument,
|
|
3070
|
+
version: "2.1",
|
|
3071
|
+
schemaVersion: "2.1"
|
|
3072
|
+
});
|
|
3073
|
+
}
|
|
2723
3074
|
return formatAtlasDocument(atlasDocument);
|
|
2724
3075
|
}
|
|
2725
3076
|
const lines = [];
|
|
@@ -2737,10 +3088,22 @@
|
|
|
2737
3088
|
return lines.join("\n");
|
|
2738
3089
|
}
|
|
2739
3090
|
function formatAtlasDocument(document) {
|
|
2740
|
-
const lines = [
|
|
3091
|
+
const lines = [`schema ${document.version}`, ""];
|
|
2741
3092
|
if (document.system) {
|
|
2742
3093
|
lines.push(...formatAtlasSystem(document.system));
|
|
2743
3094
|
}
|
|
3095
|
+
for (const group of [...document.groups].sort(compareIdLike)) {
|
|
3096
|
+
lines.push("");
|
|
3097
|
+
lines.push(...formatAtlasGroup(group));
|
|
3098
|
+
}
|
|
3099
|
+
for (const relation of [...document.relations].sort(compareIdLike)) {
|
|
3100
|
+
lines.push("");
|
|
3101
|
+
lines.push(...formatAtlasRelation(relation));
|
|
3102
|
+
}
|
|
3103
|
+
for (const event of [...document.events].sort(compareIdLike)) {
|
|
3104
|
+
lines.push("");
|
|
3105
|
+
lines.push(...formatAtlasEvent(event));
|
|
3106
|
+
}
|
|
2744
3107
|
const sortedObjects = [...document.objects].sort(compareObjects);
|
|
2745
3108
|
if (sortedObjects.length > 0 && lines.at(-1) !== "") {
|
|
2746
3109
|
lines.push("");
|
|
@@ -2756,12 +3119,25 @@
|
|
|
2756
3119
|
function formatDraftDocument(document) {
|
|
2757
3120
|
const legacy = document.version === "2.0-draft" ? document : {
|
|
2758
3121
|
...document,
|
|
2759
|
-
version: "2.0-draft"
|
|
3122
|
+
version: "2.0-draft",
|
|
3123
|
+
schemaVersion: "2.0-draft"
|
|
2760
3124
|
};
|
|
2761
3125
|
const lines = ["schema 2.0-draft", ""];
|
|
2762
3126
|
if (legacy.system) {
|
|
2763
3127
|
lines.push(...formatAtlasSystem(legacy.system));
|
|
2764
3128
|
}
|
|
3129
|
+
for (const group of [...legacy.groups].sort(compareIdLike)) {
|
|
3130
|
+
lines.push("");
|
|
3131
|
+
lines.push(...formatAtlasGroup(group));
|
|
3132
|
+
}
|
|
3133
|
+
for (const relation of [...legacy.relations].sort(compareIdLike)) {
|
|
3134
|
+
lines.push("");
|
|
3135
|
+
lines.push(...formatAtlasRelation(relation));
|
|
3136
|
+
}
|
|
3137
|
+
for (const event of [...legacy.events].sort(compareIdLike)) {
|
|
3138
|
+
lines.push("");
|
|
3139
|
+
lines.push(...formatAtlasEvent(event));
|
|
3140
|
+
}
|
|
2765
3141
|
const sortedObjects = [...legacy.objects].sort(compareObjects);
|
|
2766
3142
|
if (sortedObjects.length > 0 && lines.at(-1) !== "") {
|
|
2767
3143
|
lines.push("");
|
|
@@ -2777,11 +3153,38 @@
|
|
|
2777
3153
|
function formatSystem(system) {
|
|
2778
3154
|
return formatLines("system", system.id, system.properties, null, system.info);
|
|
2779
3155
|
}
|
|
3156
|
+
function formatLines(objectType, id, properties, placement, info2) {
|
|
3157
|
+
const lines = [`${objectType} ${id}`];
|
|
3158
|
+
const fieldLines = [...formatPlacement(placement), ...formatProperties(properties)];
|
|
3159
|
+
for (const fieldLine of fieldLines) {
|
|
3160
|
+
lines.push(` ${fieldLine}`);
|
|
3161
|
+
}
|
|
3162
|
+
const infoEntries = Object.entries(info2).sort(([left], [right]) => left.localeCompare(right));
|
|
3163
|
+
if (infoEntries.length > 0) {
|
|
3164
|
+
if (fieldLines.length > 0) {
|
|
3165
|
+
lines.push("");
|
|
3166
|
+
}
|
|
3167
|
+
lines.push(" info");
|
|
3168
|
+
for (const [key, value] of infoEntries) {
|
|
3169
|
+
lines.push(` ${key} ${quoteIfNeeded(value)}`);
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
return lines;
|
|
3173
|
+
}
|
|
2780
3174
|
function formatAtlasSystem(system) {
|
|
2781
3175
|
const lines = [`system ${system.id}`];
|
|
2782
3176
|
if (system.title) {
|
|
2783
3177
|
lines.push(` title ${quoteIfNeeded(system.title)}`);
|
|
2784
3178
|
}
|
|
3179
|
+
if (system.description) {
|
|
3180
|
+
lines.push(` description ${quoteIfNeeded(system.description)}`);
|
|
3181
|
+
}
|
|
3182
|
+
if (system.epoch) {
|
|
3183
|
+
lines.push(` epoch ${quoteIfNeeded(system.epoch)}`);
|
|
3184
|
+
}
|
|
3185
|
+
if (system.referencePlane) {
|
|
3186
|
+
lines.push(` referencePlane ${quoteIfNeeded(system.referencePlane)}`);
|
|
3187
|
+
}
|
|
2785
3188
|
lines.push("");
|
|
2786
3189
|
lines.push("defaults");
|
|
2787
3190
|
lines.push(` view ${system.defaults.view}`);
|
|
@@ -2816,18 +3219,22 @@
|
|
|
2816
3219
|
return lines;
|
|
2817
3220
|
}
|
|
2818
3221
|
function formatObject(object) {
|
|
2819
|
-
return
|
|
3222
|
+
return formatWorldOrbitObject(object.type, object.id, object);
|
|
2820
3223
|
}
|
|
2821
3224
|
function formatAtlasObject(object) {
|
|
2822
|
-
return
|
|
3225
|
+
return formatWorldOrbitObject(`object ${object.type}`, object.id, object);
|
|
2823
3226
|
}
|
|
2824
|
-
function
|
|
3227
|
+
function formatWorldOrbitObject(objectType, id, object) {
|
|
2825
3228
|
const lines = [`${objectType} ${id}`];
|
|
2826
|
-
const fieldLines = [
|
|
3229
|
+
const fieldLines = [
|
|
3230
|
+
...formatPlacement(object.placement),
|
|
3231
|
+
...formatProperties(object.properties),
|
|
3232
|
+
...formatObjectMetadata(object)
|
|
3233
|
+
];
|
|
2827
3234
|
for (const fieldLine of fieldLines) {
|
|
2828
3235
|
lines.push(` ${fieldLine}`);
|
|
2829
3236
|
}
|
|
2830
|
-
const infoEntries = Object.entries(info).sort(([left], [right]) => left.localeCompare(right));
|
|
3237
|
+
const infoEntries = Object.entries(object.info).sort(([left], [right]) => left.localeCompare(right));
|
|
2831
3238
|
if (infoEntries.length > 0) {
|
|
2832
3239
|
if (fieldLines.length > 0) {
|
|
2833
3240
|
lines.push("");
|
|
@@ -2837,6 +3244,16 @@
|
|
|
2837
3244
|
lines.push(` ${key} ${quoteIfNeeded(value)}`);
|
|
2838
3245
|
}
|
|
2839
3246
|
}
|
|
3247
|
+
for (const blockName of ["climate", "habitability", "settlement"]) {
|
|
3248
|
+
const blockEntries = Object.entries(object.typedBlocks?.[blockName] ?? {}).sort(([left], [right]) => left.localeCompare(right));
|
|
3249
|
+
if (blockEntries.length > 0) {
|
|
3250
|
+
lines.push("");
|
|
3251
|
+
lines.push(` ${blockName}`);
|
|
3252
|
+
for (const [key, value] of blockEntries) {
|
|
3253
|
+
lines.push(` ${key} ${quoteIfNeeded(value)}`);
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
2840
3257
|
return lines;
|
|
2841
3258
|
}
|
|
2842
3259
|
function formatPlacement(placement) {
|
|
@@ -2865,6 +3282,46 @@
|
|
|
2865
3282
|
function formatProperties(properties) {
|
|
2866
3283
|
return Object.keys(properties).sort(compareFieldKeys).map((key) => `${key} ${formatValue(properties[key])}`);
|
|
2867
3284
|
}
|
|
3285
|
+
function formatObjectMetadata(object) {
|
|
3286
|
+
const lines = [];
|
|
3287
|
+
if (object.groups?.length) {
|
|
3288
|
+
lines.push(`groups ${object.groups.join(" ")}`);
|
|
3289
|
+
}
|
|
3290
|
+
if (object.epoch) {
|
|
3291
|
+
lines.push(`epoch ${quoteIfNeeded(object.epoch)}`);
|
|
3292
|
+
}
|
|
3293
|
+
if (object.referencePlane) {
|
|
3294
|
+
lines.push(`referencePlane ${quoteIfNeeded(object.referencePlane)}`);
|
|
3295
|
+
}
|
|
3296
|
+
if (object.tidalLock !== void 0) {
|
|
3297
|
+
lines.push(`tidalLock ${object.tidalLock ? "true" : "false"}`);
|
|
3298
|
+
}
|
|
3299
|
+
if (object.renderHints?.renderLabel !== void 0) {
|
|
3300
|
+
lines.push(`renderLabel ${object.renderHints.renderLabel ? "true" : "false"}`);
|
|
3301
|
+
}
|
|
3302
|
+
if (object.renderHints?.renderOrbit !== void 0) {
|
|
3303
|
+
lines.push(`renderOrbit ${object.renderHints.renderOrbit ? "true" : "false"}`);
|
|
3304
|
+
}
|
|
3305
|
+
if (object.renderHints?.renderPriority !== void 0) {
|
|
3306
|
+
lines.push(`renderPriority ${object.renderHints.renderPriority}`);
|
|
3307
|
+
}
|
|
3308
|
+
if (object.resonance) {
|
|
3309
|
+
lines.push(`resonance ${object.resonance.targetObjectId} ${object.resonance.ratio}`);
|
|
3310
|
+
}
|
|
3311
|
+
for (const rule of object.deriveRules ?? []) {
|
|
3312
|
+
lines.push(`derive ${rule.field} ${rule.strategy}`);
|
|
3313
|
+
}
|
|
3314
|
+
for (const rule of object.validationRules ?? []) {
|
|
3315
|
+
lines.push(`validate ${rule.rule}`);
|
|
3316
|
+
}
|
|
3317
|
+
if (object.lockedFields?.length) {
|
|
3318
|
+
lines.push(`locked ${object.lockedFields.join(" ")}`);
|
|
3319
|
+
}
|
|
3320
|
+
for (const tolerance of object.tolerances ?? []) {
|
|
3321
|
+
lines.push(`tolerance ${tolerance.field} ${formatValue(tolerance.value)}`);
|
|
3322
|
+
}
|
|
3323
|
+
return lines;
|
|
3324
|
+
}
|
|
2868
3325
|
function formatAtlasViewpoint(viewpoint) {
|
|
2869
3326
|
const lines = [`viewpoint ${viewpoint.id}`, ` label ${quoteIfNeeded(viewpoint.label)}`];
|
|
2870
3327
|
if (viewpoint.focusObjectId) {
|
|
@@ -2892,6 +3349,9 @@
|
|
|
2892
3349
|
if (layerTokens.length > 0) {
|
|
2893
3350
|
lines.push(` layers ${layerTokens.join(" ")}`);
|
|
2894
3351
|
}
|
|
3352
|
+
if (viewpoint.events.length > 0) {
|
|
3353
|
+
lines.push(` events ${viewpoint.events.join(" ")}`);
|
|
3354
|
+
}
|
|
2895
3355
|
if (viewpoint.filter) {
|
|
2896
3356
|
lines.push(" filter");
|
|
2897
3357
|
if (viewpoint.filter.query) {
|
|
@@ -2920,6 +3380,98 @@
|
|
|
2920
3380
|
}
|
|
2921
3381
|
return lines;
|
|
2922
3382
|
}
|
|
3383
|
+
function formatAtlasGroup(group) {
|
|
3384
|
+
const lines = [`group ${group.id}`, ` label ${quoteIfNeeded(group.label)}`];
|
|
3385
|
+
if (group.summary) {
|
|
3386
|
+
lines.push(` summary ${quoteIfNeeded(group.summary)}`);
|
|
3387
|
+
}
|
|
3388
|
+
if (group.color) {
|
|
3389
|
+
lines.push(` color ${quoteIfNeeded(group.color)}`);
|
|
3390
|
+
}
|
|
3391
|
+
if (group.tags.length > 0) {
|
|
3392
|
+
lines.push(` tags ${group.tags.map(quoteIfNeeded).join(" ")}`);
|
|
3393
|
+
}
|
|
3394
|
+
if (group.hidden) {
|
|
3395
|
+
lines.push(" hidden true");
|
|
3396
|
+
}
|
|
3397
|
+
return lines;
|
|
3398
|
+
}
|
|
3399
|
+
function formatAtlasRelation(relation) {
|
|
3400
|
+
const lines = [`relation ${relation.id}`];
|
|
3401
|
+
if (relation.from) {
|
|
3402
|
+
lines.push(` from ${quoteIfNeeded(relation.from)}`);
|
|
3403
|
+
}
|
|
3404
|
+
if (relation.to) {
|
|
3405
|
+
lines.push(` to ${quoteIfNeeded(relation.to)}`);
|
|
3406
|
+
}
|
|
3407
|
+
if (relation.kind) {
|
|
3408
|
+
lines.push(` kind ${quoteIfNeeded(relation.kind)}`);
|
|
3409
|
+
}
|
|
3410
|
+
if (relation.label) {
|
|
3411
|
+
lines.push(` label ${quoteIfNeeded(relation.label)}`);
|
|
3412
|
+
}
|
|
3413
|
+
if (relation.summary) {
|
|
3414
|
+
lines.push(` summary ${quoteIfNeeded(relation.summary)}`);
|
|
3415
|
+
}
|
|
3416
|
+
if (relation.tags.length > 0) {
|
|
3417
|
+
lines.push(` tags ${relation.tags.map(quoteIfNeeded).join(" ")}`);
|
|
3418
|
+
}
|
|
3419
|
+
if (relation.color) {
|
|
3420
|
+
lines.push(` color ${quoteIfNeeded(relation.color)}`);
|
|
3421
|
+
}
|
|
3422
|
+
if (relation.hidden) {
|
|
3423
|
+
lines.push(" hidden true");
|
|
3424
|
+
}
|
|
3425
|
+
return lines;
|
|
3426
|
+
}
|
|
3427
|
+
function formatAtlasEvent(event) {
|
|
3428
|
+
const lines = [`event ${event.id}`, ` kind ${quoteIfNeeded(event.kind)}`];
|
|
3429
|
+
if (event.label) {
|
|
3430
|
+
lines.push(` label ${quoteIfNeeded(event.label)}`);
|
|
3431
|
+
}
|
|
3432
|
+
if (event.summary) {
|
|
3433
|
+
lines.push(` summary ${quoteIfNeeded(event.summary)}`);
|
|
3434
|
+
}
|
|
3435
|
+
if (event.targetObjectId) {
|
|
3436
|
+
lines.push(` target ${event.targetObjectId}`);
|
|
3437
|
+
}
|
|
3438
|
+
if (event.participantObjectIds.length > 0) {
|
|
3439
|
+
lines.push(` participants ${event.participantObjectIds.join(" ")}`);
|
|
3440
|
+
}
|
|
3441
|
+
if (event.timing) {
|
|
3442
|
+
lines.push(` timing ${quoteIfNeeded(event.timing)}`);
|
|
3443
|
+
}
|
|
3444
|
+
if (event.visibility) {
|
|
3445
|
+
lines.push(` visibility ${quoteIfNeeded(event.visibility)}`);
|
|
3446
|
+
}
|
|
3447
|
+
if (event.tags.length > 0) {
|
|
3448
|
+
lines.push(` tags ${event.tags.map(quoteIfNeeded).join(" ")}`);
|
|
3449
|
+
}
|
|
3450
|
+
if (event.color) {
|
|
3451
|
+
lines.push(` color ${quoteIfNeeded(event.color)}`);
|
|
3452
|
+
}
|
|
3453
|
+
if (event.hidden) {
|
|
3454
|
+
lines.push(" hidden true");
|
|
3455
|
+
}
|
|
3456
|
+
if (event.positions.length > 0) {
|
|
3457
|
+
lines.push("");
|
|
3458
|
+
lines.push(" positions");
|
|
3459
|
+
for (const pose of [...event.positions].sort(comparePoseObjectId)) {
|
|
3460
|
+
lines.push(` pose ${pose.objectId}`);
|
|
3461
|
+
for (const fieldLine of formatEventPoseFields(pose)) {
|
|
3462
|
+
lines.push(` ${fieldLine}`);
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
return lines;
|
|
3467
|
+
}
|
|
3468
|
+
function formatEventPoseFields(pose) {
|
|
3469
|
+
return [
|
|
3470
|
+
...formatPlacement(pose.placement),
|
|
3471
|
+
...formatOptionalUnit("inner", pose.inner),
|
|
3472
|
+
...formatOptionalUnit("outer", pose.outer)
|
|
3473
|
+
];
|
|
3474
|
+
}
|
|
2923
3475
|
function formatValue(value) {
|
|
2924
3476
|
if (Array.isArray(value)) {
|
|
2925
3477
|
return value.map((item) => quoteIfNeeded(item)).join(" ");
|
|
@@ -2961,7 +3513,7 @@
|
|
|
2961
3513
|
if (orbitFront !== void 0 || orbitBack !== void 0) {
|
|
2962
3514
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
2963
3515
|
}
|
|
2964
|
-
for (const key of ["background", "guides", "objects", "labels", "metadata"]) {
|
|
3516
|
+
for (const key of ["background", "guides", "relations", "events", "objects", "labels", "metadata"]) {
|
|
2965
3517
|
if (layers[key] !== void 0) {
|
|
2966
3518
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
2967
3519
|
}
|
|
@@ -2986,6 +3538,12 @@
|
|
|
2986
3538
|
return leftIndex - rightIndex;
|
|
2987
3539
|
return left.id.localeCompare(right.id);
|
|
2988
3540
|
}
|
|
3541
|
+
function compareIdLike(left, right) {
|
|
3542
|
+
return left.id.localeCompare(right.id);
|
|
3543
|
+
}
|
|
3544
|
+
function comparePoseObjectId(left, right) {
|
|
3545
|
+
return left.objectId.localeCompare(right.objectId);
|
|
3546
|
+
}
|
|
2989
3547
|
function objectTypeIndex(objectType) {
|
|
2990
3548
|
switch (objectType) {
|
|
2991
3549
|
case "star":
|
|
@@ -3015,107 +3573,760 @@
|
|
|
3015
3573
|
return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
3016
3574
|
}
|
|
3017
3575
|
|
|
3018
|
-
// packages/core/dist/
|
|
3019
|
-
|
|
3020
|
-
|
|
3576
|
+
// packages/core/dist/atlas-utils.js
|
|
3577
|
+
var UNIT_PATTERN2 = /^(-?\d+(?:\.\d+)?)(kpc|min|mj|rj|ky|my|gy|au|km|me|re|pc|ly|deg|sol|K|m|s|h|d|y)?$/;
|
|
3578
|
+
var BOOLEAN_VALUES2 = /* @__PURE__ */ new Map([
|
|
3579
|
+
["true", true],
|
|
3580
|
+
["false", false],
|
|
3581
|
+
["yes", true],
|
|
3582
|
+
["no", false]
|
|
3583
|
+
]);
|
|
3584
|
+
var URL_SCHEME_PATTERN2 = /^[A-Za-z][A-Za-z0-9+.-]*:/;
|
|
3585
|
+
function normalizeIdentifier2(value) {
|
|
3586
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3021
3587
|
}
|
|
3022
|
-
function
|
|
3023
|
-
return
|
|
3588
|
+
function humanizeIdentifier3(value) {
|
|
3589
|
+
return value.split(/[-_]+/).filter(Boolean).map((segment) => segment[0].toUpperCase() + segment.slice(1)).join(" ");
|
|
3024
3590
|
}
|
|
3025
|
-
function
|
|
3026
|
-
const
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
let system = null;
|
|
3030
|
-
let section = null;
|
|
3031
|
-
const objectNodes = [];
|
|
3032
|
-
let sawDefaults = false;
|
|
3033
|
-
let sawAtlas = false;
|
|
3034
|
-
const viewpointIds = /* @__PURE__ */ new Set();
|
|
3035
|
-
const annotationIds = /* @__PURE__ */ new Set();
|
|
3036
|
-
for (let index = 0; index < lines.length; index++) {
|
|
3037
|
-
const rawLine = lines[index];
|
|
3038
|
-
const lineNumber = index + 1;
|
|
3039
|
-
if (!rawLine.trim()) {
|
|
3040
|
-
continue;
|
|
3041
|
-
}
|
|
3042
|
-
const indent = getIndent(rawLine);
|
|
3043
|
-
const tokens = tokenizeLineDetailed(rawLine.slice(indent), {
|
|
3044
|
-
line: lineNumber,
|
|
3045
|
-
columnOffset: indent
|
|
3046
|
-
});
|
|
3047
|
-
if (tokens.length === 0) {
|
|
3048
|
-
continue;
|
|
3049
|
-
}
|
|
3050
|
-
if (!sawSchemaHeader) {
|
|
3051
|
-
schemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
3052
|
-
sawSchemaHeader = true;
|
|
3053
|
-
continue;
|
|
3054
|
-
}
|
|
3055
|
-
if (indent === 0) {
|
|
3056
|
-
section = startTopLevelSection(tokens, lineNumber, system, objectNodes, viewpointIds, annotationIds, {
|
|
3057
|
-
sawDefaults,
|
|
3058
|
-
sawAtlas
|
|
3059
|
-
});
|
|
3060
|
-
if (section.kind === "system") {
|
|
3061
|
-
system = section.system;
|
|
3062
|
-
} else if (section.kind === "defaults") {
|
|
3063
|
-
sawDefaults = true;
|
|
3064
|
-
} else if (section.kind === "atlas") {
|
|
3065
|
-
sawAtlas = true;
|
|
3066
|
-
}
|
|
3067
|
-
continue;
|
|
3068
|
-
}
|
|
3069
|
-
if (!section) {
|
|
3070
|
-
throw new WorldOrbitError("Indented line without parent atlas section", lineNumber, indent + 1);
|
|
3071
|
-
}
|
|
3072
|
-
handleSectionLine(section, indent, tokens, lineNumber);
|
|
3073
|
-
}
|
|
3074
|
-
if (!sawSchemaHeader) {
|
|
3075
|
-
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
|
|
3591
|
+
function parseAtlasUnitValue(input, location, fieldKey) {
|
|
3592
|
+
const match = input.match(UNIT_PATTERN2);
|
|
3593
|
+
if (!match) {
|
|
3594
|
+
throw WorldOrbitError.fromLocation(`Invalid unit value "${input}"`, location);
|
|
3076
3595
|
}
|
|
3077
|
-
const
|
|
3078
|
-
|
|
3079
|
-
|
|
3596
|
+
const unitValue = {
|
|
3597
|
+
value: Number(match[1]),
|
|
3598
|
+
unit: match[2] ?? null
|
|
3080
3599
|
};
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
system: null,
|
|
3086
|
-
objects: normalizedObjects
|
|
3087
|
-
});
|
|
3088
|
-
const diagnostics = schemaVersion === "2.0-draft" && outputVersion === "2.0" ? [
|
|
3089
|
-
{
|
|
3090
|
-
code: "load.schema.deprecatedDraft",
|
|
3091
|
-
severity: "warning",
|
|
3092
|
-
source: "upgrade",
|
|
3093
|
-
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
3600
|
+
if (fieldKey) {
|
|
3601
|
+
const schema = getFieldSchema(fieldKey);
|
|
3602
|
+
if (schema?.unitFamily && !unitFamilyAllowsUnit(schema.unitFamily, unitValue.unit)) {
|
|
3603
|
+
throw WorldOrbitError.fromLocation(`Unit "${unitValue.unit ?? "none"}" is not valid for "${fieldKey}"`, location);
|
|
3094
3604
|
}
|
|
3095
|
-
|
|
3605
|
+
}
|
|
3606
|
+
return unitValue;
|
|
3607
|
+
}
|
|
3608
|
+
function tryParseAtlasUnitValue(input) {
|
|
3609
|
+
const match = input.match(UNIT_PATTERN2);
|
|
3610
|
+
if (!match) {
|
|
3611
|
+
return null;
|
|
3612
|
+
}
|
|
3096
3613
|
return {
|
|
3614
|
+
value: Number(match[1]),
|
|
3615
|
+
unit: match[2] ?? null
|
|
3616
|
+
};
|
|
3617
|
+
}
|
|
3618
|
+
function parseAtlasNumber(input, key, location) {
|
|
3619
|
+
const value = Number(input);
|
|
3620
|
+
if (!Number.isFinite(value)) {
|
|
3621
|
+
throw WorldOrbitError.fromLocation(`Invalid numeric value "${input}" for "${key}"`, location);
|
|
3622
|
+
}
|
|
3623
|
+
return value;
|
|
3624
|
+
}
|
|
3625
|
+
function parseAtlasBoolean(input, key, location) {
|
|
3626
|
+
const parsed = BOOLEAN_VALUES2.get(input.toLowerCase());
|
|
3627
|
+
if (parsed === void 0) {
|
|
3628
|
+
throw WorldOrbitError.fromLocation(`Invalid boolean value "${input}" for "${key}"`, location);
|
|
3629
|
+
}
|
|
3630
|
+
return parsed;
|
|
3631
|
+
}
|
|
3632
|
+
function parseAtlasAtReference(target, location) {
|
|
3633
|
+
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
3634
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
3635
|
+
}
|
|
3636
|
+
const pairedMatch = target.match(/^([A-Za-z0-9._-]+)-([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
3637
|
+
if (pairedMatch) {
|
|
3638
|
+
return {
|
|
3639
|
+
kind: "lagrange",
|
|
3640
|
+
primary: pairedMatch[1],
|
|
3641
|
+
secondary: pairedMatch[2],
|
|
3642
|
+
point: pairedMatch[3]
|
|
3643
|
+
};
|
|
3644
|
+
}
|
|
3645
|
+
const simpleMatch = target.match(/^([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
3646
|
+
if (simpleMatch) {
|
|
3647
|
+
return {
|
|
3648
|
+
kind: "lagrange",
|
|
3649
|
+
primary: simpleMatch[1],
|
|
3650
|
+
secondary: null,
|
|
3651
|
+
point: simpleMatch[2]
|
|
3652
|
+
};
|
|
3653
|
+
}
|
|
3654
|
+
if (/^[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
3655
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
3656
|
+
}
|
|
3657
|
+
const anchorMatch = target.match(/^([A-Za-z0-9._-]+):([A-Za-z0-9._-]+)$/);
|
|
3658
|
+
if (anchorMatch) {
|
|
3659
|
+
return {
|
|
3660
|
+
kind: "anchor",
|
|
3661
|
+
objectId: anchorMatch[1],
|
|
3662
|
+
anchor: anchorMatch[2]
|
|
3663
|
+
};
|
|
3664
|
+
}
|
|
3665
|
+
return {
|
|
3666
|
+
kind: "named",
|
|
3667
|
+
name: target
|
|
3668
|
+
};
|
|
3669
|
+
}
|
|
3670
|
+
function validateAtlasImageSource(value, location) {
|
|
3671
|
+
if (!value) {
|
|
3672
|
+
throw WorldOrbitError.fromLocation('Field "image" must not be empty', location);
|
|
3673
|
+
}
|
|
3674
|
+
if (value.startsWith("//")) {
|
|
3675
|
+
throw WorldOrbitError.fromLocation('Field "image" must use a relative path, root-relative path, or an http/https URL', location);
|
|
3676
|
+
}
|
|
3677
|
+
const schemeMatch = value.match(URL_SCHEME_PATTERN2);
|
|
3678
|
+
if (!schemeMatch) {
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3681
|
+
const scheme = schemeMatch[0].slice(0, -1).toLowerCase();
|
|
3682
|
+
if (scheme !== "http" && scheme !== "https") {
|
|
3683
|
+
throw WorldOrbitError.fromLocation(`Field "image" does not support the "${scheme}" scheme`, location);
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
function normalizeLegacyScalarValue(key, values, location) {
|
|
3687
|
+
const schema = getFieldSchema(key);
|
|
3688
|
+
if (!schema) {
|
|
3689
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
3690
|
+
}
|
|
3691
|
+
if (schema.arity === "single" && values.length !== 1) {
|
|
3692
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
3693
|
+
}
|
|
3694
|
+
switch (schema.kind) {
|
|
3695
|
+
case "list":
|
|
3696
|
+
return values;
|
|
3697
|
+
case "boolean":
|
|
3698
|
+
return parseAtlasBoolean(singleAtlasValue(values, key, location), key, location);
|
|
3699
|
+
case "number":
|
|
3700
|
+
return parseAtlasNumber(singleAtlasValue(values, key, location), key, location);
|
|
3701
|
+
case "unit":
|
|
3702
|
+
return parseAtlasUnitValue(singleAtlasValue(values, key, location), location, key);
|
|
3703
|
+
case "string": {
|
|
3704
|
+
const value = values.join(" ").trim();
|
|
3705
|
+
if (key === "image") {
|
|
3706
|
+
validateAtlasImageSource(value, location);
|
|
3707
|
+
}
|
|
3708
|
+
return value;
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
function ensureAtlasFieldSupported(key, objectType, location) {
|
|
3713
|
+
const schema = getFieldSchema(key);
|
|
3714
|
+
if (!schema) {
|
|
3715
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
3716
|
+
}
|
|
3717
|
+
if (!schema.objectTypes.includes(objectType)) {
|
|
3718
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" is not valid on "${objectType}"`, location);
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
function singleAtlasValue(values, key, location) {
|
|
3722
|
+
if (values.length !== 1) {
|
|
3723
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
3724
|
+
}
|
|
3725
|
+
return values[0];
|
|
3726
|
+
}
|
|
3727
|
+
|
|
3728
|
+
// packages/core/dist/atlas-validate.js
|
|
3729
|
+
var SURFACE_TARGET_TYPES2 = /* @__PURE__ */ new Set(["star", "planet", "moon", "asteroid", "comet"]);
|
|
3730
|
+
var EARTH_MASSES_PER_SOLAR = 332946.0487;
|
|
3731
|
+
var JUPITER_MASSES_PER_SOLAR = 1047.3486;
|
|
3732
|
+
var AU_IN_KM2 = 1495978707e-1;
|
|
3733
|
+
var EARTH_RADIUS_IN_KM2 = 6371;
|
|
3734
|
+
var SOLAR_RADIUS_IN_KM2 = 695700;
|
|
3735
|
+
var LY_IN_AU2 = 63241.077;
|
|
3736
|
+
var PC_IN_AU2 = 206264.806;
|
|
3737
|
+
var KPC_IN_AU2 = 206264806;
|
|
3738
|
+
function collectAtlasDiagnostics(document, sourceSchemaVersion) {
|
|
3739
|
+
const diagnostics = [];
|
|
3740
|
+
const objectMap = new Map(document.objects.map((object) => [object.id, object]));
|
|
3741
|
+
const groupIds = new Set(document.groups.map((group) => group.id));
|
|
3742
|
+
const eventIds = new Set(document.events.map((event) => event.id));
|
|
3743
|
+
if (!document.system) {
|
|
3744
|
+
diagnostics.push(error("validate.system.required", "Atlas documents must declare exactly one system."));
|
|
3745
|
+
}
|
|
3746
|
+
const knownIds = /* @__PURE__ */ new Map();
|
|
3747
|
+
for (const [kind, ids] of [
|
|
3748
|
+
["group", document.groups.map((group) => group.id)],
|
|
3749
|
+
["viewpoint", document.system?.viewpoints.map((viewpoint) => viewpoint.id) ?? []],
|
|
3750
|
+
["annotation", document.system?.annotations.map((annotation) => annotation.id) ?? []],
|
|
3751
|
+
["relation", document.relations.map((relation) => relation.id)],
|
|
3752
|
+
["event", document.events.map((event) => event.id)],
|
|
3753
|
+
["object", document.objects.map((object) => object.id)]
|
|
3754
|
+
]) {
|
|
3755
|
+
for (const id of ids) {
|
|
3756
|
+
const previous = knownIds.get(id);
|
|
3757
|
+
if (previous) {
|
|
3758
|
+
diagnostics.push(error("validate.id.duplicate", `Duplicate ${kind} id "${id}" already used by ${previous}.`));
|
|
3759
|
+
} else {
|
|
3760
|
+
knownIds.set(id, kind);
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
for (const relation of document.relations) {
|
|
3765
|
+
validateRelation(relation, objectMap, diagnostics);
|
|
3766
|
+
}
|
|
3767
|
+
for (const viewpoint of document.system?.viewpoints ?? []) {
|
|
3768
|
+
validateViewpoint(viewpoint.filter, viewpoint.events ?? [], groupIds, eventIds, sourceSchemaVersion, diagnostics, viewpoint.id);
|
|
3769
|
+
}
|
|
3770
|
+
for (const object of document.objects) {
|
|
3771
|
+
validateObject(object, document.system, objectMap, groupIds, diagnostics);
|
|
3772
|
+
}
|
|
3773
|
+
for (const event of document.events) {
|
|
3774
|
+
validateEvent(event, objectMap, diagnostics);
|
|
3775
|
+
}
|
|
3776
|
+
return diagnostics;
|
|
3777
|
+
}
|
|
3778
|
+
function validateRelation(relation, objectMap, diagnostics) {
|
|
3779
|
+
if (!relation.from) {
|
|
3780
|
+
diagnostics.push(error("validate.relation.from.required", `Relation "${relation.id}" is missing a "from" target.`));
|
|
3781
|
+
} else if (!objectMap.has(relation.from)) {
|
|
3782
|
+
diagnostics.push(error("validate.relation.from.unknown", `Unknown relation source "${relation.from}" on "${relation.id}".`));
|
|
3783
|
+
}
|
|
3784
|
+
if (!relation.to) {
|
|
3785
|
+
diagnostics.push(error("validate.relation.to.required", `Relation "${relation.id}" is missing a "to" target.`));
|
|
3786
|
+
} else if (!objectMap.has(relation.to)) {
|
|
3787
|
+
diagnostics.push(error("validate.relation.to.unknown", `Unknown relation target "${relation.to}" on "${relation.id}".`));
|
|
3788
|
+
}
|
|
3789
|
+
if (!relation.kind) {
|
|
3790
|
+
diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
function validateViewpoint(filter, eventRefs, groupIds, eventIds, sourceSchemaVersion, diagnostics, viewpointId) {
|
|
3794
|
+
if (sourceSchemaVersion === "2.1") {
|
|
3795
|
+
if (filter) {
|
|
3796
|
+
for (const groupId of filter.groupIds) {
|
|
3797
|
+
if (!groupIds.has(groupId)) {
|
|
3798
|
+
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpointId}".`, void 0, `viewpoint.${viewpointId}.groups`));
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
for (const eventId of eventRefs) {
|
|
3803
|
+
if (!eventIds.has(eventId)) {
|
|
3804
|
+
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${viewpointId}".`, void 0, `viewpoint.${viewpointId}.events`));
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
3810
|
+
const placement = object.placement;
|
|
3811
|
+
const orbitPlacement = placement?.mode === "orbit" ? placement : null;
|
|
3812
|
+
const parentObject = placement?.mode === "orbit" ? objectMap.get(placement.target) ?? null : null;
|
|
3813
|
+
if (object.groups) {
|
|
3814
|
+
for (const groupId of object.groups) {
|
|
3815
|
+
if (!groupIds.has(groupId)) {
|
|
3816
|
+
diagnostics.push(warn("validate.group.unknown", `Unknown group "${groupId}" on "${object.id}".`, object.id, "groups"));
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
if (orbitPlacement) {
|
|
3821
|
+
if (!objectMap.has(orbitPlacement.target)) {
|
|
3822
|
+
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
3823
|
+
}
|
|
3824
|
+
if (orbitPlacement.distance && orbitPlacement.semiMajor) {
|
|
3825
|
+
diagnostics.push(error("validate.orbit.distanceConflict", `Object "${object.id}" cannot declare both "distance" and "semiMajor".`, object.id, "distance"));
|
|
3826
|
+
}
|
|
3827
|
+
if (orbitPlacement.phase && !object.epoch && !system?.epoch) {
|
|
3828
|
+
diagnostics.push(warn("validate.phase.epochMissing", `Object "${object.id}" sets "phase" without an object or system epoch.`, object.id, "phase"));
|
|
3829
|
+
}
|
|
3830
|
+
if (orbitPlacement.inclination && !object.referencePlane && !system?.referencePlane) {
|
|
3831
|
+
diagnostics.push(warn("validate.inclination.referencePlaneMissing", `Object "${object.id}" sets "inclination" without an object or system reference plane.`, object.id, "inclination"));
|
|
3832
|
+
}
|
|
3833
|
+
if (orbitPlacement.period && !massInSolar(parentObject?.properties.mass)) {
|
|
3834
|
+
diagnostics.push(warn("validate.period.massMissing", `Object "${object.id}" sets "period" but its central mass cannot be derived.`, object.id, "period"));
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
if (placement?.mode === "surface") {
|
|
3838
|
+
const target = objectMap.get(placement.target);
|
|
3839
|
+
if (!target) {
|
|
3840
|
+
diagnostics.push(error("validate.surface.target.unknown", `Unknown placement target "${placement.target}" on "${object.id}".`, object.id, "surface"));
|
|
3841
|
+
} else if (!SURFACE_TARGET_TYPES2.has(target.type)) {
|
|
3842
|
+
diagnostics.push(error("validate.surface.target.invalid", `Surface target "${placement.target}" on "${object.id}" is not surface-capable.`, object.id, "surface"));
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3845
|
+
if (placement?.mode === "at") {
|
|
3846
|
+
if (object.type !== "structure" && object.type !== "phenomenon") {
|
|
3847
|
+
diagnostics.push(error("validate.at.objectType", `Only structures and phenomena may use "at" placement; found "${object.type}" on "${object.id}".`, object.id, "at"));
|
|
3848
|
+
}
|
|
3849
|
+
if (!validateAtTarget(object, objectMap, diagnostics)) {
|
|
3850
|
+
diagnostics.push(error("validate.at.target.unknown", `Unknown at-reference target "${placement.target}" on "${object.id}".`, object.id, "at"));
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
if (object.resonance) {
|
|
3854
|
+
const target = objectMap.get(object.resonance.targetObjectId);
|
|
3855
|
+
if (!target) {
|
|
3856
|
+
diagnostics.push(error("validate.resonance.target.unknown", `Unknown resonance target "${object.resonance.targetObjectId}" on "${object.id}".`, object.id, "resonance"));
|
|
3857
|
+
} else if (object.placement?.mode !== "orbit" || target.placement?.mode !== "orbit" || object.placement.target !== target.placement.target) {
|
|
3858
|
+
diagnostics.push(warn("validate.resonance.orbitMismatch", `Resonance target "${object.resonance.targetObjectId}" on "${object.id}" does not share a compatible orbital parent.`, object.id, "resonance"));
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
for (const rule of object.deriveRules ?? []) {
|
|
3862
|
+
if (rule.field !== "period" || rule.strategy !== "kepler") {
|
|
3863
|
+
diagnostics.push(warn("validate.derive.unsupported", `Unsupported derive rule "${rule.field} ${rule.strategy}" on "${object.id}".`, object.id, "derive"));
|
|
3864
|
+
continue;
|
|
3865
|
+
}
|
|
3866
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
3867
|
+
if (derivedPeriodDays === null) {
|
|
3868
|
+
diagnostics.push(warn("validate.derive.inputsMissing", `Object "${object.id}" requests "derive period kepler" but lacks enough input data.`, object.id, "derive"));
|
|
3869
|
+
continue;
|
|
3870
|
+
}
|
|
3871
|
+
if (!orbitPlacement?.period) {
|
|
3872
|
+
diagnostics.push(info("validate.derive.period.available", `Object "${object.id}" can derive a Kepler period of ${formatDays(derivedPeriodDays)}.`, object.id, "derive"));
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3875
|
+
for (const rule of object.validationRules ?? []) {
|
|
3876
|
+
if (rule.rule !== "kepler") {
|
|
3877
|
+
diagnostics.push(warn("validate.rule.unsupported", `Unsupported validation rule "${rule.rule}" on "${object.id}".`, object.id, "validate"));
|
|
3878
|
+
continue;
|
|
3879
|
+
}
|
|
3880
|
+
const actualPeriodDays = durationInDays(orbitPlacement?.period);
|
|
3881
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
3882
|
+
if (actualPeriodDays === null || derivedPeriodDays === null) {
|
|
3883
|
+
continue;
|
|
3884
|
+
}
|
|
3885
|
+
const toleranceDays = toleranceForField(object, "period");
|
|
3886
|
+
if (Math.abs(actualPeriodDays - derivedPeriodDays) > toleranceDays) {
|
|
3887
|
+
diagnostics.push(error("validate.kepler.mismatch", `Object "${object.id}" fails Kepler validation for "period".`, object.id, "validate"));
|
|
3888
|
+
}
|
|
3889
|
+
}
|
|
3890
|
+
}
|
|
3891
|
+
function validateEvent(event, objectMap, diagnostics) {
|
|
3892
|
+
const fieldPrefix = `event.${event.id}`;
|
|
3893
|
+
const referencedIds = /* @__PURE__ */ new Set();
|
|
3894
|
+
if (!event.kind.trim()) {
|
|
3895
|
+
diagnostics.push(error("validate.event.kind.required", `Event "${event.id}" is missing a "kind" value.`, void 0, `${fieldPrefix}.kind`));
|
|
3896
|
+
}
|
|
3897
|
+
if (!event.targetObjectId && event.participantObjectIds.length === 0) {
|
|
3898
|
+
diagnostics.push(error("validate.event.references.required", `Event "${event.id}" must define a "target" or at least one participant.`, void 0, `${fieldPrefix}.participants`));
|
|
3899
|
+
}
|
|
3900
|
+
if (event.targetObjectId) {
|
|
3901
|
+
referencedIds.add(event.targetObjectId);
|
|
3902
|
+
if (!objectMap.has(event.targetObjectId)) {
|
|
3903
|
+
diagnostics.push(error("validate.event.target.unknown", `Unknown event target "${event.targetObjectId}" on "${event.id}".`, void 0, `${fieldPrefix}.target`));
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
const seenParticipants = /* @__PURE__ */ new Set();
|
|
3907
|
+
for (const participantId of event.participantObjectIds) {
|
|
3908
|
+
referencedIds.add(participantId);
|
|
3909
|
+
if (seenParticipants.has(participantId)) {
|
|
3910
|
+
diagnostics.push(warn("validate.event.participants.duplicate", `Event "${event.id}" repeats participant "${participantId}".`, void 0, `${fieldPrefix}.participants`));
|
|
3911
|
+
continue;
|
|
3912
|
+
}
|
|
3913
|
+
seenParticipants.add(participantId);
|
|
3914
|
+
if (!objectMap.has(participantId)) {
|
|
3915
|
+
diagnostics.push(error("validate.event.participants.unknown", `Unknown event participant "${participantId}" on "${event.id}".`, void 0, `${fieldPrefix}.participants`));
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
if (event.targetObjectId && event.participantObjectIds.length > 0 && !event.participantObjectIds.includes(event.targetObjectId)) {
|
|
3919
|
+
diagnostics.push(warn("validate.event.target.notParticipant", `Event "${event.id}" defines a target outside its participants list.`, void 0, `${fieldPrefix}.target`));
|
|
3920
|
+
}
|
|
3921
|
+
if (event.positions.length === 0) {
|
|
3922
|
+
diagnostics.push(warn("validate.event.positions.missing", `Event "${event.id}" has no positions block and cannot drive a scene snapshot.`, void 0, `${fieldPrefix}.positions`));
|
|
3923
|
+
}
|
|
3924
|
+
if (/(?:^|[-_])(solar-eclipse|lunar-eclipse|transit|occultation)(?:$|[-_])/.test(event.kind) && referencedIds.size < 3) {
|
|
3925
|
+
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`));
|
|
3926
|
+
}
|
|
3927
|
+
const poseIds = /* @__PURE__ */ new Set();
|
|
3928
|
+
for (const pose of event.positions) {
|
|
3929
|
+
const poseFieldPrefix = `${fieldPrefix}.pose.${pose.objectId}`;
|
|
3930
|
+
if (poseIds.has(pose.objectId)) {
|
|
3931
|
+
diagnostics.push(error("validate.event.pose.duplicate", `Event "${event.id}" defines "${pose.objectId}" more than once in positions.`, void 0, poseFieldPrefix));
|
|
3932
|
+
continue;
|
|
3933
|
+
}
|
|
3934
|
+
poseIds.add(pose.objectId);
|
|
3935
|
+
const object = objectMap.get(pose.objectId);
|
|
3936
|
+
if (!object) {
|
|
3937
|
+
diagnostics.push(error("validate.event.pose.object.unknown", `Unknown event pose object "${pose.objectId}" on "${event.id}".`, void 0, poseFieldPrefix));
|
|
3938
|
+
continue;
|
|
3939
|
+
}
|
|
3940
|
+
if (!referencedIds.has(pose.objectId)) {
|
|
3941
|
+
diagnostics.push(warn("validate.event.pose.unreferenced", `Event pose "${pose.objectId}" on "${event.id}" is not listed in target/participants.`, void 0, poseFieldPrefix));
|
|
3942
|
+
}
|
|
3943
|
+
validateEventPose(pose, object, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
function validateEventPose(pose, object, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
3947
|
+
const placement = pose.placement;
|
|
3948
|
+
if (!placement) {
|
|
3949
|
+
diagnostics.push(error("validate.event.pose.placement.required", `Event "${eventId}" pose "${pose.objectId}" is missing a placement mode.`, void 0, fieldPrefix));
|
|
3950
|
+
return;
|
|
3951
|
+
}
|
|
3952
|
+
if (placement.mode === "orbit") {
|
|
3953
|
+
if (!objectMap.has(placement.target)) {
|
|
3954
|
+
diagnostics.push(error("validate.event.pose.orbit.target.unknown", `Unknown event orbit target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.orbit`));
|
|
3955
|
+
}
|
|
3956
|
+
if (placement.distance && placement.semiMajor) {
|
|
3957
|
+
diagnostics.push(error("validate.event.pose.orbit.distanceConflict", `Event "${eventId}" pose "${pose.objectId}" cannot declare both "distance" and "semiMajor".`, void 0, `${fieldPrefix}.distance`));
|
|
3958
|
+
}
|
|
3959
|
+
return;
|
|
3960
|
+
}
|
|
3961
|
+
if (placement.mode === "surface") {
|
|
3962
|
+
const target = objectMap.get(placement.target);
|
|
3963
|
+
if (!target) {
|
|
3964
|
+
diagnostics.push(error("validate.event.pose.surface.target.unknown", `Unknown event surface target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.surface`));
|
|
3965
|
+
} else if (!SURFACE_TARGET_TYPES2.has(target.type)) {
|
|
3966
|
+
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`));
|
|
3967
|
+
}
|
|
3968
|
+
return;
|
|
3969
|
+
}
|
|
3970
|
+
if (placement.mode === "at") {
|
|
3971
|
+
if (object.type !== "structure" && object.type !== "phenomenon") {
|
|
3972
|
+
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`));
|
|
3973
|
+
}
|
|
3974
|
+
const reference = placement.reference;
|
|
3975
|
+
if (reference.kind === "named" && !objectMap.has(reference.name)) {
|
|
3976
|
+
diagnostics.push(error("validate.event.pose.at.target.unknown", `Unknown event at-reference target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
3977
|
+
} else if (reference.kind === "anchor" && !objectMap.has(reference.objectId)) {
|
|
3978
|
+
diagnostics.push(error("validate.event.pose.anchor.target.unknown", `Unknown event anchor target "${reference.objectId}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
3979
|
+
} else if (reference.kind === "lagrange") {
|
|
3980
|
+
if (!objectMap.has(reference.primary)) {
|
|
3981
|
+
diagnostics.push(error("validate.event.pose.lagrange.primary.unknown", `Unknown event Lagrange target "${reference.primary}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
3982
|
+
} else if (reference.secondary && !objectMap.has(reference.secondary)) {
|
|
3983
|
+
diagnostics.push(error("validate.event.pose.lagrange.secondary.unknown", `Unknown event Lagrange target "${reference.secondary}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
function validateAtTarget(object, objectMap, diagnostics) {
|
|
3989
|
+
const reference = object.placement?.mode === "at" ? object.placement.reference : null;
|
|
3990
|
+
if (!reference) {
|
|
3991
|
+
return true;
|
|
3992
|
+
}
|
|
3993
|
+
if (reference.kind === "named") {
|
|
3994
|
+
return objectMap.has(reference.name);
|
|
3995
|
+
}
|
|
3996
|
+
if (reference.kind === "anchor") {
|
|
3997
|
+
if (!objectMap.has(reference.objectId)) {
|
|
3998
|
+
diagnostics.push(error("validate.anchor.target.unknown", `Unknown anchor target "${reference.objectId}" on "${object.id}".`, object.id, "at"));
|
|
3999
|
+
return false;
|
|
4000
|
+
}
|
|
4001
|
+
return true;
|
|
4002
|
+
}
|
|
4003
|
+
if (!objectMap.has(reference.primary)) {
|
|
4004
|
+
diagnostics.push(error("validate.lagrange.primary.unknown", `Unknown Lagrange reference "${reference.primary}" on "${object.id}".`, object.id, "at"));
|
|
4005
|
+
return false;
|
|
4006
|
+
}
|
|
4007
|
+
if (reference.secondary && !objectMap.has(reference.secondary)) {
|
|
4008
|
+
diagnostics.push(error("validate.lagrange.secondary.unknown", `Unknown Lagrange reference "${reference.secondary}" on "${object.id}".`, object.id, "at"));
|
|
4009
|
+
return false;
|
|
4010
|
+
}
|
|
4011
|
+
return true;
|
|
4012
|
+
}
|
|
4013
|
+
function keplerPeriodDays(object, parentObject) {
|
|
4014
|
+
const placement = object.placement;
|
|
4015
|
+
if (!placement || placement.mode !== "orbit") {
|
|
4016
|
+
return null;
|
|
4017
|
+
}
|
|
4018
|
+
const semiMajorAu = distanceInAu(placement.semiMajor ?? placement.distance);
|
|
4019
|
+
const centralMassSolar = massInSolar(parentObject?.properties.mass);
|
|
4020
|
+
if (semiMajorAu === null || centralMassSolar === null || centralMassSolar <= 0) {
|
|
4021
|
+
return null;
|
|
4022
|
+
}
|
|
4023
|
+
const periodYears = Math.sqrt(semiMajorAu ** 3 / centralMassSolar);
|
|
4024
|
+
return periodYears * 365.25;
|
|
4025
|
+
}
|
|
4026
|
+
function distanceInAu(value) {
|
|
4027
|
+
if (!value)
|
|
4028
|
+
return null;
|
|
4029
|
+
switch (value.unit) {
|
|
4030
|
+
case null:
|
|
4031
|
+
case "au":
|
|
4032
|
+
return value.value;
|
|
4033
|
+
case "km":
|
|
4034
|
+
return value.value / AU_IN_KM2;
|
|
4035
|
+
case "m":
|
|
4036
|
+
return value.value / (AU_IN_KM2 * 1e3);
|
|
4037
|
+
case "ly":
|
|
4038
|
+
return value.value * LY_IN_AU2;
|
|
4039
|
+
case "pc":
|
|
4040
|
+
return value.value * PC_IN_AU2;
|
|
4041
|
+
case "kpc":
|
|
4042
|
+
return value.value * KPC_IN_AU2;
|
|
4043
|
+
case "re":
|
|
4044
|
+
return value.value * EARTH_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
4045
|
+
case "sol":
|
|
4046
|
+
return value.value * SOLAR_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
4047
|
+
default:
|
|
4048
|
+
return null;
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
function massInSolar(value) {
|
|
4052
|
+
if (!value || typeof value !== "object" || !("value" in value)) {
|
|
4053
|
+
return null;
|
|
4054
|
+
}
|
|
4055
|
+
const unitValue = value;
|
|
4056
|
+
switch (unitValue.unit) {
|
|
4057
|
+
case null:
|
|
4058
|
+
case "sol":
|
|
4059
|
+
return unitValue.value;
|
|
4060
|
+
case "me":
|
|
4061
|
+
return unitValue.value / EARTH_MASSES_PER_SOLAR;
|
|
4062
|
+
case "mj":
|
|
4063
|
+
return unitValue.value / JUPITER_MASSES_PER_SOLAR;
|
|
4064
|
+
default:
|
|
4065
|
+
return null;
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
function durationInDays(value) {
|
|
4069
|
+
if (!value)
|
|
4070
|
+
return null;
|
|
4071
|
+
switch (value.unit) {
|
|
4072
|
+
case null:
|
|
4073
|
+
case "d":
|
|
4074
|
+
return value.value;
|
|
4075
|
+
case "s":
|
|
4076
|
+
return value.value / 86400;
|
|
4077
|
+
case "min":
|
|
4078
|
+
return value.value / 1440;
|
|
4079
|
+
case "h":
|
|
4080
|
+
return value.value / 24;
|
|
4081
|
+
case "y":
|
|
4082
|
+
return value.value * 365.25;
|
|
4083
|
+
case "ky":
|
|
4084
|
+
return value.value * 365250;
|
|
4085
|
+
case "my":
|
|
4086
|
+
return value.value * 36525e4;
|
|
4087
|
+
case "gy":
|
|
4088
|
+
return value.value * 36525e7;
|
|
4089
|
+
default:
|
|
4090
|
+
return null;
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
function toleranceForField(object, field) {
|
|
4094
|
+
const tolerance = object.tolerances?.find((entry) => entry.field === field)?.value;
|
|
4095
|
+
if (typeof tolerance === "number") {
|
|
4096
|
+
return tolerance;
|
|
4097
|
+
}
|
|
4098
|
+
if (tolerance && typeof tolerance === "object" && "value" in tolerance) {
|
|
4099
|
+
return durationInDays(tolerance) ?? 0;
|
|
4100
|
+
}
|
|
4101
|
+
return 0;
|
|
4102
|
+
}
|
|
4103
|
+
function formatDays(days) {
|
|
4104
|
+
return `${Math.round(days * 100) / 100}d`;
|
|
4105
|
+
}
|
|
4106
|
+
function error(code, message, objectId, field) {
|
|
4107
|
+
return { code, severity: "error", source: "validate", message, objectId, field };
|
|
4108
|
+
}
|
|
4109
|
+
function warn(code, message, objectId, field) {
|
|
4110
|
+
return { code, severity: "warning", source: "validate", message, objectId, field };
|
|
4111
|
+
}
|
|
4112
|
+
function info(code, message, objectId, field) {
|
|
4113
|
+
return { code, severity: "info", source: "validate", message, objectId, field };
|
|
4114
|
+
}
|
|
4115
|
+
|
|
4116
|
+
// packages/core/dist/draft-parse.js
|
|
4117
|
+
var STRUCTURED_TYPED_BLOCKS = /* @__PURE__ */ new Set([
|
|
4118
|
+
"climate",
|
|
4119
|
+
"habitability",
|
|
4120
|
+
"settlement"
|
|
4121
|
+
]);
|
|
4122
|
+
var DRAFT_OBJECT_FIELD_SPECS = /* @__PURE__ */ new Map();
|
|
4123
|
+
for (const key of [
|
|
4124
|
+
"orbit",
|
|
4125
|
+
"distance",
|
|
4126
|
+
"semiMajor",
|
|
4127
|
+
"eccentricity",
|
|
4128
|
+
"period",
|
|
4129
|
+
"angle",
|
|
4130
|
+
"inclination",
|
|
4131
|
+
"phase",
|
|
4132
|
+
"at",
|
|
4133
|
+
"surface",
|
|
4134
|
+
"free",
|
|
4135
|
+
"kind",
|
|
4136
|
+
"class",
|
|
4137
|
+
"culture",
|
|
4138
|
+
"tags",
|
|
4139
|
+
"color",
|
|
4140
|
+
"image",
|
|
4141
|
+
"hidden",
|
|
4142
|
+
"radius",
|
|
4143
|
+
"mass",
|
|
4144
|
+
"density",
|
|
4145
|
+
"gravity",
|
|
4146
|
+
"temperature",
|
|
4147
|
+
"albedo",
|
|
4148
|
+
"atmosphere",
|
|
4149
|
+
"inner",
|
|
4150
|
+
"outer",
|
|
4151
|
+
"on",
|
|
4152
|
+
"source",
|
|
4153
|
+
"cycle"
|
|
4154
|
+
]) {
|
|
4155
|
+
const schema = getFieldSchema(key);
|
|
4156
|
+
if (schema) {
|
|
4157
|
+
DRAFT_OBJECT_FIELD_SPECS.set(key, {
|
|
4158
|
+
key,
|
|
4159
|
+
version: "2.0",
|
|
4160
|
+
inlineMode: schema.arity === "multiple" ? "multiple" : "single",
|
|
4161
|
+
allowRepeat: false,
|
|
4162
|
+
legacySchema: schema
|
|
4163
|
+
});
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
for (const spec of [
|
|
4167
|
+
{ key: "groups", inlineMode: "multiple", allowRepeat: false },
|
|
4168
|
+
{ key: "epoch", inlineMode: "single", allowRepeat: false },
|
|
4169
|
+
{ key: "referencePlane", inlineMode: "single", allowRepeat: false },
|
|
4170
|
+
{ key: "tidalLock", inlineMode: "single", allowRepeat: false },
|
|
4171
|
+
{ key: "renderLabel", inlineMode: "single", allowRepeat: false },
|
|
4172
|
+
{ key: "renderOrbit", inlineMode: "single", allowRepeat: false },
|
|
4173
|
+
{ key: "renderPriority", inlineMode: "single", allowRepeat: false },
|
|
4174
|
+
{ key: "resonance", inlineMode: "pair", allowRepeat: false },
|
|
4175
|
+
{ key: "derive", inlineMode: "pair", allowRepeat: true },
|
|
4176
|
+
{ key: "validate", inlineMode: "single", allowRepeat: true },
|
|
4177
|
+
{ key: "locked", inlineMode: "multiple", allowRepeat: false },
|
|
4178
|
+
{ key: "tolerance", inlineMode: "pair", allowRepeat: true }
|
|
4179
|
+
]) {
|
|
4180
|
+
DRAFT_OBJECT_FIELD_SPECS.set(spec.key, {
|
|
4181
|
+
key: spec.key,
|
|
4182
|
+
version: "2.1",
|
|
4183
|
+
inlineMode: spec.inlineMode,
|
|
4184
|
+
allowRepeat: spec.allowRepeat
|
|
4185
|
+
});
|
|
4186
|
+
}
|
|
4187
|
+
var DRAFT_OBJECT_FIELD_KEYS = new Set(DRAFT_OBJECT_FIELD_SPECS.keys());
|
|
4188
|
+
var EVENT_POSE_FIELD_KEYS = /* @__PURE__ */ new Set([
|
|
4189
|
+
"orbit",
|
|
4190
|
+
"distance",
|
|
4191
|
+
"semiMajor",
|
|
4192
|
+
"eccentricity",
|
|
4193
|
+
"period",
|
|
4194
|
+
"angle",
|
|
4195
|
+
"inclination",
|
|
4196
|
+
"phase",
|
|
4197
|
+
"at",
|
|
4198
|
+
"surface",
|
|
4199
|
+
"free",
|
|
4200
|
+
"inner",
|
|
4201
|
+
"outer"
|
|
4202
|
+
]);
|
|
4203
|
+
function parseWorldOrbitAtlas(source) {
|
|
4204
|
+
return parseAtlasSource(source);
|
|
4205
|
+
}
|
|
4206
|
+
function parseWorldOrbitDraft(source) {
|
|
4207
|
+
return parseAtlasSource(source, "2.0-draft");
|
|
4208
|
+
}
|
|
4209
|
+
function parseAtlasSource(source, forcedOutputVersion) {
|
|
4210
|
+
const prepared = preprocessAtlasSource(source);
|
|
4211
|
+
const lines = prepared.source.split(/\r?\n/);
|
|
4212
|
+
const diagnostics = [];
|
|
4213
|
+
let sawSchemaHeader = false;
|
|
4214
|
+
let sourceSchemaVersion = "2.0";
|
|
4215
|
+
let system = null;
|
|
4216
|
+
let section = null;
|
|
4217
|
+
const objectNodes = [];
|
|
4218
|
+
const groups = [];
|
|
4219
|
+
const relations = [];
|
|
4220
|
+
const events = [];
|
|
4221
|
+
const eventPoseNodes = /* @__PURE__ */ new Map();
|
|
4222
|
+
let sawDefaults = false;
|
|
4223
|
+
let sawAtlas = false;
|
|
4224
|
+
const viewpointIds = /* @__PURE__ */ new Set();
|
|
4225
|
+
const annotationIds = /* @__PURE__ */ new Set();
|
|
4226
|
+
const groupIds = /* @__PURE__ */ new Set();
|
|
4227
|
+
const relationIds = /* @__PURE__ */ new Set();
|
|
4228
|
+
const eventIds = /* @__PURE__ */ new Set();
|
|
4229
|
+
for (let index = 0; index < lines.length; index++) {
|
|
4230
|
+
const rawLine = lines[index];
|
|
4231
|
+
const lineNumber = index + 1;
|
|
4232
|
+
if (!rawLine.trim()) {
|
|
4233
|
+
continue;
|
|
4234
|
+
}
|
|
4235
|
+
const indent = getIndent(rawLine);
|
|
4236
|
+
const tokens = tokenizeLineDetailed(rawLine.slice(indent), {
|
|
4237
|
+
line: lineNumber,
|
|
4238
|
+
columnOffset: indent
|
|
4239
|
+
});
|
|
4240
|
+
if (tokens.length === 0) {
|
|
4241
|
+
continue;
|
|
4242
|
+
}
|
|
4243
|
+
if (!sawSchemaHeader) {
|
|
4244
|
+
sourceSchemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
4245
|
+
sawSchemaHeader = true;
|
|
4246
|
+
if (prepared.comments.length > 0 && sourceSchemaVersion !== "2.1") {
|
|
4247
|
+
diagnostics.push({
|
|
4248
|
+
code: "parse.schema21.commentCompatibility",
|
|
4249
|
+
severity: "warning",
|
|
4250
|
+
source: "parse",
|
|
4251
|
+
message: `Comments require schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
4252
|
+
line: prepared.comments[0].line,
|
|
4253
|
+
column: prepared.comments[0].column
|
|
4254
|
+
});
|
|
4255
|
+
}
|
|
4256
|
+
continue;
|
|
4257
|
+
}
|
|
4258
|
+
if (indent === 0) {
|
|
4259
|
+
section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, { sawDefaults, sawAtlas });
|
|
4260
|
+
if (section.kind === "system") {
|
|
4261
|
+
system = section.system;
|
|
4262
|
+
} else if (section.kind === "defaults") {
|
|
4263
|
+
sawDefaults = true;
|
|
4264
|
+
} else if (section.kind === "atlas") {
|
|
4265
|
+
sawAtlas = true;
|
|
4266
|
+
}
|
|
4267
|
+
continue;
|
|
4268
|
+
}
|
|
4269
|
+
if (!section) {
|
|
4270
|
+
throw new WorldOrbitError("Indented line without parent atlas section", lineNumber, indent + 1);
|
|
4271
|
+
}
|
|
4272
|
+
handleSectionLine(section, indent, tokens, lineNumber);
|
|
4273
|
+
}
|
|
4274
|
+
if (!sawSchemaHeader) {
|
|
4275
|
+
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
|
|
4276
|
+
}
|
|
4277
|
+
const objects = objectNodes.map((node) => normalizeDraftObject(node, sourceSchemaVersion, diagnostics));
|
|
4278
|
+
const normalizedEvents = events.map((event) => normalizeDraftEvent(event, eventPoseNodes.get(event.id) ?? []));
|
|
4279
|
+
const outputVersion = forcedOutputVersion ?? (sourceSchemaVersion === "2.0-draft" ? "2.0" : sourceSchemaVersion);
|
|
4280
|
+
const baseDocument = {
|
|
3097
4281
|
format: "worldorbit",
|
|
3098
|
-
version: outputVersion,
|
|
3099
4282
|
sourceVersion: "1.0",
|
|
3100
4283
|
system,
|
|
3101
|
-
|
|
4284
|
+
groups,
|
|
4285
|
+
relations,
|
|
4286
|
+
events: normalizedEvents,
|
|
4287
|
+
objects,
|
|
3102
4288
|
diagnostics
|
|
3103
4289
|
};
|
|
4290
|
+
if (outputVersion === "2.0-draft") {
|
|
4291
|
+
const document2 = {
|
|
4292
|
+
...baseDocument,
|
|
4293
|
+
version: "2.0-draft",
|
|
4294
|
+
schemaVersion: "2.0-draft"
|
|
4295
|
+
};
|
|
4296
|
+
document2.diagnostics.push(...collectAtlasDiagnostics(document2, sourceSchemaVersion));
|
|
4297
|
+
return document2;
|
|
4298
|
+
}
|
|
4299
|
+
const document = {
|
|
4300
|
+
...baseDocument,
|
|
4301
|
+
version: outputVersion,
|
|
4302
|
+
schemaVersion: outputVersion
|
|
4303
|
+
};
|
|
4304
|
+
if (sourceSchemaVersion === "2.0-draft") {
|
|
4305
|
+
document.diagnostics.push({
|
|
4306
|
+
code: "load.schema.deprecatedDraft",
|
|
4307
|
+
severity: "warning",
|
|
4308
|
+
source: "upgrade",
|
|
4309
|
+
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
4310
|
+
});
|
|
4311
|
+
}
|
|
4312
|
+
document.diagnostics.push(...collectAtlasDiagnostics(document, sourceSchemaVersion));
|
|
4313
|
+
return document;
|
|
3104
4314
|
}
|
|
3105
4315
|
function assertDraftSchemaHeader(tokens, line) {
|
|
3106
|
-
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" ||
|
|
3107
|
-
throw new WorldOrbitError('Expected atlas header "schema 2.0" or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
4316
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1"].includes(tokens[1].value.toLowerCase())) {
|
|
4317
|
+
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
3108
4318
|
}
|
|
3109
|
-
|
|
4319
|
+
const version = tokens[1].value.toLowerCase();
|
|
4320
|
+
return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
3110
4321
|
}
|
|
3111
|
-
function startTopLevelSection(tokens, line, system, objectNodes, viewpointIds, annotationIds, flags) {
|
|
4322
|
+
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, flags) {
|
|
3112
4323
|
const keyword = tokens[0]?.value.toLowerCase();
|
|
3113
4324
|
switch (keyword) {
|
|
3114
4325
|
case "system":
|
|
3115
4326
|
if (system) {
|
|
3116
4327
|
throw new WorldOrbitError('Atlas section "system" may only appear once', line, tokens[0].column);
|
|
3117
4328
|
}
|
|
3118
|
-
return startSystemSection(tokens, line);
|
|
4329
|
+
return startSystemSection(tokens, line, sourceSchemaVersion, diagnostics);
|
|
3119
4330
|
case "defaults":
|
|
3120
4331
|
if (!system) {
|
|
3121
4332
|
throw new WorldOrbitError('Atlas section "defaults" requires a preceding system declaration', line, tokens[0].column);
|
|
@@ -3145,19 +4356,28 @@
|
|
|
3145
4356
|
if (!system) {
|
|
3146
4357
|
throw new WorldOrbitError('Atlas section "viewpoint" requires a preceding system declaration', line, tokens[0].column);
|
|
3147
4358
|
}
|
|
3148
|
-
return startViewpointSection(tokens, line, system, viewpointIds);
|
|
4359
|
+
return startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaVersion, diagnostics);
|
|
3149
4360
|
case "annotation":
|
|
3150
4361
|
if (!system) {
|
|
3151
4362
|
throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
|
|
3152
4363
|
}
|
|
3153
4364
|
return startAnnotationSection(tokens, line, system, annotationIds);
|
|
4365
|
+
case "group":
|
|
4366
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "group", { line, column: tokens[0].column });
|
|
4367
|
+
return startGroupSection(tokens, line, groups, groupIds);
|
|
4368
|
+
case "relation":
|
|
4369
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "relation", { line, column: tokens[0].column });
|
|
4370
|
+
return startRelationSection(tokens, line, relations, relationIds);
|
|
4371
|
+
case "event":
|
|
4372
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "event", { line, column: tokens[0].column });
|
|
4373
|
+
return startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourceSchemaVersion, diagnostics);
|
|
3154
4374
|
case "object":
|
|
3155
|
-
return startObjectSection(tokens, line, objectNodes);
|
|
4375
|
+
return startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes);
|
|
3156
4376
|
default:
|
|
3157
4377
|
throw new WorldOrbitError(`Unknown atlas section "${tokens[0]?.value ?? ""}"`, line, tokens[0]?.column ?? 1);
|
|
3158
4378
|
}
|
|
3159
4379
|
}
|
|
3160
|
-
function startSystemSection(tokens, line) {
|
|
4380
|
+
function startSystemSection(tokens, line, sourceSchemaVersion, diagnostics) {
|
|
3161
4381
|
if (tokens.length !== 2) {
|
|
3162
4382
|
throw new WorldOrbitError("Invalid atlas system declaration", line, tokens[0]?.column ?? 1);
|
|
3163
4383
|
}
|
|
@@ -3165,6 +4385,9 @@
|
|
|
3165
4385
|
type: "system",
|
|
3166
4386
|
id: tokens[1].value,
|
|
3167
4387
|
title: null,
|
|
4388
|
+
description: null,
|
|
4389
|
+
epoch: null,
|
|
4390
|
+
referencePlane: null,
|
|
3168
4391
|
defaults: {
|
|
3169
4392
|
view: "topdown",
|
|
3170
4393
|
scale: null,
|
|
@@ -3179,10 +4402,12 @@
|
|
|
3179
4402
|
return {
|
|
3180
4403
|
kind: "system",
|
|
3181
4404
|
system,
|
|
4405
|
+
sourceSchemaVersion,
|
|
4406
|
+
diagnostics,
|
|
3182
4407
|
seenFields: /* @__PURE__ */ new Set()
|
|
3183
4408
|
};
|
|
3184
4409
|
}
|
|
3185
|
-
function startViewpointSection(tokens, line, system, viewpointIds) {
|
|
4410
|
+
function startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaVersion, diagnostics) {
|
|
3186
4411
|
if (tokens.length !== 2) {
|
|
3187
4412
|
throw new WorldOrbitError("Invalid viewpoint declaration", line, tokens[0]?.column ?? 1);
|
|
3188
4413
|
}
|
|
@@ -3199,6 +4424,7 @@
|
|
|
3199
4424
|
summary: "",
|
|
3200
4425
|
focusObjectId: null,
|
|
3201
4426
|
selectedObjectId: null,
|
|
4427
|
+
events: [],
|
|
3202
4428
|
projection: system.defaults.view,
|
|
3203
4429
|
preset: system.defaults.preset,
|
|
3204
4430
|
zoom: null,
|
|
@@ -3211,6 +4437,8 @@
|
|
|
3211
4437
|
return {
|
|
3212
4438
|
kind: "viewpoint",
|
|
3213
4439
|
viewpoint,
|
|
4440
|
+
sourceSchemaVersion,
|
|
4441
|
+
diagnostics,
|
|
3214
4442
|
seenFields: /* @__PURE__ */ new Set(),
|
|
3215
4443
|
inFilter: false,
|
|
3216
4444
|
filterIndent: null,
|
|
@@ -3244,7 +4472,107 @@
|
|
|
3244
4472
|
seenFields: /* @__PURE__ */ new Set()
|
|
3245
4473
|
};
|
|
3246
4474
|
}
|
|
3247
|
-
function
|
|
4475
|
+
function startGroupSection(tokens, line, groups, groupIds) {
|
|
4476
|
+
if (tokens.length !== 2) {
|
|
4477
|
+
throw new WorldOrbitError("Invalid group declaration", line, tokens[0]?.column ?? 1);
|
|
4478
|
+
}
|
|
4479
|
+
const id = normalizeIdentifier2(tokens[1].value);
|
|
4480
|
+
if (!id) {
|
|
4481
|
+
throw new WorldOrbitError("Group id must not be empty", line, tokens[1].column);
|
|
4482
|
+
}
|
|
4483
|
+
if (groupIds.has(id)) {
|
|
4484
|
+
throw new WorldOrbitError(`Duplicate group id "${id}"`, line, tokens[1].column);
|
|
4485
|
+
}
|
|
4486
|
+
const group = {
|
|
4487
|
+
id,
|
|
4488
|
+
label: humanizeIdentifier3(id),
|
|
4489
|
+
summary: "",
|
|
4490
|
+
color: null,
|
|
4491
|
+
tags: [],
|
|
4492
|
+
hidden: false
|
|
4493
|
+
};
|
|
4494
|
+
groups.push(group);
|
|
4495
|
+
groupIds.add(id);
|
|
4496
|
+
return {
|
|
4497
|
+
kind: "group",
|
|
4498
|
+
group,
|
|
4499
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
4500
|
+
};
|
|
4501
|
+
}
|
|
4502
|
+
function startRelationSection(tokens, line, relations, relationIds) {
|
|
4503
|
+
if (tokens.length !== 2) {
|
|
4504
|
+
throw new WorldOrbitError("Invalid relation declaration", line, tokens[0]?.column ?? 1);
|
|
4505
|
+
}
|
|
4506
|
+
const id = normalizeIdentifier2(tokens[1].value);
|
|
4507
|
+
if (!id) {
|
|
4508
|
+
throw new WorldOrbitError("Relation id must not be empty", line, tokens[1].column);
|
|
4509
|
+
}
|
|
4510
|
+
if (relationIds.has(id)) {
|
|
4511
|
+
throw new WorldOrbitError(`Duplicate relation id "${id}"`, line, tokens[1].column);
|
|
4512
|
+
}
|
|
4513
|
+
const relation = {
|
|
4514
|
+
id,
|
|
4515
|
+
from: "",
|
|
4516
|
+
to: "",
|
|
4517
|
+
kind: "",
|
|
4518
|
+
label: null,
|
|
4519
|
+
summary: null,
|
|
4520
|
+
tags: [],
|
|
4521
|
+
color: null,
|
|
4522
|
+
hidden: false
|
|
4523
|
+
};
|
|
4524
|
+
relations.push(relation);
|
|
4525
|
+
relationIds.add(id);
|
|
4526
|
+
return {
|
|
4527
|
+
kind: "relation",
|
|
4528
|
+
relation,
|
|
4529
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
4530
|
+
};
|
|
4531
|
+
}
|
|
4532
|
+
function startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourceSchemaVersion, diagnostics) {
|
|
4533
|
+
if (tokens.length !== 2) {
|
|
4534
|
+
throw new WorldOrbitError("Invalid event declaration", line, tokens[0]?.column ?? 1);
|
|
4535
|
+
}
|
|
4536
|
+
const id = normalizeIdentifier2(tokens[1].value);
|
|
4537
|
+
if (!id) {
|
|
4538
|
+
throw new WorldOrbitError("Event id must not be empty", line, tokens[1].column);
|
|
4539
|
+
}
|
|
4540
|
+
if (eventIds.has(id)) {
|
|
4541
|
+
throw new WorldOrbitError(`Duplicate event id "${id}"`, line, tokens[1].column);
|
|
4542
|
+
}
|
|
4543
|
+
const event = {
|
|
4544
|
+
id,
|
|
4545
|
+
kind: "",
|
|
4546
|
+
label: humanizeIdentifier3(id),
|
|
4547
|
+
summary: null,
|
|
4548
|
+
targetObjectId: null,
|
|
4549
|
+
participantObjectIds: [],
|
|
4550
|
+
timing: null,
|
|
4551
|
+
visibility: null,
|
|
4552
|
+
tags: [],
|
|
4553
|
+
color: null,
|
|
4554
|
+
hidden: false,
|
|
4555
|
+
positions: []
|
|
4556
|
+
};
|
|
4557
|
+
const rawPoses = [];
|
|
4558
|
+
events.push(event);
|
|
4559
|
+
eventPoseNodes.set(id, rawPoses);
|
|
4560
|
+
eventIds.add(id);
|
|
4561
|
+
return {
|
|
4562
|
+
kind: "event",
|
|
4563
|
+
event,
|
|
4564
|
+
sourceSchemaVersion,
|
|
4565
|
+
diagnostics,
|
|
4566
|
+
seenFields: /* @__PURE__ */ new Set(),
|
|
4567
|
+
rawPoses,
|
|
4568
|
+
inPositions: false,
|
|
4569
|
+
positionsIndent: null,
|
|
4570
|
+
activePose: null,
|
|
4571
|
+
poseIndent: null,
|
|
4572
|
+
activePoseSeenFields: /* @__PURE__ */ new Set()
|
|
4573
|
+
};
|
|
4574
|
+
}
|
|
4575
|
+
function startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes) {
|
|
3248
4576
|
if (tokens.length < 3) {
|
|
3249
4577
|
throw new WorldOrbitError("Invalid atlas object declaration", line, tokens[0]?.column ?? 1);
|
|
3250
4578
|
}
|
|
@@ -3255,12 +4583,11 @@
|
|
|
3255
4583
|
throw new WorldOrbitError(`Unknown object type "${objectTypeToken.value}"`, line, objectTypeToken.column);
|
|
3256
4584
|
}
|
|
3257
4585
|
const objectNode = {
|
|
3258
|
-
type: "object",
|
|
3259
4586
|
objectType,
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
blockFields: [],
|
|
4587
|
+
id: idToken.value,
|
|
4588
|
+
fields: parseInlineObjectFields(tokens.slice(3), line, objectType, sourceSchemaVersion, diagnostics),
|
|
3263
4589
|
infoEntries: [],
|
|
4590
|
+
typedBlockEntries: {},
|
|
3264
4591
|
location: {
|
|
3265
4592
|
line,
|
|
3266
4593
|
column: objectTypeToken.column
|
|
@@ -3270,8 +4597,12 @@
|
|
|
3270
4597
|
return {
|
|
3271
4598
|
kind: "object",
|
|
3272
4599
|
objectNode,
|
|
3273
|
-
|
|
3274
|
-
|
|
4600
|
+
sourceSchemaVersion,
|
|
4601
|
+
diagnostics,
|
|
4602
|
+
activeBlock: null,
|
|
4603
|
+
blockIndent: null,
|
|
4604
|
+
seenInfoKeys: /* @__PURE__ */ new Set(),
|
|
4605
|
+
seenTypedBlockKeys: {}
|
|
3275
4606
|
};
|
|
3276
4607
|
}
|
|
3277
4608
|
function handleSectionLine(section, indent, tokens, line) {
|
|
@@ -3291,6 +4622,15 @@
|
|
|
3291
4622
|
case "annotation":
|
|
3292
4623
|
applyAnnotationField(section, tokens, line);
|
|
3293
4624
|
return;
|
|
4625
|
+
case "group":
|
|
4626
|
+
applyGroupField(section, tokens, line);
|
|
4627
|
+
return;
|
|
4628
|
+
case "relation":
|
|
4629
|
+
applyRelationField(section, tokens, line);
|
|
4630
|
+
return;
|
|
4631
|
+
case "event":
|
|
4632
|
+
applyEventField(section, indent, tokens, line);
|
|
4633
|
+
return;
|
|
3294
4634
|
case "object":
|
|
3295
4635
|
applyObjectField(section, indent, tokens, line);
|
|
3296
4636
|
return;
|
|
@@ -3298,10 +4638,35 @@
|
|
|
3298
4638
|
}
|
|
3299
4639
|
function applySystemField(section, tokens, line) {
|
|
3300
4640
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
3301
|
-
|
|
3302
|
-
|
|
4641
|
+
const value = joinFieldValue(tokens, line);
|
|
4642
|
+
switch (key) {
|
|
4643
|
+
case "title":
|
|
4644
|
+
section.system.title = value;
|
|
4645
|
+
return;
|
|
4646
|
+
case "description":
|
|
4647
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
4648
|
+
line,
|
|
4649
|
+
column: tokens[0].column
|
|
4650
|
+
});
|
|
4651
|
+
section.system.description = value;
|
|
4652
|
+
return;
|
|
4653
|
+
case "epoch":
|
|
4654
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
4655
|
+
line,
|
|
4656
|
+
column: tokens[0].column
|
|
4657
|
+
});
|
|
4658
|
+
section.system.epoch = value;
|
|
4659
|
+
return;
|
|
4660
|
+
case "referenceplane":
|
|
4661
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, "referencePlane", {
|
|
4662
|
+
line,
|
|
4663
|
+
column: tokens[0].column
|
|
4664
|
+
});
|
|
4665
|
+
section.system.referencePlane = value;
|
|
4666
|
+
return;
|
|
4667
|
+
default:
|
|
4668
|
+
throw new WorldOrbitError(`Unknown system atlas field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3303
4669
|
}
|
|
3304
|
-
section.system.title = joinFieldValue(tokens, line);
|
|
3305
4670
|
}
|
|
3306
4671
|
function applyDefaultsField(section, tokens, line) {
|
|
3307
4672
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
@@ -3332,14 +4697,11 @@
|
|
|
3332
4697
|
section.metadataIndent = null;
|
|
3333
4698
|
}
|
|
3334
4699
|
if (section.inMetadata) {
|
|
3335
|
-
|
|
3336
|
-
|
|
4700
|
+
const entry = parseInfoLikeEntry(tokens, line, "Invalid atlas metadata entry");
|
|
4701
|
+
if (entry.key in section.system.atlasMetadata) {
|
|
4702
|
+
throw new WorldOrbitError(`Duplicate atlas metadata key "${entry.key}"`, line, tokens[0].column);
|
|
3337
4703
|
}
|
|
3338
|
-
|
|
3339
|
-
if (key in section.system.atlasMetadata) {
|
|
3340
|
-
throw new WorldOrbitError(`Duplicate atlas metadata key "${key}"`, line, tokens[0].column);
|
|
3341
|
-
}
|
|
3342
|
-
section.system.atlasMetadata[key] = joinFieldValue(tokens, line);
|
|
4704
|
+
section.system.atlasMetadata[entry.key] = entry.value;
|
|
3343
4705
|
return;
|
|
3344
4706
|
}
|
|
3345
4707
|
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "metadata") {
|
|
@@ -3395,7 +4757,14 @@
|
|
|
3395
4757
|
section.viewpoint.rotationDeg = parseFiniteNumber2(value, line, tokens[0].column, "rotation");
|
|
3396
4758
|
return;
|
|
3397
4759
|
case "layers":
|
|
3398
|
-
section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line);
|
|
4760
|
+
section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line, section.sourceSchemaVersion, section.diagnostics);
|
|
4761
|
+
return;
|
|
4762
|
+
case "events":
|
|
4763
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.events", {
|
|
4764
|
+
line,
|
|
4765
|
+
column: tokens[0].column
|
|
4766
|
+
});
|
|
4767
|
+
section.viewpoint.events = parseTokenList(tokens.slice(1), line, "events");
|
|
3399
4768
|
return;
|
|
3400
4769
|
default:
|
|
3401
4770
|
throw new WorldOrbitError(`Unknown viewpoint field "${tokens[0].value}"`, line, tokens[0].column);
|
|
@@ -3441,21 +4810,202 @@
|
|
|
3441
4810
|
throw new WorldOrbitError(`Unknown annotation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3442
4811
|
}
|
|
3443
4812
|
}
|
|
3444
|
-
function
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
4813
|
+
function applyGroupField(section, tokens, line) {
|
|
4814
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
4815
|
+
switch (key) {
|
|
4816
|
+
case "label":
|
|
4817
|
+
section.group.label = joinFieldValue(tokens, line);
|
|
4818
|
+
return;
|
|
4819
|
+
case "summary":
|
|
4820
|
+
section.group.summary = joinFieldValue(tokens, line);
|
|
4821
|
+
return;
|
|
4822
|
+
case "color":
|
|
4823
|
+
section.group.color = joinFieldValue(tokens, line);
|
|
4824
|
+
return;
|
|
4825
|
+
case "tags":
|
|
4826
|
+
section.group.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4827
|
+
return;
|
|
4828
|
+
case "hidden":
|
|
4829
|
+
section.group.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4830
|
+
line,
|
|
4831
|
+
column: tokens[0].column
|
|
4832
|
+
});
|
|
4833
|
+
return;
|
|
4834
|
+
default:
|
|
4835
|
+
throw new WorldOrbitError(`Unknown group field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
function applyRelationField(section, tokens, line) {
|
|
4839
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
4840
|
+
switch (key) {
|
|
4841
|
+
case "from":
|
|
4842
|
+
section.relation.from = joinFieldValue(tokens, line);
|
|
4843
|
+
return;
|
|
4844
|
+
case "to":
|
|
4845
|
+
section.relation.to = joinFieldValue(tokens, line);
|
|
4846
|
+
return;
|
|
4847
|
+
case "kind":
|
|
4848
|
+
section.relation.kind = joinFieldValue(tokens, line);
|
|
4849
|
+
return;
|
|
4850
|
+
case "label":
|
|
4851
|
+
section.relation.label = joinFieldValue(tokens, line);
|
|
4852
|
+
return;
|
|
4853
|
+
case "summary":
|
|
4854
|
+
section.relation.summary = joinFieldValue(tokens, line);
|
|
4855
|
+
return;
|
|
4856
|
+
case "tags":
|
|
4857
|
+
section.relation.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4858
|
+
return;
|
|
4859
|
+
case "color":
|
|
4860
|
+
section.relation.color = joinFieldValue(tokens, line);
|
|
4861
|
+
return;
|
|
4862
|
+
case "hidden":
|
|
4863
|
+
section.relation.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4864
|
+
line,
|
|
4865
|
+
column: tokens[0].column
|
|
4866
|
+
});
|
|
4867
|
+
return;
|
|
4868
|
+
default:
|
|
4869
|
+
throw new WorldOrbitError(`Unknown relation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4870
|
+
}
|
|
4871
|
+
}
|
|
4872
|
+
function applyEventField(section, indent, tokens, line) {
|
|
4873
|
+
if (section.activePose && indent <= (section.poseIndent ?? 0)) {
|
|
4874
|
+
section.activePose = null;
|
|
4875
|
+
section.poseIndent = null;
|
|
4876
|
+
section.activePoseSeenFields.clear();
|
|
4877
|
+
}
|
|
4878
|
+
if (!section.activePose && section.inPositions && indent <= (section.positionsIndent ?? 0)) {
|
|
4879
|
+
section.inPositions = false;
|
|
4880
|
+
section.positionsIndent = null;
|
|
4881
|
+
}
|
|
4882
|
+
if (section.activePose) {
|
|
4883
|
+
section.activePose.fields.push(parseEventPoseField(tokens, line, section.activePoseSeenFields));
|
|
4884
|
+
return;
|
|
4885
|
+
}
|
|
4886
|
+
if (section.inPositions) {
|
|
4887
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "pose") {
|
|
4888
|
+
throw new WorldOrbitError(`Unknown event positions field "${tokens[0].value}"`, line, tokens[0]?.column ?? 1);
|
|
4889
|
+
}
|
|
4890
|
+
const objectId = tokens[1].value;
|
|
4891
|
+
if (!objectId.trim()) {
|
|
4892
|
+
throw new WorldOrbitError("Event pose object id must not be empty", line, tokens[1].column);
|
|
4893
|
+
}
|
|
4894
|
+
const rawPose = {
|
|
4895
|
+
objectId,
|
|
4896
|
+
fields: [],
|
|
4897
|
+
location: { line, column: tokens[0].column }
|
|
4898
|
+
};
|
|
4899
|
+
section.rawPoses.push(rawPose);
|
|
4900
|
+
section.activePose = rawPose;
|
|
4901
|
+
section.poseIndent = indent;
|
|
4902
|
+
section.activePoseSeenFields = /* @__PURE__ */ new Set();
|
|
3448
4903
|
return;
|
|
3449
4904
|
}
|
|
3450
|
-
if (
|
|
3451
|
-
section.
|
|
3452
|
-
|
|
4905
|
+
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "positions") {
|
|
4906
|
+
if (section.seenFields.has("positions")) {
|
|
4907
|
+
throw new WorldOrbitError('Duplicate event field "positions"', line, tokens[0].column);
|
|
4908
|
+
}
|
|
4909
|
+
section.seenFields.add("positions");
|
|
4910
|
+
section.inPositions = true;
|
|
4911
|
+
section.positionsIndent = indent;
|
|
4912
|
+
return;
|
|
4913
|
+
}
|
|
4914
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
4915
|
+
switch (key) {
|
|
4916
|
+
case "kind":
|
|
4917
|
+
section.event.kind = joinFieldValue(tokens, line);
|
|
4918
|
+
return;
|
|
4919
|
+
case "label":
|
|
4920
|
+
section.event.label = joinFieldValue(tokens, line);
|
|
4921
|
+
return;
|
|
4922
|
+
case "summary":
|
|
4923
|
+
section.event.summary = joinFieldValue(tokens, line);
|
|
4924
|
+
return;
|
|
4925
|
+
case "target":
|
|
4926
|
+
section.event.targetObjectId = joinFieldValue(tokens, line);
|
|
4927
|
+
return;
|
|
4928
|
+
case "participants":
|
|
4929
|
+
section.event.participantObjectIds = parseTokenList(tokens.slice(1), line, "participants");
|
|
4930
|
+
return;
|
|
4931
|
+
case "timing":
|
|
4932
|
+
section.event.timing = joinFieldValue(tokens, line);
|
|
4933
|
+
return;
|
|
4934
|
+
case "visibility":
|
|
4935
|
+
section.event.visibility = joinFieldValue(tokens, line);
|
|
4936
|
+
return;
|
|
4937
|
+
case "tags":
|
|
4938
|
+
section.event.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4939
|
+
return;
|
|
4940
|
+
case "color":
|
|
4941
|
+
section.event.color = joinFieldValue(tokens, line);
|
|
4942
|
+
return;
|
|
4943
|
+
case "hidden":
|
|
4944
|
+
section.event.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4945
|
+
line,
|
|
4946
|
+
column: tokens[0].column
|
|
4947
|
+
});
|
|
4948
|
+
return;
|
|
4949
|
+
default:
|
|
4950
|
+
throw new WorldOrbitError(`Unknown event field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
function parseEventPoseField(tokens, line, seenFields) {
|
|
4954
|
+
if (tokens.length < 2) {
|
|
4955
|
+
throw new WorldOrbitError("Invalid event pose field line", line, tokens[0]?.column ?? 1);
|
|
4956
|
+
}
|
|
4957
|
+
const key = tokens[0].value;
|
|
4958
|
+
if (!EVENT_POSE_FIELD_KEYS.has(key)) {
|
|
4959
|
+
throw new WorldOrbitError(`Unknown event pose field "${key}"`, line, tokens[0].column);
|
|
4960
|
+
}
|
|
4961
|
+
if (seenFields.has(key)) {
|
|
4962
|
+
throw new WorldOrbitError(`Duplicate event pose field "${key}"`, line, tokens[0].column);
|
|
4963
|
+
}
|
|
4964
|
+
seenFields.add(key);
|
|
4965
|
+
return {
|
|
4966
|
+
type: "field",
|
|
4967
|
+
key,
|
|
4968
|
+
values: tokens.slice(1).map((token) => token.value),
|
|
4969
|
+
location: { line, column: tokens[0].column }
|
|
4970
|
+
};
|
|
4971
|
+
}
|
|
4972
|
+
function applyObjectField(section, indent, tokens, line) {
|
|
4973
|
+
if (section.activeBlock && indent <= (section.blockIndent ?? 0)) {
|
|
4974
|
+
section.activeBlock = null;
|
|
4975
|
+
section.blockIndent = null;
|
|
4976
|
+
}
|
|
4977
|
+
if (tokens.length === 1) {
|
|
4978
|
+
const blockName = tokens[0].value.toLowerCase();
|
|
4979
|
+
if (blockName === "info" || STRUCTURED_TYPED_BLOCKS.has(blockName)) {
|
|
4980
|
+
if (blockName !== "info") {
|
|
4981
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, blockName, { line, column: tokens[0].column });
|
|
4982
|
+
}
|
|
4983
|
+
section.activeBlock = blockName;
|
|
4984
|
+
section.blockIndent = indent;
|
|
4985
|
+
return;
|
|
4986
|
+
}
|
|
3453
4987
|
}
|
|
3454
|
-
if (section.
|
|
3455
|
-
|
|
4988
|
+
if (section.activeBlock) {
|
|
4989
|
+
const entry = parseInfoLikeEntry(tokens, line, `Invalid ${section.activeBlock} entry`);
|
|
4990
|
+
if (section.activeBlock === "info") {
|
|
4991
|
+
if (section.seenInfoKeys.has(entry.key)) {
|
|
4992
|
+
throw new WorldOrbitError(`Duplicate info key "${entry.key}"`, line, tokens[0].column);
|
|
4993
|
+
}
|
|
4994
|
+
section.seenInfoKeys.add(entry.key);
|
|
4995
|
+
section.objectNode.infoEntries.push(entry);
|
|
4996
|
+
return;
|
|
4997
|
+
}
|
|
4998
|
+
const typedBlock = section.activeBlock;
|
|
4999
|
+
const seenKeys = section.seenTypedBlockKeys[typedBlock] ?? (section.seenTypedBlockKeys[typedBlock] = /* @__PURE__ */ new Set());
|
|
5000
|
+
if (seenKeys.has(entry.key)) {
|
|
5001
|
+
throw new WorldOrbitError(`Duplicate ${typedBlock} key "${entry.key}"`, line, tokens[0].column);
|
|
5002
|
+
}
|
|
5003
|
+
seenKeys.add(entry.key);
|
|
5004
|
+
const entries = section.objectNode.typedBlockEntries[typedBlock] ?? (section.objectNode.typedBlockEntries[typedBlock] = []);
|
|
5005
|
+
entries.push(entry);
|
|
3456
5006
|
return;
|
|
3457
5007
|
}
|
|
3458
|
-
section.objectNode.
|
|
5008
|
+
section.objectNode.fields.push(parseObjectField(tokens, line, section.objectNode.objectType, section.sourceSchemaVersion, section.diagnostics));
|
|
3459
5009
|
}
|
|
3460
5010
|
function requireUniqueField(tokens, seenFields, line) {
|
|
3461
5011
|
if (tokens.length < 2) {
|
|
@@ -3475,50 +5025,46 @@
|
|
|
3475
5025
|
return tokens.slice(1).map((token) => token.value).join(" ").trim();
|
|
3476
5026
|
}
|
|
3477
5027
|
function parseObjectTypeTokens(tokens, line) {
|
|
3478
|
-
|
|
3479
|
-
throw new WorldOrbitError("Missing value for atlas field", line);
|
|
3480
|
-
}
|
|
3481
|
-
return tokens.map((token) => {
|
|
3482
|
-
const value = token.value;
|
|
3483
|
-
if (value !== "star" && value !== "planet" && value !== "moon" && value !== "belt" && value !== "asteroid" && value !== "comet" && value !== "ring" && value !== "structure" && value !== "phenomenon") {
|
|
3484
|
-
throw new WorldOrbitError(`Unknown viewpoint object type "${token.value}"`, line, token.column);
|
|
3485
|
-
}
|
|
3486
|
-
return value;
|
|
3487
|
-
});
|
|
5028
|
+
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");
|
|
3488
5029
|
}
|
|
3489
|
-
function
|
|
3490
|
-
|
|
3491
|
-
|
|
5030
|
+
function parseLayerTokens(tokens, line, sourceSchemaVersion, diagnostics) {
|
|
5031
|
+
const layers = {};
|
|
5032
|
+
for (const token of parseTokenList(tokens, line, "layers")) {
|
|
5033
|
+
const enabled = !token.startsWith("-") && !token.startsWith("!");
|
|
5034
|
+
const raw = token.replace(/^[-!]+/, "").toLowerCase();
|
|
5035
|
+
if (raw === "orbits") {
|
|
5036
|
+
layers["orbits-back"] = enabled;
|
|
5037
|
+
layers["orbits-front"] = enabled;
|
|
5038
|
+
continue;
|
|
5039
|
+
}
|
|
5040
|
+
if (raw === "background" || raw === "guides" || raw === "orbits-back" || raw === "orbits-front" || raw === "relations" || raw === "events" || raw === "objects" || raw === "labels" || raw === "metadata") {
|
|
5041
|
+
if (raw === "events" && sourceSchemaVersion && diagnostics) {
|
|
5042
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "layers.events", {
|
|
5043
|
+
line,
|
|
5044
|
+
column: tokens[0]?.column ?? 1
|
|
5045
|
+
});
|
|
5046
|
+
}
|
|
5047
|
+
layers[raw] = enabled;
|
|
5048
|
+
}
|
|
3492
5049
|
}
|
|
3493
|
-
return
|
|
5050
|
+
return layers;
|
|
3494
5051
|
}
|
|
3495
|
-
function
|
|
5052
|
+
function parseTokenList(tokens, line, fieldName) {
|
|
3496
5053
|
if (tokens.length === 0) {
|
|
3497
|
-
throw new WorldOrbitError(
|
|
5054
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, 1);
|
|
3498
5055
|
}
|
|
3499
|
-
const
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
const rawLayer = token.value.replace(/^[-!]+/, "").toLowerCase();
|
|
3503
|
-
if (rawLayer === "orbits") {
|
|
3504
|
-
next["orbits-back"] = enabled;
|
|
3505
|
-
next["orbits-front"] = enabled;
|
|
3506
|
-
continue;
|
|
3507
|
-
}
|
|
3508
|
-
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
3509
|
-
next[rawLayer] = enabled;
|
|
3510
|
-
continue;
|
|
3511
|
-
}
|
|
3512
|
-
throw new WorldOrbitError(`Unknown layer token "${token.value}"`, line, token.column);
|
|
5056
|
+
const values = tokens.map((token) => token.value).filter(Boolean);
|
|
5057
|
+
if (values.length === 0) {
|
|
5058
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, tokens[0]?.column ?? 1);
|
|
3513
5059
|
}
|
|
3514
|
-
return
|
|
5060
|
+
return values;
|
|
3515
5061
|
}
|
|
3516
5062
|
function parseProjectionValue(value, line, column) {
|
|
3517
5063
|
const normalized = value.toLowerCase();
|
|
3518
|
-
if (normalized
|
|
3519
|
-
|
|
5064
|
+
if (normalized !== "topdown" && normalized !== "isometric") {
|
|
5065
|
+
throw new WorldOrbitError(`Unknown projection "${value}"`, line, column);
|
|
3520
5066
|
}
|
|
3521
|
-
|
|
5067
|
+
return normalized;
|
|
3522
5068
|
}
|
|
3523
5069
|
function parsePresetValue(value, line, column) {
|
|
3524
5070
|
const normalized = value.toLowerCase();
|
|
@@ -3528,16 +5074,16 @@
|
|
|
3528
5074
|
throw new WorldOrbitError(`Unknown render preset "${value}"`, line, column);
|
|
3529
5075
|
}
|
|
3530
5076
|
function parsePositiveNumber2(value, line, column, field) {
|
|
3531
|
-
const parsed =
|
|
3532
|
-
if (
|
|
3533
|
-
throw new WorldOrbitError(`Field "${field}"
|
|
5077
|
+
const parsed = parseFiniteNumber2(value, line, column, field);
|
|
5078
|
+
if (parsed <= 0) {
|
|
5079
|
+
throw new WorldOrbitError(`Field "${field}" must be greater than zero`, line, column);
|
|
3534
5080
|
}
|
|
3535
5081
|
return parsed;
|
|
3536
5082
|
}
|
|
3537
5083
|
function parseFiniteNumber2(value, line, column, field) {
|
|
3538
5084
|
const parsed = Number(value);
|
|
3539
5085
|
if (!Number.isFinite(parsed)) {
|
|
3540
|
-
throw new WorldOrbitError(`
|
|
5086
|
+
throw new WorldOrbitError(`Invalid numeric value "${value}" for "${field}"`, line, column);
|
|
3541
5087
|
}
|
|
3542
5088
|
return parsed;
|
|
3543
5089
|
}
|
|
@@ -3549,28 +5095,43 @@
|
|
|
3549
5095
|
groupIds: []
|
|
3550
5096
|
};
|
|
3551
5097
|
}
|
|
3552
|
-
function
|
|
5098
|
+
function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
3553
5099
|
const fields = [];
|
|
3554
5100
|
let index = 0;
|
|
3555
5101
|
while (index < tokens.length) {
|
|
3556
5102
|
const keyToken = tokens[index];
|
|
3557
|
-
const
|
|
3558
|
-
if (!
|
|
5103
|
+
const spec = getDraftObjectFieldSpec(keyToken.value);
|
|
5104
|
+
if (!spec) {
|
|
3559
5105
|
throw new WorldOrbitError(`Unknown field "${keyToken.value}"`, line, keyToken.column);
|
|
3560
5106
|
}
|
|
5107
|
+
if (spec.version === "2.1") {
|
|
5108
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, keyToken.value, {
|
|
5109
|
+
line,
|
|
5110
|
+
column: keyToken.column
|
|
5111
|
+
});
|
|
5112
|
+
}
|
|
3561
5113
|
index++;
|
|
3562
5114
|
const valueTokens = [];
|
|
3563
|
-
if (
|
|
3564
|
-
while (index < tokens.length && !isKnownFieldKey(tokens[index].value)) {
|
|
3565
|
-
valueTokens.push(tokens[index]);
|
|
3566
|
-
index++;
|
|
3567
|
-
}
|
|
3568
|
-
} else {
|
|
5115
|
+
if (spec.inlineMode === "single") {
|
|
3569
5116
|
const nextToken = tokens[index];
|
|
3570
5117
|
if (nextToken) {
|
|
3571
5118
|
valueTokens.push(nextToken);
|
|
3572
5119
|
index++;
|
|
3573
5120
|
}
|
|
5121
|
+
} else if (spec.inlineMode === "pair") {
|
|
5122
|
+
for (let count = 0; count < 2; count++) {
|
|
5123
|
+
const nextToken = tokens[index];
|
|
5124
|
+
if (!nextToken) {
|
|
5125
|
+
break;
|
|
5126
|
+
}
|
|
5127
|
+
valueTokens.push(nextToken);
|
|
5128
|
+
index++;
|
|
5129
|
+
}
|
|
5130
|
+
} else {
|
|
5131
|
+
while (index < tokens.length && !DRAFT_OBJECT_FIELD_KEYS.has(tokens[index].value)) {
|
|
5132
|
+
valueTokens.push(tokens[index]);
|
|
5133
|
+
index++;
|
|
5134
|
+
}
|
|
3574
5135
|
}
|
|
3575
5136
|
if (valueTokens.length === 0) {
|
|
3576
5137
|
throw new WorldOrbitError(`Missing value for field "${keyToken.value}"`, line, keyToken.column);
|
|
@@ -3582,25 +5143,35 @@
|
|
|
3582
5143
|
location: { line, column: keyToken.column }
|
|
3583
5144
|
});
|
|
3584
5145
|
}
|
|
5146
|
+
validateDraftObjectFieldCompatibility(fields, objectType);
|
|
3585
5147
|
return fields;
|
|
3586
5148
|
}
|
|
3587
|
-
function
|
|
5149
|
+
function parseObjectField(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
3588
5150
|
if (tokens.length < 2) {
|
|
3589
5151
|
throw new WorldOrbitError("Invalid field line", line, tokens[0]?.column ?? 1);
|
|
3590
5152
|
}
|
|
3591
|
-
|
|
5153
|
+
const spec = getDraftObjectFieldSpec(tokens[0].value);
|
|
5154
|
+
if (!spec) {
|
|
3592
5155
|
throw new WorldOrbitError(`Unknown field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3593
5156
|
}
|
|
3594
|
-
|
|
5157
|
+
if (spec.version === "2.1") {
|
|
5158
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, tokens[0].value, {
|
|
5159
|
+
line,
|
|
5160
|
+
column: tokens[0].column
|
|
5161
|
+
});
|
|
5162
|
+
}
|
|
5163
|
+
const field = {
|
|
3595
5164
|
type: "field",
|
|
3596
5165
|
key: tokens[0].value,
|
|
3597
5166
|
values: tokens.slice(1).map((token) => token.value),
|
|
3598
5167
|
location: { line, column: tokens[0].column }
|
|
3599
5168
|
};
|
|
5169
|
+
validateDraftObjectFieldCompatibility([field], objectType);
|
|
5170
|
+
return field;
|
|
3600
5171
|
}
|
|
3601
|
-
function
|
|
5172
|
+
function parseInfoLikeEntry(tokens, line, errorMessage) {
|
|
3602
5173
|
if (tokens.length < 2) {
|
|
3603
|
-
throw new WorldOrbitError(
|
|
5174
|
+
throw new WorldOrbitError(errorMessage, line, tokens[0]?.column ?? 1);
|
|
3604
5175
|
}
|
|
3605
5176
|
return {
|
|
3606
5177
|
type: "info-entry",
|
|
@@ -3609,23 +5180,374 @@
|
|
|
3609
5180
|
location: { line, column: tokens[0].column }
|
|
3610
5181
|
};
|
|
3611
5182
|
}
|
|
3612
|
-
function
|
|
3613
|
-
|
|
5183
|
+
function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
|
|
5184
|
+
const fieldMap = collectDraftFields(node.fields);
|
|
5185
|
+
const placement = extractPlacementFromFieldMap(fieldMap);
|
|
5186
|
+
const properties = normalizeDraftProperties(node.objectType, fieldMap);
|
|
5187
|
+
const groups = parseOptionalTokenList(fieldMap.get("groups")?.[0]);
|
|
5188
|
+
const epoch = parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]);
|
|
5189
|
+
const referencePlane = parseOptionalJoinedValue(fieldMap.get("referencePlane")?.[0]);
|
|
5190
|
+
const tidalLock = fieldMap.has("tidalLock") ? parseAtlasBoolean(singleFieldValue2(fieldMap.get("tidalLock")[0]), "tidalLock", fieldMap.get("tidalLock")[0].location) : void 0;
|
|
5191
|
+
const resonance = fieldMap.has("resonance") ? parseResonanceField(fieldMap.get("resonance")[0]) : void 0;
|
|
5192
|
+
const renderHints = extractRenderHints(fieldMap);
|
|
5193
|
+
const deriveRules = fieldMap.get("derive")?.map((field) => parseDeriveField(field));
|
|
5194
|
+
const validationRules = fieldMap.get("validate")?.map((field) => ({
|
|
5195
|
+
rule: singleFieldValue2(field)
|
|
5196
|
+
}));
|
|
5197
|
+
const lockedFields = fieldMap.has("locked") ? [...new Set(fieldMap.get("locked").flatMap((field) => field.values))] : void 0;
|
|
5198
|
+
const tolerances = fieldMap.get("tolerance")?.map((field) => parseToleranceField(field));
|
|
5199
|
+
const typedBlocks = normalizeTypedBlocks(node.typedBlockEntries);
|
|
5200
|
+
const info2 = normalizeInfoEntries(node.infoEntries, "info");
|
|
5201
|
+
const object = {
|
|
5202
|
+
type: node.objectType,
|
|
5203
|
+
id: node.id,
|
|
5204
|
+
properties,
|
|
5205
|
+
placement,
|
|
5206
|
+
info: info2
|
|
5207
|
+
};
|
|
5208
|
+
if (groups.length > 0)
|
|
5209
|
+
object.groups = groups;
|
|
5210
|
+
if (epoch)
|
|
5211
|
+
object.epoch = epoch;
|
|
5212
|
+
if (referencePlane)
|
|
5213
|
+
object.referencePlane = referencePlane;
|
|
5214
|
+
if (tidalLock !== void 0)
|
|
5215
|
+
object.tidalLock = tidalLock;
|
|
5216
|
+
if (resonance)
|
|
5217
|
+
object.resonance = resonance;
|
|
5218
|
+
if (renderHints)
|
|
5219
|
+
object.renderHints = renderHints;
|
|
5220
|
+
if (deriveRules?.length)
|
|
5221
|
+
object.deriveRules = deriveRules;
|
|
5222
|
+
if (validationRules?.length)
|
|
5223
|
+
object.validationRules = validationRules;
|
|
5224
|
+
if (lockedFields?.length)
|
|
5225
|
+
object.lockedFields = lockedFields;
|
|
5226
|
+
if (tolerances?.length)
|
|
5227
|
+
object.tolerances = tolerances;
|
|
5228
|
+
if (typedBlocks && Object.keys(typedBlocks).length > 0)
|
|
5229
|
+
object.typedBlocks = typedBlocks;
|
|
5230
|
+
if (sourceSchemaVersion !== "2.1") {
|
|
5231
|
+
if (object.groups || object.epoch || object.referencePlane || object.tidalLock !== void 0 || object.resonance || object.renderHints || object.deriveRules?.length || object.validationRules?.length || object.lockedFields?.length || object.tolerances?.length || object.typedBlocks) {
|
|
5232
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, node.id, node.location);
|
|
5233
|
+
}
|
|
5234
|
+
}
|
|
5235
|
+
return object;
|
|
3614
5236
|
}
|
|
3615
|
-
function
|
|
3616
|
-
return
|
|
5237
|
+
function normalizeDraftEvent(event, rawPoses) {
|
|
5238
|
+
return {
|
|
5239
|
+
...event,
|
|
5240
|
+
participantObjectIds: [...new Set(event.participantObjectIds)],
|
|
5241
|
+
tags: [...new Set(event.tags)],
|
|
5242
|
+
positions: rawPoses.map((pose) => normalizeDraftEventPose(pose))
|
|
5243
|
+
};
|
|
5244
|
+
}
|
|
5245
|
+
function normalizeDraftEventPose(rawPose) {
|
|
5246
|
+
const fieldMap = collectDraftFields(rawPose.fields);
|
|
5247
|
+
const placement = extractPlacementFromFieldMap(fieldMap);
|
|
5248
|
+
return {
|
|
5249
|
+
objectId: rawPose.objectId,
|
|
5250
|
+
placement,
|
|
5251
|
+
inner: parseOptionalUnitField(fieldMap.get("inner")?.[0], "inner"),
|
|
5252
|
+
outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer")
|
|
5253
|
+
};
|
|
5254
|
+
}
|
|
5255
|
+
function collectDraftFields(fields) {
|
|
5256
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
5257
|
+
for (const field of fields) {
|
|
5258
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
5259
|
+
if (!spec) {
|
|
5260
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
5261
|
+
}
|
|
5262
|
+
if (!spec.allowRepeat && grouped.has(field.key)) {
|
|
5263
|
+
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
5264
|
+
}
|
|
5265
|
+
const existing = grouped.get(field.key) ?? [];
|
|
5266
|
+
existing.push(field);
|
|
5267
|
+
grouped.set(field.key, existing);
|
|
5268
|
+
}
|
|
5269
|
+
return grouped;
|
|
5270
|
+
}
|
|
5271
|
+
function extractPlacementFromFieldMap(fieldMap) {
|
|
5272
|
+
const orbitField = fieldMap.get("orbit")?.[0];
|
|
5273
|
+
const atField = fieldMap.get("at")?.[0];
|
|
5274
|
+
const surfaceField = fieldMap.get("surface")?.[0];
|
|
5275
|
+
const freeField = fieldMap.get("free")?.[0];
|
|
5276
|
+
const count = [orbitField, atField, surfaceField, freeField].filter(Boolean).length;
|
|
5277
|
+
if (count > 1) {
|
|
5278
|
+
const conflictingField = orbitField ?? atField ?? surfaceField ?? freeField;
|
|
5279
|
+
throw WorldOrbitError.fromLocation("Object has multiple placement modes", conflictingField?.location);
|
|
5280
|
+
}
|
|
5281
|
+
if (orbitField) {
|
|
5282
|
+
return {
|
|
5283
|
+
mode: "orbit",
|
|
5284
|
+
target: singleFieldValue2(orbitField),
|
|
5285
|
+
distance: parseOptionalUnitField(fieldMap.get("distance")?.[0], "distance"),
|
|
5286
|
+
semiMajor: parseOptionalUnitField(fieldMap.get("semiMajor")?.[0], "semiMajor"),
|
|
5287
|
+
eccentricity: parseOptionalNumberField(fieldMap.get("eccentricity")?.[0], "eccentricity"),
|
|
5288
|
+
period: parseOptionalUnitField(fieldMap.get("period")?.[0], "period"),
|
|
5289
|
+
angle: parseOptionalUnitField(fieldMap.get("angle")?.[0], "angle"),
|
|
5290
|
+
inclination: parseOptionalUnitField(fieldMap.get("inclination")?.[0], "inclination"),
|
|
5291
|
+
phase: parseOptionalUnitField(fieldMap.get("phase")?.[0], "phase")
|
|
5292
|
+
};
|
|
5293
|
+
}
|
|
5294
|
+
if (atField) {
|
|
5295
|
+
const target = singleFieldValue2(atField);
|
|
5296
|
+
return {
|
|
5297
|
+
mode: "at",
|
|
5298
|
+
target,
|
|
5299
|
+
reference: parseAtlasAtReference(target, atField.location)
|
|
5300
|
+
};
|
|
5301
|
+
}
|
|
5302
|
+
if (surfaceField) {
|
|
5303
|
+
return {
|
|
5304
|
+
mode: "surface",
|
|
5305
|
+
target: singleFieldValue2(surfaceField)
|
|
5306
|
+
};
|
|
5307
|
+
}
|
|
5308
|
+
if (freeField) {
|
|
5309
|
+
const raw = singleFieldValue2(freeField);
|
|
5310
|
+
const distance = tryParseAtlasUnitValue(raw);
|
|
5311
|
+
return {
|
|
5312
|
+
mode: "free",
|
|
5313
|
+
distance: distance ?? void 0,
|
|
5314
|
+
descriptor: distance ? void 0 : raw
|
|
5315
|
+
};
|
|
5316
|
+
}
|
|
5317
|
+
return null;
|
|
5318
|
+
}
|
|
5319
|
+
function normalizeDraftProperties(objectType, fieldMap) {
|
|
5320
|
+
const properties = {};
|
|
5321
|
+
for (const [key, fields] of fieldMap.entries()) {
|
|
5322
|
+
const field = fields[0];
|
|
5323
|
+
const spec = getDraftObjectFieldSpec(key);
|
|
5324
|
+
if (!field || !spec?.legacySchema || spec.legacySchema.placement) {
|
|
5325
|
+
continue;
|
|
5326
|
+
}
|
|
5327
|
+
ensureAtlasFieldSupported(key, objectType, field.location);
|
|
5328
|
+
properties[key] = normalizeLegacyScalarValue(key, field.values, field.location);
|
|
5329
|
+
}
|
|
5330
|
+
return properties;
|
|
5331
|
+
}
|
|
5332
|
+
function normalizeInfoEntries(entries, label) {
|
|
5333
|
+
const normalized = {};
|
|
5334
|
+
for (const entry of entries) {
|
|
5335
|
+
if (entry.key in normalized) {
|
|
5336
|
+
throw WorldOrbitError.fromLocation(`Duplicate ${label} key "${entry.key}"`, entry.location);
|
|
5337
|
+
}
|
|
5338
|
+
normalized[entry.key] = entry.value;
|
|
5339
|
+
}
|
|
5340
|
+
return normalized;
|
|
5341
|
+
}
|
|
5342
|
+
function normalizeTypedBlocks(typedBlockEntries) {
|
|
5343
|
+
const typedBlocks = {};
|
|
5344
|
+
for (const blockName of Object.keys(typedBlockEntries)) {
|
|
5345
|
+
const entries = typedBlockEntries[blockName];
|
|
5346
|
+
if (entries?.length) {
|
|
5347
|
+
typedBlocks[blockName] = normalizeInfoEntries(entries, blockName);
|
|
5348
|
+
}
|
|
5349
|
+
}
|
|
5350
|
+
return typedBlocks;
|
|
5351
|
+
}
|
|
5352
|
+
function extractRenderHints(fieldMap) {
|
|
5353
|
+
const renderHints = {};
|
|
5354
|
+
const renderLabelField = fieldMap.get("renderLabel")?.[0];
|
|
5355
|
+
const renderOrbitField = fieldMap.get("renderOrbit")?.[0];
|
|
5356
|
+
const renderPriorityField = fieldMap.get("renderPriority")?.[0];
|
|
5357
|
+
if (renderLabelField) {
|
|
5358
|
+
renderHints.renderLabel = parseAtlasBoolean(singleFieldValue2(renderLabelField), "renderLabel", renderLabelField.location);
|
|
5359
|
+
}
|
|
5360
|
+
if (renderOrbitField) {
|
|
5361
|
+
renderHints.renderOrbit = parseAtlasBoolean(singleFieldValue2(renderOrbitField), "renderOrbit", renderOrbitField.location);
|
|
5362
|
+
}
|
|
5363
|
+
if (renderPriorityField) {
|
|
5364
|
+
renderHints.renderPriority = parseAtlasNumber(singleFieldValue2(renderPriorityField), "renderPriority", renderPriorityField.location);
|
|
5365
|
+
}
|
|
5366
|
+
return Object.keys(renderHints).length > 0 ? renderHints : void 0;
|
|
5367
|
+
}
|
|
5368
|
+
function parseResonanceField(field) {
|
|
5369
|
+
if (field.values.length !== 2) {
|
|
5370
|
+
throw WorldOrbitError.fromLocation('Field "resonance" expects "<targetObjectId> <ratio>"', field.location);
|
|
5371
|
+
}
|
|
5372
|
+
const ratio = field.values[1];
|
|
5373
|
+
if (!/^\d+:\d+$/.test(ratio)) {
|
|
5374
|
+
throw WorldOrbitError.fromLocation(`Invalid resonance ratio "${ratio}"`, field.location);
|
|
5375
|
+
}
|
|
5376
|
+
return {
|
|
5377
|
+
targetObjectId: field.values[0],
|
|
5378
|
+
ratio
|
|
5379
|
+
};
|
|
5380
|
+
}
|
|
5381
|
+
function parseDeriveField(field) {
|
|
5382
|
+
if (field.values.length !== 2) {
|
|
5383
|
+
throw WorldOrbitError.fromLocation('Field "derive" expects "<field> <strategy>"', field.location);
|
|
5384
|
+
}
|
|
5385
|
+
return {
|
|
5386
|
+
field: field.values[0],
|
|
5387
|
+
strategy: field.values[1]
|
|
5388
|
+
};
|
|
5389
|
+
}
|
|
5390
|
+
function parseToleranceField(field) {
|
|
5391
|
+
if (field.values.length !== 2) {
|
|
5392
|
+
throw WorldOrbitError.fromLocation('Field "tolerance" expects "<field> <value>"', field.location);
|
|
5393
|
+
}
|
|
5394
|
+
const rawValue = field.values[1];
|
|
5395
|
+
const unitValue = tryParseAtlasUnitValue(rawValue);
|
|
5396
|
+
const numericValue = Number(rawValue);
|
|
5397
|
+
return {
|
|
5398
|
+
field: field.values[0],
|
|
5399
|
+
value: unitValue ?? (Number.isFinite(numericValue) ? numericValue : rawValue)
|
|
5400
|
+
};
|
|
5401
|
+
}
|
|
5402
|
+
function parseOptionalTokenList(field) {
|
|
5403
|
+
return field ? [...new Set(field.values)] : [];
|
|
5404
|
+
}
|
|
5405
|
+
function parseOptionalJoinedValue(field) {
|
|
5406
|
+
if (!field) {
|
|
5407
|
+
return null;
|
|
5408
|
+
}
|
|
5409
|
+
return field.values.join(" ").trim() || null;
|
|
5410
|
+
}
|
|
5411
|
+
function parseOptionalUnitField(field, key) {
|
|
5412
|
+
return field ? parseAtlasUnitValue(singleFieldValue2(field), field.location, key) : void 0;
|
|
5413
|
+
}
|
|
5414
|
+
function parseOptionalNumberField(field, key) {
|
|
5415
|
+
return field ? parseAtlasNumber(singleFieldValue2(field), key, field.location) : void 0;
|
|
5416
|
+
}
|
|
5417
|
+
function singleFieldValue2(field) {
|
|
5418
|
+
return singleAtlasValue(field.values, field.key, field.location);
|
|
5419
|
+
}
|
|
5420
|
+
function getDraftObjectFieldSpec(key) {
|
|
5421
|
+
return DRAFT_OBJECT_FIELD_SPECS.get(key);
|
|
5422
|
+
}
|
|
5423
|
+
function validateDraftObjectFieldCompatibility(fields, objectType) {
|
|
5424
|
+
for (const field of fields) {
|
|
5425
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
5426
|
+
if (!spec) {
|
|
5427
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
5428
|
+
}
|
|
5429
|
+
if (spec.legacySchema) {
|
|
5430
|
+
ensureAtlasFieldSupported(field.key, objectType, field.location);
|
|
5431
|
+
continue;
|
|
5432
|
+
}
|
|
5433
|
+
if ((field.key === "renderLabel" || field.key === "renderOrbit" || field.key === "tidalLock") && field.values.length !== 1) {
|
|
5434
|
+
throw WorldOrbitError.fromLocation(`Field "${field.key}" expects exactly one value`, field.location);
|
|
5435
|
+
}
|
|
5436
|
+
}
|
|
5437
|
+
}
|
|
5438
|
+
function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
5439
|
+
if (sourceSchemaVersion === "2.1") {
|
|
5440
|
+
return;
|
|
5441
|
+
}
|
|
5442
|
+
diagnostics.push({
|
|
5443
|
+
code: "parse.schema21.featureCompatibility",
|
|
5444
|
+
severity: "warning",
|
|
5445
|
+
source: "parse",
|
|
5446
|
+
message: `Feature "${featureName}" requires schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
5447
|
+
line: location.line,
|
|
5448
|
+
column: location.column
|
|
5449
|
+
});
|
|
5450
|
+
}
|
|
5451
|
+
function preprocessAtlasSource(source) {
|
|
5452
|
+
const chars = [...source];
|
|
5453
|
+
const comments = [];
|
|
5454
|
+
let inString = false;
|
|
5455
|
+
let inBlockComment = false;
|
|
5456
|
+
let blockCommentStart = null;
|
|
5457
|
+
let line = 1;
|
|
5458
|
+
let column = 1;
|
|
5459
|
+
for (let index = 0; index < chars.length; index++) {
|
|
5460
|
+
const ch = chars[index];
|
|
5461
|
+
const next = chars[index + 1];
|
|
5462
|
+
if (inBlockComment) {
|
|
5463
|
+
if (ch === "*" && next === "/") {
|
|
5464
|
+
chars[index] = " ";
|
|
5465
|
+
chars[index + 1] = " ";
|
|
5466
|
+
inBlockComment = false;
|
|
5467
|
+
blockCommentStart = null;
|
|
5468
|
+
index++;
|
|
5469
|
+
column += 2;
|
|
5470
|
+
continue;
|
|
5471
|
+
}
|
|
5472
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
5473
|
+
chars[index] = " ";
|
|
5474
|
+
}
|
|
5475
|
+
if (ch === "\n") {
|
|
5476
|
+
line++;
|
|
5477
|
+
column = 1;
|
|
5478
|
+
} else {
|
|
5479
|
+
column++;
|
|
5480
|
+
}
|
|
5481
|
+
continue;
|
|
5482
|
+
}
|
|
5483
|
+
if (!inString && ch === "/" && next === "*") {
|
|
5484
|
+
comments.push({ kind: "block", line, column });
|
|
5485
|
+
chars[index] = " ";
|
|
5486
|
+
chars[index + 1] = " ";
|
|
5487
|
+
inBlockComment = true;
|
|
5488
|
+
blockCommentStart = { line, column };
|
|
5489
|
+
index++;
|
|
5490
|
+
column += 2;
|
|
5491
|
+
continue;
|
|
5492
|
+
}
|
|
5493
|
+
if (!inString && ch === "#" && !isHexColorLiteral(chars, index)) {
|
|
5494
|
+
comments.push({ kind: "line", line, column });
|
|
5495
|
+
chars[index] = " ";
|
|
5496
|
+
let inner = index + 1;
|
|
5497
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
5498
|
+
chars[inner] = " ";
|
|
5499
|
+
inner++;
|
|
5500
|
+
}
|
|
5501
|
+
column += inner - index;
|
|
5502
|
+
index = inner - 1;
|
|
5503
|
+
continue;
|
|
5504
|
+
}
|
|
5505
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
5506
|
+
inString = !inString;
|
|
5507
|
+
}
|
|
5508
|
+
if (ch === "\n") {
|
|
5509
|
+
line++;
|
|
5510
|
+
column = 1;
|
|
5511
|
+
} else {
|
|
5512
|
+
column++;
|
|
5513
|
+
}
|
|
5514
|
+
}
|
|
5515
|
+
if (inBlockComment) {
|
|
5516
|
+
throw WorldOrbitError.fromLocation("Unclosed block comment", blockCommentStart ?? void 0);
|
|
5517
|
+
}
|
|
5518
|
+
return {
|
|
5519
|
+
source: chars.join(""),
|
|
5520
|
+
comments
|
|
5521
|
+
};
|
|
5522
|
+
}
|
|
5523
|
+
function isHexColorLiteral(chars, start) {
|
|
5524
|
+
let index = start + 1;
|
|
5525
|
+
let length = 0;
|
|
5526
|
+
while (index < chars.length && /[0-9a-f]/i.test(chars[index] ?? "")) {
|
|
5527
|
+
index++;
|
|
5528
|
+
length++;
|
|
5529
|
+
}
|
|
5530
|
+
if (![3, 4, 6, 8].includes(length)) {
|
|
5531
|
+
return false;
|
|
5532
|
+
}
|
|
5533
|
+
const next = chars[index];
|
|
5534
|
+
return next === void 0 || next === " " || next === " " || next === "\r" || next === "\n";
|
|
3617
5535
|
}
|
|
3618
5536
|
|
|
3619
5537
|
// packages/core/dist/atlas-edit.js
|
|
3620
|
-
function createEmptyAtlasDocument(systemId = "WorldOrbit") {
|
|
5538
|
+
function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "2.0") {
|
|
3621
5539
|
return {
|
|
3622
5540
|
format: "worldorbit",
|
|
3623
|
-
version
|
|
5541
|
+
version,
|
|
5542
|
+
schemaVersion: version,
|
|
3624
5543
|
sourceVersion: "1.0",
|
|
3625
5544
|
system: {
|
|
3626
5545
|
type: "system",
|
|
3627
5546
|
id: systemId,
|
|
3628
5547
|
title: systemId,
|
|
5548
|
+
description: null,
|
|
5549
|
+
epoch: null,
|
|
5550
|
+
referencePlane: null,
|
|
3629
5551
|
defaults: {
|
|
3630
5552
|
view: "topdown",
|
|
3631
5553
|
scale: null,
|
|
@@ -3637,6 +5559,9 @@
|
|
|
3637
5559
|
viewpoints: [],
|
|
3638
5560
|
annotations: []
|
|
3639
5561
|
},
|
|
5562
|
+
groups: [],
|
|
5563
|
+
relations: [],
|
|
5564
|
+
events: [],
|
|
3640
5565
|
objects: [],
|
|
3641
5566
|
diagnostics: []
|
|
3642
5567
|
};
|
|
@@ -3650,14 +5575,26 @@
|
|
|
3650
5575
|
for (const key of Object.keys(document.system.atlasMetadata).sort()) {
|
|
3651
5576
|
paths.push({ kind: "metadata", key });
|
|
3652
5577
|
}
|
|
3653
|
-
for (const viewpoint of [...document.system.viewpoints].sort(
|
|
5578
|
+
for (const viewpoint of [...document.system.viewpoints].sort(compareIdLike2)) {
|
|
3654
5579
|
paths.push({ kind: "viewpoint", id: viewpoint.id });
|
|
3655
5580
|
}
|
|
3656
|
-
for (const annotation of [...document.system.annotations].sort(
|
|
5581
|
+
for (const annotation of [...document.system.annotations].sort(compareIdLike2)) {
|
|
3657
5582
|
paths.push({ kind: "annotation", id: annotation.id });
|
|
3658
5583
|
}
|
|
3659
5584
|
}
|
|
3660
|
-
for (const
|
|
5585
|
+
for (const group of [...document.groups].sort(compareIdLike2)) {
|
|
5586
|
+
paths.push({ kind: "group", id: group.id });
|
|
5587
|
+
}
|
|
5588
|
+
for (const relation of [...document.relations].sort(compareIdLike2)) {
|
|
5589
|
+
paths.push({ kind: "relation", id: relation.id });
|
|
5590
|
+
}
|
|
5591
|
+
for (const event of [...document.events].sort(compareIdLike2)) {
|
|
5592
|
+
paths.push({ kind: "event", id: event.id });
|
|
5593
|
+
for (const pose of [...event.positions].sort(comparePoseObjectId2)) {
|
|
5594
|
+
paths.push({ kind: "event-pose", id: event.id, key: pose.objectId });
|
|
5595
|
+
}
|
|
5596
|
+
}
|
|
5597
|
+
for (const object of [...document.objects].sort(compareIdLike2)) {
|
|
3661
5598
|
paths.push({ kind: "object", id: object.id });
|
|
3662
5599
|
}
|
|
3663
5600
|
return paths;
|
|
@@ -3670,12 +5607,20 @@
|
|
|
3670
5607
|
return document.system?.defaults ?? null;
|
|
3671
5608
|
case "metadata":
|
|
3672
5609
|
return path.key ? document.system?.atlasMetadata[path.key] ?? null : null;
|
|
5610
|
+
case "group":
|
|
5611
|
+
return path.id ? findGroup(document, path.id) : null;
|
|
5612
|
+
case "event":
|
|
5613
|
+
return path.id ? findEvent(document, path.id) : null;
|
|
5614
|
+
case "event-pose":
|
|
5615
|
+
return path.id && path.key ? findEventPose(document, path.id, path.key) : null;
|
|
3673
5616
|
case "object":
|
|
3674
5617
|
return path.id ? findObject(document, path.id) : null;
|
|
3675
5618
|
case "viewpoint":
|
|
3676
5619
|
return path.id ? findViewpoint(document.system, path.id) : null;
|
|
3677
5620
|
case "annotation":
|
|
3678
5621
|
return path.id ? findAnnotation(document.system, path.id) : null;
|
|
5622
|
+
case "relation":
|
|
5623
|
+
return path.id ? findRelation(document, path.id) : null;
|
|
3679
5624
|
}
|
|
3680
5625
|
}
|
|
3681
5626
|
function upsertAtlasDocumentNode(document, path, value) {
|
|
@@ -3701,6 +5646,24 @@
|
|
|
3701
5646
|
system.atlasMetadata[path.key] = String(value);
|
|
3702
5647
|
}
|
|
3703
5648
|
return next;
|
|
5649
|
+
case "group":
|
|
5650
|
+
if (!path.id) {
|
|
5651
|
+
throw new Error('Group updates require an "id" value.');
|
|
5652
|
+
}
|
|
5653
|
+
upsertById(next.groups, value);
|
|
5654
|
+
return next;
|
|
5655
|
+
case "event":
|
|
5656
|
+
if (!path.id) {
|
|
5657
|
+
throw new Error('Event updates require an "id" value.');
|
|
5658
|
+
}
|
|
5659
|
+
upsertById(next.events, value);
|
|
5660
|
+
return next;
|
|
5661
|
+
case "event-pose":
|
|
5662
|
+
if (!path.id || !path.key) {
|
|
5663
|
+
throw new Error('Event pose updates require an event "id" and pose "key" value.');
|
|
5664
|
+
}
|
|
5665
|
+
upsertEventPose(next.events, path.id, value);
|
|
5666
|
+
return next;
|
|
3704
5667
|
case "object":
|
|
3705
5668
|
if (!path.id) {
|
|
3706
5669
|
throw new Error('Object updates require an "id" value.');
|
|
@@ -3719,6 +5682,12 @@
|
|
|
3719
5682
|
}
|
|
3720
5683
|
upsertById(system.annotations, value);
|
|
3721
5684
|
return next;
|
|
5685
|
+
case "relation":
|
|
5686
|
+
if (!path.id) {
|
|
5687
|
+
throw new Error('Relation updates require an "id" value.');
|
|
5688
|
+
}
|
|
5689
|
+
upsertById(next.relations, value);
|
|
5690
|
+
return next;
|
|
3722
5691
|
}
|
|
3723
5692
|
}
|
|
3724
5693
|
function updateAtlasDocumentNode(document, path, updater) {
|
|
@@ -3738,6 +5707,24 @@
|
|
|
3738
5707
|
next.objects = next.objects.filter((object) => object.id !== path.id);
|
|
3739
5708
|
}
|
|
3740
5709
|
return next;
|
|
5710
|
+
case "group":
|
|
5711
|
+
if (path.id) {
|
|
5712
|
+
next.groups = next.groups.filter((group) => group.id !== path.id);
|
|
5713
|
+
}
|
|
5714
|
+
return next;
|
|
5715
|
+
case "event":
|
|
5716
|
+
if (path.id) {
|
|
5717
|
+
next.events = next.events.filter((event) => event.id !== path.id);
|
|
5718
|
+
}
|
|
5719
|
+
return next;
|
|
5720
|
+
case "event-pose":
|
|
5721
|
+
if (path.id && path.key) {
|
|
5722
|
+
const event = findEvent(next, path.id);
|
|
5723
|
+
if (event) {
|
|
5724
|
+
event.positions = event.positions.filter((pose) => pose.objectId !== path.key);
|
|
5725
|
+
}
|
|
5726
|
+
}
|
|
5727
|
+
return next;
|
|
3741
5728
|
case "viewpoint":
|
|
3742
5729
|
if (path.id) {
|
|
3743
5730
|
system.viewpoints = system.viewpoints.filter((viewpoint) => viewpoint.id !== path.id);
|
|
@@ -3748,6 +5735,11 @@
|
|
|
3748
5735
|
system.annotations = system.annotations.filter((annotation) => annotation.id !== path.id);
|
|
3749
5736
|
}
|
|
3750
5737
|
return next;
|
|
5738
|
+
case "relation":
|
|
5739
|
+
if (path.id) {
|
|
5740
|
+
next.relations = next.relations.filter((relation) => relation.id !== path.id);
|
|
5741
|
+
}
|
|
5742
|
+
return next;
|
|
3751
5743
|
default:
|
|
3752
5744
|
return next;
|
|
3753
5745
|
}
|
|
@@ -3765,6 +5757,15 @@
|
|
|
3765
5757
|
id: diagnostic.objectId
|
|
3766
5758
|
};
|
|
3767
5759
|
}
|
|
5760
|
+
if (diagnostic.field?.startsWith("group.")) {
|
|
5761
|
+
const parts = diagnostic.field.split(".");
|
|
5762
|
+
if (parts[1] && findGroup(document, parts[1])) {
|
|
5763
|
+
return {
|
|
5764
|
+
kind: "group",
|
|
5765
|
+
id: parts[1]
|
|
5766
|
+
};
|
|
5767
|
+
}
|
|
5768
|
+
}
|
|
3768
5769
|
if (diagnostic.field?.startsWith("viewpoint.")) {
|
|
3769
5770
|
const parts = diagnostic.field.split(".");
|
|
3770
5771
|
if (parts[1] && findViewpoint(document.system, parts[1])) {
|
|
@@ -3783,6 +5784,31 @@
|
|
|
3783
5784
|
};
|
|
3784
5785
|
}
|
|
3785
5786
|
}
|
|
5787
|
+
if (diagnostic.field?.startsWith("relation.")) {
|
|
5788
|
+
const parts = diagnostic.field.split(".");
|
|
5789
|
+
if (parts[1] && findRelation(document, parts[1])) {
|
|
5790
|
+
return {
|
|
5791
|
+
kind: "relation",
|
|
5792
|
+
id: parts[1]
|
|
5793
|
+
};
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
if (diagnostic.field?.startsWith("event.")) {
|
|
5797
|
+
const parts = diagnostic.field.split(".");
|
|
5798
|
+
if (parts[1] && findEvent(document, parts[1])) {
|
|
5799
|
+
if (parts[2] === "pose" && parts[3] && findEventPose(document, parts[1], parts[3])) {
|
|
5800
|
+
return {
|
|
5801
|
+
kind: "event-pose",
|
|
5802
|
+
id: parts[1],
|
|
5803
|
+
key: parts[3]
|
|
5804
|
+
};
|
|
5805
|
+
}
|
|
5806
|
+
return {
|
|
5807
|
+
kind: "event",
|
|
5808
|
+
id: parts[1]
|
|
5809
|
+
};
|
|
5810
|
+
}
|
|
5811
|
+
}
|
|
3786
5812
|
if (diagnostic.field && diagnostic.field in ensureSystem(document).atlasMetadata) {
|
|
3787
5813
|
return {
|
|
3788
5814
|
kind: "metadata",
|
|
@@ -3792,9 +5818,11 @@
|
|
|
3792
5818
|
return null;
|
|
3793
5819
|
}
|
|
3794
5820
|
function validateAtlasDocumentWithDiagnostics(document) {
|
|
3795
|
-
const
|
|
3796
|
-
|
|
3797
|
-
|
|
5821
|
+
const diagnostics = [
|
|
5822
|
+
...document.diagnostics,
|
|
5823
|
+
...collectAtlasDiagnostics(document, document.version)
|
|
5824
|
+
];
|
|
5825
|
+
return resolveAtlasDiagnostics(document, diagnostics);
|
|
3798
5826
|
}
|
|
3799
5827
|
function ensureSystem(document) {
|
|
3800
5828
|
if (document.system) {
|
|
@@ -3806,6 +5834,18 @@
|
|
|
3806
5834
|
function findObject(document, objectId) {
|
|
3807
5835
|
return document.objects.find((object) => object.id === objectId) ?? null;
|
|
3808
5836
|
}
|
|
5837
|
+
function findGroup(document, groupId) {
|
|
5838
|
+
return document.groups.find((group) => group.id === groupId) ?? null;
|
|
5839
|
+
}
|
|
5840
|
+
function findRelation(document, relationId) {
|
|
5841
|
+
return document.relations.find((relation) => relation.id === relationId) ?? null;
|
|
5842
|
+
}
|
|
5843
|
+
function findEvent(document, eventId) {
|
|
5844
|
+
return document.events.find((event) => event.id === eventId) ?? null;
|
|
5845
|
+
}
|
|
5846
|
+
function findEventPose(document, eventId, objectId) {
|
|
5847
|
+
return findEvent(document, eventId)?.positions.find((pose) => pose.objectId === objectId) ?? null;
|
|
5848
|
+
}
|
|
3809
5849
|
function findViewpoint(system, viewpointId) {
|
|
3810
5850
|
return system?.viewpoints.find((viewpoint) => viewpoint.id === viewpointId) ?? null;
|
|
3811
5851
|
}
|
|
@@ -3816,20 +5856,37 @@
|
|
|
3816
5856
|
const index = items.findIndex((item) => item.id === value.id);
|
|
3817
5857
|
if (index === -1) {
|
|
3818
5858
|
items.push(value);
|
|
3819
|
-
items.sort(
|
|
5859
|
+
items.sort(compareIdLike2);
|
|
3820
5860
|
return;
|
|
3821
5861
|
}
|
|
3822
5862
|
items[index] = value;
|
|
3823
5863
|
}
|
|
3824
|
-
function
|
|
5864
|
+
function upsertEventPose(events, eventId, value) {
|
|
5865
|
+
const event = events.find((entry) => entry.id === eventId);
|
|
5866
|
+
if (!event) {
|
|
5867
|
+
throw new Error(`Unknown event "${eventId}" for pose update.`);
|
|
5868
|
+
}
|
|
5869
|
+
const index = event.positions.findIndex((entry) => entry.objectId === value.objectId);
|
|
5870
|
+
if (index === -1) {
|
|
5871
|
+
event.positions.push(value);
|
|
5872
|
+
event.positions.sort(comparePoseObjectId2);
|
|
5873
|
+
return;
|
|
5874
|
+
}
|
|
5875
|
+
event.positions[index] = value;
|
|
5876
|
+
}
|
|
5877
|
+
function compareIdLike2(left, right) {
|
|
3825
5878
|
return left.id.localeCompare(right.id);
|
|
3826
5879
|
}
|
|
5880
|
+
function comparePoseObjectId2(left, right) {
|
|
5881
|
+
return left.objectId.localeCompare(right.objectId);
|
|
5882
|
+
}
|
|
3827
5883
|
|
|
3828
5884
|
// packages/core/dist/load.js
|
|
3829
|
-
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0)?$/i;
|
|
5885
|
+
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;
|
|
5886
|
+
var ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
|
|
3830
5887
|
var LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
|
|
3831
5888
|
function detectWorldOrbitSchemaVersion(source) {
|
|
3832
|
-
for (const line of source.split(/\r?\n/)) {
|
|
5889
|
+
for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
|
|
3833
5890
|
const trimmed = line.trim();
|
|
3834
5891
|
if (!trimmed) {
|
|
3835
5892
|
continue;
|
|
@@ -3837,6 +5894,9 @@
|
|
|
3837
5894
|
if (LEGACY_DRAFT_SCHEMA_PATTERN.test(trimmed)) {
|
|
3838
5895
|
return "2.0-draft";
|
|
3839
5896
|
}
|
|
5897
|
+
if (ATLAS_SCHEMA_21_PATTERN.test(trimmed)) {
|
|
5898
|
+
return "2.1";
|
|
5899
|
+
}
|
|
3840
5900
|
if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
|
|
3841
5901
|
return "2.0";
|
|
3842
5902
|
}
|
|
@@ -3844,6 +5904,49 @@
|
|
|
3844
5904
|
}
|
|
3845
5905
|
return "1.0";
|
|
3846
5906
|
}
|
|
5907
|
+
function stripCommentsForSchemaDetection(source) {
|
|
5908
|
+
const chars = [...source];
|
|
5909
|
+
let inString = false;
|
|
5910
|
+
let inBlockComment = false;
|
|
5911
|
+
for (let index = 0; index < chars.length; index++) {
|
|
5912
|
+
const ch = chars[index];
|
|
5913
|
+
const next = chars[index + 1];
|
|
5914
|
+
if (inBlockComment) {
|
|
5915
|
+
if (ch === "*" && next === "/") {
|
|
5916
|
+
chars[index] = " ";
|
|
5917
|
+
chars[index + 1] = " ";
|
|
5918
|
+
inBlockComment = false;
|
|
5919
|
+
index++;
|
|
5920
|
+
continue;
|
|
5921
|
+
}
|
|
5922
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
5923
|
+
chars[index] = " ";
|
|
5924
|
+
}
|
|
5925
|
+
continue;
|
|
5926
|
+
}
|
|
5927
|
+
if (!inString && ch === "/" && next === "*") {
|
|
5928
|
+
chars[index] = " ";
|
|
5929
|
+
chars[index + 1] = " ";
|
|
5930
|
+
inBlockComment = true;
|
|
5931
|
+
index++;
|
|
5932
|
+
continue;
|
|
5933
|
+
}
|
|
5934
|
+
if (!inString && ch === "#") {
|
|
5935
|
+
chars[index] = " ";
|
|
5936
|
+
let inner = index + 1;
|
|
5937
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
5938
|
+
chars[inner] = " ";
|
|
5939
|
+
inner++;
|
|
5940
|
+
}
|
|
5941
|
+
index = inner - 1;
|
|
5942
|
+
continue;
|
|
5943
|
+
}
|
|
5944
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
5945
|
+
inString = !inString;
|
|
5946
|
+
}
|
|
5947
|
+
}
|
|
5948
|
+
return chars.join("");
|
|
5949
|
+
}
|
|
3847
5950
|
function loadWorldOrbitSource(source) {
|
|
3848
5951
|
const result = loadWorldOrbitSourceWithDiagnostics(source);
|
|
3849
5952
|
if (!result.ok || !result.value) {
|
|
@@ -3854,36 +5957,36 @@
|
|
|
3854
5957
|
}
|
|
3855
5958
|
function loadWorldOrbitSourceWithDiagnostics(source) {
|
|
3856
5959
|
const schemaVersion = detectWorldOrbitSchemaVersion(source);
|
|
3857
|
-
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft") {
|
|
5960
|
+
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1") {
|
|
3858
5961
|
return loadAtlasSourceWithDiagnostics(source, schemaVersion);
|
|
3859
5962
|
}
|
|
3860
5963
|
let ast;
|
|
3861
5964
|
try {
|
|
3862
5965
|
ast = parseWorldOrbit(source);
|
|
3863
|
-
} catch (
|
|
5966
|
+
} catch (error2) {
|
|
3864
5967
|
return {
|
|
3865
5968
|
ok: false,
|
|
3866
5969
|
value: null,
|
|
3867
|
-
diagnostics: [diagnosticFromError(
|
|
5970
|
+
diagnostics: [diagnosticFromError(error2, "parse")]
|
|
3868
5971
|
};
|
|
3869
5972
|
}
|
|
3870
5973
|
let document;
|
|
3871
5974
|
try {
|
|
3872
5975
|
document = normalizeDocument(ast);
|
|
3873
|
-
} catch (
|
|
5976
|
+
} catch (error2) {
|
|
3874
5977
|
return {
|
|
3875
5978
|
ok: false,
|
|
3876
5979
|
value: null,
|
|
3877
|
-
diagnostics: [diagnosticFromError(
|
|
5980
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
3878
5981
|
};
|
|
3879
5982
|
}
|
|
3880
5983
|
try {
|
|
3881
5984
|
validateDocument(document);
|
|
3882
|
-
} catch (
|
|
5985
|
+
} catch (error2) {
|
|
3883
5986
|
return {
|
|
3884
5987
|
ok: false,
|
|
3885
5988
|
value: null,
|
|
3886
|
-
diagnostics: [diagnosticFromError(
|
|
5989
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
3887
5990
|
};
|
|
3888
5991
|
}
|
|
3889
5992
|
return {
|
|
@@ -3903,30 +6006,29 @@
|
|
|
3903
6006
|
let atlasDocument;
|
|
3904
6007
|
try {
|
|
3905
6008
|
atlasDocument = parseWorldOrbitAtlas(source);
|
|
3906
|
-
} catch (
|
|
6009
|
+
} catch (error2) {
|
|
3907
6010
|
return {
|
|
3908
6011
|
ok: false,
|
|
3909
6012
|
value: null,
|
|
3910
|
-
diagnostics: [diagnosticFromError(
|
|
6013
|
+
diagnostics: [diagnosticFromError(error2, "parse", "load.atlas.failed")]
|
|
3911
6014
|
};
|
|
3912
6015
|
}
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
document = materializeAtlasDocument(atlasDocument);
|
|
3916
|
-
} catch (error) {
|
|
6016
|
+
const atlasDiagnostics = [...atlasDocument.diagnostics];
|
|
6017
|
+
if (atlasDiagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
3917
6018
|
return {
|
|
3918
6019
|
ok: false,
|
|
3919
6020
|
value: null,
|
|
3920
|
-
diagnostics:
|
|
6021
|
+
diagnostics: atlasDiagnostics
|
|
3921
6022
|
};
|
|
3922
6023
|
}
|
|
6024
|
+
let document;
|
|
3923
6025
|
try {
|
|
3924
|
-
|
|
3925
|
-
} catch (
|
|
6026
|
+
document = materializeAtlasDocument(atlasDocument);
|
|
6027
|
+
} catch (error2) {
|
|
3926
6028
|
return {
|
|
3927
6029
|
ok: false,
|
|
3928
6030
|
value: null,
|
|
3929
|
-
diagnostics: [diagnosticFromError(
|
|
6031
|
+
diagnostics: [diagnosticFromError(error2, "normalize", "load.atlas.materialize.failed")]
|
|
3930
6032
|
};
|
|
3931
6033
|
}
|
|
3932
6034
|
const loaded = {
|
|
@@ -3935,12 +6037,12 @@
|
|
|
3935
6037
|
document,
|
|
3936
6038
|
atlasDocument,
|
|
3937
6039
|
draftDocument: atlasDocument,
|
|
3938
|
-
diagnostics:
|
|
6040
|
+
diagnostics: atlasDiagnostics
|
|
3939
6041
|
};
|
|
3940
6042
|
return {
|
|
3941
6043
|
ok: true,
|
|
3942
6044
|
value: loaded,
|
|
3943
|
-
diagnostics:
|
|
6045
|
+
diagnostics: atlasDiagnostics
|
|
3944
6046
|
};
|
|
3945
6047
|
}
|
|
3946
6048
|
|