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":
|
|
@@ -512,7 +512,7 @@
|
|
|
512
512
|
}
|
|
513
513
|
|
|
514
514
|
// packages/core/dist/normalize.js
|
|
515
|
-
var UNIT_PATTERN = /^(-?\d+(?:\.\d+)?)(au|km|re|sol|
|
|
515
|
+
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)?$/;
|
|
516
516
|
var BOOLEAN_VALUES = /* @__PURE__ */ new Map([
|
|
517
517
|
["true", true],
|
|
518
518
|
["false", false],
|
|
@@ -537,7 +537,11 @@
|
|
|
537
537
|
return {
|
|
538
538
|
format: "worldorbit",
|
|
539
539
|
version: "1.0",
|
|
540
|
+
schemaVersion: "1.0",
|
|
540
541
|
system,
|
|
542
|
+
groups: [],
|
|
543
|
+
relations: [],
|
|
544
|
+
events: [],
|
|
541
545
|
objects
|
|
542
546
|
};
|
|
543
547
|
}
|
|
@@ -547,13 +551,17 @@
|
|
|
547
551
|
const fieldMap = collectFields(mergedFields);
|
|
548
552
|
const placement = extractPlacement(node.objectType, fieldMap);
|
|
549
553
|
const properties = normalizeProperties(fieldMap);
|
|
550
|
-
const
|
|
554
|
+
const info2 = normalizeInfo(node.infoEntries);
|
|
551
555
|
if (node.objectType === "system") {
|
|
552
556
|
return {
|
|
553
557
|
type: "system",
|
|
554
558
|
id: node.name,
|
|
559
|
+
title: typeof properties.title === "string" ? properties.title : null,
|
|
560
|
+
description: null,
|
|
561
|
+
epoch: null,
|
|
562
|
+
referencePlane: null,
|
|
555
563
|
properties,
|
|
556
|
-
info
|
|
564
|
+
info: info2
|
|
557
565
|
};
|
|
558
566
|
}
|
|
559
567
|
return {
|
|
@@ -561,7 +569,7 @@
|
|
|
561
569
|
id: node.name,
|
|
562
570
|
properties,
|
|
563
571
|
placement,
|
|
564
|
-
info
|
|
572
|
+
info: info2
|
|
565
573
|
};
|
|
566
574
|
}
|
|
567
575
|
function validateFieldCompatibility(objectType, fields) {
|
|
@@ -691,14 +699,14 @@
|
|
|
691
699
|
}
|
|
692
700
|
}
|
|
693
701
|
function normalizeInfo(entries) {
|
|
694
|
-
const
|
|
702
|
+
const info2 = {};
|
|
695
703
|
for (const entry of entries) {
|
|
696
|
-
if (entry.key in
|
|
704
|
+
if (entry.key in info2) {
|
|
697
705
|
throw WorldOrbitError.fromLocation(`Duplicate info key "${entry.key}"`, entry.location);
|
|
698
706
|
}
|
|
699
|
-
|
|
707
|
+
info2[entry.key] = entry.value;
|
|
700
708
|
}
|
|
701
|
-
return
|
|
709
|
+
return info2;
|
|
702
710
|
}
|
|
703
711
|
function parseAtReference(target, location) {
|
|
704
712
|
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
@@ -869,37 +877,41 @@
|
|
|
869
877
|
}
|
|
870
878
|
|
|
871
879
|
// packages/core/dist/diagnostics.js
|
|
872
|
-
function diagnosticFromError(
|
|
873
|
-
if (
|
|
880
|
+
function diagnosticFromError(error2, source, code = `${source}.failed`) {
|
|
881
|
+
if (error2 instanceof WorldOrbitError) {
|
|
874
882
|
return {
|
|
875
883
|
code,
|
|
876
884
|
severity: "error",
|
|
877
885
|
source,
|
|
878
|
-
message:
|
|
879
|
-
line:
|
|
880
|
-
column:
|
|
886
|
+
message: error2.message,
|
|
887
|
+
line: error2.line,
|
|
888
|
+
column: error2.column
|
|
881
889
|
};
|
|
882
890
|
}
|
|
883
|
-
if (
|
|
891
|
+
if (error2 instanceof Error) {
|
|
884
892
|
return {
|
|
885
893
|
code,
|
|
886
894
|
severity: "error",
|
|
887
895
|
source,
|
|
888
|
-
message:
|
|
896
|
+
message: error2.message
|
|
889
897
|
};
|
|
890
898
|
}
|
|
891
899
|
return {
|
|
892
900
|
code,
|
|
893
901
|
severity: "error",
|
|
894
902
|
source,
|
|
895
|
-
message: String(
|
|
903
|
+
message: String(error2)
|
|
896
904
|
};
|
|
897
905
|
}
|
|
898
906
|
|
|
899
907
|
// packages/core/dist/scene.js
|
|
900
908
|
var AU_IN_KM = 1495978707e-1;
|
|
901
909
|
var EARTH_RADIUS_IN_KM = 6371;
|
|
910
|
+
var JUPITER_RADIUS_IN_KM = 71492;
|
|
902
911
|
var SOLAR_RADIUS_IN_KM = 695700;
|
|
912
|
+
var LY_IN_AU = 63241.077;
|
|
913
|
+
var PC_IN_AU = 206264.806;
|
|
914
|
+
var KPC_IN_AU = 206264806;
|
|
903
915
|
var ISO_FLATTENING = 0.68;
|
|
904
916
|
var MIN_ISO_MINOR_SCALE = 0.2;
|
|
905
917
|
var ARC_SAMPLE_COUNT = 28;
|
|
@@ -913,8 +925,10 @@
|
|
|
913
925
|
const scaleModel = resolveScaleModel(layoutPreset, options.scaleModel);
|
|
914
926
|
const spacingFactor = layoutPresetSpacing(layoutPreset);
|
|
915
927
|
const systemId = document2.system?.id ?? null;
|
|
916
|
-
const
|
|
917
|
-
const
|
|
928
|
+
const activeEventId = options.activeEventId ?? null;
|
|
929
|
+
const effectiveObjects = createEffectiveObjects(document2.objects, document2.events ?? [], activeEventId);
|
|
930
|
+
const objectMap = new Map(effectiveObjects.map((object) => [object.id, object]));
|
|
931
|
+
const relationships = buildSceneRelationships(effectiveObjects, objectMap);
|
|
918
932
|
const positions = /* @__PURE__ */ new Map();
|
|
919
933
|
const orbitDrafts = [];
|
|
920
934
|
const leaderDrafts = [];
|
|
@@ -923,7 +937,7 @@
|
|
|
923
937
|
const atObjects = [];
|
|
924
938
|
const surfaceChildren = /* @__PURE__ */ new Map();
|
|
925
939
|
const orbitChildren = /* @__PURE__ */ new Map();
|
|
926
|
-
for (const object of
|
|
940
|
+
for (const object of effectiveObjects) {
|
|
927
941
|
const placement = object.placement;
|
|
928
942
|
if (!placement) {
|
|
929
943
|
rootObjects.push(object);
|
|
@@ -1018,11 +1032,14 @@
|
|
|
1018
1032
|
const objects = [...positions.values()].map((position) => createSceneObject(position, scaleModel, relationships));
|
|
1019
1033
|
const orbitVisuals = orbitDrafts.map((draft) => createOrbitVisual(draft, relationships.groupIds.get(draft.object.id) ?? null));
|
|
1020
1034
|
const leaders = leaderDrafts.map((draft) => createLeaderLine(draft));
|
|
1021
|
-
const labels = createSceneLabels(objects, height, scaleModel.labelMultiplier);
|
|
1022
|
-
const
|
|
1023
|
-
const
|
|
1035
|
+
const labels = createSceneLabels(objects, width, height, scaleModel.labelMultiplier);
|
|
1036
|
+
const relations = createSceneRelations(document2, objects);
|
|
1037
|
+
const events = createSceneEvents(document2.events ?? [], objects, activeEventId);
|
|
1038
|
+
const layers = createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels);
|
|
1039
|
+
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, scaleModel.labelMultiplier);
|
|
1040
|
+
const semanticGroups = createSceneSemanticGroups(document2, objects);
|
|
1024
1041
|
const viewpoints = createSceneViewpoints(document2, projection, frame.preset, relationships, objectMap);
|
|
1025
|
-
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels);
|
|
1042
|
+
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, scaleModel.labelMultiplier);
|
|
1026
1043
|
return {
|
|
1027
1044
|
width,
|
|
1028
1045
|
height,
|
|
@@ -1030,7 +1047,7 @@
|
|
|
1030
1047
|
renderPreset: frame.preset,
|
|
1031
1048
|
projection,
|
|
1032
1049
|
scaleModel,
|
|
1033
|
-
title: String(document2.system?.properties.title ?? document2.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1050
|
+
title: String(document2.system?.title ?? document2.system?.properties.title ?? document2.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1034
1051
|
subtitle: `${capitalizeLabel(projection)} view - ${capitalizeLabel(layoutPreset)} layout`,
|
|
1035
1052
|
systemId,
|
|
1036
1053
|
viewMode: projection,
|
|
@@ -1046,13 +1063,46 @@
|
|
|
1046
1063
|
contentBounds,
|
|
1047
1064
|
layers,
|
|
1048
1065
|
groups,
|
|
1066
|
+
semanticGroups,
|
|
1049
1067
|
viewpoints,
|
|
1068
|
+
events,
|
|
1069
|
+
activeEventId,
|
|
1050
1070
|
objects,
|
|
1051
1071
|
orbitVisuals,
|
|
1072
|
+
relations,
|
|
1052
1073
|
leaders,
|
|
1053
1074
|
labels
|
|
1054
1075
|
};
|
|
1055
1076
|
}
|
|
1077
|
+
function createEffectiveObjects(objects, events, activeEventId) {
|
|
1078
|
+
const cloned = objects.map((object) => structuredClone(object));
|
|
1079
|
+
if (!activeEventId) {
|
|
1080
|
+
return cloned;
|
|
1081
|
+
}
|
|
1082
|
+
const activeEvent = events.find((event) => event.id === activeEventId);
|
|
1083
|
+
if (!activeEvent) {
|
|
1084
|
+
return cloned;
|
|
1085
|
+
}
|
|
1086
|
+
const objectMap = new Map(cloned.map((object) => [object.id, object]));
|
|
1087
|
+
for (const pose of activeEvent.positions) {
|
|
1088
|
+
const object = objectMap.get(pose.objectId);
|
|
1089
|
+
if (!object) {
|
|
1090
|
+
continue;
|
|
1091
|
+
}
|
|
1092
|
+
object.placement = pose.placement ? structuredClone(pose.placement) : null;
|
|
1093
|
+
if (pose.inner) {
|
|
1094
|
+
object.properties.inner = { ...pose.inner };
|
|
1095
|
+
} else {
|
|
1096
|
+
delete object.properties.inner;
|
|
1097
|
+
}
|
|
1098
|
+
if (pose.outer) {
|
|
1099
|
+
object.properties.outer = { ...pose.outer };
|
|
1100
|
+
} else {
|
|
1101
|
+
delete object.properties.outer;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
return cloned;
|
|
1105
|
+
}
|
|
1056
1106
|
function resolveLayoutPreset(document2) {
|
|
1057
1107
|
const rawScale = String(document2.system?.properties.scale ?? "balanced").toLowerCase();
|
|
1058
1108
|
switch (rawScale) {
|
|
@@ -1147,6 +1197,7 @@
|
|
|
1147
1197
|
}
|
|
1148
1198
|
function createSceneObject(position, scaleModel, relationships) {
|
|
1149
1199
|
const { object, x, y, radius, sortKey, anchorX, anchorY } = position;
|
|
1200
|
+
const renderPriority = object.renderHints?.renderPriority ?? 0;
|
|
1150
1201
|
return {
|
|
1151
1202
|
renderId: createRenderId(object.id),
|
|
1152
1203
|
objectId: object.id,
|
|
@@ -1155,11 +1206,12 @@
|
|
|
1155
1206
|
ancestorIds: relationships.ancestorIds.get(object.id) ?? [],
|
|
1156
1207
|
childIds: relationships.childIds.get(object.id) ?? [],
|
|
1157
1208
|
groupId: relationships.groupIds.get(object.id) ?? null,
|
|
1209
|
+
semanticGroupIds: [...object.groups ?? []],
|
|
1158
1210
|
x,
|
|
1159
1211
|
y,
|
|
1160
1212
|
radius,
|
|
1161
1213
|
visualRadius: visualExtentForObject(object, radius, scaleModel),
|
|
1162
|
-
sortKey,
|
|
1214
|
+
sortKey: sortKey + renderPriority * 1e-3,
|
|
1163
1215
|
anchorX,
|
|
1164
1216
|
anchorY,
|
|
1165
1217
|
label: object.id,
|
|
@@ -1176,6 +1228,7 @@
|
|
|
1176
1228
|
object: draft.object,
|
|
1177
1229
|
parentId: draft.parentId,
|
|
1178
1230
|
groupId,
|
|
1231
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1179
1232
|
kind: draft.kind,
|
|
1180
1233
|
cx: draft.cx,
|
|
1181
1234
|
cy: draft.cy,
|
|
@@ -1187,7 +1240,7 @@
|
|
|
1187
1240
|
bandThickness: draft.bandThickness,
|
|
1188
1241
|
frontArcPath: draft.frontArcPath,
|
|
1189
1242
|
backArcPath: draft.backArcPath,
|
|
1190
|
-
hidden: draft.object.properties.hidden === true
|
|
1243
|
+
hidden: draft.object.properties.hidden === true || draft.object.renderHints?.renderOrbit === false
|
|
1191
1244
|
};
|
|
1192
1245
|
}
|
|
1193
1246
|
function createLeaderLine(draft) {
|
|
@@ -1196,6 +1249,7 @@
|
|
|
1196
1249
|
objectId: draft.object.id,
|
|
1197
1250
|
object: draft.object,
|
|
1198
1251
|
groupId: draft.groupId,
|
|
1252
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1199
1253
|
x1: draft.x1,
|
|
1200
1254
|
y1: draft.y1,
|
|
1201
1255
|
x2: draft.x2,
|
|
@@ -1204,42 +1258,144 @@
|
|
|
1204
1258
|
hidden: draft.object.properties.hidden === true
|
|
1205
1259
|
};
|
|
1206
1260
|
}
|
|
1207
|
-
function createSceneLabels(objects, sceneHeight, labelMultiplier) {
|
|
1261
|
+
function createSceneLabels(objects, sceneWidth, sceneHeight, labelMultiplier) {
|
|
1208
1262
|
const labels = [];
|
|
1209
1263
|
const occupied = [];
|
|
1210
|
-
const
|
|
1264
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1265
|
+
const visibleObjects = [...objects].filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false).sort(compareLabelPlacementOrder);
|
|
1211
1266
|
for (const object of visibleObjects) {
|
|
1212
|
-
const
|
|
1213
|
-
|
|
1214
|
-
let labelY = object.y + direction * (object.radius + 18 * labelMultiplier);
|
|
1215
|
-
let secondaryY = labelY + direction * (16 * labelMultiplier);
|
|
1216
|
-
let bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
|
|
1217
|
-
let attempts = 0;
|
|
1218
|
-
while (occupied.some((entry) => rectsOverlap(entry, bounds)) && attempts < 10) {
|
|
1219
|
-
labelY += direction * 14 * labelMultiplier;
|
|
1220
|
-
secondaryY += direction * 14 * labelMultiplier;
|
|
1221
|
-
bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
|
|
1222
|
-
attempts += 1;
|
|
1223
|
-
}
|
|
1224
|
-
occupied.push(bounds);
|
|
1267
|
+
const placement = selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) ?? createLabelPlacement(object, defaultVerticalDirection(object, objectMap.get(object.parentId ?? "") ?? null, sceneHeight), 0, labelMultiplier);
|
|
1268
|
+
occupied.push(createLabelRect(object, placement, labelMultiplier));
|
|
1225
1269
|
labels.push({
|
|
1226
1270
|
renderId: `${object.renderId}-label`,
|
|
1227
1271
|
objectId: object.objectId,
|
|
1228
1272
|
object: object.object,
|
|
1229
1273
|
groupId: object.groupId,
|
|
1274
|
+
semanticGroupIds: [...object.semanticGroupIds],
|
|
1230
1275
|
label: object.label,
|
|
1231
1276
|
secondaryLabel: object.secondaryLabel,
|
|
1232
|
-
x:
|
|
1233
|
-
y: labelY,
|
|
1234
|
-
secondaryY,
|
|
1235
|
-
textAnchor:
|
|
1236
|
-
direction: direction
|
|
1277
|
+
x: placement.x,
|
|
1278
|
+
y: placement.labelY,
|
|
1279
|
+
secondaryY: placement.secondaryY,
|
|
1280
|
+
textAnchor: placement.textAnchor,
|
|
1281
|
+
direction: placement.direction,
|
|
1237
1282
|
hidden: object.hidden
|
|
1238
1283
|
});
|
|
1239
1284
|
}
|
|
1240
1285
|
return labels;
|
|
1241
1286
|
}
|
|
1242
|
-
function
|
|
1287
|
+
function compareLabelPlacementOrder(left, right) {
|
|
1288
|
+
const priorityDiff = labelPlacementPriority(left) - labelPlacementPriority(right);
|
|
1289
|
+
if (priorityDiff !== 0) {
|
|
1290
|
+
return priorityDiff;
|
|
1291
|
+
}
|
|
1292
|
+
const renderPriorityDiff = (right.object.renderHints?.renderPriority ?? 0) - (left.object.renderHints?.renderPriority ?? 0);
|
|
1293
|
+
if (renderPriorityDiff !== 0) {
|
|
1294
|
+
return renderPriorityDiff;
|
|
1295
|
+
}
|
|
1296
|
+
return left.sortKey - right.sortKey;
|
|
1297
|
+
}
|
|
1298
|
+
function labelPlacementPriority(object) {
|
|
1299
|
+
switch (object.object.type) {
|
|
1300
|
+
case "star":
|
|
1301
|
+
return 0;
|
|
1302
|
+
case "planet":
|
|
1303
|
+
return 1;
|
|
1304
|
+
case "moon":
|
|
1305
|
+
return 2;
|
|
1306
|
+
case "belt":
|
|
1307
|
+
case "ring":
|
|
1308
|
+
return 3;
|
|
1309
|
+
case "asteroid":
|
|
1310
|
+
case "comet":
|
|
1311
|
+
return 4;
|
|
1312
|
+
case "structure":
|
|
1313
|
+
case "phenomenon":
|
|
1314
|
+
return 5;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
function selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) {
|
|
1318
|
+
for (const direction of preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight)) {
|
|
1319
|
+
const maxAttempts = direction === "left" || direction === "right" ? 4 : 6;
|
|
1320
|
+
for (let attempt = 0; attempt <= maxAttempts; attempt += 1) {
|
|
1321
|
+
const placement = createLabelPlacement(object, direction, attempt, labelMultiplier);
|
|
1322
|
+
const rect = createLabelRect(object, placement, labelMultiplier);
|
|
1323
|
+
if (!occupied.some((entry) => rectsOverlap(entry, rect))) {
|
|
1324
|
+
return placement;
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
return null;
|
|
1329
|
+
}
|
|
1330
|
+
function preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight) {
|
|
1331
|
+
const parent = object.parentId ? objectMap.get(object.parentId) ?? null : null;
|
|
1332
|
+
const vertical = defaultVerticalDirection(object, parent, sceneHeight);
|
|
1333
|
+
const oppositeVertical = vertical === "below" ? "above" : "below";
|
|
1334
|
+
const horizontal = defaultHorizontalDirection(object, parent, sceneWidth);
|
|
1335
|
+
const oppositeHorizontal = horizontal === "right" ? "left" : "right";
|
|
1336
|
+
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";
|
|
1337
|
+
return preferHorizontal ? [horizontal, vertical, oppositeHorizontal, oppositeVertical] : [vertical, horizontal, oppositeVertical, oppositeHorizontal];
|
|
1338
|
+
}
|
|
1339
|
+
function defaultVerticalDirection(object, parent, sceneHeight) {
|
|
1340
|
+
if (parent && Math.abs(object.y - parent.y) > 6) {
|
|
1341
|
+
return object.y >= parent.y ? "below" : "above";
|
|
1342
|
+
}
|
|
1343
|
+
return object.y > sceneHeight * 0.62 ? "above" : "below";
|
|
1344
|
+
}
|
|
1345
|
+
function defaultHorizontalDirection(object, parent, sceneWidth) {
|
|
1346
|
+
if (parent && Math.abs(object.x - parent.x) > 6) {
|
|
1347
|
+
return object.x >= parent.x ? "right" : "left";
|
|
1348
|
+
}
|
|
1349
|
+
return object.x >= sceneWidth / 2 ? "right" : "left";
|
|
1350
|
+
}
|
|
1351
|
+
function createLabelPlacement(object, direction, attempt, labelMultiplier) {
|
|
1352
|
+
const step = 14 * labelMultiplier;
|
|
1353
|
+
switch (direction) {
|
|
1354
|
+
case "above": {
|
|
1355
|
+
const labelY = object.y - (object.radius + 18 * labelMultiplier + attempt * step);
|
|
1356
|
+
return {
|
|
1357
|
+
x: object.x,
|
|
1358
|
+
labelY,
|
|
1359
|
+
secondaryY: labelY - 16 * labelMultiplier,
|
|
1360
|
+
textAnchor: "middle",
|
|
1361
|
+
direction
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
case "below": {
|
|
1365
|
+
const labelY = object.y + object.radius + 18 * labelMultiplier + attempt * step;
|
|
1366
|
+
return {
|
|
1367
|
+
x: object.x,
|
|
1368
|
+
labelY,
|
|
1369
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
1370
|
+
textAnchor: "middle",
|
|
1371
|
+
direction
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
case "left": {
|
|
1375
|
+
const x = object.x - (object.visualRadius + 16 * labelMultiplier + attempt * step);
|
|
1376
|
+
const labelY = object.y - 4 * labelMultiplier;
|
|
1377
|
+
return {
|
|
1378
|
+
x,
|
|
1379
|
+
labelY,
|
|
1380
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
1381
|
+
textAnchor: "end",
|
|
1382
|
+
direction
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
case "right": {
|
|
1386
|
+
const x = object.x + object.visualRadius + 16 * labelMultiplier + attempt * step;
|
|
1387
|
+
const labelY = object.y - 4 * labelMultiplier;
|
|
1388
|
+
return {
|
|
1389
|
+
x,
|
|
1390
|
+
labelY,
|
|
1391
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
1392
|
+
textAnchor: "start",
|
|
1393
|
+
direction
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
function createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels) {
|
|
1243
1399
|
const backOrbitIds = orbitVisuals.filter((visual) => !visual.hidden && Boolean(visual.backArcPath)).map((visual) => visual.renderId);
|
|
1244
1400
|
const frontOrbitIds = orbitVisuals.filter((visual) => !visual.hidden).map((visual) => visual.renderId);
|
|
1245
1401
|
return [
|
|
@@ -1250,6 +1406,14 @@
|
|
|
1250
1406
|
},
|
|
1251
1407
|
{ id: "orbits-back", renderIds: backOrbitIds },
|
|
1252
1408
|
{ id: "orbits-front", renderIds: frontOrbitIds },
|
|
1409
|
+
{
|
|
1410
|
+
id: "relations",
|
|
1411
|
+
renderIds: relations.filter((relation) => !relation.hidden).map((relation) => relation.renderId)
|
|
1412
|
+
},
|
|
1413
|
+
{
|
|
1414
|
+
id: "events",
|
|
1415
|
+
renderIds: events.filter((event) => !event.hidden).map((event) => event.renderId)
|
|
1416
|
+
},
|
|
1253
1417
|
{
|
|
1254
1418
|
id: "objects",
|
|
1255
1419
|
renderIds: objects.filter((object) => !object.hidden).map((object) => object.renderId)
|
|
@@ -1261,7 +1425,7 @@
|
|
|
1261
1425
|
{ id: "metadata", renderIds: ["wo-title", "wo-subtitle", "wo-meta"] }
|
|
1262
1426
|
];
|
|
1263
1427
|
}
|
|
1264
|
-
function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships) {
|
|
1428
|
+
function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, labelMultiplier) {
|
|
1265
1429
|
const groups = /* @__PURE__ */ new Map();
|
|
1266
1430
|
const ensureGroup = (groupId) => {
|
|
1267
1431
|
if (!groupId) {
|
|
@@ -1310,10 +1474,63 @@
|
|
|
1310
1474
|
}
|
|
1311
1475
|
}
|
|
1312
1476
|
for (const group of groups.values()) {
|
|
1313
|
-
group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels);
|
|
1477
|
+
group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier);
|
|
1314
1478
|
}
|
|
1315
1479
|
return [...groups.values()].sort((left, right) => left.label.localeCompare(right.label));
|
|
1316
1480
|
}
|
|
1481
|
+
function createSceneSemanticGroups(document2, objects) {
|
|
1482
|
+
return [...document2.groups].map((group) => ({
|
|
1483
|
+
id: group.id,
|
|
1484
|
+
label: group.label,
|
|
1485
|
+
summary: group.summary,
|
|
1486
|
+
color: group.color,
|
|
1487
|
+
tags: [...group.tags],
|
|
1488
|
+
hidden: group.hidden,
|
|
1489
|
+
objectIds: objects.filter((object) => !object.hidden && object.semanticGroupIds.includes(group.id)).map((object) => object.objectId)
|
|
1490
|
+
})).sort((left, right) => left.label.localeCompare(right.label));
|
|
1491
|
+
}
|
|
1492
|
+
function createSceneRelations(document2, objects) {
|
|
1493
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1494
|
+
return document2.relations.map((relation) => {
|
|
1495
|
+
const from = objectMap.get(relation.from);
|
|
1496
|
+
const to = objectMap.get(relation.to);
|
|
1497
|
+
return {
|
|
1498
|
+
renderId: `${createRenderId(relation.id)}-relation`,
|
|
1499
|
+
relationId: relation.id,
|
|
1500
|
+
relation,
|
|
1501
|
+
fromObjectId: relation.from,
|
|
1502
|
+
toObjectId: relation.to,
|
|
1503
|
+
x1: from?.x ?? 0,
|
|
1504
|
+
y1: from?.y ?? 0,
|
|
1505
|
+
x2: to?.x ?? 0,
|
|
1506
|
+
y2: to?.y ?? 0,
|
|
1507
|
+
hidden: relation.hidden || !from || !to || from.hidden || to.hidden
|
|
1508
|
+
};
|
|
1509
|
+
}).sort((left, right) => left.relation.id.localeCompare(right.relation.id));
|
|
1510
|
+
}
|
|
1511
|
+
function createSceneEvents(events, objects, activeEventId) {
|
|
1512
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1513
|
+
return events.map((event) => {
|
|
1514
|
+
const objectIds = [.../* @__PURE__ */ new Set([
|
|
1515
|
+
...event.targetObjectId ? [event.targetObjectId] : [],
|
|
1516
|
+
...event.participantObjectIds
|
|
1517
|
+
])];
|
|
1518
|
+
const positions = objectIds.map((objectId) => objectMap.get(objectId)).filter(Boolean);
|
|
1519
|
+
const centroidX = positions.length > 0 ? positions.reduce((sum, object) => sum + object.x, 0) / positions.length : 0;
|
|
1520
|
+
const centroidY = positions.length > 0 ? positions.reduce((sum, object) => sum + object.y, 0) / positions.length : 0;
|
|
1521
|
+
return {
|
|
1522
|
+
renderId: `${createRenderId(event.id)}-event`,
|
|
1523
|
+
eventId: event.id,
|
|
1524
|
+
event,
|
|
1525
|
+
objectIds,
|
|
1526
|
+
participantIds: [...event.participantObjectIds],
|
|
1527
|
+
targetObjectId: event.targetObjectId,
|
|
1528
|
+
x: centroidX,
|
|
1529
|
+
y: centroidY,
|
|
1530
|
+
hidden: event.hidden || positions.length === 0 || positions.every((object) => object.hidden) || activeEventId !== null && event.id !== activeEventId
|
|
1531
|
+
};
|
|
1532
|
+
}).sort((left, right) => left.event.id.localeCompare(right.event.id));
|
|
1533
|
+
}
|
|
1317
1534
|
function createSceneViewpoints(document2, projection, preset, relationships, objectMap) {
|
|
1318
1535
|
const generatedOverview = createGeneratedOverviewViewpoint(document2, projection, preset);
|
|
1319
1536
|
const drafts = /* @__PURE__ */ new Map();
|
|
@@ -1331,7 +1548,7 @@
|
|
|
1331
1548
|
}
|
|
1332
1549
|
const field = fieldParts.join(".").toLowerCase();
|
|
1333
1550
|
const draft = drafts.get(id) ?? { id };
|
|
1334
|
-
applyViewpointField(draft, field, value, projection, preset, relationships, objectMap);
|
|
1551
|
+
applyViewpointField(draft, field, value, document2, projection, preset, relationships, objectMap);
|
|
1335
1552
|
drafts.set(id, draft);
|
|
1336
1553
|
}
|
|
1337
1554
|
const viewpoints = [...drafts.values()].map((draft) => finalizeViewpointDraft(draft, projection, preset, objectMap)).filter(Boolean);
|
|
@@ -1359,13 +1576,15 @@
|
|
|
1359
1576
|
});
|
|
1360
1577
|
}
|
|
1361
1578
|
function createGeneratedOverviewViewpoint(document2, projection, preset) {
|
|
1362
|
-
const
|
|
1579
|
+
const title = document2.system?.title ?? document2.system?.properties.title;
|
|
1580
|
+
const label = title ? `${String(title)} Overview` : "Overview";
|
|
1363
1581
|
return {
|
|
1364
1582
|
id: "overview",
|
|
1365
1583
|
label,
|
|
1366
1584
|
summary: "Fit the whole system with the current atlas defaults.",
|
|
1367
1585
|
objectId: null,
|
|
1368
1586
|
selectedObjectId: null,
|
|
1587
|
+
eventIds: [],
|
|
1369
1588
|
projection,
|
|
1370
1589
|
preset,
|
|
1371
1590
|
rotationDeg: 0,
|
|
@@ -1375,7 +1594,7 @@
|
|
|
1375
1594
|
generated: true
|
|
1376
1595
|
};
|
|
1377
1596
|
}
|
|
1378
|
-
function applyViewpointField(draft, field, value, projection, preset, relationships, objectMap) {
|
|
1597
|
+
function applyViewpointField(draft, field, value, document2, projection, preset, relationships, objectMap) {
|
|
1379
1598
|
const normalizedValue = value.trim();
|
|
1380
1599
|
switch (field) {
|
|
1381
1600
|
case "label":
|
|
@@ -1402,6 +1621,9 @@
|
|
|
1402
1621
|
draft.select = normalizedValue;
|
|
1403
1622
|
}
|
|
1404
1623
|
return;
|
|
1624
|
+
case "events":
|
|
1625
|
+
draft.eventIds = splitListValue(normalizedValue);
|
|
1626
|
+
return;
|
|
1405
1627
|
case "projection":
|
|
1406
1628
|
case "view":
|
|
1407
1629
|
draft.projection = parseViewProjection(normalizedValue) ?? projection;
|
|
@@ -1442,7 +1664,7 @@
|
|
|
1442
1664
|
case "groups":
|
|
1443
1665
|
draft.filter = {
|
|
1444
1666
|
...draft.filter ?? createEmptyViewpointFilter(),
|
|
1445
|
-
groupIds: parseViewpointGroups(normalizedValue, relationships, objectMap)
|
|
1667
|
+
groupIds: parseViewpointGroups(normalizedValue, document2, relationships, objectMap)
|
|
1446
1668
|
};
|
|
1447
1669
|
return;
|
|
1448
1670
|
}
|
|
@@ -1458,6 +1680,7 @@
|
|
|
1458
1680
|
summary: draft.summary?.trim() || createViewpointSummary(label, objectId, filter),
|
|
1459
1681
|
objectId,
|
|
1460
1682
|
selectedObjectId,
|
|
1683
|
+
eventIds: [...new Set(draft.eventIds ?? [])],
|
|
1461
1684
|
projection: draft.projection ?? projection,
|
|
1462
1685
|
preset: draft.preset ?? preset,
|
|
1463
1686
|
rotationDeg: draft.rotationDeg ?? 0,
|
|
@@ -1515,7 +1738,7 @@
|
|
|
1515
1738
|
next["orbits-front"] = enabled;
|
|
1516
1739
|
continue;
|
|
1517
1740
|
}
|
|
1518
|
-
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1741
|
+
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "relations" || rawLayer === "events" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1519
1742
|
next[rawLayer] = enabled;
|
|
1520
1743
|
}
|
|
1521
1744
|
}
|
|
@@ -1524,8 +1747,11 @@
|
|
|
1524
1747
|
function parseViewpointObjectTypes(value) {
|
|
1525
1748
|
return splitListValue(value).filter((entry) => entry === "star" || entry === "planet" || entry === "moon" || entry === "belt" || entry === "asteroid" || entry === "comet" || entry === "ring" || entry === "structure" || entry === "phenomenon");
|
|
1526
1749
|
}
|
|
1527
|
-
function parseViewpointGroups(value, relationships, objectMap) {
|
|
1750
|
+
function parseViewpointGroups(value, document2, relationships, objectMap) {
|
|
1528
1751
|
return splitListValue(value).map((entry) => {
|
|
1752
|
+
if (document2.schemaVersion === "2.1" || document2.groups.some((group) => group.id === entry)) {
|
|
1753
|
+
return entry;
|
|
1754
|
+
}
|
|
1529
1755
|
if (entry.startsWith("wo-") && entry.endsWith("-group")) {
|
|
1530
1756
|
return entry;
|
|
1531
1757
|
}
|
|
@@ -1560,7 +1786,7 @@
|
|
|
1560
1786
|
}
|
|
1561
1787
|
return parts.join(" - ");
|
|
1562
1788
|
}
|
|
1563
|
-
function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels) {
|
|
1789
|
+
function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, labelMultiplier) {
|
|
1564
1790
|
let minX = Number.POSITIVE_INFINITY;
|
|
1565
1791
|
let minY = Number.POSITIVE_INFINITY;
|
|
1566
1792
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
@@ -1590,7 +1816,7 @@
|
|
|
1590
1816
|
for (const label of labels) {
|
|
1591
1817
|
if (label.hidden)
|
|
1592
1818
|
continue;
|
|
1593
|
-
includeLabelBounds(label, include);
|
|
1819
|
+
includeLabelBounds(label, include, labelMultiplier);
|
|
1594
1820
|
}
|
|
1595
1821
|
if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
|
|
1596
1822
|
return createBounds(0, 0, width, height);
|
|
@@ -1628,13 +1854,10 @@
|
|
|
1628
1854
|
include(object.x - object.visualRadius - 24, object.y - object.visualRadius - 16);
|
|
1629
1855
|
include(object.x + object.visualRadius + 24, object.y + object.visualRadius + 36);
|
|
1630
1856
|
}
|
|
1631
|
-
function includeLabelBounds(label, include) {
|
|
1632
|
-
const
|
|
1633
|
-
|
|
1634
|
-
include(
|
|
1635
|
-
include(label.x + labelHalfWidth, label.y + 8);
|
|
1636
|
-
include(label.x - labelHalfWidth, label.secondaryY - 14);
|
|
1637
|
-
include(label.x + labelHalfWidth, label.secondaryY + 8);
|
|
1857
|
+
function includeLabelBounds(label, include, labelMultiplier) {
|
|
1858
|
+
const bounds = createLabelRectFromText(label.x, label.y, label.secondaryY, label.textAnchor, label.direction, label.label, label.secondaryLabel, labelMultiplier);
|
|
1859
|
+
include(bounds.left, bounds.top);
|
|
1860
|
+
include(bounds.right, bounds.bottom);
|
|
1638
1861
|
}
|
|
1639
1862
|
function placeObject(object, x, y, depth, positions, orbitDrafts, leaderDrafts, context) {
|
|
1640
1863
|
if (positions.has(object.id)) {
|
|
@@ -1656,8 +1879,9 @@
|
|
|
1656
1879
|
}
|
|
1657
1880
|
const orbiting = [...context.orbitChildren.get(object.id) ?? []].sort(compareOrbiting);
|
|
1658
1881
|
const orbitMetricContext = computeOrbitMetricContext(orbiting, parent.radius, context.spacingFactor, context.scaleModel);
|
|
1882
|
+
const orbitRadiiPx = resolveOrbitRadiiPx(orbiting, orbitMetricContext);
|
|
1659
1883
|
orbiting.forEach((child, index) => {
|
|
1660
|
-
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, context);
|
|
1884
|
+
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, orbitRadiiPx[index] ?? orbitMetricContext.innerPx, context);
|
|
1661
1885
|
orbitDrafts.push({
|
|
1662
1886
|
object: child,
|
|
1663
1887
|
parentId: object.id,
|
|
@@ -1731,7 +1955,8 @@
|
|
|
1731
1955
|
metricSpread: 0,
|
|
1732
1956
|
innerPx,
|
|
1733
1957
|
stepPx,
|
|
1734
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
1958
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
1959
|
+
minimumGapPx: stepPx * 0.42
|
|
1735
1960
|
};
|
|
1736
1961
|
}
|
|
1737
1962
|
const minMetric = Math.min(...presentMetrics);
|
|
@@ -1744,10 +1969,11 @@
|
|
|
1744
1969
|
metricSpread,
|
|
1745
1970
|
innerPx,
|
|
1746
1971
|
stepPx,
|
|
1747
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
1972
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
1973
|
+
minimumGapPx: stepPx * 0.42
|
|
1748
1974
|
};
|
|
1749
1975
|
}
|
|
1750
|
-
function resolveOrbitGeometry(object, index, count, parent, metricContext, context) {
|
|
1976
|
+
function resolveOrbitGeometry(object, index, count, parent, metricContext, orbitRadiusPx, context) {
|
|
1751
1977
|
const placement = object.placement;
|
|
1752
1978
|
const band = object.type === "belt" || object.type === "ring";
|
|
1753
1979
|
if (!placement || placement.mode !== "orbit") {
|
|
@@ -1765,7 +1991,7 @@
|
|
|
1765
1991
|
};
|
|
1766
1992
|
}
|
|
1767
1993
|
const eccentricity = clampNumber(typeof placement.eccentricity === "number" ? placement.eccentricity : 0, 0, 0.92);
|
|
1768
|
-
const semiMajor =
|
|
1994
|
+
const semiMajor = orbitRadiusPx;
|
|
1769
1995
|
const baseMinor = Math.max(semiMajor * Math.sqrt(1 - eccentricity * eccentricity), semiMajor * 0.18);
|
|
1770
1996
|
const inclinationDeg = unitValueToDegrees(placement.inclination) ?? 0;
|
|
1771
1997
|
const inclinationScale = context.projection === "isometric" ? Math.max(MIN_ISO_MINOR_SCALE, Math.cos(degreesToRadians(inclinationDeg))) * ISO_FLATTENING : 1;
|
|
@@ -1795,15 +2021,19 @@
|
|
|
1795
2021
|
objectY: objectPoint.y
|
|
1796
2022
|
};
|
|
1797
2023
|
}
|
|
1798
|
-
function resolveOrbitRadiusPx(
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
2024
|
+
function resolveOrbitRadiusPx(metric, metricContext) {
|
|
2025
|
+
return metricContext.innerPx + metricContext.stepPx * log2(Math.max(metric, 0) + 1);
|
|
2026
|
+
}
|
|
2027
|
+
function resolveOrbitRadiiPx(objects, metricContext) {
|
|
2028
|
+
const radii = [];
|
|
2029
|
+
objects.forEach((object, index) => {
|
|
2030
|
+
const metric = orbitMetric(object);
|
|
2031
|
+
const fallbackRadius = metricContext.innerPx + index * metricContext.stepPx;
|
|
2032
|
+
const baseRadius = metric === null ? fallbackRadius : resolveOrbitRadiusPx(metric, metricContext);
|
|
2033
|
+
const minimumRadius = index === 0 ? metricContext.innerPx : (radii[index - 1] ?? metricContext.innerPx) + metricContext.minimumGapPx;
|
|
2034
|
+
radii.push(Math.max(baseRadius, minimumRadius));
|
|
2035
|
+
});
|
|
2036
|
+
return radii;
|
|
1807
2037
|
}
|
|
1808
2038
|
function orbitMetric(object) {
|
|
1809
2039
|
if (!object.placement || object.placement.mode !== "orbit") {
|
|
@@ -1811,6 +2041,9 @@
|
|
|
1811
2041
|
}
|
|
1812
2042
|
return toDistanceMetric(object.placement.semiMajor ?? object.placement.distance ?? null);
|
|
1813
2043
|
}
|
|
2044
|
+
function log2(value) {
|
|
2045
|
+
return Math.log(value) / Math.log(2);
|
|
2046
|
+
}
|
|
1814
2047
|
function resolveOrbitPhase(phase, index, count) {
|
|
1815
2048
|
const degreeValue = phase ? unitValueToDegrees(phase) : null;
|
|
1816
2049
|
if (degreeValue !== null) {
|
|
@@ -2014,7 +2247,7 @@
|
|
|
2014
2247
|
return null;
|
|
2015
2248
|
}
|
|
2016
2249
|
}
|
|
2017
|
-
function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels) {
|
|
2250
|
+
function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier) {
|
|
2018
2251
|
let minX = Number.POSITIVE_INFINITY;
|
|
2019
2252
|
let minY = Number.POSITIVE_INFINITY;
|
|
2020
2253
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
@@ -2043,7 +2276,7 @@
|
|
|
2043
2276
|
}
|
|
2044
2277
|
for (const label of labels) {
|
|
2045
2278
|
if (!label.hidden && group.labelIds.includes(label.objectId)) {
|
|
2046
|
-
includeLabelBounds(label, include);
|
|
2279
|
+
includeLabelBounds(label, include, labelMultiplier);
|
|
2047
2280
|
}
|
|
2048
2281
|
}
|
|
2049
2282
|
if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
|
|
@@ -2068,12 +2301,28 @@
|
|
|
2068
2301
|
}
|
|
2069
2302
|
return current.id;
|
|
2070
2303
|
}
|
|
2071
|
-
function createLabelRect(
|
|
2304
|
+
function createLabelRect(object, placement, labelMultiplier) {
|
|
2305
|
+
return createLabelRectFromText(placement.x, placement.labelY, placement.secondaryY, placement.textAnchor, placement.direction, object.label, object.secondaryLabel, labelMultiplier);
|
|
2306
|
+
}
|
|
2307
|
+
function createLabelRectFromText(x, labelY, secondaryY, textAnchor, direction, label, secondaryLabel, labelMultiplier) {
|
|
2308
|
+
const labelHalfWidth = estimateLabelHalfWidthFromText(label, secondaryLabel, labelMultiplier);
|
|
2309
|
+
const labelWidth = labelHalfWidth * 2;
|
|
2310
|
+
const topPadding = direction === "above" ? 18 : 12;
|
|
2311
|
+
const bottomPadding = direction === "above" ? 8 : 12;
|
|
2312
|
+
let left = x - labelHalfWidth;
|
|
2313
|
+
let right = x + labelHalfWidth;
|
|
2314
|
+
if (textAnchor === "start") {
|
|
2315
|
+
left = x;
|
|
2316
|
+
right = x + labelWidth;
|
|
2317
|
+
} else if (textAnchor === "end") {
|
|
2318
|
+
left = x - labelWidth;
|
|
2319
|
+
right = x;
|
|
2320
|
+
}
|
|
2072
2321
|
return {
|
|
2073
|
-
left
|
|
2074
|
-
right
|
|
2075
|
-
top: Math.min(labelY, secondaryY) -
|
|
2076
|
-
bottom: Math.max(labelY, secondaryY) +
|
|
2322
|
+
left,
|
|
2323
|
+
right,
|
|
2324
|
+
top: Math.min(labelY, secondaryY) - topPadding,
|
|
2325
|
+
bottom: Math.max(labelY, secondaryY) + bottomPadding
|
|
2077
2326
|
};
|
|
2078
2327
|
}
|
|
2079
2328
|
function rectsOverlap(left, right) {
|
|
@@ -2134,8 +2383,18 @@
|
|
|
2134
2383
|
return value.value;
|
|
2135
2384
|
case "km":
|
|
2136
2385
|
return value.value / AU_IN_KM;
|
|
2386
|
+
case "m":
|
|
2387
|
+
return value.value / 1e3 / AU_IN_KM;
|
|
2388
|
+
case "ly":
|
|
2389
|
+
return value.value * LY_IN_AU;
|
|
2390
|
+
case "pc":
|
|
2391
|
+
return value.value * PC_IN_AU;
|
|
2392
|
+
case "kpc":
|
|
2393
|
+
return value.value * KPC_IN_AU;
|
|
2137
2394
|
case "re":
|
|
2138
2395
|
return value.value * EARTH_RADIUS_IN_KM / AU_IN_KM;
|
|
2396
|
+
case "rj":
|
|
2397
|
+
return value.value * JUPITER_RADIUS_IN_KM / AU_IN_KM;
|
|
2139
2398
|
case "sol":
|
|
2140
2399
|
return value.value * SOLAR_RADIUS_IN_KM / AU_IN_KM;
|
|
2141
2400
|
default:
|
|
@@ -2250,11 +2509,6 @@
|
|
|
2250
2509
|
function customColorFor(value) {
|
|
2251
2510
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
2252
2511
|
}
|
|
2253
|
-
function estimateLabelHalfWidth(object, labelMultiplier) {
|
|
2254
|
-
const primaryWidth = object.label.length * 4.6 * labelMultiplier + 18;
|
|
2255
|
-
const secondaryWidth = object.secondaryLabel.length * 3.9 * labelMultiplier + 18;
|
|
2256
|
-
return Math.max(primaryWidth, secondaryWidth, object.visualRadius + 18);
|
|
2257
|
-
}
|
|
2258
2512
|
function estimateLabelHalfWidthFromText(label, secondaryLabel, labelMultiplier) {
|
|
2259
2513
|
const primaryWidth = label.length * 4.6 * labelMultiplier + 18;
|
|
2260
2514
|
const secondaryWidth = secondaryLabel.length * 3.9 * labelMultiplier + 18;
|
|
@@ -2271,28 +2525,95 @@
|
|
|
2271
2525
|
}
|
|
2272
2526
|
|
|
2273
2527
|
// packages/core/dist/draft.js
|
|
2274
|
-
function materializeAtlasDocument(document2) {
|
|
2528
|
+
function materializeAtlasDocument(document2, options = {}) {
|
|
2275
2529
|
const system = document2.system ? {
|
|
2276
2530
|
type: "system",
|
|
2277
2531
|
id: document2.system.id,
|
|
2532
|
+
title: document2.system.title,
|
|
2533
|
+
description: document2.system.description,
|
|
2534
|
+
epoch: document2.system.epoch,
|
|
2535
|
+
referencePlane: document2.system.referencePlane,
|
|
2278
2536
|
properties: materializeDraftSystemProperties(document2.system),
|
|
2279
2537
|
info: materializeDraftSystemInfo(document2.system)
|
|
2280
2538
|
} : null;
|
|
2539
|
+
const objects = document2.objects.map(cloneWorldOrbitObject);
|
|
2540
|
+
applyEventPoseOverrides(objects, document2.events ?? [], options.activeEventId ?? null);
|
|
2281
2541
|
return {
|
|
2282
2542
|
format: "worldorbit",
|
|
2283
2543
|
version: "1.0",
|
|
2544
|
+
schemaVersion: document2.version,
|
|
2284
2545
|
system,
|
|
2285
|
-
|
|
2546
|
+
groups: structuredClone(document2.groups ?? []),
|
|
2547
|
+
relations: structuredClone(document2.relations ?? []),
|
|
2548
|
+
events: document2.events.map(cloneWorldOrbitEvent),
|
|
2549
|
+
objects
|
|
2286
2550
|
};
|
|
2287
2551
|
}
|
|
2288
2552
|
function cloneWorldOrbitObject(object) {
|
|
2289
2553
|
return {
|
|
2290
2554
|
...object,
|
|
2555
|
+
groups: object.groups ? [...object.groups] : void 0,
|
|
2556
|
+
resonance: object.resonance ? { ...object.resonance } : object.resonance,
|
|
2557
|
+
renderHints: object.renderHints ? { ...object.renderHints } : object.renderHints,
|
|
2558
|
+
deriveRules: object.deriveRules ? object.deriveRules.map((rule) => ({ ...rule })) : void 0,
|
|
2559
|
+
validationRules: object.validationRules ? object.validationRules.map((rule) => ({ ...rule })) : void 0,
|
|
2560
|
+
lockedFields: object.lockedFields ? [...object.lockedFields] : void 0,
|
|
2561
|
+
tolerances: object.tolerances ? object.tolerances.map((entry) => ({
|
|
2562
|
+
field: entry.field,
|
|
2563
|
+
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
|
|
2564
|
+
})) : void 0,
|
|
2565
|
+
typedBlocks: object.typedBlocks ? Object.fromEntries(Object.entries(object.typedBlocks).map(([key, block]) => [key, { ...block ?? {} }])) : void 0,
|
|
2291
2566
|
properties: cloneProperties(object.properties),
|
|
2292
2567
|
placement: object.placement ? structuredClone(object.placement) : null,
|
|
2293
2568
|
info: { ...object.info }
|
|
2294
2569
|
};
|
|
2295
2570
|
}
|
|
2571
|
+
function cloneWorldOrbitEvent(event) {
|
|
2572
|
+
return {
|
|
2573
|
+
...event,
|
|
2574
|
+
participantObjectIds: [...event.participantObjectIds],
|
|
2575
|
+
tags: [...event.tags],
|
|
2576
|
+
positions: event.positions.map(cloneWorldOrbitEventPose)
|
|
2577
|
+
};
|
|
2578
|
+
}
|
|
2579
|
+
function cloneWorldOrbitEventPose(pose) {
|
|
2580
|
+
return {
|
|
2581
|
+
objectId: pose.objectId,
|
|
2582
|
+
placement: clonePlacement(pose.placement),
|
|
2583
|
+
inner: pose.inner ? { ...pose.inner } : void 0,
|
|
2584
|
+
outer: pose.outer ? { ...pose.outer } : void 0
|
|
2585
|
+
};
|
|
2586
|
+
}
|
|
2587
|
+
function clonePlacement(placement) {
|
|
2588
|
+
return placement ? structuredClone(placement) : null;
|
|
2589
|
+
}
|
|
2590
|
+
function applyEventPoseOverrides(objects, events, activeEventId) {
|
|
2591
|
+
if (!activeEventId) {
|
|
2592
|
+
return;
|
|
2593
|
+
}
|
|
2594
|
+
const event = events.find((entry) => entry.id === activeEventId);
|
|
2595
|
+
if (!event) {
|
|
2596
|
+
return;
|
|
2597
|
+
}
|
|
2598
|
+
const objectMap = new Map(objects.map((object) => [object.id, object]));
|
|
2599
|
+
for (const pose of event.positions) {
|
|
2600
|
+
const object = objectMap.get(pose.objectId);
|
|
2601
|
+
if (!object) {
|
|
2602
|
+
continue;
|
|
2603
|
+
}
|
|
2604
|
+
object.placement = clonePlacement(pose.placement);
|
|
2605
|
+
if (pose.inner) {
|
|
2606
|
+
object.properties.inner = { ...pose.inner };
|
|
2607
|
+
} else {
|
|
2608
|
+
delete object.properties.inner;
|
|
2609
|
+
}
|
|
2610
|
+
if (pose.outer) {
|
|
2611
|
+
object.properties.outer = { ...pose.outer };
|
|
2612
|
+
} else {
|
|
2613
|
+
delete object.properties.outer;
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2296
2617
|
function cloneProperties(properties) {
|
|
2297
2618
|
const next = {};
|
|
2298
2619
|
for (const [key, value] of Object.entries(properties)) {
|
|
@@ -2323,71 +2644,83 @@
|
|
|
2323
2644
|
if (system.defaults.units) {
|
|
2324
2645
|
properties.units = system.defaults.units;
|
|
2325
2646
|
}
|
|
2647
|
+
if (system.description) {
|
|
2648
|
+
properties.description = system.description;
|
|
2649
|
+
}
|
|
2650
|
+
if (system.epoch) {
|
|
2651
|
+
properties.epoch = system.epoch;
|
|
2652
|
+
}
|
|
2653
|
+
if (system.referencePlane) {
|
|
2654
|
+
properties.referencePlane = system.referencePlane;
|
|
2655
|
+
}
|
|
2326
2656
|
return properties;
|
|
2327
2657
|
}
|
|
2328
2658
|
function materializeDraftSystemInfo(system) {
|
|
2329
|
-
const
|
|
2659
|
+
const info2 = {
|
|
2330
2660
|
...system.atlasMetadata
|
|
2331
2661
|
};
|
|
2332
2662
|
if (system.defaults.theme) {
|
|
2333
|
-
|
|
2663
|
+
info2["atlas.theme"] = system.defaults.theme;
|
|
2334
2664
|
}
|
|
2335
2665
|
for (const viewpoint of system.viewpoints) {
|
|
2336
2666
|
const prefix = `viewpoint.${viewpoint.id}`;
|
|
2337
|
-
|
|
2667
|
+
info2[`${prefix}.label`] = viewpoint.label;
|
|
2338
2668
|
if (viewpoint.summary) {
|
|
2339
|
-
|
|
2669
|
+
info2[`${prefix}.summary`] = viewpoint.summary;
|
|
2340
2670
|
}
|
|
2341
2671
|
if (viewpoint.focusObjectId) {
|
|
2342
|
-
|
|
2672
|
+
info2[`${prefix}.focus`] = viewpoint.focusObjectId;
|
|
2343
2673
|
}
|
|
2344
2674
|
if (viewpoint.selectedObjectId) {
|
|
2345
|
-
|
|
2675
|
+
info2[`${prefix}.select`] = viewpoint.selectedObjectId;
|
|
2346
2676
|
}
|
|
2347
2677
|
if (viewpoint.projection) {
|
|
2348
|
-
|
|
2678
|
+
info2[`${prefix}.projection`] = viewpoint.projection;
|
|
2349
2679
|
}
|
|
2350
2680
|
if (viewpoint.preset) {
|
|
2351
|
-
|
|
2681
|
+
info2[`${prefix}.preset`] = viewpoint.preset;
|
|
2352
2682
|
}
|
|
2353
2683
|
if (viewpoint.zoom !== null) {
|
|
2354
|
-
|
|
2684
|
+
info2[`${prefix}.zoom`] = String(viewpoint.zoom);
|
|
2355
2685
|
}
|
|
2356
2686
|
if (viewpoint.rotationDeg !== 0) {
|
|
2357
|
-
|
|
2687
|
+
info2[`${prefix}.rotation`] = String(viewpoint.rotationDeg);
|
|
2358
2688
|
}
|
|
2359
2689
|
const serializedLayers = serializeViewpointLayers(viewpoint.layers);
|
|
2360
2690
|
if (serializedLayers) {
|
|
2361
|
-
|
|
2691
|
+
info2[`${prefix}.layers`] = serializedLayers;
|
|
2362
2692
|
}
|
|
2363
2693
|
if (viewpoint.filter?.query) {
|
|
2364
|
-
|
|
2694
|
+
info2[`${prefix}.query`] = viewpoint.filter.query;
|
|
2365
2695
|
}
|
|
2366
2696
|
if ((viewpoint.filter?.objectTypes.length ?? 0) > 0) {
|
|
2367
|
-
|
|
2697
|
+
info2[`${prefix}.types`] = viewpoint.filter?.objectTypes.join(" ") ?? "";
|
|
2368
2698
|
}
|
|
2369
2699
|
if ((viewpoint.filter?.tags.length ?? 0) > 0) {
|
|
2370
|
-
|
|
2700
|
+
info2[`${prefix}.tags`] = viewpoint.filter?.tags.join(" ") ?? "";
|
|
2371
2701
|
}
|
|
2372
2702
|
if ((viewpoint.filter?.groupIds.length ?? 0) > 0) {
|
|
2373
|
-
|
|
2703
|
+
info2[`${prefix}.groups`] = viewpoint.filter?.groupIds.join(" ") ?? "";
|
|
2704
|
+
}
|
|
2705
|
+
if (viewpoint.events.length > 0) {
|
|
2706
|
+
info2[`${prefix}.events`] = viewpoint.events.join(" ");
|
|
2374
2707
|
}
|
|
2375
2708
|
}
|
|
2376
2709
|
for (const annotation of system.annotations) {
|
|
2377
2710
|
const prefix = `annotation.${annotation.id}`;
|
|
2378
|
-
|
|
2711
|
+
info2[`${prefix}.label`] = annotation.label;
|
|
2379
2712
|
if (annotation.targetObjectId) {
|
|
2380
|
-
|
|
2713
|
+
info2[`${prefix}.target`] = annotation.targetObjectId;
|
|
2381
2714
|
}
|
|
2382
|
-
|
|
2715
|
+
info2[`${prefix}.body`] = annotation.body;
|
|
2383
2716
|
if (annotation.tags.length > 0) {
|
|
2384
|
-
|
|
2717
|
+
info2[`${prefix}.tags`] = annotation.tags.join(" ");
|
|
2385
2718
|
}
|
|
2386
2719
|
if (annotation.sourceObjectId) {
|
|
2387
|
-
|
|
2720
|
+
info2[`${prefix}.source`] = annotation.sourceObjectId;
|
|
2388
2721
|
}
|
|
2389
2722
|
}
|
|
2390
|
-
return
|
|
2723
|
+
return info2;
|
|
2391
2724
|
}
|
|
2392
2725
|
function serializeViewpointLayers(layers) {
|
|
2393
2726
|
const tokens = [];
|
|
@@ -2396,7 +2729,7 @@
|
|
|
2396
2729
|
if (orbitFront !== void 0 || orbitBack !== void 0) {
|
|
2397
2730
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
2398
2731
|
}
|
|
2399
|
-
for (const key of ["background", "guides", "objects", "labels", "metadata"]) {
|
|
2732
|
+
for (const key of ["background", "guides", "relations", "events", "objects", "labels", "metadata"]) {
|
|
2400
2733
|
if (layers[key] !== void 0) {
|
|
2401
2734
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
2402
2735
|
}
|
|
@@ -2404,171 +2737,838 @@
|
|
|
2404
2737
|
return tokens.join(" ");
|
|
2405
2738
|
}
|
|
2406
2739
|
|
|
2407
|
-
// packages/core/dist/
|
|
2408
|
-
|
|
2409
|
-
|
|
2740
|
+
// packages/core/dist/atlas-utils.js
|
|
2741
|
+
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)?$/;
|
|
2742
|
+
var BOOLEAN_VALUES2 = /* @__PURE__ */ new Map([
|
|
2743
|
+
["true", true],
|
|
2744
|
+
["false", false],
|
|
2745
|
+
["yes", true],
|
|
2746
|
+
["no", false]
|
|
2747
|
+
]);
|
|
2748
|
+
var URL_SCHEME_PATTERN2 = /^[A-Za-z][A-Za-z0-9+.-]*:/;
|
|
2749
|
+
function normalizeIdentifier(value) {
|
|
2750
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2410
2751
|
}
|
|
2411
|
-
function
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
let sawDefaults = false;
|
|
2419
|
-
let sawAtlas = false;
|
|
2420
|
-
const viewpointIds = /* @__PURE__ */ new Set();
|
|
2421
|
-
const annotationIds = /* @__PURE__ */ new Set();
|
|
2422
|
-
for (let index = 0; index < lines.length; index++) {
|
|
2423
|
-
const rawLine = lines[index];
|
|
2424
|
-
const lineNumber = index + 1;
|
|
2425
|
-
if (!rawLine.trim()) {
|
|
2426
|
-
continue;
|
|
2427
|
-
}
|
|
2428
|
-
const indent = getIndent(rawLine);
|
|
2429
|
-
const tokens = tokenizeLineDetailed(rawLine.slice(indent), {
|
|
2430
|
-
line: lineNumber,
|
|
2431
|
-
columnOffset: indent
|
|
2432
|
-
});
|
|
2433
|
-
if (tokens.length === 0) {
|
|
2434
|
-
continue;
|
|
2435
|
-
}
|
|
2436
|
-
if (!sawSchemaHeader) {
|
|
2437
|
-
schemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
2438
|
-
sawSchemaHeader = true;
|
|
2439
|
-
continue;
|
|
2440
|
-
}
|
|
2441
|
-
if (indent === 0) {
|
|
2442
|
-
section = startTopLevelSection(tokens, lineNumber, system, objectNodes, viewpointIds, annotationIds, {
|
|
2443
|
-
sawDefaults,
|
|
2444
|
-
sawAtlas
|
|
2445
|
-
});
|
|
2446
|
-
if (section.kind === "system") {
|
|
2447
|
-
system = section.system;
|
|
2448
|
-
} else if (section.kind === "defaults") {
|
|
2449
|
-
sawDefaults = true;
|
|
2450
|
-
} else if (section.kind === "atlas") {
|
|
2451
|
-
sawAtlas = true;
|
|
2452
|
-
}
|
|
2453
|
-
continue;
|
|
2454
|
-
}
|
|
2455
|
-
if (!section) {
|
|
2456
|
-
throw new WorldOrbitError("Indented line without parent atlas section", lineNumber, indent + 1);
|
|
2457
|
-
}
|
|
2458
|
-
handleSectionLine(section, indent, tokens, lineNumber);
|
|
2459
|
-
}
|
|
2460
|
-
if (!sawSchemaHeader) {
|
|
2461
|
-
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
|
|
2752
|
+
function humanizeIdentifier2(value) {
|
|
2753
|
+
return value.split(/[-_]+/).filter(Boolean).map((segment) => segment[0].toUpperCase() + segment.slice(1)).join(" ");
|
|
2754
|
+
}
|
|
2755
|
+
function parseAtlasUnitValue(input, location, fieldKey) {
|
|
2756
|
+
const match = input.match(UNIT_PATTERN2);
|
|
2757
|
+
if (!match) {
|
|
2758
|
+
throw WorldOrbitError.fromLocation(`Invalid unit value "${input}"`, location);
|
|
2462
2759
|
}
|
|
2463
|
-
const
|
|
2464
|
-
|
|
2465
|
-
|
|
2760
|
+
const unitValue = {
|
|
2761
|
+
value: Number(match[1]),
|
|
2762
|
+
unit: match[2] ?? null
|
|
2466
2763
|
};
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
system: null,
|
|
2472
|
-
objects: normalizedObjects
|
|
2473
|
-
});
|
|
2474
|
-
const diagnostics = schemaVersion === "2.0-draft" && outputVersion === "2.0" ? [
|
|
2475
|
-
{
|
|
2476
|
-
code: "load.schema.deprecatedDraft",
|
|
2477
|
-
severity: "warning",
|
|
2478
|
-
source: "upgrade",
|
|
2479
|
-
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
2764
|
+
if (fieldKey) {
|
|
2765
|
+
const schema = getFieldSchema(fieldKey);
|
|
2766
|
+
if (schema?.unitFamily && !unitFamilyAllowsUnit(schema.unitFamily, unitValue.unit)) {
|
|
2767
|
+
throw WorldOrbitError.fromLocation(`Unit "${unitValue.unit ?? "none"}" is not valid for "${fieldKey}"`, location);
|
|
2480
2768
|
}
|
|
2481
|
-
|
|
2769
|
+
}
|
|
2770
|
+
return unitValue;
|
|
2771
|
+
}
|
|
2772
|
+
function tryParseAtlasUnitValue(input) {
|
|
2773
|
+
const match = input.match(UNIT_PATTERN2);
|
|
2774
|
+
if (!match) {
|
|
2775
|
+
return null;
|
|
2776
|
+
}
|
|
2482
2777
|
return {
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
sourceVersion: "1.0",
|
|
2486
|
-
system,
|
|
2487
|
-
objects: normalizedObjects,
|
|
2488
|
-
diagnostics
|
|
2778
|
+
value: Number(match[1]),
|
|
2779
|
+
unit: match[2] ?? null
|
|
2489
2780
|
};
|
|
2490
2781
|
}
|
|
2491
|
-
function
|
|
2492
|
-
|
|
2493
|
-
|
|
2782
|
+
function parseAtlasNumber(input, key, location) {
|
|
2783
|
+
const value = Number(input);
|
|
2784
|
+
if (!Number.isFinite(value)) {
|
|
2785
|
+
throw WorldOrbitError.fromLocation(`Invalid numeric value "${input}" for "${key}"`, location);
|
|
2494
2786
|
}
|
|
2495
|
-
return
|
|
2787
|
+
return value;
|
|
2496
2788
|
}
|
|
2497
|
-
function
|
|
2498
|
-
const
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
if (system) {
|
|
2502
|
-
throw new WorldOrbitError('Atlas section "system" may only appear once', line, tokens[0].column);
|
|
2503
|
-
}
|
|
2504
|
-
return startSystemSection(tokens, line);
|
|
2505
|
-
case "defaults":
|
|
2506
|
-
if (!system) {
|
|
2507
|
-
throw new WorldOrbitError('Atlas section "defaults" requires a preceding system declaration', line, tokens[0].column);
|
|
2508
|
-
}
|
|
2509
|
-
if (flags.sawDefaults) {
|
|
2510
|
-
throw new WorldOrbitError('Atlas section "defaults" may only appear once', line, tokens[0].column);
|
|
2511
|
-
}
|
|
2512
|
-
return {
|
|
2513
|
-
kind: "defaults",
|
|
2514
|
-
system,
|
|
2515
|
-
seenFields: /* @__PURE__ */ new Set()
|
|
2516
|
-
};
|
|
2517
|
-
case "atlas":
|
|
2518
|
-
if (!system) {
|
|
2519
|
-
throw new WorldOrbitError('Atlas section "atlas" requires a preceding system declaration', line, tokens[0].column);
|
|
2520
|
-
}
|
|
2521
|
-
if (flags.sawAtlas) {
|
|
2522
|
-
throw new WorldOrbitError('Atlas section "atlas" may only appear once', line, tokens[0].column);
|
|
2523
|
-
}
|
|
2524
|
-
return {
|
|
2525
|
-
kind: "atlas",
|
|
2526
|
-
system,
|
|
2527
|
-
inMetadata: false,
|
|
2528
|
-
metadataIndent: null
|
|
2529
|
-
};
|
|
2530
|
-
case "viewpoint":
|
|
2531
|
-
if (!system) {
|
|
2532
|
-
throw new WorldOrbitError('Atlas section "viewpoint" requires a preceding system declaration', line, tokens[0].column);
|
|
2533
|
-
}
|
|
2534
|
-
return startViewpointSection(tokens, line, system, viewpointIds);
|
|
2535
|
-
case "annotation":
|
|
2536
|
-
if (!system) {
|
|
2537
|
-
throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
|
|
2538
|
-
}
|
|
2539
|
-
return startAnnotationSection(tokens, line, system, annotationIds);
|
|
2540
|
-
case "object":
|
|
2541
|
-
return startObjectSection(tokens, line, objectNodes);
|
|
2542
|
-
default:
|
|
2543
|
-
throw new WorldOrbitError(`Unknown atlas section "${tokens[0]?.value ?? ""}"`, line, tokens[0]?.column ?? 1);
|
|
2789
|
+
function parseAtlasBoolean(input, key, location) {
|
|
2790
|
+
const parsed = BOOLEAN_VALUES2.get(input.toLowerCase());
|
|
2791
|
+
if (parsed === void 0) {
|
|
2792
|
+
throw WorldOrbitError.fromLocation(`Invalid boolean value "${input}" for "${key}"`, location);
|
|
2544
2793
|
}
|
|
2794
|
+
return parsed;
|
|
2545
2795
|
}
|
|
2546
|
-
function
|
|
2547
|
-
if (
|
|
2548
|
-
throw
|
|
2796
|
+
function parseAtlasAtReference(target, location) {
|
|
2797
|
+
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
2798
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
2549
2799
|
}
|
|
2550
|
-
const
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2800
|
+
const pairedMatch = target.match(/^([A-Za-z0-9._-]+)-([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
2801
|
+
if (pairedMatch) {
|
|
2802
|
+
return {
|
|
2803
|
+
kind: "lagrange",
|
|
2804
|
+
primary: pairedMatch[1],
|
|
2805
|
+
secondary: pairedMatch[2],
|
|
2806
|
+
point: pairedMatch[3]
|
|
2807
|
+
};
|
|
2808
|
+
}
|
|
2809
|
+
const simpleMatch = target.match(/^([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
2810
|
+
if (simpleMatch) {
|
|
2811
|
+
return {
|
|
2812
|
+
kind: "lagrange",
|
|
2813
|
+
primary: simpleMatch[1],
|
|
2814
|
+
secondary: null,
|
|
2815
|
+
point: simpleMatch[2]
|
|
2816
|
+
};
|
|
2817
|
+
}
|
|
2818
|
+
if (/^[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
2819
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
2820
|
+
}
|
|
2821
|
+
const anchorMatch = target.match(/^([A-Za-z0-9._-]+):([A-Za-z0-9._-]+)$/);
|
|
2822
|
+
if (anchorMatch) {
|
|
2823
|
+
return {
|
|
2824
|
+
kind: "anchor",
|
|
2825
|
+
objectId: anchorMatch[1],
|
|
2826
|
+
anchor: anchorMatch[2]
|
|
2827
|
+
};
|
|
2828
|
+
}
|
|
2829
|
+
return {
|
|
2830
|
+
kind: "named",
|
|
2831
|
+
name: target
|
|
2832
|
+
};
|
|
2833
|
+
}
|
|
2834
|
+
function validateAtlasImageSource(value, location) {
|
|
2835
|
+
if (!value) {
|
|
2836
|
+
throw WorldOrbitError.fromLocation('Field "image" must not be empty', location);
|
|
2837
|
+
}
|
|
2838
|
+
if (value.startsWith("//")) {
|
|
2839
|
+
throw WorldOrbitError.fromLocation('Field "image" must use a relative path, root-relative path, or an http/https URL', location);
|
|
2840
|
+
}
|
|
2841
|
+
const schemeMatch = value.match(URL_SCHEME_PATTERN2);
|
|
2842
|
+
if (!schemeMatch) {
|
|
2843
|
+
return;
|
|
2844
|
+
}
|
|
2845
|
+
const scheme = schemeMatch[0].slice(0, -1).toLowerCase();
|
|
2846
|
+
if (scheme !== "http" && scheme !== "https") {
|
|
2847
|
+
throw WorldOrbitError.fromLocation(`Field "image" does not support the "${scheme}" scheme`, location);
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
function normalizeLegacyScalarValue(key, values, location) {
|
|
2851
|
+
const schema = getFieldSchema(key);
|
|
2852
|
+
if (!schema) {
|
|
2853
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
2854
|
+
}
|
|
2855
|
+
if (schema.arity === "single" && values.length !== 1) {
|
|
2856
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
2857
|
+
}
|
|
2858
|
+
switch (schema.kind) {
|
|
2859
|
+
case "list":
|
|
2860
|
+
return values;
|
|
2861
|
+
case "boolean":
|
|
2862
|
+
return parseAtlasBoolean(singleAtlasValue(values, key, location), key, location);
|
|
2863
|
+
case "number":
|
|
2864
|
+
return parseAtlasNumber(singleAtlasValue(values, key, location), key, location);
|
|
2865
|
+
case "unit":
|
|
2866
|
+
return parseAtlasUnitValue(singleAtlasValue(values, key, location), location, key);
|
|
2867
|
+
case "string": {
|
|
2868
|
+
const value = values.join(" ").trim();
|
|
2869
|
+
if (key === "image") {
|
|
2870
|
+
validateAtlasImageSource(value, location);
|
|
2871
|
+
}
|
|
2872
|
+
return value;
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
function ensureAtlasFieldSupported(key, objectType, location) {
|
|
2877
|
+
const schema = getFieldSchema(key);
|
|
2878
|
+
if (!schema) {
|
|
2879
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
2880
|
+
}
|
|
2881
|
+
if (!schema.objectTypes.includes(objectType)) {
|
|
2882
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" is not valid on "${objectType}"`, location);
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
function singleAtlasValue(values, key, location) {
|
|
2886
|
+
if (values.length !== 1) {
|
|
2887
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
2888
|
+
}
|
|
2889
|
+
return values[0];
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
// packages/core/dist/atlas-validate.js
|
|
2893
|
+
var SURFACE_TARGET_TYPES2 = /* @__PURE__ */ new Set(["star", "planet", "moon", "asteroid", "comet"]);
|
|
2894
|
+
var EARTH_MASSES_PER_SOLAR = 332946.0487;
|
|
2895
|
+
var JUPITER_MASSES_PER_SOLAR = 1047.3486;
|
|
2896
|
+
var AU_IN_KM2 = 1495978707e-1;
|
|
2897
|
+
var EARTH_RADIUS_IN_KM2 = 6371;
|
|
2898
|
+
var SOLAR_RADIUS_IN_KM2 = 695700;
|
|
2899
|
+
var LY_IN_AU2 = 63241.077;
|
|
2900
|
+
var PC_IN_AU2 = 206264.806;
|
|
2901
|
+
var KPC_IN_AU2 = 206264806;
|
|
2902
|
+
function collectAtlasDiagnostics(document2, sourceSchemaVersion) {
|
|
2903
|
+
const diagnostics = [];
|
|
2904
|
+
const objectMap = new Map(document2.objects.map((object) => [object.id, object]));
|
|
2905
|
+
const groupIds = new Set(document2.groups.map((group) => group.id));
|
|
2906
|
+
const eventIds = new Set(document2.events.map((event) => event.id));
|
|
2907
|
+
if (!document2.system) {
|
|
2908
|
+
diagnostics.push(error("validate.system.required", "Atlas documents must declare exactly one system."));
|
|
2909
|
+
}
|
|
2910
|
+
const knownIds = /* @__PURE__ */ new Map();
|
|
2911
|
+
for (const [kind, ids] of [
|
|
2912
|
+
["group", document2.groups.map((group) => group.id)],
|
|
2913
|
+
["viewpoint", document2.system?.viewpoints.map((viewpoint) => viewpoint.id) ?? []],
|
|
2914
|
+
["annotation", document2.system?.annotations.map((annotation) => annotation.id) ?? []],
|
|
2915
|
+
["relation", document2.relations.map((relation) => relation.id)],
|
|
2916
|
+
["event", document2.events.map((event) => event.id)],
|
|
2917
|
+
["object", document2.objects.map((object) => object.id)]
|
|
2918
|
+
]) {
|
|
2919
|
+
for (const id of ids) {
|
|
2920
|
+
const previous = knownIds.get(id);
|
|
2921
|
+
if (previous) {
|
|
2922
|
+
diagnostics.push(error("validate.id.duplicate", `Duplicate ${kind} id "${id}" already used by ${previous}.`));
|
|
2923
|
+
} else {
|
|
2924
|
+
knownIds.set(id, kind);
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
for (const relation of document2.relations) {
|
|
2929
|
+
validateRelation(relation, objectMap, diagnostics);
|
|
2930
|
+
}
|
|
2931
|
+
for (const viewpoint of document2.system?.viewpoints ?? []) {
|
|
2932
|
+
validateViewpoint(viewpoint.filter, viewpoint.events ?? [], groupIds, eventIds, sourceSchemaVersion, diagnostics, viewpoint.id);
|
|
2933
|
+
}
|
|
2934
|
+
for (const object of document2.objects) {
|
|
2935
|
+
validateObject(object, document2.system, objectMap, groupIds, diagnostics);
|
|
2936
|
+
}
|
|
2937
|
+
for (const event of document2.events) {
|
|
2938
|
+
validateEvent(event, objectMap, diagnostics);
|
|
2939
|
+
}
|
|
2940
|
+
return diagnostics;
|
|
2941
|
+
}
|
|
2942
|
+
function validateRelation(relation, objectMap, diagnostics) {
|
|
2943
|
+
if (!relation.from) {
|
|
2944
|
+
diagnostics.push(error("validate.relation.from.required", `Relation "${relation.id}" is missing a "from" target.`));
|
|
2945
|
+
} else if (!objectMap.has(relation.from)) {
|
|
2946
|
+
diagnostics.push(error("validate.relation.from.unknown", `Unknown relation source "${relation.from}" on "${relation.id}".`));
|
|
2947
|
+
}
|
|
2948
|
+
if (!relation.to) {
|
|
2949
|
+
diagnostics.push(error("validate.relation.to.required", `Relation "${relation.id}" is missing a "to" target.`));
|
|
2950
|
+
} else if (!objectMap.has(relation.to)) {
|
|
2951
|
+
diagnostics.push(error("validate.relation.to.unknown", `Unknown relation target "${relation.to}" on "${relation.id}".`));
|
|
2952
|
+
}
|
|
2953
|
+
if (!relation.kind) {
|
|
2954
|
+
diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
function validateViewpoint(filter, eventRefs, groupIds, eventIds, sourceSchemaVersion, diagnostics, viewpointId) {
|
|
2958
|
+
if (sourceSchemaVersion === "2.1") {
|
|
2959
|
+
if (filter) {
|
|
2960
|
+
for (const groupId of filter.groupIds) {
|
|
2961
|
+
if (!groupIds.has(groupId)) {
|
|
2962
|
+
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpointId}".`, void 0, `viewpoint.${viewpointId}.groups`));
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
for (const eventId of eventRefs) {
|
|
2967
|
+
if (!eventIds.has(eventId)) {
|
|
2968
|
+
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${viewpointId}".`, void 0, `viewpoint.${viewpointId}.events`));
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
2974
|
+
const placement = object.placement;
|
|
2975
|
+
const orbitPlacement = placement?.mode === "orbit" ? placement : null;
|
|
2976
|
+
const parentObject = placement?.mode === "orbit" ? objectMap.get(placement.target) ?? null : null;
|
|
2977
|
+
if (object.groups) {
|
|
2978
|
+
for (const groupId of object.groups) {
|
|
2979
|
+
if (!groupIds.has(groupId)) {
|
|
2980
|
+
diagnostics.push(warn("validate.group.unknown", `Unknown group "${groupId}" on "${object.id}".`, object.id, "groups"));
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
if (orbitPlacement) {
|
|
2985
|
+
if (!objectMap.has(orbitPlacement.target)) {
|
|
2986
|
+
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
2987
|
+
}
|
|
2988
|
+
if (orbitPlacement.distance && orbitPlacement.semiMajor) {
|
|
2989
|
+
diagnostics.push(error("validate.orbit.distanceConflict", `Object "${object.id}" cannot declare both "distance" and "semiMajor".`, object.id, "distance"));
|
|
2990
|
+
}
|
|
2991
|
+
if (orbitPlacement.phase && !object.epoch && !system?.epoch) {
|
|
2992
|
+
diagnostics.push(warn("validate.phase.epochMissing", `Object "${object.id}" sets "phase" without an object or system epoch.`, object.id, "phase"));
|
|
2993
|
+
}
|
|
2994
|
+
if (orbitPlacement.inclination && !object.referencePlane && !system?.referencePlane) {
|
|
2995
|
+
diagnostics.push(warn("validate.inclination.referencePlaneMissing", `Object "${object.id}" sets "inclination" without an object or system reference plane.`, object.id, "inclination"));
|
|
2996
|
+
}
|
|
2997
|
+
if (orbitPlacement.period && !massInSolar(parentObject?.properties.mass)) {
|
|
2998
|
+
diagnostics.push(warn("validate.period.massMissing", `Object "${object.id}" sets "period" but its central mass cannot be derived.`, object.id, "period"));
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
if (placement?.mode === "surface") {
|
|
3002
|
+
const target = objectMap.get(placement.target);
|
|
3003
|
+
if (!target) {
|
|
3004
|
+
diagnostics.push(error("validate.surface.target.unknown", `Unknown placement target "${placement.target}" on "${object.id}".`, object.id, "surface"));
|
|
3005
|
+
} else if (!SURFACE_TARGET_TYPES2.has(target.type)) {
|
|
3006
|
+
diagnostics.push(error("validate.surface.target.invalid", `Surface target "${placement.target}" on "${object.id}" is not surface-capable.`, object.id, "surface"));
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
if (placement?.mode === "at") {
|
|
3010
|
+
if (object.type !== "structure" && object.type !== "phenomenon") {
|
|
3011
|
+
diagnostics.push(error("validate.at.objectType", `Only structures and phenomena may use "at" placement; found "${object.type}" on "${object.id}".`, object.id, "at"));
|
|
3012
|
+
}
|
|
3013
|
+
if (!validateAtTarget(object, objectMap, diagnostics)) {
|
|
3014
|
+
diagnostics.push(error("validate.at.target.unknown", `Unknown at-reference target "${placement.target}" on "${object.id}".`, object.id, "at"));
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
if (object.resonance) {
|
|
3018
|
+
const target = objectMap.get(object.resonance.targetObjectId);
|
|
3019
|
+
if (!target) {
|
|
3020
|
+
diagnostics.push(error("validate.resonance.target.unknown", `Unknown resonance target "${object.resonance.targetObjectId}" on "${object.id}".`, object.id, "resonance"));
|
|
3021
|
+
} else if (object.placement?.mode !== "orbit" || target.placement?.mode !== "orbit" || object.placement.target !== target.placement.target) {
|
|
3022
|
+
diagnostics.push(warn("validate.resonance.orbitMismatch", `Resonance target "${object.resonance.targetObjectId}" on "${object.id}" does not share a compatible orbital parent.`, object.id, "resonance"));
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
for (const rule of object.deriveRules ?? []) {
|
|
3026
|
+
if (rule.field !== "period" || rule.strategy !== "kepler") {
|
|
3027
|
+
diagnostics.push(warn("validate.derive.unsupported", `Unsupported derive rule "${rule.field} ${rule.strategy}" on "${object.id}".`, object.id, "derive"));
|
|
3028
|
+
continue;
|
|
3029
|
+
}
|
|
3030
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
3031
|
+
if (derivedPeriodDays === null) {
|
|
3032
|
+
diagnostics.push(warn("validate.derive.inputsMissing", `Object "${object.id}" requests "derive period kepler" but lacks enough input data.`, object.id, "derive"));
|
|
3033
|
+
continue;
|
|
3034
|
+
}
|
|
3035
|
+
if (!orbitPlacement?.period) {
|
|
3036
|
+
diagnostics.push(info("validate.derive.period.available", `Object "${object.id}" can derive a Kepler period of ${formatDays(derivedPeriodDays)}.`, object.id, "derive"));
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
for (const rule of object.validationRules ?? []) {
|
|
3040
|
+
if (rule.rule !== "kepler") {
|
|
3041
|
+
diagnostics.push(warn("validate.rule.unsupported", `Unsupported validation rule "${rule.rule}" on "${object.id}".`, object.id, "validate"));
|
|
3042
|
+
continue;
|
|
3043
|
+
}
|
|
3044
|
+
const actualPeriodDays = durationInDays(orbitPlacement?.period);
|
|
3045
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
3046
|
+
if (actualPeriodDays === null || derivedPeriodDays === null) {
|
|
3047
|
+
continue;
|
|
3048
|
+
}
|
|
3049
|
+
const toleranceDays = toleranceForField(object, "period");
|
|
3050
|
+
if (Math.abs(actualPeriodDays - derivedPeriodDays) > toleranceDays) {
|
|
3051
|
+
diagnostics.push(error("validate.kepler.mismatch", `Object "${object.id}" fails Kepler validation for "period".`, object.id, "validate"));
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
function validateEvent(event, objectMap, diagnostics) {
|
|
3056
|
+
const fieldPrefix = `event.${event.id}`;
|
|
3057
|
+
const referencedIds = /* @__PURE__ */ new Set();
|
|
3058
|
+
if (!event.kind.trim()) {
|
|
3059
|
+
diagnostics.push(error("validate.event.kind.required", `Event "${event.id}" is missing a "kind" value.`, void 0, `${fieldPrefix}.kind`));
|
|
3060
|
+
}
|
|
3061
|
+
if (!event.targetObjectId && event.participantObjectIds.length === 0) {
|
|
3062
|
+
diagnostics.push(error("validate.event.references.required", `Event "${event.id}" must define a "target" or at least one participant.`, void 0, `${fieldPrefix}.participants`));
|
|
3063
|
+
}
|
|
3064
|
+
if (event.targetObjectId) {
|
|
3065
|
+
referencedIds.add(event.targetObjectId);
|
|
3066
|
+
if (!objectMap.has(event.targetObjectId)) {
|
|
3067
|
+
diagnostics.push(error("validate.event.target.unknown", `Unknown event target "${event.targetObjectId}" on "${event.id}".`, void 0, `${fieldPrefix}.target`));
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
const seenParticipants = /* @__PURE__ */ new Set();
|
|
3071
|
+
for (const participantId of event.participantObjectIds) {
|
|
3072
|
+
referencedIds.add(participantId);
|
|
3073
|
+
if (seenParticipants.has(participantId)) {
|
|
3074
|
+
diagnostics.push(warn("validate.event.participants.duplicate", `Event "${event.id}" repeats participant "${participantId}".`, void 0, `${fieldPrefix}.participants`));
|
|
3075
|
+
continue;
|
|
3076
|
+
}
|
|
3077
|
+
seenParticipants.add(participantId);
|
|
3078
|
+
if (!objectMap.has(participantId)) {
|
|
3079
|
+
diagnostics.push(error("validate.event.participants.unknown", `Unknown event participant "${participantId}" on "${event.id}".`, void 0, `${fieldPrefix}.participants`));
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
if (event.targetObjectId && event.participantObjectIds.length > 0 && !event.participantObjectIds.includes(event.targetObjectId)) {
|
|
3083
|
+
diagnostics.push(warn("validate.event.target.notParticipant", `Event "${event.id}" defines a target outside its participants list.`, void 0, `${fieldPrefix}.target`));
|
|
3084
|
+
}
|
|
3085
|
+
if (event.positions.length === 0) {
|
|
3086
|
+
diagnostics.push(warn("validate.event.positions.missing", `Event "${event.id}" has no positions block and cannot drive a scene snapshot.`, void 0, `${fieldPrefix}.positions`));
|
|
3087
|
+
}
|
|
3088
|
+
if (/(?:^|[-_])(solar-eclipse|lunar-eclipse|transit|occultation)(?:$|[-_])/.test(event.kind) && referencedIds.size < 3) {
|
|
3089
|
+
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`));
|
|
3090
|
+
}
|
|
3091
|
+
const poseIds = /* @__PURE__ */ new Set();
|
|
3092
|
+
for (const pose of event.positions) {
|
|
3093
|
+
const poseFieldPrefix = `${fieldPrefix}.pose.${pose.objectId}`;
|
|
3094
|
+
if (poseIds.has(pose.objectId)) {
|
|
3095
|
+
diagnostics.push(error("validate.event.pose.duplicate", `Event "${event.id}" defines "${pose.objectId}" more than once in positions.`, void 0, poseFieldPrefix));
|
|
3096
|
+
continue;
|
|
3097
|
+
}
|
|
3098
|
+
poseIds.add(pose.objectId);
|
|
3099
|
+
const object = objectMap.get(pose.objectId);
|
|
3100
|
+
if (!object) {
|
|
3101
|
+
diagnostics.push(error("validate.event.pose.object.unknown", `Unknown event pose object "${pose.objectId}" on "${event.id}".`, void 0, poseFieldPrefix));
|
|
3102
|
+
continue;
|
|
3103
|
+
}
|
|
3104
|
+
if (!referencedIds.has(pose.objectId)) {
|
|
3105
|
+
diagnostics.push(warn("validate.event.pose.unreferenced", `Event pose "${pose.objectId}" on "${event.id}" is not listed in target/participants.`, void 0, poseFieldPrefix));
|
|
3106
|
+
}
|
|
3107
|
+
validateEventPose(pose, object, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
function validateEventPose(pose, object, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
3111
|
+
const placement = pose.placement;
|
|
3112
|
+
if (!placement) {
|
|
3113
|
+
diagnostics.push(error("validate.event.pose.placement.required", `Event "${eventId}" pose "${pose.objectId}" is missing a placement mode.`, void 0, fieldPrefix));
|
|
3114
|
+
return;
|
|
3115
|
+
}
|
|
3116
|
+
if (placement.mode === "orbit") {
|
|
3117
|
+
if (!objectMap.has(placement.target)) {
|
|
3118
|
+
diagnostics.push(error("validate.event.pose.orbit.target.unknown", `Unknown event orbit target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.orbit`));
|
|
3119
|
+
}
|
|
3120
|
+
if (placement.distance && placement.semiMajor) {
|
|
3121
|
+
diagnostics.push(error("validate.event.pose.orbit.distanceConflict", `Event "${eventId}" pose "${pose.objectId}" cannot declare both "distance" and "semiMajor".`, void 0, `${fieldPrefix}.distance`));
|
|
3122
|
+
}
|
|
3123
|
+
return;
|
|
3124
|
+
}
|
|
3125
|
+
if (placement.mode === "surface") {
|
|
3126
|
+
const target = objectMap.get(placement.target);
|
|
3127
|
+
if (!target) {
|
|
3128
|
+
diagnostics.push(error("validate.event.pose.surface.target.unknown", `Unknown event surface target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.surface`));
|
|
3129
|
+
} else if (!SURFACE_TARGET_TYPES2.has(target.type)) {
|
|
3130
|
+
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`));
|
|
3131
|
+
}
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
3134
|
+
if (placement.mode === "at") {
|
|
3135
|
+
if (object.type !== "structure" && object.type !== "phenomenon") {
|
|
3136
|
+
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`));
|
|
3137
|
+
}
|
|
3138
|
+
const reference = placement.reference;
|
|
3139
|
+
if (reference.kind === "named" && !objectMap.has(reference.name)) {
|
|
3140
|
+
diagnostics.push(error("validate.event.pose.at.target.unknown", `Unknown event at-reference target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
3141
|
+
} else if (reference.kind === "anchor" && !objectMap.has(reference.objectId)) {
|
|
3142
|
+
diagnostics.push(error("validate.event.pose.anchor.target.unknown", `Unknown event anchor target "${reference.objectId}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
3143
|
+
} else if (reference.kind === "lagrange") {
|
|
3144
|
+
if (!objectMap.has(reference.primary)) {
|
|
3145
|
+
diagnostics.push(error("validate.event.pose.lagrange.primary.unknown", `Unknown event Lagrange target "${reference.primary}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
3146
|
+
} else if (reference.secondary && !objectMap.has(reference.secondary)) {
|
|
3147
|
+
diagnostics.push(error("validate.event.pose.lagrange.secondary.unknown", `Unknown event Lagrange target "${reference.secondary}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
function validateAtTarget(object, objectMap, diagnostics) {
|
|
3153
|
+
const reference = object.placement?.mode === "at" ? object.placement.reference : null;
|
|
3154
|
+
if (!reference) {
|
|
3155
|
+
return true;
|
|
3156
|
+
}
|
|
3157
|
+
if (reference.kind === "named") {
|
|
3158
|
+
return objectMap.has(reference.name);
|
|
3159
|
+
}
|
|
3160
|
+
if (reference.kind === "anchor") {
|
|
3161
|
+
if (!objectMap.has(reference.objectId)) {
|
|
3162
|
+
diagnostics.push(error("validate.anchor.target.unknown", `Unknown anchor target "${reference.objectId}" on "${object.id}".`, object.id, "at"));
|
|
3163
|
+
return false;
|
|
3164
|
+
}
|
|
3165
|
+
return true;
|
|
3166
|
+
}
|
|
3167
|
+
if (!objectMap.has(reference.primary)) {
|
|
3168
|
+
diagnostics.push(error("validate.lagrange.primary.unknown", `Unknown Lagrange reference "${reference.primary}" on "${object.id}".`, object.id, "at"));
|
|
3169
|
+
return false;
|
|
3170
|
+
}
|
|
3171
|
+
if (reference.secondary && !objectMap.has(reference.secondary)) {
|
|
3172
|
+
diagnostics.push(error("validate.lagrange.secondary.unknown", `Unknown Lagrange reference "${reference.secondary}" on "${object.id}".`, object.id, "at"));
|
|
3173
|
+
return false;
|
|
3174
|
+
}
|
|
3175
|
+
return true;
|
|
3176
|
+
}
|
|
3177
|
+
function keplerPeriodDays(object, parentObject) {
|
|
3178
|
+
const placement = object.placement;
|
|
3179
|
+
if (!placement || placement.mode !== "orbit") {
|
|
3180
|
+
return null;
|
|
3181
|
+
}
|
|
3182
|
+
const semiMajorAu = distanceInAu(placement.semiMajor ?? placement.distance);
|
|
3183
|
+
const centralMassSolar = massInSolar(parentObject?.properties.mass);
|
|
3184
|
+
if (semiMajorAu === null || centralMassSolar === null || centralMassSolar <= 0) {
|
|
3185
|
+
return null;
|
|
3186
|
+
}
|
|
3187
|
+
const periodYears = Math.sqrt(semiMajorAu ** 3 / centralMassSolar);
|
|
3188
|
+
return periodYears * 365.25;
|
|
3189
|
+
}
|
|
3190
|
+
function distanceInAu(value) {
|
|
3191
|
+
if (!value)
|
|
3192
|
+
return null;
|
|
3193
|
+
switch (value.unit) {
|
|
3194
|
+
case null:
|
|
3195
|
+
case "au":
|
|
3196
|
+
return value.value;
|
|
3197
|
+
case "km":
|
|
3198
|
+
return value.value / AU_IN_KM2;
|
|
3199
|
+
case "m":
|
|
3200
|
+
return value.value / (AU_IN_KM2 * 1e3);
|
|
3201
|
+
case "ly":
|
|
3202
|
+
return value.value * LY_IN_AU2;
|
|
3203
|
+
case "pc":
|
|
3204
|
+
return value.value * PC_IN_AU2;
|
|
3205
|
+
case "kpc":
|
|
3206
|
+
return value.value * KPC_IN_AU2;
|
|
3207
|
+
case "re":
|
|
3208
|
+
return value.value * EARTH_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
3209
|
+
case "sol":
|
|
3210
|
+
return value.value * SOLAR_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
3211
|
+
default:
|
|
3212
|
+
return null;
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
function massInSolar(value) {
|
|
3216
|
+
if (!value || typeof value !== "object" || !("value" in value)) {
|
|
3217
|
+
return null;
|
|
3218
|
+
}
|
|
3219
|
+
const unitValue = value;
|
|
3220
|
+
switch (unitValue.unit) {
|
|
3221
|
+
case null:
|
|
3222
|
+
case "sol":
|
|
3223
|
+
return unitValue.value;
|
|
3224
|
+
case "me":
|
|
3225
|
+
return unitValue.value / EARTH_MASSES_PER_SOLAR;
|
|
3226
|
+
case "mj":
|
|
3227
|
+
return unitValue.value / JUPITER_MASSES_PER_SOLAR;
|
|
3228
|
+
default:
|
|
3229
|
+
return null;
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
function durationInDays(value) {
|
|
3233
|
+
if (!value)
|
|
3234
|
+
return null;
|
|
3235
|
+
switch (value.unit) {
|
|
3236
|
+
case null:
|
|
3237
|
+
case "d":
|
|
3238
|
+
return value.value;
|
|
3239
|
+
case "s":
|
|
3240
|
+
return value.value / 86400;
|
|
3241
|
+
case "min":
|
|
3242
|
+
return value.value / 1440;
|
|
3243
|
+
case "h":
|
|
3244
|
+
return value.value / 24;
|
|
3245
|
+
case "y":
|
|
3246
|
+
return value.value * 365.25;
|
|
3247
|
+
case "ky":
|
|
3248
|
+
return value.value * 365250;
|
|
3249
|
+
case "my":
|
|
3250
|
+
return value.value * 36525e4;
|
|
3251
|
+
case "gy":
|
|
3252
|
+
return value.value * 36525e7;
|
|
3253
|
+
default:
|
|
3254
|
+
return null;
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
function toleranceForField(object, field) {
|
|
3258
|
+
const tolerance = object.tolerances?.find((entry) => entry.field === field)?.value;
|
|
3259
|
+
if (typeof tolerance === "number") {
|
|
3260
|
+
return tolerance;
|
|
3261
|
+
}
|
|
3262
|
+
if (tolerance && typeof tolerance === "object" && "value" in tolerance) {
|
|
3263
|
+
return durationInDays(tolerance) ?? 0;
|
|
3264
|
+
}
|
|
3265
|
+
return 0;
|
|
3266
|
+
}
|
|
3267
|
+
function formatDays(days) {
|
|
3268
|
+
return `${Math.round(days * 100) / 100}d`;
|
|
3269
|
+
}
|
|
3270
|
+
function error(code, message, objectId, field) {
|
|
3271
|
+
return { code, severity: "error", source: "validate", message, objectId, field };
|
|
3272
|
+
}
|
|
3273
|
+
function warn(code, message, objectId, field) {
|
|
3274
|
+
return { code, severity: "warning", source: "validate", message, objectId, field };
|
|
3275
|
+
}
|
|
3276
|
+
function info(code, message, objectId, field) {
|
|
3277
|
+
return { code, severity: "info", source: "validate", message, objectId, field };
|
|
3278
|
+
}
|
|
3279
|
+
|
|
3280
|
+
// packages/core/dist/draft-parse.js
|
|
3281
|
+
var STRUCTURED_TYPED_BLOCKS = /* @__PURE__ */ new Set([
|
|
3282
|
+
"climate",
|
|
3283
|
+
"habitability",
|
|
3284
|
+
"settlement"
|
|
3285
|
+
]);
|
|
3286
|
+
var DRAFT_OBJECT_FIELD_SPECS = /* @__PURE__ */ new Map();
|
|
3287
|
+
for (const key of [
|
|
3288
|
+
"orbit",
|
|
3289
|
+
"distance",
|
|
3290
|
+
"semiMajor",
|
|
3291
|
+
"eccentricity",
|
|
3292
|
+
"period",
|
|
3293
|
+
"angle",
|
|
3294
|
+
"inclination",
|
|
3295
|
+
"phase",
|
|
3296
|
+
"at",
|
|
3297
|
+
"surface",
|
|
3298
|
+
"free",
|
|
3299
|
+
"kind",
|
|
3300
|
+
"class",
|
|
3301
|
+
"culture",
|
|
3302
|
+
"tags",
|
|
3303
|
+
"color",
|
|
3304
|
+
"image",
|
|
3305
|
+
"hidden",
|
|
3306
|
+
"radius",
|
|
3307
|
+
"mass",
|
|
3308
|
+
"density",
|
|
3309
|
+
"gravity",
|
|
3310
|
+
"temperature",
|
|
3311
|
+
"albedo",
|
|
3312
|
+
"atmosphere",
|
|
3313
|
+
"inner",
|
|
3314
|
+
"outer",
|
|
3315
|
+
"on",
|
|
3316
|
+
"source",
|
|
3317
|
+
"cycle"
|
|
3318
|
+
]) {
|
|
3319
|
+
const schema = getFieldSchema(key);
|
|
3320
|
+
if (schema) {
|
|
3321
|
+
DRAFT_OBJECT_FIELD_SPECS.set(key, {
|
|
3322
|
+
key,
|
|
3323
|
+
version: "2.0",
|
|
3324
|
+
inlineMode: schema.arity === "multiple" ? "multiple" : "single",
|
|
3325
|
+
allowRepeat: false,
|
|
3326
|
+
legacySchema: schema
|
|
3327
|
+
});
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
for (const spec of [
|
|
3331
|
+
{ key: "groups", inlineMode: "multiple", allowRepeat: false },
|
|
3332
|
+
{ key: "epoch", inlineMode: "single", allowRepeat: false },
|
|
3333
|
+
{ key: "referencePlane", inlineMode: "single", allowRepeat: false },
|
|
3334
|
+
{ key: "tidalLock", inlineMode: "single", allowRepeat: false },
|
|
3335
|
+
{ key: "renderLabel", inlineMode: "single", allowRepeat: false },
|
|
3336
|
+
{ key: "renderOrbit", inlineMode: "single", allowRepeat: false },
|
|
3337
|
+
{ key: "renderPriority", inlineMode: "single", allowRepeat: false },
|
|
3338
|
+
{ key: "resonance", inlineMode: "pair", allowRepeat: false },
|
|
3339
|
+
{ key: "derive", inlineMode: "pair", allowRepeat: true },
|
|
3340
|
+
{ key: "validate", inlineMode: "single", allowRepeat: true },
|
|
3341
|
+
{ key: "locked", inlineMode: "multiple", allowRepeat: false },
|
|
3342
|
+
{ key: "tolerance", inlineMode: "pair", allowRepeat: true }
|
|
3343
|
+
]) {
|
|
3344
|
+
DRAFT_OBJECT_FIELD_SPECS.set(spec.key, {
|
|
3345
|
+
key: spec.key,
|
|
3346
|
+
version: "2.1",
|
|
3347
|
+
inlineMode: spec.inlineMode,
|
|
3348
|
+
allowRepeat: spec.allowRepeat
|
|
3349
|
+
});
|
|
3350
|
+
}
|
|
3351
|
+
var DRAFT_OBJECT_FIELD_KEYS = new Set(DRAFT_OBJECT_FIELD_SPECS.keys());
|
|
3352
|
+
var EVENT_POSE_FIELD_KEYS = /* @__PURE__ */ new Set([
|
|
3353
|
+
"orbit",
|
|
3354
|
+
"distance",
|
|
3355
|
+
"semiMajor",
|
|
3356
|
+
"eccentricity",
|
|
3357
|
+
"period",
|
|
3358
|
+
"angle",
|
|
3359
|
+
"inclination",
|
|
3360
|
+
"phase",
|
|
3361
|
+
"at",
|
|
3362
|
+
"surface",
|
|
3363
|
+
"free",
|
|
3364
|
+
"inner",
|
|
3365
|
+
"outer"
|
|
3366
|
+
]);
|
|
3367
|
+
function parseWorldOrbitAtlas(source) {
|
|
3368
|
+
return parseAtlasSource(source);
|
|
3369
|
+
}
|
|
3370
|
+
function parseAtlasSource(source, forcedOutputVersion) {
|
|
3371
|
+
const prepared = preprocessAtlasSource(source);
|
|
3372
|
+
const lines = prepared.source.split(/\r?\n/);
|
|
3373
|
+
const diagnostics = [];
|
|
3374
|
+
let sawSchemaHeader = false;
|
|
3375
|
+
let sourceSchemaVersion = "2.0";
|
|
3376
|
+
let system = null;
|
|
3377
|
+
let section = null;
|
|
3378
|
+
const objectNodes = [];
|
|
3379
|
+
const groups = [];
|
|
3380
|
+
const relations = [];
|
|
3381
|
+
const events = [];
|
|
3382
|
+
const eventPoseNodes = /* @__PURE__ */ new Map();
|
|
3383
|
+
let sawDefaults = false;
|
|
3384
|
+
let sawAtlas = false;
|
|
3385
|
+
const viewpointIds = /* @__PURE__ */ new Set();
|
|
3386
|
+
const annotationIds = /* @__PURE__ */ new Set();
|
|
3387
|
+
const groupIds = /* @__PURE__ */ new Set();
|
|
3388
|
+
const relationIds = /* @__PURE__ */ new Set();
|
|
3389
|
+
const eventIds = /* @__PURE__ */ new Set();
|
|
3390
|
+
for (let index = 0; index < lines.length; index++) {
|
|
3391
|
+
const rawLine = lines[index];
|
|
3392
|
+
const lineNumber = index + 1;
|
|
3393
|
+
if (!rawLine.trim()) {
|
|
3394
|
+
continue;
|
|
3395
|
+
}
|
|
3396
|
+
const indent = getIndent(rawLine);
|
|
3397
|
+
const tokens = tokenizeLineDetailed(rawLine.slice(indent), {
|
|
3398
|
+
line: lineNumber,
|
|
3399
|
+
columnOffset: indent
|
|
3400
|
+
});
|
|
3401
|
+
if (tokens.length === 0) {
|
|
3402
|
+
continue;
|
|
3403
|
+
}
|
|
3404
|
+
if (!sawSchemaHeader) {
|
|
3405
|
+
sourceSchemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
3406
|
+
sawSchemaHeader = true;
|
|
3407
|
+
if (prepared.comments.length > 0 && sourceSchemaVersion !== "2.1") {
|
|
3408
|
+
diagnostics.push({
|
|
3409
|
+
code: "parse.schema21.commentCompatibility",
|
|
3410
|
+
severity: "warning",
|
|
3411
|
+
source: "parse",
|
|
3412
|
+
message: `Comments require schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
3413
|
+
line: prepared.comments[0].line,
|
|
3414
|
+
column: prepared.comments[0].column
|
|
3415
|
+
});
|
|
3416
|
+
}
|
|
3417
|
+
continue;
|
|
3418
|
+
}
|
|
3419
|
+
if (indent === 0) {
|
|
3420
|
+
section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, { sawDefaults, sawAtlas });
|
|
3421
|
+
if (section.kind === "system") {
|
|
3422
|
+
system = section.system;
|
|
3423
|
+
} else if (section.kind === "defaults") {
|
|
3424
|
+
sawDefaults = true;
|
|
3425
|
+
} else if (section.kind === "atlas") {
|
|
3426
|
+
sawAtlas = true;
|
|
3427
|
+
}
|
|
3428
|
+
continue;
|
|
3429
|
+
}
|
|
3430
|
+
if (!section) {
|
|
3431
|
+
throw new WorldOrbitError("Indented line without parent atlas section", lineNumber, indent + 1);
|
|
3432
|
+
}
|
|
3433
|
+
handleSectionLine(section, indent, tokens, lineNumber);
|
|
3434
|
+
}
|
|
3435
|
+
if (!sawSchemaHeader) {
|
|
3436
|
+
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
|
|
3437
|
+
}
|
|
3438
|
+
const objects = objectNodes.map((node) => normalizeDraftObject(node, sourceSchemaVersion, diagnostics));
|
|
3439
|
+
const normalizedEvents = events.map((event) => normalizeDraftEvent(event, eventPoseNodes.get(event.id) ?? []));
|
|
3440
|
+
const outputVersion = forcedOutputVersion ?? (sourceSchemaVersion === "2.0-draft" ? "2.0" : sourceSchemaVersion);
|
|
3441
|
+
const baseDocument = {
|
|
3442
|
+
format: "worldorbit",
|
|
3443
|
+
sourceVersion: "1.0",
|
|
3444
|
+
system,
|
|
3445
|
+
groups,
|
|
3446
|
+
relations,
|
|
3447
|
+
events: normalizedEvents,
|
|
3448
|
+
objects,
|
|
3449
|
+
diagnostics
|
|
3450
|
+
};
|
|
3451
|
+
if (outputVersion === "2.0-draft") {
|
|
3452
|
+
const document3 = {
|
|
3453
|
+
...baseDocument,
|
|
3454
|
+
version: "2.0-draft",
|
|
3455
|
+
schemaVersion: "2.0-draft"
|
|
3456
|
+
};
|
|
3457
|
+
document3.diagnostics.push(...collectAtlasDiagnostics(document3, sourceSchemaVersion));
|
|
3458
|
+
return document3;
|
|
3459
|
+
}
|
|
3460
|
+
const document2 = {
|
|
3461
|
+
...baseDocument,
|
|
3462
|
+
version: outputVersion,
|
|
3463
|
+
schemaVersion: outputVersion
|
|
3464
|
+
};
|
|
3465
|
+
if (sourceSchemaVersion === "2.0-draft") {
|
|
3466
|
+
document2.diagnostics.push({
|
|
3467
|
+
code: "load.schema.deprecatedDraft",
|
|
3468
|
+
severity: "warning",
|
|
3469
|
+
source: "upgrade",
|
|
3470
|
+
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
3471
|
+
});
|
|
3472
|
+
}
|
|
3473
|
+
document2.diagnostics.push(...collectAtlasDiagnostics(document2, sourceSchemaVersion));
|
|
3474
|
+
return document2;
|
|
3475
|
+
}
|
|
3476
|
+
function assertDraftSchemaHeader(tokens, line) {
|
|
3477
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1"].includes(tokens[1].value.toLowerCase())) {
|
|
3478
|
+
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
3479
|
+
}
|
|
3480
|
+
const version = tokens[1].value.toLowerCase();
|
|
3481
|
+
return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
3482
|
+
}
|
|
3483
|
+
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, flags) {
|
|
3484
|
+
const keyword = tokens[0]?.value.toLowerCase();
|
|
3485
|
+
switch (keyword) {
|
|
3486
|
+
case "system":
|
|
3487
|
+
if (system) {
|
|
3488
|
+
throw new WorldOrbitError('Atlas section "system" may only appear once', line, tokens[0].column);
|
|
3489
|
+
}
|
|
3490
|
+
return startSystemSection(tokens, line, sourceSchemaVersion, diagnostics);
|
|
3491
|
+
case "defaults":
|
|
3492
|
+
if (!system) {
|
|
3493
|
+
throw new WorldOrbitError('Atlas section "defaults" requires a preceding system declaration', line, tokens[0].column);
|
|
3494
|
+
}
|
|
3495
|
+
if (flags.sawDefaults) {
|
|
3496
|
+
throw new WorldOrbitError('Atlas section "defaults" may only appear once', line, tokens[0].column);
|
|
3497
|
+
}
|
|
3498
|
+
return {
|
|
3499
|
+
kind: "defaults",
|
|
3500
|
+
system,
|
|
3501
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
3502
|
+
};
|
|
3503
|
+
case "atlas":
|
|
3504
|
+
if (!system) {
|
|
3505
|
+
throw new WorldOrbitError('Atlas section "atlas" requires a preceding system declaration', line, tokens[0].column);
|
|
3506
|
+
}
|
|
3507
|
+
if (flags.sawAtlas) {
|
|
3508
|
+
throw new WorldOrbitError('Atlas section "atlas" may only appear once', line, tokens[0].column);
|
|
3509
|
+
}
|
|
3510
|
+
return {
|
|
3511
|
+
kind: "atlas",
|
|
3512
|
+
system,
|
|
3513
|
+
inMetadata: false,
|
|
3514
|
+
metadataIndent: null
|
|
3515
|
+
};
|
|
3516
|
+
case "viewpoint":
|
|
3517
|
+
if (!system) {
|
|
3518
|
+
throw new WorldOrbitError('Atlas section "viewpoint" requires a preceding system declaration', line, tokens[0].column);
|
|
3519
|
+
}
|
|
3520
|
+
return startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaVersion, diagnostics);
|
|
3521
|
+
case "annotation":
|
|
3522
|
+
if (!system) {
|
|
3523
|
+
throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
|
|
3524
|
+
}
|
|
3525
|
+
return startAnnotationSection(tokens, line, system, annotationIds);
|
|
3526
|
+
case "group":
|
|
3527
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "group", { line, column: tokens[0].column });
|
|
3528
|
+
return startGroupSection(tokens, line, groups, groupIds);
|
|
3529
|
+
case "relation":
|
|
3530
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "relation", { line, column: tokens[0].column });
|
|
3531
|
+
return startRelationSection(tokens, line, relations, relationIds);
|
|
3532
|
+
case "event":
|
|
3533
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "event", { line, column: tokens[0].column });
|
|
3534
|
+
return startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourceSchemaVersion, diagnostics);
|
|
3535
|
+
case "object":
|
|
3536
|
+
return startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes);
|
|
3537
|
+
default:
|
|
3538
|
+
throw new WorldOrbitError(`Unknown atlas section "${tokens[0]?.value ?? ""}"`, line, tokens[0]?.column ?? 1);
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
function startSystemSection(tokens, line, sourceSchemaVersion, diagnostics) {
|
|
3542
|
+
if (tokens.length !== 2) {
|
|
3543
|
+
throw new WorldOrbitError("Invalid atlas system declaration", line, tokens[0]?.column ?? 1);
|
|
3544
|
+
}
|
|
3545
|
+
const system = {
|
|
3546
|
+
type: "system",
|
|
3547
|
+
id: tokens[1].value,
|
|
3548
|
+
title: null,
|
|
3549
|
+
description: null,
|
|
3550
|
+
epoch: null,
|
|
3551
|
+
referencePlane: null,
|
|
3552
|
+
defaults: {
|
|
3553
|
+
view: "topdown",
|
|
3554
|
+
scale: null,
|
|
3555
|
+
units: null,
|
|
3556
|
+
preset: null,
|
|
3557
|
+
theme: null
|
|
3558
|
+
},
|
|
3559
|
+
atlasMetadata: {},
|
|
3560
|
+
viewpoints: [],
|
|
3561
|
+
annotations: []
|
|
3562
|
+
};
|
|
3563
|
+
return {
|
|
3564
|
+
kind: "system",
|
|
3565
|
+
system,
|
|
3566
|
+
sourceSchemaVersion,
|
|
3567
|
+
diagnostics,
|
|
2568
3568
|
seenFields: /* @__PURE__ */ new Set()
|
|
2569
3569
|
};
|
|
2570
3570
|
}
|
|
2571
|
-
function startViewpointSection(tokens, line, system, viewpointIds) {
|
|
3571
|
+
function startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaVersion, diagnostics) {
|
|
2572
3572
|
if (tokens.length !== 2) {
|
|
2573
3573
|
throw new WorldOrbitError("Invalid viewpoint declaration", line, tokens[0]?.column ?? 1);
|
|
2574
3574
|
}
|
|
@@ -2585,6 +3585,7 @@
|
|
|
2585
3585
|
summary: "",
|
|
2586
3586
|
focusObjectId: null,
|
|
2587
3587
|
selectedObjectId: null,
|
|
3588
|
+
events: [],
|
|
2588
3589
|
projection: system.defaults.view,
|
|
2589
3590
|
preset: system.defaults.preset,
|
|
2590
3591
|
zoom: null,
|
|
@@ -2597,6 +3598,8 @@
|
|
|
2597
3598
|
return {
|
|
2598
3599
|
kind: "viewpoint",
|
|
2599
3600
|
viewpoint,
|
|
3601
|
+
sourceSchemaVersion,
|
|
3602
|
+
diagnostics,
|
|
2600
3603
|
seenFields: /* @__PURE__ */ new Set(),
|
|
2601
3604
|
inFilter: false,
|
|
2602
3605
|
filterIndent: null,
|
|
@@ -2630,7 +3633,107 @@
|
|
|
2630
3633
|
seenFields: /* @__PURE__ */ new Set()
|
|
2631
3634
|
};
|
|
2632
3635
|
}
|
|
2633
|
-
function
|
|
3636
|
+
function startGroupSection(tokens, line, groups, groupIds) {
|
|
3637
|
+
if (tokens.length !== 2) {
|
|
3638
|
+
throw new WorldOrbitError("Invalid group declaration", line, tokens[0]?.column ?? 1);
|
|
3639
|
+
}
|
|
3640
|
+
const id = normalizeIdentifier(tokens[1].value);
|
|
3641
|
+
if (!id) {
|
|
3642
|
+
throw new WorldOrbitError("Group id must not be empty", line, tokens[1].column);
|
|
3643
|
+
}
|
|
3644
|
+
if (groupIds.has(id)) {
|
|
3645
|
+
throw new WorldOrbitError(`Duplicate group id "${id}"`, line, tokens[1].column);
|
|
3646
|
+
}
|
|
3647
|
+
const group = {
|
|
3648
|
+
id,
|
|
3649
|
+
label: humanizeIdentifier2(id),
|
|
3650
|
+
summary: "",
|
|
3651
|
+
color: null,
|
|
3652
|
+
tags: [],
|
|
3653
|
+
hidden: false
|
|
3654
|
+
};
|
|
3655
|
+
groups.push(group);
|
|
3656
|
+
groupIds.add(id);
|
|
3657
|
+
return {
|
|
3658
|
+
kind: "group",
|
|
3659
|
+
group,
|
|
3660
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
3661
|
+
};
|
|
3662
|
+
}
|
|
3663
|
+
function startRelationSection(tokens, line, relations, relationIds) {
|
|
3664
|
+
if (tokens.length !== 2) {
|
|
3665
|
+
throw new WorldOrbitError("Invalid relation declaration", line, tokens[0]?.column ?? 1);
|
|
3666
|
+
}
|
|
3667
|
+
const id = normalizeIdentifier(tokens[1].value);
|
|
3668
|
+
if (!id) {
|
|
3669
|
+
throw new WorldOrbitError("Relation id must not be empty", line, tokens[1].column);
|
|
3670
|
+
}
|
|
3671
|
+
if (relationIds.has(id)) {
|
|
3672
|
+
throw new WorldOrbitError(`Duplicate relation id "${id}"`, line, tokens[1].column);
|
|
3673
|
+
}
|
|
3674
|
+
const relation = {
|
|
3675
|
+
id,
|
|
3676
|
+
from: "",
|
|
3677
|
+
to: "",
|
|
3678
|
+
kind: "",
|
|
3679
|
+
label: null,
|
|
3680
|
+
summary: null,
|
|
3681
|
+
tags: [],
|
|
3682
|
+
color: null,
|
|
3683
|
+
hidden: false
|
|
3684
|
+
};
|
|
3685
|
+
relations.push(relation);
|
|
3686
|
+
relationIds.add(id);
|
|
3687
|
+
return {
|
|
3688
|
+
kind: "relation",
|
|
3689
|
+
relation,
|
|
3690
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
3691
|
+
};
|
|
3692
|
+
}
|
|
3693
|
+
function startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourceSchemaVersion, diagnostics) {
|
|
3694
|
+
if (tokens.length !== 2) {
|
|
3695
|
+
throw new WorldOrbitError("Invalid event declaration", line, tokens[0]?.column ?? 1);
|
|
3696
|
+
}
|
|
3697
|
+
const id = normalizeIdentifier(tokens[1].value);
|
|
3698
|
+
if (!id) {
|
|
3699
|
+
throw new WorldOrbitError("Event id must not be empty", line, tokens[1].column);
|
|
3700
|
+
}
|
|
3701
|
+
if (eventIds.has(id)) {
|
|
3702
|
+
throw new WorldOrbitError(`Duplicate event id "${id}"`, line, tokens[1].column);
|
|
3703
|
+
}
|
|
3704
|
+
const event = {
|
|
3705
|
+
id,
|
|
3706
|
+
kind: "",
|
|
3707
|
+
label: humanizeIdentifier2(id),
|
|
3708
|
+
summary: null,
|
|
3709
|
+
targetObjectId: null,
|
|
3710
|
+
participantObjectIds: [],
|
|
3711
|
+
timing: null,
|
|
3712
|
+
visibility: null,
|
|
3713
|
+
tags: [],
|
|
3714
|
+
color: null,
|
|
3715
|
+
hidden: false,
|
|
3716
|
+
positions: []
|
|
3717
|
+
};
|
|
3718
|
+
const rawPoses = [];
|
|
3719
|
+
events.push(event);
|
|
3720
|
+
eventPoseNodes.set(id, rawPoses);
|
|
3721
|
+
eventIds.add(id);
|
|
3722
|
+
return {
|
|
3723
|
+
kind: "event",
|
|
3724
|
+
event,
|
|
3725
|
+
sourceSchemaVersion,
|
|
3726
|
+
diagnostics,
|
|
3727
|
+
seenFields: /* @__PURE__ */ new Set(),
|
|
3728
|
+
rawPoses,
|
|
3729
|
+
inPositions: false,
|
|
3730
|
+
positionsIndent: null,
|
|
3731
|
+
activePose: null,
|
|
3732
|
+
poseIndent: null,
|
|
3733
|
+
activePoseSeenFields: /* @__PURE__ */ new Set()
|
|
3734
|
+
};
|
|
3735
|
+
}
|
|
3736
|
+
function startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes) {
|
|
2634
3737
|
if (tokens.length < 3) {
|
|
2635
3738
|
throw new WorldOrbitError("Invalid atlas object declaration", line, tokens[0]?.column ?? 1);
|
|
2636
3739
|
}
|
|
@@ -2641,12 +3744,11 @@
|
|
|
2641
3744
|
throw new WorldOrbitError(`Unknown object type "${objectTypeToken.value}"`, line, objectTypeToken.column);
|
|
2642
3745
|
}
|
|
2643
3746
|
const objectNode = {
|
|
2644
|
-
type: "object",
|
|
2645
3747
|
objectType,
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
blockFields: [],
|
|
3748
|
+
id: idToken.value,
|
|
3749
|
+
fields: parseInlineObjectFields(tokens.slice(3), line, objectType, sourceSchemaVersion, diagnostics),
|
|
2649
3750
|
infoEntries: [],
|
|
3751
|
+
typedBlockEntries: {},
|
|
2650
3752
|
location: {
|
|
2651
3753
|
line,
|
|
2652
3754
|
column: objectTypeToken.column
|
|
@@ -2656,8 +3758,12 @@
|
|
|
2656
3758
|
return {
|
|
2657
3759
|
kind: "object",
|
|
2658
3760
|
objectNode,
|
|
2659
|
-
|
|
2660
|
-
|
|
3761
|
+
sourceSchemaVersion,
|
|
3762
|
+
diagnostics,
|
|
3763
|
+
activeBlock: null,
|
|
3764
|
+
blockIndent: null,
|
|
3765
|
+
seenInfoKeys: /* @__PURE__ */ new Set(),
|
|
3766
|
+
seenTypedBlockKeys: {}
|
|
2661
3767
|
};
|
|
2662
3768
|
}
|
|
2663
3769
|
function handleSectionLine(section, indent, tokens, line) {
|
|
@@ -2677,6 +3783,15 @@
|
|
|
2677
3783
|
case "annotation":
|
|
2678
3784
|
applyAnnotationField(section, tokens, line);
|
|
2679
3785
|
return;
|
|
3786
|
+
case "group":
|
|
3787
|
+
applyGroupField(section, tokens, line);
|
|
3788
|
+
return;
|
|
3789
|
+
case "relation":
|
|
3790
|
+
applyRelationField(section, tokens, line);
|
|
3791
|
+
return;
|
|
3792
|
+
case "event":
|
|
3793
|
+
applyEventField(section, indent, tokens, line);
|
|
3794
|
+
return;
|
|
2680
3795
|
case "object":
|
|
2681
3796
|
applyObjectField(section, indent, tokens, line);
|
|
2682
3797
|
return;
|
|
@@ -2684,10 +3799,35 @@
|
|
|
2684
3799
|
}
|
|
2685
3800
|
function applySystemField(section, tokens, line) {
|
|
2686
3801
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
2687
|
-
|
|
2688
|
-
|
|
3802
|
+
const value = joinFieldValue(tokens, line);
|
|
3803
|
+
switch (key) {
|
|
3804
|
+
case "title":
|
|
3805
|
+
section.system.title = value;
|
|
3806
|
+
return;
|
|
3807
|
+
case "description":
|
|
3808
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
3809
|
+
line,
|
|
3810
|
+
column: tokens[0].column
|
|
3811
|
+
});
|
|
3812
|
+
section.system.description = value;
|
|
3813
|
+
return;
|
|
3814
|
+
case "epoch":
|
|
3815
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
3816
|
+
line,
|
|
3817
|
+
column: tokens[0].column
|
|
3818
|
+
});
|
|
3819
|
+
section.system.epoch = value;
|
|
3820
|
+
return;
|
|
3821
|
+
case "referenceplane":
|
|
3822
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, "referencePlane", {
|
|
3823
|
+
line,
|
|
3824
|
+
column: tokens[0].column
|
|
3825
|
+
});
|
|
3826
|
+
section.system.referencePlane = value;
|
|
3827
|
+
return;
|
|
3828
|
+
default:
|
|
3829
|
+
throw new WorldOrbitError(`Unknown system atlas field "${tokens[0].value}"`, line, tokens[0].column);
|
|
2689
3830
|
}
|
|
2690
|
-
section.system.title = joinFieldValue(tokens, line);
|
|
2691
3831
|
}
|
|
2692
3832
|
function applyDefaultsField(section, tokens, line) {
|
|
2693
3833
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
@@ -2718,14 +3858,11 @@
|
|
|
2718
3858
|
section.metadataIndent = null;
|
|
2719
3859
|
}
|
|
2720
3860
|
if (section.inMetadata) {
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
const key = tokens[0].value;
|
|
2725
|
-
if (key in section.system.atlasMetadata) {
|
|
2726
|
-
throw new WorldOrbitError(`Duplicate atlas metadata key "${key}"`, line, tokens[0].column);
|
|
3861
|
+
const entry = parseInfoLikeEntry(tokens, line, "Invalid atlas metadata entry");
|
|
3862
|
+
if (entry.key in section.system.atlasMetadata) {
|
|
3863
|
+
throw new WorldOrbitError(`Duplicate atlas metadata key "${entry.key}"`, line, tokens[0].column);
|
|
2727
3864
|
}
|
|
2728
|
-
section.system.atlasMetadata[key] =
|
|
3865
|
+
section.system.atlasMetadata[entry.key] = entry.value;
|
|
2729
3866
|
return;
|
|
2730
3867
|
}
|
|
2731
3868
|
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "metadata") {
|
|
@@ -2781,7 +3918,14 @@
|
|
|
2781
3918
|
section.viewpoint.rotationDeg = parseFiniteNumber2(value, line, tokens[0].column, "rotation");
|
|
2782
3919
|
return;
|
|
2783
3920
|
case "layers":
|
|
2784
|
-
section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line);
|
|
3921
|
+
section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line, section.sourceSchemaVersion, section.diagnostics);
|
|
3922
|
+
return;
|
|
3923
|
+
case "events":
|
|
3924
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.events", {
|
|
3925
|
+
line,
|
|
3926
|
+
column: tokens[0].column
|
|
3927
|
+
});
|
|
3928
|
+
section.viewpoint.events = parseTokenList(tokens.slice(1), line, "events");
|
|
2785
3929
|
return;
|
|
2786
3930
|
default:
|
|
2787
3931
|
throw new WorldOrbitError(`Unknown viewpoint field "${tokens[0].value}"`, line, tokens[0].column);
|
|
@@ -2806,42 +3950,223 @@
|
|
|
2806
3950
|
default:
|
|
2807
3951
|
throw new WorldOrbitError(`Unknown viewpoint filter field "${tokens[0].value}"`, line, tokens[0].column);
|
|
2808
3952
|
}
|
|
2809
|
-
section.viewpoint.filter = filter;
|
|
2810
|
-
}
|
|
2811
|
-
function applyAnnotationField(section, tokens, line) {
|
|
3953
|
+
section.viewpoint.filter = filter;
|
|
3954
|
+
}
|
|
3955
|
+
function applyAnnotationField(section, tokens, line) {
|
|
3956
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
3957
|
+
switch (key) {
|
|
3958
|
+
case "label":
|
|
3959
|
+
section.annotation.label = joinFieldValue(tokens, line);
|
|
3960
|
+
return;
|
|
3961
|
+
case "target":
|
|
3962
|
+
section.annotation.targetObjectId = joinFieldValue(tokens, line);
|
|
3963
|
+
return;
|
|
3964
|
+
case "body":
|
|
3965
|
+
section.annotation.body = joinFieldValue(tokens, line);
|
|
3966
|
+
return;
|
|
3967
|
+
case "tags":
|
|
3968
|
+
section.annotation.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
3969
|
+
return;
|
|
3970
|
+
default:
|
|
3971
|
+
throw new WorldOrbitError(`Unknown annotation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
function applyGroupField(section, tokens, line) {
|
|
3975
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
3976
|
+
switch (key) {
|
|
3977
|
+
case "label":
|
|
3978
|
+
section.group.label = joinFieldValue(tokens, line);
|
|
3979
|
+
return;
|
|
3980
|
+
case "summary":
|
|
3981
|
+
section.group.summary = joinFieldValue(tokens, line);
|
|
3982
|
+
return;
|
|
3983
|
+
case "color":
|
|
3984
|
+
section.group.color = joinFieldValue(tokens, line);
|
|
3985
|
+
return;
|
|
3986
|
+
case "tags":
|
|
3987
|
+
section.group.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
3988
|
+
return;
|
|
3989
|
+
case "hidden":
|
|
3990
|
+
section.group.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
3991
|
+
line,
|
|
3992
|
+
column: tokens[0].column
|
|
3993
|
+
});
|
|
3994
|
+
return;
|
|
3995
|
+
default:
|
|
3996
|
+
throw new WorldOrbitError(`Unknown group field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
function applyRelationField(section, tokens, line) {
|
|
4000
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
4001
|
+
switch (key) {
|
|
4002
|
+
case "from":
|
|
4003
|
+
section.relation.from = joinFieldValue(tokens, line);
|
|
4004
|
+
return;
|
|
4005
|
+
case "to":
|
|
4006
|
+
section.relation.to = joinFieldValue(tokens, line);
|
|
4007
|
+
return;
|
|
4008
|
+
case "kind":
|
|
4009
|
+
section.relation.kind = joinFieldValue(tokens, line);
|
|
4010
|
+
return;
|
|
4011
|
+
case "label":
|
|
4012
|
+
section.relation.label = joinFieldValue(tokens, line);
|
|
4013
|
+
return;
|
|
4014
|
+
case "summary":
|
|
4015
|
+
section.relation.summary = joinFieldValue(tokens, line);
|
|
4016
|
+
return;
|
|
4017
|
+
case "tags":
|
|
4018
|
+
section.relation.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4019
|
+
return;
|
|
4020
|
+
case "color":
|
|
4021
|
+
section.relation.color = joinFieldValue(tokens, line);
|
|
4022
|
+
return;
|
|
4023
|
+
case "hidden":
|
|
4024
|
+
section.relation.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4025
|
+
line,
|
|
4026
|
+
column: tokens[0].column
|
|
4027
|
+
});
|
|
4028
|
+
return;
|
|
4029
|
+
default:
|
|
4030
|
+
throw new WorldOrbitError(`Unknown relation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4031
|
+
}
|
|
4032
|
+
}
|
|
4033
|
+
function applyEventField(section, indent, tokens, line) {
|
|
4034
|
+
if (section.activePose && indent <= (section.poseIndent ?? 0)) {
|
|
4035
|
+
section.activePose = null;
|
|
4036
|
+
section.poseIndent = null;
|
|
4037
|
+
section.activePoseSeenFields.clear();
|
|
4038
|
+
}
|
|
4039
|
+
if (!section.activePose && section.inPositions && indent <= (section.positionsIndent ?? 0)) {
|
|
4040
|
+
section.inPositions = false;
|
|
4041
|
+
section.positionsIndent = null;
|
|
4042
|
+
}
|
|
4043
|
+
if (section.activePose) {
|
|
4044
|
+
section.activePose.fields.push(parseEventPoseField(tokens, line, section.activePoseSeenFields));
|
|
4045
|
+
return;
|
|
4046
|
+
}
|
|
4047
|
+
if (section.inPositions) {
|
|
4048
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "pose") {
|
|
4049
|
+
throw new WorldOrbitError(`Unknown event positions field "${tokens[0].value}"`, line, tokens[0]?.column ?? 1);
|
|
4050
|
+
}
|
|
4051
|
+
const objectId = tokens[1].value;
|
|
4052
|
+
if (!objectId.trim()) {
|
|
4053
|
+
throw new WorldOrbitError("Event pose object id must not be empty", line, tokens[1].column);
|
|
4054
|
+
}
|
|
4055
|
+
const rawPose = {
|
|
4056
|
+
objectId,
|
|
4057
|
+
fields: [],
|
|
4058
|
+
location: { line, column: tokens[0].column }
|
|
4059
|
+
};
|
|
4060
|
+
section.rawPoses.push(rawPose);
|
|
4061
|
+
section.activePose = rawPose;
|
|
4062
|
+
section.poseIndent = indent;
|
|
4063
|
+
section.activePoseSeenFields = /* @__PURE__ */ new Set();
|
|
4064
|
+
return;
|
|
4065
|
+
}
|
|
4066
|
+
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "positions") {
|
|
4067
|
+
if (section.seenFields.has("positions")) {
|
|
4068
|
+
throw new WorldOrbitError('Duplicate event field "positions"', line, tokens[0].column);
|
|
4069
|
+
}
|
|
4070
|
+
section.seenFields.add("positions");
|
|
4071
|
+
section.inPositions = true;
|
|
4072
|
+
section.positionsIndent = indent;
|
|
4073
|
+
return;
|
|
4074
|
+
}
|
|
2812
4075
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
2813
4076
|
switch (key) {
|
|
4077
|
+
case "kind":
|
|
4078
|
+
section.event.kind = joinFieldValue(tokens, line);
|
|
4079
|
+
return;
|
|
2814
4080
|
case "label":
|
|
2815
|
-
section.
|
|
4081
|
+
section.event.label = joinFieldValue(tokens, line);
|
|
4082
|
+
return;
|
|
4083
|
+
case "summary":
|
|
4084
|
+
section.event.summary = joinFieldValue(tokens, line);
|
|
2816
4085
|
return;
|
|
2817
4086
|
case "target":
|
|
2818
|
-
section.
|
|
4087
|
+
section.event.targetObjectId = joinFieldValue(tokens, line);
|
|
2819
4088
|
return;
|
|
2820
|
-
case "
|
|
2821
|
-
section.
|
|
4089
|
+
case "participants":
|
|
4090
|
+
section.event.participantObjectIds = parseTokenList(tokens.slice(1), line, "participants");
|
|
4091
|
+
return;
|
|
4092
|
+
case "timing":
|
|
4093
|
+
section.event.timing = joinFieldValue(tokens, line);
|
|
4094
|
+
return;
|
|
4095
|
+
case "visibility":
|
|
4096
|
+
section.event.visibility = joinFieldValue(tokens, line);
|
|
2822
4097
|
return;
|
|
2823
4098
|
case "tags":
|
|
2824
|
-
section.
|
|
4099
|
+
section.event.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4100
|
+
return;
|
|
4101
|
+
case "color":
|
|
4102
|
+
section.event.color = joinFieldValue(tokens, line);
|
|
4103
|
+
return;
|
|
4104
|
+
case "hidden":
|
|
4105
|
+
section.event.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4106
|
+
line,
|
|
4107
|
+
column: tokens[0].column
|
|
4108
|
+
});
|
|
2825
4109
|
return;
|
|
2826
4110
|
default:
|
|
2827
|
-
throw new WorldOrbitError(`Unknown
|
|
4111
|
+
throw new WorldOrbitError(`Unknown event field "${tokens[0].value}"`, line, tokens[0].column);
|
|
2828
4112
|
}
|
|
2829
4113
|
}
|
|
2830
|
-
function
|
|
2831
|
-
if (tokens.length
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
4114
|
+
function parseEventPoseField(tokens, line, seenFields) {
|
|
4115
|
+
if (tokens.length < 2) {
|
|
4116
|
+
throw new WorldOrbitError("Invalid event pose field line", line, tokens[0]?.column ?? 1);
|
|
4117
|
+
}
|
|
4118
|
+
const key = tokens[0].value;
|
|
4119
|
+
if (!EVENT_POSE_FIELD_KEYS.has(key)) {
|
|
4120
|
+
throw new WorldOrbitError(`Unknown event pose field "${key}"`, line, tokens[0].column);
|
|
2835
4121
|
}
|
|
2836
|
-
if (
|
|
2837
|
-
|
|
2838
|
-
|
|
4122
|
+
if (seenFields.has(key)) {
|
|
4123
|
+
throw new WorldOrbitError(`Duplicate event pose field "${key}"`, line, tokens[0].column);
|
|
4124
|
+
}
|
|
4125
|
+
seenFields.add(key);
|
|
4126
|
+
return {
|
|
4127
|
+
type: "field",
|
|
4128
|
+
key,
|
|
4129
|
+
values: tokens.slice(1).map((token) => token.value),
|
|
4130
|
+
location: { line, column: tokens[0].column }
|
|
4131
|
+
};
|
|
4132
|
+
}
|
|
4133
|
+
function applyObjectField(section, indent, tokens, line) {
|
|
4134
|
+
if (section.activeBlock && indent <= (section.blockIndent ?? 0)) {
|
|
4135
|
+
section.activeBlock = null;
|
|
4136
|
+
section.blockIndent = null;
|
|
4137
|
+
}
|
|
4138
|
+
if (tokens.length === 1) {
|
|
4139
|
+
const blockName = tokens[0].value.toLowerCase();
|
|
4140
|
+
if (blockName === "info" || STRUCTURED_TYPED_BLOCKS.has(blockName)) {
|
|
4141
|
+
if (blockName !== "info") {
|
|
4142
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, blockName, { line, column: tokens[0].column });
|
|
4143
|
+
}
|
|
4144
|
+
section.activeBlock = blockName;
|
|
4145
|
+
section.blockIndent = indent;
|
|
4146
|
+
return;
|
|
4147
|
+
}
|
|
2839
4148
|
}
|
|
2840
|
-
if (section.
|
|
2841
|
-
|
|
4149
|
+
if (section.activeBlock) {
|
|
4150
|
+
const entry = parseInfoLikeEntry(tokens, line, `Invalid ${section.activeBlock} entry`);
|
|
4151
|
+
if (section.activeBlock === "info") {
|
|
4152
|
+
if (section.seenInfoKeys.has(entry.key)) {
|
|
4153
|
+
throw new WorldOrbitError(`Duplicate info key "${entry.key}"`, line, tokens[0].column);
|
|
4154
|
+
}
|
|
4155
|
+
section.seenInfoKeys.add(entry.key);
|
|
4156
|
+
section.objectNode.infoEntries.push(entry);
|
|
4157
|
+
return;
|
|
4158
|
+
}
|
|
4159
|
+
const typedBlock = section.activeBlock;
|
|
4160
|
+
const seenKeys = section.seenTypedBlockKeys[typedBlock] ?? (section.seenTypedBlockKeys[typedBlock] = /* @__PURE__ */ new Set());
|
|
4161
|
+
if (seenKeys.has(entry.key)) {
|
|
4162
|
+
throw new WorldOrbitError(`Duplicate ${typedBlock} key "${entry.key}"`, line, tokens[0].column);
|
|
4163
|
+
}
|
|
4164
|
+
seenKeys.add(entry.key);
|
|
4165
|
+
const entries = section.objectNode.typedBlockEntries[typedBlock] ?? (section.objectNode.typedBlockEntries[typedBlock] = []);
|
|
4166
|
+
entries.push(entry);
|
|
2842
4167
|
return;
|
|
2843
4168
|
}
|
|
2844
|
-
section.objectNode.
|
|
4169
|
+
section.objectNode.fields.push(parseObjectField(tokens, line, section.objectNode.objectType, section.sourceSchemaVersion, section.diagnostics));
|
|
2845
4170
|
}
|
|
2846
4171
|
function requireUniqueField(tokens, seenFields, line) {
|
|
2847
4172
|
if (tokens.length < 2) {
|
|
@@ -2861,50 +4186,46 @@
|
|
|
2861
4186
|
return tokens.slice(1).map((token) => token.value).join(" ").trim();
|
|
2862
4187
|
}
|
|
2863
4188
|
function parseObjectTypeTokens(tokens, line) {
|
|
2864
|
-
|
|
2865
|
-
throw new WorldOrbitError("Missing value for atlas field", line);
|
|
2866
|
-
}
|
|
2867
|
-
return tokens.map((token) => {
|
|
2868
|
-
const value = token.value;
|
|
2869
|
-
if (value !== "star" && value !== "planet" && value !== "moon" && value !== "belt" && value !== "asteroid" && value !== "comet" && value !== "ring" && value !== "structure" && value !== "phenomenon") {
|
|
2870
|
-
throw new WorldOrbitError(`Unknown viewpoint object type "${token.value}"`, line, token.column);
|
|
2871
|
-
}
|
|
2872
|
-
return value;
|
|
2873
|
-
});
|
|
4189
|
+
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");
|
|
2874
4190
|
}
|
|
2875
|
-
function
|
|
2876
|
-
|
|
2877
|
-
|
|
4191
|
+
function parseLayerTokens(tokens, line, sourceSchemaVersion, diagnostics) {
|
|
4192
|
+
const layers = {};
|
|
4193
|
+
for (const token of parseTokenList(tokens, line, "layers")) {
|
|
4194
|
+
const enabled = !token.startsWith("-") && !token.startsWith("!");
|
|
4195
|
+
const raw = token.replace(/^[-!]+/, "").toLowerCase();
|
|
4196
|
+
if (raw === "orbits") {
|
|
4197
|
+
layers["orbits-back"] = enabled;
|
|
4198
|
+
layers["orbits-front"] = enabled;
|
|
4199
|
+
continue;
|
|
4200
|
+
}
|
|
4201
|
+
if (raw === "background" || raw === "guides" || raw === "orbits-back" || raw === "orbits-front" || raw === "relations" || raw === "events" || raw === "objects" || raw === "labels" || raw === "metadata") {
|
|
4202
|
+
if (raw === "events" && sourceSchemaVersion && diagnostics) {
|
|
4203
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "layers.events", {
|
|
4204
|
+
line,
|
|
4205
|
+
column: tokens[0]?.column ?? 1
|
|
4206
|
+
});
|
|
4207
|
+
}
|
|
4208
|
+
layers[raw] = enabled;
|
|
4209
|
+
}
|
|
2878
4210
|
}
|
|
2879
|
-
return
|
|
4211
|
+
return layers;
|
|
2880
4212
|
}
|
|
2881
|
-
function
|
|
4213
|
+
function parseTokenList(tokens, line, fieldName) {
|
|
2882
4214
|
if (tokens.length === 0) {
|
|
2883
|
-
throw new WorldOrbitError(
|
|
4215
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, 1);
|
|
2884
4216
|
}
|
|
2885
|
-
const
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
const rawLayer = token.value.replace(/^[-!]+/, "").toLowerCase();
|
|
2889
|
-
if (rawLayer === "orbits") {
|
|
2890
|
-
next["orbits-back"] = enabled;
|
|
2891
|
-
next["orbits-front"] = enabled;
|
|
2892
|
-
continue;
|
|
2893
|
-
}
|
|
2894
|
-
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
2895
|
-
next[rawLayer] = enabled;
|
|
2896
|
-
continue;
|
|
2897
|
-
}
|
|
2898
|
-
throw new WorldOrbitError(`Unknown layer token "${token.value}"`, line, token.column);
|
|
4217
|
+
const values = tokens.map((token) => token.value).filter(Boolean);
|
|
4218
|
+
if (values.length === 0) {
|
|
4219
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, tokens[0]?.column ?? 1);
|
|
2899
4220
|
}
|
|
2900
|
-
return
|
|
4221
|
+
return values;
|
|
2901
4222
|
}
|
|
2902
4223
|
function parseProjectionValue(value, line, column) {
|
|
2903
4224
|
const normalized = value.toLowerCase();
|
|
2904
|
-
if (normalized
|
|
2905
|
-
|
|
4225
|
+
if (normalized !== "topdown" && normalized !== "isometric") {
|
|
4226
|
+
throw new WorldOrbitError(`Unknown projection "${value}"`, line, column);
|
|
2906
4227
|
}
|
|
2907
|
-
|
|
4228
|
+
return normalized;
|
|
2908
4229
|
}
|
|
2909
4230
|
function parsePresetValue(value, line, column) {
|
|
2910
4231
|
const normalized = value.toLowerCase();
|
|
@@ -2914,16 +4235,16 @@
|
|
|
2914
4235
|
throw new WorldOrbitError(`Unknown render preset "${value}"`, line, column);
|
|
2915
4236
|
}
|
|
2916
4237
|
function parsePositiveNumber2(value, line, column, field) {
|
|
2917
|
-
const parsed =
|
|
2918
|
-
if (
|
|
2919
|
-
throw new WorldOrbitError(`Field "${field}"
|
|
4238
|
+
const parsed = parseFiniteNumber2(value, line, column, field);
|
|
4239
|
+
if (parsed <= 0) {
|
|
4240
|
+
throw new WorldOrbitError(`Field "${field}" must be greater than zero`, line, column);
|
|
2920
4241
|
}
|
|
2921
4242
|
return parsed;
|
|
2922
4243
|
}
|
|
2923
4244
|
function parseFiniteNumber2(value, line, column, field) {
|
|
2924
4245
|
const parsed = Number(value);
|
|
2925
4246
|
if (!Number.isFinite(parsed)) {
|
|
2926
|
-
throw new WorldOrbitError(`
|
|
4247
|
+
throw new WorldOrbitError(`Invalid numeric value "${value}" for "${field}"`, line, column);
|
|
2927
4248
|
}
|
|
2928
4249
|
return parsed;
|
|
2929
4250
|
}
|
|
@@ -2935,28 +4256,43 @@
|
|
|
2935
4256
|
groupIds: []
|
|
2936
4257
|
};
|
|
2937
4258
|
}
|
|
2938
|
-
function
|
|
4259
|
+
function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
2939
4260
|
const fields = [];
|
|
2940
4261
|
let index = 0;
|
|
2941
4262
|
while (index < tokens.length) {
|
|
2942
4263
|
const keyToken = tokens[index];
|
|
2943
|
-
const
|
|
2944
|
-
if (!
|
|
4264
|
+
const spec = getDraftObjectFieldSpec(keyToken.value);
|
|
4265
|
+
if (!spec) {
|
|
2945
4266
|
throw new WorldOrbitError(`Unknown field "${keyToken.value}"`, line, keyToken.column);
|
|
2946
4267
|
}
|
|
4268
|
+
if (spec.version === "2.1") {
|
|
4269
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, keyToken.value, {
|
|
4270
|
+
line,
|
|
4271
|
+
column: keyToken.column
|
|
4272
|
+
});
|
|
4273
|
+
}
|
|
2947
4274
|
index++;
|
|
2948
4275
|
const valueTokens = [];
|
|
2949
|
-
if (
|
|
2950
|
-
while (index < tokens.length && !isKnownFieldKey(tokens[index].value)) {
|
|
2951
|
-
valueTokens.push(tokens[index]);
|
|
2952
|
-
index++;
|
|
2953
|
-
}
|
|
2954
|
-
} else {
|
|
4276
|
+
if (spec.inlineMode === "single") {
|
|
2955
4277
|
const nextToken = tokens[index];
|
|
2956
4278
|
if (nextToken) {
|
|
2957
4279
|
valueTokens.push(nextToken);
|
|
2958
4280
|
index++;
|
|
2959
4281
|
}
|
|
4282
|
+
} else if (spec.inlineMode === "pair") {
|
|
4283
|
+
for (let count = 0; count < 2; count++) {
|
|
4284
|
+
const nextToken = tokens[index];
|
|
4285
|
+
if (!nextToken) {
|
|
4286
|
+
break;
|
|
4287
|
+
}
|
|
4288
|
+
valueTokens.push(nextToken);
|
|
4289
|
+
index++;
|
|
4290
|
+
}
|
|
4291
|
+
} else {
|
|
4292
|
+
while (index < tokens.length && !DRAFT_OBJECT_FIELD_KEYS.has(tokens[index].value)) {
|
|
4293
|
+
valueTokens.push(tokens[index]);
|
|
4294
|
+
index++;
|
|
4295
|
+
}
|
|
2960
4296
|
}
|
|
2961
4297
|
if (valueTokens.length === 0) {
|
|
2962
4298
|
throw new WorldOrbitError(`Missing value for field "${keyToken.value}"`, line, keyToken.column);
|
|
@@ -2968,25 +4304,35 @@
|
|
|
2968
4304
|
location: { line, column: keyToken.column }
|
|
2969
4305
|
});
|
|
2970
4306
|
}
|
|
4307
|
+
validateDraftObjectFieldCompatibility(fields, objectType);
|
|
2971
4308
|
return fields;
|
|
2972
4309
|
}
|
|
2973
|
-
function
|
|
4310
|
+
function parseObjectField(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
2974
4311
|
if (tokens.length < 2) {
|
|
2975
4312
|
throw new WorldOrbitError("Invalid field line", line, tokens[0]?.column ?? 1);
|
|
2976
4313
|
}
|
|
2977
|
-
|
|
4314
|
+
const spec = getDraftObjectFieldSpec(tokens[0].value);
|
|
4315
|
+
if (!spec) {
|
|
2978
4316
|
throw new WorldOrbitError(`Unknown field "${tokens[0].value}"`, line, tokens[0].column);
|
|
2979
4317
|
}
|
|
2980
|
-
|
|
4318
|
+
if (spec.version === "2.1") {
|
|
4319
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, tokens[0].value, {
|
|
4320
|
+
line,
|
|
4321
|
+
column: tokens[0].column
|
|
4322
|
+
});
|
|
4323
|
+
}
|
|
4324
|
+
const field = {
|
|
2981
4325
|
type: "field",
|
|
2982
4326
|
key: tokens[0].value,
|
|
2983
4327
|
values: tokens.slice(1).map((token) => token.value),
|
|
2984
4328
|
location: { line, column: tokens[0].column }
|
|
2985
4329
|
};
|
|
4330
|
+
validateDraftObjectFieldCompatibility([field], objectType);
|
|
4331
|
+
return field;
|
|
2986
4332
|
}
|
|
2987
|
-
function
|
|
4333
|
+
function parseInfoLikeEntry(tokens, line, errorMessage) {
|
|
2988
4334
|
if (tokens.length < 2) {
|
|
2989
|
-
throw new WorldOrbitError(
|
|
4335
|
+
throw new WorldOrbitError(errorMessage, line, tokens[0]?.column ?? 1);
|
|
2990
4336
|
}
|
|
2991
4337
|
return {
|
|
2992
4338
|
type: "info-entry",
|
|
@@ -2995,18 +4341,366 @@
|
|
|
2995
4341
|
location: { line, column: tokens[0].column }
|
|
2996
4342
|
};
|
|
2997
4343
|
}
|
|
2998
|
-
function
|
|
2999
|
-
|
|
4344
|
+
function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
|
|
4345
|
+
const fieldMap = collectDraftFields(node.fields);
|
|
4346
|
+
const placement = extractPlacementFromFieldMap(fieldMap);
|
|
4347
|
+
const properties = normalizeDraftProperties(node.objectType, fieldMap);
|
|
4348
|
+
const groups = parseOptionalTokenList(fieldMap.get("groups")?.[0]);
|
|
4349
|
+
const epoch = parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]);
|
|
4350
|
+
const referencePlane = parseOptionalJoinedValue(fieldMap.get("referencePlane")?.[0]);
|
|
4351
|
+
const tidalLock = fieldMap.has("tidalLock") ? parseAtlasBoolean(singleFieldValue2(fieldMap.get("tidalLock")[0]), "tidalLock", fieldMap.get("tidalLock")[0].location) : void 0;
|
|
4352
|
+
const resonance = fieldMap.has("resonance") ? parseResonanceField(fieldMap.get("resonance")[0]) : void 0;
|
|
4353
|
+
const renderHints = extractRenderHints(fieldMap);
|
|
4354
|
+
const deriveRules = fieldMap.get("derive")?.map((field) => parseDeriveField(field));
|
|
4355
|
+
const validationRules = fieldMap.get("validate")?.map((field) => ({
|
|
4356
|
+
rule: singleFieldValue2(field)
|
|
4357
|
+
}));
|
|
4358
|
+
const lockedFields = fieldMap.has("locked") ? [...new Set(fieldMap.get("locked").flatMap((field) => field.values))] : void 0;
|
|
4359
|
+
const tolerances = fieldMap.get("tolerance")?.map((field) => parseToleranceField(field));
|
|
4360
|
+
const typedBlocks = normalizeTypedBlocks(node.typedBlockEntries);
|
|
4361
|
+
const info2 = normalizeInfoEntries(node.infoEntries, "info");
|
|
4362
|
+
const object = {
|
|
4363
|
+
type: node.objectType,
|
|
4364
|
+
id: node.id,
|
|
4365
|
+
properties,
|
|
4366
|
+
placement,
|
|
4367
|
+
info: info2
|
|
4368
|
+
};
|
|
4369
|
+
if (groups.length > 0)
|
|
4370
|
+
object.groups = groups;
|
|
4371
|
+
if (epoch)
|
|
4372
|
+
object.epoch = epoch;
|
|
4373
|
+
if (referencePlane)
|
|
4374
|
+
object.referencePlane = referencePlane;
|
|
4375
|
+
if (tidalLock !== void 0)
|
|
4376
|
+
object.tidalLock = tidalLock;
|
|
4377
|
+
if (resonance)
|
|
4378
|
+
object.resonance = resonance;
|
|
4379
|
+
if (renderHints)
|
|
4380
|
+
object.renderHints = renderHints;
|
|
4381
|
+
if (deriveRules?.length)
|
|
4382
|
+
object.deriveRules = deriveRules;
|
|
4383
|
+
if (validationRules?.length)
|
|
4384
|
+
object.validationRules = validationRules;
|
|
4385
|
+
if (lockedFields?.length)
|
|
4386
|
+
object.lockedFields = lockedFields;
|
|
4387
|
+
if (tolerances?.length)
|
|
4388
|
+
object.tolerances = tolerances;
|
|
4389
|
+
if (typedBlocks && Object.keys(typedBlocks).length > 0)
|
|
4390
|
+
object.typedBlocks = typedBlocks;
|
|
4391
|
+
if (sourceSchemaVersion !== "2.1") {
|
|
4392
|
+
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) {
|
|
4393
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, node.id, node.location);
|
|
4394
|
+
}
|
|
4395
|
+
}
|
|
4396
|
+
return object;
|
|
3000
4397
|
}
|
|
3001
|
-
function
|
|
3002
|
-
return
|
|
4398
|
+
function normalizeDraftEvent(event, rawPoses) {
|
|
4399
|
+
return {
|
|
4400
|
+
...event,
|
|
4401
|
+
participantObjectIds: [...new Set(event.participantObjectIds)],
|
|
4402
|
+
tags: [...new Set(event.tags)],
|
|
4403
|
+
positions: rawPoses.map((pose) => normalizeDraftEventPose(pose))
|
|
4404
|
+
};
|
|
4405
|
+
}
|
|
4406
|
+
function normalizeDraftEventPose(rawPose) {
|
|
4407
|
+
const fieldMap = collectDraftFields(rawPose.fields);
|
|
4408
|
+
const placement = extractPlacementFromFieldMap(fieldMap);
|
|
4409
|
+
return {
|
|
4410
|
+
objectId: rawPose.objectId,
|
|
4411
|
+
placement,
|
|
4412
|
+
inner: parseOptionalUnitField(fieldMap.get("inner")?.[0], "inner"),
|
|
4413
|
+
outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer")
|
|
4414
|
+
};
|
|
4415
|
+
}
|
|
4416
|
+
function collectDraftFields(fields) {
|
|
4417
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4418
|
+
for (const field of fields) {
|
|
4419
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
4420
|
+
if (!spec) {
|
|
4421
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4422
|
+
}
|
|
4423
|
+
if (!spec.allowRepeat && grouped.has(field.key)) {
|
|
4424
|
+
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
4425
|
+
}
|
|
4426
|
+
const existing = grouped.get(field.key) ?? [];
|
|
4427
|
+
existing.push(field);
|
|
4428
|
+
grouped.set(field.key, existing);
|
|
4429
|
+
}
|
|
4430
|
+
return grouped;
|
|
4431
|
+
}
|
|
4432
|
+
function extractPlacementFromFieldMap(fieldMap) {
|
|
4433
|
+
const orbitField = fieldMap.get("orbit")?.[0];
|
|
4434
|
+
const atField = fieldMap.get("at")?.[0];
|
|
4435
|
+
const surfaceField = fieldMap.get("surface")?.[0];
|
|
4436
|
+
const freeField = fieldMap.get("free")?.[0];
|
|
4437
|
+
const count = [orbitField, atField, surfaceField, freeField].filter(Boolean).length;
|
|
4438
|
+
if (count > 1) {
|
|
4439
|
+
const conflictingField = orbitField ?? atField ?? surfaceField ?? freeField;
|
|
4440
|
+
throw WorldOrbitError.fromLocation("Object has multiple placement modes", conflictingField?.location);
|
|
4441
|
+
}
|
|
4442
|
+
if (orbitField) {
|
|
4443
|
+
return {
|
|
4444
|
+
mode: "orbit",
|
|
4445
|
+
target: singleFieldValue2(orbitField),
|
|
4446
|
+
distance: parseOptionalUnitField(fieldMap.get("distance")?.[0], "distance"),
|
|
4447
|
+
semiMajor: parseOptionalUnitField(fieldMap.get("semiMajor")?.[0], "semiMajor"),
|
|
4448
|
+
eccentricity: parseOptionalNumberField(fieldMap.get("eccentricity")?.[0], "eccentricity"),
|
|
4449
|
+
period: parseOptionalUnitField(fieldMap.get("period")?.[0], "period"),
|
|
4450
|
+
angle: parseOptionalUnitField(fieldMap.get("angle")?.[0], "angle"),
|
|
4451
|
+
inclination: parseOptionalUnitField(fieldMap.get("inclination")?.[0], "inclination"),
|
|
4452
|
+
phase: parseOptionalUnitField(fieldMap.get("phase")?.[0], "phase")
|
|
4453
|
+
};
|
|
4454
|
+
}
|
|
4455
|
+
if (atField) {
|
|
4456
|
+
const target = singleFieldValue2(atField);
|
|
4457
|
+
return {
|
|
4458
|
+
mode: "at",
|
|
4459
|
+
target,
|
|
4460
|
+
reference: parseAtlasAtReference(target, atField.location)
|
|
4461
|
+
};
|
|
4462
|
+
}
|
|
4463
|
+
if (surfaceField) {
|
|
4464
|
+
return {
|
|
4465
|
+
mode: "surface",
|
|
4466
|
+
target: singleFieldValue2(surfaceField)
|
|
4467
|
+
};
|
|
4468
|
+
}
|
|
4469
|
+
if (freeField) {
|
|
4470
|
+
const raw = singleFieldValue2(freeField);
|
|
4471
|
+
const distance = tryParseAtlasUnitValue(raw);
|
|
4472
|
+
return {
|
|
4473
|
+
mode: "free",
|
|
4474
|
+
distance: distance ?? void 0,
|
|
4475
|
+
descriptor: distance ? void 0 : raw
|
|
4476
|
+
};
|
|
4477
|
+
}
|
|
4478
|
+
return null;
|
|
4479
|
+
}
|
|
4480
|
+
function normalizeDraftProperties(objectType, fieldMap) {
|
|
4481
|
+
const properties = {};
|
|
4482
|
+
for (const [key, fields] of fieldMap.entries()) {
|
|
4483
|
+
const field = fields[0];
|
|
4484
|
+
const spec = getDraftObjectFieldSpec(key);
|
|
4485
|
+
if (!field || !spec?.legacySchema || spec.legacySchema.placement) {
|
|
4486
|
+
continue;
|
|
4487
|
+
}
|
|
4488
|
+
ensureAtlasFieldSupported(key, objectType, field.location);
|
|
4489
|
+
properties[key] = normalizeLegacyScalarValue(key, field.values, field.location);
|
|
4490
|
+
}
|
|
4491
|
+
return properties;
|
|
4492
|
+
}
|
|
4493
|
+
function normalizeInfoEntries(entries, label) {
|
|
4494
|
+
const normalized = {};
|
|
4495
|
+
for (const entry of entries) {
|
|
4496
|
+
if (entry.key in normalized) {
|
|
4497
|
+
throw WorldOrbitError.fromLocation(`Duplicate ${label} key "${entry.key}"`, entry.location);
|
|
4498
|
+
}
|
|
4499
|
+
normalized[entry.key] = entry.value;
|
|
4500
|
+
}
|
|
4501
|
+
return normalized;
|
|
4502
|
+
}
|
|
4503
|
+
function normalizeTypedBlocks(typedBlockEntries) {
|
|
4504
|
+
const typedBlocks = {};
|
|
4505
|
+
for (const blockName of Object.keys(typedBlockEntries)) {
|
|
4506
|
+
const entries = typedBlockEntries[blockName];
|
|
4507
|
+
if (entries?.length) {
|
|
4508
|
+
typedBlocks[blockName] = normalizeInfoEntries(entries, blockName);
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
return typedBlocks;
|
|
4512
|
+
}
|
|
4513
|
+
function extractRenderHints(fieldMap) {
|
|
4514
|
+
const renderHints = {};
|
|
4515
|
+
const renderLabelField = fieldMap.get("renderLabel")?.[0];
|
|
4516
|
+
const renderOrbitField = fieldMap.get("renderOrbit")?.[0];
|
|
4517
|
+
const renderPriorityField = fieldMap.get("renderPriority")?.[0];
|
|
4518
|
+
if (renderLabelField) {
|
|
4519
|
+
renderHints.renderLabel = parseAtlasBoolean(singleFieldValue2(renderLabelField), "renderLabel", renderLabelField.location);
|
|
4520
|
+
}
|
|
4521
|
+
if (renderOrbitField) {
|
|
4522
|
+
renderHints.renderOrbit = parseAtlasBoolean(singleFieldValue2(renderOrbitField), "renderOrbit", renderOrbitField.location);
|
|
4523
|
+
}
|
|
4524
|
+
if (renderPriorityField) {
|
|
4525
|
+
renderHints.renderPriority = parseAtlasNumber(singleFieldValue2(renderPriorityField), "renderPriority", renderPriorityField.location);
|
|
4526
|
+
}
|
|
4527
|
+
return Object.keys(renderHints).length > 0 ? renderHints : void 0;
|
|
4528
|
+
}
|
|
4529
|
+
function parseResonanceField(field) {
|
|
4530
|
+
if (field.values.length !== 2) {
|
|
4531
|
+
throw WorldOrbitError.fromLocation('Field "resonance" expects "<targetObjectId> <ratio>"', field.location);
|
|
4532
|
+
}
|
|
4533
|
+
const ratio = field.values[1];
|
|
4534
|
+
if (!/^\d+:\d+$/.test(ratio)) {
|
|
4535
|
+
throw WorldOrbitError.fromLocation(`Invalid resonance ratio "${ratio}"`, field.location);
|
|
4536
|
+
}
|
|
4537
|
+
return {
|
|
4538
|
+
targetObjectId: field.values[0],
|
|
4539
|
+
ratio
|
|
4540
|
+
};
|
|
4541
|
+
}
|
|
4542
|
+
function parseDeriveField(field) {
|
|
4543
|
+
if (field.values.length !== 2) {
|
|
4544
|
+
throw WorldOrbitError.fromLocation('Field "derive" expects "<field> <strategy>"', field.location);
|
|
4545
|
+
}
|
|
4546
|
+
return {
|
|
4547
|
+
field: field.values[0],
|
|
4548
|
+
strategy: field.values[1]
|
|
4549
|
+
};
|
|
4550
|
+
}
|
|
4551
|
+
function parseToleranceField(field) {
|
|
4552
|
+
if (field.values.length !== 2) {
|
|
4553
|
+
throw WorldOrbitError.fromLocation('Field "tolerance" expects "<field> <value>"', field.location);
|
|
4554
|
+
}
|
|
4555
|
+
const rawValue = field.values[1];
|
|
4556
|
+
const unitValue = tryParseAtlasUnitValue(rawValue);
|
|
4557
|
+
const numericValue2 = Number(rawValue);
|
|
4558
|
+
return {
|
|
4559
|
+
field: field.values[0],
|
|
4560
|
+
value: unitValue ?? (Number.isFinite(numericValue2) ? numericValue2 : rawValue)
|
|
4561
|
+
};
|
|
4562
|
+
}
|
|
4563
|
+
function parseOptionalTokenList(field) {
|
|
4564
|
+
return field ? [...new Set(field.values)] : [];
|
|
4565
|
+
}
|
|
4566
|
+
function parseOptionalJoinedValue(field) {
|
|
4567
|
+
if (!field) {
|
|
4568
|
+
return null;
|
|
4569
|
+
}
|
|
4570
|
+
return field.values.join(" ").trim() || null;
|
|
4571
|
+
}
|
|
4572
|
+
function parseOptionalUnitField(field, key) {
|
|
4573
|
+
return field ? parseAtlasUnitValue(singleFieldValue2(field), field.location, key) : void 0;
|
|
4574
|
+
}
|
|
4575
|
+
function parseOptionalNumberField(field, key) {
|
|
4576
|
+
return field ? parseAtlasNumber(singleFieldValue2(field), key, field.location) : void 0;
|
|
4577
|
+
}
|
|
4578
|
+
function singleFieldValue2(field) {
|
|
4579
|
+
return singleAtlasValue(field.values, field.key, field.location);
|
|
4580
|
+
}
|
|
4581
|
+
function getDraftObjectFieldSpec(key) {
|
|
4582
|
+
return DRAFT_OBJECT_FIELD_SPECS.get(key);
|
|
4583
|
+
}
|
|
4584
|
+
function validateDraftObjectFieldCompatibility(fields, objectType) {
|
|
4585
|
+
for (const field of fields) {
|
|
4586
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
4587
|
+
if (!spec) {
|
|
4588
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4589
|
+
}
|
|
4590
|
+
if (spec.legacySchema) {
|
|
4591
|
+
ensureAtlasFieldSupported(field.key, objectType, field.location);
|
|
4592
|
+
continue;
|
|
4593
|
+
}
|
|
4594
|
+
if ((field.key === "renderLabel" || field.key === "renderOrbit" || field.key === "tidalLock") && field.values.length !== 1) {
|
|
4595
|
+
throw WorldOrbitError.fromLocation(`Field "${field.key}" expects exactly one value`, field.location);
|
|
4596
|
+
}
|
|
4597
|
+
}
|
|
4598
|
+
}
|
|
4599
|
+
function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
4600
|
+
if (sourceSchemaVersion === "2.1") {
|
|
4601
|
+
return;
|
|
4602
|
+
}
|
|
4603
|
+
diagnostics.push({
|
|
4604
|
+
code: "parse.schema21.featureCompatibility",
|
|
4605
|
+
severity: "warning",
|
|
4606
|
+
source: "parse",
|
|
4607
|
+
message: `Feature "${featureName}" requires schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
4608
|
+
line: location.line,
|
|
4609
|
+
column: location.column
|
|
4610
|
+
});
|
|
4611
|
+
}
|
|
4612
|
+
function preprocessAtlasSource(source) {
|
|
4613
|
+
const chars = [...source];
|
|
4614
|
+
const comments = [];
|
|
4615
|
+
let inString = false;
|
|
4616
|
+
let inBlockComment = false;
|
|
4617
|
+
let blockCommentStart = null;
|
|
4618
|
+
let line = 1;
|
|
4619
|
+
let column = 1;
|
|
4620
|
+
for (let index = 0; index < chars.length; index++) {
|
|
4621
|
+
const ch = chars[index];
|
|
4622
|
+
const next = chars[index + 1];
|
|
4623
|
+
if (inBlockComment) {
|
|
4624
|
+
if (ch === "*" && next === "/") {
|
|
4625
|
+
chars[index] = " ";
|
|
4626
|
+
chars[index + 1] = " ";
|
|
4627
|
+
inBlockComment = false;
|
|
4628
|
+
blockCommentStart = null;
|
|
4629
|
+
index++;
|
|
4630
|
+
column += 2;
|
|
4631
|
+
continue;
|
|
4632
|
+
}
|
|
4633
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
4634
|
+
chars[index] = " ";
|
|
4635
|
+
}
|
|
4636
|
+
if (ch === "\n") {
|
|
4637
|
+
line++;
|
|
4638
|
+
column = 1;
|
|
4639
|
+
} else {
|
|
4640
|
+
column++;
|
|
4641
|
+
}
|
|
4642
|
+
continue;
|
|
4643
|
+
}
|
|
4644
|
+
if (!inString && ch === "/" && next === "*") {
|
|
4645
|
+
comments.push({ kind: "block", line, column });
|
|
4646
|
+
chars[index] = " ";
|
|
4647
|
+
chars[index + 1] = " ";
|
|
4648
|
+
inBlockComment = true;
|
|
4649
|
+
blockCommentStart = { line, column };
|
|
4650
|
+
index++;
|
|
4651
|
+
column += 2;
|
|
4652
|
+
continue;
|
|
4653
|
+
}
|
|
4654
|
+
if (!inString && ch === "#" && !isHexColorLiteral(chars, index)) {
|
|
4655
|
+
comments.push({ kind: "line", line, column });
|
|
4656
|
+
chars[index] = " ";
|
|
4657
|
+
let inner = index + 1;
|
|
4658
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
4659
|
+
chars[inner] = " ";
|
|
4660
|
+
inner++;
|
|
4661
|
+
}
|
|
4662
|
+
column += inner - index;
|
|
4663
|
+
index = inner - 1;
|
|
4664
|
+
continue;
|
|
4665
|
+
}
|
|
4666
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
4667
|
+
inString = !inString;
|
|
4668
|
+
}
|
|
4669
|
+
if (ch === "\n") {
|
|
4670
|
+
line++;
|
|
4671
|
+
column = 1;
|
|
4672
|
+
} else {
|
|
4673
|
+
column++;
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
if (inBlockComment) {
|
|
4677
|
+
throw WorldOrbitError.fromLocation("Unclosed block comment", blockCommentStart ?? void 0);
|
|
4678
|
+
}
|
|
4679
|
+
return {
|
|
4680
|
+
source: chars.join(""),
|
|
4681
|
+
comments
|
|
4682
|
+
};
|
|
4683
|
+
}
|
|
4684
|
+
function isHexColorLiteral(chars, start) {
|
|
4685
|
+
let index = start + 1;
|
|
4686
|
+
let length = 0;
|
|
4687
|
+
while (index < chars.length && /[0-9a-f]/i.test(chars[index] ?? "")) {
|
|
4688
|
+
index++;
|
|
4689
|
+
length++;
|
|
4690
|
+
}
|
|
4691
|
+
if (![3, 4, 6, 8].includes(length)) {
|
|
4692
|
+
return false;
|
|
4693
|
+
}
|
|
4694
|
+
const next = chars[index];
|
|
4695
|
+
return next === void 0 || next === " " || next === " " || next === "\r" || next === "\n";
|
|
3003
4696
|
}
|
|
3004
4697
|
|
|
3005
4698
|
// packages/core/dist/load.js
|
|
3006
|
-
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0)?$/i;
|
|
4699
|
+
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;
|
|
4700
|
+
var ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
|
|
3007
4701
|
var LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
|
|
3008
4702
|
function detectWorldOrbitSchemaVersion(source) {
|
|
3009
|
-
for (const line of source.split(/\r?\n/)) {
|
|
4703
|
+
for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
|
|
3010
4704
|
const trimmed = line.trim();
|
|
3011
4705
|
if (!trimmed) {
|
|
3012
4706
|
continue;
|
|
@@ -3014,6 +4708,9 @@
|
|
|
3014
4708
|
if (LEGACY_DRAFT_SCHEMA_PATTERN.test(trimmed)) {
|
|
3015
4709
|
return "2.0-draft";
|
|
3016
4710
|
}
|
|
4711
|
+
if (ATLAS_SCHEMA_21_PATTERN.test(trimmed)) {
|
|
4712
|
+
return "2.1";
|
|
4713
|
+
}
|
|
3017
4714
|
if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
|
|
3018
4715
|
return "2.0";
|
|
3019
4716
|
}
|
|
@@ -3021,6 +4718,49 @@
|
|
|
3021
4718
|
}
|
|
3022
4719
|
return "1.0";
|
|
3023
4720
|
}
|
|
4721
|
+
function stripCommentsForSchemaDetection(source) {
|
|
4722
|
+
const chars = [...source];
|
|
4723
|
+
let inString = false;
|
|
4724
|
+
let inBlockComment = false;
|
|
4725
|
+
for (let index = 0; index < chars.length; index++) {
|
|
4726
|
+
const ch = chars[index];
|
|
4727
|
+
const next = chars[index + 1];
|
|
4728
|
+
if (inBlockComment) {
|
|
4729
|
+
if (ch === "*" && next === "/") {
|
|
4730
|
+
chars[index] = " ";
|
|
4731
|
+
chars[index + 1] = " ";
|
|
4732
|
+
inBlockComment = false;
|
|
4733
|
+
index++;
|
|
4734
|
+
continue;
|
|
4735
|
+
}
|
|
4736
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
4737
|
+
chars[index] = " ";
|
|
4738
|
+
}
|
|
4739
|
+
continue;
|
|
4740
|
+
}
|
|
4741
|
+
if (!inString && ch === "/" && next === "*") {
|
|
4742
|
+
chars[index] = " ";
|
|
4743
|
+
chars[index + 1] = " ";
|
|
4744
|
+
inBlockComment = true;
|
|
4745
|
+
index++;
|
|
4746
|
+
continue;
|
|
4747
|
+
}
|
|
4748
|
+
if (!inString && ch === "#") {
|
|
4749
|
+
chars[index] = " ";
|
|
4750
|
+
let inner = index + 1;
|
|
4751
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
4752
|
+
chars[inner] = " ";
|
|
4753
|
+
inner++;
|
|
4754
|
+
}
|
|
4755
|
+
index = inner - 1;
|
|
4756
|
+
continue;
|
|
4757
|
+
}
|
|
4758
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
4759
|
+
inString = !inString;
|
|
4760
|
+
}
|
|
4761
|
+
}
|
|
4762
|
+
return chars.join("");
|
|
4763
|
+
}
|
|
3024
4764
|
function loadWorldOrbitSource(source) {
|
|
3025
4765
|
const result = loadWorldOrbitSourceWithDiagnostics(source);
|
|
3026
4766
|
if (!result.ok || !result.value) {
|
|
@@ -3031,36 +4771,36 @@
|
|
|
3031
4771
|
}
|
|
3032
4772
|
function loadWorldOrbitSourceWithDiagnostics(source) {
|
|
3033
4773
|
const schemaVersion = detectWorldOrbitSchemaVersion(source);
|
|
3034
|
-
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft") {
|
|
4774
|
+
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1") {
|
|
3035
4775
|
return loadAtlasSourceWithDiagnostics(source, schemaVersion);
|
|
3036
4776
|
}
|
|
3037
4777
|
let ast;
|
|
3038
4778
|
try {
|
|
3039
4779
|
ast = parseWorldOrbit(source);
|
|
3040
|
-
} catch (
|
|
4780
|
+
} catch (error2) {
|
|
3041
4781
|
return {
|
|
3042
4782
|
ok: false,
|
|
3043
4783
|
value: null,
|
|
3044
|
-
diagnostics: [diagnosticFromError(
|
|
4784
|
+
diagnostics: [diagnosticFromError(error2, "parse")]
|
|
3045
4785
|
};
|
|
3046
4786
|
}
|
|
3047
4787
|
let document2;
|
|
3048
4788
|
try {
|
|
3049
4789
|
document2 = normalizeDocument(ast);
|
|
3050
|
-
} catch (
|
|
4790
|
+
} catch (error2) {
|
|
3051
4791
|
return {
|
|
3052
4792
|
ok: false,
|
|
3053
4793
|
value: null,
|
|
3054
|
-
diagnostics: [diagnosticFromError(
|
|
4794
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
3055
4795
|
};
|
|
3056
4796
|
}
|
|
3057
4797
|
try {
|
|
3058
4798
|
validateDocument(document2);
|
|
3059
|
-
} catch (
|
|
4799
|
+
} catch (error2) {
|
|
3060
4800
|
return {
|
|
3061
4801
|
ok: false,
|
|
3062
4802
|
value: null,
|
|
3063
|
-
diagnostics: [diagnosticFromError(
|
|
4803
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
3064
4804
|
};
|
|
3065
4805
|
}
|
|
3066
4806
|
return {
|
|
@@ -3080,30 +4820,29 @@
|
|
|
3080
4820
|
let atlasDocument;
|
|
3081
4821
|
try {
|
|
3082
4822
|
atlasDocument = parseWorldOrbitAtlas(source);
|
|
3083
|
-
} catch (
|
|
4823
|
+
} catch (error2) {
|
|
3084
4824
|
return {
|
|
3085
4825
|
ok: false,
|
|
3086
4826
|
value: null,
|
|
3087
|
-
diagnostics: [diagnosticFromError(
|
|
4827
|
+
diagnostics: [diagnosticFromError(error2, "parse", "load.atlas.failed")]
|
|
3088
4828
|
};
|
|
3089
4829
|
}
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
document2 = materializeAtlasDocument(atlasDocument);
|
|
3093
|
-
} catch (error) {
|
|
4830
|
+
const atlasDiagnostics = [...atlasDocument.diagnostics];
|
|
4831
|
+
if (atlasDiagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
3094
4832
|
return {
|
|
3095
4833
|
ok: false,
|
|
3096
4834
|
value: null,
|
|
3097
|
-
diagnostics:
|
|
4835
|
+
diagnostics: atlasDiagnostics
|
|
3098
4836
|
};
|
|
3099
4837
|
}
|
|
4838
|
+
let document2;
|
|
3100
4839
|
try {
|
|
3101
|
-
|
|
3102
|
-
} catch (
|
|
4840
|
+
document2 = materializeAtlasDocument(atlasDocument);
|
|
4841
|
+
} catch (error2) {
|
|
3103
4842
|
return {
|
|
3104
4843
|
ok: false,
|
|
3105
4844
|
value: null,
|
|
3106
|
-
diagnostics: [diagnosticFromError(
|
|
4845
|
+
diagnostics: [diagnosticFromError(error2, "normalize", "load.atlas.materialize.failed")]
|
|
3107
4846
|
};
|
|
3108
4847
|
}
|
|
3109
4848
|
const loaded = {
|
|
@@ -3112,12 +4851,12 @@
|
|
|
3112
4851
|
document: document2,
|
|
3113
4852
|
atlasDocument,
|
|
3114
4853
|
draftDocument: atlasDocument,
|
|
3115
|
-
diagnostics:
|
|
4854
|
+
diagnostics: atlasDiagnostics
|
|
3116
4855
|
};
|
|
3117
4856
|
return {
|
|
3118
4857
|
ok: true,
|
|
3119
4858
|
value: loaded,
|
|
3120
|
-
diagnostics:
|
|
4859
|
+
diagnostics: atlasDiagnostics
|
|
3121
4860
|
};
|
|
3122
4861
|
}
|
|
3123
4862
|
|
|
@@ -3125,6 +4864,8 @@
|
|
|
3125
4864
|
var DEFAULT_LAYERS = {
|
|
3126
4865
|
background: true,
|
|
3127
4866
|
guides: true,
|
|
4867
|
+
relations: true,
|
|
4868
|
+
events: true,
|
|
3128
4869
|
orbits: true,
|
|
3129
4870
|
objects: true,
|
|
3130
4871
|
labels: true,
|
|
@@ -3139,6 +4880,7 @@
|
|
|
3139
4880
|
backgroundGlow: "rgba(240, 180, 100, 0.18)",
|
|
3140
4881
|
panel: "rgba(7, 17, 27, 0.9)",
|
|
3141
4882
|
panelLine: "rgba(168, 207, 242, 0.18)",
|
|
4883
|
+
relation: "rgba(240, 180, 100, 0.42)",
|
|
3142
4884
|
orbit: "rgba(163, 209, 255, 0.24)",
|
|
3143
4885
|
orbitBand: "rgba(255, 190, 120, 0.28)",
|
|
3144
4886
|
guide: "rgba(255, 255, 255, 0.04)",
|
|
@@ -3161,6 +4903,7 @@
|
|
|
3161
4903
|
backgroundGlow: "rgba(120, 255, 215, 0.16)",
|
|
3162
4904
|
panel: "rgba(7, 20, 30, 0.9)",
|
|
3163
4905
|
panelLine: "rgba(120, 255, 215, 0.16)",
|
|
4906
|
+
relation: "rgba(156, 231, 255, 0.42)",
|
|
3164
4907
|
orbit: "rgba(120, 255, 215, 0.2)",
|
|
3165
4908
|
orbitBand: "rgba(137, 185, 255, 0.24)",
|
|
3166
4909
|
guide: "rgba(255, 255, 255, 0.035)",
|
|
@@ -3183,6 +4926,7 @@
|
|
|
3183
4926
|
backgroundGlow: "rgba(255, 127, 95, 0.18)",
|
|
3184
4927
|
panel: "rgba(24, 9, 13, 0.9)",
|
|
3185
4928
|
panelLine: "rgba(255, 166, 149, 0.16)",
|
|
4929
|
+
relation: "rgba(255, 178, 125, 0.42)",
|
|
3186
4930
|
orbit: "rgba(255, 188, 164, 0.22)",
|
|
3187
4931
|
orbitBand: "rgba(255, 214, 139, 0.24)",
|
|
3188
4932
|
guide: "rgba(255, 255, 255, 0.03)",
|
|
@@ -3264,7 +5008,11 @@
|
|
|
3264
5008
|
return false;
|
|
3265
5009
|
}
|
|
3266
5010
|
if (filter.groupIds?.length && (!object.groupId || !filter.groupIds.includes(object.groupId))) {
|
|
3267
|
-
|
|
5011
|
+
const hasSemanticMatch = object.semanticGroupIds.length > 0 && filter.groupIds.some((groupId) => object.semanticGroupIds.includes(groupId));
|
|
5012
|
+
const hasLegacyMatch = Boolean(object.groupId && filter.groupIds.includes(object.groupId));
|
|
5013
|
+
if (!hasSemanticMatch && !hasLegacyMatch) {
|
|
5014
|
+
return false;
|
|
5015
|
+
}
|
|
3268
5016
|
}
|
|
3269
5017
|
if (filter.tags?.length) {
|
|
3270
5018
|
const objectTags = Array.isArray(object.object.properties.tags) ? object.object.properties.tags.filter((entry) => typeof entry === "string") : [];
|
|
@@ -3320,6 +5068,8 @@
|
|
|
3320
5068
|
const imageDefinitions = buildImageDefinitions(visibleObjects);
|
|
3321
5069
|
const orbitMarkup = layers.orbits ? renderOrbitLayer(scene, visibleObjectIds, layers.structures) : { back: "", front: "" };
|
|
3322
5070
|
const leaderMarkup = layers.guides ? scene.leaders.filter((leader) => !leader.hidden).filter((leader) => visibleObjectIds.has(leader.objectId)).filter((leader) => layers.structures || !isStructureLike(leader.object)).map((leader) => `<line class="wo-leader wo-leader-${leader.mode}" x1="${leader.x1}" y1="${leader.y1}" x2="${leader.x2}" y2="${leader.y2}" data-render-id="${escapeXml(leader.renderId)}" data-group-id="${escapeAttribute(leader.groupId ?? "")}" />`).join("") : "";
|
|
5071
|
+
const relationMarkup = layers.relations ? scene.relations.filter((relation) => !relation.hidden).filter((relation) => visibleObjectIds.has(relation.fromObjectId) && visibleObjectIds.has(relation.toObjectId)).map((relation) => `<line class="wo-relation" x1="${relation.x1}" y1="${relation.y1}" x2="${relation.x2}" y2="${relation.y2}" data-render-id="${escapeXml(relation.renderId)}" data-relation-id="${escapeAttribute(relation.relationId)}" />`).join("") : "";
|
|
5072
|
+
const eventMarkup = layers.events ? scene.events.filter((event) => !event.hidden).map((event) => renderSceneEventOverlay(scene, event, visibleObjectIds, theme)).join("") : "";
|
|
3323
5073
|
const objectMarkup = layers.objects ? visibleObjects.map((object) => renderSceneObject(object, options.selectedObjectId ?? null, theme)).join("") : "";
|
|
3324
5074
|
const labelMarkup = layers.labels ? visibleLabels.map((label) => renderSceneLabel(scene, label, options.selectedObjectId ?? null)).join("") : "";
|
|
3325
5075
|
const metadataMarkup = layers.metadata ? `<text class="wo-title" x="56" y="64">${escapeXml(scene.title)}</text>
|
|
@@ -3354,6 +5104,10 @@
|
|
|
3354
5104
|
.wo-orbit-back { opacity: 0.38; stroke-dasharray: 8 6; }
|
|
3355
5105
|
.wo-orbit-front { opacity: 0.9; }
|
|
3356
5106
|
.wo-orbit-band { stroke: ${theme.orbitBand}; stroke-linecap: round; }
|
|
5107
|
+
.wo-relation { stroke: ${theme.relation}; stroke-width: 2; stroke-dasharray: 10 6; }
|
|
5108
|
+
.wo-event-line { stroke: ${theme.accent}; stroke-width: 1.6; stroke-dasharray: 5 5; opacity: 0.72; }
|
|
5109
|
+
.wo-event-node { fill: ${theme.accent}; stroke: ${theme.selected}; stroke-width: 1.4; opacity: 0.92; }
|
|
5110
|
+
.wo-event-label { fill: ${theme.accent}; font-family: ${theme.fontFamily}; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
|
|
3357
5111
|
.wo-leader { stroke: ${theme.leader}; stroke-width: 1.5; stroke-dasharray: 6 5; }
|
|
3358
5112
|
.wo-label { fill: ${theme.ink}; font-family: ${theme.fontFamily}; font-weight: 600; letter-spacing: 0.02em; }
|
|
3359
5113
|
.wo-label-secondary { fill: ${theme.muted}; font-family: ${theme.fontFamily}; font-weight: 500; }
|
|
@@ -3387,6 +5141,8 @@
|
|
|
3387
5141
|
<g data-worldorbit-world-content="true">
|
|
3388
5142
|
${layers.orbits ? `<g data-layer-id="orbits-back">${orbitMarkup.back}</g>` : ""}
|
|
3389
5143
|
${layers.guides ? `<g data-layer-id="guides">${leaderMarkup}</g>` : ""}
|
|
5144
|
+
${layers.relations ? `<g data-layer-id="relations">${relationMarkup}</g>` : ""}
|
|
5145
|
+
${layers.events ? `<g data-layer-id="events">${eventMarkup}</g>` : ""}
|
|
3390
5146
|
${layers.objects ? `<g data-layer-id="objects">${objectMarkup}</g>` : ""}
|
|
3391
5147
|
${layers.orbits ? `<g data-layer-id="orbits-front">${orbitMarkup.front}</g>` : ""}
|
|
3392
5148
|
${layers.labels ? `<g data-layer-id="labels">${labelMarkup}</g>` : ""}
|
|
@@ -3394,6 +5150,20 @@
|
|
|
3394
5150
|
</g>
|
|
3395
5151
|
</g>
|
|
3396
5152
|
</svg>`;
|
|
5153
|
+
}
|
|
5154
|
+
function renderSceneEventOverlay(scene, event, visibleObjectIds, theme) {
|
|
5155
|
+
const participants = event.objectIds.filter((objectId) => visibleObjectIds.has(objectId)).map((objectId) => scene.objects.find((object) => object.objectId === objectId && !object.hidden)).filter(Boolean);
|
|
5156
|
+
if (participants.length === 0) {
|
|
5157
|
+
return "";
|
|
5158
|
+
}
|
|
5159
|
+
const stroke = event.event.color || theme.accent;
|
|
5160
|
+
const label = event.event.label || event.event.id;
|
|
5161
|
+
const lineMarkup = participants.map((object) => `<line class="wo-event-line" x1="${event.x}" y1="${event.y}" x2="${object.x}" y2="${object.y}" stroke="${escapeAttribute(stroke)}" data-event-id="${escapeAttribute(event.eventId)}" data-object-id="${escapeAttribute(object.objectId)}" />`).join("");
|
|
5162
|
+
return `<g class="wo-event" data-render-id="${escapeXml(event.renderId)}" data-event-id="${escapeAttribute(event.eventId)}">
|
|
5163
|
+
${lineMarkup}
|
|
5164
|
+
<circle class="wo-event-node" cx="${event.x}" cy="${event.y}" r="5" fill="${escapeAttribute(stroke)}" />
|
|
5165
|
+
<text class="wo-event-label" x="${event.x}" y="${event.y - 10}" text-anchor="middle" font-size="10">${escapeXml(label)}</text>
|
|
5166
|
+
</g>`;
|
|
3397
5167
|
}
|
|
3398
5168
|
function renderOrbitLayer(scene, visibleObjectIds, includeStructures) {
|
|
3399
5169
|
const backParts = [];
|
|
@@ -3430,10 +5200,11 @@
|
|
|
3430
5200
|
function renderSceneObject(sceneObject, selectedObjectId, theme) {
|
|
3431
5201
|
const { object, x, y, radius, visualRadius } = sceneObject;
|
|
3432
5202
|
const selectionClass = selectedObjectId === sceneObject.objectId ? " wo-object-selected" : "";
|
|
5203
|
+
const kindClass = object.properties.kind ? ` wo-kind-${String(object.properties.kind).toLowerCase().replace(/[^a-z0-9-]/g, "-")}` : "";
|
|
3433
5204
|
const palette = resolveObjectPalette(sceneObject, theme);
|
|
3434
5205
|
const imageMarkup = renderObjectImage(sceneObject);
|
|
3435
5206
|
const outlineMarkup = imageMarkup ? renderObjectBody(object, x, y, radius, palette, { outlineOnly: true }) : "";
|
|
3436
|
-
return `<g class="wo-object wo-object-${object.type}${selectionClass}" data-object-id="${escapeXml(sceneObject.objectId)}" data-parent-id="${escapeAttribute(sceneObject.parentId ?? "")}" data-group-id="${escapeAttribute(sceneObject.groupId ?? "")}" data-render-id="${escapeXml(sceneObject.renderId)}" tabindex="0" role="button" aria-label="${escapeXml(`${object.type} ${sceneObject.objectId}`)}">
|
|
5207
|
+
return `<g class="wo-object wo-object-${object.type}${kindClass}${selectionClass}" data-object-id="${escapeXml(sceneObject.objectId)}" data-parent-id="${escapeAttribute(sceneObject.parentId ?? "")}" data-group-id="${escapeAttribute(sceneObject.groupId ?? "")}" data-render-id="${escapeXml(sceneObject.renderId)}" tabindex="0" role="button" aria-label="${escapeXml(`${object.type} ${sceneObject.objectId}`)}">
|
|
3437
5208
|
<circle class="wo-selection-ring" cx="${x}" cy="${y}" r="${visualRadius + 8}" />
|
|
3438
5209
|
${renderAtmosphere(sceneObject, palette)}
|
|
3439
5210
|
${renderObjectBody(object, x, y, radius, palette)}
|
|
@@ -3467,8 +5238,33 @@
|
|
|
3467
5238
|
<circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
3468
5239
|
case "structure":
|
|
3469
5240
|
return `<polygon points="${diamondPoints(x, y, radius)}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
3470
|
-
case "phenomenon":
|
|
5241
|
+
case "phenomenon": {
|
|
5242
|
+
const kind = String(object.properties.kind ?? "").toLowerCase().replace(/_/g, "-");
|
|
5243
|
+
if (options.outlineOnly) {
|
|
5244
|
+
if (kind === "black-hole" || kind === "nebula" || kind === "galaxy" || kind === "dwarf-galaxy") {
|
|
5245
|
+
return `<circle cx="${x}" cy="${y}" r="${radius}" fill="transparent" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
5246
|
+
}
|
|
5247
|
+
return `<polygon points="${phenomenonPoints(x, y, radius)}" fill="transparent" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
5248
|
+
}
|
|
5249
|
+
if (kind === "black-hole") {
|
|
5250
|
+
return `<ellipse cx="${x}" cy="${y}" rx="${radius * 2.4}" ry="${radius * 0.55}" fill="none" stroke="${palette.accentRing ?? palette.stroke}" stroke-width="3.5" />
|
|
5251
|
+
<circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" stroke="${palette.stroke}" stroke-width="2" />`;
|
|
5252
|
+
}
|
|
5253
|
+
if (kind === "galaxy") {
|
|
5254
|
+
return `<ellipse cx="${x}" cy="${y}" rx="${radius * 2.6}" ry="${radius}" fill="${palette.halo ?? "none"}" stroke="none" />
|
|
5255
|
+
<ellipse cx="${x}" cy="${y}" rx="${radius * 1.5}" ry="${radius * 0.42}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.2" />
|
|
5256
|
+
<circle cx="${x}" cy="${y}" r="${radius * 0.28}" fill="${palette.core ?? "#fff"}" stroke="none" />`;
|
|
5257
|
+
}
|
|
5258
|
+
if (kind === "dwarf-galaxy") {
|
|
5259
|
+
return `<ellipse cx="${x}" cy="${y}" rx="${radius * 1.6}" ry="${radius * 0.55}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1" />
|
|
5260
|
+
<circle cx="${x}" cy="${y}" r="${radius * 0.25}" fill="${palette.core ?? "#fff"}" stroke="none" />`;
|
|
5261
|
+
}
|
|
5262
|
+
if (kind === "nebula") {
|
|
5263
|
+
return `<circle cx="${x}" cy="${y}" r="${radius * 2.2}" fill="${palette.halo ?? "none"}" stroke="none" />
|
|
5264
|
+
<circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1" />`;
|
|
5265
|
+
}
|
|
3471
5266
|
return `<polygon points="${phenomenonPoints(x, y, radius)}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
5267
|
+
}
|
|
3472
5268
|
}
|
|
3473
5269
|
}
|
|
3474
5270
|
function renderAtmosphere(sceneObject, palette) {
|
|
@@ -3537,7 +5333,8 @@
|
|
|
3537
5333
|
}
|
|
3538
5334
|
}
|
|
3539
5335
|
function resolveObjectPalette(sceneObject, theme) {
|
|
3540
|
-
const
|
|
5336
|
+
const kind = String(sceneObject.object.properties.kind ?? "").toLowerCase().replace(/_/g, "-");
|
|
5337
|
+
const base = basePaletteForType(sceneObject.object.type, kind, theme);
|
|
3541
5338
|
const customFill = sceneObject.fillColor && isColorLike(sceneObject.fillColor) ? sceneObject.fillColor : base.fill;
|
|
3542
5339
|
const albedo = numericValue(sceneObject.object.properties.albedo);
|
|
3543
5340
|
const temperature = numericValue(sceneObject.object.properties.temperature);
|
|
@@ -3553,7 +5350,7 @@
|
|
|
3553
5350
|
tail: sceneObject.object.type === "comet" ? rgbaString(mixColors(fill, "#ffffff", 0.5) ?? fill, 0.72) : void 0
|
|
3554
5351
|
};
|
|
3555
5352
|
}
|
|
3556
|
-
function basePaletteForType(type, theme) {
|
|
5353
|
+
function basePaletteForType(type, kind, theme) {
|
|
3557
5354
|
switch (type) {
|
|
3558
5355
|
case "star":
|
|
3559
5356
|
return {
|
|
@@ -3575,8 +5372,26 @@
|
|
|
3575
5372
|
case "structure":
|
|
3576
5373
|
return { fill: theme.accentStrong, stroke: "#fff2ea" };
|
|
3577
5374
|
case "phenomenon":
|
|
3578
|
-
return
|
|
5375
|
+
return kindPhenomenonPalette(kind);
|
|
5376
|
+
}
|
|
5377
|
+
}
|
|
5378
|
+
function kindPhenomenonPalette(kind) {
|
|
5379
|
+
if (kind === "galaxy") {
|
|
5380
|
+
return { fill: "rgba(165,125,255,0.55)", stroke: "rgba(210,185,255,0.75)", halo: "rgba(160,120,255,0.10)", core: "#ede0ff" };
|
|
5381
|
+
}
|
|
5382
|
+
if (kind === "dwarf-galaxy") {
|
|
5383
|
+
return { fill: "rgba(190,165,255,0.45)", stroke: "rgba(220,205,255,0.75)", core: "#ddd0ff" };
|
|
5384
|
+
}
|
|
5385
|
+
if (kind === "black-hole") {
|
|
5386
|
+
return { fill: "#040408", stroke: "#ff6a00", accentRing: "rgba(255,140,20,0.72)" };
|
|
5387
|
+
}
|
|
5388
|
+
if (kind === "nebula") {
|
|
5389
|
+
return { fill: "rgba(105,205,255,0.45)", stroke: "rgba(180,235,255,0.72)", halo: "rgba(100,200,255,0.08)" };
|
|
5390
|
+
}
|
|
5391
|
+
if (kind === "void") {
|
|
5392
|
+
return { fill: "#05080f", stroke: "rgba(130,160,255,0.4)" };
|
|
3579
5393
|
}
|
|
5394
|
+
return { fill: "#78ffd7", stroke: "#e9fff7" };
|
|
3580
5395
|
}
|
|
3581
5396
|
function applyTemperatureAndAlbedo(baseColor, temperature, albedo, type) {
|
|
3582
5397
|
let nextColor = baseColor;
|
|
@@ -3843,11 +5658,11 @@
|
|
|
3843
5658
|
});
|
|
3844
5659
|
}
|
|
3845
5660
|
return `<figure class="${escapeAttribute3(options.className ?? "worldorbit-block worldorbit-static")}">${renderSceneToSvg(scene, options)}</figure>`;
|
|
3846
|
-
} catch (
|
|
5661
|
+
} catch (error2) {
|
|
3847
5662
|
if (options.strict) {
|
|
3848
|
-
throw
|
|
5663
|
+
throw error2;
|
|
3849
5664
|
}
|
|
3850
|
-
return renderWorldOrbitError(
|
|
5665
|
+
return renderWorldOrbitError(error2 instanceof Error ? error2.message : String(error2));
|
|
3851
5666
|
}
|
|
3852
5667
|
}
|
|
3853
5668
|
function renderWorldOrbitError(message) {
|