worldorbit 2.5.15 → 2.5.16
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 +1811 -386
- package/dist/browser/editor/dist/index.js +10534 -0
- package/dist/browser/markdown/dist/index.js +1477 -221
- package/dist/browser/viewer/dist/index.js +1569 -230
- package/dist/unpkg/core/dist/index.js +1814 -389
- package/dist/unpkg/editor/dist/index.js +10559 -0
- package/dist/unpkg/markdown/dist/index.js +1480 -224
- package/dist/unpkg/viewer/dist/index.js +1572 -233
- package/dist/unpkg/worldorbit-editor.min.js +812 -0
- package/package.json +3 -2
- package/packages/editor/dist/editor.d.ts +2 -0
- package/packages/editor/dist/editor.js +3042 -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 +53 -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
|
@@ -302,13 +302,13 @@
|
|
|
302
302
|
function unitFamilyAllowsUnit(family, unit) {
|
|
303
303
|
switch (family) {
|
|
304
304
|
case "distance":
|
|
305
|
-
return unit === null || ["au", "km", "re", "sol"].includes(unit);
|
|
305
|
+
return unit === null || ["au", "km", "m", "ly", "pc", "kpc", "re", "sol"].includes(unit);
|
|
306
306
|
case "radius":
|
|
307
|
-
return unit === null || ["km", "re", "sol"].includes(unit);
|
|
307
|
+
return unit === null || ["km", "m", "re", "rj", "sol"].includes(unit);
|
|
308
308
|
case "mass":
|
|
309
|
-
return unit === null || ["me", "sol"].includes(unit);
|
|
309
|
+
return unit === null || ["me", "mj", "sol"].includes(unit);
|
|
310
310
|
case "duration":
|
|
311
|
-
return unit === null || ["h", "d", "y"].includes(unit);
|
|
311
|
+
return unit === null || ["s", "min", "h", "d", "y", "ky", "my", "gy"].includes(unit);
|
|
312
312
|
case "angle":
|
|
313
313
|
return unit === null || unit === "deg";
|
|
314
314
|
case "generic":
|
|
@@ -515,7 +515,7 @@
|
|
|
515
515
|
}
|
|
516
516
|
|
|
517
517
|
// packages/core/dist/normalize.js
|
|
518
|
-
var UNIT_PATTERN = /^(-?\d+(?:\.\d+)?)(au|km|re|sol|
|
|
518
|
+
var UNIT_PATTERN = /^(-?\d+(?:\.\d+)?)(kpc|min|mj|rj|ky|my|gy|au|km|me|re|pc|ly|deg|sol|K|m|s|h|d|y)?$/;
|
|
519
519
|
var BOOLEAN_VALUES = /* @__PURE__ */ new Map([
|
|
520
520
|
["true", true],
|
|
521
521
|
["false", false],
|
|
@@ -540,7 +540,10 @@
|
|
|
540
540
|
return {
|
|
541
541
|
format: "worldorbit",
|
|
542
542
|
version: "1.0",
|
|
543
|
+
schemaVersion: "1.0",
|
|
543
544
|
system,
|
|
545
|
+
groups: [],
|
|
546
|
+
relations: [],
|
|
544
547
|
objects
|
|
545
548
|
};
|
|
546
549
|
}
|
|
@@ -550,13 +553,17 @@
|
|
|
550
553
|
const fieldMap = collectFields(mergedFields);
|
|
551
554
|
const placement = extractPlacement(node.objectType, fieldMap);
|
|
552
555
|
const properties = normalizeProperties(fieldMap);
|
|
553
|
-
const
|
|
556
|
+
const info2 = normalizeInfo(node.infoEntries);
|
|
554
557
|
if (node.objectType === "system") {
|
|
555
558
|
return {
|
|
556
559
|
type: "system",
|
|
557
560
|
id: node.name,
|
|
561
|
+
title: typeof properties.title === "string" ? properties.title : null,
|
|
562
|
+
description: null,
|
|
563
|
+
epoch: null,
|
|
564
|
+
referencePlane: null,
|
|
558
565
|
properties,
|
|
559
|
-
info
|
|
566
|
+
info: info2
|
|
560
567
|
};
|
|
561
568
|
}
|
|
562
569
|
return {
|
|
@@ -564,7 +571,7 @@
|
|
|
564
571
|
id: node.name,
|
|
565
572
|
properties,
|
|
566
573
|
placement,
|
|
567
|
-
info
|
|
574
|
+
info: info2
|
|
568
575
|
};
|
|
569
576
|
}
|
|
570
577
|
function validateFieldCompatibility(objectType, fields) {
|
|
@@ -694,14 +701,14 @@
|
|
|
694
701
|
}
|
|
695
702
|
}
|
|
696
703
|
function normalizeInfo(entries) {
|
|
697
|
-
const
|
|
704
|
+
const info2 = {};
|
|
698
705
|
for (const entry of entries) {
|
|
699
|
-
if (entry.key in
|
|
706
|
+
if (entry.key in info2) {
|
|
700
707
|
throw WorldOrbitError.fromLocation(`Duplicate info key "${entry.key}"`, entry.location);
|
|
701
708
|
}
|
|
702
|
-
|
|
709
|
+
info2[entry.key] = entry.value;
|
|
703
710
|
}
|
|
704
|
-
return
|
|
711
|
+
return info2;
|
|
705
712
|
}
|
|
706
713
|
function parseAtReference(target, location) {
|
|
707
714
|
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
@@ -875,38 +882,38 @@
|
|
|
875
882
|
function createDiagnostic(diagnostic) {
|
|
876
883
|
return { ...diagnostic };
|
|
877
884
|
}
|
|
878
|
-
function diagnosticFromError(
|
|
879
|
-
if (
|
|
885
|
+
function diagnosticFromError(error2, source, code = `${source}.failed`) {
|
|
886
|
+
if (error2 instanceof WorldOrbitError) {
|
|
880
887
|
return {
|
|
881
888
|
code,
|
|
882
889
|
severity: "error",
|
|
883
890
|
source,
|
|
884
|
-
message:
|
|
885
|
-
line:
|
|
886
|
-
column:
|
|
891
|
+
message: error2.message,
|
|
892
|
+
line: error2.line,
|
|
893
|
+
column: error2.column
|
|
887
894
|
};
|
|
888
895
|
}
|
|
889
|
-
if (
|
|
896
|
+
if (error2 instanceof Error) {
|
|
890
897
|
return {
|
|
891
898
|
code,
|
|
892
899
|
severity: "error",
|
|
893
900
|
source,
|
|
894
|
-
message:
|
|
901
|
+
message: error2.message
|
|
895
902
|
};
|
|
896
903
|
}
|
|
897
904
|
return {
|
|
898
905
|
code,
|
|
899
906
|
severity: "error",
|
|
900
907
|
source,
|
|
901
|
-
message: String(
|
|
908
|
+
message: String(error2)
|
|
902
909
|
};
|
|
903
910
|
}
|
|
904
911
|
function parseWithDiagnostics(source) {
|
|
905
912
|
let ast;
|
|
906
913
|
try {
|
|
907
914
|
ast = parseWorldOrbit(source);
|
|
908
|
-
} catch (
|
|
909
|
-
const diagnostic = diagnosticFromError(
|
|
915
|
+
} catch (error2) {
|
|
916
|
+
const diagnostic = diagnosticFromError(error2, "parse");
|
|
910
917
|
return {
|
|
911
918
|
ok: false,
|
|
912
919
|
value: null,
|
|
@@ -916,20 +923,20 @@
|
|
|
916
923
|
let document;
|
|
917
924
|
try {
|
|
918
925
|
document = normalizeDocument(ast);
|
|
919
|
-
} catch (
|
|
926
|
+
} catch (error2) {
|
|
920
927
|
return {
|
|
921
928
|
ok: false,
|
|
922
929
|
value: null,
|
|
923
|
-
diagnostics: [diagnosticFromError(
|
|
930
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
924
931
|
};
|
|
925
932
|
}
|
|
926
933
|
try {
|
|
927
934
|
validateDocument(document);
|
|
928
|
-
} catch (
|
|
935
|
+
} catch (error2) {
|
|
929
936
|
return {
|
|
930
937
|
ok: false,
|
|
931
938
|
value: null,
|
|
932
|
-
diagnostics: [diagnosticFromError(
|
|
939
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
933
940
|
};
|
|
934
941
|
}
|
|
935
942
|
return {
|
|
@@ -948,11 +955,11 @@
|
|
|
948
955
|
value: normalizeDocument(ast),
|
|
949
956
|
diagnostics: []
|
|
950
957
|
};
|
|
951
|
-
} catch (
|
|
958
|
+
} catch (error2) {
|
|
952
959
|
return {
|
|
953
960
|
ok: false,
|
|
954
961
|
value: null,
|
|
955
|
-
diagnostics: [diagnosticFromError(
|
|
962
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
956
963
|
};
|
|
957
964
|
}
|
|
958
965
|
}
|
|
@@ -964,11 +971,11 @@
|
|
|
964
971
|
value: document,
|
|
965
972
|
diagnostics: []
|
|
966
973
|
};
|
|
967
|
-
} catch (
|
|
974
|
+
} catch (error2) {
|
|
968
975
|
return {
|
|
969
976
|
ok: false,
|
|
970
977
|
value: null,
|
|
971
|
-
diagnostics: [diagnosticFromError(
|
|
978
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
972
979
|
};
|
|
973
980
|
}
|
|
974
981
|
}
|
|
@@ -976,7 +983,11 @@
|
|
|
976
983
|
// packages/core/dist/scene.js
|
|
977
984
|
var AU_IN_KM = 1495978707e-1;
|
|
978
985
|
var EARTH_RADIUS_IN_KM = 6371;
|
|
986
|
+
var JUPITER_RADIUS_IN_KM = 71492;
|
|
979
987
|
var SOLAR_RADIUS_IN_KM = 695700;
|
|
988
|
+
var LY_IN_AU = 63241.077;
|
|
989
|
+
var PC_IN_AU = 206264.806;
|
|
990
|
+
var KPC_IN_AU = 206264806;
|
|
980
991
|
var ISO_FLATTENING = 0.68;
|
|
981
992
|
var MIN_ISO_MINOR_SCALE = 0.2;
|
|
982
993
|
var ARC_SAMPLE_COUNT = 28;
|
|
@@ -1096,8 +1107,10 @@
|
|
|
1096
1107
|
const orbitVisuals = orbitDrafts.map((draft) => createOrbitVisual(draft, relationships.groupIds.get(draft.object.id) ?? null));
|
|
1097
1108
|
const leaders = leaderDrafts.map((draft) => createLeaderLine(draft));
|
|
1098
1109
|
const labels = createSceneLabels(objects, height, scaleModel.labelMultiplier);
|
|
1099
|
-
const
|
|
1110
|
+
const relations = createSceneRelations(document, objects);
|
|
1111
|
+
const layers = createSceneLayers(orbitVisuals, relations, leaders, objects, labels);
|
|
1100
1112
|
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships);
|
|
1113
|
+
const semanticGroups = createSceneSemanticGroups(document, objects);
|
|
1101
1114
|
const viewpoints = createSceneViewpoints(document, projection, frame.preset, relationships, objectMap);
|
|
1102
1115
|
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels);
|
|
1103
1116
|
return {
|
|
@@ -1107,7 +1120,7 @@
|
|
|
1107
1120
|
renderPreset: frame.preset,
|
|
1108
1121
|
projection,
|
|
1109
1122
|
scaleModel,
|
|
1110
|
-
title: String(document.system?.properties.title ?? document.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1123
|
+
title: String(document.system?.title ?? document.system?.properties.title ?? document.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1111
1124
|
subtitle: `${capitalizeLabel(projection)} view - ${capitalizeLabel(layoutPreset)} layout`,
|
|
1112
1125
|
systemId,
|
|
1113
1126
|
viewMode: projection,
|
|
@@ -1123,9 +1136,11 @@
|
|
|
1123
1136
|
contentBounds,
|
|
1124
1137
|
layers,
|
|
1125
1138
|
groups,
|
|
1139
|
+
semanticGroups,
|
|
1126
1140
|
viewpoints,
|
|
1127
1141
|
objects,
|
|
1128
1142
|
orbitVisuals,
|
|
1143
|
+
relations,
|
|
1129
1144
|
leaders,
|
|
1130
1145
|
labels
|
|
1131
1146
|
};
|
|
@@ -1235,6 +1250,7 @@
|
|
|
1235
1250
|
}
|
|
1236
1251
|
function createSceneObject(position, scaleModel, relationships) {
|
|
1237
1252
|
const { object, x, y, radius, sortKey, anchorX, anchorY } = position;
|
|
1253
|
+
const renderPriority = object.renderHints?.renderPriority ?? 0;
|
|
1238
1254
|
return {
|
|
1239
1255
|
renderId: createRenderId(object.id),
|
|
1240
1256
|
objectId: object.id,
|
|
@@ -1243,11 +1259,12 @@
|
|
|
1243
1259
|
ancestorIds: relationships.ancestorIds.get(object.id) ?? [],
|
|
1244
1260
|
childIds: relationships.childIds.get(object.id) ?? [],
|
|
1245
1261
|
groupId: relationships.groupIds.get(object.id) ?? null,
|
|
1262
|
+
semanticGroupIds: [...object.groups ?? []],
|
|
1246
1263
|
x,
|
|
1247
1264
|
y,
|
|
1248
1265
|
radius,
|
|
1249
1266
|
visualRadius: visualExtentForObject(object, radius, scaleModel),
|
|
1250
|
-
sortKey,
|
|
1267
|
+
sortKey: sortKey + renderPriority * 1e-3,
|
|
1251
1268
|
anchorX,
|
|
1252
1269
|
anchorY,
|
|
1253
1270
|
label: object.id,
|
|
@@ -1264,6 +1281,7 @@
|
|
|
1264
1281
|
object: draft.object,
|
|
1265
1282
|
parentId: draft.parentId,
|
|
1266
1283
|
groupId,
|
|
1284
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1267
1285
|
kind: draft.kind,
|
|
1268
1286
|
cx: draft.cx,
|
|
1269
1287
|
cy: draft.cy,
|
|
@@ -1275,7 +1293,7 @@
|
|
|
1275
1293
|
bandThickness: draft.bandThickness,
|
|
1276
1294
|
frontArcPath: draft.frontArcPath,
|
|
1277
1295
|
backArcPath: draft.backArcPath,
|
|
1278
|
-
hidden: draft.object.properties.hidden === true
|
|
1296
|
+
hidden: draft.object.properties.hidden === true || draft.object.renderHints?.renderOrbit === false
|
|
1279
1297
|
};
|
|
1280
1298
|
}
|
|
1281
1299
|
function createLeaderLine(draft) {
|
|
@@ -1284,6 +1302,7 @@
|
|
|
1284
1302
|
objectId: draft.object.id,
|
|
1285
1303
|
object: draft.object,
|
|
1286
1304
|
groupId: draft.groupId,
|
|
1305
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1287
1306
|
x1: draft.x1,
|
|
1288
1307
|
y1: draft.y1,
|
|
1289
1308
|
x2: draft.x2,
|
|
@@ -1295,7 +1314,7 @@
|
|
|
1295
1314
|
function createSceneLabels(objects, sceneHeight, labelMultiplier) {
|
|
1296
1315
|
const labels = [];
|
|
1297
1316
|
const occupied = [];
|
|
1298
|
-
const visibleObjects = [...objects].filter((object) => !object.hidden).sort((left, right) => left.sortKey - right.sortKey);
|
|
1317
|
+
const visibleObjects = [...objects].filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false).sort((left, right) => left.sortKey - right.sortKey);
|
|
1299
1318
|
for (const object of visibleObjects) {
|
|
1300
1319
|
const direction = object.y > sceneHeight * 0.62 ? -1 : 1;
|
|
1301
1320
|
const labelHalfWidth = estimateLabelHalfWidth(object, labelMultiplier);
|
|
@@ -1315,6 +1334,7 @@
|
|
|
1315
1334
|
objectId: object.objectId,
|
|
1316
1335
|
object: object.object,
|
|
1317
1336
|
groupId: object.groupId,
|
|
1337
|
+
semanticGroupIds: [...object.semanticGroupIds],
|
|
1318
1338
|
label: object.label,
|
|
1319
1339
|
secondaryLabel: object.secondaryLabel,
|
|
1320
1340
|
x: object.x,
|
|
@@ -1327,7 +1347,7 @@
|
|
|
1327
1347
|
}
|
|
1328
1348
|
return labels;
|
|
1329
1349
|
}
|
|
1330
|
-
function createSceneLayers(orbitVisuals, leaders, objects, labels) {
|
|
1350
|
+
function createSceneLayers(orbitVisuals, relations, leaders, objects, labels) {
|
|
1331
1351
|
const backOrbitIds = orbitVisuals.filter((visual) => !visual.hidden && Boolean(visual.backArcPath)).map((visual) => visual.renderId);
|
|
1332
1352
|
const frontOrbitIds = orbitVisuals.filter((visual) => !visual.hidden).map((visual) => visual.renderId);
|
|
1333
1353
|
return [
|
|
@@ -1338,6 +1358,10 @@
|
|
|
1338
1358
|
},
|
|
1339
1359
|
{ id: "orbits-back", renderIds: backOrbitIds },
|
|
1340
1360
|
{ id: "orbits-front", renderIds: frontOrbitIds },
|
|
1361
|
+
{
|
|
1362
|
+
id: "relations",
|
|
1363
|
+
renderIds: relations.filter((relation) => !relation.hidden).map((relation) => relation.renderId)
|
|
1364
|
+
},
|
|
1341
1365
|
{
|
|
1342
1366
|
id: "objects",
|
|
1343
1367
|
renderIds: objects.filter((object) => !object.hidden).map((object) => object.renderId)
|
|
@@ -1402,6 +1426,36 @@
|
|
|
1402
1426
|
}
|
|
1403
1427
|
return [...groups.values()].sort((left, right) => left.label.localeCompare(right.label));
|
|
1404
1428
|
}
|
|
1429
|
+
function createSceneSemanticGroups(document, objects) {
|
|
1430
|
+
return [...document.groups].map((group) => ({
|
|
1431
|
+
id: group.id,
|
|
1432
|
+
label: group.label,
|
|
1433
|
+
summary: group.summary,
|
|
1434
|
+
color: group.color,
|
|
1435
|
+
tags: [...group.tags],
|
|
1436
|
+
hidden: group.hidden,
|
|
1437
|
+
objectIds: objects.filter((object) => !object.hidden && object.semanticGroupIds.includes(group.id)).map((object) => object.objectId)
|
|
1438
|
+
})).sort((left, right) => left.label.localeCompare(right.label));
|
|
1439
|
+
}
|
|
1440
|
+
function createSceneRelations(document, objects) {
|
|
1441
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1442
|
+
return document.relations.map((relation) => {
|
|
1443
|
+
const from = objectMap.get(relation.from);
|
|
1444
|
+
const to = objectMap.get(relation.to);
|
|
1445
|
+
return {
|
|
1446
|
+
renderId: `${createRenderId(relation.id)}-relation`,
|
|
1447
|
+
relationId: relation.id,
|
|
1448
|
+
relation,
|
|
1449
|
+
fromObjectId: relation.from,
|
|
1450
|
+
toObjectId: relation.to,
|
|
1451
|
+
x1: from?.x ?? 0,
|
|
1452
|
+
y1: from?.y ?? 0,
|
|
1453
|
+
x2: to?.x ?? 0,
|
|
1454
|
+
y2: to?.y ?? 0,
|
|
1455
|
+
hidden: relation.hidden || !from || !to || from.hidden || to.hidden
|
|
1456
|
+
};
|
|
1457
|
+
}).sort((left, right) => left.relation.id.localeCompare(right.relation.id));
|
|
1458
|
+
}
|
|
1405
1459
|
function createSceneViewpoints(document, projection, preset, relationships, objectMap) {
|
|
1406
1460
|
const generatedOverview = createGeneratedOverviewViewpoint(document, projection, preset);
|
|
1407
1461
|
const drafts = /* @__PURE__ */ new Map();
|
|
@@ -1419,7 +1473,7 @@
|
|
|
1419
1473
|
}
|
|
1420
1474
|
const field = fieldParts.join(".").toLowerCase();
|
|
1421
1475
|
const draft = drafts.get(id) ?? { id };
|
|
1422
|
-
applyViewpointField(draft, field, value, projection, preset, relationships, objectMap);
|
|
1476
|
+
applyViewpointField(draft, field, value, document, projection, preset, relationships, objectMap);
|
|
1423
1477
|
drafts.set(id, draft);
|
|
1424
1478
|
}
|
|
1425
1479
|
const viewpoints = [...drafts.values()].map((draft) => finalizeViewpointDraft(draft, projection, preset, objectMap)).filter(Boolean);
|
|
@@ -1447,7 +1501,8 @@
|
|
|
1447
1501
|
});
|
|
1448
1502
|
}
|
|
1449
1503
|
function createGeneratedOverviewViewpoint(document, projection, preset) {
|
|
1450
|
-
const
|
|
1504
|
+
const title = document.system?.title ?? document.system?.properties.title;
|
|
1505
|
+
const label = title ? `${String(title)} Overview` : "Overview";
|
|
1451
1506
|
return {
|
|
1452
1507
|
id: "overview",
|
|
1453
1508
|
label,
|
|
@@ -1463,7 +1518,7 @@
|
|
|
1463
1518
|
generated: true
|
|
1464
1519
|
};
|
|
1465
1520
|
}
|
|
1466
|
-
function applyViewpointField(draft, field, value, projection, preset, relationships, objectMap) {
|
|
1521
|
+
function applyViewpointField(draft, field, value, document, projection, preset, relationships, objectMap) {
|
|
1467
1522
|
const normalizedValue = value.trim();
|
|
1468
1523
|
switch (field) {
|
|
1469
1524
|
case "label":
|
|
@@ -1530,7 +1585,7 @@
|
|
|
1530
1585
|
case "groups":
|
|
1531
1586
|
draft.filter = {
|
|
1532
1587
|
...draft.filter ?? createEmptyViewpointFilter(),
|
|
1533
|
-
groupIds: parseViewpointGroups(normalizedValue, relationships, objectMap)
|
|
1588
|
+
groupIds: parseViewpointGroups(normalizedValue, document, relationships, objectMap)
|
|
1534
1589
|
};
|
|
1535
1590
|
return;
|
|
1536
1591
|
}
|
|
@@ -1603,7 +1658,7 @@
|
|
|
1603
1658
|
next["orbits-front"] = enabled;
|
|
1604
1659
|
continue;
|
|
1605
1660
|
}
|
|
1606
|
-
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1661
|
+
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "relations" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1607
1662
|
next[rawLayer] = enabled;
|
|
1608
1663
|
}
|
|
1609
1664
|
}
|
|
@@ -1612,8 +1667,11 @@
|
|
|
1612
1667
|
function parseViewpointObjectTypes(value) {
|
|
1613
1668
|
return splitListValue(value).filter((entry) => entry === "star" || entry === "planet" || entry === "moon" || entry === "belt" || entry === "asteroid" || entry === "comet" || entry === "ring" || entry === "structure" || entry === "phenomenon");
|
|
1614
1669
|
}
|
|
1615
|
-
function parseViewpointGroups(value, relationships, objectMap) {
|
|
1670
|
+
function parseViewpointGroups(value, document, relationships, objectMap) {
|
|
1616
1671
|
return splitListValue(value).map((entry) => {
|
|
1672
|
+
if (document.schemaVersion === "2.1" || document.groups.some((group) => group.id === entry)) {
|
|
1673
|
+
return entry;
|
|
1674
|
+
}
|
|
1617
1675
|
if (entry.startsWith("wo-") && entry.endsWith("-group")) {
|
|
1618
1676
|
return entry;
|
|
1619
1677
|
}
|
|
@@ -1744,8 +1802,9 @@
|
|
|
1744
1802
|
}
|
|
1745
1803
|
const orbiting = [...context.orbitChildren.get(object.id) ?? []].sort(compareOrbiting);
|
|
1746
1804
|
const orbitMetricContext = computeOrbitMetricContext(orbiting, parent.radius, context.spacingFactor, context.scaleModel);
|
|
1805
|
+
const orbitRadiiPx = resolveOrbitRadiiPx(orbiting, orbitMetricContext);
|
|
1747
1806
|
orbiting.forEach((child, index) => {
|
|
1748
|
-
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, context);
|
|
1807
|
+
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, orbitRadiiPx[index] ?? orbitMetricContext.innerPx, context);
|
|
1749
1808
|
orbitDrafts.push({
|
|
1750
1809
|
object: child,
|
|
1751
1810
|
parentId: object.id,
|
|
@@ -1819,7 +1878,8 @@
|
|
|
1819
1878
|
metricSpread: 0,
|
|
1820
1879
|
innerPx,
|
|
1821
1880
|
stepPx,
|
|
1822
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
1881
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
1882
|
+
minimumGapPx: stepPx * 0.42
|
|
1823
1883
|
};
|
|
1824
1884
|
}
|
|
1825
1885
|
const minMetric = Math.min(...presentMetrics);
|
|
@@ -1832,10 +1892,11 @@
|
|
|
1832
1892
|
metricSpread,
|
|
1833
1893
|
innerPx,
|
|
1834
1894
|
stepPx,
|
|
1835
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
1895
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
1896
|
+
minimumGapPx: stepPx * 0.42
|
|
1836
1897
|
};
|
|
1837
1898
|
}
|
|
1838
|
-
function resolveOrbitGeometry(object, index, count, parent, metricContext, context) {
|
|
1899
|
+
function resolveOrbitGeometry(object, index, count, parent, metricContext, orbitRadiusPx, context) {
|
|
1839
1900
|
const placement = object.placement;
|
|
1840
1901
|
const band = object.type === "belt" || object.type === "ring";
|
|
1841
1902
|
if (!placement || placement.mode !== "orbit") {
|
|
@@ -1853,7 +1914,7 @@
|
|
|
1853
1914
|
};
|
|
1854
1915
|
}
|
|
1855
1916
|
const eccentricity = clampNumber(typeof placement.eccentricity === "number" ? placement.eccentricity : 0, 0, 0.92);
|
|
1856
|
-
const semiMajor =
|
|
1917
|
+
const semiMajor = orbitRadiusPx;
|
|
1857
1918
|
const baseMinor = Math.max(semiMajor * Math.sqrt(1 - eccentricity * eccentricity), semiMajor * 0.18);
|
|
1858
1919
|
const inclinationDeg = unitValueToDegrees(placement.inclination) ?? 0;
|
|
1859
1920
|
const inclinationScale = context.projection === "isometric" ? Math.max(MIN_ISO_MINOR_SCALE, Math.cos(degreesToRadians(inclinationDeg))) * ISO_FLATTENING : 1;
|
|
@@ -1883,15 +1944,19 @@
|
|
|
1883
1944
|
objectY: objectPoint.y
|
|
1884
1945
|
};
|
|
1885
1946
|
}
|
|
1886
|
-
function resolveOrbitRadiusPx(
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1947
|
+
function resolveOrbitRadiusPx(metric, metricContext) {
|
|
1948
|
+
return metricContext.innerPx + metricContext.stepPx * log2(Math.max(metric, 0) + 1);
|
|
1949
|
+
}
|
|
1950
|
+
function resolveOrbitRadiiPx(objects, metricContext) {
|
|
1951
|
+
const radii = [];
|
|
1952
|
+
objects.forEach((object, index) => {
|
|
1953
|
+
const metric = orbitMetric(object);
|
|
1954
|
+
const fallbackRadius = metricContext.innerPx + index * metricContext.stepPx;
|
|
1955
|
+
const baseRadius = metric === null ? fallbackRadius : resolveOrbitRadiusPx(metric, metricContext);
|
|
1956
|
+
const minimumRadius = index === 0 ? metricContext.innerPx : (radii[index - 1] ?? metricContext.innerPx) + metricContext.minimumGapPx;
|
|
1957
|
+
radii.push(Math.max(baseRadius, minimumRadius));
|
|
1958
|
+
});
|
|
1959
|
+
return radii;
|
|
1895
1960
|
}
|
|
1896
1961
|
function orbitMetric(object) {
|
|
1897
1962
|
if (!object.placement || object.placement.mode !== "orbit") {
|
|
@@ -1899,6 +1964,9 @@
|
|
|
1899
1964
|
}
|
|
1900
1965
|
return toDistanceMetric(object.placement.semiMajor ?? object.placement.distance ?? null);
|
|
1901
1966
|
}
|
|
1967
|
+
function log2(value) {
|
|
1968
|
+
return Math.log(value) / Math.log(2);
|
|
1969
|
+
}
|
|
1902
1970
|
function resolveOrbitPhase(phase, index, count) {
|
|
1903
1971
|
const degreeValue = phase ? unitValueToDegrees(phase) : null;
|
|
1904
1972
|
if (degreeValue !== null) {
|
|
@@ -2222,8 +2290,18 @@
|
|
|
2222
2290
|
return value.value;
|
|
2223
2291
|
case "km":
|
|
2224
2292
|
return value.value / AU_IN_KM;
|
|
2293
|
+
case "m":
|
|
2294
|
+
return value.value / 1e3 / AU_IN_KM;
|
|
2295
|
+
case "ly":
|
|
2296
|
+
return value.value * LY_IN_AU;
|
|
2297
|
+
case "pc":
|
|
2298
|
+
return value.value * PC_IN_AU;
|
|
2299
|
+
case "kpc":
|
|
2300
|
+
return value.value * KPC_IN_AU;
|
|
2225
2301
|
case "re":
|
|
2226
2302
|
return value.value * EARTH_RADIUS_IN_KM / AU_IN_KM;
|
|
2303
|
+
case "rj":
|
|
2304
|
+
return value.value * JUPITER_RADIUS_IN_KM / AU_IN_KM;
|
|
2227
2305
|
case "sol":
|
|
2228
2306
|
return value.value * SOLAR_RADIUS_IN_KM / AU_IN_KM;
|
|
2229
2307
|
default:
|
|
@@ -2377,8 +2455,11 @@
|
|
|
2377
2455
|
return {
|
|
2378
2456
|
format: "worldorbit",
|
|
2379
2457
|
version: "2.0",
|
|
2458
|
+
schemaVersion: "2.0",
|
|
2380
2459
|
sourceVersion: document.version,
|
|
2381
2460
|
system,
|
|
2461
|
+
groups: structuredClone(document.groups ?? []),
|
|
2462
|
+
relations: structuredClone(document.relations ?? []),
|
|
2382
2463
|
objects: document.objects.map(cloneWorldOrbitObject),
|
|
2383
2464
|
diagnostics
|
|
2384
2465
|
};
|
|
@@ -2390,13 +2471,20 @@
|
|
|
2390
2471
|
const system = document.system ? {
|
|
2391
2472
|
type: "system",
|
|
2392
2473
|
id: document.system.id,
|
|
2474
|
+
title: document.system.title,
|
|
2475
|
+
description: document.system.description,
|
|
2476
|
+
epoch: document.system.epoch,
|
|
2477
|
+
referencePlane: document.system.referencePlane,
|
|
2393
2478
|
properties: materializeDraftSystemProperties(document.system),
|
|
2394
2479
|
info: materializeDraftSystemInfo(document.system)
|
|
2395
2480
|
} : null;
|
|
2396
2481
|
return {
|
|
2397
2482
|
format: "worldorbit",
|
|
2398
2483
|
version: "1.0",
|
|
2484
|
+
schemaVersion: document.version,
|
|
2399
2485
|
system,
|
|
2486
|
+
groups: structuredClone(document.groups ?? []),
|
|
2487
|
+
relations: structuredClone(document.relations ?? []),
|
|
2400
2488
|
objects: document.objects.map(cloneWorldOrbitObject)
|
|
2401
2489
|
};
|
|
2402
2490
|
}
|
|
@@ -2411,7 +2499,10 @@
|
|
|
2411
2499
|
return {
|
|
2412
2500
|
type: "system",
|
|
2413
2501
|
id: document.system?.id ?? "WorldOrbit",
|
|
2414
|
-
title: typeof document.system?.properties.title === "string" ? document.system.properties.title : null,
|
|
2502
|
+
title: document.system?.title ?? (typeof document.system?.properties.title === "string" ? document.system.properties.title : null),
|
|
2503
|
+
description: document.system?.description ?? null,
|
|
2504
|
+
epoch: document.system?.epoch ?? null,
|
|
2505
|
+
referencePlane: document.system?.referencePlane ?? null,
|
|
2415
2506
|
defaults,
|
|
2416
2507
|
atlasMetadata,
|
|
2417
2508
|
viewpoints: scene.viewpoints.map(mapSceneViewpointToDraftViewpoint),
|
|
@@ -2538,6 +2629,17 @@
|
|
|
2538
2629
|
function cloneWorldOrbitObject(object) {
|
|
2539
2630
|
return {
|
|
2540
2631
|
...object,
|
|
2632
|
+
groups: object.groups ? [...object.groups] : void 0,
|
|
2633
|
+
resonance: object.resonance ? { ...object.resonance } : object.resonance,
|
|
2634
|
+
renderHints: object.renderHints ? { ...object.renderHints } : object.renderHints,
|
|
2635
|
+
deriveRules: object.deriveRules ? object.deriveRules.map((rule) => ({ ...rule })) : void 0,
|
|
2636
|
+
validationRules: object.validationRules ? object.validationRules.map((rule) => ({ ...rule })) : void 0,
|
|
2637
|
+
lockedFields: object.lockedFields ? [...object.lockedFields] : void 0,
|
|
2638
|
+
tolerances: object.tolerances ? object.tolerances.map((entry) => ({
|
|
2639
|
+
field: entry.field,
|
|
2640
|
+
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
|
|
2641
|
+
})) : void 0,
|
|
2642
|
+
typedBlocks: object.typedBlocks ? Object.fromEntries(Object.entries(object.typedBlocks).map(([key, block]) => [key, { ...block ?? {} }])) : void 0,
|
|
2541
2643
|
properties: cloneProperties(object.properties),
|
|
2542
2644
|
placement: object.placement ? structuredClone(object.placement) : null,
|
|
2543
2645
|
info: { ...object.info }
|
|
@@ -2582,71 +2684,80 @@
|
|
|
2582
2684
|
if (system.defaults.units) {
|
|
2583
2685
|
properties.units = system.defaults.units;
|
|
2584
2686
|
}
|
|
2687
|
+
if (system.description) {
|
|
2688
|
+
properties.description = system.description;
|
|
2689
|
+
}
|
|
2690
|
+
if (system.epoch) {
|
|
2691
|
+
properties.epoch = system.epoch;
|
|
2692
|
+
}
|
|
2693
|
+
if (system.referencePlane) {
|
|
2694
|
+
properties.referencePlane = system.referencePlane;
|
|
2695
|
+
}
|
|
2585
2696
|
return properties;
|
|
2586
2697
|
}
|
|
2587
2698
|
function materializeDraftSystemInfo(system) {
|
|
2588
|
-
const
|
|
2699
|
+
const info2 = {
|
|
2589
2700
|
...system.atlasMetadata
|
|
2590
2701
|
};
|
|
2591
2702
|
if (system.defaults.theme) {
|
|
2592
|
-
|
|
2703
|
+
info2["atlas.theme"] = system.defaults.theme;
|
|
2593
2704
|
}
|
|
2594
2705
|
for (const viewpoint of system.viewpoints) {
|
|
2595
2706
|
const prefix = `viewpoint.${viewpoint.id}`;
|
|
2596
|
-
|
|
2707
|
+
info2[`${prefix}.label`] = viewpoint.label;
|
|
2597
2708
|
if (viewpoint.summary) {
|
|
2598
|
-
|
|
2709
|
+
info2[`${prefix}.summary`] = viewpoint.summary;
|
|
2599
2710
|
}
|
|
2600
2711
|
if (viewpoint.focusObjectId) {
|
|
2601
|
-
|
|
2712
|
+
info2[`${prefix}.focus`] = viewpoint.focusObjectId;
|
|
2602
2713
|
}
|
|
2603
2714
|
if (viewpoint.selectedObjectId) {
|
|
2604
|
-
|
|
2715
|
+
info2[`${prefix}.select`] = viewpoint.selectedObjectId;
|
|
2605
2716
|
}
|
|
2606
2717
|
if (viewpoint.projection) {
|
|
2607
|
-
|
|
2718
|
+
info2[`${prefix}.projection`] = viewpoint.projection;
|
|
2608
2719
|
}
|
|
2609
2720
|
if (viewpoint.preset) {
|
|
2610
|
-
|
|
2721
|
+
info2[`${prefix}.preset`] = viewpoint.preset;
|
|
2611
2722
|
}
|
|
2612
2723
|
if (viewpoint.zoom !== null) {
|
|
2613
|
-
|
|
2724
|
+
info2[`${prefix}.zoom`] = String(viewpoint.zoom);
|
|
2614
2725
|
}
|
|
2615
2726
|
if (viewpoint.rotationDeg !== 0) {
|
|
2616
|
-
|
|
2727
|
+
info2[`${prefix}.rotation`] = String(viewpoint.rotationDeg);
|
|
2617
2728
|
}
|
|
2618
2729
|
const serializedLayers = serializeViewpointLayers(viewpoint.layers);
|
|
2619
2730
|
if (serializedLayers) {
|
|
2620
|
-
|
|
2731
|
+
info2[`${prefix}.layers`] = serializedLayers;
|
|
2621
2732
|
}
|
|
2622
2733
|
if (viewpoint.filter?.query) {
|
|
2623
|
-
|
|
2734
|
+
info2[`${prefix}.query`] = viewpoint.filter.query;
|
|
2624
2735
|
}
|
|
2625
2736
|
if ((viewpoint.filter?.objectTypes.length ?? 0) > 0) {
|
|
2626
|
-
|
|
2737
|
+
info2[`${prefix}.types`] = viewpoint.filter?.objectTypes.join(" ") ?? "";
|
|
2627
2738
|
}
|
|
2628
2739
|
if ((viewpoint.filter?.tags.length ?? 0) > 0) {
|
|
2629
|
-
|
|
2740
|
+
info2[`${prefix}.tags`] = viewpoint.filter?.tags.join(" ") ?? "";
|
|
2630
2741
|
}
|
|
2631
2742
|
if ((viewpoint.filter?.groupIds.length ?? 0) > 0) {
|
|
2632
|
-
|
|
2743
|
+
info2[`${prefix}.groups`] = viewpoint.filter?.groupIds.join(" ") ?? "";
|
|
2633
2744
|
}
|
|
2634
2745
|
}
|
|
2635
2746
|
for (const annotation of system.annotations) {
|
|
2636
2747
|
const prefix = `annotation.${annotation.id}`;
|
|
2637
|
-
|
|
2748
|
+
info2[`${prefix}.label`] = annotation.label;
|
|
2638
2749
|
if (annotation.targetObjectId) {
|
|
2639
|
-
|
|
2750
|
+
info2[`${prefix}.target`] = annotation.targetObjectId;
|
|
2640
2751
|
}
|
|
2641
|
-
|
|
2752
|
+
info2[`${prefix}.body`] = annotation.body;
|
|
2642
2753
|
if (annotation.tags.length > 0) {
|
|
2643
|
-
|
|
2754
|
+
info2[`${prefix}.tags`] = annotation.tags.join(" ");
|
|
2644
2755
|
}
|
|
2645
2756
|
if (annotation.sourceObjectId) {
|
|
2646
|
-
|
|
2757
|
+
info2[`${prefix}.source`] = annotation.sourceObjectId;
|
|
2647
2758
|
}
|
|
2648
2759
|
}
|
|
2649
|
-
return
|
|
2760
|
+
return info2;
|
|
2650
2761
|
}
|
|
2651
2762
|
function serializeViewpointLayers(layers) {
|
|
2652
2763
|
const tokens = [];
|
|
@@ -2655,7 +2766,7 @@
|
|
|
2655
2766
|
if (orbitFront !== void 0 || orbitBack !== void 0) {
|
|
2656
2767
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
2657
2768
|
}
|
|
2658
|
-
for (const key of ["background", "guides", "objects", "labels", "metadata"]) {
|
|
2769
|
+
for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
|
|
2659
2770
|
if (layers[key] !== void 0) {
|
|
2660
2771
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
2661
2772
|
}
|
|
@@ -2665,7 +2776,8 @@
|
|
|
2665
2776
|
function convertAtlasDocumentToLegacyDraft(document) {
|
|
2666
2777
|
return {
|
|
2667
2778
|
...document,
|
|
2668
|
-
version: "2.0-draft"
|
|
2779
|
+
version: "2.0-draft",
|
|
2780
|
+
schemaVersion: "2.0-draft"
|
|
2669
2781
|
};
|
|
2670
2782
|
}
|
|
2671
2783
|
|
|
@@ -2707,19 +2819,28 @@
|
|
|
2707
2819
|
];
|
|
2708
2820
|
function formatDocument(document, options = {}) {
|
|
2709
2821
|
const schema = options.schema ?? "auto";
|
|
2710
|
-
const useDraft = schema === "2.0" || schema === "2.0-draft" || document.version === "2.0" || document.version === "2.0-draft";
|
|
2822
|
+
const useDraft = schema === "2.0" || schema === "2.1" || schema === "2.0-draft" || document.version === "2.0" || document.version === "2.1" || document.version === "2.0-draft";
|
|
2711
2823
|
if (useDraft) {
|
|
2712
2824
|
if (schema === "2.0-draft") {
|
|
2713
|
-
const legacyDraftDocument = document.version === "2.0-draft" ? document : document.version === "2.0" ? {
|
|
2825
|
+
const legacyDraftDocument = document.version === "2.0-draft" ? document : document.version === "2.0" || document.version === "2.1" ? {
|
|
2714
2826
|
...document,
|
|
2715
|
-
version: "2.0-draft"
|
|
2827
|
+
version: "2.0-draft",
|
|
2828
|
+
schemaVersion: "2.0-draft"
|
|
2716
2829
|
} : upgradeDocumentToDraftV2(document);
|
|
2717
2830
|
return formatDraftDocument(legacyDraftDocument);
|
|
2718
2831
|
}
|
|
2719
|
-
const atlasDocument = document.version === "2.0" ? document : document.version === "2.0-draft" ? {
|
|
2832
|
+
const atlasDocument = document.version === "2.0" || document.version === "2.1" ? document : document.version === "2.0-draft" ? {
|
|
2720
2833
|
...document,
|
|
2721
|
-
version: "2.0"
|
|
2834
|
+
version: "2.0",
|
|
2835
|
+
schemaVersion: "2.0"
|
|
2722
2836
|
} : upgradeDocumentToV2(document);
|
|
2837
|
+
if (schema === "2.1" && atlasDocument.version !== "2.1") {
|
|
2838
|
+
return formatAtlasDocument({
|
|
2839
|
+
...atlasDocument,
|
|
2840
|
+
version: "2.1",
|
|
2841
|
+
schemaVersion: "2.1"
|
|
2842
|
+
});
|
|
2843
|
+
}
|
|
2723
2844
|
return formatAtlasDocument(atlasDocument);
|
|
2724
2845
|
}
|
|
2725
2846
|
const lines = [];
|
|
@@ -2737,10 +2858,18 @@
|
|
|
2737
2858
|
return lines.join("\n");
|
|
2738
2859
|
}
|
|
2739
2860
|
function formatAtlasDocument(document) {
|
|
2740
|
-
const lines = [
|
|
2861
|
+
const lines = [`schema ${document.version}`, ""];
|
|
2741
2862
|
if (document.system) {
|
|
2742
2863
|
lines.push(...formatAtlasSystem(document.system));
|
|
2743
2864
|
}
|
|
2865
|
+
for (const group of [...document.groups].sort(compareIdLike)) {
|
|
2866
|
+
lines.push("");
|
|
2867
|
+
lines.push(...formatAtlasGroup(group));
|
|
2868
|
+
}
|
|
2869
|
+
for (const relation of [...document.relations].sort(compareIdLike)) {
|
|
2870
|
+
lines.push("");
|
|
2871
|
+
lines.push(...formatAtlasRelation(relation));
|
|
2872
|
+
}
|
|
2744
2873
|
const sortedObjects = [...document.objects].sort(compareObjects);
|
|
2745
2874
|
if (sortedObjects.length > 0 && lines.at(-1) !== "") {
|
|
2746
2875
|
lines.push("");
|
|
@@ -2756,12 +2885,21 @@
|
|
|
2756
2885
|
function formatDraftDocument(document) {
|
|
2757
2886
|
const legacy = document.version === "2.0-draft" ? document : {
|
|
2758
2887
|
...document,
|
|
2759
|
-
version: "2.0-draft"
|
|
2888
|
+
version: "2.0-draft",
|
|
2889
|
+
schemaVersion: "2.0-draft"
|
|
2760
2890
|
};
|
|
2761
2891
|
const lines = ["schema 2.0-draft", ""];
|
|
2762
2892
|
if (legacy.system) {
|
|
2763
2893
|
lines.push(...formatAtlasSystem(legacy.system));
|
|
2764
2894
|
}
|
|
2895
|
+
for (const group of [...legacy.groups].sort(compareIdLike)) {
|
|
2896
|
+
lines.push("");
|
|
2897
|
+
lines.push(...formatAtlasGroup(group));
|
|
2898
|
+
}
|
|
2899
|
+
for (const relation of [...legacy.relations].sort(compareIdLike)) {
|
|
2900
|
+
lines.push("");
|
|
2901
|
+
lines.push(...formatAtlasRelation(relation));
|
|
2902
|
+
}
|
|
2765
2903
|
const sortedObjects = [...legacy.objects].sort(compareObjects);
|
|
2766
2904
|
if (sortedObjects.length > 0 && lines.at(-1) !== "") {
|
|
2767
2905
|
lines.push("");
|
|
@@ -2777,11 +2915,38 @@
|
|
|
2777
2915
|
function formatSystem(system) {
|
|
2778
2916
|
return formatLines("system", system.id, system.properties, null, system.info);
|
|
2779
2917
|
}
|
|
2918
|
+
function formatLines(objectType, id, properties, placement, info2) {
|
|
2919
|
+
const lines = [`${objectType} ${id}`];
|
|
2920
|
+
const fieldLines = [...formatPlacement(placement), ...formatProperties(properties)];
|
|
2921
|
+
for (const fieldLine of fieldLines) {
|
|
2922
|
+
lines.push(` ${fieldLine}`);
|
|
2923
|
+
}
|
|
2924
|
+
const infoEntries = Object.entries(info2).sort(([left], [right]) => left.localeCompare(right));
|
|
2925
|
+
if (infoEntries.length > 0) {
|
|
2926
|
+
if (fieldLines.length > 0) {
|
|
2927
|
+
lines.push("");
|
|
2928
|
+
}
|
|
2929
|
+
lines.push(" info");
|
|
2930
|
+
for (const [key, value] of infoEntries) {
|
|
2931
|
+
lines.push(` ${key} ${quoteIfNeeded(value)}`);
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
return lines;
|
|
2935
|
+
}
|
|
2780
2936
|
function formatAtlasSystem(system) {
|
|
2781
2937
|
const lines = [`system ${system.id}`];
|
|
2782
2938
|
if (system.title) {
|
|
2783
2939
|
lines.push(` title ${quoteIfNeeded(system.title)}`);
|
|
2784
2940
|
}
|
|
2941
|
+
if (system.description) {
|
|
2942
|
+
lines.push(` description ${quoteIfNeeded(system.description)}`);
|
|
2943
|
+
}
|
|
2944
|
+
if (system.epoch) {
|
|
2945
|
+
lines.push(` epoch ${quoteIfNeeded(system.epoch)}`);
|
|
2946
|
+
}
|
|
2947
|
+
if (system.referencePlane) {
|
|
2948
|
+
lines.push(` referencePlane ${quoteIfNeeded(system.referencePlane)}`);
|
|
2949
|
+
}
|
|
2785
2950
|
lines.push("");
|
|
2786
2951
|
lines.push("defaults");
|
|
2787
2952
|
lines.push(` view ${system.defaults.view}`);
|
|
@@ -2816,18 +2981,22 @@
|
|
|
2816
2981
|
return lines;
|
|
2817
2982
|
}
|
|
2818
2983
|
function formatObject(object) {
|
|
2819
|
-
return
|
|
2984
|
+
return formatWorldOrbitObject(object.type, object.id, object);
|
|
2820
2985
|
}
|
|
2821
2986
|
function formatAtlasObject(object) {
|
|
2822
|
-
return
|
|
2987
|
+
return formatWorldOrbitObject(`object ${object.type}`, object.id, object);
|
|
2823
2988
|
}
|
|
2824
|
-
function
|
|
2989
|
+
function formatWorldOrbitObject(objectType, id, object) {
|
|
2825
2990
|
const lines = [`${objectType} ${id}`];
|
|
2826
|
-
const fieldLines = [
|
|
2991
|
+
const fieldLines = [
|
|
2992
|
+
...formatPlacement(object.placement),
|
|
2993
|
+
...formatProperties(object.properties),
|
|
2994
|
+
...formatObjectMetadata(object)
|
|
2995
|
+
];
|
|
2827
2996
|
for (const fieldLine of fieldLines) {
|
|
2828
2997
|
lines.push(` ${fieldLine}`);
|
|
2829
2998
|
}
|
|
2830
|
-
const infoEntries = Object.entries(info).sort(([left], [right]) => left.localeCompare(right));
|
|
2999
|
+
const infoEntries = Object.entries(object.info).sort(([left], [right]) => left.localeCompare(right));
|
|
2831
3000
|
if (infoEntries.length > 0) {
|
|
2832
3001
|
if (fieldLines.length > 0) {
|
|
2833
3002
|
lines.push("");
|
|
@@ -2837,6 +3006,16 @@
|
|
|
2837
3006
|
lines.push(` ${key} ${quoteIfNeeded(value)}`);
|
|
2838
3007
|
}
|
|
2839
3008
|
}
|
|
3009
|
+
for (const blockName of ["climate", "habitability", "settlement"]) {
|
|
3010
|
+
const blockEntries = Object.entries(object.typedBlocks?.[blockName] ?? {}).sort(([left], [right]) => left.localeCompare(right));
|
|
3011
|
+
if (blockEntries.length > 0) {
|
|
3012
|
+
lines.push("");
|
|
3013
|
+
lines.push(` ${blockName}`);
|
|
3014
|
+
for (const [key, value] of blockEntries) {
|
|
3015
|
+
lines.push(` ${key} ${quoteIfNeeded(value)}`);
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
2840
3019
|
return lines;
|
|
2841
3020
|
}
|
|
2842
3021
|
function formatPlacement(placement) {
|
|
@@ -2865,6 +3044,46 @@
|
|
|
2865
3044
|
function formatProperties(properties) {
|
|
2866
3045
|
return Object.keys(properties).sort(compareFieldKeys).map((key) => `${key} ${formatValue(properties[key])}`);
|
|
2867
3046
|
}
|
|
3047
|
+
function formatObjectMetadata(object) {
|
|
3048
|
+
const lines = [];
|
|
3049
|
+
if (object.groups?.length) {
|
|
3050
|
+
lines.push(`groups ${object.groups.join(" ")}`);
|
|
3051
|
+
}
|
|
3052
|
+
if (object.epoch) {
|
|
3053
|
+
lines.push(`epoch ${quoteIfNeeded(object.epoch)}`);
|
|
3054
|
+
}
|
|
3055
|
+
if (object.referencePlane) {
|
|
3056
|
+
lines.push(`referencePlane ${quoteIfNeeded(object.referencePlane)}`);
|
|
3057
|
+
}
|
|
3058
|
+
if (object.tidalLock !== void 0) {
|
|
3059
|
+
lines.push(`tidalLock ${object.tidalLock ? "true" : "false"}`);
|
|
3060
|
+
}
|
|
3061
|
+
if (object.renderHints?.renderLabel !== void 0) {
|
|
3062
|
+
lines.push(`renderLabel ${object.renderHints.renderLabel ? "true" : "false"}`);
|
|
3063
|
+
}
|
|
3064
|
+
if (object.renderHints?.renderOrbit !== void 0) {
|
|
3065
|
+
lines.push(`renderOrbit ${object.renderHints.renderOrbit ? "true" : "false"}`);
|
|
3066
|
+
}
|
|
3067
|
+
if (object.renderHints?.renderPriority !== void 0) {
|
|
3068
|
+
lines.push(`renderPriority ${object.renderHints.renderPriority}`);
|
|
3069
|
+
}
|
|
3070
|
+
if (object.resonance) {
|
|
3071
|
+
lines.push(`resonance ${object.resonance.targetObjectId} ${object.resonance.ratio}`);
|
|
3072
|
+
}
|
|
3073
|
+
for (const rule of object.deriveRules ?? []) {
|
|
3074
|
+
lines.push(`derive ${rule.field} ${rule.strategy}`);
|
|
3075
|
+
}
|
|
3076
|
+
for (const rule of object.validationRules ?? []) {
|
|
3077
|
+
lines.push(`validate ${rule.rule}`);
|
|
3078
|
+
}
|
|
3079
|
+
if (object.lockedFields?.length) {
|
|
3080
|
+
lines.push(`locked ${object.lockedFields.join(" ")}`);
|
|
3081
|
+
}
|
|
3082
|
+
for (const tolerance of object.tolerances ?? []) {
|
|
3083
|
+
lines.push(`tolerance ${tolerance.field} ${formatValue(tolerance.value)}`);
|
|
3084
|
+
}
|
|
3085
|
+
return lines;
|
|
3086
|
+
}
|
|
2868
3087
|
function formatAtlasViewpoint(viewpoint) {
|
|
2869
3088
|
const lines = [`viewpoint ${viewpoint.id}`, ` label ${quoteIfNeeded(viewpoint.label)}`];
|
|
2870
3089
|
if (viewpoint.focusObjectId) {
|
|
@@ -2920,6 +3139,50 @@
|
|
|
2920
3139
|
}
|
|
2921
3140
|
return lines;
|
|
2922
3141
|
}
|
|
3142
|
+
function formatAtlasGroup(group) {
|
|
3143
|
+
const lines = [`group ${group.id}`, ` label ${quoteIfNeeded(group.label)}`];
|
|
3144
|
+
if (group.summary) {
|
|
3145
|
+
lines.push(` summary ${quoteIfNeeded(group.summary)}`);
|
|
3146
|
+
}
|
|
3147
|
+
if (group.color) {
|
|
3148
|
+
lines.push(` color ${quoteIfNeeded(group.color)}`);
|
|
3149
|
+
}
|
|
3150
|
+
if (group.tags.length > 0) {
|
|
3151
|
+
lines.push(` tags ${group.tags.map(quoteIfNeeded).join(" ")}`);
|
|
3152
|
+
}
|
|
3153
|
+
if (group.hidden) {
|
|
3154
|
+
lines.push(" hidden true");
|
|
3155
|
+
}
|
|
3156
|
+
return lines;
|
|
3157
|
+
}
|
|
3158
|
+
function formatAtlasRelation(relation) {
|
|
3159
|
+
const lines = [`relation ${relation.id}`];
|
|
3160
|
+
if (relation.from) {
|
|
3161
|
+
lines.push(` from ${quoteIfNeeded(relation.from)}`);
|
|
3162
|
+
}
|
|
3163
|
+
if (relation.to) {
|
|
3164
|
+
lines.push(` to ${quoteIfNeeded(relation.to)}`);
|
|
3165
|
+
}
|
|
3166
|
+
if (relation.kind) {
|
|
3167
|
+
lines.push(` kind ${quoteIfNeeded(relation.kind)}`);
|
|
3168
|
+
}
|
|
3169
|
+
if (relation.label) {
|
|
3170
|
+
lines.push(` label ${quoteIfNeeded(relation.label)}`);
|
|
3171
|
+
}
|
|
3172
|
+
if (relation.summary) {
|
|
3173
|
+
lines.push(` summary ${quoteIfNeeded(relation.summary)}`);
|
|
3174
|
+
}
|
|
3175
|
+
if (relation.tags.length > 0) {
|
|
3176
|
+
lines.push(` tags ${relation.tags.map(quoteIfNeeded).join(" ")}`);
|
|
3177
|
+
}
|
|
3178
|
+
if (relation.color) {
|
|
3179
|
+
lines.push(` color ${quoteIfNeeded(relation.color)}`);
|
|
3180
|
+
}
|
|
3181
|
+
if (relation.hidden) {
|
|
3182
|
+
lines.push(" hidden true");
|
|
3183
|
+
}
|
|
3184
|
+
return lines;
|
|
3185
|
+
}
|
|
2923
3186
|
function formatValue(value) {
|
|
2924
3187
|
if (Array.isArray(value)) {
|
|
2925
3188
|
return value.map((item) => quoteIfNeeded(item)).join(" ");
|
|
@@ -2961,7 +3224,7 @@
|
|
|
2961
3224
|
if (orbitFront !== void 0 || orbitBack !== void 0) {
|
|
2962
3225
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
2963
3226
|
}
|
|
2964
|
-
for (const key of ["background", "guides", "objects", "labels", "metadata"]) {
|
|
3227
|
+
for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
|
|
2965
3228
|
if (layers[key] !== void 0) {
|
|
2966
3229
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
2967
3230
|
}
|
|
@@ -2986,6 +3249,9 @@
|
|
|
2986
3249
|
return leftIndex - rightIndex;
|
|
2987
3250
|
return left.id.localeCompare(right.id);
|
|
2988
3251
|
}
|
|
3252
|
+
function compareIdLike(left, right) {
|
|
3253
|
+
return left.id.localeCompare(right.id);
|
|
3254
|
+
}
|
|
2989
3255
|
function objectTypeIndex(objectType) {
|
|
2990
3256
|
switch (objectType) {
|
|
2991
3257
|
case "star":
|
|
@@ -3015,180 +3281,716 @@
|
|
|
3015
3281
|
return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
3016
3282
|
}
|
|
3017
3283
|
|
|
3018
|
-
// packages/core/dist/
|
|
3019
|
-
|
|
3020
|
-
|
|
3284
|
+
// packages/core/dist/atlas-utils.js
|
|
3285
|
+
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)?$/;
|
|
3286
|
+
var BOOLEAN_VALUES2 = /* @__PURE__ */ new Map([
|
|
3287
|
+
["true", true],
|
|
3288
|
+
["false", false],
|
|
3289
|
+
["yes", true],
|
|
3290
|
+
["no", false]
|
|
3291
|
+
]);
|
|
3292
|
+
var URL_SCHEME_PATTERN2 = /^[A-Za-z][A-Za-z0-9+.-]*:/;
|
|
3293
|
+
function normalizeIdentifier2(value) {
|
|
3294
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3021
3295
|
}
|
|
3022
|
-
function
|
|
3023
|
-
return
|
|
3296
|
+
function humanizeIdentifier3(value) {
|
|
3297
|
+
return value.split(/[-_]+/).filter(Boolean).map((segment) => segment[0].toUpperCase() + segment.slice(1)).join(" ");
|
|
3024
3298
|
}
|
|
3025
|
-
function
|
|
3026
|
-
const
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
let system = null;
|
|
3030
|
-
let section = null;
|
|
3031
|
-
const objectNodes = [];
|
|
3032
|
-
let sawDefaults = false;
|
|
3033
|
-
let sawAtlas = false;
|
|
3034
|
-
const viewpointIds = /* @__PURE__ */ new Set();
|
|
3035
|
-
const annotationIds = /* @__PURE__ */ new Set();
|
|
3036
|
-
for (let index = 0; index < lines.length; index++) {
|
|
3037
|
-
const rawLine = lines[index];
|
|
3038
|
-
const lineNumber = index + 1;
|
|
3039
|
-
if (!rawLine.trim()) {
|
|
3040
|
-
continue;
|
|
3041
|
-
}
|
|
3042
|
-
const indent = getIndent(rawLine);
|
|
3043
|
-
const tokens = tokenizeLineDetailed(rawLine.slice(indent), {
|
|
3044
|
-
line: lineNumber,
|
|
3045
|
-
columnOffset: indent
|
|
3046
|
-
});
|
|
3047
|
-
if (tokens.length === 0) {
|
|
3048
|
-
continue;
|
|
3049
|
-
}
|
|
3050
|
-
if (!sawSchemaHeader) {
|
|
3051
|
-
schemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
3052
|
-
sawSchemaHeader = true;
|
|
3053
|
-
continue;
|
|
3054
|
-
}
|
|
3055
|
-
if (indent === 0) {
|
|
3056
|
-
section = startTopLevelSection(tokens, lineNumber, system, objectNodes, viewpointIds, annotationIds, {
|
|
3057
|
-
sawDefaults,
|
|
3058
|
-
sawAtlas
|
|
3059
|
-
});
|
|
3060
|
-
if (section.kind === "system") {
|
|
3061
|
-
system = section.system;
|
|
3062
|
-
} else if (section.kind === "defaults") {
|
|
3063
|
-
sawDefaults = true;
|
|
3064
|
-
} else if (section.kind === "atlas") {
|
|
3065
|
-
sawAtlas = true;
|
|
3066
|
-
}
|
|
3067
|
-
continue;
|
|
3068
|
-
}
|
|
3069
|
-
if (!section) {
|
|
3070
|
-
throw new WorldOrbitError("Indented line without parent atlas section", lineNumber, indent + 1);
|
|
3071
|
-
}
|
|
3072
|
-
handleSectionLine(section, indent, tokens, lineNumber);
|
|
3073
|
-
}
|
|
3074
|
-
if (!sawSchemaHeader) {
|
|
3075
|
-
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
|
|
3299
|
+
function parseAtlasUnitValue(input, location, fieldKey) {
|
|
3300
|
+
const match = input.match(UNIT_PATTERN2);
|
|
3301
|
+
if (!match) {
|
|
3302
|
+
throw WorldOrbitError.fromLocation(`Invalid unit value "${input}"`, location);
|
|
3076
3303
|
}
|
|
3077
|
-
const
|
|
3078
|
-
|
|
3079
|
-
|
|
3304
|
+
const unitValue = {
|
|
3305
|
+
value: Number(match[1]),
|
|
3306
|
+
unit: match[2] ?? null
|
|
3080
3307
|
};
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
system: null,
|
|
3086
|
-
objects: normalizedObjects
|
|
3087
|
-
});
|
|
3088
|
-
const diagnostics = schemaVersion === "2.0-draft" && outputVersion === "2.0" ? [
|
|
3089
|
-
{
|
|
3090
|
-
code: "load.schema.deprecatedDraft",
|
|
3091
|
-
severity: "warning",
|
|
3092
|
-
source: "upgrade",
|
|
3093
|
-
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
3308
|
+
if (fieldKey) {
|
|
3309
|
+
const schema = getFieldSchema(fieldKey);
|
|
3310
|
+
if (schema?.unitFamily && !unitFamilyAllowsUnit(schema.unitFamily, unitValue.unit)) {
|
|
3311
|
+
throw WorldOrbitError.fromLocation(`Unit "${unitValue.unit ?? "none"}" is not valid for "${fieldKey}"`, location);
|
|
3094
3312
|
}
|
|
3095
|
-
] : [];
|
|
3096
|
-
return {
|
|
3097
|
-
format: "worldorbit",
|
|
3098
|
-
version: outputVersion,
|
|
3099
|
-
sourceVersion: "1.0",
|
|
3100
|
-
system,
|
|
3101
|
-
objects: normalizedObjects,
|
|
3102
|
-
diagnostics
|
|
3103
|
-
};
|
|
3104
|
-
}
|
|
3105
|
-
function assertDraftSchemaHeader(tokens, line) {
|
|
3106
|
-
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || tokens[1].value.toLowerCase() !== "2.0-draft" && tokens[1].value.toLowerCase() !== "2.0") {
|
|
3107
|
-
throw new WorldOrbitError('Expected atlas header "schema 2.0" or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
3108
|
-
}
|
|
3109
|
-
return tokens[1].value.toLowerCase() === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
3110
|
-
}
|
|
3111
|
-
function startTopLevelSection(tokens, line, system, objectNodes, viewpointIds, annotationIds, flags) {
|
|
3112
|
-
const keyword = tokens[0]?.value.toLowerCase();
|
|
3113
|
-
switch (keyword) {
|
|
3114
|
-
case "system":
|
|
3115
|
-
if (system) {
|
|
3116
|
-
throw new WorldOrbitError('Atlas section "system" may only appear once', line, tokens[0].column);
|
|
3117
|
-
}
|
|
3118
|
-
return startSystemSection(tokens, line);
|
|
3119
|
-
case "defaults":
|
|
3120
|
-
if (!system) {
|
|
3121
|
-
throw new WorldOrbitError('Atlas section "defaults" requires a preceding system declaration', line, tokens[0].column);
|
|
3122
|
-
}
|
|
3123
|
-
if (flags.sawDefaults) {
|
|
3124
|
-
throw new WorldOrbitError('Atlas section "defaults" may only appear once', line, tokens[0].column);
|
|
3125
|
-
}
|
|
3126
|
-
return {
|
|
3127
|
-
kind: "defaults",
|
|
3128
|
-
system,
|
|
3129
|
-
seenFields: /* @__PURE__ */ new Set()
|
|
3130
|
-
};
|
|
3131
|
-
case "atlas":
|
|
3132
|
-
if (!system) {
|
|
3133
|
-
throw new WorldOrbitError('Atlas section "atlas" requires a preceding system declaration', line, tokens[0].column);
|
|
3134
|
-
}
|
|
3135
|
-
if (flags.sawAtlas) {
|
|
3136
|
-
throw new WorldOrbitError('Atlas section "atlas" may only appear once', line, tokens[0].column);
|
|
3137
|
-
}
|
|
3138
|
-
return {
|
|
3139
|
-
kind: "atlas",
|
|
3140
|
-
system,
|
|
3141
|
-
inMetadata: false,
|
|
3142
|
-
metadataIndent: null
|
|
3143
|
-
};
|
|
3144
|
-
case "viewpoint":
|
|
3145
|
-
if (!system) {
|
|
3146
|
-
throw new WorldOrbitError('Atlas section "viewpoint" requires a preceding system declaration', line, tokens[0].column);
|
|
3147
|
-
}
|
|
3148
|
-
return startViewpointSection(tokens, line, system, viewpointIds);
|
|
3149
|
-
case "annotation":
|
|
3150
|
-
if (!system) {
|
|
3151
|
-
throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
|
|
3152
|
-
}
|
|
3153
|
-
return startAnnotationSection(tokens, line, system, annotationIds);
|
|
3154
|
-
case "object":
|
|
3155
|
-
return startObjectSection(tokens, line, objectNodes);
|
|
3156
|
-
default:
|
|
3157
|
-
throw new WorldOrbitError(`Unknown atlas section "${tokens[0]?.value ?? ""}"`, line, tokens[0]?.column ?? 1);
|
|
3158
3313
|
}
|
|
3314
|
+
return unitValue;
|
|
3159
3315
|
}
|
|
3160
|
-
function
|
|
3161
|
-
|
|
3162
|
-
|
|
3316
|
+
function tryParseAtlasUnitValue(input) {
|
|
3317
|
+
const match = input.match(UNIT_PATTERN2);
|
|
3318
|
+
if (!match) {
|
|
3319
|
+
return null;
|
|
3163
3320
|
}
|
|
3164
|
-
const system = {
|
|
3165
|
-
type: "system",
|
|
3166
|
-
id: tokens[1].value,
|
|
3167
|
-
title: null,
|
|
3168
|
-
defaults: {
|
|
3169
|
-
view: "topdown",
|
|
3170
|
-
scale: null,
|
|
3171
|
-
units: null,
|
|
3172
|
-
preset: null,
|
|
3173
|
-
theme: null
|
|
3174
|
-
},
|
|
3175
|
-
atlasMetadata: {},
|
|
3176
|
-
viewpoints: [],
|
|
3177
|
-
annotations: []
|
|
3178
|
-
};
|
|
3179
3321
|
return {
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
seenFields: /* @__PURE__ */ new Set()
|
|
3322
|
+
value: Number(match[1]),
|
|
3323
|
+
unit: match[2] ?? null
|
|
3183
3324
|
};
|
|
3184
3325
|
}
|
|
3185
|
-
function
|
|
3186
|
-
|
|
3187
|
-
|
|
3326
|
+
function parseAtlasNumber(input, key, location) {
|
|
3327
|
+
const value = Number(input);
|
|
3328
|
+
if (!Number.isFinite(value)) {
|
|
3329
|
+
throw WorldOrbitError.fromLocation(`Invalid numeric value "${input}" for "${key}"`, location);
|
|
3188
3330
|
}
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3331
|
+
return value;
|
|
3332
|
+
}
|
|
3333
|
+
function parseAtlasBoolean(input, key, location) {
|
|
3334
|
+
const parsed = BOOLEAN_VALUES2.get(input.toLowerCase());
|
|
3335
|
+
if (parsed === void 0) {
|
|
3336
|
+
throw WorldOrbitError.fromLocation(`Invalid boolean value "${input}" for "${key}"`, location);
|
|
3337
|
+
}
|
|
3338
|
+
return parsed;
|
|
3339
|
+
}
|
|
3340
|
+
function parseAtlasAtReference(target, location) {
|
|
3341
|
+
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
3342
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
3343
|
+
}
|
|
3344
|
+
const pairedMatch = target.match(/^([A-Za-z0-9._-]+)-([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
3345
|
+
if (pairedMatch) {
|
|
3346
|
+
return {
|
|
3347
|
+
kind: "lagrange",
|
|
3348
|
+
primary: pairedMatch[1],
|
|
3349
|
+
secondary: pairedMatch[2],
|
|
3350
|
+
point: pairedMatch[3]
|
|
3351
|
+
};
|
|
3352
|
+
}
|
|
3353
|
+
const simpleMatch = target.match(/^([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
3354
|
+
if (simpleMatch) {
|
|
3355
|
+
return {
|
|
3356
|
+
kind: "lagrange",
|
|
3357
|
+
primary: simpleMatch[1],
|
|
3358
|
+
secondary: null,
|
|
3359
|
+
point: simpleMatch[2]
|
|
3360
|
+
};
|
|
3361
|
+
}
|
|
3362
|
+
if (/^[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
3363
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
3364
|
+
}
|
|
3365
|
+
const anchorMatch = target.match(/^([A-Za-z0-9._-]+):([A-Za-z0-9._-]+)$/);
|
|
3366
|
+
if (anchorMatch) {
|
|
3367
|
+
return {
|
|
3368
|
+
kind: "anchor",
|
|
3369
|
+
objectId: anchorMatch[1],
|
|
3370
|
+
anchor: anchorMatch[2]
|
|
3371
|
+
};
|
|
3372
|
+
}
|
|
3373
|
+
return {
|
|
3374
|
+
kind: "named",
|
|
3375
|
+
name: target
|
|
3376
|
+
};
|
|
3377
|
+
}
|
|
3378
|
+
function validateAtlasImageSource(value, location) {
|
|
3379
|
+
if (!value) {
|
|
3380
|
+
throw WorldOrbitError.fromLocation('Field "image" must not be empty', location);
|
|
3381
|
+
}
|
|
3382
|
+
if (value.startsWith("//")) {
|
|
3383
|
+
throw WorldOrbitError.fromLocation('Field "image" must use a relative path, root-relative path, or an http/https URL', location);
|
|
3384
|
+
}
|
|
3385
|
+
const schemeMatch = value.match(URL_SCHEME_PATTERN2);
|
|
3386
|
+
if (!schemeMatch) {
|
|
3387
|
+
return;
|
|
3388
|
+
}
|
|
3389
|
+
const scheme = schemeMatch[0].slice(0, -1).toLowerCase();
|
|
3390
|
+
if (scheme !== "http" && scheme !== "https") {
|
|
3391
|
+
throw WorldOrbitError.fromLocation(`Field "image" does not support the "${scheme}" scheme`, location);
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
function normalizeLegacyScalarValue(key, values, location) {
|
|
3395
|
+
const schema = getFieldSchema(key);
|
|
3396
|
+
if (!schema) {
|
|
3397
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
3398
|
+
}
|
|
3399
|
+
if (schema.arity === "single" && values.length !== 1) {
|
|
3400
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
3401
|
+
}
|
|
3402
|
+
switch (schema.kind) {
|
|
3403
|
+
case "list":
|
|
3404
|
+
return values;
|
|
3405
|
+
case "boolean":
|
|
3406
|
+
return parseAtlasBoolean(singleAtlasValue(values, key, location), key, location);
|
|
3407
|
+
case "number":
|
|
3408
|
+
return parseAtlasNumber(singleAtlasValue(values, key, location), key, location);
|
|
3409
|
+
case "unit":
|
|
3410
|
+
return parseAtlasUnitValue(singleAtlasValue(values, key, location), location, key);
|
|
3411
|
+
case "string": {
|
|
3412
|
+
const value = values.join(" ").trim();
|
|
3413
|
+
if (key === "image") {
|
|
3414
|
+
validateAtlasImageSource(value, location);
|
|
3415
|
+
}
|
|
3416
|
+
return value;
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
function ensureAtlasFieldSupported(key, objectType, location) {
|
|
3421
|
+
const schema = getFieldSchema(key);
|
|
3422
|
+
if (!schema) {
|
|
3423
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
3424
|
+
}
|
|
3425
|
+
if (!schema.objectTypes.includes(objectType)) {
|
|
3426
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" is not valid on "${objectType}"`, location);
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
function singleAtlasValue(values, key, location) {
|
|
3430
|
+
if (values.length !== 1) {
|
|
3431
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
3432
|
+
}
|
|
3433
|
+
return values[0];
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3436
|
+
// packages/core/dist/atlas-validate.js
|
|
3437
|
+
var SURFACE_TARGET_TYPES2 = /* @__PURE__ */ new Set(["star", "planet", "moon", "asteroid", "comet"]);
|
|
3438
|
+
var EARTH_MASSES_PER_SOLAR = 332946.0487;
|
|
3439
|
+
var JUPITER_MASSES_PER_SOLAR = 1047.3486;
|
|
3440
|
+
var AU_IN_KM2 = 1495978707e-1;
|
|
3441
|
+
var EARTH_RADIUS_IN_KM2 = 6371;
|
|
3442
|
+
var SOLAR_RADIUS_IN_KM2 = 695700;
|
|
3443
|
+
var LY_IN_AU2 = 63241.077;
|
|
3444
|
+
var PC_IN_AU2 = 206264.806;
|
|
3445
|
+
var KPC_IN_AU2 = 206264806;
|
|
3446
|
+
function collectAtlasDiagnostics(document, sourceSchemaVersion) {
|
|
3447
|
+
const diagnostics = [];
|
|
3448
|
+
const objectMap = new Map(document.objects.map((object) => [object.id, object]));
|
|
3449
|
+
const groupIds = new Set(document.groups.map((group) => group.id));
|
|
3450
|
+
if (!document.system) {
|
|
3451
|
+
diagnostics.push(error("validate.system.required", "Atlas documents must declare exactly one system."));
|
|
3452
|
+
}
|
|
3453
|
+
const knownIds = /* @__PURE__ */ new Map();
|
|
3454
|
+
for (const [kind, ids] of [
|
|
3455
|
+
["group", document.groups.map((group) => group.id)],
|
|
3456
|
+
["viewpoint", document.system?.viewpoints.map((viewpoint) => viewpoint.id) ?? []],
|
|
3457
|
+
["annotation", document.system?.annotations.map((annotation) => annotation.id) ?? []],
|
|
3458
|
+
["relation", document.relations.map((relation) => relation.id)],
|
|
3459
|
+
["object", document.objects.map((object) => object.id)]
|
|
3460
|
+
]) {
|
|
3461
|
+
for (const id of ids) {
|
|
3462
|
+
const previous = knownIds.get(id);
|
|
3463
|
+
if (previous) {
|
|
3464
|
+
diagnostics.push(error("validate.id.duplicate", `Duplicate ${kind} id "${id}" already used by ${previous}.`));
|
|
3465
|
+
} else {
|
|
3466
|
+
knownIds.set(id, kind);
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
for (const relation of document.relations) {
|
|
3471
|
+
validateRelation(relation, objectMap, diagnostics);
|
|
3472
|
+
}
|
|
3473
|
+
for (const viewpoint of document.system?.viewpoints ?? []) {
|
|
3474
|
+
validateViewpointFilter(viewpoint.filter, groupIds, sourceSchemaVersion, diagnostics, viewpoint.id);
|
|
3475
|
+
}
|
|
3476
|
+
for (const object of document.objects) {
|
|
3477
|
+
validateObject(object, document.system, objectMap, groupIds, diagnostics);
|
|
3478
|
+
}
|
|
3479
|
+
return diagnostics;
|
|
3480
|
+
}
|
|
3481
|
+
function validateRelation(relation, objectMap, diagnostics) {
|
|
3482
|
+
if (!relation.from) {
|
|
3483
|
+
diagnostics.push(error("validate.relation.from.required", `Relation "${relation.id}" is missing a "from" target.`));
|
|
3484
|
+
} else if (!objectMap.has(relation.from)) {
|
|
3485
|
+
diagnostics.push(error("validate.relation.from.unknown", `Unknown relation source "${relation.from}" on "${relation.id}".`));
|
|
3486
|
+
}
|
|
3487
|
+
if (!relation.to) {
|
|
3488
|
+
diagnostics.push(error("validate.relation.to.required", `Relation "${relation.id}" is missing a "to" target.`));
|
|
3489
|
+
} else if (!objectMap.has(relation.to)) {
|
|
3490
|
+
diagnostics.push(error("validate.relation.to.unknown", `Unknown relation target "${relation.to}" on "${relation.id}".`));
|
|
3491
|
+
}
|
|
3492
|
+
if (!relation.kind) {
|
|
3493
|
+
diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
function validateViewpointFilter(filter, groupIds, sourceSchemaVersion, diagnostics, viewpointId) {
|
|
3497
|
+
if (!filter || sourceSchemaVersion !== "2.1") {
|
|
3498
|
+
return;
|
|
3499
|
+
}
|
|
3500
|
+
for (const groupId of filter.groupIds) {
|
|
3501
|
+
if (!groupIds.has(groupId)) {
|
|
3502
|
+
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpointId}".`));
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
3507
|
+
const placement = object.placement;
|
|
3508
|
+
const orbitPlacement = placement?.mode === "orbit" ? placement : null;
|
|
3509
|
+
const parentObject = placement?.mode === "orbit" ? objectMap.get(placement.target) ?? null : null;
|
|
3510
|
+
if (object.groups) {
|
|
3511
|
+
for (const groupId of object.groups) {
|
|
3512
|
+
if (!groupIds.has(groupId)) {
|
|
3513
|
+
diagnostics.push(warn("validate.group.unknown", `Unknown group "${groupId}" on "${object.id}".`, object.id, "groups"));
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
if (orbitPlacement) {
|
|
3518
|
+
if (!objectMap.has(orbitPlacement.target)) {
|
|
3519
|
+
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
3520
|
+
}
|
|
3521
|
+
if (orbitPlacement.distance && orbitPlacement.semiMajor) {
|
|
3522
|
+
diagnostics.push(error("validate.orbit.distanceConflict", `Object "${object.id}" cannot declare both "distance" and "semiMajor".`, object.id, "distance"));
|
|
3523
|
+
}
|
|
3524
|
+
if (orbitPlacement.phase && !object.epoch && !system?.epoch) {
|
|
3525
|
+
diagnostics.push(warn("validate.phase.epochMissing", `Object "${object.id}" sets "phase" without an object or system epoch.`, object.id, "phase"));
|
|
3526
|
+
}
|
|
3527
|
+
if (orbitPlacement.inclination && !object.referencePlane && !system?.referencePlane) {
|
|
3528
|
+
diagnostics.push(warn("validate.inclination.referencePlaneMissing", `Object "${object.id}" sets "inclination" without an object or system reference plane.`, object.id, "inclination"));
|
|
3529
|
+
}
|
|
3530
|
+
if (orbitPlacement.period && !massInSolar(parentObject?.properties.mass)) {
|
|
3531
|
+
diagnostics.push(warn("validate.period.massMissing", `Object "${object.id}" sets "period" but its central mass cannot be derived.`, object.id, "period"));
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
if (placement?.mode === "surface") {
|
|
3535
|
+
const target = objectMap.get(placement.target);
|
|
3536
|
+
if (!target) {
|
|
3537
|
+
diagnostics.push(error("validate.surface.target.unknown", `Unknown placement target "${placement.target}" on "${object.id}".`, object.id, "surface"));
|
|
3538
|
+
} else if (!SURFACE_TARGET_TYPES2.has(target.type)) {
|
|
3539
|
+
diagnostics.push(error("validate.surface.target.invalid", `Surface target "${placement.target}" on "${object.id}" is not surface-capable.`, object.id, "surface"));
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
if (placement?.mode === "at") {
|
|
3543
|
+
if (object.type !== "structure" && object.type !== "phenomenon") {
|
|
3544
|
+
diagnostics.push(error("validate.at.objectType", `Only structures and phenomena may use "at" placement; found "${object.type}" on "${object.id}".`, object.id, "at"));
|
|
3545
|
+
}
|
|
3546
|
+
if (!validateAtTarget(object, objectMap, diagnostics)) {
|
|
3547
|
+
diagnostics.push(error("validate.at.target.unknown", `Unknown at-reference target "${placement.target}" on "${object.id}".`, object.id, "at"));
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
if (object.resonance) {
|
|
3551
|
+
const target = objectMap.get(object.resonance.targetObjectId);
|
|
3552
|
+
if (!target) {
|
|
3553
|
+
diagnostics.push(error("validate.resonance.target.unknown", `Unknown resonance target "${object.resonance.targetObjectId}" on "${object.id}".`, object.id, "resonance"));
|
|
3554
|
+
} else if (object.placement?.mode !== "orbit" || target.placement?.mode !== "orbit" || object.placement.target !== target.placement.target) {
|
|
3555
|
+
diagnostics.push(warn("validate.resonance.orbitMismatch", `Resonance target "${object.resonance.targetObjectId}" on "${object.id}" does not share a compatible orbital parent.`, object.id, "resonance"));
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
for (const rule of object.deriveRules ?? []) {
|
|
3559
|
+
if (rule.field !== "period" || rule.strategy !== "kepler") {
|
|
3560
|
+
diagnostics.push(warn("validate.derive.unsupported", `Unsupported derive rule "${rule.field} ${rule.strategy}" on "${object.id}".`, object.id, "derive"));
|
|
3561
|
+
continue;
|
|
3562
|
+
}
|
|
3563
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
3564
|
+
if (derivedPeriodDays === null) {
|
|
3565
|
+
diagnostics.push(warn("validate.derive.inputsMissing", `Object "${object.id}" requests "derive period kepler" but lacks enough input data.`, object.id, "derive"));
|
|
3566
|
+
continue;
|
|
3567
|
+
}
|
|
3568
|
+
if (!orbitPlacement?.period) {
|
|
3569
|
+
diagnostics.push(info("validate.derive.period.available", `Object "${object.id}" can derive a Kepler period of ${formatDays(derivedPeriodDays)}.`, object.id, "derive"));
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
for (const rule of object.validationRules ?? []) {
|
|
3573
|
+
if (rule.rule !== "kepler") {
|
|
3574
|
+
diagnostics.push(warn("validate.rule.unsupported", `Unsupported validation rule "${rule.rule}" on "${object.id}".`, object.id, "validate"));
|
|
3575
|
+
continue;
|
|
3576
|
+
}
|
|
3577
|
+
const actualPeriodDays = durationInDays(orbitPlacement?.period);
|
|
3578
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
3579
|
+
if (actualPeriodDays === null || derivedPeriodDays === null) {
|
|
3580
|
+
continue;
|
|
3581
|
+
}
|
|
3582
|
+
const toleranceDays = toleranceForField(object, "period");
|
|
3583
|
+
if (Math.abs(actualPeriodDays - derivedPeriodDays) > toleranceDays) {
|
|
3584
|
+
diagnostics.push(error("validate.kepler.mismatch", `Object "${object.id}" fails Kepler validation for "period".`, object.id, "validate"));
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
function validateAtTarget(object, objectMap, diagnostics) {
|
|
3589
|
+
const reference = object.placement?.mode === "at" ? object.placement.reference : null;
|
|
3590
|
+
if (!reference) {
|
|
3591
|
+
return true;
|
|
3592
|
+
}
|
|
3593
|
+
if (reference.kind === "named") {
|
|
3594
|
+
return objectMap.has(reference.name);
|
|
3595
|
+
}
|
|
3596
|
+
if (reference.kind === "anchor") {
|
|
3597
|
+
if (!objectMap.has(reference.objectId)) {
|
|
3598
|
+
diagnostics.push(error("validate.anchor.target.unknown", `Unknown anchor target "${reference.objectId}" on "${object.id}".`, object.id, "at"));
|
|
3599
|
+
return false;
|
|
3600
|
+
}
|
|
3601
|
+
return true;
|
|
3602
|
+
}
|
|
3603
|
+
if (!objectMap.has(reference.primary)) {
|
|
3604
|
+
diagnostics.push(error("validate.lagrange.primary.unknown", `Unknown Lagrange reference "${reference.primary}" on "${object.id}".`, object.id, "at"));
|
|
3605
|
+
return false;
|
|
3606
|
+
}
|
|
3607
|
+
if (reference.secondary && !objectMap.has(reference.secondary)) {
|
|
3608
|
+
diagnostics.push(error("validate.lagrange.secondary.unknown", `Unknown Lagrange reference "${reference.secondary}" on "${object.id}".`, object.id, "at"));
|
|
3609
|
+
return false;
|
|
3610
|
+
}
|
|
3611
|
+
return true;
|
|
3612
|
+
}
|
|
3613
|
+
function keplerPeriodDays(object, parentObject) {
|
|
3614
|
+
const placement = object.placement;
|
|
3615
|
+
if (!placement || placement.mode !== "orbit") {
|
|
3616
|
+
return null;
|
|
3617
|
+
}
|
|
3618
|
+
const semiMajorAu = distanceInAu(placement.semiMajor ?? placement.distance);
|
|
3619
|
+
const centralMassSolar = massInSolar(parentObject?.properties.mass);
|
|
3620
|
+
if (semiMajorAu === null || centralMassSolar === null || centralMassSolar <= 0) {
|
|
3621
|
+
return null;
|
|
3622
|
+
}
|
|
3623
|
+
const periodYears = Math.sqrt(semiMajorAu ** 3 / centralMassSolar);
|
|
3624
|
+
return periodYears * 365.25;
|
|
3625
|
+
}
|
|
3626
|
+
function distanceInAu(value) {
|
|
3627
|
+
if (!value)
|
|
3628
|
+
return null;
|
|
3629
|
+
switch (value.unit) {
|
|
3630
|
+
case null:
|
|
3631
|
+
case "au":
|
|
3632
|
+
return value.value;
|
|
3633
|
+
case "km":
|
|
3634
|
+
return value.value / AU_IN_KM2;
|
|
3635
|
+
case "m":
|
|
3636
|
+
return value.value / (AU_IN_KM2 * 1e3);
|
|
3637
|
+
case "ly":
|
|
3638
|
+
return value.value * LY_IN_AU2;
|
|
3639
|
+
case "pc":
|
|
3640
|
+
return value.value * PC_IN_AU2;
|
|
3641
|
+
case "kpc":
|
|
3642
|
+
return value.value * KPC_IN_AU2;
|
|
3643
|
+
case "re":
|
|
3644
|
+
return value.value * EARTH_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
3645
|
+
case "sol":
|
|
3646
|
+
return value.value * SOLAR_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
3647
|
+
default:
|
|
3648
|
+
return null;
|
|
3649
|
+
}
|
|
3650
|
+
}
|
|
3651
|
+
function massInSolar(value) {
|
|
3652
|
+
if (!value || typeof value !== "object" || !("value" in value)) {
|
|
3653
|
+
return null;
|
|
3654
|
+
}
|
|
3655
|
+
const unitValue = value;
|
|
3656
|
+
switch (unitValue.unit) {
|
|
3657
|
+
case null:
|
|
3658
|
+
case "sol":
|
|
3659
|
+
return unitValue.value;
|
|
3660
|
+
case "me":
|
|
3661
|
+
return unitValue.value / EARTH_MASSES_PER_SOLAR;
|
|
3662
|
+
case "mj":
|
|
3663
|
+
return unitValue.value / JUPITER_MASSES_PER_SOLAR;
|
|
3664
|
+
default:
|
|
3665
|
+
return null;
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
function durationInDays(value) {
|
|
3669
|
+
if (!value)
|
|
3670
|
+
return null;
|
|
3671
|
+
switch (value.unit) {
|
|
3672
|
+
case null:
|
|
3673
|
+
case "d":
|
|
3674
|
+
return value.value;
|
|
3675
|
+
case "s":
|
|
3676
|
+
return value.value / 86400;
|
|
3677
|
+
case "min":
|
|
3678
|
+
return value.value / 1440;
|
|
3679
|
+
case "h":
|
|
3680
|
+
return value.value / 24;
|
|
3681
|
+
case "y":
|
|
3682
|
+
return value.value * 365.25;
|
|
3683
|
+
case "ky":
|
|
3684
|
+
return value.value * 365250;
|
|
3685
|
+
case "my":
|
|
3686
|
+
return value.value * 36525e4;
|
|
3687
|
+
case "gy":
|
|
3688
|
+
return value.value * 36525e7;
|
|
3689
|
+
default:
|
|
3690
|
+
return null;
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
function toleranceForField(object, field) {
|
|
3694
|
+
const tolerance = object.tolerances?.find((entry) => entry.field === field)?.value;
|
|
3695
|
+
if (typeof tolerance === "number") {
|
|
3696
|
+
return tolerance;
|
|
3697
|
+
}
|
|
3698
|
+
if (tolerance && typeof tolerance === "object" && "value" in tolerance) {
|
|
3699
|
+
return durationInDays(tolerance) ?? 0;
|
|
3700
|
+
}
|
|
3701
|
+
return 0;
|
|
3702
|
+
}
|
|
3703
|
+
function formatDays(days) {
|
|
3704
|
+
return `${Math.round(days * 100) / 100}d`;
|
|
3705
|
+
}
|
|
3706
|
+
function error(code, message, objectId, field) {
|
|
3707
|
+
return { code, severity: "error", source: "validate", message, objectId, field };
|
|
3708
|
+
}
|
|
3709
|
+
function warn(code, message, objectId, field) {
|
|
3710
|
+
return { code, severity: "warning", source: "validate", message, objectId, field };
|
|
3711
|
+
}
|
|
3712
|
+
function info(code, message, objectId, field) {
|
|
3713
|
+
return { code, severity: "info", source: "validate", message, objectId, field };
|
|
3714
|
+
}
|
|
3715
|
+
|
|
3716
|
+
// packages/core/dist/draft-parse.js
|
|
3717
|
+
var STRUCTURED_TYPED_BLOCKS = /* @__PURE__ */ new Set([
|
|
3718
|
+
"climate",
|
|
3719
|
+
"habitability",
|
|
3720
|
+
"settlement"
|
|
3721
|
+
]);
|
|
3722
|
+
var DRAFT_OBJECT_FIELD_SPECS = /* @__PURE__ */ new Map();
|
|
3723
|
+
for (const key of [
|
|
3724
|
+
"orbit",
|
|
3725
|
+
"distance",
|
|
3726
|
+
"semiMajor",
|
|
3727
|
+
"eccentricity",
|
|
3728
|
+
"period",
|
|
3729
|
+
"angle",
|
|
3730
|
+
"inclination",
|
|
3731
|
+
"phase",
|
|
3732
|
+
"at",
|
|
3733
|
+
"surface",
|
|
3734
|
+
"free",
|
|
3735
|
+
"kind",
|
|
3736
|
+
"class",
|
|
3737
|
+
"culture",
|
|
3738
|
+
"tags",
|
|
3739
|
+
"color",
|
|
3740
|
+
"image",
|
|
3741
|
+
"hidden",
|
|
3742
|
+
"radius",
|
|
3743
|
+
"mass",
|
|
3744
|
+
"density",
|
|
3745
|
+
"gravity",
|
|
3746
|
+
"temperature",
|
|
3747
|
+
"albedo",
|
|
3748
|
+
"atmosphere",
|
|
3749
|
+
"inner",
|
|
3750
|
+
"outer",
|
|
3751
|
+
"on",
|
|
3752
|
+
"source",
|
|
3753
|
+
"cycle"
|
|
3754
|
+
]) {
|
|
3755
|
+
const schema = getFieldSchema(key);
|
|
3756
|
+
if (schema) {
|
|
3757
|
+
DRAFT_OBJECT_FIELD_SPECS.set(key, {
|
|
3758
|
+
key,
|
|
3759
|
+
version: "2.0",
|
|
3760
|
+
inlineMode: schema.arity === "multiple" ? "multiple" : "single",
|
|
3761
|
+
allowRepeat: false,
|
|
3762
|
+
legacySchema: schema
|
|
3763
|
+
});
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
for (const spec of [
|
|
3767
|
+
{ key: "groups", inlineMode: "multiple", allowRepeat: false },
|
|
3768
|
+
{ key: "epoch", inlineMode: "single", allowRepeat: false },
|
|
3769
|
+
{ key: "referencePlane", inlineMode: "single", allowRepeat: false },
|
|
3770
|
+
{ key: "tidalLock", inlineMode: "single", allowRepeat: false },
|
|
3771
|
+
{ key: "renderLabel", inlineMode: "single", allowRepeat: false },
|
|
3772
|
+
{ key: "renderOrbit", inlineMode: "single", allowRepeat: false },
|
|
3773
|
+
{ key: "renderPriority", inlineMode: "single", allowRepeat: false },
|
|
3774
|
+
{ key: "resonance", inlineMode: "pair", allowRepeat: false },
|
|
3775
|
+
{ key: "derive", inlineMode: "pair", allowRepeat: true },
|
|
3776
|
+
{ key: "validate", inlineMode: "single", allowRepeat: true },
|
|
3777
|
+
{ key: "locked", inlineMode: "multiple", allowRepeat: false },
|
|
3778
|
+
{ key: "tolerance", inlineMode: "pair", allowRepeat: true }
|
|
3779
|
+
]) {
|
|
3780
|
+
DRAFT_OBJECT_FIELD_SPECS.set(spec.key, {
|
|
3781
|
+
key: spec.key,
|
|
3782
|
+
version: "2.1",
|
|
3783
|
+
inlineMode: spec.inlineMode,
|
|
3784
|
+
allowRepeat: spec.allowRepeat
|
|
3785
|
+
});
|
|
3786
|
+
}
|
|
3787
|
+
var DRAFT_OBJECT_FIELD_KEYS = new Set(DRAFT_OBJECT_FIELD_SPECS.keys());
|
|
3788
|
+
function parseWorldOrbitAtlas(source) {
|
|
3789
|
+
return parseAtlasSource(source);
|
|
3790
|
+
}
|
|
3791
|
+
function parseWorldOrbitDraft(source) {
|
|
3792
|
+
return parseAtlasSource(source, "2.0-draft");
|
|
3793
|
+
}
|
|
3794
|
+
function parseAtlasSource(source, forcedOutputVersion) {
|
|
3795
|
+
const prepared = preprocessAtlasSource(source);
|
|
3796
|
+
const lines = prepared.source.split(/\r?\n/);
|
|
3797
|
+
const diagnostics = [];
|
|
3798
|
+
let sawSchemaHeader = false;
|
|
3799
|
+
let sourceSchemaVersion = "2.0";
|
|
3800
|
+
let system = null;
|
|
3801
|
+
let section = null;
|
|
3802
|
+
const objectNodes = [];
|
|
3803
|
+
const groups = [];
|
|
3804
|
+
const relations = [];
|
|
3805
|
+
let sawDefaults = false;
|
|
3806
|
+
let sawAtlas = false;
|
|
3807
|
+
const viewpointIds = /* @__PURE__ */ new Set();
|
|
3808
|
+
const annotationIds = /* @__PURE__ */ new Set();
|
|
3809
|
+
const groupIds = /* @__PURE__ */ new Set();
|
|
3810
|
+
const relationIds = /* @__PURE__ */ new Set();
|
|
3811
|
+
for (let index = 0; index < lines.length; index++) {
|
|
3812
|
+
const rawLine = lines[index];
|
|
3813
|
+
const lineNumber = index + 1;
|
|
3814
|
+
if (!rawLine.trim()) {
|
|
3815
|
+
continue;
|
|
3816
|
+
}
|
|
3817
|
+
const indent = getIndent(rawLine);
|
|
3818
|
+
const tokens = tokenizeLineDetailed(rawLine.slice(indent), {
|
|
3819
|
+
line: lineNumber,
|
|
3820
|
+
columnOffset: indent
|
|
3821
|
+
});
|
|
3822
|
+
if (tokens.length === 0) {
|
|
3823
|
+
continue;
|
|
3824
|
+
}
|
|
3825
|
+
if (!sawSchemaHeader) {
|
|
3826
|
+
sourceSchemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
3827
|
+
sawSchemaHeader = true;
|
|
3828
|
+
if (prepared.comments.length > 0 && sourceSchemaVersion !== "2.1") {
|
|
3829
|
+
diagnostics.push({
|
|
3830
|
+
code: "parse.schema21.commentCompatibility",
|
|
3831
|
+
severity: "warning",
|
|
3832
|
+
source: "parse",
|
|
3833
|
+
message: `Comments require schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
3834
|
+
line: prepared.comments[0].line,
|
|
3835
|
+
column: prepared.comments[0].column
|
|
3836
|
+
});
|
|
3837
|
+
}
|
|
3838
|
+
continue;
|
|
3839
|
+
}
|
|
3840
|
+
if (indent === 0) {
|
|
3841
|
+
section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, { sawDefaults, sawAtlas });
|
|
3842
|
+
if (section.kind === "system") {
|
|
3843
|
+
system = section.system;
|
|
3844
|
+
} else if (section.kind === "defaults") {
|
|
3845
|
+
sawDefaults = true;
|
|
3846
|
+
} else if (section.kind === "atlas") {
|
|
3847
|
+
sawAtlas = true;
|
|
3848
|
+
}
|
|
3849
|
+
continue;
|
|
3850
|
+
}
|
|
3851
|
+
if (!section) {
|
|
3852
|
+
throw new WorldOrbitError("Indented line without parent atlas section", lineNumber, indent + 1);
|
|
3853
|
+
}
|
|
3854
|
+
handleSectionLine(section, indent, tokens, lineNumber);
|
|
3855
|
+
}
|
|
3856
|
+
if (!sawSchemaHeader) {
|
|
3857
|
+
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
|
|
3858
|
+
}
|
|
3859
|
+
const objects = objectNodes.map((node) => normalizeDraftObject(node, sourceSchemaVersion, diagnostics));
|
|
3860
|
+
const outputVersion = forcedOutputVersion ?? (sourceSchemaVersion === "2.0-draft" ? "2.0" : sourceSchemaVersion);
|
|
3861
|
+
const baseDocument = {
|
|
3862
|
+
format: "worldorbit",
|
|
3863
|
+
sourceVersion: "1.0",
|
|
3864
|
+
system,
|
|
3865
|
+
groups,
|
|
3866
|
+
relations,
|
|
3867
|
+
objects,
|
|
3868
|
+
diagnostics
|
|
3869
|
+
};
|
|
3870
|
+
if (outputVersion === "2.0-draft") {
|
|
3871
|
+
const document2 = {
|
|
3872
|
+
...baseDocument,
|
|
3873
|
+
version: "2.0-draft",
|
|
3874
|
+
schemaVersion: "2.0-draft"
|
|
3875
|
+
};
|
|
3876
|
+
document2.diagnostics.push(...collectAtlasDiagnostics(document2, sourceSchemaVersion));
|
|
3877
|
+
return document2;
|
|
3878
|
+
}
|
|
3879
|
+
const document = {
|
|
3880
|
+
...baseDocument,
|
|
3881
|
+
version: outputVersion,
|
|
3882
|
+
schemaVersion: outputVersion
|
|
3883
|
+
};
|
|
3884
|
+
if (sourceSchemaVersion === "2.0-draft") {
|
|
3885
|
+
document.diagnostics.push({
|
|
3886
|
+
code: "load.schema.deprecatedDraft",
|
|
3887
|
+
severity: "warning",
|
|
3888
|
+
source: "upgrade",
|
|
3889
|
+
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
3890
|
+
});
|
|
3891
|
+
}
|
|
3892
|
+
document.diagnostics.push(...collectAtlasDiagnostics(document, sourceSchemaVersion));
|
|
3893
|
+
return document;
|
|
3894
|
+
}
|
|
3895
|
+
function assertDraftSchemaHeader(tokens, line) {
|
|
3896
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1"].includes(tokens[1].value.toLowerCase())) {
|
|
3897
|
+
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
3898
|
+
}
|
|
3899
|
+
const version = tokens[1].value.toLowerCase();
|
|
3900
|
+
return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
3901
|
+
}
|
|
3902
|
+
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, flags) {
|
|
3903
|
+
const keyword = tokens[0]?.value.toLowerCase();
|
|
3904
|
+
switch (keyword) {
|
|
3905
|
+
case "system":
|
|
3906
|
+
if (system) {
|
|
3907
|
+
throw new WorldOrbitError('Atlas section "system" may only appear once', line, tokens[0].column);
|
|
3908
|
+
}
|
|
3909
|
+
return startSystemSection(tokens, line, sourceSchemaVersion, diagnostics);
|
|
3910
|
+
case "defaults":
|
|
3911
|
+
if (!system) {
|
|
3912
|
+
throw new WorldOrbitError('Atlas section "defaults" requires a preceding system declaration', line, tokens[0].column);
|
|
3913
|
+
}
|
|
3914
|
+
if (flags.sawDefaults) {
|
|
3915
|
+
throw new WorldOrbitError('Atlas section "defaults" may only appear once', line, tokens[0].column);
|
|
3916
|
+
}
|
|
3917
|
+
return {
|
|
3918
|
+
kind: "defaults",
|
|
3919
|
+
system,
|
|
3920
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
3921
|
+
};
|
|
3922
|
+
case "atlas":
|
|
3923
|
+
if (!system) {
|
|
3924
|
+
throw new WorldOrbitError('Atlas section "atlas" requires a preceding system declaration', line, tokens[0].column);
|
|
3925
|
+
}
|
|
3926
|
+
if (flags.sawAtlas) {
|
|
3927
|
+
throw new WorldOrbitError('Atlas section "atlas" may only appear once', line, tokens[0].column);
|
|
3928
|
+
}
|
|
3929
|
+
return {
|
|
3930
|
+
kind: "atlas",
|
|
3931
|
+
system,
|
|
3932
|
+
inMetadata: false,
|
|
3933
|
+
metadataIndent: null
|
|
3934
|
+
};
|
|
3935
|
+
case "viewpoint":
|
|
3936
|
+
if (!system) {
|
|
3937
|
+
throw new WorldOrbitError('Atlas section "viewpoint" requires a preceding system declaration', line, tokens[0].column);
|
|
3938
|
+
}
|
|
3939
|
+
return startViewpointSection(tokens, line, system, viewpointIds);
|
|
3940
|
+
case "annotation":
|
|
3941
|
+
if (!system) {
|
|
3942
|
+
throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
|
|
3943
|
+
}
|
|
3944
|
+
return startAnnotationSection(tokens, line, system, annotationIds);
|
|
3945
|
+
case "group":
|
|
3946
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "group", { line, column: tokens[0].column });
|
|
3947
|
+
return startGroupSection(tokens, line, groups, groupIds);
|
|
3948
|
+
case "relation":
|
|
3949
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "relation", { line, column: tokens[0].column });
|
|
3950
|
+
return startRelationSection(tokens, line, relations, relationIds);
|
|
3951
|
+
case "object":
|
|
3952
|
+
return startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes);
|
|
3953
|
+
default:
|
|
3954
|
+
throw new WorldOrbitError(`Unknown atlas section "${tokens[0]?.value ?? ""}"`, line, tokens[0]?.column ?? 1);
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
function startSystemSection(tokens, line, sourceSchemaVersion, diagnostics) {
|
|
3958
|
+
if (tokens.length !== 2) {
|
|
3959
|
+
throw new WorldOrbitError("Invalid atlas system declaration", line, tokens[0]?.column ?? 1);
|
|
3960
|
+
}
|
|
3961
|
+
const system = {
|
|
3962
|
+
type: "system",
|
|
3963
|
+
id: tokens[1].value,
|
|
3964
|
+
title: null,
|
|
3965
|
+
description: null,
|
|
3966
|
+
epoch: null,
|
|
3967
|
+
referencePlane: null,
|
|
3968
|
+
defaults: {
|
|
3969
|
+
view: "topdown",
|
|
3970
|
+
scale: null,
|
|
3971
|
+
units: null,
|
|
3972
|
+
preset: null,
|
|
3973
|
+
theme: null
|
|
3974
|
+
},
|
|
3975
|
+
atlasMetadata: {},
|
|
3976
|
+
viewpoints: [],
|
|
3977
|
+
annotations: []
|
|
3978
|
+
};
|
|
3979
|
+
return {
|
|
3980
|
+
kind: "system",
|
|
3981
|
+
system,
|
|
3982
|
+
sourceSchemaVersion,
|
|
3983
|
+
diagnostics,
|
|
3984
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
3985
|
+
};
|
|
3986
|
+
}
|
|
3987
|
+
function startViewpointSection(tokens, line, system, viewpointIds) {
|
|
3988
|
+
if (tokens.length !== 2) {
|
|
3989
|
+
throw new WorldOrbitError("Invalid viewpoint declaration", line, tokens[0]?.column ?? 1);
|
|
3990
|
+
}
|
|
3991
|
+
const id = normalizeIdentifier2(tokens[1].value);
|
|
3992
|
+
if (!id) {
|
|
3993
|
+
throw new WorldOrbitError("Viewpoint id must not be empty", line, tokens[1].column);
|
|
3192
3994
|
}
|
|
3193
3995
|
if (viewpointIds.has(id)) {
|
|
3194
3996
|
throw new WorldOrbitError(`Duplicate viewpoint id "${id}"`, line, tokens[1].column);
|
|
@@ -3244,7 +4046,64 @@
|
|
|
3244
4046
|
seenFields: /* @__PURE__ */ new Set()
|
|
3245
4047
|
};
|
|
3246
4048
|
}
|
|
3247
|
-
function
|
|
4049
|
+
function startGroupSection(tokens, line, groups, groupIds) {
|
|
4050
|
+
if (tokens.length !== 2) {
|
|
4051
|
+
throw new WorldOrbitError("Invalid group declaration", line, tokens[0]?.column ?? 1);
|
|
4052
|
+
}
|
|
4053
|
+
const id = normalizeIdentifier2(tokens[1].value);
|
|
4054
|
+
if (!id) {
|
|
4055
|
+
throw new WorldOrbitError("Group id must not be empty", line, tokens[1].column);
|
|
4056
|
+
}
|
|
4057
|
+
if (groupIds.has(id)) {
|
|
4058
|
+
throw new WorldOrbitError(`Duplicate group id "${id}"`, line, tokens[1].column);
|
|
4059
|
+
}
|
|
4060
|
+
const group = {
|
|
4061
|
+
id,
|
|
4062
|
+
label: humanizeIdentifier3(id),
|
|
4063
|
+
summary: "",
|
|
4064
|
+
color: null,
|
|
4065
|
+
tags: [],
|
|
4066
|
+
hidden: false
|
|
4067
|
+
};
|
|
4068
|
+
groups.push(group);
|
|
4069
|
+
groupIds.add(id);
|
|
4070
|
+
return {
|
|
4071
|
+
kind: "group",
|
|
4072
|
+
group,
|
|
4073
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
4074
|
+
};
|
|
4075
|
+
}
|
|
4076
|
+
function startRelationSection(tokens, line, relations, relationIds) {
|
|
4077
|
+
if (tokens.length !== 2) {
|
|
4078
|
+
throw new WorldOrbitError("Invalid relation declaration", line, tokens[0]?.column ?? 1);
|
|
4079
|
+
}
|
|
4080
|
+
const id = normalizeIdentifier2(tokens[1].value);
|
|
4081
|
+
if (!id) {
|
|
4082
|
+
throw new WorldOrbitError("Relation id must not be empty", line, tokens[1].column);
|
|
4083
|
+
}
|
|
4084
|
+
if (relationIds.has(id)) {
|
|
4085
|
+
throw new WorldOrbitError(`Duplicate relation id "${id}"`, line, tokens[1].column);
|
|
4086
|
+
}
|
|
4087
|
+
const relation = {
|
|
4088
|
+
id,
|
|
4089
|
+
from: "",
|
|
4090
|
+
to: "",
|
|
4091
|
+
kind: "",
|
|
4092
|
+
label: null,
|
|
4093
|
+
summary: null,
|
|
4094
|
+
tags: [],
|
|
4095
|
+
color: null,
|
|
4096
|
+
hidden: false
|
|
4097
|
+
};
|
|
4098
|
+
relations.push(relation);
|
|
4099
|
+
relationIds.add(id);
|
|
4100
|
+
return {
|
|
4101
|
+
kind: "relation",
|
|
4102
|
+
relation,
|
|
4103
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
4104
|
+
};
|
|
4105
|
+
}
|
|
4106
|
+
function startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes) {
|
|
3248
4107
|
if (tokens.length < 3) {
|
|
3249
4108
|
throw new WorldOrbitError("Invalid atlas object declaration", line, tokens[0]?.column ?? 1);
|
|
3250
4109
|
}
|
|
@@ -3255,12 +4114,11 @@
|
|
|
3255
4114
|
throw new WorldOrbitError(`Unknown object type "${objectTypeToken.value}"`, line, objectTypeToken.column);
|
|
3256
4115
|
}
|
|
3257
4116
|
const objectNode = {
|
|
3258
|
-
type: "object",
|
|
3259
4117
|
objectType,
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
blockFields: [],
|
|
4118
|
+
id: idToken.value,
|
|
4119
|
+
fields: parseInlineObjectFields(tokens.slice(3), line, objectType, sourceSchemaVersion, diagnostics),
|
|
3263
4120
|
infoEntries: [],
|
|
4121
|
+
typedBlockEntries: {},
|
|
3264
4122
|
location: {
|
|
3265
4123
|
line,
|
|
3266
4124
|
column: objectTypeToken.column
|
|
@@ -3270,8 +4128,12 @@
|
|
|
3270
4128
|
return {
|
|
3271
4129
|
kind: "object",
|
|
3272
4130
|
objectNode,
|
|
3273
|
-
|
|
3274
|
-
|
|
4131
|
+
sourceSchemaVersion,
|
|
4132
|
+
diagnostics,
|
|
4133
|
+
activeBlock: null,
|
|
4134
|
+
blockIndent: null,
|
|
4135
|
+
seenInfoKeys: /* @__PURE__ */ new Set(),
|
|
4136
|
+
seenTypedBlockKeys: {}
|
|
3275
4137
|
};
|
|
3276
4138
|
}
|
|
3277
4139
|
function handleSectionLine(section, indent, tokens, line) {
|
|
@@ -3291,6 +4153,12 @@
|
|
|
3291
4153
|
case "annotation":
|
|
3292
4154
|
applyAnnotationField(section, tokens, line);
|
|
3293
4155
|
return;
|
|
4156
|
+
case "group":
|
|
4157
|
+
applyGroupField(section, tokens, line);
|
|
4158
|
+
return;
|
|
4159
|
+
case "relation":
|
|
4160
|
+
applyRelationField(section, tokens, line);
|
|
4161
|
+
return;
|
|
3294
4162
|
case "object":
|
|
3295
4163
|
applyObjectField(section, indent, tokens, line);
|
|
3296
4164
|
return;
|
|
@@ -3298,10 +4166,35 @@
|
|
|
3298
4166
|
}
|
|
3299
4167
|
function applySystemField(section, tokens, line) {
|
|
3300
4168
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
3301
|
-
|
|
3302
|
-
|
|
4169
|
+
const value = joinFieldValue(tokens, line);
|
|
4170
|
+
switch (key) {
|
|
4171
|
+
case "title":
|
|
4172
|
+
section.system.title = value;
|
|
4173
|
+
return;
|
|
4174
|
+
case "description":
|
|
4175
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
4176
|
+
line,
|
|
4177
|
+
column: tokens[0].column
|
|
4178
|
+
});
|
|
4179
|
+
section.system.description = value;
|
|
4180
|
+
return;
|
|
4181
|
+
case "epoch":
|
|
4182
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
4183
|
+
line,
|
|
4184
|
+
column: tokens[0].column
|
|
4185
|
+
});
|
|
4186
|
+
section.system.epoch = value;
|
|
4187
|
+
return;
|
|
4188
|
+
case "referenceplane":
|
|
4189
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, "referencePlane", {
|
|
4190
|
+
line,
|
|
4191
|
+
column: tokens[0].column
|
|
4192
|
+
});
|
|
4193
|
+
section.system.referencePlane = value;
|
|
4194
|
+
return;
|
|
4195
|
+
default:
|
|
4196
|
+
throw new WorldOrbitError(`Unknown system atlas field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3303
4197
|
}
|
|
3304
|
-
section.system.title = joinFieldValue(tokens, line);
|
|
3305
4198
|
}
|
|
3306
4199
|
function applyDefaultsField(section, tokens, line) {
|
|
3307
4200
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
@@ -3332,14 +4225,11 @@
|
|
|
3332
4225
|
section.metadataIndent = null;
|
|
3333
4226
|
}
|
|
3334
4227
|
if (section.inMetadata) {
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
const key = tokens[0].value;
|
|
3339
|
-
if (key in section.system.atlasMetadata) {
|
|
3340
|
-
throw new WorldOrbitError(`Duplicate atlas metadata key "${key}"`, line, tokens[0].column);
|
|
4228
|
+
const entry = parseInfoLikeEntry(tokens, line, "Invalid atlas metadata entry");
|
|
4229
|
+
if (entry.key in section.system.atlasMetadata) {
|
|
4230
|
+
throw new WorldOrbitError(`Duplicate atlas metadata key "${entry.key}"`, line, tokens[0].column);
|
|
3341
4231
|
}
|
|
3342
|
-
section.system.atlasMetadata[key] =
|
|
4232
|
+
section.system.atlasMetadata[entry.key] = entry.value;
|
|
3343
4233
|
return;
|
|
3344
4234
|
}
|
|
3345
4235
|
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "metadata") {
|
|
@@ -3412,50 +4302,131 @@
|
|
|
3412
4302
|
filter.objectTypes = parseObjectTypeTokens(tokens.slice(1), line);
|
|
3413
4303
|
break;
|
|
3414
4304
|
case "tags":
|
|
3415
|
-
filter.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
3416
|
-
break;
|
|
3417
|
-
case "groups":
|
|
3418
|
-
filter.groupIds = parseTokenList(tokens.slice(1), line, "groups");
|
|
3419
|
-
break;
|
|
4305
|
+
filter.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4306
|
+
break;
|
|
4307
|
+
case "groups":
|
|
4308
|
+
filter.groupIds = parseTokenList(tokens.slice(1), line, "groups");
|
|
4309
|
+
break;
|
|
4310
|
+
default:
|
|
4311
|
+
throw new WorldOrbitError(`Unknown viewpoint filter field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4312
|
+
}
|
|
4313
|
+
section.viewpoint.filter = filter;
|
|
4314
|
+
}
|
|
4315
|
+
function applyAnnotationField(section, tokens, line) {
|
|
4316
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
4317
|
+
switch (key) {
|
|
4318
|
+
case "label":
|
|
4319
|
+
section.annotation.label = joinFieldValue(tokens, line);
|
|
4320
|
+
return;
|
|
4321
|
+
case "target":
|
|
4322
|
+
section.annotation.targetObjectId = joinFieldValue(tokens, line);
|
|
4323
|
+
return;
|
|
4324
|
+
case "body":
|
|
4325
|
+
section.annotation.body = joinFieldValue(tokens, line);
|
|
4326
|
+
return;
|
|
4327
|
+
case "tags":
|
|
4328
|
+
section.annotation.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4329
|
+
return;
|
|
4330
|
+
default:
|
|
4331
|
+
throw new WorldOrbitError(`Unknown annotation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4332
|
+
}
|
|
4333
|
+
}
|
|
4334
|
+
function applyGroupField(section, tokens, line) {
|
|
4335
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
4336
|
+
switch (key) {
|
|
4337
|
+
case "label":
|
|
4338
|
+
section.group.label = joinFieldValue(tokens, line);
|
|
4339
|
+
return;
|
|
4340
|
+
case "summary":
|
|
4341
|
+
section.group.summary = joinFieldValue(tokens, line);
|
|
4342
|
+
return;
|
|
4343
|
+
case "color":
|
|
4344
|
+
section.group.color = joinFieldValue(tokens, line);
|
|
4345
|
+
return;
|
|
4346
|
+
case "tags":
|
|
4347
|
+
section.group.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4348
|
+
return;
|
|
4349
|
+
case "hidden":
|
|
4350
|
+
section.group.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4351
|
+
line,
|
|
4352
|
+
column: tokens[0].column
|
|
4353
|
+
});
|
|
4354
|
+
return;
|
|
3420
4355
|
default:
|
|
3421
|
-
throw new WorldOrbitError(`Unknown
|
|
4356
|
+
throw new WorldOrbitError(`Unknown group field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3422
4357
|
}
|
|
3423
|
-
section.viewpoint.filter = filter;
|
|
3424
4358
|
}
|
|
3425
|
-
function
|
|
4359
|
+
function applyRelationField(section, tokens, line) {
|
|
3426
4360
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
3427
4361
|
switch (key) {
|
|
3428
|
-
case "
|
|
3429
|
-
section.
|
|
4362
|
+
case "from":
|
|
4363
|
+
section.relation.from = joinFieldValue(tokens, line);
|
|
3430
4364
|
return;
|
|
3431
|
-
case "
|
|
3432
|
-
section.
|
|
4365
|
+
case "to":
|
|
4366
|
+
section.relation.to = joinFieldValue(tokens, line);
|
|
3433
4367
|
return;
|
|
3434
|
-
case "
|
|
3435
|
-
section.
|
|
4368
|
+
case "kind":
|
|
4369
|
+
section.relation.kind = joinFieldValue(tokens, line);
|
|
4370
|
+
return;
|
|
4371
|
+
case "label":
|
|
4372
|
+
section.relation.label = joinFieldValue(tokens, line);
|
|
4373
|
+
return;
|
|
4374
|
+
case "summary":
|
|
4375
|
+
section.relation.summary = joinFieldValue(tokens, line);
|
|
3436
4376
|
return;
|
|
3437
4377
|
case "tags":
|
|
3438
|
-
section.
|
|
4378
|
+
section.relation.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4379
|
+
return;
|
|
4380
|
+
case "color":
|
|
4381
|
+
section.relation.color = joinFieldValue(tokens, line);
|
|
4382
|
+
return;
|
|
4383
|
+
case "hidden":
|
|
4384
|
+
section.relation.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4385
|
+
line,
|
|
4386
|
+
column: tokens[0].column
|
|
4387
|
+
});
|
|
3439
4388
|
return;
|
|
3440
4389
|
default:
|
|
3441
|
-
throw new WorldOrbitError(`Unknown
|
|
4390
|
+
throw new WorldOrbitError(`Unknown relation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3442
4391
|
}
|
|
3443
4392
|
}
|
|
3444
4393
|
function applyObjectField(section, indent, tokens, line) {
|
|
3445
|
-
if (
|
|
3446
|
-
section.
|
|
3447
|
-
section.
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
4394
|
+
if (section.activeBlock && indent <= (section.blockIndent ?? 0)) {
|
|
4395
|
+
section.activeBlock = null;
|
|
4396
|
+
section.blockIndent = null;
|
|
4397
|
+
}
|
|
4398
|
+
if (tokens.length === 1) {
|
|
4399
|
+
const blockName = tokens[0].value.toLowerCase();
|
|
4400
|
+
if (blockName === "info" || STRUCTURED_TYPED_BLOCKS.has(blockName)) {
|
|
4401
|
+
if (blockName !== "info") {
|
|
4402
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, blockName, { line, column: tokens[0].column });
|
|
4403
|
+
}
|
|
4404
|
+
section.activeBlock = blockName;
|
|
4405
|
+
section.blockIndent = indent;
|
|
4406
|
+
return;
|
|
4407
|
+
}
|
|
3453
4408
|
}
|
|
3454
|
-
if (section.
|
|
3455
|
-
|
|
4409
|
+
if (section.activeBlock) {
|
|
4410
|
+
const entry = parseInfoLikeEntry(tokens, line, `Invalid ${section.activeBlock} entry`);
|
|
4411
|
+
if (section.activeBlock === "info") {
|
|
4412
|
+
if (section.seenInfoKeys.has(entry.key)) {
|
|
4413
|
+
throw new WorldOrbitError(`Duplicate info key "${entry.key}"`, line, tokens[0].column);
|
|
4414
|
+
}
|
|
4415
|
+
section.seenInfoKeys.add(entry.key);
|
|
4416
|
+
section.objectNode.infoEntries.push(entry);
|
|
4417
|
+
return;
|
|
4418
|
+
}
|
|
4419
|
+
const typedBlock = section.activeBlock;
|
|
4420
|
+
const seenKeys = section.seenTypedBlockKeys[typedBlock] ?? (section.seenTypedBlockKeys[typedBlock] = /* @__PURE__ */ new Set());
|
|
4421
|
+
if (seenKeys.has(entry.key)) {
|
|
4422
|
+
throw new WorldOrbitError(`Duplicate ${typedBlock} key "${entry.key}"`, line, tokens[0].column);
|
|
4423
|
+
}
|
|
4424
|
+
seenKeys.add(entry.key);
|
|
4425
|
+
const entries = section.objectNode.typedBlockEntries[typedBlock] ?? (section.objectNode.typedBlockEntries[typedBlock] = []);
|
|
4426
|
+
entries.push(entry);
|
|
3456
4427
|
return;
|
|
3457
4428
|
}
|
|
3458
|
-
section.objectNode.
|
|
4429
|
+
section.objectNode.fields.push(parseObjectField(tokens, line, section.objectNode.objectType, section.sourceSchemaVersion, section.diagnostics));
|
|
3459
4430
|
}
|
|
3460
4431
|
function requireUniqueField(tokens, seenFields, line) {
|
|
3461
4432
|
if (tokens.length < 2) {
|
|
@@ -3475,50 +4446,40 @@
|
|
|
3475
4446
|
return tokens.slice(1).map((token) => token.value).join(" ").trim();
|
|
3476
4447
|
}
|
|
3477
4448
|
function parseObjectTypeTokens(tokens, line) {
|
|
3478
|
-
|
|
3479
|
-
throw new WorldOrbitError("Missing value for atlas field", line);
|
|
3480
|
-
}
|
|
3481
|
-
return tokens.map((token) => {
|
|
3482
|
-
const value = token.value;
|
|
3483
|
-
if (value !== "star" && value !== "planet" && value !== "moon" && value !== "belt" && value !== "asteroid" && value !== "comet" && value !== "ring" && value !== "structure" && value !== "phenomenon") {
|
|
3484
|
-
throw new WorldOrbitError(`Unknown viewpoint object type "${token.value}"`, line, token.column);
|
|
3485
|
-
}
|
|
3486
|
-
return value;
|
|
3487
|
-
});
|
|
3488
|
-
}
|
|
3489
|
-
function parseTokenList(tokens, line, field) {
|
|
3490
|
-
if (tokens.length === 0) {
|
|
3491
|
-
throw new WorldOrbitError(`Missing value for field "${field}"`, line);
|
|
3492
|
-
}
|
|
3493
|
-
return tokens.map((token) => token.value);
|
|
4449
|
+
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");
|
|
3494
4450
|
}
|
|
3495
4451
|
function parseLayerTokens(tokens, line) {
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
if (rawLayer === "orbits") {
|
|
3504
|
-
next["orbits-back"] = enabled;
|
|
3505
|
-
next["orbits-front"] = enabled;
|
|
4452
|
+
const layers = {};
|
|
4453
|
+
for (const token of parseTokenList(tokens, line, "layers")) {
|
|
4454
|
+
const enabled = !token.startsWith("-") && !token.startsWith("!");
|
|
4455
|
+
const raw = token.replace(/^[-!]+/, "").toLowerCase();
|
|
4456
|
+
if (raw === "orbits") {
|
|
4457
|
+
layers["orbits-back"] = enabled;
|
|
4458
|
+
layers["orbits-front"] = enabled;
|
|
3506
4459
|
continue;
|
|
3507
4460
|
}
|
|
3508
|
-
if (
|
|
3509
|
-
|
|
3510
|
-
continue;
|
|
4461
|
+
if (raw === "background" || raw === "guides" || raw === "orbits-back" || raw === "orbits-front" || raw === "relations" || raw === "objects" || raw === "labels" || raw === "metadata") {
|
|
4462
|
+
layers[raw] = enabled;
|
|
3511
4463
|
}
|
|
3512
|
-
throw new WorldOrbitError(`Unknown layer token "${token.value}"`, line, token.column);
|
|
3513
4464
|
}
|
|
3514
|
-
return
|
|
4465
|
+
return layers;
|
|
4466
|
+
}
|
|
4467
|
+
function parseTokenList(tokens, line, fieldName) {
|
|
4468
|
+
if (tokens.length === 0) {
|
|
4469
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, 1);
|
|
4470
|
+
}
|
|
4471
|
+
const values = tokens.map((token) => token.value).filter(Boolean);
|
|
4472
|
+
if (values.length === 0) {
|
|
4473
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, tokens[0]?.column ?? 1);
|
|
4474
|
+
}
|
|
4475
|
+
return values;
|
|
3515
4476
|
}
|
|
3516
4477
|
function parseProjectionValue(value, line, column) {
|
|
3517
4478
|
const normalized = value.toLowerCase();
|
|
3518
|
-
if (normalized
|
|
3519
|
-
|
|
4479
|
+
if (normalized !== "topdown" && normalized !== "isometric") {
|
|
4480
|
+
throw new WorldOrbitError(`Unknown projection "${value}"`, line, column);
|
|
3520
4481
|
}
|
|
3521
|
-
|
|
4482
|
+
return normalized;
|
|
3522
4483
|
}
|
|
3523
4484
|
function parsePresetValue(value, line, column) {
|
|
3524
4485
|
const normalized = value.toLowerCase();
|
|
@@ -3528,16 +4489,16 @@
|
|
|
3528
4489
|
throw new WorldOrbitError(`Unknown render preset "${value}"`, line, column);
|
|
3529
4490
|
}
|
|
3530
4491
|
function parsePositiveNumber2(value, line, column, field) {
|
|
3531
|
-
const parsed =
|
|
3532
|
-
if (
|
|
3533
|
-
throw new WorldOrbitError(`Field "${field}"
|
|
4492
|
+
const parsed = parseFiniteNumber2(value, line, column, field);
|
|
4493
|
+
if (parsed <= 0) {
|
|
4494
|
+
throw new WorldOrbitError(`Field "${field}" must be greater than zero`, line, column);
|
|
3534
4495
|
}
|
|
3535
4496
|
return parsed;
|
|
3536
4497
|
}
|
|
3537
4498
|
function parseFiniteNumber2(value, line, column, field) {
|
|
3538
4499
|
const parsed = Number(value);
|
|
3539
4500
|
if (!Number.isFinite(parsed)) {
|
|
3540
|
-
throw new WorldOrbitError(`
|
|
4501
|
+
throw new WorldOrbitError(`Invalid numeric value "${value}" for "${field}"`, line, column);
|
|
3541
4502
|
}
|
|
3542
4503
|
return parsed;
|
|
3543
4504
|
}
|
|
@@ -3549,28 +4510,43 @@
|
|
|
3549
4510
|
groupIds: []
|
|
3550
4511
|
};
|
|
3551
4512
|
}
|
|
3552
|
-
function
|
|
4513
|
+
function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
3553
4514
|
const fields = [];
|
|
3554
4515
|
let index = 0;
|
|
3555
4516
|
while (index < tokens.length) {
|
|
3556
4517
|
const keyToken = tokens[index];
|
|
3557
|
-
const
|
|
3558
|
-
if (!
|
|
4518
|
+
const spec = getDraftObjectFieldSpec(keyToken.value);
|
|
4519
|
+
if (!spec) {
|
|
3559
4520
|
throw new WorldOrbitError(`Unknown field "${keyToken.value}"`, line, keyToken.column);
|
|
3560
4521
|
}
|
|
4522
|
+
if (spec.version === "2.1") {
|
|
4523
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, keyToken.value, {
|
|
4524
|
+
line,
|
|
4525
|
+
column: keyToken.column
|
|
4526
|
+
});
|
|
4527
|
+
}
|
|
3561
4528
|
index++;
|
|
3562
4529
|
const valueTokens = [];
|
|
3563
|
-
if (
|
|
3564
|
-
while (index < tokens.length && !isKnownFieldKey(tokens[index].value)) {
|
|
3565
|
-
valueTokens.push(tokens[index]);
|
|
3566
|
-
index++;
|
|
3567
|
-
}
|
|
3568
|
-
} else {
|
|
4530
|
+
if (spec.inlineMode === "single") {
|
|
3569
4531
|
const nextToken = tokens[index];
|
|
3570
4532
|
if (nextToken) {
|
|
3571
4533
|
valueTokens.push(nextToken);
|
|
3572
4534
|
index++;
|
|
3573
4535
|
}
|
|
4536
|
+
} else if (spec.inlineMode === "pair") {
|
|
4537
|
+
for (let count = 0; count < 2; count++) {
|
|
4538
|
+
const nextToken = tokens[index];
|
|
4539
|
+
if (!nextToken) {
|
|
4540
|
+
break;
|
|
4541
|
+
}
|
|
4542
|
+
valueTokens.push(nextToken);
|
|
4543
|
+
index++;
|
|
4544
|
+
}
|
|
4545
|
+
} else {
|
|
4546
|
+
while (index < tokens.length && !DRAFT_OBJECT_FIELD_KEYS.has(tokens[index].value)) {
|
|
4547
|
+
valueTokens.push(tokens[index]);
|
|
4548
|
+
index++;
|
|
4549
|
+
}
|
|
3574
4550
|
}
|
|
3575
4551
|
if (valueTokens.length === 0) {
|
|
3576
4552
|
throw new WorldOrbitError(`Missing value for field "${keyToken.value}"`, line, keyToken.column);
|
|
@@ -3582,25 +4558,35 @@
|
|
|
3582
4558
|
location: { line, column: keyToken.column }
|
|
3583
4559
|
});
|
|
3584
4560
|
}
|
|
4561
|
+
validateDraftObjectFieldCompatibility(fields, objectType);
|
|
3585
4562
|
return fields;
|
|
3586
4563
|
}
|
|
3587
|
-
function
|
|
4564
|
+
function parseObjectField(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
3588
4565
|
if (tokens.length < 2) {
|
|
3589
4566
|
throw new WorldOrbitError("Invalid field line", line, tokens[0]?.column ?? 1);
|
|
3590
4567
|
}
|
|
3591
|
-
|
|
4568
|
+
const spec = getDraftObjectFieldSpec(tokens[0].value);
|
|
4569
|
+
if (!spec) {
|
|
3592
4570
|
throw new WorldOrbitError(`Unknown field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3593
4571
|
}
|
|
3594
|
-
|
|
4572
|
+
if (spec.version === "2.1") {
|
|
4573
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, tokens[0].value, {
|
|
4574
|
+
line,
|
|
4575
|
+
column: tokens[0].column
|
|
4576
|
+
});
|
|
4577
|
+
}
|
|
4578
|
+
const field = {
|
|
3595
4579
|
type: "field",
|
|
3596
4580
|
key: tokens[0].value,
|
|
3597
4581
|
values: tokens.slice(1).map((token) => token.value),
|
|
3598
4582
|
location: { line, column: tokens[0].column }
|
|
3599
4583
|
};
|
|
4584
|
+
validateDraftObjectFieldCompatibility([field], objectType);
|
|
4585
|
+
return field;
|
|
3600
4586
|
}
|
|
3601
|
-
function
|
|
4587
|
+
function parseInfoLikeEntry(tokens, line, errorMessage) {
|
|
3602
4588
|
if (tokens.length < 2) {
|
|
3603
|
-
throw new WorldOrbitError(
|
|
4589
|
+
throw new WorldOrbitError(errorMessage, line, tokens[0]?.column ?? 1);
|
|
3604
4590
|
}
|
|
3605
4591
|
return {
|
|
3606
4592
|
type: "info-entry",
|
|
@@ -3609,23 +4595,356 @@
|
|
|
3609
4595
|
location: { line, column: tokens[0].column }
|
|
3610
4596
|
};
|
|
3611
4597
|
}
|
|
3612
|
-
function
|
|
3613
|
-
|
|
4598
|
+
function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
|
|
4599
|
+
const fieldMap = collectDraftFields(node.fields);
|
|
4600
|
+
const placement = extractDraftPlacement(node.objectType, fieldMap);
|
|
4601
|
+
const properties = normalizeDraftProperties(node.objectType, fieldMap);
|
|
4602
|
+
const groups = parseOptionalTokenList(fieldMap.get("groups")?.[0]);
|
|
4603
|
+
const epoch = parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]);
|
|
4604
|
+
const referencePlane = parseOptionalJoinedValue(fieldMap.get("referencePlane")?.[0]);
|
|
4605
|
+
const tidalLock = fieldMap.has("tidalLock") ? parseAtlasBoolean(singleFieldValue2(fieldMap.get("tidalLock")[0]), "tidalLock", fieldMap.get("tidalLock")[0].location) : void 0;
|
|
4606
|
+
const resonance = fieldMap.has("resonance") ? parseResonanceField(fieldMap.get("resonance")[0]) : void 0;
|
|
4607
|
+
const renderHints = extractRenderHints(fieldMap);
|
|
4608
|
+
const deriveRules = fieldMap.get("derive")?.map((field) => parseDeriveField(field));
|
|
4609
|
+
const validationRules = fieldMap.get("validate")?.map((field) => ({
|
|
4610
|
+
rule: singleFieldValue2(field)
|
|
4611
|
+
}));
|
|
4612
|
+
const lockedFields = fieldMap.has("locked") ? [...new Set(fieldMap.get("locked").flatMap((field) => field.values))] : void 0;
|
|
4613
|
+
const tolerances = fieldMap.get("tolerance")?.map((field) => parseToleranceField(field));
|
|
4614
|
+
const typedBlocks = normalizeTypedBlocks(node.typedBlockEntries);
|
|
4615
|
+
const info2 = normalizeInfoEntries(node.infoEntries, "info");
|
|
4616
|
+
const object = {
|
|
4617
|
+
type: node.objectType,
|
|
4618
|
+
id: node.id,
|
|
4619
|
+
properties,
|
|
4620
|
+
placement,
|
|
4621
|
+
info: info2
|
|
4622
|
+
};
|
|
4623
|
+
if (groups.length > 0)
|
|
4624
|
+
object.groups = groups;
|
|
4625
|
+
if (epoch)
|
|
4626
|
+
object.epoch = epoch;
|
|
4627
|
+
if (referencePlane)
|
|
4628
|
+
object.referencePlane = referencePlane;
|
|
4629
|
+
if (tidalLock !== void 0)
|
|
4630
|
+
object.tidalLock = tidalLock;
|
|
4631
|
+
if (resonance)
|
|
4632
|
+
object.resonance = resonance;
|
|
4633
|
+
if (renderHints)
|
|
4634
|
+
object.renderHints = renderHints;
|
|
4635
|
+
if (deriveRules?.length)
|
|
4636
|
+
object.deriveRules = deriveRules;
|
|
4637
|
+
if (validationRules?.length)
|
|
4638
|
+
object.validationRules = validationRules;
|
|
4639
|
+
if (lockedFields?.length)
|
|
4640
|
+
object.lockedFields = lockedFields;
|
|
4641
|
+
if (tolerances?.length)
|
|
4642
|
+
object.tolerances = tolerances;
|
|
4643
|
+
if (typedBlocks && Object.keys(typedBlocks).length > 0)
|
|
4644
|
+
object.typedBlocks = typedBlocks;
|
|
4645
|
+
if (sourceSchemaVersion !== "2.1") {
|
|
4646
|
+
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) {
|
|
4647
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, node.id, node.location);
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
return object;
|
|
3614
4651
|
}
|
|
3615
|
-
function
|
|
3616
|
-
|
|
4652
|
+
function collectDraftFields(fields) {
|
|
4653
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4654
|
+
for (const field of fields) {
|
|
4655
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
4656
|
+
if (!spec) {
|
|
4657
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4658
|
+
}
|
|
4659
|
+
if (!spec.allowRepeat && grouped.has(field.key)) {
|
|
4660
|
+
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
4661
|
+
}
|
|
4662
|
+
const existing = grouped.get(field.key) ?? [];
|
|
4663
|
+
existing.push(field);
|
|
4664
|
+
grouped.set(field.key, existing);
|
|
4665
|
+
}
|
|
4666
|
+
return grouped;
|
|
4667
|
+
}
|
|
4668
|
+
function extractDraftPlacement(objectType, fieldMap) {
|
|
4669
|
+
const orbitField = fieldMap.get("orbit")?.[0];
|
|
4670
|
+
const atField = fieldMap.get("at")?.[0];
|
|
4671
|
+
const surfaceField = fieldMap.get("surface")?.[0];
|
|
4672
|
+
const freeField = fieldMap.get("free")?.[0];
|
|
4673
|
+
const count = [orbitField, atField, surfaceField, freeField].filter(Boolean).length;
|
|
4674
|
+
if (count > 1) {
|
|
4675
|
+
const conflictingField = orbitField ?? atField ?? surfaceField ?? freeField;
|
|
4676
|
+
throw WorldOrbitError.fromLocation("Object has multiple placement modes", conflictingField?.location);
|
|
4677
|
+
}
|
|
4678
|
+
if (orbitField) {
|
|
4679
|
+
return {
|
|
4680
|
+
mode: "orbit",
|
|
4681
|
+
target: singleFieldValue2(orbitField),
|
|
4682
|
+
distance: parseOptionalUnitField(fieldMap.get("distance")?.[0], "distance"),
|
|
4683
|
+
semiMajor: parseOptionalUnitField(fieldMap.get("semiMajor")?.[0], "semiMajor"),
|
|
4684
|
+
eccentricity: parseOptionalNumberField(fieldMap.get("eccentricity")?.[0], "eccentricity"),
|
|
4685
|
+
period: parseOptionalUnitField(fieldMap.get("period")?.[0], "period"),
|
|
4686
|
+
angle: parseOptionalUnitField(fieldMap.get("angle")?.[0], "angle"),
|
|
4687
|
+
inclination: parseOptionalUnitField(fieldMap.get("inclination")?.[0], "inclination"),
|
|
4688
|
+
phase: parseOptionalUnitField(fieldMap.get("phase")?.[0], "phase")
|
|
4689
|
+
};
|
|
4690
|
+
}
|
|
4691
|
+
if (atField) {
|
|
4692
|
+
const target = singleFieldValue2(atField);
|
|
4693
|
+
return {
|
|
4694
|
+
mode: "at",
|
|
4695
|
+
target,
|
|
4696
|
+
reference: parseAtlasAtReference(target, atField.location)
|
|
4697
|
+
};
|
|
4698
|
+
}
|
|
4699
|
+
if (surfaceField) {
|
|
4700
|
+
return {
|
|
4701
|
+
mode: "surface",
|
|
4702
|
+
target: singleFieldValue2(surfaceField)
|
|
4703
|
+
};
|
|
4704
|
+
}
|
|
4705
|
+
if (freeField) {
|
|
4706
|
+
const raw = singleFieldValue2(freeField);
|
|
4707
|
+
const distance = tryParseAtlasUnitValue(raw);
|
|
4708
|
+
return {
|
|
4709
|
+
mode: "free",
|
|
4710
|
+
distance: distance ?? void 0,
|
|
4711
|
+
descriptor: distance ? void 0 : raw
|
|
4712
|
+
};
|
|
4713
|
+
}
|
|
4714
|
+
return null;
|
|
4715
|
+
}
|
|
4716
|
+
function normalizeDraftProperties(objectType, fieldMap) {
|
|
4717
|
+
const properties = {};
|
|
4718
|
+
for (const [key, fields] of fieldMap.entries()) {
|
|
4719
|
+
const field = fields[0];
|
|
4720
|
+
const spec = getDraftObjectFieldSpec(key);
|
|
4721
|
+
if (!field || !spec?.legacySchema || spec.legacySchema.placement) {
|
|
4722
|
+
continue;
|
|
4723
|
+
}
|
|
4724
|
+
ensureAtlasFieldSupported(key, objectType, field.location);
|
|
4725
|
+
properties[key] = normalizeLegacyScalarValue(key, field.values, field.location);
|
|
4726
|
+
}
|
|
4727
|
+
return properties;
|
|
4728
|
+
}
|
|
4729
|
+
function normalizeInfoEntries(entries, label) {
|
|
4730
|
+
const normalized = {};
|
|
4731
|
+
for (const entry of entries) {
|
|
4732
|
+
if (entry.key in normalized) {
|
|
4733
|
+
throw WorldOrbitError.fromLocation(`Duplicate ${label} key "${entry.key}"`, entry.location);
|
|
4734
|
+
}
|
|
4735
|
+
normalized[entry.key] = entry.value;
|
|
4736
|
+
}
|
|
4737
|
+
return normalized;
|
|
4738
|
+
}
|
|
4739
|
+
function normalizeTypedBlocks(typedBlockEntries) {
|
|
4740
|
+
const typedBlocks = {};
|
|
4741
|
+
for (const blockName of Object.keys(typedBlockEntries)) {
|
|
4742
|
+
const entries = typedBlockEntries[blockName];
|
|
4743
|
+
if (entries?.length) {
|
|
4744
|
+
typedBlocks[blockName] = normalizeInfoEntries(entries, blockName);
|
|
4745
|
+
}
|
|
4746
|
+
}
|
|
4747
|
+
return typedBlocks;
|
|
4748
|
+
}
|
|
4749
|
+
function extractRenderHints(fieldMap) {
|
|
4750
|
+
const renderHints = {};
|
|
4751
|
+
const renderLabelField = fieldMap.get("renderLabel")?.[0];
|
|
4752
|
+
const renderOrbitField = fieldMap.get("renderOrbit")?.[0];
|
|
4753
|
+
const renderPriorityField = fieldMap.get("renderPriority")?.[0];
|
|
4754
|
+
if (renderLabelField) {
|
|
4755
|
+
renderHints.renderLabel = parseAtlasBoolean(singleFieldValue2(renderLabelField), "renderLabel", renderLabelField.location);
|
|
4756
|
+
}
|
|
4757
|
+
if (renderOrbitField) {
|
|
4758
|
+
renderHints.renderOrbit = parseAtlasBoolean(singleFieldValue2(renderOrbitField), "renderOrbit", renderOrbitField.location);
|
|
4759
|
+
}
|
|
4760
|
+
if (renderPriorityField) {
|
|
4761
|
+
renderHints.renderPriority = parseAtlasNumber(singleFieldValue2(renderPriorityField), "renderPriority", renderPriorityField.location);
|
|
4762
|
+
}
|
|
4763
|
+
return Object.keys(renderHints).length > 0 ? renderHints : void 0;
|
|
4764
|
+
}
|
|
4765
|
+
function parseResonanceField(field) {
|
|
4766
|
+
if (field.values.length !== 2) {
|
|
4767
|
+
throw WorldOrbitError.fromLocation('Field "resonance" expects "<targetObjectId> <ratio>"', field.location);
|
|
4768
|
+
}
|
|
4769
|
+
const ratio = field.values[1];
|
|
4770
|
+
if (!/^\d+:\d+$/.test(ratio)) {
|
|
4771
|
+
throw WorldOrbitError.fromLocation(`Invalid resonance ratio "${ratio}"`, field.location);
|
|
4772
|
+
}
|
|
4773
|
+
return {
|
|
4774
|
+
targetObjectId: field.values[0],
|
|
4775
|
+
ratio
|
|
4776
|
+
};
|
|
4777
|
+
}
|
|
4778
|
+
function parseDeriveField(field) {
|
|
4779
|
+
if (field.values.length !== 2) {
|
|
4780
|
+
throw WorldOrbitError.fromLocation('Field "derive" expects "<field> <strategy>"', field.location);
|
|
4781
|
+
}
|
|
4782
|
+
return {
|
|
4783
|
+
field: field.values[0],
|
|
4784
|
+
strategy: field.values[1]
|
|
4785
|
+
};
|
|
4786
|
+
}
|
|
4787
|
+
function parseToleranceField(field) {
|
|
4788
|
+
if (field.values.length !== 2) {
|
|
4789
|
+
throw WorldOrbitError.fromLocation('Field "tolerance" expects "<field> <value>"', field.location);
|
|
4790
|
+
}
|
|
4791
|
+
const rawValue = field.values[1];
|
|
4792
|
+
const unitValue = tryParseAtlasUnitValue(rawValue);
|
|
4793
|
+
const numericValue = Number(rawValue);
|
|
4794
|
+
return {
|
|
4795
|
+
field: field.values[0],
|
|
4796
|
+
value: unitValue ?? (Number.isFinite(numericValue) ? numericValue : rawValue)
|
|
4797
|
+
};
|
|
4798
|
+
}
|
|
4799
|
+
function parseOptionalTokenList(field) {
|
|
4800
|
+
return field ? [...new Set(field.values)] : [];
|
|
4801
|
+
}
|
|
4802
|
+
function parseOptionalJoinedValue(field) {
|
|
4803
|
+
if (!field) {
|
|
4804
|
+
return null;
|
|
4805
|
+
}
|
|
4806
|
+
return field.values.join(" ").trim() || null;
|
|
4807
|
+
}
|
|
4808
|
+
function parseOptionalUnitField(field, key) {
|
|
4809
|
+
return field ? parseAtlasUnitValue(singleFieldValue2(field), field.location, key) : void 0;
|
|
4810
|
+
}
|
|
4811
|
+
function parseOptionalNumberField(field, key) {
|
|
4812
|
+
return field ? parseAtlasNumber(singleFieldValue2(field), key, field.location) : void 0;
|
|
4813
|
+
}
|
|
4814
|
+
function singleFieldValue2(field) {
|
|
4815
|
+
return singleAtlasValue(field.values, field.key, field.location);
|
|
4816
|
+
}
|
|
4817
|
+
function getDraftObjectFieldSpec(key) {
|
|
4818
|
+
return DRAFT_OBJECT_FIELD_SPECS.get(key);
|
|
4819
|
+
}
|
|
4820
|
+
function validateDraftObjectFieldCompatibility(fields, objectType) {
|
|
4821
|
+
for (const field of fields) {
|
|
4822
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
4823
|
+
if (!spec) {
|
|
4824
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4825
|
+
}
|
|
4826
|
+
if (spec.legacySchema) {
|
|
4827
|
+
ensureAtlasFieldSupported(field.key, objectType, field.location);
|
|
4828
|
+
continue;
|
|
4829
|
+
}
|
|
4830
|
+
if ((field.key === "renderLabel" || field.key === "renderOrbit" || field.key === "tidalLock") && field.values.length !== 1) {
|
|
4831
|
+
throw WorldOrbitError.fromLocation(`Field "${field.key}" expects exactly one value`, field.location);
|
|
4832
|
+
}
|
|
4833
|
+
}
|
|
4834
|
+
}
|
|
4835
|
+
function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
4836
|
+
if (sourceSchemaVersion === "2.1") {
|
|
4837
|
+
return;
|
|
4838
|
+
}
|
|
4839
|
+
diagnostics.push({
|
|
4840
|
+
code: "parse.schema21.featureCompatibility",
|
|
4841
|
+
severity: "warning",
|
|
4842
|
+
source: "parse",
|
|
4843
|
+
message: `Feature "${featureName}" requires schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
4844
|
+
line: location.line,
|
|
4845
|
+
column: location.column
|
|
4846
|
+
});
|
|
4847
|
+
}
|
|
4848
|
+
function preprocessAtlasSource(source) {
|
|
4849
|
+
const chars = [...source];
|
|
4850
|
+
const comments = [];
|
|
4851
|
+
let inString = false;
|
|
4852
|
+
let inBlockComment = false;
|
|
4853
|
+
let blockCommentStart = null;
|
|
4854
|
+
let line = 1;
|
|
4855
|
+
let column = 1;
|
|
4856
|
+
for (let index = 0; index < chars.length; index++) {
|
|
4857
|
+
const ch = chars[index];
|
|
4858
|
+
const next = chars[index + 1];
|
|
4859
|
+
if (inBlockComment) {
|
|
4860
|
+
if (ch === "*" && next === "/") {
|
|
4861
|
+
chars[index] = " ";
|
|
4862
|
+
chars[index + 1] = " ";
|
|
4863
|
+
inBlockComment = false;
|
|
4864
|
+
blockCommentStart = null;
|
|
4865
|
+
index++;
|
|
4866
|
+
column += 2;
|
|
4867
|
+
continue;
|
|
4868
|
+
}
|
|
4869
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
4870
|
+
chars[index] = " ";
|
|
4871
|
+
}
|
|
4872
|
+
if (ch === "\n") {
|
|
4873
|
+
line++;
|
|
4874
|
+
column = 1;
|
|
4875
|
+
} else {
|
|
4876
|
+
column++;
|
|
4877
|
+
}
|
|
4878
|
+
continue;
|
|
4879
|
+
}
|
|
4880
|
+
if (!inString && ch === "/" && next === "*") {
|
|
4881
|
+
comments.push({ kind: "block", line, column });
|
|
4882
|
+
chars[index] = " ";
|
|
4883
|
+
chars[index + 1] = " ";
|
|
4884
|
+
inBlockComment = true;
|
|
4885
|
+
blockCommentStart = { line, column };
|
|
4886
|
+
index++;
|
|
4887
|
+
column += 2;
|
|
4888
|
+
continue;
|
|
4889
|
+
}
|
|
4890
|
+
if (!inString && ch === "#" && !isHexColorLiteral(chars, index)) {
|
|
4891
|
+
comments.push({ kind: "line", line, column });
|
|
4892
|
+
chars[index] = " ";
|
|
4893
|
+
let inner = index + 1;
|
|
4894
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
4895
|
+
chars[inner] = " ";
|
|
4896
|
+
inner++;
|
|
4897
|
+
}
|
|
4898
|
+
column += inner - index;
|
|
4899
|
+
index = inner - 1;
|
|
4900
|
+
continue;
|
|
4901
|
+
}
|
|
4902
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
4903
|
+
inString = !inString;
|
|
4904
|
+
}
|
|
4905
|
+
if (ch === "\n") {
|
|
4906
|
+
line++;
|
|
4907
|
+
column = 1;
|
|
4908
|
+
} else {
|
|
4909
|
+
column++;
|
|
4910
|
+
}
|
|
4911
|
+
}
|
|
4912
|
+
if (inBlockComment) {
|
|
4913
|
+
throw WorldOrbitError.fromLocation("Unclosed block comment", blockCommentStart ?? void 0);
|
|
4914
|
+
}
|
|
4915
|
+
return {
|
|
4916
|
+
source: chars.join(""),
|
|
4917
|
+
comments
|
|
4918
|
+
};
|
|
4919
|
+
}
|
|
4920
|
+
function isHexColorLiteral(chars, start) {
|
|
4921
|
+
let index = start + 1;
|
|
4922
|
+
let length = 0;
|
|
4923
|
+
while (index < chars.length && /[0-9a-f]/i.test(chars[index] ?? "")) {
|
|
4924
|
+
index++;
|
|
4925
|
+
length++;
|
|
4926
|
+
}
|
|
4927
|
+
if (![3, 4, 6, 8].includes(length)) {
|
|
4928
|
+
return false;
|
|
4929
|
+
}
|
|
4930
|
+
const next = chars[index];
|
|
4931
|
+
return next === void 0 || next === " " || next === " " || next === "\r" || next === "\n";
|
|
3617
4932
|
}
|
|
3618
4933
|
|
|
3619
4934
|
// packages/core/dist/atlas-edit.js
|
|
3620
|
-
function createEmptyAtlasDocument(systemId = "WorldOrbit") {
|
|
4935
|
+
function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "2.0") {
|
|
3621
4936
|
return {
|
|
3622
4937
|
format: "worldorbit",
|
|
3623
|
-
version
|
|
4938
|
+
version,
|
|
4939
|
+
schemaVersion: version,
|
|
3624
4940
|
sourceVersion: "1.0",
|
|
3625
4941
|
system: {
|
|
3626
4942
|
type: "system",
|
|
3627
4943
|
id: systemId,
|
|
3628
4944
|
title: systemId,
|
|
4945
|
+
description: null,
|
|
4946
|
+
epoch: null,
|
|
4947
|
+
referencePlane: null,
|
|
3629
4948
|
defaults: {
|
|
3630
4949
|
view: "topdown",
|
|
3631
4950
|
scale: null,
|
|
@@ -3637,6 +4956,8 @@
|
|
|
3637
4956
|
viewpoints: [],
|
|
3638
4957
|
annotations: []
|
|
3639
4958
|
},
|
|
4959
|
+
groups: [],
|
|
4960
|
+
relations: [],
|
|
3640
4961
|
objects: [],
|
|
3641
4962
|
diagnostics: []
|
|
3642
4963
|
};
|
|
@@ -3650,14 +4971,20 @@
|
|
|
3650
4971
|
for (const key of Object.keys(document.system.atlasMetadata).sort()) {
|
|
3651
4972
|
paths.push({ kind: "metadata", key });
|
|
3652
4973
|
}
|
|
3653
|
-
for (const viewpoint of [...document.system.viewpoints].sort(
|
|
4974
|
+
for (const viewpoint of [...document.system.viewpoints].sort(compareIdLike2)) {
|
|
3654
4975
|
paths.push({ kind: "viewpoint", id: viewpoint.id });
|
|
3655
4976
|
}
|
|
3656
|
-
for (const annotation of [...document.system.annotations].sort(
|
|
4977
|
+
for (const annotation of [...document.system.annotations].sort(compareIdLike2)) {
|
|
3657
4978
|
paths.push({ kind: "annotation", id: annotation.id });
|
|
3658
4979
|
}
|
|
3659
4980
|
}
|
|
3660
|
-
for (const
|
|
4981
|
+
for (const group of [...document.groups].sort(compareIdLike2)) {
|
|
4982
|
+
paths.push({ kind: "group", id: group.id });
|
|
4983
|
+
}
|
|
4984
|
+
for (const relation of [...document.relations].sort(compareIdLike2)) {
|
|
4985
|
+
paths.push({ kind: "relation", id: relation.id });
|
|
4986
|
+
}
|
|
4987
|
+
for (const object of [...document.objects].sort(compareIdLike2)) {
|
|
3661
4988
|
paths.push({ kind: "object", id: object.id });
|
|
3662
4989
|
}
|
|
3663
4990
|
return paths;
|
|
@@ -3670,12 +4997,16 @@
|
|
|
3670
4997
|
return document.system?.defaults ?? null;
|
|
3671
4998
|
case "metadata":
|
|
3672
4999
|
return path.key ? document.system?.atlasMetadata[path.key] ?? null : null;
|
|
5000
|
+
case "group":
|
|
5001
|
+
return path.id ? findGroup(document, path.id) : null;
|
|
3673
5002
|
case "object":
|
|
3674
5003
|
return path.id ? findObject(document, path.id) : null;
|
|
3675
5004
|
case "viewpoint":
|
|
3676
5005
|
return path.id ? findViewpoint(document.system, path.id) : null;
|
|
3677
5006
|
case "annotation":
|
|
3678
5007
|
return path.id ? findAnnotation(document.system, path.id) : null;
|
|
5008
|
+
case "relation":
|
|
5009
|
+
return path.id ? findRelation(document, path.id) : null;
|
|
3679
5010
|
}
|
|
3680
5011
|
}
|
|
3681
5012
|
function upsertAtlasDocumentNode(document, path, value) {
|
|
@@ -3701,6 +5032,12 @@
|
|
|
3701
5032
|
system.atlasMetadata[path.key] = String(value);
|
|
3702
5033
|
}
|
|
3703
5034
|
return next;
|
|
5035
|
+
case "group":
|
|
5036
|
+
if (!path.id) {
|
|
5037
|
+
throw new Error('Group updates require an "id" value.');
|
|
5038
|
+
}
|
|
5039
|
+
upsertById(next.groups, value);
|
|
5040
|
+
return next;
|
|
3704
5041
|
case "object":
|
|
3705
5042
|
if (!path.id) {
|
|
3706
5043
|
throw new Error('Object updates require an "id" value.');
|
|
@@ -3719,6 +5056,12 @@
|
|
|
3719
5056
|
}
|
|
3720
5057
|
upsertById(system.annotations, value);
|
|
3721
5058
|
return next;
|
|
5059
|
+
case "relation":
|
|
5060
|
+
if (!path.id) {
|
|
5061
|
+
throw new Error('Relation updates require an "id" value.');
|
|
5062
|
+
}
|
|
5063
|
+
upsertById(next.relations, value);
|
|
5064
|
+
return next;
|
|
3722
5065
|
}
|
|
3723
5066
|
}
|
|
3724
5067
|
function updateAtlasDocumentNode(document, path, updater) {
|
|
@@ -3738,6 +5081,11 @@
|
|
|
3738
5081
|
next.objects = next.objects.filter((object) => object.id !== path.id);
|
|
3739
5082
|
}
|
|
3740
5083
|
return next;
|
|
5084
|
+
case "group":
|
|
5085
|
+
if (path.id) {
|
|
5086
|
+
next.groups = next.groups.filter((group) => group.id !== path.id);
|
|
5087
|
+
}
|
|
5088
|
+
return next;
|
|
3741
5089
|
case "viewpoint":
|
|
3742
5090
|
if (path.id) {
|
|
3743
5091
|
system.viewpoints = system.viewpoints.filter((viewpoint) => viewpoint.id !== path.id);
|
|
@@ -3748,6 +5096,11 @@
|
|
|
3748
5096
|
system.annotations = system.annotations.filter((annotation) => annotation.id !== path.id);
|
|
3749
5097
|
}
|
|
3750
5098
|
return next;
|
|
5099
|
+
case "relation":
|
|
5100
|
+
if (path.id) {
|
|
5101
|
+
next.relations = next.relations.filter((relation) => relation.id !== path.id);
|
|
5102
|
+
}
|
|
5103
|
+
return next;
|
|
3751
5104
|
default:
|
|
3752
5105
|
return next;
|
|
3753
5106
|
}
|
|
@@ -3765,6 +5118,15 @@
|
|
|
3765
5118
|
id: diagnostic.objectId
|
|
3766
5119
|
};
|
|
3767
5120
|
}
|
|
5121
|
+
if (diagnostic.field?.startsWith("group.")) {
|
|
5122
|
+
const parts = diagnostic.field.split(".");
|
|
5123
|
+
if (parts[1] && findGroup(document, parts[1])) {
|
|
5124
|
+
return {
|
|
5125
|
+
kind: "group",
|
|
5126
|
+
id: parts[1]
|
|
5127
|
+
};
|
|
5128
|
+
}
|
|
5129
|
+
}
|
|
3768
5130
|
if (diagnostic.field?.startsWith("viewpoint.")) {
|
|
3769
5131
|
const parts = diagnostic.field.split(".");
|
|
3770
5132
|
if (parts[1] && findViewpoint(document.system, parts[1])) {
|
|
@@ -3783,6 +5145,15 @@
|
|
|
3783
5145
|
};
|
|
3784
5146
|
}
|
|
3785
5147
|
}
|
|
5148
|
+
if (diagnostic.field?.startsWith("relation.")) {
|
|
5149
|
+
const parts = diagnostic.field.split(".");
|
|
5150
|
+
if (parts[1] && findRelation(document, parts[1])) {
|
|
5151
|
+
return {
|
|
5152
|
+
kind: "relation",
|
|
5153
|
+
id: parts[1]
|
|
5154
|
+
};
|
|
5155
|
+
}
|
|
5156
|
+
}
|
|
3786
5157
|
if (diagnostic.field && diagnostic.field in ensureSystem(document).atlasMetadata) {
|
|
3787
5158
|
return {
|
|
3788
5159
|
kind: "metadata",
|
|
@@ -3792,9 +5163,11 @@
|
|
|
3792
5163
|
return null;
|
|
3793
5164
|
}
|
|
3794
5165
|
function validateAtlasDocumentWithDiagnostics(document) {
|
|
3795
|
-
const
|
|
3796
|
-
|
|
3797
|
-
|
|
5166
|
+
const diagnostics = [
|
|
5167
|
+
...document.diagnostics,
|
|
5168
|
+
...collectAtlasDiagnostics(document, document.version)
|
|
5169
|
+
];
|
|
5170
|
+
return resolveAtlasDiagnostics(document, diagnostics);
|
|
3798
5171
|
}
|
|
3799
5172
|
function ensureSystem(document) {
|
|
3800
5173
|
if (document.system) {
|
|
@@ -3806,6 +5179,12 @@
|
|
|
3806
5179
|
function findObject(document, objectId) {
|
|
3807
5180
|
return document.objects.find((object) => object.id === objectId) ?? null;
|
|
3808
5181
|
}
|
|
5182
|
+
function findGroup(document, groupId) {
|
|
5183
|
+
return document.groups.find((group) => group.id === groupId) ?? null;
|
|
5184
|
+
}
|
|
5185
|
+
function findRelation(document, relationId) {
|
|
5186
|
+
return document.relations.find((relation) => relation.id === relationId) ?? null;
|
|
5187
|
+
}
|
|
3809
5188
|
function findViewpoint(system, viewpointId) {
|
|
3810
5189
|
return system?.viewpoints.find((viewpoint) => viewpoint.id === viewpointId) ?? null;
|
|
3811
5190
|
}
|
|
@@ -3816,20 +5195,21 @@
|
|
|
3816
5195
|
const index = items.findIndex((item) => item.id === value.id);
|
|
3817
5196
|
if (index === -1) {
|
|
3818
5197
|
items.push(value);
|
|
3819
|
-
items.sort(
|
|
5198
|
+
items.sort(compareIdLike2);
|
|
3820
5199
|
return;
|
|
3821
5200
|
}
|
|
3822
5201
|
items[index] = value;
|
|
3823
5202
|
}
|
|
3824
|
-
function
|
|
5203
|
+
function compareIdLike2(left, right) {
|
|
3825
5204
|
return left.id.localeCompare(right.id);
|
|
3826
5205
|
}
|
|
3827
5206
|
|
|
3828
5207
|
// packages/core/dist/load.js
|
|
3829
|
-
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0)?$/i;
|
|
5208
|
+
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;
|
|
5209
|
+
var ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
|
|
3830
5210
|
var LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
|
|
3831
5211
|
function detectWorldOrbitSchemaVersion(source) {
|
|
3832
|
-
for (const line of source.split(/\r?\n/)) {
|
|
5212
|
+
for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
|
|
3833
5213
|
const trimmed = line.trim();
|
|
3834
5214
|
if (!trimmed) {
|
|
3835
5215
|
continue;
|
|
@@ -3837,6 +5217,9 @@
|
|
|
3837
5217
|
if (LEGACY_DRAFT_SCHEMA_PATTERN.test(trimmed)) {
|
|
3838
5218
|
return "2.0-draft";
|
|
3839
5219
|
}
|
|
5220
|
+
if (ATLAS_SCHEMA_21_PATTERN.test(trimmed)) {
|
|
5221
|
+
return "2.1";
|
|
5222
|
+
}
|
|
3840
5223
|
if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
|
|
3841
5224
|
return "2.0";
|
|
3842
5225
|
}
|
|
@@ -3844,6 +5227,49 @@
|
|
|
3844
5227
|
}
|
|
3845
5228
|
return "1.0";
|
|
3846
5229
|
}
|
|
5230
|
+
function stripCommentsForSchemaDetection(source) {
|
|
5231
|
+
const chars = [...source];
|
|
5232
|
+
let inString = false;
|
|
5233
|
+
let inBlockComment = false;
|
|
5234
|
+
for (let index = 0; index < chars.length; index++) {
|
|
5235
|
+
const ch = chars[index];
|
|
5236
|
+
const next = chars[index + 1];
|
|
5237
|
+
if (inBlockComment) {
|
|
5238
|
+
if (ch === "*" && next === "/") {
|
|
5239
|
+
chars[index] = " ";
|
|
5240
|
+
chars[index + 1] = " ";
|
|
5241
|
+
inBlockComment = false;
|
|
5242
|
+
index++;
|
|
5243
|
+
continue;
|
|
5244
|
+
}
|
|
5245
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
5246
|
+
chars[index] = " ";
|
|
5247
|
+
}
|
|
5248
|
+
continue;
|
|
5249
|
+
}
|
|
5250
|
+
if (!inString && ch === "/" && next === "*") {
|
|
5251
|
+
chars[index] = " ";
|
|
5252
|
+
chars[index + 1] = " ";
|
|
5253
|
+
inBlockComment = true;
|
|
5254
|
+
index++;
|
|
5255
|
+
continue;
|
|
5256
|
+
}
|
|
5257
|
+
if (!inString && ch === "#") {
|
|
5258
|
+
chars[index] = " ";
|
|
5259
|
+
let inner = index + 1;
|
|
5260
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
5261
|
+
chars[inner] = " ";
|
|
5262
|
+
inner++;
|
|
5263
|
+
}
|
|
5264
|
+
index = inner - 1;
|
|
5265
|
+
continue;
|
|
5266
|
+
}
|
|
5267
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
5268
|
+
inString = !inString;
|
|
5269
|
+
}
|
|
5270
|
+
}
|
|
5271
|
+
return chars.join("");
|
|
5272
|
+
}
|
|
3847
5273
|
function loadWorldOrbitSource(source) {
|
|
3848
5274
|
const result = loadWorldOrbitSourceWithDiagnostics(source);
|
|
3849
5275
|
if (!result.ok || !result.value) {
|
|
@@ -3854,36 +5280,36 @@
|
|
|
3854
5280
|
}
|
|
3855
5281
|
function loadWorldOrbitSourceWithDiagnostics(source) {
|
|
3856
5282
|
const schemaVersion = detectWorldOrbitSchemaVersion(source);
|
|
3857
|
-
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft") {
|
|
5283
|
+
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1") {
|
|
3858
5284
|
return loadAtlasSourceWithDiagnostics(source, schemaVersion);
|
|
3859
5285
|
}
|
|
3860
5286
|
let ast;
|
|
3861
5287
|
try {
|
|
3862
5288
|
ast = parseWorldOrbit(source);
|
|
3863
|
-
} catch (
|
|
5289
|
+
} catch (error2) {
|
|
3864
5290
|
return {
|
|
3865
5291
|
ok: false,
|
|
3866
5292
|
value: null,
|
|
3867
|
-
diagnostics: [diagnosticFromError(
|
|
5293
|
+
diagnostics: [diagnosticFromError(error2, "parse")]
|
|
3868
5294
|
};
|
|
3869
5295
|
}
|
|
3870
5296
|
let document;
|
|
3871
5297
|
try {
|
|
3872
5298
|
document = normalizeDocument(ast);
|
|
3873
|
-
} catch (
|
|
5299
|
+
} catch (error2) {
|
|
3874
5300
|
return {
|
|
3875
5301
|
ok: false,
|
|
3876
5302
|
value: null,
|
|
3877
|
-
diagnostics: [diagnosticFromError(
|
|
5303
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
3878
5304
|
};
|
|
3879
5305
|
}
|
|
3880
5306
|
try {
|
|
3881
5307
|
validateDocument(document);
|
|
3882
|
-
} catch (
|
|
5308
|
+
} catch (error2) {
|
|
3883
5309
|
return {
|
|
3884
5310
|
ok: false,
|
|
3885
5311
|
value: null,
|
|
3886
|
-
diagnostics: [diagnosticFromError(
|
|
5312
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
3887
5313
|
};
|
|
3888
5314
|
}
|
|
3889
5315
|
return {
|
|
@@ -3903,30 +5329,29 @@
|
|
|
3903
5329
|
let atlasDocument;
|
|
3904
5330
|
try {
|
|
3905
5331
|
atlasDocument = parseWorldOrbitAtlas(source);
|
|
3906
|
-
} catch (
|
|
5332
|
+
} catch (error2) {
|
|
3907
5333
|
return {
|
|
3908
5334
|
ok: false,
|
|
3909
5335
|
value: null,
|
|
3910
|
-
diagnostics: [diagnosticFromError(
|
|
5336
|
+
diagnostics: [diagnosticFromError(error2, "parse", "load.atlas.failed")]
|
|
3911
5337
|
};
|
|
3912
5338
|
}
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
document = materializeAtlasDocument(atlasDocument);
|
|
3916
|
-
} catch (error) {
|
|
5339
|
+
const atlasDiagnostics = [...atlasDocument.diagnostics];
|
|
5340
|
+
if (atlasDiagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
3917
5341
|
return {
|
|
3918
5342
|
ok: false,
|
|
3919
5343
|
value: null,
|
|
3920
|
-
diagnostics:
|
|
5344
|
+
diagnostics: atlasDiagnostics
|
|
3921
5345
|
};
|
|
3922
5346
|
}
|
|
5347
|
+
let document;
|
|
3923
5348
|
try {
|
|
3924
|
-
|
|
3925
|
-
} catch (
|
|
5349
|
+
document = materializeAtlasDocument(atlasDocument);
|
|
5350
|
+
} catch (error2) {
|
|
3926
5351
|
return {
|
|
3927
5352
|
ok: false,
|
|
3928
5353
|
value: null,
|
|
3929
|
-
diagnostics: [diagnosticFromError(
|
|
5354
|
+
diagnostics: [diagnosticFromError(error2, "normalize", "load.atlas.materialize.failed")]
|
|
3930
5355
|
};
|
|
3931
5356
|
}
|
|
3932
5357
|
const loaded = {
|
|
@@ -3935,12 +5360,12 @@
|
|
|
3935
5360
|
document,
|
|
3936
5361
|
atlasDocument,
|
|
3937
5362
|
draftDocument: atlasDocument,
|
|
3938
|
-
diagnostics:
|
|
5363
|
+
diagnostics: atlasDiagnostics
|
|
3939
5364
|
};
|
|
3940
5365
|
return {
|
|
3941
5366
|
ok: true,
|
|
3942
5367
|
value: loaded,
|
|
3943
|
-
diagnostics:
|
|
5368
|
+
diagnostics: atlasDiagnostics
|
|
3944
5369
|
};
|
|
3945
5370
|
}
|
|
3946
5371
|
|