worldorbit 2.5.12 → 2.5.15
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 +37 -11
- package/dist/unpkg/worldorbit-core.min.js +12 -5
- package/dist/unpkg/worldorbit-markdown.min.js +29 -20
- package/dist/unpkg/worldorbit-viewer.min.js +52 -38
- package/dist/unpkg/worldorbit.js +1737 -245
- package/dist/unpkg/worldorbit.min.js +56 -42
- package/package.json +2 -2
- package/packages/core/README.md +5 -1
- package/packages/core/dist/atlas-edit.d.ts +2 -2
- package/packages/core/dist/atlas-edit.js +70 -7
- package/packages/core/dist/atlas-utils.d.ts +22 -0
- package/packages/core/dist/atlas-utils.js +189 -0
- package/packages/core/dist/atlas-validate.d.ts +2 -0
- package/packages/core/dist/atlas-validate.js +285 -0
- package/packages/core/dist/draft-parse.js +786 -153
- package/packages/core/dist/draft.d.ts +3 -0
- package/packages/core/dist/draft.js +47 -3
- package/packages/core/dist/format.js +165 -9
- package/packages/core/dist/load.js +58 -13
- package/packages/core/dist/normalize.js +7 -0
- package/packages/core/dist/scene.js +92 -27
- package/packages/core/dist/types.d.ts +97 -3
- package/packages/markdown/README.md +1 -1
- package/packages/viewer/README.md +2 -1
- package/packages/viewer/dist/atlas-state.js +7 -1
- package/packages/viewer/dist/atlas-viewer.js +35 -1
- package/packages/viewer/dist/render.js +16 -7
- package/packages/viewer/dist/theme.js +4 -0
- package/packages/viewer/dist/tooltip.js +35 -0
- package/packages/viewer/dist/types.d.ts +7 -0
- package/packages/viewer/dist/viewer.js +4 -0
- package/packages/editor/dist/editor.d.ts +0 -2
- package/packages/editor/dist/editor.js +0 -2626
- package/packages/editor/dist/index.d.ts +0 -2
- package/packages/editor/dist/index.js +0 -1
- package/packages/editor/dist/types.d.ts +0 -53
- package/packages/editor/dist/types.js +0 -1
- package/packages/markdown/dist/html.d.ts +0 -3
- package/packages/markdown/dist/html.js +0 -57
- package/packages/markdown/dist/index.d.ts +0 -4
- package/packages/markdown/dist/index.js +0 -3
- package/packages/markdown/dist/rehype.d.ts +0 -10
- package/packages/markdown/dist/rehype.js +0 -49
- package/packages/markdown/dist/remark.d.ts +0 -9
- package/packages/markdown/dist/remark.js +0 -28
- package/packages/markdown/dist/types.d.ts +0 -11
- package/packages/markdown/dist/types.js +0 -1
package/dist/unpkg/worldorbit.js
CHANGED
|
@@ -643,7 +643,10 @@ var WorldOrbit = (() => {
|
|
|
643
643
|
return {
|
|
644
644
|
format: "worldorbit",
|
|
645
645
|
version: "1.0",
|
|
646
|
+
schemaVersion: "1.0",
|
|
646
647
|
system,
|
|
648
|
+
groups: [],
|
|
649
|
+
relations: [],
|
|
647
650
|
objects
|
|
648
651
|
};
|
|
649
652
|
}
|
|
@@ -653,13 +656,17 @@ var WorldOrbit = (() => {
|
|
|
653
656
|
const fieldMap = collectFields(mergedFields);
|
|
654
657
|
const placement = extractPlacement(node.objectType, fieldMap);
|
|
655
658
|
const properties = normalizeProperties(fieldMap);
|
|
656
|
-
const
|
|
659
|
+
const info2 = normalizeInfo(node.infoEntries);
|
|
657
660
|
if (node.objectType === "system") {
|
|
658
661
|
return {
|
|
659
662
|
type: "system",
|
|
660
663
|
id: node.name,
|
|
664
|
+
title: typeof properties.title === "string" ? properties.title : null,
|
|
665
|
+
description: null,
|
|
666
|
+
epoch: null,
|
|
667
|
+
referencePlane: null,
|
|
661
668
|
properties,
|
|
662
|
-
info
|
|
669
|
+
info: info2
|
|
663
670
|
};
|
|
664
671
|
}
|
|
665
672
|
return {
|
|
@@ -667,7 +674,7 @@ var WorldOrbit = (() => {
|
|
|
667
674
|
id: node.name,
|
|
668
675
|
properties,
|
|
669
676
|
placement,
|
|
670
|
-
info
|
|
677
|
+
info: info2
|
|
671
678
|
};
|
|
672
679
|
}
|
|
673
680
|
function validateFieldCompatibility(objectType, fields) {
|
|
@@ -797,14 +804,14 @@ var WorldOrbit = (() => {
|
|
|
797
804
|
}
|
|
798
805
|
}
|
|
799
806
|
function normalizeInfo(entries) {
|
|
800
|
-
const
|
|
807
|
+
const info2 = {};
|
|
801
808
|
for (const entry of entries) {
|
|
802
|
-
if (entry.key in
|
|
809
|
+
if (entry.key in info2) {
|
|
803
810
|
throw WorldOrbitError.fromLocation(`Duplicate info key "${entry.key}"`, entry.location);
|
|
804
811
|
}
|
|
805
|
-
|
|
812
|
+
info2[entry.key] = entry.value;
|
|
806
813
|
}
|
|
807
|
-
return
|
|
814
|
+
return info2;
|
|
808
815
|
}
|
|
809
816
|
function parseAtReference(target, location) {
|
|
810
817
|
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
@@ -978,38 +985,38 @@ var WorldOrbit = (() => {
|
|
|
978
985
|
function createDiagnostic(diagnostic) {
|
|
979
986
|
return { ...diagnostic };
|
|
980
987
|
}
|
|
981
|
-
function diagnosticFromError(
|
|
982
|
-
if (
|
|
988
|
+
function diagnosticFromError(error2, source, code = `${source}.failed`) {
|
|
989
|
+
if (error2 instanceof WorldOrbitError) {
|
|
983
990
|
return {
|
|
984
991
|
code,
|
|
985
992
|
severity: "error",
|
|
986
993
|
source,
|
|
987
|
-
message:
|
|
988
|
-
line:
|
|
989
|
-
column:
|
|
994
|
+
message: error2.message,
|
|
995
|
+
line: error2.line,
|
|
996
|
+
column: error2.column
|
|
990
997
|
};
|
|
991
998
|
}
|
|
992
|
-
if (
|
|
999
|
+
if (error2 instanceof Error) {
|
|
993
1000
|
return {
|
|
994
1001
|
code,
|
|
995
1002
|
severity: "error",
|
|
996
1003
|
source,
|
|
997
|
-
message:
|
|
1004
|
+
message: error2.message
|
|
998
1005
|
};
|
|
999
1006
|
}
|
|
1000
1007
|
return {
|
|
1001
1008
|
code,
|
|
1002
1009
|
severity: "error",
|
|
1003
1010
|
source,
|
|
1004
|
-
message: String(
|
|
1011
|
+
message: String(error2)
|
|
1005
1012
|
};
|
|
1006
1013
|
}
|
|
1007
1014
|
function parseWithDiagnostics(source) {
|
|
1008
1015
|
let ast;
|
|
1009
1016
|
try {
|
|
1010
1017
|
ast = parseWorldOrbit(source);
|
|
1011
|
-
} catch (
|
|
1012
|
-
const diagnostic = diagnosticFromError(
|
|
1018
|
+
} catch (error2) {
|
|
1019
|
+
const diagnostic = diagnosticFromError(error2, "parse");
|
|
1013
1020
|
return {
|
|
1014
1021
|
ok: false,
|
|
1015
1022
|
value: null,
|
|
@@ -1019,20 +1026,20 @@ var WorldOrbit = (() => {
|
|
|
1019
1026
|
let document2;
|
|
1020
1027
|
try {
|
|
1021
1028
|
document2 = normalizeDocument(ast);
|
|
1022
|
-
} catch (
|
|
1029
|
+
} catch (error2) {
|
|
1023
1030
|
return {
|
|
1024
1031
|
ok: false,
|
|
1025
1032
|
value: null,
|
|
1026
|
-
diagnostics: [diagnosticFromError(
|
|
1033
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
1027
1034
|
};
|
|
1028
1035
|
}
|
|
1029
1036
|
try {
|
|
1030
1037
|
validateDocument(document2);
|
|
1031
|
-
} catch (
|
|
1038
|
+
} catch (error2) {
|
|
1032
1039
|
return {
|
|
1033
1040
|
ok: false,
|
|
1034
1041
|
value: null,
|
|
1035
|
-
diagnostics: [diagnosticFromError(
|
|
1042
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
1036
1043
|
};
|
|
1037
1044
|
}
|
|
1038
1045
|
return {
|
|
@@ -1051,11 +1058,11 @@ var WorldOrbit = (() => {
|
|
|
1051
1058
|
value: normalizeDocument(ast),
|
|
1052
1059
|
diagnostics: []
|
|
1053
1060
|
};
|
|
1054
|
-
} catch (
|
|
1061
|
+
} catch (error2) {
|
|
1055
1062
|
return {
|
|
1056
1063
|
ok: false,
|
|
1057
1064
|
value: null,
|
|
1058
|
-
diagnostics: [diagnosticFromError(
|
|
1065
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
1059
1066
|
};
|
|
1060
1067
|
}
|
|
1061
1068
|
}
|
|
@@ -1067,11 +1074,11 @@ var WorldOrbit = (() => {
|
|
|
1067
1074
|
value: document2,
|
|
1068
1075
|
diagnostics: []
|
|
1069
1076
|
};
|
|
1070
|
-
} catch (
|
|
1077
|
+
} catch (error2) {
|
|
1071
1078
|
return {
|
|
1072
1079
|
ok: false,
|
|
1073
1080
|
value: null,
|
|
1074
|
-
diagnostics: [diagnosticFromError(
|
|
1081
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
1075
1082
|
};
|
|
1076
1083
|
}
|
|
1077
1084
|
}
|
|
@@ -1203,8 +1210,10 @@ var WorldOrbit = (() => {
|
|
|
1203
1210
|
const orbitVisuals = orbitDrafts.map((draft) => createOrbitVisual(draft, relationships.groupIds.get(draft.object.id) ?? null));
|
|
1204
1211
|
const leaders = leaderDrafts.map((draft) => createLeaderLine(draft));
|
|
1205
1212
|
const labels = createSceneLabels(objects, height, scaleModel.labelMultiplier);
|
|
1206
|
-
const
|
|
1213
|
+
const relations = createSceneRelations(document2, objects);
|
|
1214
|
+
const layers = createSceneLayers(orbitVisuals, relations, leaders, objects, labels);
|
|
1207
1215
|
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships);
|
|
1216
|
+
const semanticGroups = createSceneSemanticGroups(document2, objects);
|
|
1208
1217
|
const viewpoints = createSceneViewpoints(document2, projection, frame.preset, relationships, objectMap);
|
|
1209
1218
|
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels);
|
|
1210
1219
|
return {
|
|
@@ -1214,7 +1223,7 @@ var WorldOrbit = (() => {
|
|
|
1214
1223
|
renderPreset: frame.preset,
|
|
1215
1224
|
projection,
|
|
1216
1225
|
scaleModel,
|
|
1217
|
-
title: String(document2.system?.properties.title ?? document2.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1226
|
+
title: String(document2.system?.title ?? document2.system?.properties.title ?? document2.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1218
1227
|
subtitle: `${capitalizeLabel(projection)} view - ${capitalizeLabel(layoutPreset)} layout`,
|
|
1219
1228
|
systemId,
|
|
1220
1229
|
viewMode: projection,
|
|
@@ -1230,9 +1239,11 @@ var WorldOrbit = (() => {
|
|
|
1230
1239
|
contentBounds,
|
|
1231
1240
|
layers,
|
|
1232
1241
|
groups,
|
|
1242
|
+
semanticGroups,
|
|
1233
1243
|
viewpoints,
|
|
1234
1244
|
objects,
|
|
1235
1245
|
orbitVisuals,
|
|
1246
|
+
relations,
|
|
1236
1247
|
leaders,
|
|
1237
1248
|
labels
|
|
1238
1249
|
};
|
|
@@ -1342,6 +1353,7 @@ var WorldOrbit = (() => {
|
|
|
1342
1353
|
}
|
|
1343
1354
|
function createSceneObject(position, scaleModel, relationships) {
|
|
1344
1355
|
const { object, x, y, radius, sortKey, anchorX, anchorY } = position;
|
|
1356
|
+
const renderPriority = object.renderHints?.renderPriority ?? 0;
|
|
1345
1357
|
return {
|
|
1346
1358
|
renderId: createRenderId(object.id),
|
|
1347
1359
|
objectId: object.id,
|
|
@@ -1350,11 +1362,12 @@ var WorldOrbit = (() => {
|
|
|
1350
1362
|
ancestorIds: relationships.ancestorIds.get(object.id) ?? [],
|
|
1351
1363
|
childIds: relationships.childIds.get(object.id) ?? [],
|
|
1352
1364
|
groupId: relationships.groupIds.get(object.id) ?? null,
|
|
1365
|
+
semanticGroupIds: [...object.groups ?? []],
|
|
1353
1366
|
x,
|
|
1354
1367
|
y,
|
|
1355
1368
|
radius,
|
|
1356
1369
|
visualRadius: visualExtentForObject(object, radius, scaleModel),
|
|
1357
|
-
sortKey,
|
|
1370
|
+
sortKey: sortKey + renderPriority * 1e-3,
|
|
1358
1371
|
anchorX,
|
|
1359
1372
|
anchorY,
|
|
1360
1373
|
label: object.id,
|
|
@@ -1371,6 +1384,7 @@ var WorldOrbit = (() => {
|
|
|
1371
1384
|
object: draft.object,
|
|
1372
1385
|
parentId: draft.parentId,
|
|
1373
1386
|
groupId,
|
|
1387
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1374
1388
|
kind: draft.kind,
|
|
1375
1389
|
cx: draft.cx,
|
|
1376
1390
|
cy: draft.cy,
|
|
@@ -1382,7 +1396,7 @@ var WorldOrbit = (() => {
|
|
|
1382
1396
|
bandThickness: draft.bandThickness,
|
|
1383
1397
|
frontArcPath: draft.frontArcPath,
|
|
1384
1398
|
backArcPath: draft.backArcPath,
|
|
1385
|
-
hidden: draft.object.properties.hidden === true
|
|
1399
|
+
hidden: draft.object.properties.hidden === true || draft.object.renderHints?.renderOrbit === false
|
|
1386
1400
|
};
|
|
1387
1401
|
}
|
|
1388
1402
|
function createLeaderLine(draft) {
|
|
@@ -1391,6 +1405,7 @@ var WorldOrbit = (() => {
|
|
|
1391
1405
|
objectId: draft.object.id,
|
|
1392
1406
|
object: draft.object,
|
|
1393
1407
|
groupId: draft.groupId,
|
|
1408
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1394
1409
|
x1: draft.x1,
|
|
1395
1410
|
y1: draft.y1,
|
|
1396
1411
|
x2: draft.x2,
|
|
@@ -1402,7 +1417,7 @@ var WorldOrbit = (() => {
|
|
|
1402
1417
|
function createSceneLabels(objects, sceneHeight, labelMultiplier) {
|
|
1403
1418
|
const labels = [];
|
|
1404
1419
|
const occupied = [];
|
|
1405
|
-
const visibleObjects = [...objects].filter((object) => !object.hidden).sort((left, right) => left.sortKey - right.sortKey);
|
|
1420
|
+
const visibleObjects = [...objects].filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false).sort((left, right) => left.sortKey - right.sortKey);
|
|
1406
1421
|
for (const object of visibleObjects) {
|
|
1407
1422
|
const direction = object.y > sceneHeight * 0.62 ? -1 : 1;
|
|
1408
1423
|
const labelHalfWidth = estimateLabelHalfWidth(object, labelMultiplier);
|
|
@@ -1422,6 +1437,7 @@ var WorldOrbit = (() => {
|
|
|
1422
1437
|
objectId: object.objectId,
|
|
1423
1438
|
object: object.object,
|
|
1424
1439
|
groupId: object.groupId,
|
|
1440
|
+
semanticGroupIds: [...object.semanticGroupIds],
|
|
1425
1441
|
label: object.label,
|
|
1426
1442
|
secondaryLabel: object.secondaryLabel,
|
|
1427
1443
|
x: object.x,
|
|
@@ -1434,7 +1450,7 @@ var WorldOrbit = (() => {
|
|
|
1434
1450
|
}
|
|
1435
1451
|
return labels;
|
|
1436
1452
|
}
|
|
1437
|
-
function createSceneLayers(orbitVisuals, leaders, objects, labels) {
|
|
1453
|
+
function createSceneLayers(orbitVisuals, relations, leaders, objects, labels) {
|
|
1438
1454
|
const backOrbitIds = orbitVisuals.filter((visual) => !visual.hidden && Boolean(visual.backArcPath)).map((visual) => visual.renderId);
|
|
1439
1455
|
const frontOrbitIds = orbitVisuals.filter((visual) => !visual.hidden).map((visual) => visual.renderId);
|
|
1440
1456
|
return [
|
|
@@ -1445,6 +1461,10 @@ var WorldOrbit = (() => {
|
|
|
1445
1461
|
},
|
|
1446
1462
|
{ id: "orbits-back", renderIds: backOrbitIds },
|
|
1447
1463
|
{ id: "orbits-front", renderIds: frontOrbitIds },
|
|
1464
|
+
{
|
|
1465
|
+
id: "relations",
|
|
1466
|
+
renderIds: relations.filter((relation) => !relation.hidden).map((relation) => relation.renderId)
|
|
1467
|
+
},
|
|
1448
1468
|
{
|
|
1449
1469
|
id: "objects",
|
|
1450
1470
|
renderIds: objects.filter((object) => !object.hidden).map((object) => object.renderId)
|
|
@@ -1509,6 +1529,36 @@ var WorldOrbit = (() => {
|
|
|
1509
1529
|
}
|
|
1510
1530
|
return [...groups.values()].sort((left, right) => left.label.localeCompare(right.label));
|
|
1511
1531
|
}
|
|
1532
|
+
function createSceneSemanticGroups(document2, objects) {
|
|
1533
|
+
return [...document2.groups].map((group) => ({
|
|
1534
|
+
id: group.id,
|
|
1535
|
+
label: group.label,
|
|
1536
|
+
summary: group.summary,
|
|
1537
|
+
color: group.color,
|
|
1538
|
+
tags: [...group.tags],
|
|
1539
|
+
hidden: group.hidden,
|
|
1540
|
+
objectIds: objects.filter((object) => !object.hidden && object.semanticGroupIds.includes(group.id)).map((object) => object.objectId)
|
|
1541
|
+
})).sort((left, right) => left.label.localeCompare(right.label));
|
|
1542
|
+
}
|
|
1543
|
+
function createSceneRelations(document2, objects) {
|
|
1544
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1545
|
+
return document2.relations.map((relation) => {
|
|
1546
|
+
const from = objectMap.get(relation.from);
|
|
1547
|
+
const to = objectMap.get(relation.to);
|
|
1548
|
+
return {
|
|
1549
|
+
renderId: `${createRenderId(relation.id)}-relation`,
|
|
1550
|
+
relationId: relation.id,
|
|
1551
|
+
relation,
|
|
1552
|
+
fromObjectId: relation.from,
|
|
1553
|
+
toObjectId: relation.to,
|
|
1554
|
+
x1: from?.x ?? 0,
|
|
1555
|
+
y1: from?.y ?? 0,
|
|
1556
|
+
x2: to?.x ?? 0,
|
|
1557
|
+
y2: to?.y ?? 0,
|
|
1558
|
+
hidden: relation.hidden || !from || !to || from.hidden || to.hidden
|
|
1559
|
+
};
|
|
1560
|
+
}).sort((left, right) => left.relation.id.localeCompare(right.relation.id));
|
|
1561
|
+
}
|
|
1512
1562
|
function createSceneViewpoints(document2, projection, preset, relationships, objectMap) {
|
|
1513
1563
|
const generatedOverview = createGeneratedOverviewViewpoint(document2, projection, preset);
|
|
1514
1564
|
const drafts = /* @__PURE__ */ new Map();
|
|
@@ -1526,7 +1576,7 @@ var WorldOrbit = (() => {
|
|
|
1526
1576
|
}
|
|
1527
1577
|
const field = fieldParts.join(".").toLowerCase();
|
|
1528
1578
|
const draft = drafts.get(id) ?? { id };
|
|
1529
|
-
applyViewpointField(draft, field, value, projection, preset, relationships, objectMap);
|
|
1579
|
+
applyViewpointField(draft, field, value, document2, projection, preset, relationships, objectMap);
|
|
1530
1580
|
drafts.set(id, draft);
|
|
1531
1581
|
}
|
|
1532
1582
|
const viewpoints = [...drafts.values()].map((draft) => finalizeViewpointDraft(draft, projection, preset, objectMap)).filter(Boolean);
|
|
@@ -1554,7 +1604,8 @@ var WorldOrbit = (() => {
|
|
|
1554
1604
|
});
|
|
1555
1605
|
}
|
|
1556
1606
|
function createGeneratedOverviewViewpoint(document2, projection, preset) {
|
|
1557
|
-
const
|
|
1607
|
+
const title = document2.system?.title ?? document2.system?.properties.title;
|
|
1608
|
+
const label = title ? `${String(title)} Overview` : "Overview";
|
|
1558
1609
|
return {
|
|
1559
1610
|
id: "overview",
|
|
1560
1611
|
label,
|
|
@@ -1570,7 +1621,7 @@ var WorldOrbit = (() => {
|
|
|
1570
1621
|
generated: true
|
|
1571
1622
|
};
|
|
1572
1623
|
}
|
|
1573
|
-
function applyViewpointField(draft, field, value, projection, preset, relationships, objectMap) {
|
|
1624
|
+
function applyViewpointField(draft, field, value, document2, projection, preset, relationships, objectMap) {
|
|
1574
1625
|
const normalizedValue = value.trim();
|
|
1575
1626
|
switch (field) {
|
|
1576
1627
|
case "label":
|
|
@@ -1637,7 +1688,7 @@ var WorldOrbit = (() => {
|
|
|
1637
1688
|
case "groups":
|
|
1638
1689
|
draft.filter = {
|
|
1639
1690
|
...draft.filter ?? createEmptyViewpointFilter(),
|
|
1640
|
-
groupIds: parseViewpointGroups(normalizedValue, relationships, objectMap)
|
|
1691
|
+
groupIds: parseViewpointGroups(normalizedValue, document2, relationships, objectMap)
|
|
1641
1692
|
};
|
|
1642
1693
|
return;
|
|
1643
1694
|
}
|
|
@@ -1710,7 +1761,7 @@ var WorldOrbit = (() => {
|
|
|
1710
1761
|
next["orbits-front"] = enabled;
|
|
1711
1762
|
continue;
|
|
1712
1763
|
}
|
|
1713
|
-
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1764
|
+
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "relations" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1714
1765
|
next[rawLayer] = enabled;
|
|
1715
1766
|
}
|
|
1716
1767
|
}
|
|
@@ -1719,8 +1770,11 @@ var WorldOrbit = (() => {
|
|
|
1719
1770
|
function parseViewpointObjectTypes(value) {
|
|
1720
1771
|
return splitListValue(value).filter((entry) => entry === "star" || entry === "planet" || entry === "moon" || entry === "belt" || entry === "asteroid" || entry === "comet" || entry === "ring" || entry === "structure" || entry === "phenomenon");
|
|
1721
1772
|
}
|
|
1722
|
-
function parseViewpointGroups(value, relationships, objectMap) {
|
|
1773
|
+
function parseViewpointGroups(value, document2, relationships, objectMap) {
|
|
1723
1774
|
return splitListValue(value).map((entry) => {
|
|
1775
|
+
if (document2.schemaVersion === "2.1" || document2.groups.some((group) => group.id === entry)) {
|
|
1776
|
+
return entry;
|
|
1777
|
+
}
|
|
1724
1778
|
if (entry.startsWith("wo-") && entry.endsWith("-group")) {
|
|
1725
1779
|
return entry;
|
|
1726
1780
|
}
|
|
@@ -1851,8 +1905,9 @@ var WorldOrbit = (() => {
|
|
|
1851
1905
|
}
|
|
1852
1906
|
const orbiting = [...context.orbitChildren.get(object.id) ?? []].sort(compareOrbiting);
|
|
1853
1907
|
const orbitMetricContext = computeOrbitMetricContext(orbiting, parent.radius, context.spacingFactor, context.scaleModel);
|
|
1908
|
+
const orbitRadiiPx = resolveOrbitRadiiPx(orbiting, orbitMetricContext);
|
|
1854
1909
|
orbiting.forEach((child, index) => {
|
|
1855
|
-
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, context);
|
|
1910
|
+
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, orbitRadiiPx[index] ?? orbitMetricContext.innerPx, context);
|
|
1856
1911
|
orbitDrafts.push({
|
|
1857
1912
|
object: child,
|
|
1858
1913
|
parentId: object.id,
|
|
@@ -1926,7 +1981,8 @@ var WorldOrbit = (() => {
|
|
|
1926
1981
|
metricSpread: 0,
|
|
1927
1982
|
innerPx,
|
|
1928
1983
|
stepPx,
|
|
1929
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
1984
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
1985
|
+
minimumGapPx: stepPx * 0.42
|
|
1930
1986
|
};
|
|
1931
1987
|
}
|
|
1932
1988
|
const minMetric = Math.min(...presentMetrics);
|
|
@@ -1939,10 +1995,11 @@ var WorldOrbit = (() => {
|
|
|
1939
1995
|
metricSpread,
|
|
1940
1996
|
innerPx,
|
|
1941
1997
|
stepPx,
|
|
1942
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
1998
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
1999
|
+
minimumGapPx: stepPx * 0.42
|
|
1943
2000
|
};
|
|
1944
2001
|
}
|
|
1945
|
-
function resolveOrbitGeometry(object, index, count, parent, metricContext, context) {
|
|
2002
|
+
function resolveOrbitGeometry(object, index, count, parent, metricContext, orbitRadiusPx, context) {
|
|
1946
2003
|
const placement = object.placement;
|
|
1947
2004
|
const band = object.type === "belt" || object.type === "ring";
|
|
1948
2005
|
if (!placement || placement.mode !== "orbit") {
|
|
@@ -1960,7 +2017,7 @@ var WorldOrbit = (() => {
|
|
|
1960
2017
|
};
|
|
1961
2018
|
}
|
|
1962
2019
|
const eccentricity = clampNumber(typeof placement.eccentricity === "number" ? placement.eccentricity : 0, 0, 0.92);
|
|
1963
|
-
const semiMajor =
|
|
2020
|
+
const semiMajor = orbitRadiusPx;
|
|
1964
2021
|
const baseMinor = Math.max(semiMajor * Math.sqrt(1 - eccentricity * eccentricity), semiMajor * 0.18);
|
|
1965
2022
|
const inclinationDeg = unitValueToDegrees(placement.inclination) ?? 0;
|
|
1966
2023
|
const inclinationScale = context.projection === "isometric" ? Math.max(MIN_ISO_MINOR_SCALE, Math.cos(degreesToRadians(inclinationDeg))) * ISO_FLATTENING : 1;
|
|
@@ -1990,15 +2047,19 @@ var WorldOrbit = (() => {
|
|
|
1990
2047
|
objectY: objectPoint.y
|
|
1991
2048
|
};
|
|
1992
2049
|
}
|
|
1993
|
-
function resolveOrbitRadiusPx(
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2050
|
+
function resolveOrbitRadiusPx(metric, metricContext) {
|
|
2051
|
+
return metricContext.innerPx + metricContext.stepPx * log2(Math.max(metric, 0) + 1);
|
|
2052
|
+
}
|
|
2053
|
+
function resolveOrbitRadiiPx(objects, metricContext) {
|
|
2054
|
+
const radii = [];
|
|
2055
|
+
objects.forEach((object, index) => {
|
|
2056
|
+
const metric = orbitMetric(object);
|
|
2057
|
+
const fallbackRadius = metricContext.innerPx + index * metricContext.stepPx;
|
|
2058
|
+
const baseRadius = metric === null ? fallbackRadius : resolveOrbitRadiusPx(metric, metricContext);
|
|
2059
|
+
const minimumRadius = index === 0 ? metricContext.innerPx : (radii[index - 1] ?? metricContext.innerPx) + metricContext.minimumGapPx;
|
|
2060
|
+
radii.push(Math.max(baseRadius, minimumRadius));
|
|
2061
|
+
});
|
|
2062
|
+
return radii;
|
|
2002
2063
|
}
|
|
2003
2064
|
function orbitMetric(object) {
|
|
2004
2065
|
if (!object.placement || object.placement.mode !== "orbit") {
|
|
@@ -2006,6 +2067,9 @@ var WorldOrbit = (() => {
|
|
|
2006
2067
|
}
|
|
2007
2068
|
return toDistanceMetric(object.placement.semiMajor ?? object.placement.distance ?? null);
|
|
2008
2069
|
}
|
|
2070
|
+
function log2(value) {
|
|
2071
|
+
return Math.log(value) / Math.log(2);
|
|
2072
|
+
}
|
|
2009
2073
|
function resolveOrbitPhase(phase, index, count) {
|
|
2010
2074
|
const degreeValue = phase ? unitValueToDegrees(phase) : null;
|
|
2011
2075
|
if (degreeValue !== null) {
|
|
@@ -2494,8 +2558,11 @@ var WorldOrbit = (() => {
|
|
|
2494
2558
|
return {
|
|
2495
2559
|
format: "worldorbit",
|
|
2496
2560
|
version: "2.0",
|
|
2561
|
+
schemaVersion: "2.0",
|
|
2497
2562
|
sourceVersion: document2.version,
|
|
2498
2563
|
system,
|
|
2564
|
+
groups: structuredClone(document2.groups ?? []),
|
|
2565
|
+
relations: structuredClone(document2.relations ?? []),
|
|
2499
2566
|
objects: document2.objects.map(cloneWorldOrbitObject),
|
|
2500
2567
|
diagnostics
|
|
2501
2568
|
};
|
|
@@ -2507,13 +2574,20 @@ var WorldOrbit = (() => {
|
|
|
2507
2574
|
const system = document2.system ? {
|
|
2508
2575
|
type: "system",
|
|
2509
2576
|
id: document2.system.id,
|
|
2577
|
+
title: document2.system.title,
|
|
2578
|
+
description: document2.system.description,
|
|
2579
|
+
epoch: document2.system.epoch,
|
|
2580
|
+
referencePlane: document2.system.referencePlane,
|
|
2510
2581
|
properties: materializeDraftSystemProperties(document2.system),
|
|
2511
2582
|
info: materializeDraftSystemInfo(document2.system)
|
|
2512
2583
|
} : null;
|
|
2513
2584
|
return {
|
|
2514
2585
|
format: "worldorbit",
|
|
2515
2586
|
version: "1.0",
|
|
2587
|
+
schemaVersion: document2.version,
|
|
2516
2588
|
system,
|
|
2589
|
+
groups: structuredClone(document2.groups ?? []),
|
|
2590
|
+
relations: structuredClone(document2.relations ?? []),
|
|
2517
2591
|
objects: document2.objects.map(cloneWorldOrbitObject)
|
|
2518
2592
|
};
|
|
2519
2593
|
}
|
|
@@ -2528,7 +2602,10 @@ var WorldOrbit = (() => {
|
|
|
2528
2602
|
return {
|
|
2529
2603
|
type: "system",
|
|
2530
2604
|
id: document2.system?.id ?? "WorldOrbit",
|
|
2531
|
-
title: typeof document2.system?.properties.title === "string" ? document2.system.properties.title : null,
|
|
2605
|
+
title: document2.system?.title ?? (typeof document2.system?.properties.title === "string" ? document2.system.properties.title : null),
|
|
2606
|
+
description: document2.system?.description ?? null,
|
|
2607
|
+
epoch: document2.system?.epoch ?? null,
|
|
2608
|
+
referencePlane: document2.system?.referencePlane ?? null,
|
|
2532
2609
|
defaults,
|
|
2533
2610
|
atlasMetadata,
|
|
2534
2611
|
viewpoints: scene.viewpoints.map(mapSceneViewpointToDraftViewpoint),
|
|
@@ -2655,6 +2732,17 @@ var WorldOrbit = (() => {
|
|
|
2655
2732
|
function cloneWorldOrbitObject(object) {
|
|
2656
2733
|
return {
|
|
2657
2734
|
...object,
|
|
2735
|
+
groups: object.groups ? [...object.groups] : void 0,
|
|
2736
|
+
resonance: object.resonance ? { ...object.resonance } : object.resonance,
|
|
2737
|
+
renderHints: object.renderHints ? { ...object.renderHints } : object.renderHints,
|
|
2738
|
+
deriveRules: object.deriveRules ? object.deriveRules.map((rule) => ({ ...rule })) : void 0,
|
|
2739
|
+
validationRules: object.validationRules ? object.validationRules.map((rule) => ({ ...rule })) : void 0,
|
|
2740
|
+
lockedFields: object.lockedFields ? [...object.lockedFields] : void 0,
|
|
2741
|
+
tolerances: object.tolerances ? object.tolerances.map((entry) => ({
|
|
2742
|
+
field: entry.field,
|
|
2743
|
+
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
|
|
2744
|
+
})) : void 0,
|
|
2745
|
+
typedBlocks: object.typedBlocks ? Object.fromEntries(Object.entries(object.typedBlocks).map(([key, block]) => [key, { ...block ?? {} }])) : void 0,
|
|
2658
2746
|
properties: cloneProperties(object.properties),
|
|
2659
2747
|
placement: object.placement ? structuredClone(object.placement) : null,
|
|
2660
2748
|
info: { ...object.info }
|
|
@@ -2699,71 +2787,80 @@ var WorldOrbit = (() => {
|
|
|
2699
2787
|
if (system.defaults.units) {
|
|
2700
2788
|
properties.units = system.defaults.units;
|
|
2701
2789
|
}
|
|
2790
|
+
if (system.description) {
|
|
2791
|
+
properties.description = system.description;
|
|
2792
|
+
}
|
|
2793
|
+
if (system.epoch) {
|
|
2794
|
+
properties.epoch = system.epoch;
|
|
2795
|
+
}
|
|
2796
|
+
if (system.referencePlane) {
|
|
2797
|
+
properties.referencePlane = system.referencePlane;
|
|
2798
|
+
}
|
|
2702
2799
|
return properties;
|
|
2703
2800
|
}
|
|
2704
2801
|
function materializeDraftSystemInfo(system) {
|
|
2705
|
-
const
|
|
2802
|
+
const info2 = {
|
|
2706
2803
|
...system.atlasMetadata
|
|
2707
2804
|
};
|
|
2708
2805
|
if (system.defaults.theme) {
|
|
2709
|
-
|
|
2806
|
+
info2["atlas.theme"] = system.defaults.theme;
|
|
2710
2807
|
}
|
|
2711
2808
|
for (const viewpoint of system.viewpoints) {
|
|
2712
2809
|
const prefix = `viewpoint.${viewpoint.id}`;
|
|
2713
|
-
|
|
2810
|
+
info2[`${prefix}.label`] = viewpoint.label;
|
|
2714
2811
|
if (viewpoint.summary) {
|
|
2715
|
-
|
|
2812
|
+
info2[`${prefix}.summary`] = viewpoint.summary;
|
|
2716
2813
|
}
|
|
2717
2814
|
if (viewpoint.focusObjectId) {
|
|
2718
|
-
|
|
2815
|
+
info2[`${prefix}.focus`] = viewpoint.focusObjectId;
|
|
2719
2816
|
}
|
|
2720
2817
|
if (viewpoint.selectedObjectId) {
|
|
2721
|
-
|
|
2818
|
+
info2[`${prefix}.select`] = viewpoint.selectedObjectId;
|
|
2722
2819
|
}
|
|
2723
2820
|
if (viewpoint.projection) {
|
|
2724
|
-
|
|
2821
|
+
info2[`${prefix}.projection`] = viewpoint.projection;
|
|
2725
2822
|
}
|
|
2726
2823
|
if (viewpoint.preset) {
|
|
2727
|
-
|
|
2824
|
+
info2[`${prefix}.preset`] = viewpoint.preset;
|
|
2728
2825
|
}
|
|
2729
2826
|
if (viewpoint.zoom !== null) {
|
|
2730
|
-
|
|
2827
|
+
info2[`${prefix}.zoom`] = String(viewpoint.zoom);
|
|
2731
2828
|
}
|
|
2732
2829
|
if (viewpoint.rotationDeg !== 0) {
|
|
2733
|
-
|
|
2830
|
+
info2[`${prefix}.rotation`] = String(viewpoint.rotationDeg);
|
|
2734
2831
|
}
|
|
2735
2832
|
const serializedLayers = serializeViewpointLayers(viewpoint.layers);
|
|
2736
2833
|
if (serializedLayers) {
|
|
2737
|
-
|
|
2834
|
+
info2[`${prefix}.layers`] = serializedLayers;
|
|
2738
2835
|
}
|
|
2739
2836
|
if (viewpoint.filter?.query) {
|
|
2740
|
-
|
|
2837
|
+
info2[`${prefix}.query`] = viewpoint.filter.query;
|
|
2741
2838
|
}
|
|
2742
2839
|
if ((viewpoint.filter?.objectTypes.length ?? 0) > 0) {
|
|
2743
|
-
|
|
2840
|
+
info2[`${prefix}.types`] = viewpoint.filter?.objectTypes.join(" ") ?? "";
|
|
2744
2841
|
}
|
|
2745
2842
|
if ((viewpoint.filter?.tags.length ?? 0) > 0) {
|
|
2746
|
-
|
|
2843
|
+
info2[`${prefix}.tags`] = viewpoint.filter?.tags.join(" ") ?? "";
|
|
2747
2844
|
}
|
|
2748
2845
|
if ((viewpoint.filter?.groupIds.length ?? 0) > 0) {
|
|
2749
|
-
|
|
2846
|
+
info2[`${prefix}.groups`] = viewpoint.filter?.groupIds.join(" ") ?? "";
|
|
2750
2847
|
}
|
|
2751
2848
|
}
|
|
2752
2849
|
for (const annotation of system.annotations) {
|
|
2753
2850
|
const prefix = `annotation.${annotation.id}`;
|
|
2754
|
-
|
|
2851
|
+
info2[`${prefix}.label`] = annotation.label;
|
|
2755
2852
|
if (annotation.targetObjectId) {
|
|
2756
|
-
|
|
2853
|
+
info2[`${prefix}.target`] = annotation.targetObjectId;
|
|
2757
2854
|
}
|
|
2758
|
-
|
|
2855
|
+
info2[`${prefix}.body`] = annotation.body;
|
|
2759
2856
|
if (annotation.tags.length > 0) {
|
|
2760
|
-
|
|
2857
|
+
info2[`${prefix}.tags`] = annotation.tags.join(" ");
|
|
2761
2858
|
}
|
|
2762
2859
|
if (annotation.sourceObjectId) {
|
|
2763
|
-
|
|
2860
|
+
info2[`${prefix}.source`] = annotation.sourceObjectId;
|
|
2764
2861
|
}
|
|
2765
2862
|
}
|
|
2766
|
-
return
|
|
2863
|
+
return info2;
|
|
2767
2864
|
}
|
|
2768
2865
|
function serializeViewpointLayers(layers) {
|
|
2769
2866
|
const tokens = [];
|
|
@@ -2772,7 +2869,7 @@ var WorldOrbit = (() => {
|
|
|
2772
2869
|
if (orbitFront !== void 0 || orbitBack !== void 0) {
|
|
2773
2870
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
2774
2871
|
}
|
|
2775
|
-
for (const key of ["background", "guides", "objects", "labels", "metadata"]) {
|
|
2872
|
+
for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
|
|
2776
2873
|
if (layers[key] !== void 0) {
|
|
2777
2874
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
2778
2875
|
}
|
|
@@ -2782,7 +2879,8 @@ var WorldOrbit = (() => {
|
|
|
2782
2879
|
function convertAtlasDocumentToLegacyDraft(document2) {
|
|
2783
2880
|
return {
|
|
2784
2881
|
...document2,
|
|
2785
|
-
version: "2.0-draft"
|
|
2882
|
+
version: "2.0-draft",
|
|
2883
|
+
schemaVersion: "2.0-draft"
|
|
2786
2884
|
};
|
|
2787
2885
|
}
|
|
2788
2886
|
|
|
@@ -2824,19 +2922,28 @@ var WorldOrbit = (() => {
|
|
|
2824
2922
|
];
|
|
2825
2923
|
function formatDocument(document2, options = {}) {
|
|
2826
2924
|
const schema = options.schema ?? "auto";
|
|
2827
|
-
const useDraft = schema === "2.0" || schema === "2.0-draft" || document2.version === "2.0" || document2.version === "2.0-draft";
|
|
2925
|
+
const useDraft = schema === "2.0" || schema === "2.1" || schema === "2.0-draft" || document2.version === "2.0" || document2.version === "2.1" || document2.version === "2.0-draft";
|
|
2828
2926
|
if (useDraft) {
|
|
2829
2927
|
if (schema === "2.0-draft") {
|
|
2830
|
-
const legacyDraftDocument = document2.version === "2.0-draft" ? document2 : document2.version === "2.0" ? {
|
|
2928
|
+
const legacyDraftDocument = document2.version === "2.0-draft" ? document2 : document2.version === "2.0" || document2.version === "2.1" ? {
|
|
2831
2929
|
...document2,
|
|
2832
|
-
version: "2.0-draft"
|
|
2930
|
+
version: "2.0-draft",
|
|
2931
|
+
schemaVersion: "2.0-draft"
|
|
2833
2932
|
} : upgradeDocumentToDraftV2(document2);
|
|
2834
2933
|
return formatDraftDocument(legacyDraftDocument);
|
|
2835
2934
|
}
|
|
2836
|
-
const atlasDocument = document2.version === "2.0" ? document2 : document2.version === "2.0-draft" ? {
|
|
2935
|
+
const atlasDocument = document2.version === "2.0" || document2.version === "2.1" ? document2 : document2.version === "2.0-draft" ? {
|
|
2837
2936
|
...document2,
|
|
2838
|
-
version: "2.0"
|
|
2937
|
+
version: "2.0",
|
|
2938
|
+
schemaVersion: "2.0"
|
|
2839
2939
|
} : upgradeDocumentToV2(document2);
|
|
2940
|
+
if (schema === "2.1" && atlasDocument.version !== "2.1") {
|
|
2941
|
+
return formatAtlasDocument({
|
|
2942
|
+
...atlasDocument,
|
|
2943
|
+
version: "2.1",
|
|
2944
|
+
schemaVersion: "2.1"
|
|
2945
|
+
});
|
|
2946
|
+
}
|
|
2840
2947
|
return formatAtlasDocument(atlasDocument);
|
|
2841
2948
|
}
|
|
2842
2949
|
const lines = [];
|
|
@@ -2854,10 +2961,18 @@ var WorldOrbit = (() => {
|
|
|
2854
2961
|
return lines.join("\n");
|
|
2855
2962
|
}
|
|
2856
2963
|
function formatAtlasDocument(document2) {
|
|
2857
|
-
const lines = [
|
|
2964
|
+
const lines = [`schema ${document2.version}`, ""];
|
|
2858
2965
|
if (document2.system) {
|
|
2859
2966
|
lines.push(...formatAtlasSystem(document2.system));
|
|
2860
2967
|
}
|
|
2968
|
+
for (const group of [...document2.groups].sort(compareIdLike)) {
|
|
2969
|
+
lines.push("");
|
|
2970
|
+
lines.push(...formatAtlasGroup(group));
|
|
2971
|
+
}
|
|
2972
|
+
for (const relation of [...document2.relations].sort(compareIdLike)) {
|
|
2973
|
+
lines.push("");
|
|
2974
|
+
lines.push(...formatAtlasRelation(relation));
|
|
2975
|
+
}
|
|
2861
2976
|
const sortedObjects = [...document2.objects].sort(compareObjects);
|
|
2862
2977
|
if (sortedObjects.length > 0 && lines.at(-1) !== "") {
|
|
2863
2978
|
lines.push("");
|
|
@@ -2873,12 +2988,21 @@ var WorldOrbit = (() => {
|
|
|
2873
2988
|
function formatDraftDocument(document2) {
|
|
2874
2989
|
const legacy = document2.version === "2.0-draft" ? document2 : {
|
|
2875
2990
|
...document2,
|
|
2876
|
-
version: "2.0-draft"
|
|
2991
|
+
version: "2.0-draft",
|
|
2992
|
+
schemaVersion: "2.0-draft"
|
|
2877
2993
|
};
|
|
2878
2994
|
const lines = ["schema 2.0-draft", ""];
|
|
2879
2995
|
if (legacy.system) {
|
|
2880
2996
|
lines.push(...formatAtlasSystem(legacy.system));
|
|
2881
2997
|
}
|
|
2998
|
+
for (const group of [...legacy.groups].sort(compareIdLike)) {
|
|
2999
|
+
lines.push("");
|
|
3000
|
+
lines.push(...formatAtlasGroup(group));
|
|
3001
|
+
}
|
|
3002
|
+
for (const relation of [...legacy.relations].sort(compareIdLike)) {
|
|
3003
|
+
lines.push("");
|
|
3004
|
+
lines.push(...formatAtlasRelation(relation));
|
|
3005
|
+
}
|
|
2882
3006
|
const sortedObjects = [...legacy.objects].sort(compareObjects);
|
|
2883
3007
|
if (sortedObjects.length > 0 && lines.at(-1) !== "") {
|
|
2884
3008
|
lines.push("");
|
|
@@ -2894,11 +3018,38 @@ var WorldOrbit = (() => {
|
|
|
2894
3018
|
function formatSystem(system) {
|
|
2895
3019
|
return formatLines("system", system.id, system.properties, null, system.info);
|
|
2896
3020
|
}
|
|
3021
|
+
function formatLines(objectType, id, properties, placement, info2) {
|
|
3022
|
+
const lines = [`${objectType} ${id}`];
|
|
3023
|
+
const fieldLines = [...formatPlacement(placement), ...formatProperties(properties)];
|
|
3024
|
+
for (const fieldLine of fieldLines) {
|
|
3025
|
+
lines.push(` ${fieldLine}`);
|
|
3026
|
+
}
|
|
3027
|
+
const infoEntries = Object.entries(info2).sort(([left], [right]) => left.localeCompare(right));
|
|
3028
|
+
if (infoEntries.length > 0) {
|
|
3029
|
+
if (fieldLines.length > 0) {
|
|
3030
|
+
lines.push("");
|
|
3031
|
+
}
|
|
3032
|
+
lines.push(" info");
|
|
3033
|
+
for (const [key, value] of infoEntries) {
|
|
3034
|
+
lines.push(` ${key} ${quoteIfNeeded(value)}`);
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
return lines;
|
|
3038
|
+
}
|
|
2897
3039
|
function formatAtlasSystem(system) {
|
|
2898
3040
|
const lines = [`system ${system.id}`];
|
|
2899
3041
|
if (system.title) {
|
|
2900
3042
|
lines.push(` title ${quoteIfNeeded(system.title)}`);
|
|
2901
3043
|
}
|
|
3044
|
+
if (system.description) {
|
|
3045
|
+
lines.push(` description ${quoteIfNeeded(system.description)}`);
|
|
3046
|
+
}
|
|
3047
|
+
if (system.epoch) {
|
|
3048
|
+
lines.push(` epoch ${quoteIfNeeded(system.epoch)}`);
|
|
3049
|
+
}
|
|
3050
|
+
if (system.referencePlane) {
|
|
3051
|
+
lines.push(` referencePlane ${quoteIfNeeded(system.referencePlane)}`);
|
|
3052
|
+
}
|
|
2902
3053
|
lines.push("");
|
|
2903
3054
|
lines.push("defaults");
|
|
2904
3055
|
lines.push(` view ${system.defaults.view}`);
|
|
@@ -2933,18 +3084,22 @@ var WorldOrbit = (() => {
|
|
|
2933
3084
|
return lines;
|
|
2934
3085
|
}
|
|
2935
3086
|
function formatObject(object) {
|
|
2936
|
-
return
|
|
3087
|
+
return formatWorldOrbitObject(object.type, object.id, object);
|
|
2937
3088
|
}
|
|
2938
3089
|
function formatAtlasObject(object) {
|
|
2939
|
-
return
|
|
3090
|
+
return formatWorldOrbitObject(`object ${object.type}`, object.id, object);
|
|
2940
3091
|
}
|
|
2941
|
-
function
|
|
3092
|
+
function formatWorldOrbitObject(objectType, id, object) {
|
|
2942
3093
|
const lines = [`${objectType} ${id}`];
|
|
2943
|
-
const fieldLines = [
|
|
3094
|
+
const fieldLines = [
|
|
3095
|
+
...formatPlacement(object.placement),
|
|
3096
|
+
...formatProperties(object.properties),
|
|
3097
|
+
...formatObjectMetadata(object)
|
|
3098
|
+
];
|
|
2944
3099
|
for (const fieldLine of fieldLines) {
|
|
2945
3100
|
lines.push(` ${fieldLine}`);
|
|
2946
3101
|
}
|
|
2947
|
-
const infoEntries = Object.entries(info).sort(([left], [right]) => left.localeCompare(right));
|
|
3102
|
+
const infoEntries = Object.entries(object.info).sort(([left], [right]) => left.localeCompare(right));
|
|
2948
3103
|
if (infoEntries.length > 0) {
|
|
2949
3104
|
if (fieldLines.length > 0) {
|
|
2950
3105
|
lines.push("");
|
|
@@ -2954,6 +3109,16 @@ var WorldOrbit = (() => {
|
|
|
2954
3109
|
lines.push(` ${key} ${quoteIfNeeded(value)}`);
|
|
2955
3110
|
}
|
|
2956
3111
|
}
|
|
3112
|
+
for (const blockName of ["climate", "habitability", "settlement"]) {
|
|
3113
|
+
const blockEntries = Object.entries(object.typedBlocks?.[blockName] ?? {}).sort(([left], [right]) => left.localeCompare(right));
|
|
3114
|
+
if (blockEntries.length > 0) {
|
|
3115
|
+
lines.push("");
|
|
3116
|
+
lines.push(` ${blockName}`);
|
|
3117
|
+
for (const [key, value] of blockEntries) {
|
|
3118
|
+
lines.push(` ${key} ${quoteIfNeeded(value)}`);
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
2957
3122
|
return lines;
|
|
2958
3123
|
}
|
|
2959
3124
|
function formatPlacement(placement) {
|
|
@@ -2982,6 +3147,46 @@ var WorldOrbit = (() => {
|
|
|
2982
3147
|
function formatProperties(properties) {
|
|
2983
3148
|
return Object.keys(properties).sort(compareFieldKeys).map((key) => `${key} ${formatValue(properties[key])}`);
|
|
2984
3149
|
}
|
|
3150
|
+
function formatObjectMetadata(object) {
|
|
3151
|
+
const lines = [];
|
|
3152
|
+
if (object.groups?.length) {
|
|
3153
|
+
lines.push(`groups ${object.groups.join(" ")}`);
|
|
3154
|
+
}
|
|
3155
|
+
if (object.epoch) {
|
|
3156
|
+
lines.push(`epoch ${quoteIfNeeded(object.epoch)}`);
|
|
3157
|
+
}
|
|
3158
|
+
if (object.referencePlane) {
|
|
3159
|
+
lines.push(`referencePlane ${quoteIfNeeded(object.referencePlane)}`);
|
|
3160
|
+
}
|
|
3161
|
+
if (object.tidalLock !== void 0) {
|
|
3162
|
+
lines.push(`tidalLock ${object.tidalLock ? "true" : "false"}`);
|
|
3163
|
+
}
|
|
3164
|
+
if (object.renderHints?.renderLabel !== void 0) {
|
|
3165
|
+
lines.push(`renderLabel ${object.renderHints.renderLabel ? "true" : "false"}`);
|
|
3166
|
+
}
|
|
3167
|
+
if (object.renderHints?.renderOrbit !== void 0) {
|
|
3168
|
+
lines.push(`renderOrbit ${object.renderHints.renderOrbit ? "true" : "false"}`);
|
|
3169
|
+
}
|
|
3170
|
+
if (object.renderHints?.renderPriority !== void 0) {
|
|
3171
|
+
lines.push(`renderPriority ${object.renderHints.renderPriority}`);
|
|
3172
|
+
}
|
|
3173
|
+
if (object.resonance) {
|
|
3174
|
+
lines.push(`resonance ${object.resonance.targetObjectId} ${object.resonance.ratio}`);
|
|
3175
|
+
}
|
|
3176
|
+
for (const rule of object.deriveRules ?? []) {
|
|
3177
|
+
lines.push(`derive ${rule.field} ${rule.strategy}`);
|
|
3178
|
+
}
|
|
3179
|
+
for (const rule of object.validationRules ?? []) {
|
|
3180
|
+
lines.push(`validate ${rule.rule}`);
|
|
3181
|
+
}
|
|
3182
|
+
if (object.lockedFields?.length) {
|
|
3183
|
+
lines.push(`locked ${object.lockedFields.join(" ")}`);
|
|
3184
|
+
}
|
|
3185
|
+
for (const tolerance of object.tolerances ?? []) {
|
|
3186
|
+
lines.push(`tolerance ${tolerance.field} ${formatValue(tolerance.value)}`);
|
|
3187
|
+
}
|
|
3188
|
+
return lines;
|
|
3189
|
+
}
|
|
2985
3190
|
function formatAtlasViewpoint(viewpoint) {
|
|
2986
3191
|
const lines = [`viewpoint ${viewpoint.id}`, ` label ${quoteIfNeeded(viewpoint.label)}`];
|
|
2987
3192
|
if (viewpoint.focusObjectId) {
|
|
@@ -3037,6 +3242,50 @@ var WorldOrbit = (() => {
|
|
|
3037
3242
|
}
|
|
3038
3243
|
return lines;
|
|
3039
3244
|
}
|
|
3245
|
+
function formatAtlasGroup(group) {
|
|
3246
|
+
const lines = [`group ${group.id}`, ` label ${quoteIfNeeded(group.label)}`];
|
|
3247
|
+
if (group.summary) {
|
|
3248
|
+
lines.push(` summary ${quoteIfNeeded(group.summary)}`);
|
|
3249
|
+
}
|
|
3250
|
+
if (group.color) {
|
|
3251
|
+
lines.push(` color ${quoteIfNeeded(group.color)}`);
|
|
3252
|
+
}
|
|
3253
|
+
if (group.tags.length > 0) {
|
|
3254
|
+
lines.push(` tags ${group.tags.map(quoteIfNeeded).join(" ")}`);
|
|
3255
|
+
}
|
|
3256
|
+
if (group.hidden) {
|
|
3257
|
+
lines.push(" hidden true");
|
|
3258
|
+
}
|
|
3259
|
+
return lines;
|
|
3260
|
+
}
|
|
3261
|
+
function formatAtlasRelation(relation) {
|
|
3262
|
+
const lines = [`relation ${relation.id}`];
|
|
3263
|
+
if (relation.from) {
|
|
3264
|
+
lines.push(` from ${quoteIfNeeded(relation.from)}`);
|
|
3265
|
+
}
|
|
3266
|
+
if (relation.to) {
|
|
3267
|
+
lines.push(` to ${quoteIfNeeded(relation.to)}`);
|
|
3268
|
+
}
|
|
3269
|
+
if (relation.kind) {
|
|
3270
|
+
lines.push(` kind ${quoteIfNeeded(relation.kind)}`);
|
|
3271
|
+
}
|
|
3272
|
+
if (relation.label) {
|
|
3273
|
+
lines.push(` label ${quoteIfNeeded(relation.label)}`);
|
|
3274
|
+
}
|
|
3275
|
+
if (relation.summary) {
|
|
3276
|
+
lines.push(` summary ${quoteIfNeeded(relation.summary)}`);
|
|
3277
|
+
}
|
|
3278
|
+
if (relation.tags.length > 0) {
|
|
3279
|
+
lines.push(` tags ${relation.tags.map(quoteIfNeeded).join(" ")}`);
|
|
3280
|
+
}
|
|
3281
|
+
if (relation.color) {
|
|
3282
|
+
lines.push(` color ${quoteIfNeeded(relation.color)}`);
|
|
3283
|
+
}
|
|
3284
|
+
if (relation.hidden) {
|
|
3285
|
+
lines.push(" hidden true");
|
|
3286
|
+
}
|
|
3287
|
+
return lines;
|
|
3288
|
+
}
|
|
3040
3289
|
function formatValue(value) {
|
|
3041
3290
|
if (Array.isArray(value)) {
|
|
3042
3291
|
return value.map((item) => quoteIfNeeded(item)).join(" ");
|
|
@@ -3078,7 +3327,7 @@ var WorldOrbit = (() => {
|
|
|
3078
3327
|
if (orbitFront !== void 0 || orbitBack !== void 0) {
|
|
3079
3328
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
3080
3329
|
}
|
|
3081
|
-
for (const key of ["background", "guides", "objects", "labels", "metadata"]) {
|
|
3330
|
+
for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
|
|
3082
3331
|
if (layers[key] !== void 0) {
|
|
3083
3332
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
3084
3333
|
}
|
|
@@ -3103,6 +3352,9 @@ var WorldOrbit = (() => {
|
|
|
3103
3352
|
return leftIndex - rightIndex;
|
|
3104
3353
|
return left.id.localeCompare(right.id);
|
|
3105
3354
|
}
|
|
3355
|
+
function compareIdLike(left, right) {
|
|
3356
|
+
return left.id.localeCompare(right.id);
|
|
3357
|
+
}
|
|
3106
3358
|
function objectTypeIndex(objectType) {
|
|
3107
3359
|
switch (objectType) {
|
|
3108
3360
|
case "star":
|
|
@@ -3132,24 +3384,533 @@ var WorldOrbit = (() => {
|
|
|
3132
3384
|
return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
3133
3385
|
}
|
|
3134
3386
|
|
|
3387
|
+
// packages/core/dist/atlas-utils.js
|
|
3388
|
+
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)?$/;
|
|
3389
|
+
var BOOLEAN_VALUES2 = /* @__PURE__ */ new Map([
|
|
3390
|
+
["true", true],
|
|
3391
|
+
["false", false],
|
|
3392
|
+
["yes", true],
|
|
3393
|
+
["no", false]
|
|
3394
|
+
]);
|
|
3395
|
+
var URL_SCHEME_PATTERN2 = /^[A-Za-z][A-Za-z0-9+.-]*:/;
|
|
3396
|
+
function normalizeIdentifier2(value) {
|
|
3397
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3398
|
+
}
|
|
3399
|
+
function humanizeIdentifier3(value) {
|
|
3400
|
+
return value.split(/[-_]+/).filter(Boolean).map((segment) => segment[0].toUpperCase() + segment.slice(1)).join(" ");
|
|
3401
|
+
}
|
|
3402
|
+
function parseAtlasUnitValue(input, location, fieldKey) {
|
|
3403
|
+
const match = input.match(UNIT_PATTERN2);
|
|
3404
|
+
if (!match) {
|
|
3405
|
+
throw WorldOrbitError.fromLocation(`Invalid unit value "${input}"`, location);
|
|
3406
|
+
}
|
|
3407
|
+
const unitValue = {
|
|
3408
|
+
value: Number(match[1]),
|
|
3409
|
+
unit: match[2] ?? null
|
|
3410
|
+
};
|
|
3411
|
+
if (fieldKey) {
|
|
3412
|
+
const schema = getFieldSchema(fieldKey);
|
|
3413
|
+
if (schema?.unitFamily && !unitFamilyAllowsUnit(schema.unitFamily, unitValue.unit)) {
|
|
3414
|
+
throw WorldOrbitError.fromLocation(`Unit "${unitValue.unit ?? "none"}" is not valid for "${fieldKey}"`, location);
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
return unitValue;
|
|
3418
|
+
}
|
|
3419
|
+
function tryParseAtlasUnitValue(input) {
|
|
3420
|
+
const match = input.match(UNIT_PATTERN2);
|
|
3421
|
+
if (!match) {
|
|
3422
|
+
return null;
|
|
3423
|
+
}
|
|
3424
|
+
return {
|
|
3425
|
+
value: Number(match[1]),
|
|
3426
|
+
unit: match[2] ?? null
|
|
3427
|
+
};
|
|
3428
|
+
}
|
|
3429
|
+
function parseAtlasNumber(input, key, location) {
|
|
3430
|
+
const value = Number(input);
|
|
3431
|
+
if (!Number.isFinite(value)) {
|
|
3432
|
+
throw WorldOrbitError.fromLocation(`Invalid numeric value "${input}" for "${key}"`, location);
|
|
3433
|
+
}
|
|
3434
|
+
return value;
|
|
3435
|
+
}
|
|
3436
|
+
function parseAtlasBoolean(input, key, location) {
|
|
3437
|
+
const parsed = BOOLEAN_VALUES2.get(input.toLowerCase());
|
|
3438
|
+
if (parsed === void 0) {
|
|
3439
|
+
throw WorldOrbitError.fromLocation(`Invalid boolean value "${input}" for "${key}"`, location);
|
|
3440
|
+
}
|
|
3441
|
+
return parsed;
|
|
3442
|
+
}
|
|
3443
|
+
function parseAtlasAtReference(target, location) {
|
|
3444
|
+
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
3445
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
3446
|
+
}
|
|
3447
|
+
const pairedMatch = target.match(/^([A-Za-z0-9._-]+)-([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
3448
|
+
if (pairedMatch) {
|
|
3449
|
+
return {
|
|
3450
|
+
kind: "lagrange",
|
|
3451
|
+
primary: pairedMatch[1],
|
|
3452
|
+
secondary: pairedMatch[2],
|
|
3453
|
+
point: pairedMatch[3]
|
|
3454
|
+
};
|
|
3455
|
+
}
|
|
3456
|
+
const simpleMatch = target.match(/^([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
3457
|
+
if (simpleMatch) {
|
|
3458
|
+
return {
|
|
3459
|
+
kind: "lagrange",
|
|
3460
|
+
primary: simpleMatch[1],
|
|
3461
|
+
secondary: null,
|
|
3462
|
+
point: simpleMatch[2]
|
|
3463
|
+
};
|
|
3464
|
+
}
|
|
3465
|
+
if (/^[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
3466
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
3467
|
+
}
|
|
3468
|
+
const anchorMatch = target.match(/^([A-Za-z0-9._-]+):([A-Za-z0-9._-]+)$/);
|
|
3469
|
+
if (anchorMatch) {
|
|
3470
|
+
return {
|
|
3471
|
+
kind: "anchor",
|
|
3472
|
+
objectId: anchorMatch[1],
|
|
3473
|
+
anchor: anchorMatch[2]
|
|
3474
|
+
};
|
|
3475
|
+
}
|
|
3476
|
+
return {
|
|
3477
|
+
kind: "named",
|
|
3478
|
+
name: target
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3481
|
+
function validateAtlasImageSource(value, location) {
|
|
3482
|
+
if (!value) {
|
|
3483
|
+
throw WorldOrbitError.fromLocation('Field "image" must not be empty', location);
|
|
3484
|
+
}
|
|
3485
|
+
if (value.startsWith("//")) {
|
|
3486
|
+
throw WorldOrbitError.fromLocation('Field "image" must use a relative path, root-relative path, or an http/https URL', location);
|
|
3487
|
+
}
|
|
3488
|
+
const schemeMatch = value.match(URL_SCHEME_PATTERN2);
|
|
3489
|
+
if (!schemeMatch) {
|
|
3490
|
+
return;
|
|
3491
|
+
}
|
|
3492
|
+
const scheme = schemeMatch[0].slice(0, -1).toLowerCase();
|
|
3493
|
+
if (scheme !== "http" && scheme !== "https") {
|
|
3494
|
+
throw WorldOrbitError.fromLocation(`Field "image" does not support the "${scheme}" scheme`, location);
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
function normalizeLegacyScalarValue(key, values, location) {
|
|
3498
|
+
const schema = getFieldSchema(key);
|
|
3499
|
+
if (!schema) {
|
|
3500
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
3501
|
+
}
|
|
3502
|
+
if (schema.arity === "single" && values.length !== 1) {
|
|
3503
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
3504
|
+
}
|
|
3505
|
+
switch (schema.kind) {
|
|
3506
|
+
case "list":
|
|
3507
|
+
return values;
|
|
3508
|
+
case "boolean":
|
|
3509
|
+
return parseAtlasBoolean(singleAtlasValue(values, key, location), key, location);
|
|
3510
|
+
case "number":
|
|
3511
|
+
return parseAtlasNumber(singleAtlasValue(values, key, location), key, location);
|
|
3512
|
+
case "unit":
|
|
3513
|
+
return parseAtlasUnitValue(singleAtlasValue(values, key, location), location, key);
|
|
3514
|
+
case "string": {
|
|
3515
|
+
const value = values.join(" ").trim();
|
|
3516
|
+
if (key === "image") {
|
|
3517
|
+
validateAtlasImageSource(value, location);
|
|
3518
|
+
}
|
|
3519
|
+
return value;
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
function ensureAtlasFieldSupported(key, objectType, location) {
|
|
3524
|
+
const schema = getFieldSchema(key);
|
|
3525
|
+
if (!schema) {
|
|
3526
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
3527
|
+
}
|
|
3528
|
+
if (!schema.objectTypes.includes(objectType)) {
|
|
3529
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" is not valid on "${objectType}"`, location);
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
function singleAtlasValue(values, key, location) {
|
|
3533
|
+
if (values.length !== 1) {
|
|
3534
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
3535
|
+
}
|
|
3536
|
+
return values[0];
|
|
3537
|
+
}
|
|
3538
|
+
|
|
3539
|
+
// packages/core/dist/atlas-validate.js
|
|
3540
|
+
var SURFACE_TARGET_TYPES2 = /* @__PURE__ */ new Set(["star", "planet", "moon", "asteroid", "comet"]);
|
|
3541
|
+
var EARTH_MASSES_PER_SOLAR = 332946.0487;
|
|
3542
|
+
var JUPITER_MASSES_PER_SOLAR = 1047.3486;
|
|
3543
|
+
var AU_IN_KM2 = 1495978707e-1;
|
|
3544
|
+
var EARTH_RADIUS_IN_KM2 = 6371;
|
|
3545
|
+
var SOLAR_RADIUS_IN_KM2 = 695700;
|
|
3546
|
+
var LY_IN_AU2 = 63241.077;
|
|
3547
|
+
var PC_IN_AU2 = 206264.806;
|
|
3548
|
+
var KPC_IN_AU2 = 206264806;
|
|
3549
|
+
function collectAtlasDiagnostics(document2, sourceSchemaVersion) {
|
|
3550
|
+
const diagnostics = [];
|
|
3551
|
+
const objectMap = new Map(document2.objects.map((object) => [object.id, object]));
|
|
3552
|
+
const groupIds = new Set(document2.groups.map((group) => group.id));
|
|
3553
|
+
if (!document2.system) {
|
|
3554
|
+
diagnostics.push(error("validate.system.required", "Atlas documents must declare exactly one system."));
|
|
3555
|
+
}
|
|
3556
|
+
const knownIds = /* @__PURE__ */ new Map();
|
|
3557
|
+
for (const [kind, ids] of [
|
|
3558
|
+
["group", document2.groups.map((group) => group.id)],
|
|
3559
|
+
["viewpoint", document2.system?.viewpoints.map((viewpoint) => viewpoint.id) ?? []],
|
|
3560
|
+
["annotation", document2.system?.annotations.map((annotation) => annotation.id) ?? []],
|
|
3561
|
+
["relation", document2.relations.map((relation) => relation.id)],
|
|
3562
|
+
["object", document2.objects.map((object) => object.id)]
|
|
3563
|
+
]) {
|
|
3564
|
+
for (const id of ids) {
|
|
3565
|
+
const previous = knownIds.get(id);
|
|
3566
|
+
if (previous) {
|
|
3567
|
+
diagnostics.push(error("validate.id.duplicate", `Duplicate ${kind} id "${id}" already used by ${previous}.`));
|
|
3568
|
+
} else {
|
|
3569
|
+
knownIds.set(id, kind);
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3573
|
+
for (const relation of document2.relations) {
|
|
3574
|
+
validateRelation(relation, objectMap, diagnostics);
|
|
3575
|
+
}
|
|
3576
|
+
for (const viewpoint of document2.system?.viewpoints ?? []) {
|
|
3577
|
+
validateViewpointFilter(viewpoint.filter, groupIds, sourceSchemaVersion, diagnostics, viewpoint.id);
|
|
3578
|
+
}
|
|
3579
|
+
for (const object of document2.objects) {
|
|
3580
|
+
validateObject(object, document2.system, objectMap, groupIds, diagnostics);
|
|
3581
|
+
}
|
|
3582
|
+
return diagnostics;
|
|
3583
|
+
}
|
|
3584
|
+
function validateRelation(relation, objectMap, diagnostics) {
|
|
3585
|
+
if (!relation.from) {
|
|
3586
|
+
diagnostics.push(error("validate.relation.from.required", `Relation "${relation.id}" is missing a "from" target.`));
|
|
3587
|
+
} else if (!objectMap.has(relation.from)) {
|
|
3588
|
+
diagnostics.push(error("validate.relation.from.unknown", `Unknown relation source "${relation.from}" on "${relation.id}".`));
|
|
3589
|
+
}
|
|
3590
|
+
if (!relation.to) {
|
|
3591
|
+
diagnostics.push(error("validate.relation.to.required", `Relation "${relation.id}" is missing a "to" target.`));
|
|
3592
|
+
} else if (!objectMap.has(relation.to)) {
|
|
3593
|
+
diagnostics.push(error("validate.relation.to.unknown", `Unknown relation target "${relation.to}" on "${relation.id}".`));
|
|
3594
|
+
}
|
|
3595
|
+
if (!relation.kind) {
|
|
3596
|
+
diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
function validateViewpointFilter(filter, groupIds, sourceSchemaVersion, diagnostics, viewpointId) {
|
|
3600
|
+
if (!filter || sourceSchemaVersion !== "2.1") {
|
|
3601
|
+
return;
|
|
3602
|
+
}
|
|
3603
|
+
for (const groupId of filter.groupIds) {
|
|
3604
|
+
if (!groupIds.has(groupId)) {
|
|
3605
|
+
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpointId}".`));
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
3610
|
+
const placement = object.placement;
|
|
3611
|
+
const orbitPlacement = placement?.mode === "orbit" ? placement : null;
|
|
3612
|
+
const parentObject = placement?.mode === "orbit" ? objectMap.get(placement.target) ?? null : null;
|
|
3613
|
+
if (object.groups) {
|
|
3614
|
+
for (const groupId of object.groups) {
|
|
3615
|
+
if (!groupIds.has(groupId)) {
|
|
3616
|
+
diagnostics.push(warn("validate.group.unknown", `Unknown group "${groupId}" on "${object.id}".`, object.id, "groups"));
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
if (orbitPlacement) {
|
|
3621
|
+
if (!objectMap.has(orbitPlacement.target)) {
|
|
3622
|
+
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
3623
|
+
}
|
|
3624
|
+
if (orbitPlacement.distance && orbitPlacement.semiMajor) {
|
|
3625
|
+
diagnostics.push(error("validate.orbit.distanceConflict", `Object "${object.id}" cannot declare both "distance" and "semiMajor".`, object.id, "distance"));
|
|
3626
|
+
}
|
|
3627
|
+
if (orbitPlacement.phase && !object.epoch && !system?.epoch) {
|
|
3628
|
+
diagnostics.push(warn("validate.phase.epochMissing", `Object "${object.id}" sets "phase" without an object or system epoch.`, object.id, "phase"));
|
|
3629
|
+
}
|
|
3630
|
+
if (orbitPlacement.inclination && !object.referencePlane && !system?.referencePlane) {
|
|
3631
|
+
diagnostics.push(warn("validate.inclination.referencePlaneMissing", `Object "${object.id}" sets "inclination" without an object or system reference plane.`, object.id, "inclination"));
|
|
3632
|
+
}
|
|
3633
|
+
if (orbitPlacement.period && !massInSolar(parentObject?.properties.mass)) {
|
|
3634
|
+
diagnostics.push(warn("validate.period.massMissing", `Object "${object.id}" sets "period" but its central mass cannot be derived.`, object.id, "period"));
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
if (placement?.mode === "surface") {
|
|
3638
|
+
const target = objectMap.get(placement.target);
|
|
3639
|
+
if (!target) {
|
|
3640
|
+
diagnostics.push(error("validate.surface.target.unknown", `Unknown placement target "${placement.target}" on "${object.id}".`, object.id, "surface"));
|
|
3641
|
+
} else if (!SURFACE_TARGET_TYPES2.has(target.type)) {
|
|
3642
|
+
diagnostics.push(error("validate.surface.target.invalid", `Surface target "${placement.target}" on "${object.id}" is not surface-capable.`, object.id, "surface"));
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
if (placement?.mode === "at") {
|
|
3646
|
+
if (object.type !== "structure" && object.type !== "phenomenon") {
|
|
3647
|
+
diagnostics.push(error("validate.at.objectType", `Only structures and phenomena may use "at" placement; found "${object.type}" on "${object.id}".`, object.id, "at"));
|
|
3648
|
+
}
|
|
3649
|
+
if (!validateAtTarget(object, objectMap, diagnostics)) {
|
|
3650
|
+
diagnostics.push(error("validate.at.target.unknown", `Unknown at-reference target "${placement.target}" on "${object.id}".`, object.id, "at"));
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
if (object.resonance) {
|
|
3654
|
+
const target = objectMap.get(object.resonance.targetObjectId);
|
|
3655
|
+
if (!target) {
|
|
3656
|
+
diagnostics.push(error("validate.resonance.target.unknown", `Unknown resonance target "${object.resonance.targetObjectId}" on "${object.id}".`, object.id, "resonance"));
|
|
3657
|
+
} else if (object.placement?.mode !== "orbit" || target.placement?.mode !== "orbit" || object.placement.target !== target.placement.target) {
|
|
3658
|
+
diagnostics.push(warn("validate.resonance.orbitMismatch", `Resonance target "${object.resonance.targetObjectId}" on "${object.id}" does not share a compatible orbital parent.`, object.id, "resonance"));
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
for (const rule of object.deriveRules ?? []) {
|
|
3662
|
+
if (rule.field !== "period" || rule.strategy !== "kepler") {
|
|
3663
|
+
diagnostics.push(warn("validate.derive.unsupported", `Unsupported derive rule "${rule.field} ${rule.strategy}" on "${object.id}".`, object.id, "derive"));
|
|
3664
|
+
continue;
|
|
3665
|
+
}
|
|
3666
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
3667
|
+
if (derivedPeriodDays === null) {
|
|
3668
|
+
diagnostics.push(warn("validate.derive.inputsMissing", `Object "${object.id}" requests "derive period kepler" but lacks enough input data.`, object.id, "derive"));
|
|
3669
|
+
continue;
|
|
3670
|
+
}
|
|
3671
|
+
if (!orbitPlacement?.period) {
|
|
3672
|
+
diagnostics.push(info("validate.derive.period.available", `Object "${object.id}" can derive a Kepler period of ${formatDays(derivedPeriodDays)}.`, object.id, "derive"));
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
for (const rule of object.validationRules ?? []) {
|
|
3676
|
+
if (rule.rule !== "kepler") {
|
|
3677
|
+
diagnostics.push(warn("validate.rule.unsupported", `Unsupported validation rule "${rule.rule}" on "${object.id}".`, object.id, "validate"));
|
|
3678
|
+
continue;
|
|
3679
|
+
}
|
|
3680
|
+
const actualPeriodDays = durationInDays(orbitPlacement?.period);
|
|
3681
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
3682
|
+
if (actualPeriodDays === null || derivedPeriodDays === null) {
|
|
3683
|
+
continue;
|
|
3684
|
+
}
|
|
3685
|
+
const toleranceDays = toleranceForField(object, "period");
|
|
3686
|
+
if (Math.abs(actualPeriodDays - derivedPeriodDays) > toleranceDays) {
|
|
3687
|
+
diagnostics.push(error("validate.kepler.mismatch", `Object "${object.id}" fails Kepler validation for "period".`, object.id, "validate"));
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
function validateAtTarget(object, objectMap, diagnostics) {
|
|
3692
|
+
const reference = object.placement?.mode === "at" ? object.placement.reference : null;
|
|
3693
|
+
if (!reference) {
|
|
3694
|
+
return true;
|
|
3695
|
+
}
|
|
3696
|
+
if (reference.kind === "named") {
|
|
3697
|
+
return objectMap.has(reference.name);
|
|
3698
|
+
}
|
|
3699
|
+
if (reference.kind === "anchor") {
|
|
3700
|
+
if (!objectMap.has(reference.objectId)) {
|
|
3701
|
+
diagnostics.push(error("validate.anchor.target.unknown", `Unknown anchor target "${reference.objectId}" on "${object.id}".`, object.id, "at"));
|
|
3702
|
+
return false;
|
|
3703
|
+
}
|
|
3704
|
+
return true;
|
|
3705
|
+
}
|
|
3706
|
+
if (!objectMap.has(reference.primary)) {
|
|
3707
|
+
diagnostics.push(error("validate.lagrange.primary.unknown", `Unknown Lagrange reference "${reference.primary}" on "${object.id}".`, object.id, "at"));
|
|
3708
|
+
return false;
|
|
3709
|
+
}
|
|
3710
|
+
if (reference.secondary && !objectMap.has(reference.secondary)) {
|
|
3711
|
+
diagnostics.push(error("validate.lagrange.secondary.unknown", `Unknown Lagrange reference "${reference.secondary}" on "${object.id}".`, object.id, "at"));
|
|
3712
|
+
return false;
|
|
3713
|
+
}
|
|
3714
|
+
return true;
|
|
3715
|
+
}
|
|
3716
|
+
function keplerPeriodDays(object, parentObject) {
|
|
3717
|
+
const placement = object.placement;
|
|
3718
|
+
if (!placement || placement.mode !== "orbit") {
|
|
3719
|
+
return null;
|
|
3720
|
+
}
|
|
3721
|
+
const semiMajorAu = distanceInAu(placement.semiMajor ?? placement.distance);
|
|
3722
|
+
const centralMassSolar = massInSolar(parentObject?.properties.mass);
|
|
3723
|
+
if (semiMajorAu === null || centralMassSolar === null || centralMassSolar <= 0) {
|
|
3724
|
+
return null;
|
|
3725
|
+
}
|
|
3726
|
+
const periodYears = Math.sqrt(semiMajorAu ** 3 / centralMassSolar);
|
|
3727
|
+
return periodYears * 365.25;
|
|
3728
|
+
}
|
|
3729
|
+
function distanceInAu(value) {
|
|
3730
|
+
if (!value)
|
|
3731
|
+
return null;
|
|
3732
|
+
switch (value.unit) {
|
|
3733
|
+
case null:
|
|
3734
|
+
case "au":
|
|
3735
|
+
return value.value;
|
|
3736
|
+
case "km":
|
|
3737
|
+
return value.value / AU_IN_KM2;
|
|
3738
|
+
case "m":
|
|
3739
|
+
return value.value / (AU_IN_KM2 * 1e3);
|
|
3740
|
+
case "ly":
|
|
3741
|
+
return value.value * LY_IN_AU2;
|
|
3742
|
+
case "pc":
|
|
3743
|
+
return value.value * PC_IN_AU2;
|
|
3744
|
+
case "kpc":
|
|
3745
|
+
return value.value * KPC_IN_AU2;
|
|
3746
|
+
case "re":
|
|
3747
|
+
return value.value * EARTH_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
3748
|
+
case "sol":
|
|
3749
|
+
return value.value * SOLAR_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
3750
|
+
default:
|
|
3751
|
+
return null;
|
|
3752
|
+
}
|
|
3753
|
+
}
|
|
3754
|
+
function massInSolar(value) {
|
|
3755
|
+
if (!value || typeof value !== "object" || !("value" in value)) {
|
|
3756
|
+
return null;
|
|
3757
|
+
}
|
|
3758
|
+
const unitValue = value;
|
|
3759
|
+
switch (unitValue.unit) {
|
|
3760
|
+
case null:
|
|
3761
|
+
case "sol":
|
|
3762
|
+
return unitValue.value;
|
|
3763
|
+
case "me":
|
|
3764
|
+
return unitValue.value / EARTH_MASSES_PER_SOLAR;
|
|
3765
|
+
case "mj":
|
|
3766
|
+
return unitValue.value / JUPITER_MASSES_PER_SOLAR;
|
|
3767
|
+
default:
|
|
3768
|
+
return null;
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
function durationInDays(value) {
|
|
3772
|
+
if (!value)
|
|
3773
|
+
return null;
|
|
3774
|
+
switch (value.unit) {
|
|
3775
|
+
case null:
|
|
3776
|
+
case "d":
|
|
3777
|
+
return value.value;
|
|
3778
|
+
case "s":
|
|
3779
|
+
return value.value / 86400;
|
|
3780
|
+
case "min":
|
|
3781
|
+
return value.value / 1440;
|
|
3782
|
+
case "h":
|
|
3783
|
+
return value.value / 24;
|
|
3784
|
+
case "y":
|
|
3785
|
+
return value.value * 365.25;
|
|
3786
|
+
case "ky":
|
|
3787
|
+
return value.value * 365250;
|
|
3788
|
+
case "my":
|
|
3789
|
+
return value.value * 36525e4;
|
|
3790
|
+
case "gy":
|
|
3791
|
+
return value.value * 36525e7;
|
|
3792
|
+
default:
|
|
3793
|
+
return null;
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
function toleranceForField(object, field) {
|
|
3797
|
+
const tolerance = object.tolerances?.find((entry) => entry.field === field)?.value;
|
|
3798
|
+
if (typeof tolerance === "number") {
|
|
3799
|
+
return tolerance;
|
|
3800
|
+
}
|
|
3801
|
+
if (tolerance && typeof tolerance === "object" && "value" in tolerance) {
|
|
3802
|
+
return durationInDays(tolerance) ?? 0;
|
|
3803
|
+
}
|
|
3804
|
+
return 0;
|
|
3805
|
+
}
|
|
3806
|
+
function formatDays(days) {
|
|
3807
|
+
return `${Math.round(days * 100) / 100}d`;
|
|
3808
|
+
}
|
|
3809
|
+
function error(code, message, objectId, field) {
|
|
3810
|
+
return { code, severity: "error", source: "validate", message, objectId, field };
|
|
3811
|
+
}
|
|
3812
|
+
function warn(code, message, objectId, field) {
|
|
3813
|
+
return { code, severity: "warning", source: "validate", message, objectId, field };
|
|
3814
|
+
}
|
|
3815
|
+
function info(code, message, objectId, field) {
|
|
3816
|
+
return { code, severity: "info", source: "validate", message, objectId, field };
|
|
3817
|
+
}
|
|
3818
|
+
|
|
3135
3819
|
// packages/core/dist/draft-parse.js
|
|
3820
|
+
var STRUCTURED_TYPED_BLOCKS = /* @__PURE__ */ new Set([
|
|
3821
|
+
"climate",
|
|
3822
|
+
"habitability",
|
|
3823
|
+
"settlement"
|
|
3824
|
+
]);
|
|
3825
|
+
var DRAFT_OBJECT_FIELD_SPECS = /* @__PURE__ */ new Map();
|
|
3826
|
+
for (const key of [
|
|
3827
|
+
"orbit",
|
|
3828
|
+
"distance",
|
|
3829
|
+
"semiMajor",
|
|
3830
|
+
"eccentricity",
|
|
3831
|
+
"period",
|
|
3832
|
+
"angle",
|
|
3833
|
+
"inclination",
|
|
3834
|
+
"phase",
|
|
3835
|
+
"at",
|
|
3836
|
+
"surface",
|
|
3837
|
+
"free",
|
|
3838
|
+
"kind",
|
|
3839
|
+
"class",
|
|
3840
|
+
"culture",
|
|
3841
|
+
"tags",
|
|
3842
|
+
"color",
|
|
3843
|
+
"image",
|
|
3844
|
+
"hidden",
|
|
3845
|
+
"radius",
|
|
3846
|
+
"mass",
|
|
3847
|
+
"density",
|
|
3848
|
+
"gravity",
|
|
3849
|
+
"temperature",
|
|
3850
|
+
"albedo",
|
|
3851
|
+
"atmosphere",
|
|
3852
|
+
"inner",
|
|
3853
|
+
"outer",
|
|
3854
|
+
"on",
|
|
3855
|
+
"source",
|
|
3856
|
+
"cycle"
|
|
3857
|
+
]) {
|
|
3858
|
+
const schema = getFieldSchema(key);
|
|
3859
|
+
if (schema) {
|
|
3860
|
+
DRAFT_OBJECT_FIELD_SPECS.set(key, {
|
|
3861
|
+
key,
|
|
3862
|
+
version: "2.0",
|
|
3863
|
+
inlineMode: schema.arity === "multiple" ? "multiple" : "single",
|
|
3864
|
+
allowRepeat: false,
|
|
3865
|
+
legacySchema: schema
|
|
3866
|
+
});
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
for (const spec of [
|
|
3870
|
+
{ key: "groups", inlineMode: "multiple", allowRepeat: false },
|
|
3871
|
+
{ key: "epoch", inlineMode: "single", allowRepeat: false },
|
|
3872
|
+
{ key: "referencePlane", inlineMode: "single", allowRepeat: false },
|
|
3873
|
+
{ key: "tidalLock", inlineMode: "single", allowRepeat: false },
|
|
3874
|
+
{ key: "renderLabel", inlineMode: "single", allowRepeat: false },
|
|
3875
|
+
{ key: "renderOrbit", inlineMode: "single", allowRepeat: false },
|
|
3876
|
+
{ key: "renderPriority", inlineMode: "single", allowRepeat: false },
|
|
3877
|
+
{ key: "resonance", inlineMode: "pair", allowRepeat: false },
|
|
3878
|
+
{ key: "derive", inlineMode: "pair", allowRepeat: true },
|
|
3879
|
+
{ key: "validate", inlineMode: "single", allowRepeat: true },
|
|
3880
|
+
{ key: "locked", inlineMode: "multiple", allowRepeat: false },
|
|
3881
|
+
{ key: "tolerance", inlineMode: "pair", allowRepeat: true }
|
|
3882
|
+
]) {
|
|
3883
|
+
DRAFT_OBJECT_FIELD_SPECS.set(spec.key, {
|
|
3884
|
+
key: spec.key,
|
|
3885
|
+
version: "2.1",
|
|
3886
|
+
inlineMode: spec.inlineMode,
|
|
3887
|
+
allowRepeat: spec.allowRepeat
|
|
3888
|
+
});
|
|
3889
|
+
}
|
|
3890
|
+
var DRAFT_OBJECT_FIELD_KEYS = new Set(DRAFT_OBJECT_FIELD_SPECS.keys());
|
|
3136
3891
|
function parseWorldOrbitAtlas(source) {
|
|
3137
|
-
return parseAtlasSource(source
|
|
3892
|
+
return parseAtlasSource(source);
|
|
3138
3893
|
}
|
|
3139
3894
|
function parseWorldOrbitDraft(source) {
|
|
3140
3895
|
return parseAtlasSource(source, "2.0-draft");
|
|
3141
3896
|
}
|
|
3142
|
-
function parseAtlasSource(source,
|
|
3143
|
-
const
|
|
3897
|
+
function parseAtlasSource(source, forcedOutputVersion) {
|
|
3898
|
+
const prepared = preprocessAtlasSource(source);
|
|
3899
|
+
const lines = prepared.source.split(/\r?\n/);
|
|
3900
|
+
const diagnostics = [];
|
|
3144
3901
|
let sawSchemaHeader = false;
|
|
3145
|
-
let
|
|
3902
|
+
let sourceSchemaVersion = "2.0";
|
|
3146
3903
|
let system = null;
|
|
3147
3904
|
let section = null;
|
|
3148
3905
|
const objectNodes = [];
|
|
3906
|
+
const groups = [];
|
|
3907
|
+
const relations = [];
|
|
3149
3908
|
let sawDefaults = false;
|
|
3150
3909
|
let sawAtlas = false;
|
|
3151
3910
|
const viewpointIds = /* @__PURE__ */ new Set();
|
|
3152
3911
|
const annotationIds = /* @__PURE__ */ new Set();
|
|
3912
|
+
const groupIds = /* @__PURE__ */ new Set();
|
|
3913
|
+
const relationIds = /* @__PURE__ */ new Set();
|
|
3153
3914
|
for (let index = 0; index < lines.length; index++) {
|
|
3154
3915
|
const rawLine = lines[index];
|
|
3155
3916
|
const lineNumber = index + 1;
|
|
@@ -3165,15 +3926,22 @@ var WorldOrbit = (() => {
|
|
|
3165
3926
|
continue;
|
|
3166
3927
|
}
|
|
3167
3928
|
if (!sawSchemaHeader) {
|
|
3168
|
-
|
|
3929
|
+
sourceSchemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
3169
3930
|
sawSchemaHeader = true;
|
|
3931
|
+
if (prepared.comments.length > 0 && sourceSchemaVersion !== "2.1") {
|
|
3932
|
+
diagnostics.push({
|
|
3933
|
+
code: "parse.schema21.commentCompatibility",
|
|
3934
|
+
severity: "warning",
|
|
3935
|
+
source: "parse",
|
|
3936
|
+
message: `Comments require schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
3937
|
+
line: prepared.comments[0].line,
|
|
3938
|
+
column: prepared.comments[0].column
|
|
3939
|
+
});
|
|
3940
|
+
}
|
|
3170
3941
|
continue;
|
|
3171
3942
|
}
|
|
3172
3943
|
if (indent === 0) {
|
|
3173
|
-
section = startTopLevelSection(tokens, lineNumber, system, objectNodes, viewpointIds, annotationIds, {
|
|
3174
|
-
sawDefaults,
|
|
3175
|
-
sawAtlas
|
|
3176
|
-
});
|
|
3944
|
+
section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, { sawDefaults, sawAtlas });
|
|
3177
3945
|
if (section.kind === "system") {
|
|
3178
3946
|
system = section.system;
|
|
3179
3947
|
} else if (section.kind === "defaults") {
|
|
@@ -3191,48 +3959,57 @@ var WorldOrbit = (() => {
|
|
|
3191
3959
|
if (!sawSchemaHeader) {
|
|
3192
3960
|
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
|
|
3193
3961
|
}
|
|
3194
|
-
const
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
};
|
|
3198
|
-
const normalizedObjects = normalizeDocument(ast).objects;
|
|
3199
|
-
validateDocument({
|
|
3962
|
+
const objects = objectNodes.map((node) => normalizeDraftObject(node, sourceSchemaVersion, diagnostics));
|
|
3963
|
+
const outputVersion = forcedOutputVersion ?? (sourceSchemaVersion === "2.0-draft" ? "2.0" : sourceSchemaVersion);
|
|
3964
|
+
const baseDocument = {
|
|
3200
3965
|
format: "worldorbit",
|
|
3201
|
-
version: "1.0",
|
|
3202
|
-
system: null,
|
|
3203
|
-
objects: normalizedObjects
|
|
3204
|
-
});
|
|
3205
|
-
const diagnostics = schemaVersion === "2.0-draft" && outputVersion === "2.0" ? [
|
|
3206
|
-
{
|
|
3207
|
-
code: "load.schema.deprecatedDraft",
|
|
3208
|
-
severity: "warning",
|
|
3209
|
-
source: "upgrade",
|
|
3210
|
-
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
3211
|
-
}
|
|
3212
|
-
] : [];
|
|
3213
|
-
return {
|
|
3214
|
-
format: "worldorbit",
|
|
3215
|
-
version: outputVersion,
|
|
3216
3966
|
sourceVersion: "1.0",
|
|
3217
3967
|
system,
|
|
3218
|
-
|
|
3968
|
+
groups,
|
|
3969
|
+
relations,
|
|
3970
|
+
objects,
|
|
3219
3971
|
diagnostics
|
|
3220
3972
|
};
|
|
3973
|
+
if (outputVersion === "2.0-draft") {
|
|
3974
|
+
const document3 = {
|
|
3975
|
+
...baseDocument,
|
|
3976
|
+
version: "2.0-draft",
|
|
3977
|
+
schemaVersion: "2.0-draft"
|
|
3978
|
+
};
|
|
3979
|
+
document3.diagnostics.push(...collectAtlasDiagnostics(document3, sourceSchemaVersion));
|
|
3980
|
+
return document3;
|
|
3981
|
+
}
|
|
3982
|
+
const document2 = {
|
|
3983
|
+
...baseDocument,
|
|
3984
|
+
version: outputVersion,
|
|
3985
|
+
schemaVersion: outputVersion
|
|
3986
|
+
};
|
|
3987
|
+
if (sourceSchemaVersion === "2.0-draft") {
|
|
3988
|
+
document2.diagnostics.push({
|
|
3989
|
+
code: "load.schema.deprecatedDraft",
|
|
3990
|
+
severity: "warning",
|
|
3991
|
+
source: "upgrade",
|
|
3992
|
+
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
3993
|
+
});
|
|
3994
|
+
}
|
|
3995
|
+
document2.diagnostics.push(...collectAtlasDiagnostics(document2, sourceSchemaVersion));
|
|
3996
|
+
return document2;
|
|
3221
3997
|
}
|
|
3222
3998
|
function assertDraftSchemaHeader(tokens, line) {
|
|
3223
|
-
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" ||
|
|
3224
|
-
throw new WorldOrbitError('Expected atlas header "schema 2.0" or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
3999
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1"].includes(tokens[1].value.toLowerCase())) {
|
|
4000
|
+
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
3225
4001
|
}
|
|
3226
|
-
|
|
4002
|
+
const version = tokens[1].value.toLowerCase();
|
|
4003
|
+
return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
3227
4004
|
}
|
|
3228
|
-
function startTopLevelSection(tokens, line, system, objectNodes, viewpointIds, annotationIds, flags) {
|
|
4005
|
+
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, flags) {
|
|
3229
4006
|
const keyword = tokens[0]?.value.toLowerCase();
|
|
3230
4007
|
switch (keyword) {
|
|
3231
4008
|
case "system":
|
|
3232
4009
|
if (system) {
|
|
3233
4010
|
throw new WorldOrbitError('Atlas section "system" may only appear once', line, tokens[0].column);
|
|
3234
4011
|
}
|
|
3235
|
-
return startSystemSection(tokens, line);
|
|
4012
|
+
return startSystemSection(tokens, line, sourceSchemaVersion, diagnostics);
|
|
3236
4013
|
case "defaults":
|
|
3237
4014
|
if (!system) {
|
|
3238
4015
|
throw new WorldOrbitError('Atlas section "defaults" requires a preceding system declaration', line, tokens[0].column);
|
|
@@ -3268,13 +4045,19 @@ var WorldOrbit = (() => {
|
|
|
3268
4045
|
throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
|
|
3269
4046
|
}
|
|
3270
4047
|
return startAnnotationSection(tokens, line, system, annotationIds);
|
|
4048
|
+
case "group":
|
|
4049
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "group", { line, column: tokens[0].column });
|
|
4050
|
+
return startGroupSection(tokens, line, groups, groupIds);
|
|
4051
|
+
case "relation":
|
|
4052
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "relation", { line, column: tokens[0].column });
|
|
4053
|
+
return startRelationSection(tokens, line, relations, relationIds);
|
|
3271
4054
|
case "object":
|
|
3272
|
-
return startObjectSection(tokens, line, objectNodes);
|
|
4055
|
+
return startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes);
|
|
3273
4056
|
default:
|
|
3274
4057
|
throw new WorldOrbitError(`Unknown atlas section "${tokens[0]?.value ?? ""}"`, line, tokens[0]?.column ?? 1);
|
|
3275
4058
|
}
|
|
3276
4059
|
}
|
|
3277
|
-
function startSystemSection(tokens, line) {
|
|
4060
|
+
function startSystemSection(tokens, line, sourceSchemaVersion, diagnostics) {
|
|
3278
4061
|
if (tokens.length !== 2) {
|
|
3279
4062
|
throw new WorldOrbitError("Invalid atlas system declaration", line, tokens[0]?.column ?? 1);
|
|
3280
4063
|
}
|
|
@@ -3282,6 +4065,9 @@ var WorldOrbit = (() => {
|
|
|
3282
4065
|
type: "system",
|
|
3283
4066
|
id: tokens[1].value,
|
|
3284
4067
|
title: null,
|
|
4068
|
+
description: null,
|
|
4069
|
+
epoch: null,
|
|
4070
|
+
referencePlane: null,
|
|
3285
4071
|
defaults: {
|
|
3286
4072
|
view: "topdown",
|
|
3287
4073
|
scale: null,
|
|
@@ -3296,6 +4082,8 @@ var WorldOrbit = (() => {
|
|
|
3296
4082
|
return {
|
|
3297
4083
|
kind: "system",
|
|
3298
4084
|
system,
|
|
4085
|
+
sourceSchemaVersion,
|
|
4086
|
+
diagnostics,
|
|
3299
4087
|
seenFields: /* @__PURE__ */ new Set()
|
|
3300
4088
|
};
|
|
3301
4089
|
}
|
|
@@ -3361,7 +4149,64 @@ var WorldOrbit = (() => {
|
|
|
3361
4149
|
seenFields: /* @__PURE__ */ new Set()
|
|
3362
4150
|
};
|
|
3363
4151
|
}
|
|
3364
|
-
function
|
|
4152
|
+
function startGroupSection(tokens, line, groups, groupIds) {
|
|
4153
|
+
if (tokens.length !== 2) {
|
|
4154
|
+
throw new WorldOrbitError("Invalid group declaration", line, tokens[0]?.column ?? 1);
|
|
4155
|
+
}
|
|
4156
|
+
const id = normalizeIdentifier2(tokens[1].value);
|
|
4157
|
+
if (!id) {
|
|
4158
|
+
throw new WorldOrbitError("Group id must not be empty", line, tokens[1].column);
|
|
4159
|
+
}
|
|
4160
|
+
if (groupIds.has(id)) {
|
|
4161
|
+
throw new WorldOrbitError(`Duplicate group id "${id}"`, line, tokens[1].column);
|
|
4162
|
+
}
|
|
4163
|
+
const group = {
|
|
4164
|
+
id,
|
|
4165
|
+
label: humanizeIdentifier3(id),
|
|
4166
|
+
summary: "",
|
|
4167
|
+
color: null,
|
|
4168
|
+
tags: [],
|
|
4169
|
+
hidden: false
|
|
4170
|
+
};
|
|
4171
|
+
groups.push(group);
|
|
4172
|
+
groupIds.add(id);
|
|
4173
|
+
return {
|
|
4174
|
+
kind: "group",
|
|
4175
|
+
group,
|
|
4176
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
4177
|
+
};
|
|
4178
|
+
}
|
|
4179
|
+
function startRelationSection(tokens, line, relations, relationIds) {
|
|
4180
|
+
if (tokens.length !== 2) {
|
|
4181
|
+
throw new WorldOrbitError("Invalid relation declaration", line, tokens[0]?.column ?? 1);
|
|
4182
|
+
}
|
|
4183
|
+
const id = normalizeIdentifier2(tokens[1].value);
|
|
4184
|
+
if (!id) {
|
|
4185
|
+
throw new WorldOrbitError("Relation id must not be empty", line, tokens[1].column);
|
|
4186
|
+
}
|
|
4187
|
+
if (relationIds.has(id)) {
|
|
4188
|
+
throw new WorldOrbitError(`Duplicate relation id "${id}"`, line, tokens[1].column);
|
|
4189
|
+
}
|
|
4190
|
+
const relation = {
|
|
4191
|
+
id,
|
|
4192
|
+
from: "",
|
|
4193
|
+
to: "",
|
|
4194
|
+
kind: "",
|
|
4195
|
+
label: null,
|
|
4196
|
+
summary: null,
|
|
4197
|
+
tags: [],
|
|
4198
|
+
color: null,
|
|
4199
|
+
hidden: false
|
|
4200
|
+
};
|
|
4201
|
+
relations.push(relation);
|
|
4202
|
+
relationIds.add(id);
|
|
4203
|
+
return {
|
|
4204
|
+
kind: "relation",
|
|
4205
|
+
relation,
|
|
4206
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
4207
|
+
};
|
|
4208
|
+
}
|
|
4209
|
+
function startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes) {
|
|
3365
4210
|
if (tokens.length < 3) {
|
|
3366
4211
|
throw new WorldOrbitError("Invalid atlas object declaration", line, tokens[0]?.column ?? 1);
|
|
3367
4212
|
}
|
|
@@ -3372,12 +4217,11 @@ var WorldOrbit = (() => {
|
|
|
3372
4217
|
throw new WorldOrbitError(`Unknown object type "${objectTypeToken.value}"`, line, objectTypeToken.column);
|
|
3373
4218
|
}
|
|
3374
4219
|
const objectNode = {
|
|
3375
|
-
type: "object",
|
|
3376
4220
|
objectType,
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
blockFields: [],
|
|
4221
|
+
id: idToken.value,
|
|
4222
|
+
fields: parseInlineObjectFields(tokens.slice(3), line, objectType, sourceSchemaVersion, diagnostics),
|
|
3380
4223
|
infoEntries: [],
|
|
4224
|
+
typedBlockEntries: {},
|
|
3381
4225
|
location: {
|
|
3382
4226
|
line,
|
|
3383
4227
|
column: objectTypeToken.column
|
|
@@ -3387,8 +4231,12 @@ var WorldOrbit = (() => {
|
|
|
3387
4231
|
return {
|
|
3388
4232
|
kind: "object",
|
|
3389
4233
|
objectNode,
|
|
3390
|
-
|
|
3391
|
-
|
|
4234
|
+
sourceSchemaVersion,
|
|
4235
|
+
diagnostics,
|
|
4236
|
+
activeBlock: null,
|
|
4237
|
+
blockIndent: null,
|
|
4238
|
+
seenInfoKeys: /* @__PURE__ */ new Set(),
|
|
4239
|
+
seenTypedBlockKeys: {}
|
|
3392
4240
|
};
|
|
3393
4241
|
}
|
|
3394
4242
|
function handleSectionLine(section, indent, tokens, line) {
|
|
@@ -3408,6 +4256,12 @@ var WorldOrbit = (() => {
|
|
|
3408
4256
|
case "annotation":
|
|
3409
4257
|
applyAnnotationField(section, tokens, line);
|
|
3410
4258
|
return;
|
|
4259
|
+
case "group":
|
|
4260
|
+
applyGroupField(section, tokens, line);
|
|
4261
|
+
return;
|
|
4262
|
+
case "relation":
|
|
4263
|
+
applyRelationField(section, tokens, line);
|
|
4264
|
+
return;
|
|
3411
4265
|
case "object":
|
|
3412
4266
|
applyObjectField(section, indent, tokens, line);
|
|
3413
4267
|
return;
|
|
@@ -3415,10 +4269,35 @@ var WorldOrbit = (() => {
|
|
|
3415
4269
|
}
|
|
3416
4270
|
function applySystemField(section, tokens, line) {
|
|
3417
4271
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
3418
|
-
|
|
3419
|
-
|
|
4272
|
+
const value = joinFieldValue(tokens, line);
|
|
4273
|
+
switch (key) {
|
|
4274
|
+
case "title":
|
|
4275
|
+
section.system.title = value;
|
|
4276
|
+
return;
|
|
4277
|
+
case "description":
|
|
4278
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
4279
|
+
line,
|
|
4280
|
+
column: tokens[0].column
|
|
4281
|
+
});
|
|
4282
|
+
section.system.description = value;
|
|
4283
|
+
return;
|
|
4284
|
+
case "epoch":
|
|
4285
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
4286
|
+
line,
|
|
4287
|
+
column: tokens[0].column
|
|
4288
|
+
});
|
|
4289
|
+
section.system.epoch = value;
|
|
4290
|
+
return;
|
|
4291
|
+
case "referenceplane":
|
|
4292
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, "referencePlane", {
|
|
4293
|
+
line,
|
|
4294
|
+
column: tokens[0].column
|
|
4295
|
+
});
|
|
4296
|
+
section.system.referencePlane = value;
|
|
4297
|
+
return;
|
|
4298
|
+
default:
|
|
4299
|
+
throw new WorldOrbitError(`Unknown system atlas field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3420
4300
|
}
|
|
3421
|
-
section.system.title = joinFieldValue(tokens, line);
|
|
3422
4301
|
}
|
|
3423
4302
|
function applyDefaultsField(section, tokens, line) {
|
|
3424
4303
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
@@ -3449,14 +4328,11 @@ var WorldOrbit = (() => {
|
|
|
3449
4328
|
section.metadataIndent = null;
|
|
3450
4329
|
}
|
|
3451
4330
|
if (section.inMetadata) {
|
|
3452
|
-
|
|
3453
|
-
|
|
4331
|
+
const entry = parseInfoLikeEntry(tokens, line, "Invalid atlas metadata entry");
|
|
4332
|
+
if (entry.key in section.system.atlasMetadata) {
|
|
4333
|
+
throw new WorldOrbitError(`Duplicate atlas metadata key "${entry.key}"`, line, tokens[0].column);
|
|
3454
4334
|
}
|
|
3455
|
-
|
|
3456
|
-
if (key in section.system.atlasMetadata) {
|
|
3457
|
-
throw new WorldOrbitError(`Duplicate atlas metadata key "${key}"`, line, tokens[0].column);
|
|
3458
|
-
}
|
|
3459
|
-
section.system.atlasMetadata[key] = joinFieldValue(tokens, line);
|
|
4335
|
+
section.system.atlasMetadata[entry.key] = entry.value;
|
|
3460
4336
|
return;
|
|
3461
4337
|
}
|
|
3462
4338
|
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "metadata") {
|
|
@@ -3552,27 +4428,108 @@ var WorldOrbit = (() => {
|
|
|
3552
4428
|
section.annotation.body = joinFieldValue(tokens, line);
|
|
3553
4429
|
return;
|
|
3554
4430
|
case "tags":
|
|
3555
|
-
section.annotation.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4431
|
+
section.annotation.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4432
|
+
return;
|
|
4433
|
+
default:
|
|
4434
|
+
throw new WorldOrbitError(`Unknown annotation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4435
|
+
}
|
|
4436
|
+
}
|
|
4437
|
+
function applyGroupField(section, tokens, line) {
|
|
4438
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
4439
|
+
switch (key) {
|
|
4440
|
+
case "label":
|
|
4441
|
+
section.group.label = joinFieldValue(tokens, line);
|
|
4442
|
+
return;
|
|
4443
|
+
case "summary":
|
|
4444
|
+
section.group.summary = joinFieldValue(tokens, line);
|
|
4445
|
+
return;
|
|
4446
|
+
case "color":
|
|
4447
|
+
section.group.color = joinFieldValue(tokens, line);
|
|
4448
|
+
return;
|
|
4449
|
+
case "tags":
|
|
4450
|
+
section.group.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4451
|
+
return;
|
|
4452
|
+
case "hidden":
|
|
4453
|
+
section.group.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4454
|
+
line,
|
|
4455
|
+
column: tokens[0].column
|
|
4456
|
+
});
|
|
4457
|
+
return;
|
|
4458
|
+
default:
|
|
4459
|
+
throw new WorldOrbitError(`Unknown group field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
function applyRelationField(section, tokens, line) {
|
|
4463
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
4464
|
+
switch (key) {
|
|
4465
|
+
case "from":
|
|
4466
|
+
section.relation.from = joinFieldValue(tokens, line);
|
|
4467
|
+
return;
|
|
4468
|
+
case "to":
|
|
4469
|
+
section.relation.to = joinFieldValue(tokens, line);
|
|
4470
|
+
return;
|
|
4471
|
+
case "kind":
|
|
4472
|
+
section.relation.kind = joinFieldValue(tokens, line);
|
|
4473
|
+
return;
|
|
4474
|
+
case "label":
|
|
4475
|
+
section.relation.label = joinFieldValue(tokens, line);
|
|
4476
|
+
return;
|
|
4477
|
+
case "summary":
|
|
4478
|
+
section.relation.summary = joinFieldValue(tokens, line);
|
|
4479
|
+
return;
|
|
4480
|
+
case "tags":
|
|
4481
|
+
section.relation.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4482
|
+
return;
|
|
4483
|
+
case "color":
|
|
4484
|
+
section.relation.color = joinFieldValue(tokens, line);
|
|
4485
|
+
return;
|
|
4486
|
+
case "hidden":
|
|
4487
|
+
section.relation.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4488
|
+
line,
|
|
4489
|
+
column: tokens[0].column
|
|
4490
|
+
});
|
|
3556
4491
|
return;
|
|
3557
4492
|
default:
|
|
3558
|
-
throw new WorldOrbitError(`Unknown
|
|
4493
|
+
throw new WorldOrbitError(`Unknown relation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3559
4494
|
}
|
|
3560
4495
|
}
|
|
3561
4496
|
function applyObjectField(section, indent, tokens, line) {
|
|
3562
|
-
if (
|
|
3563
|
-
section.
|
|
3564
|
-
section.
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
4497
|
+
if (section.activeBlock && indent <= (section.blockIndent ?? 0)) {
|
|
4498
|
+
section.activeBlock = null;
|
|
4499
|
+
section.blockIndent = null;
|
|
4500
|
+
}
|
|
4501
|
+
if (tokens.length === 1) {
|
|
4502
|
+
const blockName = tokens[0].value.toLowerCase();
|
|
4503
|
+
if (blockName === "info" || STRUCTURED_TYPED_BLOCKS.has(blockName)) {
|
|
4504
|
+
if (blockName !== "info") {
|
|
4505
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, blockName, { line, column: tokens[0].column });
|
|
4506
|
+
}
|
|
4507
|
+
section.activeBlock = blockName;
|
|
4508
|
+
section.blockIndent = indent;
|
|
4509
|
+
return;
|
|
4510
|
+
}
|
|
3570
4511
|
}
|
|
3571
|
-
if (section.
|
|
3572
|
-
|
|
4512
|
+
if (section.activeBlock) {
|
|
4513
|
+
const entry = parseInfoLikeEntry(tokens, line, `Invalid ${section.activeBlock} entry`);
|
|
4514
|
+
if (section.activeBlock === "info") {
|
|
4515
|
+
if (section.seenInfoKeys.has(entry.key)) {
|
|
4516
|
+
throw new WorldOrbitError(`Duplicate info key "${entry.key}"`, line, tokens[0].column);
|
|
4517
|
+
}
|
|
4518
|
+
section.seenInfoKeys.add(entry.key);
|
|
4519
|
+
section.objectNode.infoEntries.push(entry);
|
|
4520
|
+
return;
|
|
4521
|
+
}
|
|
4522
|
+
const typedBlock = section.activeBlock;
|
|
4523
|
+
const seenKeys = section.seenTypedBlockKeys[typedBlock] ?? (section.seenTypedBlockKeys[typedBlock] = /* @__PURE__ */ new Set());
|
|
4524
|
+
if (seenKeys.has(entry.key)) {
|
|
4525
|
+
throw new WorldOrbitError(`Duplicate ${typedBlock} key "${entry.key}"`, line, tokens[0].column);
|
|
4526
|
+
}
|
|
4527
|
+
seenKeys.add(entry.key);
|
|
4528
|
+
const entries = section.objectNode.typedBlockEntries[typedBlock] ?? (section.objectNode.typedBlockEntries[typedBlock] = []);
|
|
4529
|
+
entries.push(entry);
|
|
3573
4530
|
return;
|
|
3574
4531
|
}
|
|
3575
|
-
section.objectNode.
|
|
4532
|
+
section.objectNode.fields.push(parseObjectField(tokens, line, section.objectNode.objectType, section.sourceSchemaVersion, section.diagnostics));
|
|
3576
4533
|
}
|
|
3577
4534
|
function requireUniqueField(tokens, seenFields, line) {
|
|
3578
4535
|
if (tokens.length < 2) {
|
|
@@ -3592,50 +4549,40 @@ var WorldOrbit = (() => {
|
|
|
3592
4549
|
return tokens.slice(1).map((token) => token.value).join(" ").trim();
|
|
3593
4550
|
}
|
|
3594
4551
|
function parseObjectTypeTokens(tokens, line) {
|
|
3595
|
-
|
|
3596
|
-
throw new WorldOrbitError("Missing value for atlas field", line);
|
|
3597
|
-
}
|
|
3598
|
-
return tokens.map((token) => {
|
|
3599
|
-
const value = token.value;
|
|
3600
|
-
if (value !== "star" && value !== "planet" && value !== "moon" && value !== "belt" && value !== "asteroid" && value !== "comet" && value !== "ring" && value !== "structure" && value !== "phenomenon") {
|
|
3601
|
-
throw new WorldOrbitError(`Unknown viewpoint object type "${token.value}"`, line, token.column);
|
|
3602
|
-
}
|
|
3603
|
-
return value;
|
|
3604
|
-
});
|
|
3605
|
-
}
|
|
3606
|
-
function parseTokenList(tokens, line, field) {
|
|
3607
|
-
if (tokens.length === 0) {
|
|
3608
|
-
throw new WorldOrbitError(`Missing value for field "${field}"`, line);
|
|
3609
|
-
}
|
|
3610
|
-
return tokens.map((token) => token.value);
|
|
4552
|
+
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");
|
|
3611
4553
|
}
|
|
3612
4554
|
function parseLayerTokens(tokens, line) {
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
if (rawLayer === "orbits") {
|
|
3621
|
-
next["orbits-back"] = enabled;
|
|
3622
|
-
next["orbits-front"] = enabled;
|
|
4555
|
+
const layers = {};
|
|
4556
|
+
for (const token of parseTokenList(tokens, line, "layers")) {
|
|
4557
|
+
const enabled = !token.startsWith("-") && !token.startsWith("!");
|
|
4558
|
+
const raw = token.replace(/^[-!]+/, "").toLowerCase();
|
|
4559
|
+
if (raw === "orbits") {
|
|
4560
|
+
layers["orbits-back"] = enabled;
|
|
4561
|
+
layers["orbits-front"] = enabled;
|
|
3623
4562
|
continue;
|
|
3624
4563
|
}
|
|
3625
|
-
if (
|
|
3626
|
-
|
|
3627
|
-
continue;
|
|
4564
|
+
if (raw === "background" || raw === "guides" || raw === "orbits-back" || raw === "orbits-front" || raw === "relations" || raw === "objects" || raw === "labels" || raw === "metadata") {
|
|
4565
|
+
layers[raw] = enabled;
|
|
3628
4566
|
}
|
|
3629
|
-
throw new WorldOrbitError(`Unknown layer token "${token.value}"`, line, token.column);
|
|
3630
4567
|
}
|
|
3631
|
-
return
|
|
4568
|
+
return layers;
|
|
4569
|
+
}
|
|
4570
|
+
function parseTokenList(tokens, line, fieldName) {
|
|
4571
|
+
if (tokens.length === 0) {
|
|
4572
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, 1);
|
|
4573
|
+
}
|
|
4574
|
+
const values = tokens.map((token) => token.value).filter(Boolean);
|
|
4575
|
+
if (values.length === 0) {
|
|
4576
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, tokens[0]?.column ?? 1);
|
|
4577
|
+
}
|
|
4578
|
+
return values;
|
|
3632
4579
|
}
|
|
3633
4580
|
function parseProjectionValue(value, line, column) {
|
|
3634
4581
|
const normalized = value.toLowerCase();
|
|
3635
|
-
if (normalized
|
|
3636
|
-
|
|
4582
|
+
if (normalized !== "topdown" && normalized !== "isometric") {
|
|
4583
|
+
throw new WorldOrbitError(`Unknown projection "${value}"`, line, column);
|
|
3637
4584
|
}
|
|
3638
|
-
|
|
4585
|
+
return normalized;
|
|
3639
4586
|
}
|
|
3640
4587
|
function parsePresetValue(value, line, column) {
|
|
3641
4588
|
const normalized = value.toLowerCase();
|
|
@@ -3645,16 +4592,16 @@ var WorldOrbit = (() => {
|
|
|
3645
4592
|
throw new WorldOrbitError(`Unknown render preset "${value}"`, line, column);
|
|
3646
4593
|
}
|
|
3647
4594
|
function parsePositiveNumber2(value, line, column, field) {
|
|
3648
|
-
const parsed =
|
|
3649
|
-
if (
|
|
3650
|
-
throw new WorldOrbitError(`Field "${field}"
|
|
4595
|
+
const parsed = parseFiniteNumber2(value, line, column, field);
|
|
4596
|
+
if (parsed <= 0) {
|
|
4597
|
+
throw new WorldOrbitError(`Field "${field}" must be greater than zero`, line, column);
|
|
3651
4598
|
}
|
|
3652
4599
|
return parsed;
|
|
3653
4600
|
}
|
|
3654
4601
|
function parseFiniteNumber2(value, line, column, field) {
|
|
3655
4602
|
const parsed = Number(value);
|
|
3656
4603
|
if (!Number.isFinite(parsed)) {
|
|
3657
|
-
throw new WorldOrbitError(`
|
|
4604
|
+
throw new WorldOrbitError(`Invalid numeric value "${value}" for "${field}"`, line, column);
|
|
3658
4605
|
}
|
|
3659
4606
|
return parsed;
|
|
3660
4607
|
}
|
|
@@ -3666,28 +4613,43 @@ var WorldOrbit = (() => {
|
|
|
3666
4613
|
groupIds: []
|
|
3667
4614
|
};
|
|
3668
4615
|
}
|
|
3669
|
-
function
|
|
4616
|
+
function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
3670
4617
|
const fields = [];
|
|
3671
4618
|
let index = 0;
|
|
3672
4619
|
while (index < tokens.length) {
|
|
3673
4620
|
const keyToken = tokens[index];
|
|
3674
|
-
const
|
|
3675
|
-
if (!
|
|
4621
|
+
const spec = getDraftObjectFieldSpec(keyToken.value);
|
|
4622
|
+
if (!spec) {
|
|
3676
4623
|
throw new WorldOrbitError(`Unknown field "${keyToken.value}"`, line, keyToken.column);
|
|
3677
4624
|
}
|
|
4625
|
+
if (spec.version === "2.1") {
|
|
4626
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, keyToken.value, {
|
|
4627
|
+
line,
|
|
4628
|
+
column: keyToken.column
|
|
4629
|
+
});
|
|
4630
|
+
}
|
|
3678
4631
|
index++;
|
|
3679
4632
|
const valueTokens = [];
|
|
3680
|
-
if (
|
|
3681
|
-
while (index < tokens.length && !isKnownFieldKey(tokens[index].value)) {
|
|
3682
|
-
valueTokens.push(tokens[index]);
|
|
3683
|
-
index++;
|
|
3684
|
-
}
|
|
3685
|
-
} else {
|
|
4633
|
+
if (spec.inlineMode === "single") {
|
|
3686
4634
|
const nextToken = tokens[index];
|
|
3687
4635
|
if (nextToken) {
|
|
3688
4636
|
valueTokens.push(nextToken);
|
|
3689
4637
|
index++;
|
|
3690
4638
|
}
|
|
4639
|
+
} else if (spec.inlineMode === "pair") {
|
|
4640
|
+
for (let count = 0; count < 2; count++) {
|
|
4641
|
+
const nextToken = tokens[index];
|
|
4642
|
+
if (!nextToken) {
|
|
4643
|
+
break;
|
|
4644
|
+
}
|
|
4645
|
+
valueTokens.push(nextToken);
|
|
4646
|
+
index++;
|
|
4647
|
+
}
|
|
4648
|
+
} else {
|
|
4649
|
+
while (index < tokens.length && !DRAFT_OBJECT_FIELD_KEYS.has(tokens[index].value)) {
|
|
4650
|
+
valueTokens.push(tokens[index]);
|
|
4651
|
+
index++;
|
|
4652
|
+
}
|
|
3691
4653
|
}
|
|
3692
4654
|
if (valueTokens.length === 0) {
|
|
3693
4655
|
throw new WorldOrbitError(`Missing value for field "${keyToken.value}"`, line, keyToken.column);
|
|
@@ -3699,25 +4661,35 @@ var WorldOrbit = (() => {
|
|
|
3699
4661
|
location: { line, column: keyToken.column }
|
|
3700
4662
|
});
|
|
3701
4663
|
}
|
|
4664
|
+
validateDraftObjectFieldCompatibility(fields, objectType);
|
|
3702
4665
|
return fields;
|
|
3703
4666
|
}
|
|
3704
|
-
function
|
|
4667
|
+
function parseObjectField(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
3705
4668
|
if (tokens.length < 2) {
|
|
3706
4669
|
throw new WorldOrbitError("Invalid field line", line, tokens[0]?.column ?? 1);
|
|
3707
4670
|
}
|
|
3708
|
-
|
|
4671
|
+
const spec = getDraftObjectFieldSpec(tokens[0].value);
|
|
4672
|
+
if (!spec) {
|
|
3709
4673
|
throw new WorldOrbitError(`Unknown field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3710
4674
|
}
|
|
3711
|
-
|
|
4675
|
+
if (spec.version === "2.1") {
|
|
4676
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, tokens[0].value, {
|
|
4677
|
+
line,
|
|
4678
|
+
column: tokens[0].column
|
|
4679
|
+
});
|
|
4680
|
+
}
|
|
4681
|
+
const field = {
|
|
3712
4682
|
type: "field",
|
|
3713
4683
|
key: tokens[0].value,
|
|
3714
4684
|
values: tokens.slice(1).map((token) => token.value),
|
|
3715
4685
|
location: { line, column: tokens[0].column }
|
|
3716
4686
|
};
|
|
4687
|
+
validateDraftObjectFieldCompatibility([field], objectType);
|
|
4688
|
+
return field;
|
|
3717
4689
|
}
|
|
3718
|
-
function
|
|
4690
|
+
function parseInfoLikeEntry(tokens, line, errorMessage) {
|
|
3719
4691
|
if (tokens.length < 2) {
|
|
3720
|
-
throw new WorldOrbitError(
|
|
4692
|
+
throw new WorldOrbitError(errorMessage, line, tokens[0]?.column ?? 1);
|
|
3721
4693
|
}
|
|
3722
4694
|
return {
|
|
3723
4695
|
type: "info-entry",
|
|
@@ -3726,23 +4698,356 @@ var WorldOrbit = (() => {
|
|
|
3726
4698
|
location: { line, column: tokens[0].column }
|
|
3727
4699
|
};
|
|
3728
4700
|
}
|
|
3729
|
-
function
|
|
3730
|
-
|
|
4701
|
+
function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
|
|
4702
|
+
const fieldMap = collectDraftFields(node.fields);
|
|
4703
|
+
const placement = extractDraftPlacement(node.objectType, fieldMap);
|
|
4704
|
+
const properties = normalizeDraftProperties(node.objectType, fieldMap);
|
|
4705
|
+
const groups = parseOptionalTokenList(fieldMap.get("groups")?.[0]);
|
|
4706
|
+
const epoch = parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]);
|
|
4707
|
+
const referencePlane = parseOptionalJoinedValue(fieldMap.get("referencePlane")?.[0]);
|
|
4708
|
+
const tidalLock = fieldMap.has("tidalLock") ? parseAtlasBoolean(singleFieldValue2(fieldMap.get("tidalLock")[0]), "tidalLock", fieldMap.get("tidalLock")[0].location) : void 0;
|
|
4709
|
+
const resonance = fieldMap.has("resonance") ? parseResonanceField(fieldMap.get("resonance")[0]) : void 0;
|
|
4710
|
+
const renderHints = extractRenderHints(fieldMap);
|
|
4711
|
+
const deriveRules = fieldMap.get("derive")?.map((field) => parseDeriveField(field));
|
|
4712
|
+
const validationRules = fieldMap.get("validate")?.map((field) => ({
|
|
4713
|
+
rule: singleFieldValue2(field)
|
|
4714
|
+
}));
|
|
4715
|
+
const lockedFields = fieldMap.has("locked") ? [...new Set(fieldMap.get("locked").flatMap((field) => field.values))] : void 0;
|
|
4716
|
+
const tolerances = fieldMap.get("tolerance")?.map((field) => parseToleranceField(field));
|
|
4717
|
+
const typedBlocks = normalizeTypedBlocks(node.typedBlockEntries);
|
|
4718
|
+
const info2 = normalizeInfoEntries(node.infoEntries, "info");
|
|
4719
|
+
const object = {
|
|
4720
|
+
type: node.objectType,
|
|
4721
|
+
id: node.id,
|
|
4722
|
+
properties,
|
|
4723
|
+
placement,
|
|
4724
|
+
info: info2
|
|
4725
|
+
};
|
|
4726
|
+
if (groups.length > 0)
|
|
4727
|
+
object.groups = groups;
|
|
4728
|
+
if (epoch)
|
|
4729
|
+
object.epoch = epoch;
|
|
4730
|
+
if (referencePlane)
|
|
4731
|
+
object.referencePlane = referencePlane;
|
|
4732
|
+
if (tidalLock !== void 0)
|
|
4733
|
+
object.tidalLock = tidalLock;
|
|
4734
|
+
if (resonance)
|
|
4735
|
+
object.resonance = resonance;
|
|
4736
|
+
if (renderHints)
|
|
4737
|
+
object.renderHints = renderHints;
|
|
4738
|
+
if (deriveRules?.length)
|
|
4739
|
+
object.deriveRules = deriveRules;
|
|
4740
|
+
if (validationRules?.length)
|
|
4741
|
+
object.validationRules = validationRules;
|
|
4742
|
+
if (lockedFields?.length)
|
|
4743
|
+
object.lockedFields = lockedFields;
|
|
4744
|
+
if (tolerances?.length)
|
|
4745
|
+
object.tolerances = tolerances;
|
|
4746
|
+
if (typedBlocks && Object.keys(typedBlocks).length > 0)
|
|
4747
|
+
object.typedBlocks = typedBlocks;
|
|
4748
|
+
if (sourceSchemaVersion !== "2.1") {
|
|
4749
|
+
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) {
|
|
4750
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, node.id, node.location);
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4753
|
+
return object;
|
|
4754
|
+
}
|
|
4755
|
+
function collectDraftFields(fields) {
|
|
4756
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4757
|
+
for (const field of fields) {
|
|
4758
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
4759
|
+
if (!spec) {
|
|
4760
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4761
|
+
}
|
|
4762
|
+
if (!spec.allowRepeat && grouped.has(field.key)) {
|
|
4763
|
+
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
4764
|
+
}
|
|
4765
|
+
const existing = grouped.get(field.key) ?? [];
|
|
4766
|
+
existing.push(field);
|
|
4767
|
+
grouped.set(field.key, existing);
|
|
4768
|
+
}
|
|
4769
|
+
return grouped;
|
|
3731
4770
|
}
|
|
3732
|
-
function
|
|
3733
|
-
|
|
4771
|
+
function extractDraftPlacement(objectType, fieldMap) {
|
|
4772
|
+
const orbitField = fieldMap.get("orbit")?.[0];
|
|
4773
|
+
const atField = fieldMap.get("at")?.[0];
|
|
4774
|
+
const surfaceField = fieldMap.get("surface")?.[0];
|
|
4775
|
+
const freeField = fieldMap.get("free")?.[0];
|
|
4776
|
+
const count = [orbitField, atField, surfaceField, freeField].filter(Boolean).length;
|
|
4777
|
+
if (count > 1) {
|
|
4778
|
+
const conflictingField = orbitField ?? atField ?? surfaceField ?? freeField;
|
|
4779
|
+
throw WorldOrbitError.fromLocation("Object has multiple placement modes", conflictingField?.location);
|
|
4780
|
+
}
|
|
4781
|
+
if (orbitField) {
|
|
4782
|
+
return {
|
|
4783
|
+
mode: "orbit",
|
|
4784
|
+
target: singleFieldValue2(orbitField),
|
|
4785
|
+
distance: parseOptionalUnitField(fieldMap.get("distance")?.[0], "distance"),
|
|
4786
|
+
semiMajor: parseOptionalUnitField(fieldMap.get("semiMajor")?.[0], "semiMajor"),
|
|
4787
|
+
eccentricity: parseOptionalNumberField(fieldMap.get("eccentricity")?.[0], "eccentricity"),
|
|
4788
|
+
period: parseOptionalUnitField(fieldMap.get("period")?.[0], "period"),
|
|
4789
|
+
angle: parseOptionalUnitField(fieldMap.get("angle")?.[0], "angle"),
|
|
4790
|
+
inclination: parseOptionalUnitField(fieldMap.get("inclination")?.[0], "inclination"),
|
|
4791
|
+
phase: parseOptionalUnitField(fieldMap.get("phase")?.[0], "phase")
|
|
4792
|
+
};
|
|
4793
|
+
}
|
|
4794
|
+
if (atField) {
|
|
4795
|
+
const target = singleFieldValue2(atField);
|
|
4796
|
+
return {
|
|
4797
|
+
mode: "at",
|
|
4798
|
+
target,
|
|
4799
|
+
reference: parseAtlasAtReference(target, atField.location)
|
|
4800
|
+
};
|
|
4801
|
+
}
|
|
4802
|
+
if (surfaceField) {
|
|
4803
|
+
return {
|
|
4804
|
+
mode: "surface",
|
|
4805
|
+
target: singleFieldValue2(surfaceField)
|
|
4806
|
+
};
|
|
4807
|
+
}
|
|
4808
|
+
if (freeField) {
|
|
4809
|
+
const raw = singleFieldValue2(freeField);
|
|
4810
|
+
const distance = tryParseAtlasUnitValue(raw);
|
|
4811
|
+
return {
|
|
4812
|
+
mode: "free",
|
|
4813
|
+
distance: distance ?? void 0,
|
|
4814
|
+
descriptor: distance ? void 0 : raw
|
|
4815
|
+
};
|
|
4816
|
+
}
|
|
4817
|
+
return null;
|
|
4818
|
+
}
|
|
4819
|
+
function normalizeDraftProperties(objectType, fieldMap) {
|
|
4820
|
+
const properties = {};
|
|
4821
|
+
for (const [key, fields] of fieldMap.entries()) {
|
|
4822
|
+
const field = fields[0];
|
|
4823
|
+
const spec = getDraftObjectFieldSpec(key);
|
|
4824
|
+
if (!field || !spec?.legacySchema || spec.legacySchema.placement) {
|
|
4825
|
+
continue;
|
|
4826
|
+
}
|
|
4827
|
+
ensureAtlasFieldSupported(key, objectType, field.location);
|
|
4828
|
+
properties[key] = normalizeLegacyScalarValue(key, field.values, field.location);
|
|
4829
|
+
}
|
|
4830
|
+
return properties;
|
|
4831
|
+
}
|
|
4832
|
+
function normalizeInfoEntries(entries, label) {
|
|
4833
|
+
const normalized = {};
|
|
4834
|
+
for (const entry of entries) {
|
|
4835
|
+
if (entry.key in normalized) {
|
|
4836
|
+
throw WorldOrbitError.fromLocation(`Duplicate ${label} key "${entry.key}"`, entry.location);
|
|
4837
|
+
}
|
|
4838
|
+
normalized[entry.key] = entry.value;
|
|
4839
|
+
}
|
|
4840
|
+
return normalized;
|
|
4841
|
+
}
|
|
4842
|
+
function normalizeTypedBlocks(typedBlockEntries) {
|
|
4843
|
+
const typedBlocks = {};
|
|
4844
|
+
for (const blockName of Object.keys(typedBlockEntries)) {
|
|
4845
|
+
const entries = typedBlockEntries[blockName];
|
|
4846
|
+
if (entries?.length) {
|
|
4847
|
+
typedBlocks[blockName] = normalizeInfoEntries(entries, blockName);
|
|
4848
|
+
}
|
|
4849
|
+
}
|
|
4850
|
+
return typedBlocks;
|
|
4851
|
+
}
|
|
4852
|
+
function extractRenderHints(fieldMap) {
|
|
4853
|
+
const renderHints = {};
|
|
4854
|
+
const renderLabelField = fieldMap.get("renderLabel")?.[0];
|
|
4855
|
+
const renderOrbitField = fieldMap.get("renderOrbit")?.[0];
|
|
4856
|
+
const renderPriorityField = fieldMap.get("renderPriority")?.[0];
|
|
4857
|
+
if (renderLabelField) {
|
|
4858
|
+
renderHints.renderLabel = parseAtlasBoolean(singleFieldValue2(renderLabelField), "renderLabel", renderLabelField.location);
|
|
4859
|
+
}
|
|
4860
|
+
if (renderOrbitField) {
|
|
4861
|
+
renderHints.renderOrbit = parseAtlasBoolean(singleFieldValue2(renderOrbitField), "renderOrbit", renderOrbitField.location);
|
|
4862
|
+
}
|
|
4863
|
+
if (renderPriorityField) {
|
|
4864
|
+
renderHints.renderPriority = parseAtlasNumber(singleFieldValue2(renderPriorityField), "renderPriority", renderPriorityField.location);
|
|
4865
|
+
}
|
|
4866
|
+
return Object.keys(renderHints).length > 0 ? renderHints : void 0;
|
|
4867
|
+
}
|
|
4868
|
+
function parseResonanceField(field) {
|
|
4869
|
+
if (field.values.length !== 2) {
|
|
4870
|
+
throw WorldOrbitError.fromLocation('Field "resonance" expects "<targetObjectId> <ratio>"', field.location);
|
|
4871
|
+
}
|
|
4872
|
+
const ratio = field.values[1];
|
|
4873
|
+
if (!/^\d+:\d+$/.test(ratio)) {
|
|
4874
|
+
throw WorldOrbitError.fromLocation(`Invalid resonance ratio "${ratio}"`, field.location);
|
|
4875
|
+
}
|
|
4876
|
+
return {
|
|
4877
|
+
targetObjectId: field.values[0],
|
|
4878
|
+
ratio
|
|
4879
|
+
};
|
|
4880
|
+
}
|
|
4881
|
+
function parseDeriveField(field) {
|
|
4882
|
+
if (field.values.length !== 2) {
|
|
4883
|
+
throw WorldOrbitError.fromLocation('Field "derive" expects "<field> <strategy>"', field.location);
|
|
4884
|
+
}
|
|
4885
|
+
return {
|
|
4886
|
+
field: field.values[0],
|
|
4887
|
+
strategy: field.values[1]
|
|
4888
|
+
};
|
|
4889
|
+
}
|
|
4890
|
+
function parseToleranceField(field) {
|
|
4891
|
+
if (field.values.length !== 2) {
|
|
4892
|
+
throw WorldOrbitError.fromLocation('Field "tolerance" expects "<field> <value>"', field.location);
|
|
4893
|
+
}
|
|
4894
|
+
const rawValue = field.values[1];
|
|
4895
|
+
const unitValue = tryParseAtlasUnitValue(rawValue);
|
|
4896
|
+
const numericValue2 = Number(rawValue);
|
|
4897
|
+
return {
|
|
4898
|
+
field: field.values[0],
|
|
4899
|
+
value: unitValue ?? (Number.isFinite(numericValue2) ? numericValue2 : rawValue)
|
|
4900
|
+
};
|
|
4901
|
+
}
|
|
4902
|
+
function parseOptionalTokenList(field) {
|
|
4903
|
+
return field ? [...new Set(field.values)] : [];
|
|
4904
|
+
}
|
|
4905
|
+
function parseOptionalJoinedValue(field) {
|
|
4906
|
+
if (!field) {
|
|
4907
|
+
return null;
|
|
4908
|
+
}
|
|
4909
|
+
return field.values.join(" ").trim() || null;
|
|
4910
|
+
}
|
|
4911
|
+
function parseOptionalUnitField(field, key) {
|
|
4912
|
+
return field ? parseAtlasUnitValue(singleFieldValue2(field), field.location, key) : void 0;
|
|
4913
|
+
}
|
|
4914
|
+
function parseOptionalNumberField(field, key) {
|
|
4915
|
+
return field ? parseAtlasNumber(singleFieldValue2(field), key, field.location) : void 0;
|
|
4916
|
+
}
|
|
4917
|
+
function singleFieldValue2(field) {
|
|
4918
|
+
return singleAtlasValue(field.values, field.key, field.location);
|
|
4919
|
+
}
|
|
4920
|
+
function getDraftObjectFieldSpec(key) {
|
|
4921
|
+
return DRAFT_OBJECT_FIELD_SPECS.get(key);
|
|
4922
|
+
}
|
|
4923
|
+
function validateDraftObjectFieldCompatibility(fields, objectType) {
|
|
4924
|
+
for (const field of fields) {
|
|
4925
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
4926
|
+
if (!spec) {
|
|
4927
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4928
|
+
}
|
|
4929
|
+
if (spec.legacySchema) {
|
|
4930
|
+
ensureAtlasFieldSupported(field.key, objectType, field.location);
|
|
4931
|
+
continue;
|
|
4932
|
+
}
|
|
4933
|
+
if ((field.key === "renderLabel" || field.key === "renderOrbit" || field.key === "tidalLock") && field.values.length !== 1) {
|
|
4934
|
+
throw WorldOrbitError.fromLocation(`Field "${field.key}" expects exactly one value`, field.location);
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4938
|
+
function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
4939
|
+
if (sourceSchemaVersion === "2.1") {
|
|
4940
|
+
return;
|
|
4941
|
+
}
|
|
4942
|
+
diagnostics.push({
|
|
4943
|
+
code: "parse.schema21.featureCompatibility",
|
|
4944
|
+
severity: "warning",
|
|
4945
|
+
source: "parse",
|
|
4946
|
+
message: `Feature "${featureName}" requires schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
4947
|
+
line: location.line,
|
|
4948
|
+
column: location.column
|
|
4949
|
+
});
|
|
4950
|
+
}
|
|
4951
|
+
function preprocessAtlasSource(source) {
|
|
4952
|
+
const chars = [...source];
|
|
4953
|
+
const comments = [];
|
|
4954
|
+
let inString = false;
|
|
4955
|
+
let inBlockComment = false;
|
|
4956
|
+
let blockCommentStart = null;
|
|
4957
|
+
let line = 1;
|
|
4958
|
+
let column = 1;
|
|
4959
|
+
for (let index = 0; index < chars.length; index++) {
|
|
4960
|
+
const ch = chars[index];
|
|
4961
|
+
const next = chars[index + 1];
|
|
4962
|
+
if (inBlockComment) {
|
|
4963
|
+
if (ch === "*" && next === "/") {
|
|
4964
|
+
chars[index] = " ";
|
|
4965
|
+
chars[index + 1] = " ";
|
|
4966
|
+
inBlockComment = false;
|
|
4967
|
+
blockCommentStart = null;
|
|
4968
|
+
index++;
|
|
4969
|
+
column += 2;
|
|
4970
|
+
continue;
|
|
4971
|
+
}
|
|
4972
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
4973
|
+
chars[index] = " ";
|
|
4974
|
+
}
|
|
4975
|
+
if (ch === "\n") {
|
|
4976
|
+
line++;
|
|
4977
|
+
column = 1;
|
|
4978
|
+
} else {
|
|
4979
|
+
column++;
|
|
4980
|
+
}
|
|
4981
|
+
continue;
|
|
4982
|
+
}
|
|
4983
|
+
if (!inString && ch === "/" && next === "*") {
|
|
4984
|
+
comments.push({ kind: "block", line, column });
|
|
4985
|
+
chars[index] = " ";
|
|
4986
|
+
chars[index + 1] = " ";
|
|
4987
|
+
inBlockComment = true;
|
|
4988
|
+
blockCommentStart = { line, column };
|
|
4989
|
+
index++;
|
|
4990
|
+
column += 2;
|
|
4991
|
+
continue;
|
|
4992
|
+
}
|
|
4993
|
+
if (!inString && ch === "#" && !isHexColorLiteral(chars, index)) {
|
|
4994
|
+
comments.push({ kind: "line", line, column });
|
|
4995
|
+
chars[index] = " ";
|
|
4996
|
+
let inner = index + 1;
|
|
4997
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
4998
|
+
chars[inner] = " ";
|
|
4999
|
+
inner++;
|
|
5000
|
+
}
|
|
5001
|
+
column += inner - index;
|
|
5002
|
+
index = inner - 1;
|
|
5003
|
+
continue;
|
|
5004
|
+
}
|
|
5005
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
5006
|
+
inString = !inString;
|
|
5007
|
+
}
|
|
5008
|
+
if (ch === "\n") {
|
|
5009
|
+
line++;
|
|
5010
|
+
column = 1;
|
|
5011
|
+
} else {
|
|
5012
|
+
column++;
|
|
5013
|
+
}
|
|
5014
|
+
}
|
|
5015
|
+
if (inBlockComment) {
|
|
5016
|
+
throw WorldOrbitError.fromLocation("Unclosed block comment", blockCommentStart ?? void 0);
|
|
5017
|
+
}
|
|
5018
|
+
return {
|
|
5019
|
+
source: chars.join(""),
|
|
5020
|
+
comments
|
|
5021
|
+
};
|
|
5022
|
+
}
|
|
5023
|
+
function isHexColorLiteral(chars, start) {
|
|
5024
|
+
let index = start + 1;
|
|
5025
|
+
let length = 0;
|
|
5026
|
+
while (index < chars.length && /[0-9a-f]/i.test(chars[index] ?? "")) {
|
|
5027
|
+
index++;
|
|
5028
|
+
length++;
|
|
5029
|
+
}
|
|
5030
|
+
if (![3, 4, 6, 8].includes(length)) {
|
|
5031
|
+
return false;
|
|
5032
|
+
}
|
|
5033
|
+
const next = chars[index];
|
|
5034
|
+
return next === void 0 || next === " " || next === " " || next === "\r" || next === "\n";
|
|
3734
5035
|
}
|
|
3735
5036
|
|
|
3736
5037
|
// packages/core/dist/atlas-edit.js
|
|
3737
|
-
function createEmptyAtlasDocument(systemId = "WorldOrbit") {
|
|
5038
|
+
function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "2.0") {
|
|
3738
5039
|
return {
|
|
3739
5040
|
format: "worldorbit",
|
|
3740
|
-
version
|
|
5041
|
+
version,
|
|
5042
|
+
schemaVersion: version,
|
|
3741
5043
|
sourceVersion: "1.0",
|
|
3742
5044
|
system: {
|
|
3743
5045
|
type: "system",
|
|
3744
5046
|
id: systemId,
|
|
3745
5047
|
title: systemId,
|
|
5048
|
+
description: null,
|
|
5049
|
+
epoch: null,
|
|
5050
|
+
referencePlane: null,
|
|
3746
5051
|
defaults: {
|
|
3747
5052
|
view: "topdown",
|
|
3748
5053
|
scale: null,
|
|
@@ -3754,6 +5059,8 @@ var WorldOrbit = (() => {
|
|
|
3754
5059
|
viewpoints: [],
|
|
3755
5060
|
annotations: []
|
|
3756
5061
|
},
|
|
5062
|
+
groups: [],
|
|
5063
|
+
relations: [],
|
|
3757
5064
|
objects: [],
|
|
3758
5065
|
diagnostics: []
|
|
3759
5066
|
};
|
|
@@ -3767,14 +5074,20 @@ var WorldOrbit = (() => {
|
|
|
3767
5074
|
for (const key of Object.keys(document2.system.atlasMetadata).sort()) {
|
|
3768
5075
|
paths.push({ kind: "metadata", key });
|
|
3769
5076
|
}
|
|
3770
|
-
for (const viewpoint of [...document2.system.viewpoints].sort(
|
|
5077
|
+
for (const viewpoint of [...document2.system.viewpoints].sort(compareIdLike2)) {
|
|
3771
5078
|
paths.push({ kind: "viewpoint", id: viewpoint.id });
|
|
3772
5079
|
}
|
|
3773
|
-
for (const annotation of [...document2.system.annotations].sort(
|
|
5080
|
+
for (const annotation of [...document2.system.annotations].sort(compareIdLike2)) {
|
|
3774
5081
|
paths.push({ kind: "annotation", id: annotation.id });
|
|
3775
5082
|
}
|
|
3776
5083
|
}
|
|
3777
|
-
for (const
|
|
5084
|
+
for (const group of [...document2.groups].sort(compareIdLike2)) {
|
|
5085
|
+
paths.push({ kind: "group", id: group.id });
|
|
5086
|
+
}
|
|
5087
|
+
for (const relation of [...document2.relations].sort(compareIdLike2)) {
|
|
5088
|
+
paths.push({ kind: "relation", id: relation.id });
|
|
5089
|
+
}
|
|
5090
|
+
for (const object of [...document2.objects].sort(compareIdLike2)) {
|
|
3778
5091
|
paths.push({ kind: "object", id: object.id });
|
|
3779
5092
|
}
|
|
3780
5093
|
return paths;
|
|
@@ -3787,12 +5100,16 @@ var WorldOrbit = (() => {
|
|
|
3787
5100
|
return document2.system?.defaults ?? null;
|
|
3788
5101
|
case "metadata":
|
|
3789
5102
|
return path.key ? document2.system?.atlasMetadata[path.key] ?? null : null;
|
|
5103
|
+
case "group":
|
|
5104
|
+
return path.id ? findGroup(document2, path.id) : null;
|
|
3790
5105
|
case "object":
|
|
3791
5106
|
return path.id ? findObject(document2, path.id) : null;
|
|
3792
5107
|
case "viewpoint":
|
|
3793
5108
|
return path.id ? findViewpoint(document2.system, path.id) : null;
|
|
3794
5109
|
case "annotation":
|
|
3795
5110
|
return path.id ? findAnnotation(document2.system, path.id) : null;
|
|
5111
|
+
case "relation":
|
|
5112
|
+
return path.id ? findRelation(document2, path.id) : null;
|
|
3796
5113
|
}
|
|
3797
5114
|
}
|
|
3798
5115
|
function upsertAtlasDocumentNode(document2, path, value) {
|
|
@@ -3818,6 +5135,12 @@ var WorldOrbit = (() => {
|
|
|
3818
5135
|
system.atlasMetadata[path.key] = String(value);
|
|
3819
5136
|
}
|
|
3820
5137
|
return next;
|
|
5138
|
+
case "group":
|
|
5139
|
+
if (!path.id) {
|
|
5140
|
+
throw new Error('Group updates require an "id" value.');
|
|
5141
|
+
}
|
|
5142
|
+
upsertById(next.groups, value);
|
|
5143
|
+
return next;
|
|
3821
5144
|
case "object":
|
|
3822
5145
|
if (!path.id) {
|
|
3823
5146
|
throw new Error('Object updates require an "id" value.');
|
|
@@ -3836,6 +5159,12 @@ var WorldOrbit = (() => {
|
|
|
3836
5159
|
}
|
|
3837
5160
|
upsertById(system.annotations, value);
|
|
3838
5161
|
return next;
|
|
5162
|
+
case "relation":
|
|
5163
|
+
if (!path.id) {
|
|
5164
|
+
throw new Error('Relation updates require an "id" value.');
|
|
5165
|
+
}
|
|
5166
|
+
upsertById(next.relations, value);
|
|
5167
|
+
return next;
|
|
3839
5168
|
}
|
|
3840
5169
|
}
|
|
3841
5170
|
function updateAtlasDocumentNode(document2, path, updater) {
|
|
@@ -3855,6 +5184,11 @@ var WorldOrbit = (() => {
|
|
|
3855
5184
|
next.objects = next.objects.filter((object) => object.id !== path.id);
|
|
3856
5185
|
}
|
|
3857
5186
|
return next;
|
|
5187
|
+
case "group":
|
|
5188
|
+
if (path.id) {
|
|
5189
|
+
next.groups = next.groups.filter((group) => group.id !== path.id);
|
|
5190
|
+
}
|
|
5191
|
+
return next;
|
|
3858
5192
|
case "viewpoint":
|
|
3859
5193
|
if (path.id) {
|
|
3860
5194
|
system.viewpoints = system.viewpoints.filter((viewpoint) => viewpoint.id !== path.id);
|
|
@@ -3865,6 +5199,11 @@ var WorldOrbit = (() => {
|
|
|
3865
5199
|
system.annotations = system.annotations.filter((annotation) => annotation.id !== path.id);
|
|
3866
5200
|
}
|
|
3867
5201
|
return next;
|
|
5202
|
+
case "relation":
|
|
5203
|
+
if (path.id) {
|
|
5204
|
+
next.relations = next.relations.filter((relation) => relation.id !== path.id);
|
|
5205
|
+
}
|
|
5206
|
+
return next;
|
|
3868
5207
|
default:
|
|
3869
5208
|
return next;
|
|
3870
5209
|
}
|
|
@@ -3882,6 +5221,15 @@ var WorldOrbit = (() => {
|
|
|
3882
5221
|
id: diagnostic.objectId
|
|
3883
5222
|
};
|
|
3884
5223
|
}
|
|
5224
|
+
if (diagnostic.field?.startsWith("group.")) {
|
|
5225
|
+
const parts = diagnostic.field.split(".");
|
|
5226
|
+
if (parts[1] && findGroup(document2, parts[1])) {
|
|
5227
|
+
return {
|
|
5228
|
+
kind: "group",
|
|
5229
|
+
id: parts[1]
|
|
5230
|
+
};
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
3885
5233
|
if (diagnostic.field?.startsWith("viewpoint.")) {
|
|
3886
5234
|
const parts = diagnostic.field.split(".");
|
|
3887
5235
|
if (parts[1] && findViewpoint(document2.system, parts[1])) {
|
|
@@ -3900,6 +5248,15 @@ var WorldOrbit = (() => {
|
|
|
3900
5248
|
};
|
|
3901
5249
|
}
|
|
3902
5250
|
}
|
|
5251
|
+
if (diagnostic.field?.startsWith("relation.")) {
|
|
5252
|
+
const parts = diagnostic.field.split(".");
|
|
5253
|
+
if (parts[1] && findRelation(document2, parts[1])) {
|
|
5254
|
+
return {
|
|
5255
|
+
kind: "relation",
|
|
5256
|
+
id: parts[1]
|
|
5257
|
+
};
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
3903
5260
|
if (diagnostic.field && diagnostic.field in ensureSystem(document2).atlasMetadata) {
|
|
3904
5261
|
return {
|
|
3905
5262
|
kind: "metadata",
|
|
@@ -3909,9 +5266,11 @@ var WorldOrbit = (() => {
|
|
|
3909
5266
|
return null;
|
|
3910
5267
|
}
|
|
3911
5268
|
function validateAtlasDocumentWithDiagnostics(document2) {
|
|
3912
|
-
const
|
|
3913
|
-
|
|
3914
|
-
|
|
5269
|
+
const diagnostics = [
|
|
5270
|
+
...document2.diagnostics,
|
|
5271
|
+
...collectAtlasDiagnostics(document2, document2.version)
|
|
5272
|
+
];
|
|
5273
|
+
return resolveAtlasDiagnostics(document2, diagnostics);
|
|
3915
5274
|
}
|
|
3916
5275
|
function ensureSystem(document2) {
|
|
3917
5276
|
if (document2.system) {
|
|
@@ -3923,6 +5282,12 @@ var WorldOrbit = (() => {
|
|
|
3923
5282
|
function findObject(document2, objectId) {
|
|
3924
5283
|
return document2.objects.find((object) => object.id === objectId) ?? null;
|
|
3925
5284
|
}
|
|
5285
|
+
function findGroup(document2, groupId) {
|
|
5286
|
+
return document2.groups.find((group) => group.id === groupId) ?? null;
|
|
5287
|
+
}
|
|
5288
|
+
function findRelation(document2, relationId) {
|
|
5289
|
+
return document2.relations.find((relation) => relation.id === relationId) ?? null;
|
|
5290
|
+
}
|
|
3926
5291
|
function findViewpoint(system, viewpointId) {
|
|
3927
5292
|
return system?.viewpoints.find((viewpoint) => viewpoint.id === viewpointId) ?? null;
|
|
3928
5293
|
}
|
|
@@ -3933,20 +5298,21 @@ var WorldOrbit = (() => {
|
|
|
3933
5298
|
const index = items.findIndex((item) => item.id === value.id);
|
|
3934
5299
|
if (index === -1) {
|
|
3935
5300
|
items.push(value);
|
|
3936
|
-
items.sort(
|
|
5301
|
+
items.sort(compareIdLike2);
|
|
3937
5302
|
return;
|
|
3938
5303
|
}
|
|
3939
5304
|
items[index] = value;
|
|
3940
5305
|
}
|
|
3941
|
-
function
|
|
5306
|
+
function compareIdLike2(left, right) {
|
|
3942
5307
|
return left.id.localeCompare(right.id);
|
|
3943
5308
|
}
|
|
3944
5309
|
|
|
3945
5310
|
// packages/core/dist/load.js
|
|
3946
|
-
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0)?$/i;
|
|
5311
|
+
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;
|
|
5312
|
+
var ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
|
|
3947
5313
|
var LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
|
|
3948
5314
|
function detectWorldOrbitSchemaVersion(source) {
|
|
3949
|
-
for (const line of source.split(/\r?\n/)) {
|
|
5315
|
+
for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
|
|
3950
5316
|
const trimmed = line.trim();
|
|
3951
5317
|
if (!trimmed) {
|
|
3952
5318
|
continue;
|
|
@@ -3954,6 +5320,9 @@ var WorldOrbit = (() => {
|
|
|
3954
5320
|
if (LEGACY_DRAFT_SCHEMA_PATTERN.test(trimmed)) {
|
|
3955
5321
|
return "2.0-draft";
|
|
3956
5322
|
}
|
|
5323
|
+
if (ATLAS_SCHEMA_21_PATTERN.test(trimmed)) {
|
|
5324
|
+
return "2.1";
|
|
5325
|
+
}
|
|
3957
5326
|
if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
|
|
3958
5327
|
return "2.0";
|
|
3959
5328
|
}
|
|
@@ -3961,6 +5330,49 @@ var WorldOrbit = (() => {
|
|
|
3961
5330
|
}
|
|
3962
5331
|
return "1.0";
|
|
3963
5332
|
}
|
|
5333
|
+
function stripCommentsForSchemaDetection(source) {
|
|
5334
|
+
const chars = [...source];
|
|
5335
|
+
let inString = false;
|
|
5336
|
+
let inBlockComment = false;
|
|
5337
|
+
for (let index = 0; index < chars.length; index++) {
|
|
5338
|
+
const ch = chars[index];
|
|
5339
|
+
const next = chars[index + 1];
|
|
5340
|
+
if (inBlockComment) {
|
|
5341
|
+
if (ch === "*" && next === "/") {
|
|
5342
|
+
chars[index] = " ";
|
|
5343
|
+
chars[index + 1] = " ";
|
|
5344
|
+
inBlockComment = false;
|
|
5345
|
+
index++;
|
|
5346
|
+
continue;
|
|
5347
|
+
}
|
|
5348
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
5349
|
+
chars[index] = " ";
|
|
5350
|
+
}
|
|
5351
|
+
continue;
|
|
5352
|
+
}
|
|
5353
|
+
if (!inString && ch === "/" && next === "*") {
|
|
5354
|
+
chars[index] = " ";
|
|
5355
|
+
chars[index + 1] = " ";
|
|
5356
|
+
inBlockComment = true;
|
|
5357
|
+
index++;
|
|
5358
|
+
continue;
|
|
5359
|
+
}
|
|
5360
|
+
if (!inString && ch === "#") {
|
|
5361
|
+
chars[index] = " ";
|
|
5362
|
+
let inner = index + 1;
|
|
5363
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
5364
|
+
chars[inner] = " ";
|
|
5365
|
+
inner++;
|
|
5366
|
+
}
|
|
5367
|
+
index = inner - 1;
|
|
5368
|
+
continue;
|
|
5369
|
+
}
|
|
5370
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
5371
|
+
inString = !inString;
|
|
5372
|
+
}
|
|
5373
|
+
}
|
|
5374
|
+
return chars.join("");
|
|
5375
|
+
}
|
|
3964
5376
|
function loadWorldOrbitSource(source) {
|
|
3965
5377
|
const result = loadWorldOrbitSourceWithDiagnostics(source);
|
|
3966
5378
|
if (!result.ok || !result.value) {
|
|
@@ -3971,36 +5383,36 @@ var WorldOrbit = (() => {
|
|
|
3971
5383
|
}
|
|
3972
5384
|
function loadWorldOrbitSourceWithDiagnostics(source) {
|
|
3973
5385
|
const schemaVersion = detectWorldOrbitSchemaVersion(source);
|
|
3974
|
-
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft") {
|
|
5386
|
+
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1") {
|
|
3975
5387
|
return loadAtlasSourceWithDiagnostics(source, schemaVersion);
|
|
3976
5388
|
}
|
|
3977
5389
|
let ast;
|
|
3978
5390
|
try {
|
|
3979
5391
|
ast = parseWorldOrbit(source);
|
|
3980
|
-
} catch (
|
|
5392
|
+
} catch (error2) {
|
|
3981
5393
|
return {
|
|
3982
5394
|
ok: false,
|
|
3983
5395
|
value: null,
|
|
3984
|
-
diagnostics: [diagnosticFromError(
|
|
5396
|
+
diagnostics: [diagnosticFromError(error2, "parse")]
|
|
3985
5397
|
};
|
|
3986
5398
|
}
|
|
3987
5399
|
let document2;
|
|
3988
5400
|
try {
|
|
3989
5401
|
document2 = normalizeDocument(ast);
|
|
3990
|
-
} catch (
|
|
5402
|
+
} catch (error2) {
|
|
3991
5403
|
return {
|
|
3992
5404
|
ok: false,
|
|
3993
5405
|
value: null,
|
|
3994
|
-
diagnostics: [diagnosticFromError(
|
|
5406
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
3995
5407
|
};
|
|
3996
5408
|
}
|
|
3997
5409
|
try {
|
|
3998
5410
|
validateDocument(document2);
|
|
3999
|
-
} catch (
|
|
5411
|
+
} catch (error2) {
|
|
4000
5412
|
return {
|
|
4001
5413
|
ok: false,
|
|
4002
5414
|
value: null,
|
|
4003
|
-
diagnostics: [diagnosticFromError(
|
|
5415
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
4004
5416
|
};
|
|
4005
5417
|
}
|
|
4006
5418
|
return {
|
|
@@ -4020,30 +5432,29 @@ var WorldOrbit = (() => {
|
|
|
4020
5432
|
let atlasDocument;
|
|
4021
5433
|
try {
|
|
4022
5434
|
atlasDocument = parseWorldOrbitAtlas(source);
|
|
4023
|
-
} catch (
|
|
5435
|
+
} catch (error2) {
|
|
4024
5436
|
return {
|
|
4025
5437
|
ok: false,
|
|
4026
5438
|
value: null,
|
|
4027
|
-
diagnostics: [diagnosticFromError(
|
|
5439
|
+
diagnostics: [diagnosticFromError(error2, "parse", "load.atlas.failed")]
|
|
4028
5440
|
};
|
|
4029
5441
|
}
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
document2 = materializeAtlasDocument(atlasDocument);
|
|
4033
|
-
} catch (error) {
|
|
5442
|
+
const atlasDiagnostics = [...atlasDocument.diagnostics];
|
|
5443
|
+
if (atlasDiagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
4034
5444
|
return {
|
|
4035
5445
|
ok: false,
|
|
4036
5446
|
value: null,
|
|
4037
|
-
diagnostics:
|
|
5447
|
+
diagnostics: atlasDiagnostics
|
|
4038
5448
|
};
|
|
4039
5449
|
}
|
|
5450
|
+
let document2;
|
|
4040
5451
|
try {
|
|
4041
|
-
|
|
4042
|
-
} catch (
|
|
5452
|
+
document2 = materializeAtlasDocument(atlasDocument);
|
|
5453
|
+
} catch (error2) {
|
|
4043
5454
|
return {
|
|
4044
5455
|
ok: false,
|
|
4045
5456
|
value: null,
|
|
4046
|
-
diagnostics: [diagnosticFromError(
|
|
5457
|
+
diagnostics: [diagnosticFromError(error2, "normalize", "load.atlas.materialize.failed")]
|
|
4047
5458
|
};
|
|
4048
5459
|
}
|
|
4049
5460
|
const loaded = {
|
|
@@ -4052,12 +5463,12 @@ var WorldOrbit = (() => {
|
|
|
4052
5463
|
document: document2,
|
|
4053
5464
|
atlasDocument,
|
|
4054
5465
|
draftDocument: atlasDocument,
|
|
4055
|
-
diagnostics:
|
|
5466
|
+
diagnostics: atlasDiagnostics
|
|
4056
5467
|
};
|
|
4057
5468
|
return {
|
|
4058
5469
|
ok: true,
|
|
4059
5470
|
value: loaded,
|
|
4060
|
-
diagnostics:
|
|
5471
|
+
diagnostics: atlasDiagnostics
|
|
4061
5472
|
};
|
|
4062
5473
|
}
|
|
4063
5474
|
|
|
@@ -4128,6 +5539,7 @@ var WorldOrbit = (() => {
|
|
|
4128
5539
|
var DEFAULT_LAYERS = {
|
|
4129
5540
|
background: true,
|
|
4130
5541
|
guides: true,
|
|
5542
|
+
relations: true,
|
|
4131
5543
|
orbits: true,
|
|
4132
5544
|
objects: true,
|
|
4133
5545
|
labels: true,
|
|
@@ -4142,6 +5554,7 @@ var WorldOrbit = (() => {
|
|
|
4142
5554
|
backgroundGlow: "rgba(240, 180, 100, 0.18)",
|
|
4143
5555
|
panel: "rgba(7, 17, 27, 0.9)",
|
|
4144
5556
|
panelLine: "rgba(168, 207, 242, 0.18)",
|
|
5557
|
+
relation: "rgba(240, 180, 100, 0.42)",
|
|
4145
5558
|
orbit: "rgba(163, 209, 255, 0.24)",
|
|
4146
5559
|
orbitBand: "rgba(255, 190, 120, 0.28)",
|
|
4147
5560
|
guide: "rgba(255, 255, 255, 0.04)",
|
|
@@ -4164,6 +5577,7 @@ var WorldOrbit = (() => {
|
|
|
4164
5577
|
backgroundGlow: "rgba(120, 255, 215, 0.16)",
|
|
4165
5578
|
panel: "rgba(7, 20, 30, 0.9)",
|
|
4166
5579
|
panelLine: "rgba(120, 255, 215, 0.16)",
|
|
5580
|
+
relation: "rgba(156, 231, 255, 0.42)",
|
|
4167
5581
|
orbit: "rgba(120, 255, 215, 0.2)",
|
|
4168
5582
|
orbitBand: "rgba(137, 185, 255, 0.24)",
|
|
4169
5583
|
guide: "rgba(255, 255, 255, 0.035)",
|
|
@@ -4186,6 +5600,7 @@ var WorldOrbit = (() => {
|
|
|
4186
5600
|
backgroundGlow: "rgba(255, 127, 95, 0.18)",
|
|
4187
5601
|
panel: "rgba(24, 9, 13, 0.9)",
|
|
4188
5602
|
panelLine: "rgba(255, 166, 149, 0.16)",
|
|
5603
|
+
relation: "rgba(255, 178, 125, 0.42)",
|
|
4189
5604
|
orbit: "rgba(255, 188, 164, 0.22)",
|
|
4190
5605
|
orbitBand: "rgba(255, 214, 139, 0.24)",
|
|
4191
5606
|
guide: "rgba(255, 255, 255, 0.03)",
|
|
@@ -4338,6 +5753,7 @@ var WorldOrbit = (() => {
|
|
|
4338
5753
|
return {
|
|
4339
5754
|
background: viewpoint.layers.background,
|
|
4340
5755
|
guides: viewpoint.layers.guides,
|
|
5756
|
+
relations: viewpoint.layers.relations,
|
|
4341
5757
|
orbits: viewpoint.layers["orbits-front"] === void 0 && viewpoint.layers["orbits-back"] === void 0 ? void 0 : viewpoint.layers["orbits-front"] !== false || viewpoint.layers["orbits-back"] !== false,
|
|
4342
5758
|
objects: viewpoint.layers.objects,
|
|
4343
5759
|
labels: viewpoint.layers.labels,
|
|
@@ -4375,7 +5791,11 @@ var WorldOrbit = (() => {
|
|
|
4375
5791
|
return false;
|
|
4376
5792
|
}
|
|
4377
5793
|
if (filter.groupIds?.length && (!object.groupId || !filter.groupIds.includes(object.groupId))) {
|
|
4378
|
-
|
|
5794
|
+
const hasSemanticMatch = object.semanticGroupIds.length > 0 && filter.groupIds.some((groupId) => object.semanticGroupIds.includes(groupId));
|
|
5795
|
+
const hasLegacyMatch = Boolean(object.groupId && filter.groupIds.includes(object.groupId));
|
|
5796
|
+
if (!hasSemanticMatch && !hasLegacyMatch) {
|
|
5797
|
+
return false;
|
|
5798
|
+
}
|
|
4379
5799
|
}
|
|
4380
5800
|
if (filter.tags?.length) {
|
|
4381
5801
|
const objectTags = Array.isArray(object.object.properties.tags) ? object.object.properties.tags.filter((entry) => typeof entry === "string") : [];
|
|
@@ -4619,6 +6039,7 @@ var WorldOrbit = (() => {
|
|
|
4619
6039
|
const imageDefinitions = buildImageDefinitions(visibleObjects);
|
|
4620
6040
|
const orbitMarkup = layers.orbits ? renderOrbitLayer(scene, visibleObjectIds, layers.structures) : { back: "", front: "" };
|
|
4621
6041
|
const leaderMarkup = layers.guides ? scene.leaders.filter((leader) => !leader.hidden).filter((leader) => visibleObjectIds.has(leader.objectId)).filter((leader) => layers.structures || !isStructureLike(leader.object)).map((leader) => `<line class="wo-leader wo-leader-${leader.mode}" x1="${leader.x1}" y1="${leader.y1}" x2="${leader.x2}" y2="${leader.y2}" data-render-id="${escapeXml(leader.renderId)}" data-group-id="${escapeAttribute(leader.groupId ?? "")}" />`).join("") : "";
|
|
6042
|
+
const relationMarkup = layers.relations ? scene.relations.filter((relation) => !relation.hidden).filter((relation) => visibleObjectIds.has(relation.fromObjectId) && visibleObjectIds.has(relation.toObjectId)).map((relation) => `<line class="wo-relation" x1="${relation.x1}" y1="${relation.y1}" x2="${relation.x2}" y2="${relation.y2}" data-render-id="${escapeXml(relation.renderId)}" data-relation-id="${escapeAttribute(relation.relationId)}" />`).join("") : "";
|
|
4622
6043
|
const objectMarkup = layers.objects ? visibleObjects.map((object) => renderSceneObject(object, options.selectedObjectId ?? null, theme)).join("") : "";
|
|
4623
6044
|
const labelMarkup = layers.labels ? visibleLabels.map((label) => renderSceneLabel(scene, label, options.selectedObjectId ?? null)).join("") : "";
|
|
4624
6045
|
const metadataMarkup = layers.metadata ? `<text class="wo-title" x="56" y="64">${escapeXml(scene.title)}</text>
|
|
@@ -4653,6 +6074,7 @@ var WorldOrbit = (() => {
|
|
|
4653
6074
|
.wo-orbit-back { opacity: 0.38; stroke-dasharray: 8 6; }
|
|
4654
6075
|
.wo-orbit-front { opacity: 0.9; }
|
|
4655
6076
|
.wo-orbit-band { stroke: ${theme.orbitBand}; stroke-linecap: round; }
|
|
6077
|
+
.wo-relation { stroke: ${theme.relation}; stroke-width: 2; stroke-dasharray: 10 6; }
|
|
4656
6078
|
.wo-leader { stroke: ${theme.leader}; stroke-width: 1.5; stroke-dasharray: 6 5; }
|
|
4657
6079
|
.wo-label { fill: ${theme.ink}; font-family: ${theme.fontFamily}; font-weight: 600; letter-spacing: 0.02em; }
|
|
4658
6080
|
.wo-label-secondary { fill: ${theme.muted}; font-family: ${theme.fontFamily}; font-weight: 500; }
|
|
@@ -4686,6 +6108,7 @@ var WorldOrbit = (() => {
|
|
|
4686
6108
|
<g data-worldorbit-world-content="true">
|
|
4687
6109
|
${layers.orbits ? `<g data-layer-id="orbits-back">${orbitMarkup.back}</g>` : ""}
|
|
4688
6110
|
${layers.guides ? `<g data-layer-id="guides">${leaderMarkup}</g>` : ""}
|
|
6111
|
+
${layers.relations ? `<g data-layer-id="relations">${relationMarkup}</g>` : ""}
|
|
4689
6112
|
${layers.objects ? `<g data-layer-id="objects">${objectMarkup}</g>` : ""}
|
|
4690
6113
|
${layers.orbits ? `<g data-layer-id="orbits-front">${orbitMarkup.front}</g>` : ""}
|
|
4691
6114
|
${layers.labels ? `<g data-layer-id="labels">${labelMarkup}</g>` : ""}
|
|
@@ -5257,6 +6680,41 @@ var WorldOrbit = (() => {
|
|
|
5257
6680
|
});
|
|
5258
6681
|
}
|
|
5259
6682
|
const placement = details.object.placement;
|
|
6683
|
+
if (details.object.groups?.length) {
|
|
6684
|
+
fields.set("groups", {
|
|
6685
|
+
key: "groups",
|
|
6686
|
+
label: "Groups",
|
|
6687
|
+
value: details.object.groups.join(", ")
|
|
6688
|
+
});
|
|
6689
|
+
}
|
|
6690
|
+
if (details.object.epoch) {
|
|
6691
|
+
fields.set("epoch", {
|
|
6692
|
+
key: "epoch",
|
|
6693
|
+
label: "Epoch",
|
|
6694
|
+
value: details.object.epoch
|
|
6695
|
+
});
|
|
6696
|
+
}
|
|
6697
|
+
if (details.object.referencePlane) {
|
|
6698
|
+
fields.set("referencePlane", {
|
|
6699
|
+
key: "referencePlane",
|
|
6700
|
+
label: "Reference Plane",
|
|
6701
|
+
value: details.object.referencePlane
|
|
6702
|
+
});
|
|
6703
|
+
}
|
|
6704
|
+
if (details.object.tidalLock !== void 0) {
|
|
6705
|
+
fields.set("tidalLock", {
|
|
6706
|
+
key: "tidalLock",
|
|
6707
|
+
label: "Tidal Lock",
|
|
6708
|
+
value: details.object.tidalLock ? "true" : "false"
|
|
6709
|
+
});
|
|
6710
|
+
}
|
|
6711
|
+
if (details.object.resonance) {
|
|
6712
|
+
fields.set("resonance", {
|
|
6713
|
+
key: "resonance",
|
|
6714
|
+
label: "Resonance",
|
|
6715
|
+
value: `${details.object.resonance.targetObjectId} ${details.object.resonance.ratio}`
|
|
6716
|
+
});
|
|
6717
|
+
}
|
|
5260
6718
|
if (placement?.mode === "at") {
|
|
5261
6719
|
fields.set("placement", {
|
|
5262
6720
|
key: "placement",
|
|
@@ -5931,8 +7389,10 @@ var WorldOrbit = (() => {
|
|
|
5931
7389
|
renderObject,
|
|
5932
7390
|
label: scene.labels.find((label) => label.objectId === renderObject.objectId && !label.hidden) ?? null,
|
|
5933
7391
|
group: scene.groups.find((group) => group.renderId === renderObject.groupId) ?? null,
|
|
7392
|
+
semanticGroups: scene.semanticGroups.filter((group) => renderObject.semanticGroupIds.includes(group.id)),
|
|
5934
7393
|
orbit: scene.orbitVisuals.find((orbit) => orbit.objectId === renderObject.objectId && !orbit.hidden) ?? null,
|
|
5935
7394
|
relatedOrbits: scene.orbitVisuals.filter((orbit) => !orbit.hidden && (orbit.objectId === renderObject.objectId || renderObject.ancestorIds.includes(orbit.objectId) || renderObject.childIds.includes(orbit.objectId))),
|
|
7395
|
+
relations: scene.relations.filter((relation) => !relation.hidden && (relation.fromObjectId === renderObject.objectId || relation.toObjectId === renderObject.objectId)),
|
|
5936
7396
|
parent: getObjectById(renderObject.parentId),
|
|
5937
7397
|
children: renderObject.childIds.map((childId) => getObjectById(childId)).filter(Boolean),
|
|
5938
7398
|
ancestors: renderObject.ancestorIds.map((ancestorId) => getObjectById(ancestorId)).filter(Boolean),
|
|
@@ -6557,6 +8017,7 @@ var WorldOrbit = (() => {
|
|
|
6557
8017
|
const controls = {
|
|
6558
8018
|
search: options.controls?.search ?? true,
|
|
6559
8019
|
typeFilter: options.controls?.typeFilter ?? true,
|
|
8020
|
+
groupFilter: options.controls?.groupFilter ?? true,
|
|
6560
8021
|
viewpointSelect: options.controls?.viewpointSelect ?? true,
|
|
6561
8022
|
inspector: options.controls?.inspector ?? true,
|
|
6562
8023
|
bookmarks: options.controls?.bookmarks ?? true
|
|
@@ -6566,6 +8027,7 @@ var WorldOrbit = (() => {
|
|
|
6566
8027
|
const toolbar = container.querySelector("[data-atlas-toolbar]");
|
|
6567
8028
|
const searchInput = container.querySelector("[data-atlas-search]");
|
|
6568
8029
|
const typeFilterSelect = container.querySelector("[data-atlas-type-filter]");
|
|
8030
|
+
const groupFilterSelect = container.querySelector("[data-atlas-group-filter]");
|
|
6569
8031
|
const viewpointSelect = container.querySelector("[data-atlas-viewpoint]");
|
|
6570
8032
|
const bookmarkButton = container.querySelector("[data-atlas-bookmark]");
|
|
6571
8033
|
const bookmarkList = container.querySelector("[data-atlas-bookmarks]");
|
|
@@ -6578,6 +8040,7 @@ var WorldOrbit = (() => {
|
|
|
6578
8040
|
const baseFilter = normalizeViewerFilter(options.initialFilter ?? null);
|
|
6579
8041
|
let searchQuery = options.initialQuery?.trim() ?? baseFilter?.query ?? "";
|
|
6580
8042
|
let objectTypeFilter = options.initialObjectType ?? (baseFilter?.objectTypes?.length === 1 ? baseFilter.objectTypes[0] : null);
|
|
8043
|
+
let groupFilter = baseFilter?.groupIds?.[0] ?? null;
|
|
6581
8044
|
let bookmarks = [];
|
|
6582
8045
|
let viewer;
|
|
6583
8046
|
viewer = createInteractiveViewer(stage, {
|
|
@@ -6625,6 +8088,7 @@ var WorldOrbit = (() => {
|
|
|
6625
8088
|
});
|
|
6626
8089
|
applyCurrentFilter();
|
|
6627
8090
|
populateViewpoints();
|
|
8091
|
+
populateGroups();
|
|
6628
8092
|
syncControlsFromFilter(viewer.getFilter());
|
|
6629
8093
|
renderBookmarks();
|
|
6630
8094
|
updateSearchResults();
|
|
@@ -6637,6 +8101,10 @@ var WorldOrbit = (() => {
|
|
|
6637
8101
|
objectTypeFilter = typeFilterSelect.value || null;
|
|
6638
8102
|
applyCurrentFilter();
|
|
6639
8103
|
});
|
|
8104
|
+
groupFilterSelect?.addEventListener("change", () => {
|
|
8105
|
+
groupFilter = groupFilterSelect.value || null;
|
|
8106
|
+
applyCurrentFilter();
|
|
8107
|
+
});
|
|
6640
8108
|
viewpointSelect?.addEventListener("change", () => {
|
|
6641
8109
|
const activeViewer = requireViewer();
|
|
6642
8110
|
if (!viewpointSelect.value) {
|
|
@@ -6778,6 +8246,7 @@ var WorldOrbit = (() => {
|
|
|
6778
8246
|
return api;
|
|
6779
8247
|
function refreshAfterInputChange() {
|
|
6780
8248
|
populateViewpoints();
|
|
8249
|
+
populateGroups();
|
|
6781
8250
|
applyCurrentFilter();
|
|
6782
8251
|
renderBookmarks();
|
|
6783
8252
|
updateSearchResults();
|
|
@@ -6794,19 +8263,23 @@ var WorldOrbit = (() => {
|
|
|
6794
8263
|
query: searchQuery || void 0,
|
|
6795
8264
|
objectTypes: objectTypeFilter ? [objectTypeFilter] : void 0,
|
|
6796
8265
|
tags: baseFilter?.tags,
|
|
6797
|
-
groupIds: baseFilter?.groupIds,
|
|
8266
|
+
groupIds: groupFilter ? [groupFilter] : baseFilter?.groupIds,
|
|
6798
8267
|
includeAncestors: baseFilter?.includeAncestors ?? true
|
|
6799
8268
|
});
|
|
6800
8269
|
}
|
|
6801
8270
|
function syncControlsFromFilter(filter) {
|
|
6802
8271
|
searchQuery = filter?.query?.trim() ?? "";
|
|
6803
8272
|
objectTypeFilter = filter?.objectTypes?.length === 1 ? filter.objectTypes[0] : null;
|
|
8273
|
+
groupFilter = filter?.groupIds?.length === 1 ? filter.groupIds[0] : null;
|
|
6804
8274
|
if (searchInput && document.activeElement !== searchInput) {
|
|
6805
8275
|
searchInput.value = searchQuery;
|
|
6806
8276
|
}
|
|
6807
8277
|
if (typeFilterSelect) {
|
|
6808
8278
|
typeFilterSelect.value = objectTypeFilter ?? "";
|
|
6809
8279
|
}
|
|
8280
|
+
if (groupFilterSelect) {
|
|
8281
|
+
groupFilterSelect.value = groupFilter ?? "";
|
|
8282
|
+
}
|
|
6810
8283
|
}
|
|
6811
8284
|
function populateViewpoints() {
|
|
6812
8285
|
if (!viewpointSelect) {
|
|
@@ -6820,6 +8293,17 @@ var WorldOrbit = (() => {
|
|
|
6820
8293
|
].join("");
|
|
6821
8294
|
viewpointSelect.value = active;
|
|
6822
8295
|
}
|
|
8296
|
+
function populateGroups() {
|
|
8297
|
+
if (!groupFilterSelect) {
|
|
8298
|
+
return;
|
|
8299
|
+
}
|
|
8300
|
+
const activeViewer = requireViewer();
|
|
8301
|
+
groupFilterSelect.innerHTML = [
|
|
8302
|
+
`<option value="">All groups</option>`,
|
|
8303
|
+
...activeViewer.getScene().semanticGroups.map((group) => `<option value="${escapeHtml2(group.id)}">${escapeHtml2(group.label)}</option>`)
|
|
8304
|
+
].join("");
|
|
8305
|
+
groupFilterSelect.value = groupFilter ?? "";
|
|
8306
|
+
}
|
|
6823
8307
|
function syncViewpointControl() {
|
|
6824
8308
|
if (!viewpointSelect) {
|
|
6825
8309
|
return;
|
|
@@ -6853,6 +8337,8 @@ var WorldOrbit = (() => {
|
|
|
6853
8337
|
projection: activeViewer.getScene().projection,
|
|
6854
8338
|
renderPreset: activeViewer.getScene().renderPreset,
|
|
6855
8339
|
groupCount: activeViewer.getScene().groups.length,
|
|
8340
|
+
semanticGroupCount: activeViewer.getScene().semanticGroups.length,
|
|
8341
|
+
relationCount: activeViewer.getScene().relations.length,
|
|
6856
8342
|
viewpointCount: activeViewer.getScene().viewpoints.length
|
|
6857
8343
|
}
|
|
6858
8344
|
};
|
|
@@ -6885,6 +8371,12 @@ var WorldOrbit = (() => {
|
|
|
6885
8371
|
<option value="phenomenon">Phenomenon</option>
|
|
6886
8372
|
</select>
|
|
6887
8373
|
</label>` : "",
|
|
8374
|
+
controls.groupFilter ? `<label class="wo-atlas-field">
|
|
8375
|
+
<span>Group</span>
|
|
8376
|
+
<select data-atlas-group-filter>
|
|
8377
|
+
<option value="">All groups</option>
|
|
8378
|
+
</select>
|
|
8379
|
+
</label>` : "",
|
|
6888
8380
|
controls.viewpointSelect ? `<label class="wo-atlas-field">
|
|
6889
8381
|
<span>Viewpoint</span>
|
|
6890
8382
|
<select data-atlas-viewpoint>
|