worldorbit 2.5.13 → 2.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -11
- package/dist/browser/core/dist/index.js +1811 -386
- package/dist/browser/editor/dist/index.js +10534 -0
- package/dist/browser/markdown/dist/index.js +1477 -221
- package/dist/browser/viewer/dist/index.js +1569 -230
- package/dist/unpkg/core/dist/index.js +1814 -389
- package/dist/unpkg/editor/dist/index.js +10559 -0
- package/dist/unpkg/markdown/dist/index.js +1480 -224
- package/dist/unpkg/viewer/dist/index.js +1572 -233
- package/dist/unpkg/worldorbit-core.min.js +12 -5
- package/dist/unpkg/worldorbit-editor.min.js +812 -0
- package/dist/unpkg/worldorbit-markdown.min.js +32 -23
- package/dist/unpkg/worldorbit-viewer.min.js +55 -41
- package/dist/unpkg/worldorbit.js +1713 -231
- package/dist/unpkg/worldorbit.min.js +58 -44
- package/package.json +3 -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 +66 -13
- package/packages/core/dist/types.d.ts +97 -3
- package/packages/editor/dist/editor.js +44 -0
- 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
|
@@ -19,8 +19,8 @@ var WorldOrbit = (() => {
|
|
|
19
19
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
20
|
|
|
21
21
|
// packages/core/dist/index.js
|
|
22
|
-
var
|
|
23
|
-
__export(
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
24
|
WORLDORBIT_FIELD_KEYS: () => WORLDORBIT_FIELD_KEYS,
|
|
25
25
|
WORLDORBIT_FIELD_SCHEMAS: () => WORLDORBIT_FIELD_SCHEMAS,
|
|
26
26
|
WORLDORBIT_OBJECT_TYPES: () => WORLDORBIT_OBJECT_TYPES,
|
|
@@ -373,13 +373,13 @@ var WorldOrbit = (() => {
|
|
|
373
373
|
function unitFamilyAllowsUnit(family, unit) {
|
|
374
374
|
switch (family) {
|
|
375
375
|
case "distance":
|
|
376
|
-
return unit === null || ["au", "km", "re", "sol"].includes(unit);
|
|
376
|
+
return unit === null || ["au", "km", "m", "ly", "pc", "kpc", "re", "sol"].includes(unit);
|
|
377
377
|
case "radius":
|
|
378
|
-
return unit === null || ["km", "re", "sol"].includes(unit);
|
|
378
|
+
return unit === null || ["km", "m", "re", "rj", "sol"].includes(unit);
|
|
379
379
|
case "mass":
|
|
380
|
-
return unit === null || ["me", "sol"].includes(unit);
|
|
380
|
+
return unit === null || ["me", "mj", "sol"].includes(unit);
|
|
381
381
|
case "duration":
|
|
382
|
-
return unit === null || ["h", "d", "y"].includes(unit);
|
|
382
|
+
return unit === null || ["s", "min", "h", "d", "y", "ky", "my", "gy"].includes(unit);
|
|
383
383
|
case "angle":
|
|
384
384
|
return unit === null || unit === "deg";
|
|
385
385
|
case "generic":
|
|
@@ -586,7 +586,7 @@ var WorldOrbit = (() => {
|
|
|
586
586
|
}
|
|
587
587
|
|
|
588
588
|
// packages/core/dist/normalize.js
|
|
589
|
-
var UNIT_PATTERN = /^(-?\d+(?:\.\d+)?)(au|km|re|sol|
|
|
589
|
+
var UNIT_PATTERN = /^(-?\d+(?:\.\d+)?)(kpc|min|mj|rj|ky|my|gy|au|km|me|re|pc|ly|deg|sol|K|m|s|h|d|y)?$/;
|
|
590
590
|
var BOOLEAN_VALUES = /* @__PURE__ */ new Map([
|
|
591
591
|
["true", true],
|
|
592
592
|
["false", false],
|
|
@@ -611,7 +611,10 @@ var WorldOrbit = (() => {
|
|
|
611
611
|
return {
|
|
612
612
|
format: "worldorbit",
|
|
613
613
|
version: "1.0",
|
|
614
|
+
schemaVersion: "1.0",
|
|
614
615
|
system,
|
|
616
|
+
groups: [],
|
|
617
|
+
relations: [],
|
|
615
618
|
objects
|
|
616
619
|
};
|
|
617
620
|
}
|
|
@@ -621,13 +624,17 @@ var WorldOrbit = (() => {
|
|
|
621
624
|
const fieldMap = collectFields(mergedFields);
|
|
622
625
|
const placement = extractPlacement(node.objectType, fieldMap);
|
|
623
626
|
const properties = normalizeProperties(fieldMap);
|
|
624
|
-
const
|
|
627
|
+
const info2 = normalizeInfo(node.infoEntries);
|
|
625
628
|
if (node.objectType === "system") {
|
|
626
629
|
return {
|
|
627
630
|
type: "system",
|
|
628
631
|
id: node.name,
|
|
632
|
+
title: typeof properties.title === "string" ? properties.title : null,
|
|
633
|
+
description: null,
|
|
634
|
+
epoch: null,
|
|
635
|
+
referencePlane: null,
|
|
629
636
|
properties,
|
|
630
|
-
info
|
|
637
|
+
info: info2
|
|
631
638
|
};
|
|
632
639
|
}
|
|
633
640
|
return {
|
|
@@ -635,7 +642,7 @@ var WorldOrbit = (() => {
|
|
|
635
642
|
id: node.name,
|
|
636
643
|
properties,
|
|
637
644
|
placement,
|
|
638
|
-
info
|
|
645
|
+
info: info2
|
|
639
646
|
};
|
|
640
647
|
}
|
|
641
648
|
function validateFieldCompatibility(objectType, fields) {
|
|
@@ -765,14 +772,14 @@ var WorldOrbit = (() => {
|
|
|
765
772
|
}
|
|
766
773
|
}
|
|
767
774
|
function normalizeInfo(entries) {
|
|
768
|
-
const
|
|
775
|
+
const info2 = {};
|
|
769
776
|
for (const entry of entries) {
|
|
770
|
-
if (entry.key in
|
|
777
|
+
if (entry.key in info2) {
|
|
771
778
|
throw WorldOrbitError.fromLocation(`Duplicate info key "${entry.key}"`, entry.location);
|
|
772
779
|
}
|
|
773
|
-
|
|
780
|
+
info2[entry.key] = entry.value;
|
|
774
781
|
}
|
|
775
|
-
return
|
|
782
|
+
return info2;
|
|
776
783
|
}
|
|
777
784
|
function parseAtReference(target, location) {
|
|
778
785
|
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
@@ -946,38 +953,38 @@ var WorldOrbit = (() => {
|
|
|
946
953
|
function createDiagnostic(diagnostic) {
|
|
947
954
|
return { ...diagnostic };
|
|
948
955
|
}
|
|
949
|
-
function diagnosticFromError(
|
|
950
|
-
if (
|
|
956
|
+
function diagnosticFromError(error2, source, code = `${source}.failed`) {
|
|
957
|
+
if (error2 instanceof WorldOrbitError) {
|
|
951
958
|
return {
|
|
952
959
|
code,
|
|
953
960
|
severity: "error",
|
|
954
961
|
source,
|
|
955
|
-
message:
|
|
956
|
-
line:
|
|
957
|
-
column:
|
|
962
|
+
message: error2.message,
|
|
963
|
+
line: error2.line,
|
|
964
|
+
column: error2.column
|
|
958
965
|
};
|
|
959
966
|
}
|
|
960
|
-
if (
|
|
967
|
+
if (error2 instanceof Error) {
|
|
961
968
|
return {
|
|
962
969
|
code,
|
|
963
970
|
severity: "error",
|
|
964
971
|
source,
|
|
965
|
-
message:
|
|
972
|
+
message: error2.message
|
|
966
973
|
};
|
|
967
974
|
}
|
|
968
975
|
return {
|
|
969
976
|
code,
|
|
970
977
|
severity: "error",
|
|
971
978
|
source,
|
|
972
|
-
message: String(
|
|
979
|
+
message: String(error2)
|
|
973
980
|
};
|
|
974
981
|
}
|
|
975
982
|
function parseWithDiagnostics(source) {
|
|
976
983
|
let ast;
|
|
977
984
|
try {
|
|
978
985
|
ast = parseWorldOrbit(source);
|
|
979
|
-
} catch (
|
|
980
|
-
const diagnostic = diagnosticFromError(
|
|
986
|
+
} catch (error2) {
|
|
987
|
+
const diagnostic = diagnosticFromError(error2, "parse");
|
|
981
988
|
return {
|
|
982
989
|
ok: false,
|
|
983
990
|
value: null,
|
|
@@ -987,20 +994,20 @@ var WorldOrbit = (() => {
|
|
|
987
994
|
let document;
|
|
988
995
|
try {
|
|
989
996
|
document = normalizeDocument(ast);
|
|
990
|
-
} catch (
|
|
997
|
+
} catch (error2) {
|
|
991
998
|
return {
|
|
992
999
|
ok: false,
|
|
993
1000
|
value: null,
|
|
994
|
-
diagnostics: [diagnosticFromError(
|
|
1001
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
995
1002
|
};
|
|
996
1003
|
}
|
|
997
1004
|
try {
|
|
998
1005
|
validateDocument(document);
|
|
999
|
-
} catch (
|
|
1006
|
+
} catch (error2) {
|
|
1000
1007
|
return {
|
|
1001
1008
|
ok: false,
|
|
1002
1009
|
value: null,
|
|
1003
|
-
diagnostics: [diagnosticFromError(
|
|
1010
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
1004
1011
|
};
|
|
1005
1012
|
}
|
|
1006
1013
|
return {
|
|
@@ -1019,11 +1026,11 @@ var WorldOrbit = (() => {
|
|
|
1019
1026
|
value: normalizeDocument(ast),
|
|
1020
1027
|
diagnostics: []
|
|
1021
1028
|
};
|
|
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
|
}
|
|
@@ -1035,11 +1042,11 @@ var WorldOrbit = (() => {
|
|
|
1035
1042
|
value: document,
|
|
1036
1043
|
diagnostics: []
|
|
1037
1044
|
};
|
|
1038
|
-
} catch (
|
|
1045
|
+
} catch (error2) {
|
|
1039
1046
|
return {
|
|
1040
1047
|
ok: false,
|
|
1041
1048
|
value: null,
|
|
1042
|
-
diagnostics: [diagnosticFromError(
|
|
1049
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
1043
1050
|
};
|
|
1044
1051
|
}
|
|
1045
1052
|
}
|
|
@@ -1047,7 +1054,11 @@ var WorldOrbit = (() => {
|
|
|
1047
1054
|
// packages/core/dist/scene.js
|
|
1048
1055
|
var AU_IN_KM = 1495978707e-1;
|
|
1049
1056
|
var EARTH_RADIUS_IN_KM = 6371;
|
|
1057
|
+
var JUPITER_RADIUS_IN_KM = 71492;
|
|
1050
1058
|
var SOLAR_RADIUS_IN_KM = 695700;
|
|
1059
|
+
var LY_IN_AU = 63241.077;
|
|
1060
|
+
var PC_IN_AU = 206264.806;
|
|
1061
|
+
var KPC_IN_AU = 206264806;
|
|
1051
1062
|
var ISO_FLATTENING = 0.68;
|
|
1052
1063
|
var MIN_ISO_MINOR_SCALE = 0.2;
|
|
1053
1064
|
var ARC_SAMPLE_COUNT = 28;
|
|
@@ -1167,8 +1178,10 @@ var WorldOrbit = (() => {
|
|
|
1167
1178
|
const orbitVisuals = orbitDrafts.map((draft) => createOrbitVisual(draft, relationships.groupIds.get(draft.object.id) ?? null));
|
|
1168
1179
|
const leaders = leaderDrafts.map((draft) => createLeaderLine(draft));
|
|
1169
1180
|
const labels = createSceneLabels(objects, height, scaleModel.labelMultiplier);
|
|
1170
|
-
const
|
|
1181
|
+
const relations = createSceneRelations(document, objects);
|
|
1182
|
+
const layers = createSceneLayers(orbitVisuals, relations, leaders, objects, labels);
|
|
1171
1183
|
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships);
|
|
1184
|
+
const semanticGroups = createSceneSemanticGroups(document, objects);
|
|
1172
1185
|
const viewpoints = createSceneViewpoints(document, projection, frame.preset, relationships, objectMap);
|
|
1173
1186
|
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels);
|
|
1174
1187
|
return {
|
|
@@ -1178,7 +1191,7 @@ var WorldOrbit = (() => {
|
|
|
1178
1191
|
renderPreset: frame.preset,
|
|
1179
1192
|
projection,
|
|
1180
1193
|
scaleModel,
|
|
1181
|
-
title: String(document.system?.properties.title ?? document.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1194
|
+
title: String(document.system?.title ?? document.system?.properties.title ?? document.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1182
1195
|
subtitle: `${capitalizeLabel(projection)} view - ${capitalizeLabel(layoutPreset)} layout`,
|
|
1183
1196
|
systemId,
|
|
1184
1197
|
viewMode: projection,
|
|
@@ -1194,9 +1207,11 @@ var WorldOrbit = (() => {
|
|
|
1194
1207
|
contentBounds,
|
|
1195
1208
|
layers,
|
|
1196
1209
|
groups,
|
|
1210
|
+
semanticGroups,
|
|
1197
1211
|
viewpoints,
|
|
1198
1212
|
objects,
|
|
1199
1213
|
orbitVisuals,
|
|
1214
|
+
relations,
|
|
1200
1215
|
leaders,
|
|
1201
1216
|
labels
|
|
1202
1217
|
};
|
|
@@ -1306,6 +1321,7 @@ var WorldOrbit = (() => {
|
|
|
1306
1321
|
}
|
|
1307
1322
|
function createSceneObject(position, scaleModel, relationships) {
|
|
1308
1323
|
const { object, x, y, radius, sortKey, anchorX, anchorY } = position;
|
|
1324
|
+
const renderPriority = object.renderHints?.renderPriority ?? 0;
|
|
1309
1325
|
return {
|
|
1310
1326
|
renderId: createRenderId(object.id),
|
|
1311
1327
|
objectId: object.id,
|
|
@@ -1314,11 +1330,12 @@ var WorldOrbit = (() => {
|
|
|
1314
1330
|
ancestorIds: relationships.ancestorIds.get(object.id) ?? [],
|
|
1315
1331
|
childIds: relationships.childIds.get(object.id) ?? [],
|
|
1316
1332
|
groupId: relationships.groupIds.get(object.id) ?? null,
|
|
1333
|
+
semanticGroupIds: [...object.groups ?? []],
|
|
1317
1334
|
x,
|
|
1318
1335
|
y,
|
|
1319
1336
|
radius,
|
|
1320
1337
|
visualRadius: visualExtentForObject(object, radius, scaleModel),
|
|
1321
|
-
sortKey,
|
|
1338
|
+
sortKey: sortKey + renderPriority * 1e-3,
|
|
1322
1339
|
anchorX,
|
|
1323
1340
|
anchorY,
|
|
1324
1341
|
label: object.id,
|
|
@@ -1335,6 +1352,7 @@ var WorldOrbit = (() => {
|
|
|
1335
1352
|
object: draft.object,
|
|
1336
1353
|
parentId: draft.parentId,
|
|
1337
1354
|
groupId,
|
|
1355
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1338
1356
|
kind: draft.kind,
|
|
1339
1357
|
cx: draft.cx,
|
|
1340
1358
|
cy: draft.cy,
|
|
@@ -1346,7 +1364,7 @@ var WorldOrbit = (() => {
|
|
|
1346
1364
|
bandThickness: draft.bandThickness,
|
|
1347
1365
|
frontArcPath: draft.frontArcPath,
|
|
1348
1366
|
backArcPath: draft.backArcPath,
|
|
1349
|
-
hidden: draft.object.properties.hidden === true
|
|
1367
|
+
hidden: draft.object.properties.hidden === true || draft.object.renderHints?.renderOrbit === false
|
|
1350
1368
|
};
|
|
1351
1369
|
}
|
|
1352
1370
|
function createLeaderLine(draft) {
|
|
@@ -1355,6 +1373,7 @@ var WorldOrbit = (() => {
|
|
|
1355
1373
|
objectId: draft.object.id,
|
|
1356
1374
|
object: draft.object,
|
|
1357
1375
|
groupId: draft.groupId,
|
|
1376
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1358
1377
|
x1: draft.x1,
|
|
1359
1378
|
y1: draft.y1,
|
|
1360
1379
|
x2: draft.x2,
|
|
@@ -1366,7 +1385,7 @@ var WorldOrbit = (() => {
|
|
|
1366
1385
|
function createSceneLabels(objects, sceneHeight, labelMultiplier) {
|
|
1367
1386
|
const labels = [];
|
|
1368
1387
|
const occupied = [];
|
|
1369
|
-
const visibleObjects = [...objects].filter((object) => !object.hidden).sort((left, right) => left.sortKey - right.sortKey);
|
|
1388
|
+
const visibleObjects = [...objects].filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false).sort((left, right) => left.sortKey - right.sortKey);
|
|
1370
1389
|
for (const object of visibleObjects) {
|
|
1371
1390
|
const direction = object.y > sceneHeight * 0.62 ? -1 : 1;
|
|
1372
1391
|
const labelHalfWidth = estimateLabelHalfWidth(object, labelMultiplier);
|
|
@@ -1386,6 +1405,7 @@ var WorldOrbit = (() => {
|
|
|
1386
1405
|
objectId: object.objectId,
|
|
1387
1406
|
object: object.object,
|
|
1388
1407
|
groupId: object.groupId,
|
|
1408
|
+
semanticGroupIds: [...object.semanticGroupIds],
|
|
1389
1409
|
label: object.label,
|
|
1390
1410
|
secondaryLabel: object.secondaryLabel,
|
|
1391
1411
|
x: object.x,
|
|
@@ -1398,7 +1418,7 @@ var WorldOrbit = (() => {
|
|
|
1398
1418
|
}
|
|
1399
1419
|
return labels;
|
|
1400
1420
|
}
|
|
1401
|
-
function createSceneLayers(orbitVisuals, leaders, objects, labels) {
|
|
1421
|
+
function createSceneLayers(orbitVisuals, relations, leaders, objects, labels) {
|
|
1402
1422
|
const backOrbitIds = orbitVisuals.filter((visual) => !visual.hidden && Boolean(visual.backArcPath)).map((visual) => visual.renderId);
|
|
1403
1423
|
const frontOrbitIds = orbitVisuals.filter((visual) => !visual.hidden).map((visual) => visual.renderId);
|
|
1404
1424
|
return [
|
|
@@ -1409,6 +1429,10 @@ var WorldOrbit = (() => {
|
|
|
1409
1429
|
},
|
|
1410
1430
|
{ id: "orbits-back", renderIds: backOrbitIds },
|
|
1411
1431
|
{ id: "orbits-front", renderIds: frontOrbitIds },
|
|
1432
|
+
{
|
|
1433
|
+
id: "relations",
|
|
1434
|
+
renderIds: relations.filter((relation) => !relation.hidden).map((relation) => relation.renderId)
|
|
1435
|
+
},
|
|
1412
1436
|
{
|
|
1413
1437
|
id: "objects",
|
|
1414
1438
|
renderIds: objects.filter((object) => !object.hidden).map((object) => object.renderId)
|
|
@@ -1473,6 +1497,36 @@ var WorldOrbit = (() => {
|
|
|
1473
1497
|
}
|
|
1474
1498
|
return [...groups.values()].sort((left, right) => left.label.localeCompare(right.label));
|
|
1475
1499
|
}
|
|
1500
|
+
function createSceneSemanticGroups(document, objects) {
|
|
1501
|
+
return [...document.groups].map((group) => ({
|
|
1502
|
+
id: group.id,
|
|
1503
|
+
label: group.label,
|
|
1504
|
+
summary: group.summary,
|
|
1505
|
+
color: group.color,
|
|
1506
|
+
tags: [...group.tags],
|
|
1507
|
+
hidden: group.hidden,
|
|
1508
|
+
objectIds: objects.filter((object) => !object.hidden && object.semanticGroupIds.includes(group.id)).map((object) => object.objectId)
|
|
1509
|
+
})).sort((left, right) => left.label.localeCompare(right.label));
|
|
1510
|
+
}
|
|
1511
|
+
function createSceneRelations(document, objects) {
|
|
1512
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1513
|
+
return document.relations.map((relation) => {
|
|
1514
|
+
const from = objectMap.get(relation.from);
|
|
1515
|
+
const to = objectMap.get(relation.to);
|
|
1516
|
+
return {
|
|
1517
|
+
renderId: `${createRenderId(relation.id)}-relation`,
|
|
1518
|
+
relationId: relation.id,
|
|
1519
|
+
relation,
|
|
1520
|
+
fromObjectId: relation.from,
|
|
1521
|
+
toObjectId: relation.to,
|
|
1522
|
+
x1: from?.x ?? 0,
|
|
1523
|
+
y1: from?.y ?? 0,
|
|
1524
|
+
x2: to?.x ?? 0,
|
|
1525
|
+
y2: to?.y ?? 0,
|
|
1526
|
+
hidden: relation.hidden || !from || !to || from.hidden || to.hidden
|
|
1527
|
+
};
|
|
1528
|
+
}).sort((left, right) => left.relation.id.localeCompare(right.relation.id));
|
|
1529
|
+
}
|
|
1476
1530
|
function createSceneViewpoints(document, projection, preset, relationships, objectMap) {
|
|
1477
1531
|
const generatedOverview = createGeneratedOverviewViewpoint(document, projection, preset);
|
|
1478
1532
|
const drafts = /* @__PURE__ */ new Map();
|
|
@@ -1490,7 +1544,7 @@ var WorldOrbit = (() => {
|
|
|
1490
1544
|
}
|
|
1491
1545
|
const field = fieldParts.join(".").toLowerCase();
|
|
1492
1546
|
const draft = drafts.get(id) ?? { id };
|
|
1493
|
-
applyViewpointField(draft, field, value, projection, preset, relationships, objectMap);
|
|
1547
|
+
applyViewpointField(draft, field, value, document, projection, preset, relationships, objectMap);
|
|
1494
1548
|
drafts.set(id, draft);
|
|
1495
1549
|
}
|
|
1496
1550
|
const viewpoints = [...drafts.values()].map((draft) => finalizeViewpointDraft(draft, projection, preset, objectMap)).filter(Boolean);
|
|
@@ -1518,7 +1572,8 @@ var WorldOrbit = (() => {
|
|
|
1518
1572
|
});
|
|
1519
1573
|
}
|
|
1520
1574
|
function createGeneratedOverviewViewpoint(document, projection, preset) {
|
|
1521
|
-
const
|
|
1575
|
+
const title = document.system?.title ?? document.system?.properties.title;
|
|
1576
|
+
const label = title ? `${String(title)} Overview` : "Overview";
|
|
1522
1577
|
return {
|
|
1523
1578
|
id: "overview",
|
|
1524
1579
|
label,
|
|
@@ -1534,7 +1589,7 @@ var WorldOrbit = (() => {
|
|
|
1534
1589
|
generated: true
|
|
1535
1590
|
};
|
|
1536
1591
|
}
|
|
1537
|
-
function applyViewpointField(draft, field, value, projection, preset, relationships, objectMap) {
|
|
1592
|
+
function applyViewpointField(draft, field, value, document, projection, preset, relationships, objectMap) {
|
|
1538
1593
|
const normalizedValue = value.trim();
|
|
1539
1594
|
switch (field) {
|
|
1540
1595
|
case "label":
|
|
@@ -1601,7 +1656,7 @@ var WorldOrbit = (() => {
|
|
|
1601
1656
|
case "groups":
|
|
1602
1657
|
draft.filter = {
|
|
1603
1658
|
...draft.filter ?? createEmptyViewpointFilter(),
|
|
1604
|
-
groupIds: parseViewpointGroups(normalizedValue, relationships, objectMap)
|
|
1659
|
+
groupIds: parseViewpointGroups(normalizedValue, document, relationships, objectMap)
|
|
1605
1660
|
};
|
|
1606
1661
|
return;
|
|
1607
1662
|
}
|
|
@@ -1674,7 +1729,7 @@ var WorldOrbit = (() => {
|
|
|
1674
1729
|
next["orbits-front"] = enabled;
|
|
1675
1730
|
continue;
|
|
1676
1731
|
}
|
|
1677
|
-
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1732
|
+
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "relations" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1678
1733
|
next[rawLayer] = enabled;
|
|
1679
1734
|
}
|
|
1680
1735
|
}
|
|
@@ -1683,8 +1738,11 @@ var WorldOrbit = (() => {
|
|
|
1683
1738
|
function parseViewpointObjectTypes(value) {
|
|
1684
1739
|
return splitListValue(value).filter((entry) => entry === "star" || entry === "planet" || entry === "moon" || entry === "belt" || entry === "asteroid" || entry === "comet" || entry === "ring" || entry === "structure" || entry === "phenomenon");
|
|
1685
1740
|
}
|
|
1686
|
-
function parseViewpointGroups(value, relationships, objectMap) {
|
|
1741
|
+
function parseViewpointGroups(value, document, relationships, objectMap) {
|
|
1687
1742
|
return splitListValue(value).map((entry) => {
|
|
1743
|
+
if (document.schemaVersion === "2.1" || document.groups.some((group) => group.id === entry)) {
|
|
1744
|
+
return entry;
|
|
1745
|
+
}
|
|
1688
1746
|
if (entry.startsWith("wo-") && entry.endsWith("-group")) {
|
|
1689
1747
|
return entry;
|
|
1690
1748
|
}
|
|
@@ -1815,8 +1873,9 @@ var WorldOrbit = (() => {
|
|
|
1815
1873
|
}
|
|
1816
1874
|
const orbiting = [...context.orbitChildren.get(object.id) ?? []].sort(compareOrbiting);
|
|
1817
1875
|
const orbitMetricContext = computeOrbitMetricContext(orbiting, parent.radius, context.spacingFactor, context.scaleModel);
|
|
1876
|
+
const orbitRadiiPx = resolveOrbitRadiiPx(orbiting, orbitMetricContext);
|
|
1818
1877
|
orbiting.forEach((child, index) => {
|
|
1819
|
-
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, context);
|
|
1878
|
+
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, orbitRadiiPx[index] ?? orbitMetricContext.innerPx, context);
|
|
1820
1879
|
orbitDrafts.push({
|
|
1821
1880
|
object: child,
|
|
1822
1881
|
parentId: object.id,
|
|
@@ -1890,7 +1949,8 @@ var WorldOrbit = (() => {
|
|
|
1890
1949
|
metricSpread: 0,
|
|
1891
1950
|
innerPx,
|
|
1892
1951
|
stepPx,
|
|
1893
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
1952
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
1953
|
+
minimumGapPx: stepPx * 0.42
|
|
1894
1954
|
};
|
|
1895
1955
|
}
|
|
1896
1956
|
const minMetric = Math.min(...presentMetrics);
|
|
@@ -1903,10 +1963,11 @@ var WorldOrbit = (() => {
|
|
|
1903
1963
|
metricSpread,
|
|
1904
1964
|
innerPx,
|
|
1905
1965
|
stepPx,
|
|
1906
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
1966
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
1967
|
+
minimumGapPx: stepPx * 0.42
|
|
1907
1968
|
};
|
|
1908
1969
|
}
|
|
1909
|
-
function resolveOrbitGeometry(object, index, count, parent, metricContext, context) {
|
|
1970
|
+
function resolveOrbitGeometry(object, index, count, parent, metricContext, orbitRadiusPx, context) {
|
|
1910
1971
|
const placement = object.placement;
|
|
1911
1972
|
const band = object.type === "belt" || object.type === "ring";
|
|
1912
1973
|
if (!placement || placement.mode !== "orbit") {
|
|
@@ -1924,7 +1985,7 @@ var WorldOrbit = (() => {
|
|
|
1924
1985
|
};
|
|
1925
1986
|
}
|
|
1926
1987
|
const eccentricity = clampNumber(typeof placement.eccentricity === "number" ? placement.eccentricity : 0, 0, 0.92);
|
|
1927
|
-
const semiMajor =
|
|
1988
|
+
const semiMajor = orbitRadiusPx;
|
|
1928
1989
|
const baseMinor = Math.max(semiMajor * Math.sqrt(1 - eccentricity * eccentricity), semiMajor * 0.18);
|
|
1929
1990
|
const inclinationDeg = unitValueToDegrees(placement.inclination) ?? 0;
|
|
1930
1991
|
const inclinationScale = context.projection === "isometric" ? Math.max(MIN_ISO_MINOR_SCALE, Math.cos(degreesToRadians(inclinationDeg))) * ISO_FLATTENING : 1;
|
|
@@ -1954,15 +2015,19 @@ var WorldOrbit = (() => {
|
|
|
1954
2015
|
objectY: objectPoint.y
|
|
1955
2016
|
};
|
|
1956
2017
|
}
|
|
1957
|
-
function resolveOrbitRadiusPx(
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
2018
|
+
function resolveOrbitRadiusPx(metric, metricContext) {
|
|
2019
|
+
return metricContext.innerPx + metricContext.stepPx * log2(Math.max(metric, 0) + 1);
|
|
2020
|
+
}
|
|
2021
|
+
function resolveOrbitRadiiPx(objects, metricContext) {
|
|
2022
|
+
const radii = [];
|
|
2023
|
+
objects.forEach((object, index) => {
|
|
2024
|
+
const metric = orbitMetric(object);
|
|
2025
|
+
const fallbackRadius = metricContext.innerPx + index * metricContext.stepPx;
|
|
2026
|
+
const baseRadius = metric === null ? fallbackRadius : resolveOrbitRadiusPx(metric, metricContext);
|
|
2027
|
+
const minimumRadius = index === 0 ? metricContext.innerPx : (radii[index - 1] ?? metricContext.innerPx) + metricContext.minimumGapPx;
|
|
2028
|
+
radii.push(Math.max(baseRadius, minimumRadius));
|
|
2029
|
+
});
|
|
2030
|
+
return radii;
|
|
1966
2031
|
}
|
|
1967
2032
|
function orbitMetric(object) {
|
|
1968
2033
|
if (!object.placement || object.placement.mode !== "orbit") {
|
|
@@ -1970,6 +2035,9 @@ var WorldOrbit = (() => {
|
|
|
1970
2035
|
}
|
|
1971
2036
|
return toDistanceMetric(object.placement.semiMajor ?? object.placement.distance ?? null);
|
|
1972
2037
|
}
|
|
2038
|
+
function log2(value) {
|
|
2039
|
+
return Math.log(value) / Math.log(2);
|
|
2040
|
+
}
|
|
1973
2041
|
function resolveOrbitPhase(phase, index, count) {
|
|
1974
2042
|
const degreeValue = phase ? unitValueToDegrees(phase) : null;
|
|
1975
2043
|
if (degreeValue !== null) {
|
|
@@ -2293,8 +2361,18 @@ var WorldOrbit = (() => {
|
|
|
2293
2361
|
return value.value;
|
|
2294
2362
|
case "km":
|
|
2295
2363
|
return value.value / AU_IN_KM;
|
|
2364
|
+
case "m":
|
|
2365
|
+
return value.value / 1e3 / AU_IN_KM;
|
|
2366
|
+
case "ly":
|
|
2367
|
+
return value.value * LY_IN_AU;
|
|
2368
|
+
case "pc":
|
|
2369
|
+
return value.value * PC_IN_AU;
|
|
2370
|
+
case "kpc":
|
|
2371
|
+
return value.value * KPC_IN_AU;
|
|
2296
2372
|
case "re":
|
|
2297
2373
|
return value.value * EARTH_RADIUS_IN_KM / AU_IN_KM;
|
|
2374
|
+
case "rj":
|
|
2375
|
+
return value.value * JUPITER_RADIUS_IN_KM / AU_IN_KM;
|
|
2298
2376
|
case "sol":
|
|
2299
2377
|
return value.value * SOLAR_RADIUS_IN_KM / AU_IN_KM;
|
|
2300
2378
|
default:
|
|
@@ -2448,8 +2526,11 @@ var WorldOrbit = (() => {
|
|
|
2448
2526
|
return {
|
|
2449
2527
|
format: "worldorbit",
|
|
2450
2528
|
version: "2.0",
|
|
2529
|
+
schemaVersion: "2.0",
|
|
2451
2530
|
sourceVersion: document.version,
|
|
2452
2531
|
system,
|
|
2532
|
+
groups: structuredClone(document.groups ?? []),
|
|
2533
|
+
relations: structuredClone(document.relations ?? []),
|
|
2453
2534
|
objects: document.objects.map(cloneWorldOrbitObject),
|
|
2454
2535
|
diagnostics
|
|
2455
2536
|
};
|
|
@@ -2461,13 +2542,20 @@ var WorldOrbit = (() => {
|
|
|
2461
2542
|
const system = document.system ? {
|
|
2462
2543
|
type: "system",
|
|
2463
2544
|
id: document.system.id,
|
|
2545
|
+
title: document.system.title,
|
|
2546
|
+
description: document.system.description,
|
|
2547
|
+
epoch: document.system.epoch,
|
|
2548
|
+
referencePlane: document.system.referencePlane,
|
|
2464
2549
|
properties: materializeDraftSystemProperties(document.system),
|
|
2465
2550
|
info: materializeDraftSystemInfo(document.system)
|
|
2466
2551
|
} : null;
|
|
2467
2552
|
return {
|
|
2468
2553
|
format: "worldorbit",
|
|
2469
2554
|
version: "1.0",
|
|
2555
|
+
schemaVersion: document.version,
|
|
2470
2556
|
system,
|
|
2557
|
+
groups: structuredClone(document.groups ?? []),
|
|
2558
|
+
relations: structuredClone(document.relations ?? []),
|
|
2471
2559
|
objects: document.objects.map(cloneWorldOrbitObject)
|
|
2472
2560
|
};
|
|
2473
2561
|
}
|
|
@@ -2482,7 +2570,10 @@ var WorldOrbit = (() => {
|
|
|
2482
2570
|
return {
|
|
2483
2571
|
type: "system",
|
|
2484
2572
|
id: document.system?.id ?? "WorldOrbit",
|
|
2485
|
-
title: typeof document.system?.properties.title === "string" ? document.system.properties.title : null,
|
|
2573
|
+
title: document.system?.title ?? (typeof document.system?.properties.title === "string" ? document.system.properties.title : null),
|
|
2574
|
+
description: document.system?.description ?? null,
|
|
2575
|
+
epoch: document.system?.epoch ?? null,
|
|
2576
|
+
referencePlane: document.system?.referencePlane ?? null,
|
|
2486
2577
|
defaults,
|
|
2487
2578
|
atlasMetadata,
|
|
2488
2579
|
viewpoints: scene.viewpoints.map(mapSceneViewpointToDraftViewpoint),
|
|
@@ -2609,6 +2700,17 @@ var WorldOrbit = (() => {
|
|
|
2609
2700
|
function cloneWorldOrbitObject(object) {
|
|
2610
2701
|
return {
|
|
2611
2702
|
...object,
|
|
2703
|
+
groups: object.groups ? [...object.groups] : void 0,
|
|
2704
|
+
resonance: object.resonance ? { ...object.resonance } : object.resonance,
|
|
2705
|
+
renderHints: object.renderHints ? { ...object.renderHints } : object.renderHints,
|
|
2706
|
+
deriveRules: object.deriveRules ? object.deriveRules.map((rule) => ({ ...rule })) : void 0,
|
|
2707
|
+
validationRules: object.validationRules ? object.validationRules.map((rule) => ({ ...rule })) : void 0,
|
|
2708
|
+
lockedFields: object.lockedFields ? [...object.lockedFields] : void 0,
|
|
2709
|
+
tolerances: object.tolerances ? object.tolerances.map((entry) => ({
|
|
2710
|
+
field: entry.field,
|
|
2711
|
+
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
|
|
2712
|
+
})) : void 0,
|
|
2713
|
+
typedBlocks: object.typedBlocks ? Object.fromEntries(Object.entries(object.typedBlocks).map(([key, block]) => [key, { ...block ?? {} }])) : void 0,
|
|
2612
2714
|
properties: cloneProperties(object.properties),
|
|
2613
2715
|
placement: object.placement ? structuredClone(object.placement) : null,
|
|
2614
2716
|
info: { ...object.info }
|
|
@@ -2653,71 +2755,80 @@ var WorldOrbit = (() => {
|
|
|
2653
2755
|
if (system.defaults.units) {
|
|
2654
2756
|
properties.units = system.defaults.units;
|
|
2655
2757
|
}
|
|
2758
|
+
if (system.description) {
|
|
2759
|
+
properties.description = system.description;
|
|
2760
|
+
}
|
|
2761
|
+
if (system.epoch) {
|
|
2762
|
+
properties.epoch = system.epoch;
|
|
2763
|
+
}
|
|
2764
|
+
if (system.referencePlane) {
|
|
2765
|
+
properties.referencePlane = system.referencePlane;
|
|
2766
|
+
}
|
|
2656
2767
|
return properties;
|
|
2657
2768
|
}
|
|
2658
2769
|
function materializeDraftSystemInfo(system) {
|
|
2659
|
-
const
|
|
2770
|
+
const info2 = {
|
|
2660
2771
|
...system.atlasMetadata
|
|
2661
2772
|
};
|
|
2662
2773
|
if (system.defaults.theme) {
|
|
2663
|
-
|
|
2774
|
+
info2["atlas.theme"] = system.defaults.theme;
|
|
2664
2775
|
}
|
|
2665
2776
|
for (const viewpoint of system.viewpoints) {
|
|
2666
2777
|
const prefix = `viewpoint.${viewpoint.id}`;
|
|
2667
|
-
|
|
2778
|
+
info2[`${prefix}.label`] = viewpoint.label;
|
|
2668
2779
|
if (viewpoint.summary) {
|
|
2669
|
-
|
|
2780
|
+
info2[`${prefix}.summary`] = viewpoint.summary;
|
|
2670
2781
|
}
|
|
2671
2782
|
if (viewpoint.focusObjectId) {
|
|
2672
|
-
|
|
2783
|
+
info2[`${prefix}.focus`] = viewpoint.focusObjectId;
|
|
2673
2784
|
}
|
|
2674
2785
|
if (viewpoint.selectedObjectId) {
|
|
2675
|
-
|
|
2786
|
+
info2[`${prefix}.select`] = viewpoint.selectedObjectId;
|
|
2676
2787
|
}
|
|
2677
2788
|
if (viewpoint.projection) {
|
|
2678
|
-
|
|
2789
|
+
info2[`${prefix}.projection`] = viewpoint.projection;
|
|
2679
2790
|
}
|
|
2680
2791
|
if (viewpoint.preset) {
|
|
2681
|
-
|
|
2792
|
+
info2[`${prefix}.preset`] = viewpoint.preset;
|
|
2682
2793
|
}
|
|
2683
2794
|
if (viewpoint.zoom !== null) {
|
|
2684
|
-
|
|
2795
|
+
info2[`${prefix}.zoom`] = String(viewpoint.zoom);
|
|
2685
2796
|
}
|
|
2686
2797
|
if (viewpoint.rotationDeg !== 0) {
|
|
2687
|
-
|
|
2798
|
+
info2[`${prefix}.rotation`] = String(viewpoint.rotationDeg);
|
|
2688
2799
|
}
|
|
2689
2800
|
const serializedLayers = serializeViewpointLayers(viewpoint.layers);
|
|
2690
2801
|
if (serializedLayers) {
|
|
2691
|
-
|
|
2802
|
+
info2[`${prefix}.layers`] = serializedLayers;
|
|
2692
2803
|
}
|
|
2693
2804
|
if (viewpoint.filter?.query) {
|
|
2694
|
-
|
|
2805
|
+
info2[`${prefix}.query`] = viewpoint.filter.query;
|
|
2695
2806
|
}
|
|
2696
2807
|
if ((viewpoint.filter?.objectTypes.length ?? 0) > 0) {
|
|
2697
|
-
|
|
2808
|
+
info2[`${prefix}.types`] = viewpoint.filter?.objectTypes.join(" ") ?? "";
|
|
2698
2809
|
}
|
|
2699
2810
|
if ((viewpoint.filter?.tags.length ?? 0) > 0) {
|
|
2700
|
-
|
|
2811
|
+
info2[`${prefix}.tags`] = viewpoint.filter?.tags.join(" ") ?? "";
|
|
2701
2812
|
}
|
|
2702
2813
|
if ((viewpoint.filter?.groupIds.length ?? 0) > 0) {
|
|
2703
|
-
|
|
2814
|
+
info2[`${prefix}.groups`] = viewpoint.filter?.groupIds.join(" ") ?? "";
|
|
2704
2815
|
}
|
|
2705
2816
|
}
|
|
2706
2817
|
for (const annotation of system.annotations) {
|
|
2707
2818
|
const prefix = `annotation.${annotation.id}`;
|
|
2708
|
-
|
|
2819
|
+
info2[`${prefix}.label`] = annotation.label;
|
|
2709
2820
|
if (annotation.targetObjectId) {
|
|
2710
|
-
|
|
2821
|
+
info2[`${prefix}.target`] = annotation.targetObjectId;
|
|
2711
2822
|
}
|
|
2712
|
-
|
|
2823
|
+
info2[`${prefix}.body`] = annotation.body;
|
|
2713
2824
|
if (annotation.tags.length > 0) {
|
|
2714
|
-
|
|
2825
|
+
info2[`${prefix}.tags`] = annotation.tags.join(" ");
|
|
2715
2826
|
}
|
|
2716
2827
|
if (annotation.sourceObjectId) {
|
|
2717
|
-
|
|
2828
|
+
info2[`${prefix}.source`] = annotation.sourceObjectId;
|
|
2718
2829
|
}
|
|
2719
2830
|
}
|
|
2720
|
-
return
|
|
2831
|
+
return info2;
|
|
2721
2832
|
}
|
|
2722
2833
|
function serializeViewpointLayers(layers) {
|
|
2723
2834
|
const tokens = [];
|
|
@@ -2726,7 +2837,7 @@ var WorldOrbit = (() => {
|
|
|
2726
2837
|
if (orbitFront !== void 0 || orbitBack !== void 0) {
|
|
2727
2838
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
2728
2839
|
}
|
|
2729
|
-
for (const key of ["background", "guides", "objects", "labels", "metadata"]) {
|
|
2840
|
+
for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
|
|
2730
2841
|
if (layers[key] !== void 0) {
|
|
2731
2842
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
2732
2843
|
}
|
|
@@ -2736,7 +2847,8 @@ var WorldOrbit = (() => {
|
|
|
2736
2847
|
function convertAtlasDocumentToLegacyDraft(document) {
|
|
2737
2848
|
return {
|
|
2738
2849
|
...document,
|
|
2739
|
-
version: "2.0-draft"
|
|
2850
|
+
version: "2.0-draft",
|
|
2851
|
+
schemaVersion: "2.0-draft"
|
|
2740
2852
|
};
|
|
2741
2853
|
}
|
|
2742
2854
|
|
|
@@ -2778,19 +2890,28 @@ var WorldOrbit = (() => {
|
|
|
2778
2890
|
];
|
|
2779
2891
|
function formatDocument(document, options = {}) {
|
|
2780
2892
|
const schema = options.schema ?? "auto";
|
|
2781
|
-
const useDraft = schema === "2.0" || schema === "2.0-draft" || document.version === "2.0" || document.version === "2.0-draft";
|
|
2893
|
+
const useDraft = schema === "2.0" || schema === "2.1" || schema === "2.0-draft" || document.version === "2.0" || document.version === "2.1" || document.version === "2.0-draft";
|
|
2782
2894
|
if (useDraft) {
|
|
2783
2895
|
if (schema === "2.0-draft") {
|
|
2784
|
-
const legacyDraftDocument = document.version === "2.0-draft" ? document : document.version === "2.0" ? {
|
|
2896
|
+
const legacyDraftDocument = document.version === "2.0-draft" ? document : document.version === "2.0" || document.version === "2.1" ? {
|
|
2785
2897
|
...document,
|
|
2786
|
-
version: "2.0-draft"
|
|
2898
|
+
version: "2.0-draft",
|
|
2899
|
+
schemaVersion: "2.0-draft"
|
|
2787
2900
|
} : upgradeDocumentToDraftV2(document);
|
|
2788
2901
|
return formatDraftDocument(legacyDraftDocument);
|
|
2789
2902
|
}
|
|
2790
|
-
const atlasDocument = document.version === "2.0" ? document : document.version === "2.0-draft" ? {
|
|
2903
|
+
const atlasDocument = document.version === "2.0" || document.version === "2.1" ? document : document.version === "2.0-draft" ? {
|
|
2791
2904
|
...document,
|
|
2792
|
-
version: "2.0"
|
|
2905
|
+
version: "2.0",
|
|
2906
|
+
schemaVersion: "2.0"
|
|
2793
2907
|
} : upgradeDocumentToV2(document);
|
|
2908
|
+
if (schema === "2.1" && atlasDocument.version !== "2.1") {
|
|
2909
|
+
return formatAtlasDocument({
|
|
2910
|
+
...atlasDocument,
|
|
2911
|
+
version: "2.1",
|
|
2912
|
+
schemaVersion: "2.1"
|
|
2913
|
+
});
|
|
2914
|
+
}
|
|
2794
2915
|
return formatAtlasDocument(atlasDocument);
|
|
2795
2916
|
}
|
|
2796
2917
|
const lines = [];
|
|
@@ -2808,10 +2929,18 @@ var WorldOrbit = (() => {
|
|
|
2808
2929
|
return lines.join("\n");
|
|
2809
2930
|
}
|
|
2810
2931
|
function formatAtlasDocument(document) {
|
|
2811
|
-
const lines = [
|
|
2932
|
+
const lines = [`schema ${document.version}`, ""];
|
|
2812
2933
|
if (document.system) {
|
|
2813
2934
|
lines.push(...formatAtlasSystem(document.system));
|
|
2814
2935
|
}
|
|
2936
|
+
for (const group of [...document.groups].sort(compareIdLike)) {
|
|
2937
|
+
lines.push("");
|
|
2938
|
+
lines.push(...formatAtlasGroup(group));
|
|
2939
|
+
}
|
|
2940
|
+
for (const relation of [...document.relations].sort(compareIdLike)) {
|
|
2941
|
+
lines.push("");
|
|
2942
|
+
lines.push(...formatAtlasRelation(relation));
|
|
2943
|
+
}
|
|
2815
2944
|
const sortedObjects = [...document.objects].sort(compareObjects);
|
|
2816
2945
|
if (sortedObjects.length > 0 && lines.at(-1) !== "") {
|
|
2817
2946
|
lines.push("");
|
|
@@ -2827,12 +2956,21 @@ var WorldOrbit = (() => {
|
|
|
2827
2956
|
function formatDraftDocument(document) {
|
|
2828
2957
|
const legacy = document.version === "2.0-draft" ? document : {
|
|
2829
2958
|
...document,
|
|
2830
|
-
version: "2.0-draft"
|
|
2959
|
+
version: "2.0-draft",
|
|
2960
|
+
schemaVersion: "2.0-draft"
|
|
2831
2961
|
};
|
|
2832
2962
|
const lines = ["schema 2.0-draft", ""];
|
|
2833
2963
|
if (legacy.system) {
|
|
2834
2964
|
lines.push(...formatAtlasSystem(legacy.system));
|
|
2835
2965
|
}
|
|
2966
|
+
for (const group of [...legacy.groups].sort(compareIdLike)) {
|
|
2967
|
+
lines.push("");
|
|
2968
|
+
lines.push(...formatAtlasGroup(group));
|
|
2969
|
+
}
|
|
2970
|
+
for (const relation of [...legacy.relations].sort(compareIdLike)) {
|
|
2971
|
+
lines.push("");
|
|
2972
|
+
lines.push(...formatAtlasRelation(relation));
|
|
2973
|
+
}
|
|
2836
2974
|
const sortedObjects = [...legacy.objects].sort(compareObjects);
|
|
2837
2975
|
if (sortedObjects.length > 0 && lines.at(-1) !== "") {
|
|
2838
2976
|
lines.push("");
|
|
@@ -2848,11 +2986,38 @@ var WorldOrbit = (() => {
|
|
|
2848
2986
|
function formatSystem(system) {
|
|
2849
2987
|
return formatLines("system", system.id, system.properties, null, system.info);
|
|
2850
2988
|
}
|
|
2989
|
+
function formatLines(objectType, id, properties, placement, info2) {
|
|
2990
|
+
const lines = [`${objectType} ${id}`];
|
|
2991
|
+
const fieldLines = [...formatPlacement(placement), ...formatProperties(properties)];
|
|
2992
|
+
for (const fieldLine of fieldLines) {
|
|
2993
|
+
lines.push(` ${fieldLine}`);
|
|
2994
|
+
}
|
|
2995
|
+
const infoEntries = Object.entries(info2).sort(([left], [right]) => left.localeCompare(right));
|
|
2996
|
+
if (infoEntries.length > 0) {
|
|
2997
|
+
if (fieldLines.length > 0) {
|
|
2998
|
+
lines.push("");
|
|
2999
|
+
}
|
|
3000
|
+
lines.push(" info");
|
|
3001
|
+
for (const [key, value] of infoEntries) {
|
|
3002
|
+
lines.push(` ${key} ${quoteIfNeeded(value)}`);
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
return lines;
|
|
3006
|
+
}
|
|
2851
3007
|
function formatAtlasSystem(system) {
|
|
2852
3008
|
const lines = [`system ${system.id}`];
|
|
2853
3009
|
if (system.title) {
|
|
2854
3010
|
lines.push(` title ${quoteIfNeeded(system.title)}`);
|
|
2855
3011
|
}
|
|
3012
|
+
if (system.description) {
|
|
3013
|
+
lines.push(` description ${quoteIfNeeded(system.description)}`);
|
|
3014
|
+
}
|
|
3015
|
+
if (system.epoch) {
|
|
3016
|
+
lines.push(` epoch ${quoteIfNeeded(system.epoch)}`);
|
|
3017
|
+
}
|
|
3018
|
+
if (system.referencePlane) {
|
|
3019
|
+
lines.push(` referencePlane ${quoteIfNeeded(system.referencePlane)}`);
|
|
3020
|
+
}
|
|
2856
3021
|
lines.push("");
|
|
2857
3022
|
lines.push("defaults");
|
|
2858
3023
|
lines.push(` view ${system.defaults.view}`);
|
|
@@ -2887,18 +3052,22 @@ var WorldOrbit = (() => {
|
|
|
2887
3052
|
return lines;
|
|
2888
3053
|
}
|
|
2889
3054
|
function formatObject(object) {
|
|
2890
|
-
return
|
|
3055
|
+
return formatWorldOrbitObject(object.type, object.id, object);
|
|
2891
3056
|
}
|
|
2892
3057
|
function formatAtlasObject(object) {
|
|
2893
|
-
return
|
|
3058
|
+
return formatWorldOrbitObject(`object ${object.type}`, object.id, object);
|
|
2894
3059
|
}
|
|
2895
|
-
function
|
|
3060
|
+
function formatWorldOrbitObject(objectType, id, object) {
|
|
2896
3061
|
const lines = [`${objectType} ${id}`];
|
|
2897
|
-
const fieldLines = [
|
|
3062
|
+
const fieldLines = [
|
|
3063
|
+
...formatPlacement(object.placement),
|
|
3064
|
+
...formatProperties(object.properties),
|
|
3065
|
+
...formatObjectMetadata(object)
|
|
3066
|
+
];
|
|
2898
3067
|
for (const fieldLine of fieldLines) {
|
|
2899
3068
|
lines.push(` ${fieldLine}`);
|
|
2900
3069
|
}
|
|
2901
|
-
const infoEntries = Object.entries(info).sort(([left], [right]) => left.localeCompare(right));
|
|
3070
|
+
const infoEntries = Object.entries(object.info).sort(([left], [right]) => left.localeCompare(right));
|
|
2902
3071
|
if (infoEntries.length > 0) {
|
|
2903
3072
|
if (fieldLines.length > 0) {
|
|
2904
3073
|
lines.push("");
|
|
@@ -2908,6 +3077,16 @@ var WorldOrbit = (() => {
|
|
|
2908
3077
|
lines.push(` ${key} ${quoteIfNeeded(value)}`);
|
|
2909
3078
|
}
|
|
2910
3079
|
}
|
|
3080
|
+
for (const blockName of ["climate", "habitability", "settlement"]) {
|
|
3081
|
+
const blockEntries = Object.entries(object.typedBlocks?.[blockName] ?? {}).sort(([left], [right]) => left.localeCompare(right));
|
|
3082
|
+
if (blockEntries.length > 0) {
|
|
3083
|
+
lines.push("");
|
|
3084
|
+
lines.push(` ${blockName}`);
|
|
3085
|
+
for (const [key, value] of blockEntries) {
|
|
3086
|
+
lines.push(` ${key} ${quoteIfNeeded(value)}`);
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
2911
3090
|
return lines;
|
|
2912
3091
|
}
|
|
2913
3092
|
function formatPlacement(placement) {
|
|
@@ -2936,6 +3115,46 @@ var WorldOrbit = (() => {
|
|
|
2936
3115
|
function formatProperties(properties) {
|
|
2937
3116
|
return Object.keys(properties).sort(compareFieldKeys).map((key) => `${key} ${formatValue(properties[key])}`);
|
|
2938
3117
|
}
|
|
3118
|
+
function formatObjectMetadata(object) {
|
|
3119
|
+
const lines = [];
|
|
3120
|
+
if (object.groups?.length) {
|
|
3121
|
+
lines.push(`groups ${object.groups.join(" ")}`);
|
|
3122
|
+
}
|
|
3123
|
+
if (object.epoch) {
|
|
3124
|
+
lines.push(`epoch ${quoteIfNeeded(object.epoch)}`);
|
|
3125
|
+
}
|
|
3126
|
+
if (object.referencePlane) {
|
|
3127
|
+
lines.push(`referencePlane ${quoteIfNeeded(object.referencePlane)}`);
|
|
3128
|
+
}
|
|
3129
|
+
if (object.tidalLock !== void 0) {
|
|
3130
|
+
lines.push(`tidalLock ${object.tidalLock ? "true" : "false"}`);
|
|
3131
|
+
}
|
|
3132
|
+
if (object.renderHints?.renderLabel !== void 0) {
|
|
3133
|
+
lines.push(`renderLabel ${object.renderHints.renderLabel ? "true" : "false"}`);
|
|
3134
|
+
}
|
|
3135
|
+
if (object.renderHints?.renderOrbit !== void 0) {
|
|
3136
|
+
lines.push(`renderOrbit ${object.renderHints.renderOrbit ? "true" : "false"}`);
|
|
3137
|
+
}
|
|
3138
|
+
if (object.renderHints?.renderPriority !== void 0) {
|
|
3139
|
+
lines.push(`renderPriority ${object.renderHints.renderPriority}`);
|
|
3140
|
+
}
|
|
3141
|
+
if (object.resonance) {
|
|
3142
|
+
lines.push(`resonance ${object.resonance.targetObjectId} ${object.resonance.ratio}`);
|
|
3143
|
+
}
|
|
3144
|
+
for (const rule of object.deriveRules ?? []) {
|
|
3145
|
+
lines.push(`derive ${rule.field} ${rule.strategy}`);
|
|
3146
|
+
}
|
|
3147
|
+
for (const rule of object.validationRules ?? []) {
|
|
3148
|
+
lines.push(`validate ${rule.rule}`);
|
|
3149
|
+
}
|
|
3150
|
+
if (object.lockedFields?.length) {
|
|
3151
|
+
lines.push(`locked ${object.lockedFields.join(" ")}`);
|
|
3152
|
+
}
|
|
3153
|
+
for (const tolerance of object.tolerances ?? []) {
|
|
3154
|
+
lines.push(`tolerance ${tolerance.field} ${formatValue(tolerance.value)}`);
|
|
3155
|
+
}
|
|
3156
|
+
return lines;
|
|
3157
|
+
}
|
|
2939
3158
|
function formatAtlasViewpoint(viewpoint) {
|
|
2940
3159
|
const lines = [`viewpoint ${viewpoint.id}`, ` label ${quoteIfNeeded(viewpoint.label)}`];
|
|
2941
3160
|
if (viewpoint.focusObjectId) {
|
|
@@ -2991,6 +3210,50 @@ var WorldOrbit = (() => {
|
|
|
2991
3210
|
}
|
|
2992
3211
|
return lines;
|
|
2993
3212
|
}
|
|
3213
|
+
function formatAtlasGroup(group) {
|
|
3214
|
+
const lines = [`group ${group.id}`, ` label ${quoteIfNeeded(group.label)}`];
|
|
3215
|
+
if (group.summary) {
|
|
3216
|
+
lines.push(` summary ${quoteIfNeeded(group.summary)}`);
|
|
3217
|
+
}
|
|
3218
|
+
if (group.color) {
|
|
3219
|
+
lines.push(` color ${quoteIfNeeded(group.color)}`);
|
|
3220
|
+
}
|
|
3221
|
+
if (group.tags.length > 0) {
|
|
3222
|
+
lines.push(` tags ${group.tags.map(quoteIfNeeded).join(" ")}`);
|
|
3223
|
+
}
|
|
3224
|
+
if (group.hidden) {
|
|
3225
|
+
lines.push(" hidden true");
|
|
3226
|
+
}
|
|
3227
|
+
return lines;
|
|
3228
|
+
}
|
|
3229
|
+
function formatAtlasRelation(relation) {
|
|
3230
|
+
const lines = [`relation ${relation.id}`];
|
|
3231
|
+
if (relation.from) {
|
|
3232
|
+
lines.push(` from ${quoteIfNeeded(relation.from)}`);
|
|
3233
|
+
}
|
|
3234
|
+
if (relation.to) {
|
|
3235
|
+
lines.push(` to ${quoteIfNeeded(relation.to)}`);
|
|
3236
|
+
}
|
|
3237
|
+
if (relation.kind) {
|
|
3238
|
+
lines.push(` kind ${quoteIfNeeded(relation.kind)}`);
|
|
3239
|
+
}
|
|
3240
|
+
if (relation.label) {
|
|
3241
|
+
lines.push(` label ${quoteIfNeeded(relation.label)}`);
|
|
3242
|
+
}
|
|
3243
|
+
if (relation.summary) {
|
|
3244
|
+
lines.push(` summary ${quoteIfNeeded(relation.summary)}`);
|
|
3245
|
+
}
|
|
3246
|
+
if (relation.tags.length > 0) {
|
|
3247
|
+
lines.push(` tags ${relation.tags.map(quoteIfNeeded).join(" ")}`);
|
|
3248
|
+
}
|
|
3249
|
+
if (relation.color) {
|
|
3250
|
+
lines.push(` color ${quoteIfNeeded(relation.color)}`);
|
|
3251
|
+
}
|
|
3252
|
+
if (relation.hidden) {
|
|
3253
|
+
lines.push(" hidden true");
|
|
3254
|
+
}
|
|
3255
|
+
return lines;
|
|
3256
|
+
}
|
|
2994
3257
|
function formatValue(value) {
|
|
2995
3258
|
if (Array.isArray(value)) {
|
|
2996
3259
|
return value.map((item) => quoteIfNeeded(item)).join(" ");
|
|
@@ -3032,7 +3295,7 @@ var WorldOrbit = (() => {
|
|
|
3032
3295
|
if (orbitFront !== void 0 || orbitBack !== void 0) {
|
|
3033
3296
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
3034
3297
|
}
|
|
3035
|
-
for (const key of ["background", "guides", "objects", "labels", "metadata"]) {
|
|
3298
|
+
for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
|
|
3036
3299
|
if (layers[key] !== void 0) {
|
|
3037
3300
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
3038
3301
|
}
|
|
@@ -3057,6 +3320,9 @@ var WorldOrbit = (() => {
|
|
|
3057
3320
|
return leftIndex - rightIndex;
|
|
3058
3321
|
return left.id.localeCompare(right.id);
|
|
3059
3322
|
}
|
|
3323
|
+
function compareIdLike(left, right) {
|
|
3324
|
+
return left.id.localeCompare(right.id);
|
|
3325
|
+
}
|
|
3060
3326
|
function objectTypeIndex(objectType) {
|
|
3061
3327
|
switch (objectType) {
|
|
3062
3328
|
case "star":
|
|
@@ -3086,180 +3352,716 @@ var WorldOrbit = (() => {
|
|
|
3086
3352
|
return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
3087
3353
|
}
|
|
3088
3354
|
|
|
3089
|
-
// packages/core/dist/
|
|
3090
|
-
|
|
3091
|
-
|
|
3355
|
+
// packages/core/dist/atlas-utils.js
|
|
3356
|
+
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)?$/;
|
|
3357
|
+
var BOOLEAN_VALUES2 = /* @__PURE__ */ new Map([
|
|
3358
|
+
["true", true],
|
|
3359
|
+
["false", false],
|
|
3360
|
+
["yes", true],
|
|
3361
|
+
["no", false]
|
|
3362
|
+
]);
|
|
3363
|
+
var URL_SCHEME_PATTERN2 = /^[A-Za-z][A-Za-z0-9+.-]*:/;
|
|
3364
|
+
function normalizeIdentifier2(value) {
|
|
3365
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3092
3366
|
}
|
|
3093
|
-
function
|
|
3094
|
-
return
|
|
3367
|
+
function humanizeIdentifier3(value) {
|
|
3368
|
+
return value.split(/[-_]+/).filter(Boolean).map((segment) => segment[0].toUpperCase() + segment.slice(1)).join(" ");
|
|
3095
3369
|
}
|
|
3096
|
-
function
|
|
3097
|
-
const
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
let system = null;
|
|
3101
|
-
let section = null;
|
|
3102
|
-
const objectNodes = [];
|
|
3103
|
-
let sawDefaults = false;
|
|
3104
|
-
let sawAtlas = false;
|
|
3105
|
-
const viewpointIds = /* @__PURE__ */ new Set();
|
|
3106
|
-
const annotationIds = /* @__PURE__ */ new Set();
|
|
3107
|
-
for (let index = 0; index < lines.length; index++) {
|
|
3108
|
-
const rawLine = lines[index];
|
|
3109
|
-
const lineNumber = index + 1;
|
|
3110
|
-
if (!rawLine.trim()) {
|
|
3111
|
-
continue;
|
|
3112
|
-
}
|
|
3113
|
-
const indent = getIndent(rawLine);
|
|
3114
|
-
const tokens = tokenizeLineDetailed(rawLine.slice(indent), {
|
|
3115
|
-
line: lineNumber,
|
|
3116
|
-
columnOffset: indent
|
|
3117
|
-
});
|
|
3118
|
-
if (tokens.length === 0) {
|
|
3119
|
-
continue;
|
|
3120
|
-
}
|
|
3121
|
-
if (!sawSchemaHeader) {
|
|
3122
|
-
schemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
3123
|
-
sawSchemaHeader = true;
|
|
3124
|
-
continue;
|
|
3125
|
-
}
|
|
3126
|
-
if (indent === 0) {
|
|
3127
|
-
section = startTopLevelSection(tokens, lineNumber, system, objectNodes, viewpointIds, annotationIds, {
|
|
3128
|
-
sawDefaults,
|
|
3129
|
-
sawAtlas
|
|
3130
|
-
});
|
|
3131
|
-
if (section.kind === "system") {
|
|
3132
|
-
system = section.system;
|
|
3133
|
-
} else if (section.kind === "defaults") {
|
|
3134
|
-
sawDefaults = true;
|
|
3135
|
-
} else if (section.kind === "atlas") {
|
|
3136
|
-
sawAtlas = true;
|
|
3137
|
-
}
|
|
3138
|
-
continue;
|
|
3139
|
-
}
|
|
3140
|
-
if (!section) {
|
|
3141
|
-
throw new WorldOrbitError("Indented line without parent atlas section", lineNumber, indent + 1);
|
|
3142
|
-
}
|
|
3143
|
-
handleSectionLine(section, indent, tokens, lineNumber);
|
|
3144
|
-
}
|
|
3145
|
-
if (!sawSchemaHeader) {
|
|
3146
|
-
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
|
|
3370
|
+
function parseAtlasUnitValue(input, location, fieldKey) {
|
|
3371
|
+
const match = input.match(UNIT_PATTERN2);
|
|
3372
|
+
if (!match) {
|
|
3373
|
+
throw WorldOrbitError.fromLocation(`Invalid unit value "${input}"`, location);
|
|
3147
3374
|
}
|
|
3148
|
-
const
|
|
3149
|
-
|
|
3150
|
-
|
|
3375
|
+
const unitValue = {
|
|
3376
|
+
value: Number(match[1]),
|
|
3377
|
+
unit: match[2] ?? null
|
|
3151
3378
|
};
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
system: null,
|
|
3157
|
-
objects: normalizedObjects
|
|
3158
|
-
});
|
|
3159
|
-
const diagnostics = schemaVersion === "2.0-draft" && outputVersion === "2.0" ? [
|
|
3160
|
-
{
|
|
3161
|
-
code: "load.schema.deprecatedDraft",
|
|
3162
|
-
severity: "warning",
|
|
3163
|
-
source: "upgrade",
|
|
3164
|
-
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
3379
|
+
if (fieldKey) {
|
|
3380
|
+
const schema = getFieldSchema(fieldKey);
|
|
3381
|
+
if (schema?.unitFamily && !unitFamilyAllowsUnit(schema.unitFamily, unitValue.unit)) {
|
|
3382
|
+
throw WorldOrbitError.fromLocation(`Unit "${unitValue.unit ?? "none"}" is not valid for "${fieldKey}"`, location);
|
|
3165
3383
|
}
|
|
3166
|
-
] : [];
|
|
3167
|
-
return {
|
|
3168
|
-
format: "worldorbit",
|
|
3169
|
-
version: outputVersion,
|
|
3170
|
-
sourceVersion: "1.0",
|
|
3171
|
-
system,
|
|
3172
|
-
objects: normalizedObjects,
|
|
3173
|
-
diagnostics
|
|
3174
|
-
};
|
|
3175
|
-
}
|
|
3176
|
-
function assertDraftSchemaHeader(tokens, line) {
|
|
3177
|
-
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || tokens[1].value.toLowerCase() !== "2.0-draft" && tokens[1].value.toLowerCase() !== "2.0") {
|
|
3178
|
-
throw new WorldOrbitError('Expected atlas header "schema 2.0" or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
3179
|
-
}
|
|
3180
|
-
return tokens[1].value.toLowerCase() === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
3181
|
-
}
|
|
3182
|
-
function startTopLevelSection(tokens, line, system, objectNodes, viewpointIds, annotationIds, flags) {
|
|
3183
|
-
const keyword = tokens[0]?.value.toLowerCase();
|
|
3184
|
-
switch (keyword) {
|
|
3185
|
-
case "system":
|
|
3186
|
-
if (system) {
|
|
3187
|
-
throw new WorldOrbitError('Atlas section "system" may only appear once', line, tokens[0].column);
|
|
3188
|
-
}
|
|
3189
|
-
return startSystemSection(tokens, line);
|
|
3190
|
-
case "defaults":
|
|
3191
|
-
if (!system) {
|
|
3192
|
-
throw new WorldOrbitError('Atlas section "defaults" requires a preceding system declaration', line, tokens[0].column);
|
|
3193
|
-
}
|
|
3194
|
-
if (flags.sawDefaults) {
|
|
3195
|
-
throw new WorldOrbitError('Atlas section "defaults" may only appear once', line, tokens[0].column);
|
|
3196
|
-
}
|
|
3197
|
-
return {
|
|
3198
|
-
kind: "defaults",
|
|
3199
|
-
system,
|
|
3200
|
-
seenFields: /* @__PURE__ */ new Set()
|
|
3201
|
-
};
|
|
3202
|
-
case "atlas":
|
|
3203
|
-
if (!system) {
|
|
3204
|
-
throw new WorldOrbitError('Atlas section "atlas" requires a preceding system declaration', line, tokens[0].column);
|
|
3205
|
-
}
|
|
3206
|
-
if (flags.sawAtlas) {
|
|
3207
|
-
throw new WorldOrbitError('Atlas section "atlas" may only appear once', line, tokens[0].column);
|
|
3208
|
-
}
|
|
3209
|
-
return {
|
|
3210
|
-
kind: "atlas",
|
|
3211
|
-
system,
|
|
3212
|
-
inMetadata: false,
|
|
3213
|
-
metadataIndent: null
|
|
3214
|
-
};
|
|
3215
|
-
case "viewpoint":
|
|
3216
|
-
if (!system) {
|
|
3217
|
-
throw new WorldOrbitError('Atlas section "viewpoint" requires a preceding system declaration', line, tokens[0].column);
|
|
3218
|
-
}
|
|
3219
|
-
return startViewpointSection(tokens, line, system, viewpointIds);
|
|
3220
|
-
case "annotation":
|
|
3221
|
-
if (!system) {
|
|
3222
|
-
throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
|
|
3223
|
-
}
|
|
3224
|
-
return startAnnotationSection(tokens, line, system, annotationIds);
|
|
3225
|
-
case "object":
|
|
3226
|
-
return startObjectSection(tokens, line, objectNodes);
|
|
3227
|
-
default:
|
|
3228
|
-
throw new WorldOrbitError(`Unknown atlas section "${tokens[0]?.value ?? ""}"`, line, tokens[0]?.column ?? 1);
|
|
3229
3384
|
}
|
|
3385
|
+
return unitValue;
|
|
3230
3386
|
}
|
|
3231
|
-
function
|
|
3232
|
-
|
|
3233
|
-
|
|
3387
|
+
function tryParseAtlasUnitValue(input) {
|
|
3388
|
+
const match = input.match(UNIT_PATTERN2);
|
|
3389
|
+
if (!match) {
|
|
3390
|
+
return null;
|
|
3234
3391
|
}
|
|
3235
|
-
const system = {
|
|
3236
|
-
type: "system",
|
|
3237
|
-
id: tokens[1].value,
|
|
3238
|
-
title: null,
|
|
3239
|
-
defaults: {
|
|
3240
|
-
view: "topdown",
|
|
3241
|
-
scale: null,
|
|
3242
|
-
units: null,
|
|
3243
|
-
preset: null,
|
|
3244
|
-
theme: null
|
|
3245
|
-
},
|
|
3246
|
-
atlasMetadata: {},
|
|
3247
|
-
viewpoints: [],
|
|
3248
|
-
annotations: []
|
|
3249
|
-
};
|
|
3250
3392
|
return {
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
seenFields: /* @__PURE__ */ new Set()
|
|
3393
|
+
value: Number(match[1]),
|
|
3394
|
+
unit: match[2] ?? null
|
|
3254
3395
|
};
|
|
3255
3396
|
}
|
|
3256
|
-
function
|
|
3257
|
-
|
|
3258
|
-
|
|
3397
|
+
function parseAtlasNumber(input, key, location) {
|
|
3398
|
+
const value = Number(input);
|
|
3399
|
+
if (!Number.isFinite(value)) {
|
|
3400
|
+
throw WorldOrbitError.fromLocation(`Invalid numeric value "${input}" for "${key}"`, location);
|
|
3259
3401
|
}
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3402
|
+
return value;
|
|
3403
|
+
}
|
|
3404
|
+
function parseAtlasBoolean(input, key, location) {
|
|
3405
|
+
const parsed = BOOLEAN_VALUES2.get(input.toLowerCase());
|
|
3406
|
+
if (parsed === void 0) {
|
|
3407
|
+
throw WorldOrbitError.fromLocation(`Invalid boolean value "${input}" for "${key}"`, location);
|
|
3408
|
+
}
|
|
3409
|
+
return parsed;
|
|
3410
|
+
}
|
|
3411
|
+
function parseAtlasAtReference(target, location) {
|
|
3412
|
+
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
3413
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
3414
|
+
}
|
|
3415
|
+
const pairedMatch = target.match(/^([A-Za-z0-9._-]+)-([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
3416
|
+
if (pairedMatch) {
|
|
3417
|
+
return {
|
|
3418
|
+
kind: "lagrange",
|
|
3419
|
+
primary: pairedMatch[1],
|
|
3420
|
+
secondary: pairedMatch[2],
|
|
3421
|
+
point: pairedMatch[3]
|
|
3422
|
+
};
|
|
3423
|
+
}
|
|
3424
|
+
const simpleMatch = target.match(/^([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
3425
|
+
if (simpleMatch) {
|
|
3426
|
+
return {
|
|
3427
|
+
kind: "lagrange",
|
|
3428
|
+
primary: simpleMatch[1],
|
|
3429
|
+
secondary: null,
|
|
3430
|
+
point: simpleMatch[2]
|
|
3431
|
+
};
|
|
3432
|
+
}
|
|
3433
|
+
if (/^[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
3434
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
3435
|
+
}
|
|
3436
|
+
const anchorMatch = target.match(/^([A-Za-z0-9._-]+):([A-Za-z0-9._-]+)$/);
|
|
3437
|
+
if (anchorMatch) {
|
|
3438
|
+
return {
|
|
3439
|
+
kind: "anchor",
|
|
3440
|
+
objectId: anchorMatch[1],
|
|
3441
|
+
anchor: anchorMatch[2]
|
|
3442
|
+
};
|
|
3443
|
+
}
|
|
3444
|
+
return {
|
|
3445
|
+
kind: "named",
|
|
3446
|
+
name: target
|
|
3447
|
+
};
|
|
3448
|
+
}
|
|
3449
|
+
function validateAtlasImageSource(value, location) {
|
|
3450
|
+
if (!value) {
|
|
3451
|
+
throw WorldOrbitError.fromLocation('Field "image" must not be empty', location);
|
|
3452
|
+
}
|
|
3453
|
+
if (value.startsWith("//")) {
|
|
3454
|
+
throw WorldOrbitError.fromLocation('Field "image" must use a relative path, root-relative path, or an http/https URL', location);
|
|
3455
|
+
}
|
|
3456
|
+
const schemeMatch = value.match(URL_SCHEME_PATTERN2);
|
|
3457
|
+
if (!schemeMatch) {
|
|
3458
|
+
return;
|
|
3459
|
+
}
|
|
3460
|
+
const scheme = schemeMatch[0].slice(0, -1).toLowerCase();
|
|
3461
|
+
if (scheme !== "http" && scheme !== "https") {
|
|
3462
|
+
throw WorldOrbitError.fromLocation(`Field "image" does not support the "${scheme}" scheme`, location);
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
function normalizeLegacyScalarValue(key, values, location) {
|
|
3466
|
+
const schema = getFieldSchema(key);
|
|
3467
|
+
if (!schema) {
|
|
3468
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
3469
|
+
}
|
|
3470
|
+
if (schema.arity === "single" && values.length !== 1) {
|
|
3471
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
3472
|
+
}
|
|
3473
|
+
switch (schema.kind) {
|
|
3474
|
+
case "list":
|
|
3475
|
+
return values;
|
|
3476
|
+
case "boolean":
|
|
3477
|
+
return parseAtlasBoolean(singleAtlasValue(values, key, location), key, location);
|
|
3478
|
+
case "number":
|
|
3479
|
+
return parseAtlasNumber(singleAtlasValue(values, key, location), key, location);
|
|
3480
|
+
case "unit":
|
|
3481
|
+
return parseAtlasUnitValue(singleAtlasValue(values, key, location), location, key);
|
|
3482
|
+
case "string": {
|
|
3483
|
+
const value = values.join(" ").trim();
|
|
3484
|
+
if (key === "image") {
|
|
3485
|
+
validateAtlasImageSource(value, location);
|
|
3486
|
+
}
|
|
3487
|
+
return value;
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
function ensureAtlasFieldSupported(key, objectType, location) {
|
|
3492
|
+
const schema = getFieldSchema(key);
|
|
3493
|
+
if (!schema) {
|
|
3494
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
3495
|
+
}
|
|
3496
|
+
if (!schema.objectTypes.includes(objectType)) {
|
|
3497
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" is not valid on "${objectType}"`, location);
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3500
|
+
function singleAtlasValue(values, key, location) {
|
|
3501
|
+
if (values.length !== 1) {
|
|
3502
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
3503
|
+
}
|
|
3504
|
+
return values[0];
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
// packages/core/dist/atlas-validate.js
|
|
3508
|
+
var SURFACE_TARGET_TYPES2 = /* @__PURE__ */ new Set(["star", "planet", "moon", "asteroid", "comet"]);
|
|
3509
|
+
var EARTH_MASSES_PER_SOLAR = 332946.0487;
|
|
3510
|
+
var JUPITER_MASSES_PER_SOLAR = 1047.3486;
|
|
3511
|
+
var AU_IN_KM2 = 1495978707e-1;
|
|
3512
|
+
var EARTH_RADIUS_IN_KM2 = 6371;
|
|
3513
|
+
var SOLAR_RADIUS_IN_KM2 = 695700;
|
|
3514
|
+
var LY_IN_AU2 = 63241.077;
|
|
3515
|
+
var PC_IN_AU2 = 206264.806;
|
|
3516
|
+
var KPC_IN_AU2 = 206264806;
|
|
3517
|
+
function collectAtlasDiagnostics(document, sourceSchemaVersion) {
|
|
3518
|
+
const diagnostics = [];
|
|
3519
|
+
const objectMap = new Map(document.objects.map((object) => [object.id, object]));
|
|
3520
|
+
const groupIds = new Set(document.groups.map((group) => group.id));
|
|
3521
|
+
if (!document.system) {
|
|
3522
|
+
diagnostics.push(error("validate.system.required", "Atlas documents must declare exactly one system."));
|
|
3523
|
+
}
|
|
3524
|
+
const knownIds = /* @__PURE__ */ new Map();
|
|
3525
|
+
for (const [kind, ids] of [
|
|
3526
|
+
["group", document.groups.map((group) => group.id)],
|
|
3527
|
+
["viewpoint", document.system?.viewpoints.map((viewpoint) => viewpoint.id) ?? []],
|
|
3528
|
+
["annotation", document.system?.annotations.map((annotation) => annotation.id) ?? []],
|
|
3529
|
+
["relation", document.relations.map((relation) => relation.id)],
|
|
3530
|
+
["object", document.objects.map((object) => object.id)]
|
|
3531
|
+
]) {
|
|
3532
|
+
for (const id of ids) {
|
|
3533
|
+
const previous = knownIds.get(id);
|
|
3534
|
+
if (previous) {
|
|
3535
|
+
diagnostics.push(error("validate.id.duplicate", `Duplicate ${kind} id "${id}" already used by ${previous}.`));
|
|
3536
|
+
} else {
|
|
3537
|
+
knownIds.set(id, kind);
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
for (const relation of document.relations) {
|
|
3542
|
+
validateRelation(relation, objectMap, diagnostics);
|
|
3543
|
+
}
|
|
3544
|
+
for (const viewpoint of document.system?.viewpoints ?? []) {
|
|
3545
|
+
validateViewpointFilter(viewpoint.filter, groupIds, sourceSchemaVersion, diagnostics, viewpoint.id);
|
|
3546
|
+
}
|
|
3547
|
+
for (const object of document.objects) {
|
|
3548
|
+
validateObject(object, document.system, objectMap, groupIds, diagnostics);
|
|
3549
|
+
}
|
|
3550
|
+
return diagnostics;
|
|
3551
|
+
}
|
|
3552
|
+
function validateRelation(relation, objectMap, diagnostics) {
|
|
3553
|
+
if (!relation.from) {
|
|
3554
|
+
diagnostics.push(error("validate.relation.from.required", `Relation "${relation.id}" is missing a "from" target.`));
|
|
3555
|
+
} else if (!objectMap.has(relation.from)) {
|
|
3556
|
+
diagnostics.push(error("validate.relation.from.unknown", `Unknown relation source "${relation.from}" on "${relation.id}".`));
|
|
3557
|
+
}
|
|
3558
|
+
if (!relation.to) {
|
|
3559
|
+
diagnostics.push(error("validate.relation.to.required", `Relation "${relation.id}" is missing a "to" target.`));
|
|
3560
|
+
} else if (!objectMap.has(relation.to)) {
|
|
3561
|
+
diagnostics.push(error("validate.relation.to.unknown", `Unknown relation target "${relation.to}" on "${relation.id}".`));
|
|
3562
|
+
}
|
|
3563
|
+
if (!relation.kind) {
|
|
3564
|
+
diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
function validateViewpointFilter(filter, groupIds, sourceSchemaVersion, diagnostics, viewpointId) {
|
|
3568
|
+
if (!filter || sourceSchemaVersion !== "2.1") {
|
|
3569
|
+
return;
|
|
3570
|
+
}
|
|
3571
|
+
for (const groupId of filter.groupIds) {
|
|
3572
|
+
if (!groupIds.has(groupId)) {
|
|
3573
|
+
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpointId}".`));
|
|
3574
|
+
}
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
3578
|
+
const placement = object.placement;
|
|
3579
|
+
const orbitPlacement = placement?.mode === "orbit" ? placement : null;
|
|
3580
|
+
const parentObject = placement?.mode === "orbit" ? objectMap.get(placement.target) ?? null : null;
|
|
3581
|
+
if (object.groups) {
|
|
3582
|
+
for (const groupId of object.groups) {
|
|
3583
|
+
if (!groupIds.has(groupId)) {
|
|
3584
|
+
diagnostics.push(warn("validate.group.unknown", `Unknown group "${groupId}" on "${object.id}".`, object.id, "groups"));
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
if (orbitPlacement) {
|
|
3589
|
+
if (!objectMap.has(orbitPlacement.target)) {
|
|
3590
|
+
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
3591
|
+
}
|
|
3592
|
+
if (orbitPlacement.distance && orbitPlacement.semiMajor) {
|
|
3593
|
+
diagnostics.push(error("validate.orbit.distanceConflict", `Object "${object.id}" cannot declare both "distance" and "semiMajor".`, object.id, "distance"));
|
|
3594
|
+
}
|
|
3595
|
+
if (orbitPlacement.phase && !object.epoch && !system?.epoch) {
|
|
3596
|
+
diagnostics.push(warn("validate.phase.epochMissing", `Object "${object.id}" sets "phase" without an object or system epoch.`, object.id, "phase"));
|
|
3597
|
+
}
|
|
3598
|
+
if (orbitPlacement.inclination && !object.referencePlane && !system?.referencePlane) {
|
|
3599
|
+
diagnostics.push(warn("validate.inclination.referencePlaneMissing", `Object "${object.id}" sets "inclination" without an object or system reference plane.`, object.id, "inclination"));
|
|
3600
|
+
}
|
|
3601
|
+
if (orbitPlacement.period && !massInSolar(parentObject?.properties.mass)) {
|
|
3602
|
+
diagnostics.push(warn("validate.period.massMissing", `Object "${object.id}" sets "period" but its central mass cannot be derived.`, object.id, "period"));
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
if (placement?.mode === "surface") {
|
|
3606
|
+
const target = objectMap.get(placement.target);
|
|
3607
|
+
if (!target) {
|
|
3608
|
+
diagnostics.push(error("validate.surface.target.unknown", `Unknown placement target "${placement.target}" on "${object.id}".`, object.id, "surface"));
|
|
3609
|
+
} else if (!SURFACE_TARGET_TYPES2.has(target.type)) {
|
|
3610
|
+
diagnostics.push(error("validate.surface.target.invalid", `Surface target "${placement.target}" on "${object.id}" is not surface-capable.`, object.id, "surface"));
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
if (placement?.mode === "at") {
|
|
3614
|
+
if (object.type !== "structure" && object.type !== "phenomenon") {
|
|
3615
|
+
diagnostics.push(error("validate.at.objectType", `Only structures and phenomena may use "at" placement; found "${object.type}" on "${object.id}".`, object.id, "at"));
|
|
3616
|
+
}
|
|
3617
|
+
if (!validateAtTarget(object, objectMap, diagnostics)) {
|
|
3618
|
+
diagnostics.push(error("validate.at.target.unknown", `Unknown at-reference target "${placement.target}" on "${object.id}".`, object.id, "at"));
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3621
|
+
if (object.resonance) {
|
|
3622
|
+
const target = objectMap.get(object.resonance.targetObjectId);
|
|
3623
|
+
if (!target) {
|
|
3624
|
+
diagnostics.push(error("validate.resonance.target.unknown", `Unknown resonance target "${object.resonance.targetObjectId}" on "${object.id}".`, object.id, "resonance"));
|
|
3625
|
+
} else if (object.placement?.mode !== "orbit" || target.placement?.mode !== "orbit" || object.placement.target !== target.placement.target) {
|
|
3626
|
+
diagnostics.push(warn("validate.resonance.orbitMismatch", `Resonance target "${object.resonance.targetObjectId}" on "${object.id}" does not share a compatible orbital parent.`, object.id, "resonance"));
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
for (const rule of object.deriveRules ?? []) {
|
|
3630
|
+
if (rule.field !== "period" || rule.strategy !== "kepler") {
|
|
3631
|
+
diagnostics.push(warn("validate.derive.unsupported", `Unsupported derive rule "${rule.field} ${rule.strategy}" on "${object.id}".`, object.id, "derive"));
|
|
3632
|
+
continue;
|
|
3633
|
+
}
|
|
3634
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
3635
|
+
if (derivedPeriodDays === null) {
|
|
3636
|
+
diagnostics.push(warn("validate.derive.inputsMissing", `Object "${object.id}" requests "derive period kepler" but lacks enough input data.`, object.id, "derive"));
|
|
3637
|
+
continue;
|
|
3638
|
+
}
|
|
3639
|
+
if (!orbitPlacement?.period) {
|
|
3640
|
+
diagnostics.push(info("validate.derive.period.available", `Object "${object.id}" can derive a Kepler period of ${formatDays(derivedPeriodDays)}.`, object.id, "derive"));
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
for (const rule of object.validationRules ?? []) {
|
|
3644
|
+
if (rule.rule !== "kepler") {
|
|
3645
|
+
diagnostics.push(warn("validate.rule.unsupported", `Unsupported validation rule "${rule.rule}" on "${object.id}".`, object.id, "validate"));
|
|
3646
|
+
continue;
|
|
3647
|
+
}
|
|
3648
|
+
const actualPeriodDays = durationInDays(orbitPlacement?.period);
|
|
3649
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
3650
|
+
if (actualPeriodDays === null || derivedPeriodDays === null) {
|
|
3651
|
+
continue;
|
|
3652
|
+
}
|
|
3653
|
+
const toleranceDays = toleranceForField(object, "period");
|
|
3654
|
+
if (Math.abs(actualPeriodDays - derivedPeriodDays) > toleranceDays) {
|
|
3655
|
+
diagnostics.push(error("validate.kepler.mismatch", `Object "${object.id}" fails Kepler validation for "period".`, object.id, "validate"));
|
|
3656
|
+
}
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
function validateAtTarget(object, objectMap, diagnostics) {
|
|
3660
|
+
const reference = object.placement?.mode === "at" ? object.placement.reference : null;
|
|
3661
|
+
if (!reference) {
|
|
3662
|
+
return true;
|
|
3663
|
+
}
|
|
3664
|
+
if (reference.kind === "named") {
|
|
3665
|
+
return objectMap.has(reference.name);
|
|
3666
|
+
}
|
|
3667
|
+
if (reference.kind === "anchor") {
|
|
3668
|
+
if (!objectMap.has(reference.objectId)) {
|
|
3669
|
+
diagnostics.push(error("validate.anchor.target.unknown", `Unknown anchor target "${reference.objectId}" on "${object.id}".`, object.id, "at"));
|
|
3670
|
+
return false;
|
|
3671
|
+
}
|
|
3672
|
+
return true;
|
|
3673
|
+
}
|
|
3674
|
+
if (!objectMap.has(reference.primary)) {
|
|
3675
|
+
diagnostics.push(error("validate.lagrange.primary.unknown", `Unknown Lagrange reference "${reference.primary}" on "${object.id}".`, object.id, "at"));
|
|
3676
|
+
return false;
|
|
3677
|
+
}
|
|
3678
|
+
if (reference.secondary && !objectMap.has(reference.secondary)) {
|
|
3679
|
+
diagnostics.push(error("validate.lagrange.secondary.unknown", `Unknown Lagrange reference "${reference.secondary}" on "${object.id}".`, object.id, "at"));
|
|
3680
|
+
return false;
|
|
3681
|
+
}
|
|
3682
|
+
return true;
|
|
3683
|
+
}
|
|
3684
|
+
function keplerPeriodDays(object, parentObject) {
|
|
3685
|
+
const placement = object.placement;
|
|
3686
|
+
if (!placement || placement.mode !== "orbit") {
|
|
3687
|
+
return null;
|
|
3688
|
+
}
|
|
3689
|
+
const semiMajorAu = distanceInAu(placement.semiMajor ?? placement.distance);
|
|
3690
|
+
const centralMassSolar = massInSolar(parentObject?.properties.mass);
|
|
3691
|
+
if (semiMajorAu === null || centralMassSolar === null || centralMassSolar <= 0) {
|
|
3692
|
+
return null;
|
|
3693
|
+
}
|
|
3694
|
+
const periodYears = Math.sqrt(semiMajorAu ** 3 / centralMassSolar);
|
|
3695
|
+
return periodYears * 365.25;
|
|
3696
|
+
}
|
|
3697
|
+
function distanceInAu(value) {
|
|
3698
|
+
if (!value)
|
|
3699
|
+
return null;
|
|
3700
|
+
switch (value.unit) {
|
|
3701
|
+
case null:
|
|
3702
|
+
case "au":
|
|
3703
|
+
return value.value;
|
|
3704
|
+
case "km":
|
|
3705
|
+
return value.value / AU_IN_KM2;
|
|
3706
|
+
case "m":
|
|
3707
|
+
return value.value / (AU_IN_KM2 * 1e3);
|
|
3708
|
+
case "ly":
|
|
3709
|
+
return value.value * LY_IN_AU2;
|
|
3710
|
+
case "pc":
|
|
3711
|
+
return value.value * PC_IN_AU2;
|
|
3712
|
+
case "kpc":
|
|
3713
|
+
return value.value * KPC_IN_AU2;
|
|
3714
|
+
case "re":
|
|
3715
|
+
return value.value * EARTH_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
3716
|
+
case "sol":
|
|
3717
|
+
return value.value * SOLAR_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
3718
|
+
default:
|
|
3719
|
+
return null;
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
function massInSolar(value) {
|
|
3723
|
+
if (!value || typeof value !== "object" || !("value" in value)) {
|
|
3724
|
+
return null;
|
|
3725
|
+
}
|
|
3726
|
+
const unitValue = value;
|
|
3727
|
+
switch (unitValue.unit) {
|
|
3728
|
+
case null:
|
|
3729
|
+
case "sol":
|
|
3730
|
+
return unitValue.value;
|
|
3731
|
+
case "me":
|
|
3732
|
+
return unitValue.value / EARTH_MASSES_PER_SOLAR;
|
|
3733
|
+
case "mj":
|
|
3734
|
+
return unitValue.value / JUPITER_MASSES_PER_SOLAR;
|
|
3735
|
+
default:
|
|
3736
|
+
return null;
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
function durationInDays(value) {
|
|
3740
|
+
if (!value)
|
|
3741
|
+
return null;
|
|
3742
|
+
switch (value.unit) {
|
|
3743
|
+
case null:
|
|
3744
|
+
case "d":
|
|
3745
|
+
return value.value;
|
|
3746
|
+
case "s":
|
|
3747
|
+
return value.value / 86400;
|
|
3748
|
+
case "min":
|
|
3749
|
+
return value.value / 1440;
|
|
3750
|
+
case "h":
|
|
3751
|
+
return value.value / 24;
|
|
3752
|
+
case "y":
|
|
3753
|
+
return value.value * 365.25;
|
|
3754
|
+
case "ky":
|
|
3755
|
+
return value.value * 365250;
|
|
3756
|
+
case "my":
|
|
3757
|
+
return value.value * 36525e4;
|
|
3758
|
+
case "gy":
|
|
3759
|
+
return value.value * 36525e7;
|
|
3760
|
+
default:
|
|
3761
|
+
return null;
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
function toleranceForField(object, field) {
|
|
3765
|
+
const tolerance = object.tolerances?.find((entry) => entry.field === field)?.value;
|
|
3766
|
+
if (typeof tolerance === "number") {
|
|
3767
|
+
return tolerance;
|
|
3768
|
+
}
|
|
3769
|
+
if (tolerance && typeof tolerance === "object" && "value" in tolerance) {
|
|
3770
|
+
return durationInDays(tolerance) ?? 0;
|
|
3771
|
+
}
|
|
3772
|
+
return 0;
|
|
3773
|
+
}
|
|
3774
|
+
function formatDays(days) {
|
|
3775
|
+
return `${Math.round(days * 100) / 100}d`;
|
|
3776
|
+
}
|
|
3777
|
+
function error(code, message, objectId, field) {
|
|
3778
|
+
return { code, severity: "error", source: "validate", message, objectId, field };
|
|
3779
|
+
}
|
|
3780
|
+
function warn(code, message, objectId, field) {
|
|
3781
|
+
return { code, severity: "warning", source: "validate", message, objectId, field };
|
|
3782
|
+
}
|
|
3783
|
+
function info(code, message, objectId, field) {
|
|
3784
|
+
return { code, severity: "info", source: "validate", message, objectId, field };
|
|
3785
|
+
}
|
|
3786
|
+
|
|
3787
|
+
// packages/core/dist/draft-parse.js
|
|
3788
|
+
var STRUCTURED_TYPED_BLOCKS = /* @__PURE__ */ new Set([
|
|
3789
|
+
"climate",
|
|
3790
|
+
"habitability",
|
|
3791
|
+
"settlement"
|
|
3792
|
+
]);
|
|
3793
|
+
var DRAFT_OBJECT_FIELD_SPECS = /* @__PURE__ */ new Map();
|
|
3794
|
+
for (const key of [
|
|
3795
|
+
"orbit",
|
|
3796
|
+
"distance",
|
|
3797
|
+
"semiMajor",
|
|
3798
|
+
"eccentricity",
|
|
3799
|
+
"period",
|
|
3800
|
+
"angle",
|
|
3801
|
+
"inclination",
|
|
3802
|
+
"phase",
|
|
3803
|
+
"at",
|
|
3804
|
+
"surface",
|
|
3805
|
+
"free",
|
|
3806
|
+
"kind",
|
|
3807
|
+
"class",
|
|
3808
|
+
"culture",
|
|
3809
|
+
"tags",
|
|
3810
|
+
"color",
|
|
3811
|
+
"image",
|
|
3812
|
+
"hidden",
|
|
3813
|
+
"radius",
|
|
3814
|
+
"mass",
|
|
3815
|
+
"density",
|
|
3816
|
+
"gravity",
|
|
3817
|
+
"temperature",
|
|
3818
|
+
"albedo",
|
|
3819
|
+
"atmosphere",
|
|
3820
|
+
"inner",
|
|
3821
|
+
"outer",
|
|
3822
|
+
"on",
|
|
3823
|
+
"source",
|
|
3824
|
+
"cycle"
|
|
3825
|
+
]) {
|
|
3826
|
+
const schema = getFieldSchema(key);
|
|
3827
|
+
if (schema) {
|
|
3828
|
+
DRAFT_OBJECT_FIELD_SPECS.set(key, {
|
|
3829
|
+
key,
|
|
3830
|
+
version: "2.0",
|
|
3831
|
+
inlineMode: schema.arity === "multiple" ? "multiple" : "single",
|
|
3832
|
+
allowRepeat: false,
|
|
3833
|
+
legacySchema: schema
|
|
3834
|
+
});
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
for (const spec of [
|
|
3838
|
+
{ key: "groups", inlineMode: "multiple", allowRepeat: false },
|
|
3839
|
+
{ key: "epoch", inlineMode: "single", allowRepeat: false },
|
|
3840
|
+
{ key: "referencePlane", inlineMode: "single", allowRepeat: false },
|
|
3841
|
+
{ key: "tidalLock", inlineMode: "single", allowRepeat: false },
|
|
3842
|
+
{ key: "renderLabel", inlineMode: "single", allowRepeat: false },
|
|
3843
|
+
{ key: "renderOrbit", inlineMode: "single", allowRepeat: false },
|
|
3844
|
+
{ key: "renderPriority", inlineMode: "single", allowRepeat: false },
|
|
3845
|
+
{ key: "resonance", inlineMode: "pair", allowRepeat: false },
|
|
3846
|
+
{ key: "derive", inlineMode: "pair", allowRepeat: true },
|
|
3847
|
+
{ key: "validate", inlineMode: "single", allowRepeat: true },
|
|
3848
|
+
{ key: "locked", inlineMode: "multiple", allowRepeat: false },
|
|
3849
|
+
{ key: "tolerance", inlineMode: "pair", allowRepeat: true }
|
|
3850
|
+
]) {
|
|
3851
|
+
DRAFT_OBJECT_FIELD_SPECS.set(spec.key, {
|
|
3852
|
+
key: spec.key,
|
|
3853
|
+
version: "2.1",
|
|
3854
|
+
inlineMode: spec.inlineMode,
|
|
3855
|
+
allowRepeat: spec.allowRepeat
|
|
3856
|
+
});
|
|
3857
|
+
}
|
|
3858
|
+
var DRAFT_OBJECT_FIELD_KEYS = new Set(DRAFT_OBJECT_FIELD_SPECS.keys());
|
|
3859
|
+
function parseWorldOrbitAtlas(source) {
|
|
3860
|
+
return parseAtlasSource(source);
|
|
3861
|
+
}
|
|
3862
|
+
function parseWorldOrbitDraft(source) {
|
|
3863
|
+
return parseAtlasSource(source, "2.0-draft");
|
|
3864
|
+
}
|
|
3865
|
+
function parseAtlasSource(source, forcedOutputVersion) {
|
|
3866
|
+
const prepared = preprocessAtlasSource(source);
|
|
3867
|
+
const lines = prepared.source.split(/\r?\n/);
|
|
3868
|
+
const diagnostics = [];
|
|
3869
|
+
let sawSchemaHeader = false;
|
|
3870
|
+
let sourceSchemaVersion = "2.0";
|
|
3871
|
+
let system = null;
|
|
3872
|
+
let section = null;
|
|
3873
|
+
const objectNodes = [];
|
|
3874
|
+
const groups = [];
|
|
3875
|
+
const relations = [];
|
|
3876
|
+
let sawDefaults = false;
|
|
3877
|
+
let sawAtlas = false;
|
|
3878
|
+
const viewpointIds = /* @__PURE__ */ new Set();
|
|
3879
|
+
const annotationIds = /* @__PURE__ */ new Set();
|
|
3880
|
+
const groupIds = /* @__PURE__ */ new Set();
|
|
3881
|
+
const relationIds = /* @__PURE__ */ new Set();
|
|
3882
|
+
for (let index = 0; index < lines.length; index++) {
|
|
3883
|
+
const rawLine = lines[index];
|
|
3884
|
+
const lineNumber = index + 1;
|
|
3885
|
+
if (!rawLine.trim()) {
|
|
3886
|
+
continue;
|
|
3887
|
+
}
|
|
3888
|
+
const indent = getIndent(rawLine);
|
|
3889
|
+
const tokens = tokenizeLineDetailed(rawLine.slice(indent), {
|
|
3890
|
+
line: lineNumber,
|
|
3891
|
+
columnOffset: indent
|
|
3892
|
+
});
|
|
3893
|
+
if (tokens.length === 0) {
|
|
3894
|
+
continue;
|
|
3895
|
+
}
|
|
3896
|
+
if (!sawSchemaHeader) {
|
|
3897
|
+
sourceSchemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
3898
|
+
sawSchemaHeader = true;
|
|
3899
|
+
if (prepared.comments.length > 0 && sourceSchemaVersion !== "2.1") {
|
|
3900
|
+
diagnostics.push({
|
|
3901
|
+
code: "parse.schema21.commentCompatibility",
|
|
3902
|
+
severity: "warning",
|
|
3903
|
+
source: "parse",
|
|
3904
|
+
message: `Comments require schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
3905
|
+
line: prepared.comments[0].line,
|
|
3906
|
+
column: prepared.comments[0].column
|
|
3907
|
+
});
|
|
3908
|
+
}
|
|
3909
|
+
continue;
|
|
3910
|
+
}
|
|
3911
|
+
if (indent === 0) {
|
|
3912
|
+
section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, { sawDefaults, sawAtlas });
|
|
3913
|
+
if (section.kind === "system") {
|
|
3914
|
+
system = section.system;
|
|
3915
|
+
} else if (section.kind === "defaults") {
|
|
3916
|
+
sawDefaults = true;
|
|
3917
|
+
} else if (section.kind === "atlas") {
|
|
3918
|
+
sawAtlas = true;
|
|
3919
|
+
}
|
|
3920
|
+
continue;
|
|
3921
|
+
}
|
|
3922
|
+
if (!section) {
|
|
3923
|
+
throw new WorldOrbitError("Indented line without parent atlas section", lineNumber, indent + 1);
|
|
3924
|
+
}
|
|
3925
|
+
handleSectionLine(section, indent, tokens, lineNumber);
|
|
3926
|
+
}
|
|
3927
|
+
if (!sawSchemaHeader) {
|
|
3928
|
+
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
|
|
3929
|
+
}
|
|
3930
|
+
const objects = objectNodes.map((node) => normalizeDraftObject(node, sourceSchemaVersion, diagnostics));
|
|
3931
|
+
const outputVersion = forcedOutputVersion ?? (sourceSchemaVersion === "2.0-draft" ? "2.0" : sourceSchemaVersion);
|
|
3932
|
+
const baseDocument = {
|
|
3933
|
+
format: "worldorbit",
|
|
3934
|
+
sourceVersion: "1.0",
|
|
3935
|
+
system,
|
|
3936
|
+
groups,
|
|
3937
|
+
relations,
|
|
3938
|
+
objects,
|
|
3939
|
+
diagnostics
|
|
3940
|
+
};
|
|
3941
|
+
if (outputVersion === "2.0-draft") {
|
|
3942
|
+
const document2 = {
|
|
3943
|
+
...baseDocument,
|
|
3944
|
+
version: "2.0-draft",
|
|
3945
|
+
schemaVersion: "2.0-draft"
|
|
3946
|
+
};
|
|
3947
|
+
document2.diagnostics.push(...collectAtlasDiagnostics(document2, sourceSchemaVersion));
|
|
3948
|
+
return document2;
|
|
3949
|
+
}
|
|
3950
|
+
const document = {
|
|
3951
|
+
...baseDocument,
|
|
3952
|
+
version: outputVersion,
|
|
3953
|
+
schemaVersion: outputVersion
|
|
3954
|
+
};
|
|
3955
|
+
if (sourceSchemaVersion === "2.0-draft") {
|
|
3956
|
+
document.diagnostics.push({
|
|
3957
|
+
code: "load.schema.deprecatedDraft",
|
|
3958
|
+
severity: "warning",
|
|
3959
|
+
source: "upgrade",
|
|
3960
|
+
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
3961
|
+
});
|
|
3962
|
+
}
|
|
3963
|
+
document.diagnostics.push(...collectAtlasDiagnostics(document, sourceSchemaVersion));
|
|
3964
|
+
return document;
|
|
3965
|
+
}
|
|
3966
|
+
function assertDraftSchemaHeader(tokens, line) {
|
|
3967
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1"].includes(tokens[1].value.toLowerCase())) {
|
|
3968
|
+
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
3969
|
+
}
|
|
3970
|
+
const version = tokens[1].value.toLowerCase();
|
|
3971
|
+
return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
3972
|
+
}
|
|
3973
|
+
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, flags) {
|
|
3974
|
+
const keyword = tokens[0]?.value.toLowerCase();
|
|
3975
|
+
switch (keyword) {
|
|
3976
|
+
case "system":
|
|
3977
|
+
if (system) {
|
|
3978
|
+
throw new WorldOrbitError('Atlas section "system" may only appear once', line, tokens[0].column);
|
|
3979
|
+
}
|
|
3980
|
+
return startSystemSection(tokens, line, sourceSchemaVersion, diagnostics);
|
|
3981
|
+
case "defaults":
|
|
3982
|
+
if (!system) {
|
|
3983
|
+
throw new WorldOrbitError('Atlas section "defaults" requires a preceding system declaration', line, tokens[0].column);
|
|
3984
|
+
}
|
|
3985
|
+
if (flags.sawDefaults) {
|
|
3986
|
+
throw new WorldOrbitError('Atlas section "defaults" may only appear once', line, tokens[0].column);
|
|
3987
|
+
}
|
|
3988
|
+
return {
|
|
3989
|
+
kind: "defaults",
|
|
3990
|
+
system,
|
|
3991
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
3992
|
+
};
|
|
3993
|
+
case "atlas":
|
|
3994
|
+
if (!system) {
|
|
3995
|
+
throw new WorldOrbitError('Atlas section "atlas" requires a preceding system declaration', line, tokens[0].column);
|
|
3996
|
+
}
|
|
3997
|
+
if (flags.sawAtlas) {
|
|
3998
|
+
throw new WorldOrbitError('Atlas section "atlas" may only appear once', line, tokens[0].column);
|
|
3999
|
+
}
|
|
4000
|
+
return {
|
|
4001
|
+
kind: "atlas",
|
|
4002
|
+
system,
|
|
4003
|
+
inMetadata: false,
|
|
4004
|
+
metadataIndent: null
|
|
4005
|
+
};
|
|
4006
|
+
case "viewpoint":
|
|
4007
|
+
if (!system) {
|
|
4008
|
+
throw new WorldOrbitError('Atlas section "viewpoint" requires a preceding system declaration', line, tokens[0].column);
|
|
4009
|
+
}
|
|
4010
|
+
return startViewpointSection(tokens, line, system, viewpointIds);
|
|
4011
|
+
case "annotation":
|
|
4012
|
+
if (!system) {
|
|
4013
|
+
throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
|
|
4014
|
+
}
|
|
4015
|
+
return startAnnotationSection(tokens, line, system, annotationIds);
|
|
4016
|
+
case "group":
|
|
4017
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "group", { line, column: tokens[0].column });
|
|
4018
|
+
return startGroupSection(tokens, line, groups, groupIds);
|
|
4019
|
+
case "relation":
|
|
4020
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "relation", { line, column: tokens[0].column });
|
|
4021
|
+
return startRelationSection(tokens, line, relations, relationIds);
|
|
4022
|
+
case "object":
|
|
4023
|
+
return startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes);
|
|
4024
|
+
default:
|
|
4025
|
+
throw new WorldOrbitError(`Unknown atlas section "${tokens[0]?.value ?? ""}"`, line, tokens[0]?.column ?? 1);
|
|
4026
|
+
}
|
|
4027
|
+
}
|
|
4028
|
+
function startSystemSection(tokens, line, sourceSchemaVersion, diagnostics) {
|
|
4029
|
+
if (tokens.length !== 2) {
|
|
4030
|
+
throw new WorldOrbitError("Invalid atlas system declaration", line, tokens[0]?.column ?? 1);
|
|
4031
|
+
}
|
|
4032
|
+
const system = {
|
|
4033
|
+
type: "system",
|
|
4034
|
+
id: tokens[1].value,
|
|
4035
|
+
title: null,
|
|
4036
|
+
description: null,
|
|
4037
|
+
epoch: null,
|
|
4038
|
+
referencePlane: null,
|
|
4039
|
+
defaults: {
|
|
4040
|
+
view: "topdown",
|
|
4041
|
+
scale: null,
|
|
4042
|
+
units: null,
|
|
4043
|
+
preset: null,
|
|
4044
|
+
theme: null
|
|
4045
|
+
},
|
|
4046
|
+
atlasMetadata: {},
|
|
4047
|
+
viewpoints: [],
|
|
4048
|
+
annotations: []
|
|
4049
|
+
};
|
|
4050
|
+
return {
|
|
4051
|
+
kind: "system",
|
|
4052
|
+
system,
|
|
4053
|
+
sourceSchemaVersion,
|
|
4054
|
+
diagnostics,
|
|
4055
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
4056
|
+
};
|
|
4057
|
+
}
|
|
4058
|
+
function startViewpointSection(tokens, line, system, viewpointIds) {
|
|
4059
|
+
if (tokens.length !== 2) {
|
|
4060
|
+
throw new WorldOrbitError("Invalid viewpoint declaration", line, tokens[0]?.column ?? 1);
|
|
4061
|
+
}
|
|
4062
|
+
const id = normalizeIdentifier2(tokens[1].value);
|
|
4063
|
+
if (!id) {
|
|
4064
|
+
throw new WorldOrbitError("Viewpoint id must not be empty", line, tokens[1].column);
|
|
3263
4065
|
}
|
|
3264
4066
|
if (viewpointIds.has(id)) {
|
|
3265
4067
|
throw new WorldOrbitError(`Duplicate viewpoint id "${id}"`, line, tokens[1].column);
|
|
@@ -3315,7 +4117,64 @@ var WorldOrbit = (() => {
|
|
|
3315
4117
|
seenFields: /* @__PURE__ */ new Set()
|
|
3316
4118
|
};
|
|
3317
4119
|
}
|
|
3318
|
-
function
|
|
4120
|
+
function startGroupSection(tokens, line, groups, groupIds) {
|
|
4121
|
+
if (tokens.length !== 2) {
|
|
4122
|
+
throw new WorldOrbitError("Invalid group declaration", line, tokens[0]?.column ?? 1);
|
|
4123
|
+
}
|
|
4124
|
+
const id = normalizeIdentifier2(tokens[1].value);
|
|
4125
|
+
if (!id) {
|
|
4126
|
+
throw new WorldOrbitError("Group id must not be empty", line, tokens[1].column);
|
|
4127
|
+
}
|
|
4128
|
+
if (groupIds.has(id)) {
|
|
4129
|
+
throw new WorldOrbitError(`Duplicate group id "${id}"`, line, tokens[1].column);
|
|
4130
|
+
}
|
|
4131
|
+
const group = {
|
|
4132
|
+
id,
|
|
4133
|
+
label: humanizeIdentifier3(id),
|
|
4134
|
+
summary: "",
|
|
4135
|
+
color: null,
|
|
4136
|
+
tags: [],
|
|
4137
|
+
hidden: false
|
|
4138
|
+
};
|
|
4139
|
+
groups.push(group);
|
|
4140
|
+
groupIds.add(id);
|
|
4141
|
+
return {
|
|
4142
|
+
kind: "group",
|
|
4143
|
+
group,
|
|
4144
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
4145
|
+
};
|
|
4146
|
+
}
|
|
4147
|
+
function startRelationSection(tokens, line, relations, relationIds) {
|
|
4148
|
+
if (tokens.length !== 2) {
|
|
4149
|
+
throw new WorldOrbitError("Invalid relation declaration", line, tokens[0]?.column ?? 1);
|
|
4150
|
+
}
|
|
4151
|
+
const id = normalizeIdentifier2(tokens[1].value);
|
|
4152
|
+
if (!id) {
|
|
4153
|
+
throw new WorldOrbitError("Relation id must not be empty", line, tokens[1].column);
|
|
4154
|
+
}
|
|
4155
|
+
if (relationIds.has(id)) {
|
|
4156
|
+
throw new WorldOrbitError(`Duplicate relation id "${id}"`, line, tokens[1].column);
|
|
4157
|
+
}
|
|
4158
|
+
const relation = {
|
|
4159
|
+
id,
|
|
4160
|
+
from: "",
|
|
4161
|
+
to: "",
|
|
4162
|
+
kind: "",
|
|
4163
|
+
label: null,
|
|
4164
|
+
summary: null,
|
|
4165
|
+
tags: [],
|
|
4166
|
+
color: null,
|
|
4167
|
+
hidden: false
|
|
4168
|
+
};
|
|
4169
|
+
relations.push(relation);
|
|
4170
|
+
relationIds.add(id);
|
|
4171
|
+
return {
|
|
4172
|
+
kind: "relation",
|
|
4173
|
+
relation,
|
|
4174
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
4175
|
+
};
|
|
4176
|
+
}
|
|
4177
|
+
function startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes) {
|
|
3319
4178
|
if (tokens.length < 3) {
|
|
3320
4179
|
throw new WorldOrbitError("Invalid atlas object declaration", line, tokens[0]?.column ?? 1);
|
|
3321
4180
|
}
|
|
@@ -3326,12 +4185,11 @@ var WorldOrbit = (() => {
|
|
|
3326
4185
|
throw new WorldOrbitError(`Unknown object type "${objectTypeToken.value}"`, line, objectTypeToken.column);
|
|
3327
4186
|
}
|
|
3328
4187
|
const objectNode = {
|
|
3329
|
-
type: "object",
|
|
3330
4188
|
objectType,
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
blockFields: [],
|
|
4189
|
+
id: idToken.value,
|
|
4190
|
+
fields: parseInlineObjectFields(tokens.slice(3), line, objectType, sourceSchemaVersion, diagnostics),
|
|
3334
4191
|
infoEntries: [],
|
|
4192
|
+
typedBlockEntries: {},
|
|
3335
4193
|
location: {
|
|
3336
4194
|
line,
|
|
3337
4195
|
column: objectTypeToken.column
|
|
@@ -3341,8 +4199,12 @@ var WorldOrbit = (() => {
|
|
|
3341
4199
|
return {
|
|
3342
4200
|
kind: "object",
|
|
3343
4201
|
objectNode,
|
|
3344
|
-
|
|
3345
|
-
|
|
4202
|
+
sourceSchemaVersion,
|
|
4203
|
+
diagnostics,
|
|
4204
|
+
activeBlock: null,
|
|
4205
|
+
blockIndent: null,
|
|
4206
|
+
seenInfoKeys: /* @__PURE__ */ new Set(),
|
|
4207
|
+
seenTypedBlockKeys: {}
|
|
3346
4208
|
};
|
|
3347
4209
|
}
|
|
3348
4210
|
function handleSectionLine(section, indent, tokens, line) {
|
|
@@ -3362,6 +4224,12 @@ var WorldOrbit = (() => {
|
|
|
3362
4224
|
case "annotation":
|
|
3363
4225
|
applyAnnotationField(section, tokens, line);
|
|
3364
4226
|
return;
|
|
4227
|
+
case "group":
|
|
4228
|
+
applyGroupField(section, tokens, line);
|
|
4229
|
+
return;
|
|
4230
|
+
case "relation":
|
|
4231
|
+
applyRelationField(section, tokens, line);
|
|
4232
|
+
return;
|
|
3365
4233
|
case "object":
|
|
3366
4234
|
applyObjectField(section, indent, tokens, line);
|
|
3367
4235
|
return;
|
|
@@ -3369,10 +4237,35 @@ var WorldOrbit = (() => {
|
|
|
3369
4237
|
}
|
|
3370
4238
|
function applySystemField(section, tokens, line) {
|
|
3371
4239
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
3372
|
-
|
|
3373
|
-
|
|
4240
|
+
const value = joinFieldValue(tokens, line);
|
|
4241
|
+
switch (key) {
|
|
4242
|
+
case "title":
|
|
4243
|
+
section.system.title = value;
|
|
4244
|
+
return;
|
|
4245
|
+
case "description":
|
|
4246
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
4247
|
+
line,
|
|
4248
|
+
column: tokens[0].column
|
|
4249
|
+
});
|
|
4250
|
+
section.system.description = value;
|
|
4251
|
+
return;
|
|
4252
|
+
case "epoch":
|
|
4253
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
4254
|
+
line,
|
|
4255
|
+
column: tokens[0].column
|
|
4256
|
+
});
|
|
4257
|
+
section.system.epoch = value;
|
|
4258
|
+
return;
|
|
4259
|
+
case "referenceplane":
|
|
4260
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, "referencePlane", {
|
|
4261
|
+
line,
|
|
4262
|
+
column: tokens[0].column
|
|
4263
|
+
});
|
|
4264
|
+
section.system.referencePlane = value;
|
|
4265
|
+
return;
|
|
4266
|
+
default:
|
|
4267
|
+
throw new WorldOrbitError(`Unknown system atlas field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3374
4268
|
}
|
|
3375
|
-
section.system.title = joinFieldValue(tokens, line);
|
|
3376
4269
|
}
|
|
3377
4270
|
function applyDefaultsField(section, tokens, line) {
|
|
3378
4271
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
@@ -3403,14 +4296,11 @@ var WorldOrbit = (() => {
|
|
|
3403
4296
|
section.metadataIndent = null;
|
|
3404
4297
|
}
|
|
3405
4298
|
if (section.inMetadata) {
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
const key = tokens[0].value;
|
|
3410
|
-
if (key in section.system.atlasMetadata) {
|
|
3411
|
-
throw new WorldOrbitError(`Duplicate atlas metadata key "${key}"`, line, tokens[0].column);
|
|
4299
|
+
const entry = parseInfoLikeEntry(tokens, line, "Invalid atlas metadata entry");
|
|
4300
|
+
if (entry.key in section.system.atlasMetadata) {
|
|
4301
|
+
throw new WorldOrbitError(`Duplicate atlas metadata key "${entry.key}"`, line, tokens[0].column);
|
|
3412
4302
|
}
|
|
3413
|
-
section.system.atlasMetadata[key] =
|
|
4303
|
+
section.system.atlasMetadata[entry.key] = entry.value;
|
|
3414
4304
|
return;
|
|
3415
4305
|
}
|
|
3416
4306
|
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "metadata") {
|
|
@@ -3483,50 +4373,131 @@ var WorldOrbit = (() => {
|
|
|
3483
4373
|
filter.objectTypes = parseObjectTypeTokens(tokens.slice(1), line);
|
|
3484
4374
|
break;
|
|
3485
4375
|
case "tags":
|
|
3486
|
-
filter.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
3487
|
-
break;
|
|
3488
|
-
case "groups":
|
|
3489
|
-
filter.groupIds = parseTokenList(tokens.slice(1), line, "groups");
|
|
3490
|
-
break;
|
|
4376
|
+
filter.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4377
|
+
break;
|
|
4378
|
+
case "groups":
|
|
4379
|
+
filter.groupIds = parseTokenList(tokens.slice(1), line, "groups");
|
|
4380
|
+
break;
|
|
4381
|
+
default:
|
|
4382
|
+
throw new WorldOrbitError(`Unknown viewpoint filter field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4383
|
+
}
|
|
4384
|
+
section.viewpoint.filter = filter;
|
|
4385
|
+
}
|
|
4386
|
+
function applyAnnotationField(section, tokens, line) {
|
|
4387
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
4388
|
+
switch (key) {
|
|
4389
|
+
case "label":
|
|
4390
|
+
section.annotation.label = joinFieldValue(tokens, line);
|
|
4391
|
+
return;
|
|
4392
|
+
case "target":
|
|
4393
|
+
section.annotation.targetObjectId = joinFieldValue(tokens, line);
|
|
4394
|
+
return;
|
|
4395
|
+
case "body":
|
|
4396
|
+
section.annotation.body = joinFieldValue(tokens, line);
|
|
4397
|
+
return;
|
|
4398
|
+
case "tags":
|
|
4399
|
+
section.annotation.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4400
|
+
return;
|
|
4401
|
+
default:
|
|
4402
|
+
throw new WorldOrbitError(`Unknown annotation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
function applyGroupField(section, tokens, line) {
|
|
4406
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
4407
|
+
switch (key) {
|
|
4408
|
+
case "label":
|
|
4409
|
+
section.group.label = joinFieldValue(tokens, line);
|
|
4410
|
+
return;
|
|
4411
|
+
case "summary":
|
|
4412
|
+
section.group.summary = joinFieldValue(tokens, line);
|
|
4413
|
+
return;
|
|
4414
|
+
case "color":
|
|
4415
|
+
section.group.color = joinFieldValue(tokens, line);
|
|
4416
|
+
return;
|
|
4417
|
+
case "tags":
|
|
4418
|
+
section.group.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4419
|
+
return;
|
|
4420
|
+
case "hidden":
|
|
4421
|
+
section.group.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4422
|
+
line,
|
|
4423
|
+
column: tokens[0].column
|
|
4424
|
+
});
|
|
4425
|
+
return;
|
|
3491
4426
|
default:
|
|
3492
|
-
throw new WorldOrbitError(`Unknown
|
|
4427
|
+
throw new WorldOrbitError(`Unknown group field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3493
4428
|
}
|
|
3494
|
-
section.viewpoint.filter = filter;
|
|
3495
4429
|
}
|
|
3496
|
-
function
|
|
4430
|
+
function applyRelationField(section, tokens, line) {
|
|
3497
4431
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
3498
4432
|
switch (key) {
|
|
3499
|
-
case "
|
|
3500
|
-
section.
|
|
4433
|
+
case "from":
|
|
4434
|
+
section.relation.from = joinFieldValue(tokens, line);
|
|
3501
4435
|
return;
|
|
3502
|
-
case "
|
|
3503
|
-
section.
|
|
4436
|
+
case "to":
|
|
4437
|
+
section.relation.to = joinFieldValue(tokens, line);
|
|
3504
4438
|
return;
|
|
3505
|
-
case "
|
|
3506
|
-
section.
|
|
4439
|
+
case "kind":
|
|
4440
|
+
section.relation.kind = joinFieldValue(tokens, line);
|
|
4441
|
+
return;
|
|
4442
|
+
case "label":
|
|
4443
|
+
section.relation.label = joinFieldValue(tokens, line);
|
|
4444
|
+
return;
|
|
4445
|
+
case "summary":
|
|
4446
|
+
section.relation.summary = joinFieldValue(tokens, line);
|
|
3507
4447
|
return;
|
|
3508
4448
|
case "tags":
|
|
3509
|
-
section.
|
|
4449
|
+
section.relation.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4450
|
+
return;
|
|
4451
|
+
case "color":
|
|
4452
|
+
section.relation.color = joinFieldValue(tokens, line);
|
|
4453
|
+
return;
|
|
4454
|
+
case "hidden":
|
|
4455
|
+
section.relation.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4456
|
+
line,
|
|
4457
|
+
column: tokens[0].column
|
|
4458
|
+
});
|
|
3510
4459
|
return;
|
|
3511
4460
|
default:
|
|
3512
|
-
throw new WorldOrbitError(`Unknown
|
|
4461
|
+
throw new WorldOrbitError(`Unknown relation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3513
4462
|
}
|
|
3514
4463
|
}
|
|
3515
4464
|
function applyObjectField(section, indent, tokens, line) {
|
|
3516
|
-
if (
|
|
3517
|
-
section.
|
|
3518
|
-
section.
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
4465
|
+
if (section.activeBlock && indent <= (section.blockIndent ?? 0)) {
|
|
4466
|
+
section.activeBlock = null;
|
|
4467
|
+
section.blockIndent = null;
|
|
4468
|
+
}
|
|
4469
|
+
if (tokens.length === 1) {
|
|
4470
|
+
const blockName = tokens[0].value.toLowerCase();
|
|
4471
|
+
if (blockName === "info" || STRUCTURED_TYPED_BLOCKS.has(blockName)) {
|
|
4472
|
+
if (blockName !== "info") {
|
|
4473
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, blockName, { line, column: tokens[0].column });
|
|
4474
|
+
}
|
|
4475
|
+
section.activeBlock = blockName;
|
|
4476
|
+
section.blockIndent = indent;
|
|
4477
|
+
return;
|
|
4478
|
+
}
|
|
3524
4479
|
}
|
|
3525
|
-
if (section.
|
|
3526
|
-
|
|
4480
|
+
if (section.activeBlock) {
|
|
4481
|
+
const entry = parseInfoLikeEntry(tokens, line, `Invalid ${section.activeBlock} entry`);
|
|
4482
|
+
if (section.activeBlock === "info") {
|
|
4483
|
+
if (section.seenInfoKeys.has(entry.key)) {
|
|
4484
|
+
throw new WorldOrbitError(`Duplicate info key "${entry.key}"`, line, tokens[0].column);
|
|
4485
|
+
}
|
|
4486
|
+
section.seenInfoKeys.add(entry.key);
|
|
4487
|
+
section.objectNode.infoEntries.push(entry);
|
|
4488
|
+
return;
|
|
4489
|
+
}
|
|
4490
|
+
const typedBlock = section.activeBlock;
|
|
4491
|
+
const seenKeys = section.seenTypedBlockKeys[typedBlock] ?? (section.seenTypedBlockKeys[typedBlock] = /* @__PURE__ */ new Set());
|
|
4492
|
+
if (seenKeys.has(entry.key)) {
|
|
4493
|
+
throw new WorldOrbitError(`Duplicate ${typedBlock} key "${entry.key}"`, line, tokens[0].column);
|
|
4494
|
+
}
|
|
4495
|
+
seenKeys.add(entry.key);
|
|
4496
|
+
const entries = section.objectNode.typedBlockEntries[typedBlock] ?? (section.objectNode.typedBlockEntries[typedBlock] = []);
|
|
4497
|
+
entries.push(entry);
|
|
3527
4498
|
return;
|
|
3528
4499
|
}
|
|
3529
|
-
section.objectNode.
|
|
4500
|
+
section.objectNode.fields.push(parseObjectField(tokens, line, section.objectNode.objectType, section.sourceSchemaVersion, section.diagnostics));
|
|
3530
4501
|
}
|
|
3531
4502
|
function requireUniqueField(tokens, seenFields, line) {
|
|
3532
4503
|
if (tokens.length < 2) {
|
|
@@ -3546,50 +4517,40 @@ var WorldOrbit = (() => {
|
|
|
3546
4517
|
return tokens.slice(1).map((token) => token.value).join(" ").trim();
|
|
3547
4518
|
}
|
|
3548
4519
|
function parseObjectTypeTokens(tokens, line) {
|
|
3549
|
-
|
|
3550
|
-
throw new WorldOrbitError("Missing value for atlas field", line);
|
|
3551
|
-
}
|
|
3552
|
-
return tokens.map((token) => {
|
|
3553
|
-
const value = token.value;
|
|
3554
|
-
if (value !== "star" && value !== "planet" && value !== "moon" && value !== "belt" && value !== "asteroid" && value !== "comet" && value !== "ring" && value !== "structure" && value !== "phenomenon") {
|
|
3555
|
-
throw new WorldOrbitError(`Unknown viewpoint object type "${token.value}"`, line, token.column);
|
|
3556
|
-
}
|
|
3557
|
-
return value;
|
|
3558
|
-
});
|
|
3559
|
-
}
|
|
3560
|
-
function parseTokenList(tokens, line, field) {
|
|
3561
|
-
if (tokens.length === 0) {
|
|
3562
|
-
throw new WorldOrbitError(`Missing value for field "${field}"`, line);
|
|
3563
|
-
}
|
|
3564
|
-
return tokens.map((token) => token.value);
|
|
4520
|
+
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");
|
|
3565
4521
|
}
|
|
3566
4522
|
function parseLayerTokens(tokens, line) {
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
if (rawLayer === "orbits") {
|
|
3575
|
-
next["orbits-back"] = enabled;
|
|
3576
|
-
next["orbits-front"] = enabled;
|
|
4523
|
+
const layers = {};
|
|
4524
|
+
for (const token of parseTokenList(tokens, line, "layers")) {
|
|
4525
|
+
const enabled = !token.startsWith("-") && !token.startsWith("!");
|
|
4526
|
+
const raw = token.replace(/^[-!]+/, "").toLowerCase();
|
|
4527
|
+
if (raw === "orbits") {
|
|
4528
|
+
layers["orbits-back"] = enabled;
|
|
4529
|
+
layers["orbits-front"] = enabled;
|
|
3577
4530
|
continue;
|
|
3578
4531
|
}
|
|
3579
|
-
if (
|
|
3580
|
-
|
|
3581
|
-
continue;
|
|
4532
|
+
if (raw === "background" || raw === "guides" || raw === "orbits-back" || raw === "orbits-front" || raw === "relations" || raw === "objects" || raw === "labels" || raw === "metadata") {
|
|
4533
|
+
layers[raw] = enabled;
|
|
3582
4534
|
}
|
|
3583
|
-
throw new WorldOrbitError(`Unknown layer token "${token.value}"`, line, token.column);
|
|
3584
4535
|
}
|
|
3585
|
-
return
|
|
4536
|
+
return layers;
|
|
4537
|
+
}
|
|
4538
|
+
function parseTokenList(tokens, line, fieldName) {
|
|
4539
|
+
if (tokens.length === 0) {
|
|
4540
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, 1);
|
|
4541
|
+
}
|
|
4542
|
+
const values = tokens.map((token) => token.value).filter(Boolean);
|
|
4543
|
+
if (values.length === 0) {
|
|
4544
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, tokens[0]?.column ?? 1);
|
|
4545
|
+
}
|
|
4546
|
+
return values;
|
|
3586
4547
|
}
|
|
3587
4548
|
function parseProjectionValue(value, line, column) {
|
|
3588
4549
|
const normalized = value.toLowerCase();
|
|
3589
|
-
if (normalized
|
|
3590
|
-
|
|
4550
|
+
if (normalized !== "topdown" && normalized !== "isometric") {
|
|
4551
|
+
throw new WorldOrbitError(`Unknown projection "${value}"`, line, column);
|
|
3591
4552
|
}
|
|
3592
|
-
|
|
4553
|
+
return normalized;
|
|
3593
4554
|
}
|
|
3594
4555
|
function parsePresetValue(value, line, column) {
|
|
3595
4556
|
const normalized = value.toLowerCase();
|
|
@@ -3599,16 +4560,16 @@ var WorldOrbit = (() => {
|
|
|
3599
4560
|
throw new WorldOrbitError(`Unknown render preset "${value}"`, line, column);
|
|
3600
4561
|
}
|
|
3601
4562
|
function parsePositiveNumber2(value, line, column, field) {
|
|
3602
|
-
const parsed =
|
|
3603
|
-
if (
|
|
3604
|
-
throw new WorldOrbitError(`Field "${field}"
|
|
4563
|
+
const parsed = parseFiniteNumber2(value, line, column, field);
|
|
4564
|
+
if (parsed <= 0) {
|
|
4565
|
+
throw new WorldOrbitError(`Field "${field}" must be greater than zero`, line, column);
|
|
3605
4566
|
}
|
|
3606
4567
|
return parsed;
|
|
3607
4568
|
}
|
|
3608
4569
|
function parseFiniteNumber2(value, line, column, field) {
|
|
3609
4570
|
const parsed = Number(value);
|
|
3610
4571
|
if (!Number.isFinite(parsed)) {
|
|
3611
|
-
throw new WorldOrbitError(`
|
|
4572
|
+
throw new WorldOrbitError(`Invalid numeric value "${value}" for "${field}"`, line, column);
|
|
3612
4573
|
}
|
|
3613
4574
|
return parsed;
|
|
3614
4575
|
}
|
|
@@ -3620,28 +4581,43 @@ var WorldOrbit = (() => {
|
|
|
3620
4581
|
groupIds: []
|
|
3621
4582
|
};
|
|
3622
4583
|
}
|
|
3623
|
-
function
|
|
4584
|
+
function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
3624
4585
|
const fields = [];
|
|
3625
4586
|
let index = 0;
|
|
3626
4587
|
while (index < tokens.length) {
|
|
3627
4588
|
const keyToken = tokens[index];
|
|
3628
|
-
const
|
|
3629
|
-
if (!
|
|
4589
|
+
const spec = getDraftObjectFieldSpec(keyToken.value);
|
|
4590
|
+
if (!spec) {
|
|
3630
4591
|
throw new WorldOrbitError(`Unknown field "${keyToken.value}"`, line, keyToken.column);
|
|
3631
4592
|
}
|
|
4593
|
+
if (spec.version === "2.1") {
|
|
4594
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, keyToken.value, {
|
|
4595
|
+
line,
|
|
4596
|
+
column: keyToken.column
|
|
4597
|
+
});
|
|
4598
|
+
}
|
|
3632
4599
|
index++;
|
|
3633
4600
|
const valueTokens = [];
|
|
3634
|
-
if (
|
|
3635
|
-
while (index < tokens.length && !isKnownFieldKey(tokens[index].value)) {
|
|
3636
|
-
valueTokens.push(tokens[index]);
|
|
3637
|
-
index++;
|
|
3638
|
-
}
|
|
3639
|
-
} else {
|
|
4601
|
+
if (spec.inlineMode === "single") {
|
|
3640
4602
|
const nextToken = tokens[index];
|
|
3641
4603
|
if (nextToken) {
|
|
3642
4604
|
valueTokens.push(nextToken);
|
|
3643
4605
|
index++;
|
|
3644
4606
|
}
|
|
4607
|
+
} else if (spec.inlineMode === "pair") {
|
|
4608
|
+
for (let count = 0; count < 2; count++) {
|
|
4609
|
+
const nextToken = tokens[index];
|
|
4610
|
+
if (!nextToken) {
|
|
4611
|
+
break;
|
|
4612
|
+
}
|
|
4613
|
+
valueTokens.push(nextToken);
|
|
4614
|
+
index++;
|
|
4615
|
+
}
|
|
4616
|
+
} else {
|
|
4617
|
+
while (index < tokens.length && !DRAFT_OBJECT_FIELD_KEYS.has(tokens[index].value)) {
|
|
4618
|
+
valueTokens.push(tokens[index]);
|
|
4619
|
+
index++;
|
|
4620
|
+
}
|
|
3645
4621
|
}
|
|
3646
4622
|
if (valueTokens.length === 0) {
|
|
3647
4623
|
throw new WorldOrbitError(`Missing value for field "${keyToken.value}"`, line, keyToken.column);
|
|
@@ -3653,25 +4629,35 @@ var WorldOrbit = (() => {
|
|
|
3653
4629
|
location: { line, column: keyToken.column }
|
|
3654
4630
|
});
|
|
3655
4631
|
}
|
|
4632
|
+
validateDraftObjectFieldCompatibility(fields, objectType);
|
|
3656
4633
|
return fields;
|
|
3657
4634
|
}
|
|
3658
|
-
function
|
|
4635
|
+
function parseObjectField(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
3659
4636
|
if (tokens.length < 2) {
|
|
3660
4637
|
throw new WorldOrbitError("Invalid field line", line, tokens[0]?.column ?? 1);
|
|
3661
4638
|
}
|
|
3662
|
-
|
|
4639
|
+
const spec = getDraftObjectFieldSpec(tokens[0].value);
|
|
4640
|
+
if (!spec) {
|
|
3663
4641
|
throw new WorldOrbitError(`Unknown field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3664
4642
|
}
|
|
3665
|
-
|
|
4643
|
+
if (spec.version === "2.1") {
|
|
4644
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, tokens[0].value, {
|
|
4645
|
+
line,
|
|
4646
|
+
column: tokens[0].column
|
|
4647
|
+
});
|
|
4648
|
+
}
|
|
4649
|
+
const field = {
|
|
3666
4650
|
type: "field",
|
|
3667
4651
|
key: tokens[0].value,
|
|
3668
4652
|
values: tokens.slice(1).map((token) => token.value),
|
|
3669
4653
|
location: { line, column: tokens[0].column }
|
|
3670
4654
|
};
|
|
4655
|
+
validateDraftObjectFieldCompatibility([field], objectType);
|
|
4656
|
+
return field;
|
|
3671
4657
|
}
|
|
3672
|
-
function
|
|
4658
|
+
function parseInfoLikeEntry(tokens, line, errorMessage) {
|
|
3673
4659
|
if (tokens.length < 2) {
|
|
3674
|
-
throw new WorldOrbitError(
|
|
4660
|
+
throw new WorldOrbitError(errorMessage, line, tokens[0]?.column ?? 1);
|
|
3675
4661
|
}
|
|
3676
4662
|
return {
|
|
3677
4663
|
type: "info-entry",
|
|
@@ -3680,23 +4666,356 @@ var WorldOrbit = (() => {
|
|
|
3680
4666
|
location: { line, column: tokens[0].column }
|
|
3681
4667
|
};
|
|
3682
4668
|
}
|
|
3683
|
-
function
|
|
3684
|
-
|
|
4669
|
+
function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
|
|
4670
|
+
const fieldMap = collectDraftFields(node.fields);
|
|
4671
|
+
const placement = extractDraftPlacement(node.objectType, fieldMap);
|
|
4672
|
+
const properties = normalizeDraftProperties(node.objectType, fieldMap);
|
|
4673
|
+
const groups = parseOptionalTokenList(fieldMap.get("groups")?.[0]);
|
|
4674
|
+
const epoch = parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]);
|
|
4675
|
+
const referencePlane = parseOptionalJoinedValue(fieldMap.get("referencePlane")?.[0]);
|
|
4676
|
+
const tidalLock = fieldMap.has("tidalLock") ? parseAtlasBoolean(singleFieldValue2(fieldMap.get("tidalLock")[0]), "tidalLock", fieldMap.get("tidalLock")[0].location) : void 0;
|
|
4677
|
+
const resonance = fieldMap.has("resonance") ? parseResonanceField(fieldMap.get("resonance")[0]) : void 0;
|
|
4678
|
+
const renderHints = extractRenderHints(fieldMap);
|
|
4679
|
+
const deriveRules = fieldMap.get("derive")?.map((field) => parseDeriveField(field));
|
|
4680
|
+
const validationRules = fieldMap.get("validate")?.map((field) => ({
|
|
4681
|
+
rule: singleFieldValue2(field)
|
|
4682
|
+
}));
|
|
4683
|
+
const lockedFields = fieldMap.has("locked") ? [...new Set(fieldMap.get("locked").flatMap((field) => field.values))] : void 0;
|
|
4684
|
+
const tolerances = fieldMap.get("tolerance")?.map((field) => parseToleranceField(field));
|
|
4685
|
+
const typedBlocks = normalizeTypedBlocks(node.typedBlockEntries);
|
|
4686
|
+
const info2 = normalizeInfoEntries(node.infoEntries, "info");
|
|
4687
|
+
const object = {
|
|
4688
|
+
type: node.objectType,
|
|
4689
|
+
id: node.id,
|
|
4690
|
+
properties,
|
|
4691
|
+
placement,
|
|
4692
|
+
info: info2
|
|
4693
|
+
};
|
|
4694
|
+
if (groups.length > 0)
|
|
4695
|
+
object.groups = groups;
|
|
4696
|
+
if (epoch)
|
|
4697
|
+
object.epoch = epoch;
|
|
4698
|
+
if (referencePlane)
|
|
4699
|
+
object.referencePlane = referencePlane;
|
|
4700
|
+
if (tidalLock !== void 0)
|
|
4701
|
+
object.tidalLock = tidalLock;
|
|
4702
|
+
if (resonance)
|
|
4703
|
+
object.resonance = resonance;
|
|
4704
|
+
if (renderHints)
|
|
4705
|
+
object.renderHints = renderHints;
|
|
4706
|
+
if (deriveRules?.length)
|
|
4707
|
+
object.deriveRules = deriveRules;
|
|
4708
|
+
if (validationRules?.length)
|
|
4709
|
+
object.validationRules = validationRules;
|
|
4710
|
+
if (lockedFields?.length)
|
|
4711
|
+
object.lockedFields = lockedFields;
|
|
4712
|
+
if (tolerances?.length)
|
|
4713
|
+
object.tolerances = tolerances;
|
|
4714
|
+
if (typedBlocks && Object.keys(typedBlocks).length > 0)
|
|
4715
|
+
object.typedBlocks = typedBlocks;
|
|
4716
|
+
if (sourceSchemaVersion !== "2.1") {
|
|
4717
|
+
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) {
|
|
4718
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, node.id, node.location);
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
return object;
|
|
3685
4722
|
}
|
|
3686
|
-
function
|
|
3687
|
-
|
|
4723
|
+
function collectDraftFields(fields) {
|
|
4724
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4725
|
+
for (const field of fields) {
|
|
4726
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
4727
|
+
if (!spec) {
|
|
4728
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4729
|
+
}
|
|
4730
|
+
if (!spec.allowRepeat && grouped.has(field.key)) {
|
|
4731
|
+
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
4732
|
+
}
|
|
4733
|
+
const existing = grouped.get(field.key) ?? [];
|
|
4734
|
+
existing.push(field);
|
|
4735
|
+
grouped.set(field.key, existing);
|
|
4736
|
+
}
|
|
4737
|
+
return grouped;
|
|
4738
|
+
}
|
|
4739
|
+
function extractDraftPlacement(objectType, fieldMap) {
|
|
4740
|
+
const orbitField = fieldMap.get("orbit")?.[0];
|
|
4741
|
+
const atField = fieldMap.get("at")?.[0];
|
|
4742
|
+
const surfaceField = fieldMap.get("surface")?.[0];
|
|
4743
|
+
const freeField = fieldMap.get("free")?.[0];
|
|
4744
|
+
const count = [orbitField, atField, surfaceField, freeField].filter(Boolean).length;
|
|
4745
|
+
if (count > 1) {
|
|
4746
|
+
const conflictingField = orbitField ?? atField ?? surfaceField ?? freeField;
|
|
4747
|
+
throw WorldOrbitError.fromLocation("Object has multiple placement modes", conflictingField?.location);
|
|
4748
|
+
}
|
|
4749
|
+
if (orbitField) {
|
|
4750
|
+
return {
|
|
4751
|
+
mode: "orbit",
|
|
4752
|
+
target: singleFieldValue2(orbitField),
|
|
4753
|
+
distance: parseOptionalUnitField(fieldMap.get("distance")?.[0], "distance"),
|
|
4754
|
+
semiMajor: parseOptionalUnitField(fieldMap.get("semiMajor")?.[0], "semiMajor"),
|
|
4755
|
+
eccentricity: parseOptionalNumberField(fieldMap.get("eccentricity")?.[0], "eccentricity"),
|
|
4756
|
+
period: parseOptionalUnitField(fieldMap.get("period")?.[0], "period"),
|
|
4757
|
+
angle: parseOptionalUnitField(fieldMap.get("angle")?.[0], "angle"),
|
|
4758
|
+
inclination: parseOptionalUnitField(fieldMap.get("inclination")?.[0], "inclination"),
|
|
4759
|
+
phase: parseOptionalUnitField(fieldMap.get("phase")?.[0], "phase")
|
|
4760
|
+
};
|
|
4761
|
+
}
|
|
4762
|
+
if (atField) {
|
|
4763
|
+
const target = singleFieldValue2(atField);
|
|
4764
|
+
return {
|
|
4765
|
+
mode: "at",
|
|
4766
|
+
target,
|
|
4767
|
+
reference: parseAtlasAtReference(target, atField.location)
|
|
4768
|
+
};
|
|
4769
|
+
}
|
|
4770
|
+
if (surfaceField) {
|
|
4771
|
+
return {
|
|
4772
|
+
mode: "surface",
|
|
4773
|
+
target: singleFieldValue2(surfaceField)
|
|
4774
|
+
};
|
|
4775
|
+
}
|
|
4776
|
+
if (freeField) {
|
|
4777
|
+
const raw = singleFieldValue2(freeField);
|
|
4778
|
+
const distance = tryParseAtlasUnitValue(raw);
|
|
4779
|
+
return {
|
|
4780
|
+
mode: "free",
|
|
4781
|
+
distance: distance ?? void 0,
|
|
4782
|
+
descriptor: distance ? void 0 : raw
|
|
4783
|
+
};
|
|
4784
|
+
}
|
|
4785
|
+
return null;
|
|
4786
|
+
}
|
|
4787
|
+
function normalizeDraftProperties(objectType, fieldMap) {
|
|
4788
|
+
const properties = {};
|
|
4789
|
+
for (const [key, fields] of fieldMap.entries()) {
|
|
4790
|
+
const field = fields[0];
|
|
4791
|
+
const spec = getDraftObjectFieldSpec(key);
|
|
4792
|
+
if (!field || !spec?.legacySchema || spec.legacySchema.placement) {
|
|
4793
|
+
continue;
|
|
4794
|
+
}
|
|
4795
|
+
ensureAtlasFieldSupported(key, objectType, field.location);
|
|
4796
|
+
properties[key] = normalizeLegacyScalarValue(key, field.values, field.location);
|
|
4797
|
+
}
|
|
4798
|
+
return properties;
|
|
4799
|
+
}
|
|
4800
|
+
function normalizeInfoEntries(entries, label) {
|
|
4801
|
+
const normalized = {};
|
|
4802
|
+
for (const entry of entries) {
|
|
4803
|
+
if (entry.key in normalized) {
|
|
4804
|
+
throw WorldOrbitError.fromLocation(`Duplicate ${label} key "${entry.key}"`, entry.location);
|
|
4805
|
+
}
|
|
4806
|
+
normalized[entry.key] = entry.value;
|
|
4807
|
+
}
|
|
4808
|
+
return normalized;
|
|
4809
|
+
}
|
|
4810
|
+
function normalizeTypedBlocks(typedBlockEntries) {
|
|
4811
|
+
const typedBlocks = {};
|
|
4812
|
+
for (const blockName of Object.keys(typedBlockEntries)) {
|
|
4813
|
+
const entries = typedBlockEntries[blockName];
|
|
4814
|
+
if (entries?.length) {
|
|
4815
|
+
typedBlocks[blockName] = normalizeInfoEntries(entries, blockName);
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4818
|
+
return typedBlocks;
|
|
4819
|
+
}
|
|
4820
|
+
function extractRenderHints(fieldMap) {
|
|
4821
|
+
const renderHints = {};
|
|
4822
|
+
const renderLabelField = fieldMap.get("renderLabel")?.[0];
|
|
4823
|
+
const renderOrbitField = fieldMap.get("renderOrbit")?.[0];
|
|
4824
|
+
const renderPriorityField = fieldMap.get("renderPriority")?.[0];
|
|
4825
|
+
if (renderLabelField) {
|
|
4826
|
+
renderHints.renderLabel = parseAtlasBoolean(singleFieldValue2(renderLabelField), "renderLabel", renderLabelField.location);
|
|
4827
|
+
}
|
|
4828
|
+
if (renderOrbitField) {
|
|
4829
|
+
renderHints.renderOrbit = parseAtlasBoolean(singleFieldValue2(renderOrbitField), "renderOrbit", renderOrbitField.location);
|
|
4830
|
+
}
|
|
4831
|
+
if (renderPriorityField) {
|
|
4832
|
+
renderHints.renderPriority = parseAtlasNumber(singleFieldValue2(renderPriorityField), "renderPriority", renderPriorityField.location);
|
|
4833
|
+
}
|
|
4834
|
+
return Object.keys(renderHints).length > 0 ? renderHints : void 0;
|
|
4835
|
+
}
|
|
4836
|
+
function parseResonanceField(field) {
|
|
4837
|
+
if (field.values.length !== 2) {
|
|
4838
|
+
throw WorldOrbitError.fromLocation('Field "resonance" expects "<targetObjectId> <ratio>"', field.location);
|
|
4839
|
+
}
|
|
4840
|
+
const ratio = field.values[1];
|
|
4841
|
+
if (!/^\d+:\d+$/.test(ratio)) {
|
|
4842
|
+
throw WorldOrbitError.fromLocation(`Invalid resonance ratio "${ratio}"`, field.location);
|
|
4843
|
+
}
|
|
4844
|
+
return {
|
|
4845
|
+
targetObjectId: field.values[0],
|
|
4846
|
+
ratio
|
|
4847
|
+
};
|
|
4848
|
+
}
|
|
4849
|
+
function parseDeriveField(field) {
|
|
4850
|
+
if (field.values.length !== 2) {
|
|
4851
|
+
throw WorldOrbitError.fromLocation('Field "derive" expects "<field> <strategy>"', field.location);
|
|
4852
|
+
}
|
|
4853
|
+
return {
|
|
4854
|
+
field: field.values[0],
|
|
4855
|
+
strategy: field.values[1]
|
|
4856
|
+
};
|
|
4857
|
+
}
|
|
4858
|
+
function parseToleranceField(field) {
|
|
4859
|
+
if (field.values.length !== 2) {
|
|
4860
|
+
throw WorldOrbitError.fromLocation('Field "tolerance" expects "<field> <value>"', field.location);
|
|
4861
|
+
}
|
|
4862
|
+
const rawValue = field.values[1];
|
|
4863
|
+
const unitValue = tryParseAtlasUnitValue(rawValue);
|
|
4864
|
+
const numericValue = Number(rawValue);
|
|
4865
|
+
return {
|
|
4866
|
+
field: field.values[0],
|
|
4867
|
+
value: unitValue ?? (Number.isFinite(numericValue) ? numericValue : rawValue)
|
|
4868
|
+
};
|
|
4869
|
+
}
|
|
4870
|
+
function parseOptionalTokenList(field) {
|
|
4871
|
+
return field ? [...new Set(field.values)] : [];
|
|
4872
|
+
}
|
|
4873
|
+
function parseOptionalJoinedValue(field) {
|
|
4874
|
+
if (!field) {
|
|
4875
|
+
return null;
|
|
4876
|
+
}
|
|
4877
|
+
return field.values.join(" ").trim() || null;
|
|
4878
|
+
}
|
|
4879
|
+
function parseOptionalUnitField(field, key) {
|
|
4880
|
+
return field ? parseAtlasUnitValue(singleFieldValue2(field), field.location, key) : void 0;
|
|
4881
|
+
}
|
|
4882
|
+
function parseOptionalNumberField(field, key) {
|
|
4883
|
+
return field ? parseAtlasNumber(singleFieldValue2(field), key, field.location) : void 0;
|
|
4884
|
+
}
|
|
4885
|
+
function singleFieldValue2(field) {
|
|
4886
|
+
return singleAtlasValue(field.values, field.key, field.location);
|
|
4887
|
+
}
|
|
4888
|
+
function getDraftObjectFieldSpec(key) {
|
|
4889
|
+
return DRAFT_OBJECT_FIELD_SPECS.get(key);
|
|
4890
|
+
}
|
|
4891
|
+
function validateDraftObjectFieldCompatibility(fields, objectType) {
|
|
4892
|
+
for (const field of fields) {
|
|
4893
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
4894
|
+
if (!spec) {
|
|
4895
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4896
|
+
}
|
|
4897
|
+
if (spec.legacySchema) {
|
|
4898
|
+
ensureAtlasFieldSupported(field.key, objectType, field.location);
|
|
4899
|
+
continue;
|
|
4900
|
+
}
|
|
4901
|
+
if ((field.key === "renderLabel" || field.key === "renderOrbit" || field.key === "tidalLock") && field.values.length !== 1) {
|
|
4902
|
+
throw WorldOrbitError.fromLocation(`Field "${field.key}" expects exactly one value`, field.location);
|
|
4903
|
+
}
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
4907
|
+
if (sourceSchemaVersion === "2.1") {
|
|
4908
|
+
return;
|
|
4909
|
+
}
|
|
4910
|
+
diagnostics.push({
|
|
4911
|
+
code: "parse.schema21.featureCompatibility",
|
|
4912
|
+
severity: "warning",
|
|
4913
|
+
source: "parse",
|
|
4914
|
+
message: `Feature "${featureName}" requires schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
4915
|
+
line: location.line,
|
|
4916
|
+
column: location.column
|
|
4917
|
+
});
|
|
4918
|
+
}
|
|
4919
|
+
function preprocessAtlasSource(source) {
|
|
4920
|
+
const chars = [...source];
|
|
4921
|
+
const comments = [];
|
|
4922
|
+
let inString = false;
|
|
4923
|
+
let inBlockComment = false;
|
|
4924
|
+
let blockCommentStart = null;
|
|
4925
|
+
let line = 1;
|
|
4926
|
+
let column = 1;
|
|
4927
|
+
for (let index = 0; index < chars.length; index++) {
|
|
4928
|
+
const ch = chars[index];
|
|
4929
|
+
const next = chars[index + 1];
|
|
4930
|
+
if (inBlockComment) {
|
|
4931
|
+
if (ch === "*" && next === "/") {
|
|
4932
|
+
chars[index] = " ";
|
|
4933
|
+
chars[index + 1] = " ";
|
|
4934
|
+
inBlockComment = false;
|
|
4935
|
+
blockCommentStart = null;
|
|
4936
|
+
index++;
|
|
4937
|
+
column += 2;
|
|
4938
|
+
continue;
|
|
4939
|
+
}
|
|
4940
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
4941
|
+
chars[index] = " ";
|
|
4942
|
+
}
|
|
4943
|
+
if (ch === "\n") {
|
|
4944
|
+
line++;
|
|
4945
|
+
column = 1;
|
|
4946
|
+
} else {
|
|
4947
|
+
column++;
|
|
4948
|
+
}
|
|
4949
|
+
continue;
|
|
4950
|
+
}
|
|
4951
|
+
if (!inString && ch === "/" && next === "*") {
|
|
4952
|
+
comments.push({ kind: "block", line, column });
|
|
4953
|
+
chars[index] = " ";
|
|
4954
|
+
chars[index + 1] = " ";
|
|
4955
|
+
inBlockComment = true;
|
|
4956
|
+
blockCommentStart = { line, column };
|
|
4957
|
+
index++;
|
|
4958
|
+
column += 2;
|
|
4959
|
+
continue;
|
|
4960
|
+
}
|
|
4961
|
+
if (!inString && ch === "#" && !isHexColorLiteral(chars, index)) {
|
|
4962
|
+
comments.push({ kind: "line", line, column });
|
|
4963
|
+
chars[index] = " ";
|
|
4964
|
+
let inner = index + 1;
|
|
4965
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
4966
|
+
chars[inner] = " ";
|
|
4967
|
+
inner++;
|
|
4968
|
+
}
|
|
4969
|
+
column += inner - index;
|
|
4970
|
+
index = inner - 1;
|
|
4971
|
+
continue;
|
|
4972
|
+
}
|
|
4973
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
4974
|
+
inString = !inString;
|
|
4975
|
+
}
|
|
4976
|
+
if (ch === "\n") {
|
|
4977
|
+
line++;
|
|
4978
|
+
column = 1;
|
|
4979
|
+
} else {
|
|
4980
|
+
column++;
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
if (inBlockComment) {
|
|
4984
|
+
throw WorldOrbitError.fromLocation("Unclosed block comment", blockCommentStart ?? void 0);
|
|
4985
|
+
}
|
|
4986
|
+
return {
|
|
4987
|
+
source: chars.join(""),
|
|
4988
|
+
comments
|
|
4989
|
+
};
|
|
4990
|
+
}
|
|
4991
|
+
function isHexColorLiteral(chars, start) {
|
|
4992
|
+
let index = start + 1;
|
|
4993
|
+
let length = 0;
|
|
4994
|
+
while (index < chars.length && /[0-9a-f]/i.test(chars[index] ?? "")) {
|
|
4995
|
+
index++;
|
|
4996
|
+
length++;
|
|
4997
|
+
}
|
|
4998
|
+
if (![3, 4, 6, 8].includes(length)) {
|
|
4999
|
+
return false;
|
|
5000
|
+
}
|
|
5001
|
+
const next = chars[index];
|
|
5002
|
+
return next === void 0 || next === " " || next === " " || next === "\r" || next === "\n";
|
|
3688
5003
|
}
|
|
3689
5004
|
|
|
3690
5005
|
// packages/core/dist/atlas-edit.js
|
|
3691
|
-
function createEmptyAtlasDocument(systemId = "WorldOrbit") {
|
|
5006
|
+
function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "2.0") {
|
|
3692
5007
|
return {
|
|
3693
5008
|
format: "worldorbit",
|
|
3694
|
-
version
|
|
5009
|
+
version,
|
|
5010
|
+
schemaVersion: version,
|
|
3695
5011
|
sourceVersion: "1.0",
|
|
3696
5012
|
system: {
|
|
3697
5013
|
type: "system",
|
|
3698
5014
|
id: systemId,
|
|
3699
5015
|
title: systemId,
|
|
5016
|
+
description: null,
|
|
5017
|
+
epoch: null,
|
|
5018
|
+
referencePlane: null,
|
|
3700
5019
|
defaults: {
|
|
3701
5020
|
view: "topdown",
|
|
3702
5021
|
scale: null,
|
|
@@ -3708,6 +5027,8 @@ var WorldOrbit = (() => {
|
|
|
3708
5027
|
viewpoints: [],
|
|
3709
5028
|
annotations: []
|
|
3710
5029
|
},
|
|
5030
|
+
groups: [],
|
|
5031
|
+
relations: [],
|
|
3711
5032
|
objects: [],
|
|
3712
5033
|
diagnostics: []
|
|
3713
5034
|
};
|
|
@@ -3721,14 +5042,20 @@ var WorldOrbit = (() => {
|
|
|
3721
5042
|
for (const key of Object.keys(document.system.atlasMetadata).sort()) {
|
|
3722
5043
|
paths.push({ kind: "metadata", key });
|
|
3723
5044
|
}
|
|
3724
|
-
for (const viewpoint of [...document.system.viewpoints].sort(
|
|
5045
|
+
for (const viewpoint of [...document.system.viewpoints].sort(compareIdLike2)) {
|
|
3725
5046
|
paths.push({ kind: "viewpoint", id: viewpoint.id });
|
|
3726
5047
|
}
|
|
3727
|
-
for (const annotation of [...document.system.annotations].sort(
|
|
5048
|
+
for (const annotation of [...document.system.annotations].sort(compareIdLike2)) {
|
|
3728
5049
|
paths.push({ kind: "annotation", id: annotation.id });
|
|
3729
5050
|
}
|
|
3730
5051
|
}
|
|
3731
|
-
for (const
|
|
5052
|
+
for (const group of [...document.groups].sort(compareIdLike2)) {
|
|
5053
|
+
paths.push({ kind: "group", id: group.id });
|
|
5054
|
+
}
|
|
5055
|
+
for (const relation of [...document.relations].sort(compareIdLike2)) {
|
|
5056
|
+
paths.push({ kind: "relation", id: relation.id });
|
|
5057
|
+
}
|
|
5058
|
+
for (const object of [...document.objects].sort(compareIdLike2)) {
|
|
3732
5059
|
paths.push({ kind: "object", id: object.id });
|
|
3733
5060
|
}
|
|
3734
5061
|
return paths;
|
|
@@ -3741,12 +5068,16 @@ var WorldOrbit = (() => {
|
|
|
3741
5068
|
return document.system?.defaults ?? null;
|
|
3742
5069
|
case "metadata":
|
|
3743
5070
|
return path.key ? document.system?.atlasMetadata[path.key] ?? null : null;
|
|
5071
|
+
case "group":
|
|
5072
|
+
return path.id ? findGroup(document, path.id) : null;
|
|
3744
5073
|
case "object":
|
|
3745
5074
|
return path.id ? findObject(document, path.id) : null;
|
|
3746
5075
|
case "viewpoint":
|
|
3747
5076
|
return path.id ? findViewpoint(document.system, path.id) : null;
|
|
3748
5077
|
case "annotation":
|
|
3749
5078
|
return path.id ? findAnnotation(document.system, path.id) : null;
|
|
5079
|
+
case "relation":
|
|
5080
|
+
return path.id ? findRelation(document, path.id) : null;
|
|
3750
5081
|
}
|
|
3751
5082
|
}
|
|
3752
5083
|
function upsertAtlasDocumentNode(document, path, value) {
|
|
@@ -3772,6 +5103,12 @@ var WorldOrbit = (() => {
|
|
|
3772
5103
|
system.atlasMetadata[path.key] = String(value);
|
|
3773
5104
|
}
|
|
3774
5105
|
return next;
|
|
5106
|
+
case "group":
|
|
5107
|
+
if (!path.id) {
|
|
5108
|
+
throw new Error('Group updates require an "id" value.');
|
|
5109
|
+
}
|
|
5110
|
+
upsertById(next.groups, value);
|
|
5111
|
+
return next;
|
|
3775
5112
|
case "object":
|
|
3776
5113
|
if (!path.id) {
|
|
3777
5114
|
throw new Error('Object updates require an "id" value.');
|
|
@@ -3790,6 +5127,12 @@ var WorldOrbit = (() => {
|
|
|
3790
5127
|
}
|
|
3791
5128
|
upsertById(system.annotations, value);
|
|
3792
5129
|
return next;
|
|
5130
|
+
case "relation":
|
|
5131
|
+
if (!path.id) {
|
|
5132
|
+
throw new Error('Relation updates require an "id" value.');
|
|
5133
|
+
}
|
|
5134
|
+
upsertById(next.relations, value);
|
|
5135
|
+
return next;
|
|
3793
5136
|
}
|
|
3794
5137
|
}
|
|
3795
5138
|
function updateAtlasDocumentNode(document, path, updater) {
|
|
@@ -3809,6 +5152,11 @@ var WorldOrbit = (() => {
|
|
|
3809
5152
|
next.objects = next.objects.filter((object) => object.id !== path.id);
|
|
3810
5153
|
}
|
|
3811
5154
|
return next;
|
|
5155
|
+
case "group":
|
|
5156
|
+
if (path.id) {
|
|
5157
|
+
next.groups = next.groups.filter((group) => group.id !== path.id);
|
|
5158
|
+
}
|
|
5159
|
+
return next;
|
|
3812
5160
|
case "viewpoint":
|
|
3813
5161
|
if (path.id) {
|
|
3814
5162
|
system.viewpoints = system.viewpoints.filter((viewpoint) => viewpoint.id !== path.id);
|
|
@@ -3819,6 +5167,11 @@ var WorldOrbit = (() => {
|
|
|
3819
5167
|
system.annotations = system.annotations.filter((annotation) => annotation.id !== path.id);
|
|
3820
5168
|
}
|
|
3821
5169
|
return next;
|
|
5170
|
+
case "relation":
|
|
5171
|
+
if (path.id) {
|
|
5172
|
+
next.relations = next.relations.filter((relation) => relation.id !== path.id);
|
|
5173
|
+
}
|
|
5174
|
+
return next;
|
|
3822
5175
|
default:
|
|
3823
5176
|
return next;
|
|
3824
5177
|
}
|
|
@@ -3836,6 +5189,15 @@ var WorldOrbit = (() => {
|
|
|
3836
5189
|
id: diagnostic.objectId
|
|
3837
5190
|
};
|
|
3838
5191
|
}
|
|
5192
|
+
if (diagnostic.field?.startsWith("group.")) {
|
|
5193
|
+
const parts = diagnostic.field.split(".");
|
|
5194
|
+
if (parts[1] && findGroup(document, parts[1])) {
|
|
5195
|
+
return {
|
|
5196
|
+
kind: "group",
|
|
5197
|
+
id: parts[1]
|
|
5198
|
+
};
|
|
5199
|
+
}
|
|
5200
|
+
}
|
|
3839
5201
|
if (diagnostic.field?.startsWith("viewpoint.")) {
|
|
3840
5202
|
const parts = diagnostic.field.split(".");
|
|
3841
5203
|
if (parts[1] && findViewpoint(document.system, parts[1])) {
|
|
@@ -3854,6 +5216,15 @@ var WorldOrbit = (() => {
|
|
|
3854
5216
|
};
|
|
3855
5217
|
}
|
|
3856
5218
|
}
|
|
5219
|
+
if (diagnostic.field?.startsWith("relation.")) {
|
|
5220
|
+
const parts = diagnostic.field.split(".");
|
|
5221
|
+
if (parts[1] && findRelation(document, parts[1])) {
|
|
5222
|
+
return {
|
|
5223
|
+
kind: "relation",
|
|
5224
|
+
id: parts[1]
|
|
5225
|
+
};
|
|
5226
|
+
}
|
|
5227
|
+
}
|
|
3857
5228
|
if (diagnostic.field && diagnostic.field in ensureSystem(document).atlasMetadata) {
|
|
3858
5229
|
return {
|
|
3859
5230
|
kind: "metadata",
|
|
@@ -3863,9 +5234,11 @@ var WorldOrbit = (() => {
|
|
|
3863
5234
|
return null;
|
|
3864
5235
|
}
|
|
3865
5236
|
function validateAtlasDocumentWithDiagnostics(document) {
|
|
3866
|
-
const
|
|
3867
|
-
|
|
3868
|
-
|
|
5237
|
+
const diagnostics = [
|
|
5238
|
+
...document.diagnostics,
|
|
5239
|
+
...collectAtlasDiagnostics(document, document.version)
|
|
5240
|
+
];
|
|
5241
|
+
return resolveAtlasDiagnostics(document, diagnostics);
|
|
3869
5242
|
}
|
|
3870
5243
|
function ensureSystem(document) {
|
|
3871
5244
|
if (document.system) {
|
|
@@ -3877,6 +5250,12 @@ var WorldOrbit = (() => {
|
|
|
3877
5250
|
function findObject(document, objectId) {
|
|
3878
5251
|
return document.objects.find((object) => object.id === objectId) ?? null;
|
|
3879
5252
|
}
|
|
5253
|
+
function findGroup(document, groupId) {
|
|
5254
|
+
return document.groups.find((group) => group.id === groupId) ?? null;
|
|
5255
|
+
}
|
|
5256
|
+
function findRelation(document, relationId) {
|
|
5257
|
+
return document.relations.find((relation) => relation.id === relationId) ?? null;
|
|
5258
|
+
}
|
|
3880
5259
|
function findViewpoint(system, viewpointId) {
|
|
3881
5260
|
return system?.viewpoints.find((viewpoint) => viewpoint.id === viewpointId) ?? null;
|
|
3882
5261
|
}
|
|
@@ -3887,20 +5266,21 @@ var WorldOrbit = (() => {
|
|
|
3887
5266
|
const index = items.findIndex((item) => item.id === value.id);
|
|
3888
5267
|
if (index === -1) {
|
|
3889
5268
|
items.push(value);
|
|
3890
|
-
items.sort(
|
|
5269
|
+
items.sort(compareIdLike2);
|
|
3891
5270
|
return;
|
|
3892
5271
|
}
|
|
3893
5272
|
items[index] = value;
|
|
3894
5273
|
}
|
|
3895
|
-
function
|
|
5274
|
+
function compareIdLike2(left, right) {
|
|
3896
5275
|
return left.id.localeCompare(right.id);
|
|
3897
5276
|
}
|
|
3898
5277
|
|
|
3899
5278
|
// packages/core/dist/load.js
|
|
3900
|
-
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0)?$/i;
|
|
5279
|
+
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;
|
|
5280
|
+
var ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
|
|
3901
5281
|
var LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
|
|
3902
5282
|
function detectWorldOrbitSchemaVersion(source) {
|
|
3903
|
-
for (const line of source.split(/\r?\n/)) {
|
|
5283
|
+
for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
|
|
3904
5284
|
const trimmed = line.trim();
|
|
3905
5285
|
if (!trimmed) {
|
|
3906
5286
|
continue;
|
|
@@ -3908,6 +5288,9 @@ var WorldOrbit = (() => {
|
|
|
3908
5288
|
if (LEGACY_DRAFT_SCHEMA_PATTERN.test(trimmed)) {
|
|
3909
5289
|
return "2.0-draft";
|
|
3910
5290
|
}
|
|
5291
|
+
if (ATLAS_SCHEMA_21_PATTERN.test(trimmed)) {
|
|
5292
|
+
return "2.1";
|
|
5293
|
+
}
|
|
3911
5294
|
if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
|
|
3912
5295
|
return "2.0";
|
|
3913
5296
|
}
|
|
@@ -3915,6 +5298,49 @@ var WorldOrbit = (() => {
|
|
|
3915
5298
|
}
|
|
3916
5299
|
return "1.0";
|
|
3917
5300
|
}
|
|
5301
|
+
function stripCommentsForSchemaDetection(source) {
|
|
5302
|
+
const chars = [...source];
|
|
5303
|
+
let inString = false;
|
|
5304
|
+
let inBlockComment = false;
|
|
5305
|
+
for (let index = 0; index < chars.length; index++) {
|
|
5306
|
+
const ch = chars[index];
|
|
5307
|
+
const next = chars[index + 1];
|
|
5308
|
+
if (inBlockComment) {
|
|
5309
|
+
if (ch === "*" && next === "/") {
|
|
5310
|
+
chars[index] = " ";
|
|
5311
|
+
chars[index + 1] = " ";
|
|
5312
|
+
inBlockComment = false;
|
|
5313
|
+
index++;
|
|
5314
|
+
continue;
|
|
5315
|
+
}
|
|
5316
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
5317
|
+
chars[index] = " ";
|
|
5318
|
+
}
|
|
5319
|
+
continue;
|
|
5320
|
+
}
|
|
5321
|
+
if (!inString && ch === "/" && next === "*") {
|
|
5322
|
+
chars[index] = " ";
|
|
5323
|
+
chars[index + 1] = " ";
|
|
5324
|
+
inBlockComment = true;
|
|
5325
|
+
index++;
|
|
5326
|
+
continue;
|
|
5327
|
+
}
|
|
5328
|
+
if (!inString && ch === "#") {
|
|
5329
|
+
chars[index] = " ";
|
|
5330
|
+
let inner = index + 1;
|
|
5331
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
5332
|
+
chars[inner] = " ";
|
|
5333
|
+
inner++;
|
|
5334
|
+
}
|
|
5335
|
+
index = inner - 1;
|
|
5336
|
+
continue;
|
|
5337
|
+
}
|
|
5338
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
5339
|
+
inString = !inString;
|
|
5340
|
+
}
|
|
5341
|
+
}
|
|
5342
|
+
return chars.join("");
|
|
5343
|
+
}
|
|
3918
5344
|
function loadWorldOrbitSource(source) {
|
|
3919
5345
|
const result = loadWorldOrbitSourceWithDiagnostics(source);
|
|
3920
5346
|
if (!result.ok || !result.value) {
|
|
@@ -3925,36 +5351,36 @@ var WorldOrbit = (() => {
|
|
|
3925
5351
|
}
|
|
3926
5352
|
function loadWorldOrbitSourceWithDiagnostics(source) {
|
|
3927
5353
|
const schemaVersion = detectWorldOrbitSchemaVersion(source);
|
|
3928
|
-
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft") {
|
|
5354
|
+
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1") {
|
|
3929
5355
|
return loadAtlasSourceWithDiagnostics(source, schemaVersion);
|
|
3930
5356
|
}
|
|
3931
5357
|
let ast;
|
|
3932
5358
|
try {
|
|
3933
5359
|
ast = parseWorldOrbit(source);
|
|
3934
|
-
} catch (
|
|
5360
|
+
} catch (error2) {
|
|
3935
5361
|
return {
|
|
3936
5362
|
ok: false,
|
|
3937
5363
|
value: null,
|
|
3938
|
-
diagnostics: [diagnosticFromError(
|
|
5364
|
+
diagnostics: [diagnosticFromError(error2, "parse")]
|
|
3939
5365
|
};
|
|
3940
5366
|
}
|
|
3941
5367
|
let document;
|
|
3942
5368
|
try {
|
|
3943
5369
|
document = normalizeDocument(ast);
|
|
3944
|
-
} catch (
|
|
5370
|
+
} catch (error2) {
|
|
3945
5371
|
return {
|
|
3946
5372
|
ok: false,
|
|
3947
5373
|
value: null,
|
|
3948
|
-
diagnostics: [diagnosticFromError(
|
|
5374
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
3949
5375
|
};
|
|
3950
5376
|
}
|
|
3951
5377
|
try {
|
|
3952
5378
|
validateDocument(document);
|
|
3953
|
-
} catch (
|
|
5379
|
+
} catch (error2) {
|
|
3954
5380
|
return {
|
|
3955
5381
|
ok: false,
|
|
3956
5382
|
value: null,
|
|
3957
|
-
diagnostics: [diagnosticFromError(
|
|
5383
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
3958
5384
|
};
|
|
3959
5385
|
}
|
|
3960
5386
|
return {
|
|
@@ -3974,30 +5400,29 @@ var WorldOrbit = (() => {
|
|
|
3974
5400
|
let atlasDocument;
|
|
3975
5401
|
try {
|
|
3976
5402
|
atlasDocument = parseWorldOrbitAtlas(source);
|
|
3977
|
-
} catch (
|
|
5403
|
+
} catch (error2) {
|
|
3978
5404
|
return {
|
|
3979
5405
|
ok: false,
|
|
3980
5406
|
value: null,
|
|
3981
|
-
diagnostics: [diagnosticFromError(
|
|
5407
|
+
diagnostics: [diagnosticFromError(error2, "parse", "load.atlas.failed")]
|
|
3982
5408
|
};
|
|
3983
5409
|
}
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
document = materializeAtlasDocument(atlasDocument);
|
|
3987
|
-
} catch (error) {
|
|
5410
|
+
const atlasDiagnostics = [...atlasDocument.diagnostics];
|
|
5411
|
+
if (atlasDiagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
3988
5412
|
return {
|
|
3989
5413
|
ok: false,
|
|
3990
5414
|
value: null,
|
|
3991
|
-
diagnostics:
|
|
5415
|
+
diagnostics: atlasDiagnostics
|
|
3992
5416
|
};
|
|
3993
5417
|
}
|
|
5418
|
+
let document;
|
|
3994
5419
|
try {
|
|
3995
|
-
|
|
3996
|
-
} catch (
|
|
5420
|
+
document = materializeAtlasDocument(atlasDocument);
|
|
5421
|
+
} catch (error2) {
|
|
3997
5422
|
return {
|
|
3998
5423
|
ok: false,
|
|
3999
5424
|
value: null,
|
|
4000
|
-
diagnostics: [diagnosticFromError(
|
|
5425
|
+
diagnostics: [diagnosticFromError(error2, "normalize", "load.atlas.materialize.failed")]
|
|
4001
5426
|
};
|
|
4002
5427
|
}
|
|
4003
5428
|
const loaded = {
|
|
@@ -4006,12 +5431,12 @@ var WorldOrbit = (() => {
|
|
|
4006
5431
|
document,
|
|
4007
5432
|
atlasDocument,
|
|
4008
5433
|
draftDocument: atlasDocument,
|
|
4009
|
-
diagnostics:
|
|
5434
|
+
diagnostics: atlasDiagnostics
|
|
4010
5435
|
};
|
|
4011
5436
|
return {
|
|
4012
5437
|
ok: true,
|
|
4013
5438
|
value: loaded,
|
|
4014
|
-
diagnostics:
|
|
5439
|
+
diagnostics: atlasDiagnostics
|
|
4015
5440
|
};
|
|
4016
5441
|
}
|
|
4017
5442
|
|
|
@@ -4077,5 +5502,5 @@ var WorldOrbit = (() => {
|
|
|
4077
5502
|
function stringify(document, options = {}) {
|
|
4078
5503
|
return formatDocument(document, options);
|
|
4079
5504
|
}
|
|
4080
|
-
return __toCommonJS(
|
|
5505
|
+
return __toCommonJS(index_exports);
|
|
4081
5506
|
})();
|