worldorbit 2.5.15 → 2.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/browser/core/dist/index.js +1811 -386
- package/dist/browser/editor/dist/index.js +10534 -0
- package/dist/browser/markdown/dist/index.js +1477 -221
- package/dist/browser/viewer/dist/index.js +1569 -230
- package/dist/unpkg/core/dist/index.js +1814 -389
- package/dist/unpkg/editor/dist/index.js +10559 -0
- package/dist/unpkg/markdown/dist/index.js +1480 -224
- package/dist/unpkg/viewer/dist/index.js +1572 -233
- package/dist/unpkg/worldorbit-editor.min.js +812 -0
- package/package.json +3 -2
- package/packages/editor/dist/editor.d.ts +2 -0
- package/packages/editor/dist/editor.js +3042 -0
- package/packages/editor/dist/index.d.ts +2 -0
- package/packages/editor/dist/index.js +1 -0
- package/packages/editor/dist/types.d.ts +53 -0
- package/packages/editor/dist/types.js +1 -0
- package/packages/markdown/dist/html.d.ts +3 -0
- package/packages/markdown/dist/html.js +57 -0
- package/packages/markdown/dist/index.d.ts +4 -0
- package/packages/markdown/dist/index.js +3 -0
- package/packages/markdown/dist/rehype.d.ts +10 -0
- package/packages/markdown/dist/rehype.js +49 -0
- package/packages/markdown/dist/remark.d.ts +9 -0
- package/packages/markdown/dist/remark.js +28 -0
- package/packages/markdown/dist/types.d.ts +11 -0
- package/packages/markdown/dist/types.js +1 -0
|
@@ -19,8 +19,8 @@ var WorldOrbit = (() => {
|
|
|
19
19
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
20
|
|
|
21
21
|
// packages/markdown/dist/index.js
|
|
22
|
-
var
|
|
23
|
-
__export(
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
24
|
rehypeWorldOrbit: () => rehypeWorldOrbit,
|
|
25
25
|
remarkWorldOrbit: () => remarkWorldOrbit,
|
|
26
26
|
renderWorldOrbitBlock: () => renderWorldOrbitBlock,
|
|
@@ -329,13 +329,13 @@ var WorldOrbit = (() => {
|
|
|
329
329
|
function unitFamilyAllowsUnit(family, unit) {
|
|
330
330
|
switch (family) {
|
|
331
331
|
case "distance":
|
|
332
|
-
return unit === null || ["au", "km", "re", "sol"].includes(unit);
|
|
332
|
+
return unit === null || ["au", "km", "m", "ly", "pc", "kpc", "re", "sol"].includes(unit);
|
|
333
333
|
case "radius":
|
|
334
|
-
return unit === null || ["km", "re", "sol"].includes(unit);
|
|
334
|
+
return unit === null || ["km", "m", "re", "rj", "sol"].includes(unit);
|
|
335
335
|
case "mass":
|
|
336
|
-
return unit === null || ["me", "sol"].includes(unit);
|
|
336
|
+
return unit === null || ["me", "mj", "sol"].includes(unit);
|
|
337
337
|
case "duration":
|
|
338
|
-
return unit === null || ["h", "d", "y"].includes(unit);
|
|
338
|
+
return unit === null || ["s", "min", "h", "d", "y", "ky", "my", "gy"].includes(unit);
|
|
339
339
|
case "angle":
|
|
340
340
|
return unit === null || unit === "deg";
|
|
341
341
|
case "generic":
|
|
@@ -539,7 +539,7 @@ var WorldOrbit = (() => {
|
|
|
539
539
|
}
|
|
540
540
|
|
|
541
541
|
// packages/core/dist/normalize.js
|
|
542
|
-
var UNIT_PATTERN = /^(-?\d+(?:\.\d+)?)(au|km|re|sol|
|
|
542
|
+
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)?$/;
|
|
543
543
|
var BOOLEAN_VALUES = /* @__PURE__ */ new Map([
|
|
544
544
|
["true", true],
|
|
545
545
|
["false", false],
|
|
@@ -564,7 +564,10 @@ var WorldOrbit = (() => {
|
|
|
564
564
|
return {
|
|
565
565
|
format: "worldorbit",
|
|
566
566
|
version: "1.0",
|
|
567
|
+
schemaVersion: "1.0",
|
|
567
568
|
system,
|
|
569
|
+
groups: [],
|
|
570
|
+
relations: [],
|
|
568
571
|
objects
|
|
569
572
|
};
|
|
570
573
|
}
|
|
@@ -574,13 +577,17 @@ var WorldOrbit = (() => {
|
|
|
574
577
|
const fieldMap = collectFields(mergedFields);
|
|
575
578
|
const placement = extractPlacement(node.objectType, fieldMap);
|
|
576
579
|
const properties = normalizeProperties(fieldMap);
|
|
577
|
-
const
|
|
580
|
+
const info2 = normalizeInfo(node.infoEntries);
|
|
578
581
|
if (node.objectType === "system") {
|
|
579
582
|
return {
|
|
580
583
|
type: "system",
|
|
581
584
|
id: node.name,
|
|
585
|
+
title: typeof properties.title === "string" ? properties.title : null,
|
|
586
|
+
description: null,
|
|
587
|
+
epoch: null,
|
|
588
|
+
referencePlane: null,
|
|
582
589
|
properties,
|
|
583
|
-
info
|
|
590
|
+
info: info2
|
|
584
591
|
};
|
|
585
592
|
}
|
|
586
593
|
return {
|
|
@@ -588,7 +595,7 @@ var WorldOrbit = (() => {
|
|
|
588
595
|
id: node.name,
|
|
589
596
|
properties,
|
|
590
597
|
placement,
|
|
591
|
-
info
|
|
598
|
+
info: info2
|
|
592
599
|
};
|
|
593
600
|
}
|
|
594
601
|
function validateFieldCompatibility(objectType, fields) {
|
|
@@ -718,14 +725,14 @@ var WorldOrbit = (() => {
|
|
|
718
725
|
}
|
|
719
726
|
}
|
|
720
727
|
function normalizeInfo(entries) {
|
|
721
|
-
const
|
|
728
|
+
const info2 = {};
|
|
722
729
|
for (const entry of entries) {
|
|
723
|
-
if (entry.key in
|
|
730
|
+
if (entry.key in info2) {
|
|
724
731
|
throw WorldOrbitError.fromLocation(`Duplicate info key "${entry.key}"`, entry.location);
|
|
725
732
|
}
|
|
726
|
-
|
|
733
|
+
info2[entry.key] = entry.value;
|
|
727
734
|
}
|
|
728
|
-
return
|
|
735
|
+
return info2;
|
|
729
736
|
}
|
|
730
737
|
function parseAtReference(target, location) {
|
|
731
738
|
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
@@ -896,37 +903,41 @@ var WorldOrbit = (() => {
|
|
|
896
903
|
}
|
|
897
904
|
|
|
898
905
|
// packages/core/dist/diagnostics.js
|
|
899
|
-
function diagnosticFromError(
|
|
900
|
-
if (
|
|
906
|
+
function diagnosticFromError(error2, source, code = `${source}.failed`) {
|
|
907
|
+
if (error2 instanceof WorldOrbitError) {
|
|
901
908
|
return {
|
|
902
909
|
code,
|
|
903
910
|
severity: "error",
|
|
904
911
|
source,
|
|
905
|
-
message:
|
|
906
|
-
line:
|
|
907
|
-
column:
|
|
912
|
+
message: error2.message,
|
|
913
|
+
line: error2.line,
|
|
914
|
+
column: error2.column
|
|
908
915
|
};
|
|
909
916
|
}
|
|
910
|
-
if (
|
|
917
|
+
if (error2 instanceof Error) {
|
|
911
918
|
return {
|
|
912
919
|
code,
|
|
913
920
|
severity: "error",
|
|
914
921
|
source,
|
|
915
|
-
message:
|
|
922
|
+
message: error2.message
|
|
916
923
|
};
|
|
917
924
|
}
|
|
918
925
|
return {
|
|
919
926
|
code,
|
|
920
927
|
severity: "error",
|
|
921
928
|
source,
|
|
922
|
-
message: String(
|
|
929
|
+
message: String(error2)
|
|
923
930
|
};
|
|
924
931
|
}
|
|
925
932
|
|
|
926
933
|
// packages/core/dist/scene.js
|
|
927
934
|
var AU_IN_KM = 1495978707e-1;
|
|
928
935
|
var EARTH_RADIUS_IN_KM = 6371;
|
|
936
|
+
var JUPITER_RADIUS_IN_KM = 71492;
|
|
929
937
|
var SOLAR_RADIUS_IN_KM = 695700;
|
|
938
|
+
var LY_IN_AU = 63241.077;
|
|
939
|
+
var PC_IN_AU = 206264.806;
|
|
940
|
+
var KPC_IN_AU = 206264806;
|
|
930
941
|
var ISO_FLATTENING = 0.68;
|
|
931
942
|
var MIN_ISO_MINOR_SCALE = 0.2;
|
|
932
943
|
var ARC_SAMPLE_COUNT = 28;
|
|
@@ -1046,8 +1057,10 @@ var WorldOrbit = (() => {
|
|
|
1046
1057
|
const orbitVisuals = orbitDrafts.map((draft) => createOrbitVisual(draft, relationships.groupIds.get(draft.object.id) ?? null));
|
|
1047
1058
|
const leaders = leaderDrafts.map((draft) => createLeaderLine(draft));
|
|
1048
1059
|
const labels = createSceneLabels(objects, height, scaleModel.labelMultiplier);
|
|
1049
|
-
const
|
|
1060
|
+
const relations = createSceneRelations(document2, objects);
|
|
1061
|
+
const layers = createSceneLayers(orbitVisuals, relations, leaders, objects, labels);
|
|
1050
1062
|
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships);
|
|
1063
|
+
const semanticGroups = createSceneSemanticGroups(document2, objects);
|
|
1051
1064
|
const viewpoints = createSceneViewpoints(document2, projection, frame.preset, relationships, objectMap);
|
|
1052
1065
|
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels);
|
|
1053
1066
|
return {
|
|
@@ -1057,7 +1070,7 @@ var WorldOrbit = (() => {
|
|
|
1057
1070
|
renderPreset: frame.preset,
|
|
1058
1071
|
projection,
|
|
1059
1072
|
scaleModel,
|
|
1060
|
-
title: String(document2.system?.properties.title ?? document2.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1073
|
+
title: String(document2.system?.title ?? document2.system?.properties.title ?? document2.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1061
1074
|
subtitle: `${capitalizeLabel(projection)} view - ${capitalizeLabel(layoutPreset)} layout`,
|
|
1062
1075
|
systemId,
|
|
1063
1076
|
viewMode: projection,
|
|
@@ -1073,9 +1086,11 @@ var WorldOrbit = (() => {
|
|
|
1073
1086
|
contentBounds,
|
|
1074
1087
|
layers,
|
|
1075
1088
|
groups,
|
|
1089
|
+
semanticGroups,
|
|
1076
1090
|
viewpoints,
|
|
1077
1091
|
objects,
|
|
1078
1092
|
orbitVisuals,
|
|
1093
|
+
relations,
|
|
1079
1094
|
leaders,
|
|
1080
1095
|
labels
|
|
1081
1096
|
};
|
|
@@ -1174,6 +1189,7 @@ var WorldOrbit = (() => {
|
|
|
1174
1189
|
}
|
|
1175
1190
|
function createSceneObject(position, scaleModel, relationships) {
|
|
1176
1191
|
const { object, x, y, radius, sortKey, anchorX, anchorY } = position;
|
|
1192
|
+
const renderPriority = object.renderHints?.renderPriority ?? 0;
|
|
1177
1193
|
return {
|
|
1178
1194
|
renderId: createRenderId(object.id),
|
|
1179
1195
|
objectId: object.id,
|
|
@@ -1182,11 +1198,12 @@ var WorldOrbit = (() => {
|
|
|
1182
1198
|
ancestorIds: relationships.ancestorIds.get(object.id) ?? [],
|
|
1183
1199
|
childIds: relationships.childIds.get(object.id) ?? [],
|
|
1184
1200
|
groupId: relationships.groupIds.get(object.id) ?? null,
|
|
1201
|
+
semanticGroupIds: [...object.groups ?? []],
|
|
1185
1202
|
x,
|
|
1186
1203
|
y,
|
|
1187
1204
|
radius,
|
|
1188
1205
|
visualRadius: visualExtentForObject(object, radius, scaleModel),
|
|
1189
|
-
sortKey,
|
|
1206
|
+
sortKey: sortKey + renderPriority * 1e-3,
|
|
1190
1207
|
anchorX,
|
|
1191
1208
|
anchorY,
|
|
1192
1209
|
label: object.id,
|
|
@@ -1203,6 +1220,7 @@ var WorldOrbit = (() => {
|
|
|
1203
1220
|
object: draft.object,
|
|
1204
1221
|
parentId: draft.parentId,
|
|
1205
1222
|
groupId,
|
|
1223
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1206
1224
|
kind: draft.kind,
|
|
1207
1225
|
cx: draft.cx,
|
|
1208
1226
|
cy: draft.cy,
|
|
@@ -1214,7 +1232,7 @@ var WorldOrbit = (() => {
|
|
|
1214
1232
|
bandThickness: draft.bandThickness,
|
|
1215
1233
|
frontArcPath: draft.frontArcPath,
|
|
1216
1234
|
backArcPath: draft.backArcPath,
|
|
1217
|
-
hidden: draft.object.properties.hidden === true
|
|
1235
|
+
hidden: draft.object.properties.hidden === true || draft.object.renderHints?.renderOrbit === false
|
|
1218
1236
|
};
|
|
1219
1237
|
}
|
|
1220
1238
|
function createLeaderLine(draft) {
|
|
@@ -1223,6 +1241,7 @@ var WorldOrbit = (() => {
|
|
|
1223
1241
|
objectId: draft.object.id,
|
|
1224
1242
|
object: draft.object,
|
|
1225
1243
|
groupId: draft.groupId,
|
|
1244
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1226
1245
|
x1: draft.x1,
|
|
1227
1246
|
y1: draft.y1,
|
|
1228
1247
|
x2: draft.x2,
|
|
@@ -1234,7 +1253,7 @@ var WorldOrbit = (() => {
|
|
|
1234
1253
|
function createSceneLabels(objects, sceneHeight, labelMultiplier) {
|
|
1235
1254
|
const labels = [];
|
|
1236
1255
|
const occupied = [];
|
|
1237
|
-
const visibleObjects = [...objects].filter((object) => !object.hidden).sort((left, right) => left.sortKey - right.sortKey);
|
|
1256
|
+
const visibleObjects = [...objects].filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false).sort((left, right) => left.sortKey - right.sortKey);
|
|
1238
1257
|
for (const object of visibleObjects) {
|
|
1239
1258
|
const direction = object.y > sceneHeight * 0.62 ? -1 : 1;
|
|
1240
1259
|
const labelHalfWidth = estimateLabelHalfWidth(object, labelMultiplier);
|
|
@@ -1254,6 +1273,7 @@ var WorldOrbit = (() => {
|
|
|
1254
1273
|
objectId: object.objectId,
|
|
1255
1274
|
object: object.object,
|
|
1256
1275
|
groupId: object.groupId,
|
|
1276
|
+
semanticGroupIds: [...object.semanticGroupIds],
|
|
1257
1277
|
label: object.label,
|
|
1258
1278
|
secondaryLabel: object.secondaryLabel,
|
|
1259
1279
|
x: object.x,
|
|
@@ -1266,7 +1286,7 @@ var WorldOrbit = (() => {
|
|
|
1266
1286
|
}
|
|
1267
1287
|
return labels;
|
|
1268
1288
|
}
|
|
1269
|
-
function createSceneLayers(orbitVisuals, leaders, objects, labels) {
|
|
1289
|
+
function createSceneLayers(orbitVisuals, relations, leaders, objects, labels) {
|
|
1270
1290
|
const backOrbitIds = orbitVisuals.filter((visual) => !visual.hidden && Boolean(visual.backArcPath)).map((visual) => visual.renderId);
|
|
1271
1291
|
const frontOrbitIds = orbitVisuals.filter((visual) => !visual.hidden).map((visual) => visual.renderId);
|
|
1272
1292
|
return [
|
|
@@ -1277,6 +1297,10 @@ var WorldOrbit = (() => {
|
|
|
1277
1297
|
},
|
|
1278
1298
|
{ id: "orbits-back", renderIds: backOrbitIds },
|
|
1279
1299
|
{ id: "orbits-front", renderIds: frontOrbitIds },
|
|
1300
|
+
{
|
|
1301
|
+
id: "relations",
|
|
1302
|
+
renderIds: relations.filter((relation) => !relation.hidden).map((relation) => relation.renderId)
|
|
1303
|
+
},
|
|
1280
1304
|
{
|
|
1281
1305
|
id: "objects",
|
|
1282
1306
|
renderIds: objects.filter((object) => !object.hidden).map((object) => object.renderId)
|
|
@@ -1341,6 +1365,36 @@ var WorldOrbit = (() => {
|
|
|
1341
1365
|
}
|
|
1342
1366
|
return [...groups.values()].sort((left, right) => left.label.localeCompare(right.label));
|
|
1343
1367
|
}
|
|
1368
|
+
function createSceneSemanticGroups(document2, objects) {
|
|
1369
|
+
return [...document2.groups].map((group) => ({
|
|
1370
|
+
id: group.id,
|
|
1371
|
+
label: group.label,
|
|
1372
|
+
summary: group.summary,
|
|
1373
|
+
color: group.color,
|
|
1374
|
+
tags: [...group.tags],
|
|
1375
|
+
hidden: group.hidden,
|
|
1376
|
+
objectIds: objects.filter((object) => !object.hidden && object.semanticGroupIds.includes(group.id)).map((object) => object.objectId)
|
|
1377
|
+
})).sort((left, right) => left.label.localeCompare(right.label));
|
|
1378
|
+
}
|
|
1379
|
+
function createSceneRelations(document2, objects) {
|
|
1380
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1381
|
+
return document2.relations.map((relation) => {
|
|
1382
|
+
const from = objectMap.get(relation.from);
|
|
1383
|
+
const to = objectMap.get(relation.to);
|
|
1384
|
+
return {
|
|
1385
|
+
renderId: `${createRenderId(relation.id)}-relation`,
|
|
1386
|
+
relationId: relation.id,
|
|
1387
|
+
relation,
|
|
1388
|
+
fromObjectId: relation.from,
|
|
1389
|
+
toObjectId: relation.to,
|
|
1390
|
+
x1: from?.x ?? 0,
|
|
1391
|
+
y1: from?.y ?? 0,
|
|
1392
|
+
x2: to?.x ?? 0,
|
|
1393
|
+
y2: to?.y ?? 0,
|
|
1394
|
+
hidden: relation.hidden || !from || !to || from.hidden || to.hidden
|
|
1395
|
+
};
|
|
1396
|
+
}).sort((left, right) => left.relation.id.localeCompare(right.relation.id));
|
|
1397
|
+
}
|
|
1344
1398
|
function createSceneViewpoints(document2, projection, preset, relationships, objectMap) {
|
|
1345
1399
|
const generatedOverview = createGeneratedOverviewViewpoint(document2, projection, preset);
|
|
1346
1400
|
const drafts = /* @__PURE__ */ new Map();
|
|
@@ -1358,7 +1412,7 @@ var WorldOrbit = (() => {
|
|
|
1358
1412
|
}
|
|
1359
1413
|
const field = fieldParts.join(".").toLowerCase();
|
|
1360
1414
|
const draft = drafts.get(id) ?? { id };
|
|
1361
|
-
applyViewpointField(draft, field, value, projection, preset, relationships, objectMap);
|
|
1415
|
+
applyViewpointField(draft, field, value, document2, projection, preset, relationships, objectMap);
|
|
1362
1416
|
drafts.set(id, draft);
|
|
1363
1417
|
}
|
|
1364
1418
|
const viewpoints = [...drafts.values()].map((draft) => finalizeViewpointDraft(draft, projection, preset, objectMap)).filter(Boolean);
|
|
@@ -1386,7 +1440,8 @@ var WorldOrbit = (() => {
|
|
|
1386
1440
|
});
|
|
1387
1441
|
}
|
|
1388
1442
|
function createGeneratedOverviewViewpoint(document2, projection, preset) {
|
|
1389
|
-
const
|
|
1443
|
+
const title = document2.system?.title ?? document2.system?.properties.title;
|
|
1444
|
+
const label = title ? `${String(title)} Overview` : "Overview";
|
|
1390
1445
|
return {
|
|
1391
1446
|
id: "overview",
|
|
1392
1447
|
label,
|
|
@@ -1402,7 +1457,7 @@ var WorldOrbit = (() => {
|
|
|
1402
1457
|
generated: true
|
|
1403
1458
|
};
|
|
1404
1459
|
}
|
|
1405
|
-
function applyViewpointField(draft, field, value, projection, preset, relationships, objectMap) {
|
|
1460
|
+
function applyViewpointField(draft, field, value, document2, projection, preset, relationships, objectMap) {
|
|
1406
1461
|
const normalizedValue = value.trim();
|
|
1407
1462
|
switch (field) {
|
|
1408
1463
|
case "label":
|
|
@@ -1469,7 +1524,7 @@ var WorldOrbit = (() => {
|
|
|
1469
1524
|
case "groups":
|
|
1470
1525
|
draft.filter = {
|
|
1471
1526
|
...draft.filter ?? createEmptyViewpointFilter(),
|
|
1472
|
-
groupIds: parseViewpointGroups(normalizedValue, relationships, objectMap)
|
|
1527
|
+
groupIds: parseViewpointGroups(normalizedValue, document2, relationships, objectMap)
|
|
1473
1528
|
};
|
|
1474
1529
|
return;
|
|
1475
1530
|
}
|
|
@@ -1542,7 +1597,7 @@ var WorldOrbit = (() => {
|
|
|
1542
1597
|
next["orbits-front"] = enabled;
|
|
1543
1598
|
continue;
|
|
1544
1599
|
}
|
|
1545
|
-
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1600
|
+
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "relations" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1546
1601
|
next[rawLayer] = enabled;
|
|
1547
1602
|
}
|
|
1548
1603
|
}
|
|
@@ -1551,8 +1606,11 @@ var WorldOrbit = (() => {
|
|
|
1551
1606
|
function parseViewpointObjectTypes(value) {
|
|
1552
1607
|
return splitListValue(value).filter((entry) => entry === "star" || entry === "planet" || entry === "moon" || entry === "belt" || entry === "asteroid" || entry === "comet" || entry === "ring" || entry === "structure" || entry === "phenomenon");
|
|
1553
1608
|
}
|
|
1554
|
-
function parseViewpointGroups(value, relationships, objectMap) {
|
|
1609
|
+
function parseViewpointGroups(value, document2, relationships, objectMap) {
|
|
1555
1610
|
return splitListValue(value).map((entry) => {
|
|
1611
|
+
if (document2.schemaVersion === "2.1" || document2.groups.some((group) => group.id === entry)) {
|
|
1612
|
+
return entry;
|
|
1613
|
+
}
|
|
1556
1614
|
if (entry.startsWith("wo-") && entry.endsWith("-group")) {
|
|
1557
1615
|
return entry;
|
|
1558
1616
|
}
|
|
@@ -1683,8 +1741,9 @@ var WorldOrbit = (() => {
|
|
|
1683
1741
|
}
|
|
1684
1742
|
const orbiting = [...context.orbitChildren.get(object.id) ?? []].sort(compareOrbiting);
|
|
1685
1743
|
const orbitMetricContext = computeOrbitMetricContext(orbiting, parent.radius, context.spacingFactor, context.scaleModel);
|
|
1744
|
+
const orbitRadiiPx = resolveOrbitRadiiPx(orbiting, orbitMetricContext);
|
|
1686
1745
|
orbiting.forEach((child, index) => {
|
|
1687
|
-
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, context);
|
|
1746
|
+
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, orbitRadiiPx[index] ?? orbitMetricContext.innerPx, context);
|
|
1688
1747
|
orbitDrafts.push({
|
|
1689
1748
|
object: child,
|
|
1690
1749
|
parentId: object.id,
|
|
@@ -1758,7 +1817,8 @@ var WorldOrbit = (() => {
|
|
|
1758
1817
|
metricSpread: 0,
|
|
1759
1818
|
innerPx,
|
|
1760
1819
|
stepPx,
|
|
1761
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
1820
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
1821
|
+
minimumGapPx: stepPx * 0.42
|
|
1762
1822
|
};
|
|
1763
1823
|
}
|
|
1764
1824
|
const minMetric = Math.min(...presentMetrics);
|
|
@@ -1771,10 +1831,11 @@ var WorldOrbit = (() => {
|
|
|
1771
1831
|
metricSpread,
|
|
1772
1832
|
innerPx,
|
|
1773
1833
|
stepPx,
|
|
1774
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
1834
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
1835
|
+
minimumGapPx: stepPx * 0.42
|
|
1775
1836
|
};
|
|
1776
1837
|
}
|
|
1777
|
-
function resolveOrbitGeometry(object, index, count, parent, metricContext, context) {
|
|
1838
|
+
function resolveOrbitGeometry(object, index, count, parent, metricContext, orbitRadiusPx, context) {
|
|
1778
1839
|
const placement = object.placement;
|
|
1779
1840
|
const band = object.type === "belt" || object.type === "ring";
|
|
1780
1841
|
if (!placement || placement.mode !== "orbit") {
|
|
@@ -1792,7 +1853,7 @@ var WorldOrbit = (() => {
|
|
|
1792
1853
|
};
|
|
1793
1854
|
}
|
|
1794
1855
|
const eccentricity = clampNumber(typeof placement.eccentricity === "number" ? placement.eccentricity : 0, 0, 0.92);
|
|
1795
|
-
const semiMajor =
|
|
1856
|
+
const semiMajor = orbitRadiusPx;
|
|
1796
1857
|
const baseMinor = Math.max(semiMajor * Math.sqrt(1 - eccentricity * eccentricity), semiMajor * 0.18);
|
|
1797
1858
|
const inclinationDeg = unitValueToDegrees(placement.inclination) ?? 0;
|
|
1798
1859
|
const inclinationScale = context.projection === "isometric" ? Math.max(MIN_ISO_MINOR_SCALE, Math.cos(degreesToRadians(inclinationDeg))) * ISO_FLATTENING : 1;
|
|
@@ -1822,15 +1883,19 @@ var WorldOrbit = (() => {
|
|
|
1822
1883
|
objectY: objectPoint.y
|
|
1823
1884
|
};
|
|
1824
1885
|
}
|
|
1825
|
-
function resolveOrbitRadiusPx(
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1886
|
+
function resolveOrbitRadiusPx(metric, metricContext) {
|
|
1887
|
+
return metricContext.innerPx + metricContext.stepPx * log2(Math.max(metric, 0) + 1);
|
|
1888
|
+
}
|
|
1889
|
+
function resolveOrbitRadiiPx(objects, metricContext) {
|
|
1890
|
+
const radii = [];
|
|
1891
|
+
objects.forEach((object, index) => {
|
|
1892
|
+
const metric = orbitMetric(object);
|
|
1893
|
+
const fallbackRadius = metricContext.innerPx + index * metricContext.stepPx;
|
|
1894
|
+
const baseRadius = metric === null ? fallbackRadius : resolveOrbitRadiusPx(metric, metricContext);
|
|
1895
|
+
const minimumRadius = index === 0 ? metricContext.innerPx : (radii[index - 1] ?? metricContext.innerPx) + metricContext.minimumGapPx;
|
|
1896
|
+
radii.push(Math.max(baseRadius, minimumRadius));
|
|
1897
|
+
});
|
|
1898
|
+
return radii;
|
|
1834
1899
|
}
|
|
1835
1900
|
function orbitMetric(object) {
|
|
1836
1901
|
if (!object.placement || object.placement.mode !== "orbit") {
|
|
@@ -1838,6 +1903,9 @@ var WorldOrbit = (() => {
|
|
|
1838
1903
|
}
|
|
1839
1904
|
return toDistanceMetric(object.placement.semiMajor ?? object.placement.distance ?? null);
|
|
1840
1905
|
}
|
|
1906
|
+
function log2(value) {
|
|
1907
|
+
return Math.log(value) / Math.log(2);
|
|
1908
|
+
}
|
|
1841
1909
|
function resolveOrbitPhase(phase, index, count) {
|
|
1842
1910
|
const degreeValue = phase ? unitValueToDegrees(phase) : null;
|
|
1843
1911
|
if (degreeValue !== null) {
|
|
@@ -2161,8 +2229,18 @@ var WorldOrbit = (() => {
|
|
|
2161
2229
|
return value.value;
|
|
2162
2230
|
case "km":
|
|
2163
2231
|
return value.value / AU_IN_KM;
|
|
2232
|
+
case "m":
|
|
2233
|
+
return value.value / 1e3 / AU_IN_KM;
|
|
2234
|
+
case "ly":
|
|
2235
|
+
return value.value * LY_IN_AU;
|
|
2236
|
+
case "pc":
|
|
2237
|
+
return value.value * PC_IN_AU;
|
|
2238
|
+
case "kpc":
|
|
2239
|
+
return value.value * KPC_IN_AU;
|
|
2164
2240
|
case "re":
|
|
2165
2241
|
return value.value * EARTH_RADIUS_IN_KM / AU_IN_KM;
|
|
2242
|
+
case "rj":
|
|
2243
|
+
return value.value * JUPITER_RADIUS_IN_KM / AU_IN_KM;
|
|
2166
2244
|
case "sol":
|
|
2167
2245
|
return value.value * SOLAR_RADIUS_IN_KM / AU_IN_KM;
|
|
2168
2246
|
default:
|
|
@@ -2302,19 +2380,37 @@ var WorldOrbit = (() => {
|
|
|
2302
2380
|
const system = document2.system ? {
|
|
2303
2381
|
type: "system",
|
|
2304
2382
|
id: document2.system.id,
|
|
2383
|
+
title: document2.system.title,
|
|
2384
|
+
description: document2.system.description,
|
|
2385
|
+
epoch: document2.system.epoch,
|
|
2386
|
+
referencePlane: document2.system.referencePlane,
|
|
2305
2387
|
properties: materializeDraftSystemProperties(document2.system),
|
|
2306
2388
|
info: materializeDraftSystemInfo(document2.system)
|
|
2307
2389
|
} : null;
|
|
2308
2390
|
return {
|
|
2309
2391
|
format: "worldorbit",
|
|
2310
2392
|
version: "1.0",
|
|
2393
|
+
schemaVersion: document2.version,
|
|
2311
2394
|
system,
|
|
2395
|
+
groups: structuredClone(document2.groups ?? []),
|
|
2396
|
+
relations: structuredClone(document2.relations ?? []),
|
|
2312
2397
|
objects: document2.objects.map(cloneWorldOrbitObject)
|
|
2313
2398
|
};
|
|
2314
2399
|
}
|
|
2315
2400
|
function cloneWorldOrbitObject(object) {
|
|
2316
2401
|
return {
|
|
2317
2402
|
...object,
|
|
2403
|
+
groups: object.groups ? [...object.groups] : void 0,
|
|
2404
|
+
resonance: object.resonance ? { ...object.resonance } : object.resonance,
|
|
2405
|
+
renderHints: object.renderHints ? { ...object.renderHints } : object.renderHints,
|
|
2406
|
+
deriveRules: object.deriveRules ? object.deriveRules.map((rule) => ({ ...rule })) : void 0,
|
|
2407
|
+
validationRules: object.validationRules ? object.validationRules.map((rule) => ({ ...rule })) : void 0,
|
|
2408
|
+
lockedFields: object.lockedFields ? [...object.lockedFields] : void 0,
|
|
2409
|
+
tolerances: object.tolerances ? object.tolerances.map((entry) => ({
|
|
2410
|
+
field: entry.field,
|
|
2411
|
+
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
|
|
2412
|
+
})) : void 0,
|
|
2413
|
+
typedBlocks: object.typedBlocks ? Object.fromEntries(Object.entries(object.typedBlocks).map(([key, block]) => [key, { ...block ?? {} }])) : void 0,
|
|
2318
2414
|
properties: cloneProperties(object.properties),
|
|
2319
2415
|
placement: object.placement ? structuredClone(object.placement) : null,
|
|
2320
2416
|
info: { ...object.info }
|
|
@@ -2350,71 +2446,80 @@ var WorldOrbit = (() => {
|
|
|
2350
2446
|
if (system.defaults.units) {
|
|
2351
2447
|
properties.units = system.defaults.units;
|
|
2352
2448
|
}
|
|
2449
|
+
if (system.description) {
|
|
2450
|
+
properties.description = system.description;
|
|
2451
|
+
}
|
|
2452
|
+
if (system.epoch) {
|
|
2453
|
+
properties.epoch = system.epoch;
|
|
2454
|
+
}
|
|
2455
|
+
if (system.referencePlane) {
|
|
2456
|
+
properties.referencePlane = system.referencePlane;
|
|
2457
|
+
}
|
|
2353
2458
|
return properties;
|
|
2354
2459
|
}
|
|
2355
2460
|
function materializeDraftSystemInfo(system) {
|
|
2356
|
-
const
|
|
2461
|
+
const info2 = {
|
|
2357
2462
|
...system.atlasMetadata
|
|
2358
2463
|
};
|
|
2359
2464
|
if (system.defaults.theme) {
|
|
2360
|
-
|
|
2465
|
+
info2["atlas.theme"] = system.defaults.theme;
|
|
2361
2466
|
}
|
|
2362
2467
|
for (const viewpoint of system.viewpoints) {
|
|
2363
2468
|
const prefix = `viewpoint.${viewpoint.id}`;
|
|
2364
|
-
|
|
2469
|
+
info2[`${prefix}.label`] = viewpoint.label;
|
|
2365
2470
|
if (viewpoint.summary) {
|
|
2366
|
-
|
|
2471
|
+
info2[`${prefix}.summary`] = viewpoint.summary;
|
|
2367
2472
|
}
|
|
2368
2473
|
if (viewpoint.focusObjectId) {
|
|
2369
|
-
|
|
2474
|
+
info2[`${prefix}.focus`] = viewpoint.focusObjectId;
|
|
2370
2475
|
}
|
|
2371
2476
|
if (viewpoint.selectedObjectId) {
|
|
2372
|
-
|
|
2477
|
+
info2[`${prefix}.select`] = viewpoint.selectedObjectId;
|
|
2373
2478
|
}
|
|
2374
2479
|
if (viewpoint.projection) {
|
|
2375
|
-
|
|
2480
|
+
info2[`${prefix}.projection`] = viewpoint.projection;
|
|
2376
2481
|
}
|
|
2377
2482
|
if (viewpoint.preset) {
|
|
2378
|
-
|
|
2483
|
+
info2[`${prefix}.preset`] = viewpoint.preset;
|
|
2379
2484
|
}
|
|
2380
2485
|
if (viewpoint.zoom !== null) {
|
|
2381
|
-
|
|
2486
|
+
info2[`${prefix}.zoom`] = String(viewpoint.zoom);
|
|
2382
2487
|
}
|
|
2383
2488
|
if (viewpoint.rotationDeg !== 0) {
|
|
2384
|
-
|
|
2489
|
+
info2[`${prefix}.rotation`] = String(viewpoint.rotationDeg);
|
|
2385
2490
|
}
|
|
2386
2491
|
const serializedLayers = serializeViewpointLayers(viewpoint.layers);
|
|
2387
2492
|
if (serializedLayers) {
|
|
2388
|
-
|
|
2493
|
+
info2[`${prefix}.layers`] = serializedLayers;
|
|
2389
2494
|
}
|
|
2390
2495
|
if (viewpoint.filter?.query) {
|
|
2391
|
-
|
|
2496
|
+
info2[`${prefix}.query`] = viewpoint.filter.query;
|
|
2392
2497
|
}
|
|
2393
2498
|
if ((viewpoint.filter?.objectTypes.length ?? 0) > 0) {
|
|
2394
|
-
|
|
2499
|
+
info2[`${prefix}.types`] = viewpoint.filter?.objectTypes.join(" ") ?? "";
|
|
2395
2500
|
}
|
|
2396
2501
|
if ((viewpoint.filter?.tags.length ?? 0) > 0) {
|
|
2397
|
-
|
|
2502
|
+
info2[`${prefix}.tags`] = viewpoint.filter?.tags.join(" ") ?? "";
|
|
2398
2503
|
}
|
|
2399
2504
|
if ((viewpoint.filter?.groupIds.length ?? 0) > 0) {
|
|
2400
|
-
|
|
2505
|
+
info2[`${prefix}.groups`] = viewpoint.filter?.groupIds.join(" ") ?? "";
|
|
2401
2506
|
}
|
|
2402
2507
|
}
|
|
2403
2508
|
for (const annotation of system.annotations) {
|
|
2404
2509
|
const prefix = `annotation.${annotation.id}`;
|
|
2405
|
-
|
|
2510
|
+
info2[`${prefix}.label`] = annotation.label;
|
|
2406
2511
|
if (annotation.targetObjectId) {
|
|
2407
|
-
|
|
2512
|
+
info2[`${prefix}.target`] = annotation.targetObjectId;
|
|
2408
2513
|
}
|
|
2409
|
-
|
|
2514
|
+
info2[`${prefix}.body`] = annotation.body;
|
|
2410
2515
|
if (annotation.tags.length > 0) {
|
|
2411
|
-
|
|
2516
|
+
info2[`${prefix}.tags`] = annotation.tags.join(" ");
|
|
2412
2517
|
}
|
|
2413
2518
|
if (annotation.sourceObjectId) {
|
|
2414
|
-
|
|
2519
|
+
info2[`${prefix}.source`] = annotation.sourceObjectId;
|
|
2415
2520
|
}
|
|
2416
2521
|
}
|
|
2417
|
-
return
|
|
2522
|
+
return info2;
|
|
2418
2523
|
}
|
|
2419
2524
|
function serializeViewpointLayers(layers) {
|
|
2420
2525
|
const tokens = [];
|
|
@@ -2423,7 +2528,7 @@ var WorldOrbit = (() => {
|
|
|
2423
2528
|
if (orbitFront !== void 0 || orbitBack !== void 0) {
|
|
2424
2529
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
2425
2530
|
}
|
|
2426
|
-
for (const key of ["background", "guides", "objects", "labels", "metadata"]) {
|
|
2531
|
+
for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
|
|
2427
2532
|
if (layers[key] !== void 0) {
|
|
2428
2533
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
2429
2534
|
}
|
|
@@ -2431,21 +2536,530 @@ var WorldOrbit = (() => {
|
|
|
2431
2536
|
return tokens.join(" ");
|
|
2432
2537
|
}
|
|
2433
2538
|
|
|
2539
|
+
// packages/core/dist/atlas-utils.js
|
|
2540
|
+
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)?$/;
|
|
2541
|
+
var BOOLEAN_VALUES2 = /* @__PURE__ */ new Map([
|
|
2542
|
+
["true", true],
|
|
2543
|
+
["false", false],
|
|
2544
|
+
["yes", true],
|
|
2545
|
+
["no", false]
|
|
2546
|
+
]);
|
|
2547
|
+
var URL_SCHEME_PATTERN2 = /^[A-Za-z][A-Za-z0-9+.-]*:/;
|
|
2548
|
+
function normalizeIdentifier(value) {
|
|
2549
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2550
|
+
}
|
|
2551
|
+
function humanizeIdentifier2(value) {
|
|
2552
|
+
return value.split(/[-_]+/).filter(Boolean).map((segment) => segment[0].toUpperCase() + segment.slice(1)).join(" ");
|
|
2553
|
+
}
|
|
2554
|
+
function parseAtlasUnitValue(input, location, fieldKey) {
|
|
2555
|
+
const match = input.match(UNIT_PATTERN2);
|
|
2556
|
+
if (!match) {
|
|
2557
|
+
throw WorldOrbitError.fromLocation(`Invalid unit value "${input}"`, location);
|
|
2558
|
+
}
|
|
2559
|
+
const unitValue = {
|
|
2560
|
+
value: Number(match[1]),
|
|
2561
|
+
unit: match[2] ?? null
|
|
2562
|
+
};
|
|
2563
|
+
if (fieldKey) {
|
|
2564
|
+
const schema = getFieldSchema(fieldKey);
|
|
2565
|
+
if (schema?.unitFamily && !unitFamilyAllowsUnit(schema.unitFamily, unitValue.unit)) {
|
|
2566
|
+
throw WorldOrbitError.fromLocation(`Unit "${unitValue.unit ?? "none"}" is not valid for "${fieldKey}"`, location);
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
return unitValue;
|
|
2570
|
+
}
|
|
2571
|
+
function tryParseAtlasUnitValue(input) {
|
|
2572
|
+
const match = input.match(UNIT_PATTERN2);
|
|
2573
|
+
if (!match) {
|
|
2574
|
+
return null;
|
|
2575
|
+
}
|
|
2576
|
+
return {
|
|
2577
|
+
value: Number(match[1]),
|
|
2578
|
+
unit: match[2] ?? null
|
|
2579
|
+
};
|
|
2580
|
+
}
|
|
2581
|
+
function parseAtlasNumber(input, key, location) {
|
|
2582
|
+
const value = Number(input);
|
|
2583
|
+
if (!Number.isFinite(value)) {
|
|
2584
|
+
throw WorldOrbitError.fromLocation(`Invalid numeric value "${input}" for "${key}"`, location);
|
|
2585
|
+
}
|
|
2586
|
+
return value;
|
|
2587
|
+
}
|
|
2588
|
+
function parseAtlasBoolean(input, key, location) {
|
|
2589
|
+
const parsed = BOOLEAN_VALUES2.get(input.toLowerCase());
|
|
2590
|
+
if (parsed === void 0) {
|
|
2591
|
+
throw WorldOrbitError.fromLocation(`Invalid boolean value "${input}" for "${key}"`, location);
|
|
2592
|
+
}
|
|
2593
|
+
return parsed;
|
|
2594
|
+
}
|
|
2595
|
+
function parseAtlasAtReference(target, location) {
|
|
2596
|
+
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
2597
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
2598
|
+
}
|
|
2599
|
+
const pairedMatch = target.match(/^([A-Za-z0-9._-]+)-([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
2600
|
+
if (pairedMatch) {
|
|
2601
|
+
return {
|
|
2602
|
+
kind: "lagrange",
|
|
2603
|
+
primary: pairedMatch[1],
|
|
2604
|
+
secondary: pairedMatch[2],
|
|
2605
|
+
point: pairedMatch[3]
|
|
2606
|
+
};
|
|
2607
|
+
}
|
|
2608
|
+
const simpleMatch = target.match(/^([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
2609
|
+
if (simpleMatch) {
|
|
2610
|
+
return {
|
|
2611
|
+
kind: "lagrange",
|
|
2612
|
+
primary: simpleMatch[1],
|
|
2613
|
+
secondary: null,
|
|
2614
|
+
point: simpleMatch[2]
|
|
2615
|
+
};
|
|
2616
|
+
}
|
|
2617
|
+
if (/^[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
2618
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
2619
|
+
}
|
|
2620
|
+
const anchorMatch = target.match(/^([A-Za-z0-9._-]+):([A-Za-z0-9._-]+)$/);
|
|
2621
|
+
if (anchorMatch) {
|
|
2622
|
+
return {
|
|
2623
|
+
kind: "anchor",
|
|
2624
|
+
objectId: anchorMatch[1],
|
|
2625
|
+
anchor: anchorMatch[2]
|
|
2626
|
+
};
|
|
2627
|
+
}
|
|
2628
|
+
return {
|
|
2629
|
+
kind: "named",
|
|
2630
|
+
name: target
|
|
2631
|
+
};
|
|
2632
|
+
}
|
|
2633
|
+
function validateAtlasImageSource(value, location) {
|
|
2634
|
+
if (!value) {
|
|
2635
|
+
throw WorldOrbitError.fromLocation('Field "image" must not be empty', location);
|
|
2636
|
+
}
|
|
2637
|
+
if (value.startsWith("//")) {
|
|
2638
|
+
throw WorldOrbitError.fromLocation('Field "image" must use a relative path, root-relative path, or an http/https URL', location);
|
|
2639
|
+
}
|
|
2640
|
+
const schemeMatch = value.match(URL_SCHEME_PATTERN2);
|
|
2641
|
+
if (!schemeMatch) {
|
|
2642
|
+
return;
|
|
2643
|
+
}
|
|
2644
|
+
const scheme = schemeMatch[0].slice(0, -1).toLowerCase();
|
|
2645
|
+
if (scheme !== "http" && scheme !== "https") {
|
|
2646
|
+
throw WorldOrbitError.fromLocation(`Field "image" does not support the "${scheme}" scheme`, location);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
function normalizeLegacyScalarValue(key, values, location) {
|
|
2650
|
+
const schema = getFieldSchema(key);
|
|
2651
|
+
if (!schema) {
|
|
2652
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
2653
|
+
}
|
|
2654
|
+
if (schema.arity === "single" && values.length !== 1) {
|
|
2655
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
2656
|
+
}
|
|
2657
|
+
switch (schema.kind) {
|
|
2658
|
+
case "list":
|
|
2659
|
+
return values;
|
|
2660
|
+
case "boolean":
|
|
2661
|
+
return parseAtlasBoolean(singleAtlasValue(values, key, location), key, location);
|
|
2662
|
+
case "number":
|
|
2663
|
+
return parseAtlasNumber(singleAtlasValue(values, key, location), key, location);
|
|
2664
|
+
case "unit":
|
|
2665
|
+
return parseAtlasUnitValue(singleAtlasValue(values, key, location), location, key);
|
|
2666
|
+
case "string": {
|
|
2667
|
+
const value = values.join(" ").trim();
|
|
2668
|
+
if (key === "image") {
|
|
2669
|
+
validateAtlasImageSource(value, location);
|
|
2670
|
+
}
|
|
2671
|
+
return value;
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
function ensureAtlasFieldSupported(key, objectType, location) {
|
|
2676
|
+
const schema = getFieldSchema(key);
|
|
2677
|
+
if (!schema) {
|
|
2678
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
2679
|
+
}
|
|
2680
|
+
if (!schema.objectTypes.includes(objectType)) {
|
|
2681
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" is not valid on "${objectType}"`, location);
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
function singleAtlasValue(values, key, location) {
|
|
2685
|
+
if (values.length !== 1) {
|
|
2686
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
2687
|
+
}
|
|
2688
|
+
return values[0];
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2691
|
+
// packages/core/dist/atlas-validate.js
|
|
2692
|
+
var SURFACE_TARGET_TYPES2 = /* @__PURE__ */ new Set(["star", "planet", "moon", "asteroid", "comet"]);
|
|
2693
|
+
var EARTH_MASSES_PER_SOLAR = 332946.0487;
|
|
2694
|
+
var JUPITER_MASSES_PER_SOLAR = 1047.3486;
|
|
2695
|
+
var AU_IN_KM2 = 1495978707e-1;
|
|
2696
|
+
var EARTH_RADIUS_IN_KM2 = 6371;
|
|
2697
|
+
var SOLAR_RADIUS_IN_KM2 = 695700;
|
|
2698
|
+
var LY_IN_AU2 = 63241.077;
|
|
2699
|
+
var PC_IN_AU2 = 206264.806;
|
|
2700
|
+
var KPC_IN_AU2 = 206264806;
|
|
2701
|
+
function collectAtlasDiagnostics(document2, sourceSchemaVersion) {
|
|
2702
|
+
const diagnostics = [];
|
|
2703
|
+
const objectMap = new Map(document2.objects.map((object) => [object.id, object]));
|
|
2704
|
+
const groupIds = new Set(document2.groups.map((group) => group.id));
|
|
2705
|
+
if (!document2.system) {
|
|
2706
|
+
diagnostics.push(error("validate.system.required", "Atlas documents must declare exactly one system."));
|
|
2707
|
+
}
|
|
2708
|
+
const knownIds = /* @__PURE__ */ new Map();
|
|
2709
|
+
for (const [kind, ids] of [
|
|
2710
|
+
["group", document2.groups.map((group) => group.id)],
|
|
2711
|
+
["viewpoint", document2.system?.viewpoints.map((viewpoint) => viewpoint.id) ?? []],
|
|
2712
|
+
["annotation", document2.system?.annotations.map((annotation) => annotation.id) ?? []],
|
|
2713
|
+
["relation", document2.relations.map((relation) => relation.id)],
|
|
2714
|
+
["object", document2.objects.map((object) => object.id)]
|
|
2715
|
+
]) {
|
|
2716
|
+
for (const id of ids) {
|
|
2717
|
+
const previous = knownIds.get(id);
|
|
2718
|
+
if (previous) {
|
|
2719
|
+
diagnostics.push(error("validate.id.duplicate", `Duplicate ${kind} id "${id}" already used by ${previous}.`));
|
|
2720
|
+
} else {
|
|
2721
|
+
knownIds.set(id, kind);
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
for (const relation of document2.relations) {
|
|
2726
|
+
validateRelation(relation, objectMap, diagnostics);
|
|
2727
|
+
}
|
|
2728
|
+
for (const viewpoint of document2.system?.viewpoints ?? []) {
|
|
2729
|
+
validateViewpointFilter(viewpoint.filter, groupIds, sourceSchemaVersion, diagnostics, viewpoint.id);
|
|
2730
|
+
}
|
|
2731
|
+
for (const object of document2.objects) {
|
|
2732
|
+
validateObject(object, document2.system, objectMap, groupIds, diagnostics);
|
|
2733
|
+
}
|
|
2734
|
+
return diagnostics;
|
|
2735
|
+
}
|
|
2736
|
+
function validateRelation(relation, objectMap, diagnostics) {
|
|
2737
|
+
if (!relation.from) {
|
|
2738
|
+
diagnostics.push(error("validate.relation.from.required", `Relation "${relation.id}" is missing a "from" target.`));
|
|
2739
|
+
} else if (!objectMap.has(relation.from)) {
|
|
2740
|
+
diagnostics.push(error("validate.relation.from.unknown", `Unknown relation source "${relation.from}" on "${relation.id}".`));
|
|
2741
|
+
}
|
|
2742
|
+
if (!relation.to) {
|
|
2743
|
+
diagnostics.push(error("validate.relation.to.required", `Relation "${relation.id}" is missing a "to" target.`));
|
|
2744
|
+
} else if (!objectMap.has(relation.to)) {
|
|
2745
|
+
diagnostics.push(error("validate.relation.to.unknown", `Unknown relation target "${relation.to}" on "${relation.id}".`));
|
|
2746
|
+
}
|
|
2747
|
+
if (!relation.kind) {
|
|
2748
|
+
diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
function validateViewpointFilter(filter, groupIds, sourceSchemaVersion, diagnostics, viewpointId) {
|
|
2752
|
+
if (!filter || sourceSchemaVersion !== "2.1") {
|
|
2753
|
+
return;
|
|
2754
|
+
}
|
|
2755
|
+
for (const groupId of filter.groupIds) {
|
|
2756
|
+
if (!groupIds.has(groupId)) {
|
|
2757
|
+
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpointId}".`));
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
2762
|
+
const placement = object.placement;
|
|
2763
|
+
const orbitPlacement = placement?.mode === "orbit" ? placement : null;
|
|
2764
|
+
const parentObject = placement?.mode === "orbit" ? objectMap.get(placement.target) ?? null : null;
|
|
2765
|
+
if (object.groups) {
|
|
2766
|
+
for (const groupId of object.groups) {
|
|
2767
|
+
if (!groupIds.has(groupId)) {
|
|
2768
|
+
diagnostics.push(warn("validate.group.unknown", `Unknown group "${groupId}" on "${object.id}".`, object.id, "groups"));
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
if (orbitPlacement) {
|
|
2773
|
+
if (!objectMap.has(orbitPlacement.target)) {
|
|
2774
|
+
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
2775
|
+
}
|
|
2776
|
+
if (orbitPlacement.distance && orbitPlacement.semiMajor) {
|
|
2777
|
+
diagnostics.push(error("validate.orbit.distanceConflict", `Object "${object.id}" cannot declare both "distance" and "semiMajor".`, object.id, "distance"));
|
|
2778
|
+
}
|
|
2779
|
+
if (orbitPlacement.phase && !object.epoch && !system?.epoch) {
|
|
2780
|
+
diagnostics.push(warn("validate.phase.epochMissing", `Object "${object.id}" sets "phase" without an object or system epoch.`, object.id, "phase"));
|
|
2781
|
+
}
|
|
2782
|
+
if (orbitPlacement.inclination && !object.referencePlane && !system?.referencePlane) {
|
|
2783
|
+
diagnostics.push(warn("validate.inclination.referencePlaneMissing", `Object "${object.id}" sets "inclination" without an object or system reference plane.`, object.id, "inclination"));
|
|
2784
|
+
}
|
|
2785
|
+
if (orbitPlacement.period && !massInSolar(parentObject?.properties.mass)) {
|
|
2786
|
+
diagnostics.push(warn("validate.period.massMissing", `Object "${object.id}" sets "period" but its central mass cannot be derived.`, object.id, "period"));
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
if (placement?.mode === "surface") {
|
|
2790
|
+
const target = objectMap.get(placement.target);
|
|
2791
|
+
if (!target) {
|
|
2792
|
+
diagnostics.push(error("validate.surface.target.unknown", `Unknown placement target "${placement.target}" on "${object.id}".`, object.id, "surface"));
|
|
2793
|
+
} else if (!SURFACE_TARGET_TYPES2.has(target.type)) {
|
|
2794
|
+
diagnostics.push(error("validate.surface.target.invalid", `Surface target "${placement.target}" on "${object.id}" is not surface-capable.`, object.id, "surface"));
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
if (placement?.mode === "at") {
|
|
2798
|
+
if (object.type !== "structure" && object.type !== "phenomenon") {
|
|
2799
|
+
diagnostics.push(error("validate.at.objectType", `Only structures and phenomena may use "at" placement; found "${object.type}" on "${object.id}".`, object.id, "at"));
|
|
2800
|
+
}
|
|
2801
|
+
if (!validateAtTarget(object, objectMap, diagnostics)) {
|
|
2802
|
+
diagnostics.push(error("validate.at.target.unknown", `Unknown at-reference target "${placement.target}" on "${object.id}".`, object.id, "at"));
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
if (object.resonance) {
|
|
2806
|
+
const target = objectMap.get(object.resonance.targetObjectId);
|
|
2807
|
+
if (!target) {
|
|
2808
|
+
diagnostics.push(error("validate.resonance.target.unknown", `Unknown resonance target "${object.resonance.targetObjectId}" on "${object.id}".`, object.id, "resonance"));
|
|
2809
|
+
} else if (object.placement?.mode !== "orbit" || target.placement?.mode !== "orbit" || object.placement.target !== target.placement.target) {
|
|
2810
|
+
diagnostics.push(warn("validate.resonance.orbitMismatch", `Resonance target "${object.resonance.targetObjectId}" on "${object.id}" does not share a compatible orbital parent.`, object.id, "resonance"));
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
for (const rule of object.deriveRules ?? []) {
|
|
2814
|
+
if (rule.field !== "period" || rule.strategy !== "kepler") {
|
|
2815
|
+
diagnostics.push(warn("validate.derive.unsupported", `Unsupported derive rule "${rule.field} ${rule.strategy}" on "${object.id}".`, object.id, "derive"));
|
|
2816
|
+
continue;
|
|
2817
|
+
}
|
|
2818
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
2819
|
+
if (derivedPeriodDays === null) {
|
|
2820
|
+
diagnostics.push(warn("validate.derive.inputsMissing", `Object "${object.id}" requests "derive period kepler" but lacks enough input data.`, object.id, "derive"));
|
|
2821
|
+
continue;
|
|
2822
|
+
}
|
|
2823
|
+
if (!orbitPlacement?.period) {
|
|
2824
|
+
diagnostics.push(info("validate.derive.period.available", `Object "${object.id}" can derive a Kepler period of ${formatDays(derivedPeriodDays)}.`, object.id, "derive"));
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
for (const rule of object.validationRules ?? []) {
|
|
2828
|
+
if (rule.rule !== "kepler") {
|
|
2829
|
+
diagnostics.push(warn("validate.rule.unsupported", `Unsupported validation rule "${rule.rule}" on "${object.id}".`, object.id, "validate"));
|
|
2830
|
+
continue;
|
|
2831
|
+
}
|
|
2832
|
+
const actualPeriodDays = durationInDays(orbitPlacement?.period);
|
|
2833
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
2834
|
+
if (actualPeriodDays === null || derivedPeriodDays === null) {
|
|
2835
|
+
continue;
|
|
2836
|
+
}
|
|
2837
|
+
const toleranceDays = toleranceForField(object, "period");
|
|
2838
|
+
if (Math.abs(actualPeriodDays - derivedPeriodDays) > toleranceDays) {
|
|
2839
|
+
diagnostics.push(error("validate.kepler.mismatch", `Object "${object.id}" fails Kepler validation for "period".`, object.id, "validate"));
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
function validateAtTarget(object, objectMap, diagnostics) {
|
|
2844
|
+
const reference = object.placement?.mode === "at" ? object.placement.reference : null;
|
|
2845
|
+
if (!reference) {
|
|
2846
|
+
return true;
|
|
2847
|
+
}
|
|
2848
|
+
if (reference.kind === "named") {
|
|
2849
|
+
return objectMap.has(reference.name);
|
|
2850
|
+
}
|
|
2851
|
+
if (reference.kind === "anchor") {
|
|
2852
|
+
if (!objectMap.has(reference.objectId)) {
|
|
2853
|
+
diagnostics.push(error("validate.anchor.target.unknown", `Unknown anchor target "${reference.objectId}" on "${object.id}".`, object.id, "at"));
|
|
2854
|
+
return false;
|
|
2855
|
+
}
|
|
2856
|
+
return true;
|
|
2857
|
+
}
|
|
2858
|
+
if (!objectMap.has(reference.primary)) {
|
|
2859
|
+
diagnostics.push(error("validate.lagrange.primary.unknown", `Unknown Lagrange reference "${reference.primary}" on "${object.id}".`, object.id, "at"));
|
|
2860
|
+
return false;
|
|
2861
|
+
}
|
|
2862
|
+
if (reference.secondary && !objectMap.has(reference.secondary)) {
|
|
2863
|
+
diagnostics.push(error("validate.lagrange.secondary.unknown", `Unknown Lagrange reference "${reference.secondary}" on "${object.id}".`, object.id, "at"));
|
|
2864
|
+
return false;
|
|
2865
|
+
}
|
|
2866
|
+
return true;
|
|
2867
|
+
}
|
|
2868
|
+
function keplerPeriodDays(object, parentObject) {
|
|
2869
|
+
const placement = object.placement;
|
|
2870
|
+
if (!placement || placement.mode !== "orbit") {
|
|
2871
|
+
return null;
|
|
2872
|
+
}
|
|
2873
|
+
const semiMajorAu = distanceInAu(placement.semiMajor ?? placement.distance);
|
|
2874
|
+
const centralMassSolar = massInSolar(parentObject?.properties.mass);
|
|
2875
|
+
if (semiMajorAu === null || centralMassSolar === null || centralMassSolar <= 0) {
|
|
2876
|
+
return null;
|
|
2877
|
+
}
|
|
2878
|
+
const periodYears = Math.sqrt(semiMajorAu ** 3 / centralMassSolar);
|
|
2879
|
+
return periodYears * 365.25;
|
|
2880
|
+
}
|
|
2881
|
+
function distanceInAu(value) {
|
|
2882
|
+
if (!value)
|
|
2883
|
+
return null;
|
|
2884
|
+
switch (value.unit) {
|
|
2885
|
+
case null:
|
|
2886
|
+
case "au":
|
|
2887
|
+
return value.value;
|
|
2888
|
+
case "km":
|
|
2889
|
+
return value.value / AU_IN_KM2;
|
|
2890
|
+
case "m":
|
|
2891
|
+
return value.value / (AU_IN_KM2 * 1e3);
|
|
2892
|
+
case "ly":
|
|
2893
|
+
return value.value * LY_IN_AU2;
|
|
2894
|
+
case "pc":
|
|
2895
|
+
return value.value * PC_IN_AU2;
|
|
2896
|
+
case "kpc":
|
|
2897
|
+
return value.value * KPC_IN_AU2;
|
|
2898
|
+
case "re":
|
|
2899
|
+
return value.value * EARTH_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
2900
|
+
case "sol":
|
|
2901
|
+
return value.value * SOLAR_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
2902
|
+
default:
|
|
2903
|
+
return null;
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
function massInSolar(value) {
|
|
2907
|
+
if (!value || typeof value !== "object" || !("value" in value)) {
|
|
2908
|
+
return null;
|
|
2909
|
+
}
|
|
2910
|
+
const unitValue = value;
|
|
2911
|
+
switch (unitValue.unit) {
|
|
2912
|
+
case null:
|
|
2913
|
+
case "sol":
|
|
2914
|
+
return unitValue.value;
|
|
2915
|
+
case "me":
|
|
2916
|
+
return unitValue.value / EARTH_MASSES_PER_SOLAR;
|
|
2917
|
+
case "mj":
|
|
2918
|
+
return unitValue.value / JUPITER_MASSES_PER_SOLAR;
|
|
2919
|
+
default:
|
|
2920
|
+
return null;
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
function durationInDays(value) {
|
|
2924
|
+
if (!value)
|
|
2925
|
+
return null;
|
|
2926
|
+
switch (value.unit) {
|
|
2927
|
+
case null:
|
|
2928
|
+
case "d":
|
|
2929
|
+
return value.value;
|
|
2930
|
+
case "s":
|
|
2931
|
+
return value.value / 86400;
|
|
2932
|
+
case "min":
|
|
2933
|
+
return value.value / 1440;
|
|
2934
|
+
case "h":
|
|
2935
|
+
return value.value / 24;
|
|
2936
|
+
case "y":
|
|
2937
|
+
return value.value * 365.25;
|
|
2938
|
+
case "ky":
|
|
2939
|
+
return value.value * 365250;
|
|
2940
|
+
case "my":
|
|
2941
|
+
return value.value * 36525e4;
|
|
2942
|
+
case "gy":
|
|
2943
|
+
return value.value * 36525e7;
|
|
2944
|
+
default:
|
|
2945
|
+
return null;
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
function toleranceForField(object, field) {
|
|
2949
|
+
const tolerance = object.tolerances?.find((entry) => entry.field === field)?.value;
|
|
2950
|
+
if (typeof tolerance === "number") {
|
|
2951
|
+
return tolerance;
|
|
2952
|
+
}
|
|
2953
|
+
if (tolerance && typeof tolerance === "object" && "value" in tolerance) {
|
|
2954
|
+
return durationInDays(tolerance) ?? 0;
|
|
2955
|
+
}
|
|
2956
|
+
return 0;
|
|
2957
|
+
}
|
|
2958
|
+
function formatDays(days) {
|
|
2959
|
+
return `${Math.round(days * 100) / 100}d`;
|
|
2960
|
+
}
|
|
2961
|
+
function error(code, message, objectId, field) {
|
|
2962
|
+
return { code, severity: "error", source: "validate", message, objectId, field };
|
|
2963
|
+
}
|
|
2964
|
+
function warn(code, message, objectId, field) {
|
|
2965
|
+
return { code, severity: "warning", source: "validate", message, objectId, field };
|
|
2966
|
+
}
|
|
2967
|
+
function info(code, message, objectId, field) {
|
|
2968
|
+
return { code, severity: "info", source: "validate", message, objectId, field };
|
|
2969
|
+
}
|
|
2970
|
+
|
|
2434
2971
|
// packages/core/dist/draft-parse.js
|
|
2972
|
+
var STRUCTURED_TYPED_BLOCKS = /* @__PURE__ */ new Set([
|
|
2973
|
+
"climate",
|
|
2974
|
+
"habitability",
|
|
2975
|
+
"settlement"
|
|
2976
|
+
]);
|
|
2977
|
+
var DRAFT_OBJECT_FIELD_SPECS = /* @__PURE__ */ new Map();
|
|
2978
|
+
for (const key of [
|
|
2979
|
+
"orbit",
|
|
2980
|
+
"distance",
|
|
2981
|
+
"semiMajor",
|
|
2982
|
+
"eccentricity",
|
|
2983
|
+
"period",
|
|
2984
|
+
"angle",
|
|
2985
|
+
"inclination",
|
|
2986
|
+
"phase",
|
|
2987
|
+
"at",
|
|
2988
|
+
"surface",
|
|
2989
|
+
"free",
|
|
2990
|
+
"kind",
|
|
2991
|
+
"class",
|
|
2992
|
+
"culture",
|
|
2993
|
+
"tags",
|
|
2994
|
+
"color",
|
|
2995
|
+
"image",
|
|
2996
|
+
"hidden",
|
|
2997
|
+
"radius",
|
|
2998
|
+
"mass",
|
|
2999
|
+
"density",
|
|
3000
|
+
"gravity",
|
|
3001
|
+
"temperature",
|
|
3002
|
+
"albedo",
|
|
3003
|
+
"atmosphere",
|
|
3004
|
+
"inner",
|
|
3005
|
+
"outer",
|
|
3006
|
+
"on",
|
|
3007
|
+
"source",
|
|
3008
|
+
"cycle"
|
|
3009
|
+
]) {
|
|
3010
|
+
const schema = getFieldSchema(key);
|
|
3011
|
+
if (schema) {
|
|
3012
|
+
DRAFT_OBJECT_FIELD_SPECS.set(key, {
|
|
3013
|
+
key,
|
|
3014
|
+
version: "2.0",
|
|
3015
|
+
inlineMode: schema.arity === "multiple" ? "multiple" : "single",
|
|
3016
|
+
allowRepeat: false,
|
|
3017
|
+
legacySchema: schema
|
|
3018
|
+
});
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
for (const spec of [
|
|
3022
|
+
{ key: "groups", inlineMode: "multiple", allowRepeat: false },
|
|
3023
|
+
{ key: "epoch", inlineMode: "single", allowRepeat: false },
|
|
3024
|
+
{ key: "referencePlane", inlineMode: "single", allowRepeat: false },
|
|
3025
|
+
{ key: "tidalLock", inlineMode: "single", allowRepeat: false },
|
|
3026
|
+
{ key: "renderLabel", inlineMode: "single", allowRepeat: false },
|
|
3027
|
+
{ key: "renderOrbit", inlineMode: "single", allowRepeat: false },
|
|
3028
|
+
{ key: "renderPriority", inlineMode: "single", allowRepeat: false },
|
|
3029
|
+
{ key: "resonance", inlineMode: "pair", allowRepeat: false },
|
|
3030
|
+
{ key: "derive", inlineMode: "pair", allowRepeat: true },
|
|
3031
|
+
{ key: "validate", inlineMode: "single", allowRepeat: true },
|
|
3032
|
+
{ key: "locked", inlineMode: "multiple", allowRepeat: false },
|
|
3033
|
+
{ key: "tolerance", inlineMode: "pair", allowRepeat: true }
|
|
3034
|
+
]) {
|
|
3035
|
+
DRAFT_OBJECT_FIELD_SPECS.set(spec.key, {
|
|
3036
|
+
key: spec.key,
|
|
3037
|
+
version: "2.1",
|
|
3038
|
+
inlineMode: spec.inlineMode,
|
|
3039
|
+
allowRepeat: spec.allowRepeat
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
3042
|
+
var DRAFT_OBJECT_FIELD_KEYS = new Set(DRAFT_OBJECT_FIELD_SPECS.keys());
|
|
2435
3043
|
function parseWorldOrbitAtlas(source) {
|
|
2436
|
-
return parseAtlasSource(source
|
|
3044
|
+
return parseAtlasSource(source);
|
|
2437
3045
|
}
|
|
2438
|
-
function parseAtlasSource(source,
|
|
2439
|
-
const
|
|
3046
|
+
function parseAtlasSource(source, forcedOutputVersion) {
|
|
3047
|
+
const prepared = preprocessAtlasSource(source);
|
|
3048
|
+
const lines = prepared.source.split(/\r?\n/);
|
|
3049
|
+
const diagnostics = [];
|
|
2440
3050
|
let sawSchemaHeader = false;
|
|
2441
|
-
let
|
|
3051
|
+
let sourceSchemaVersion = "2.0";
|
|
2442
3052
|
let system = null;
|
|
2443
3053
|
let section = null;
|
|
2444
3054
|
const objectNodes = [];
|
|
3055
|
+
const groups = [];
|
|
3056
|
+
const relations = [];
|
|
2445
3057
|
let sawDefaults = false;
|
|
2446
3058
|
let sawAtlas = false;
|
|
2447
3059
|
const viewpointIds = /* @__PURE__ */ new Set();
|
|
2448
3060
|
const annotationIds = /* @__PURE__ */ new Set();
|
|
3061
|
+
const groupIds = /* @__PURE__ */ new Set();
|
|
3062
|
+
const relationIds = /* @__PURE__ */ new Set();
|
|
2449
3063
|
for (let index = 0; index < lines.length; index++) {
|
|
2450
3064
|
const rawLine = lines[index];
|
|
2451
3065
|
const lineNumber = index + 1;
|
|
@@ -2461,15 +3075,22 @@ var WorldOrbit = (() => {
|
|
|
2461
3075
|
continue;
|
|
2462
3076
|
}
|
|
2463
3077
|
if (!sawSchemaHeader) {
|
|
2464
|
-
|
|
3078
|
+
sourceSchemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
2465
3079
|
sawSchemaHeader = true;
|
|
3080
|
+
if (prepared.comments.length > 0 && sourceSchemaVersion !== "2.1") {
|
|
3081
|
+
diagnostics.push({
|
|
3082
|
+
code: "parse.schema21.commentCompatibility",
|
|
3083
|
+
severity: "warning",
|
|
3084
|
+
source: "parse",
|
|
3085
|
+
message: `Comments require schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
3086
|
+
line: prepared.comments[0].line,
|
|
3087
|
+
column: prepared.comments[0].column
|
|
3088
|
+
});
|
|
3089
|
+
}
|
|
2466
3090
|
continue;
|
|
2467
3091
|
}
|
|
2468
3092
|
if (indent === 0) {
|
|
2469
|
-
section = startTopLevelSection(tokens, lineNumber, system, objectNodes, viewpointIds, annotationIds, {
|
|
2470
|
-
sawDefaults,
|
|
2471
|
-
sawAtlas
|
|
2472
|
-
});
|
|
3093
|
+
section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, { sawDefaults, sawAtlas });
|
|
2473
3094
|
if (section.kind === "system") {
|
|
2474
3095
|
system = section.system;
|
|
2475
3096
|
} else if (section.kind === "defaults") {
|
|
@@ -2487,48 +3108,57 @@ var WorldOrbit = (() => {
|
|
|
2487
3108
|
if (!sawSchemaHeader) {
|
|
2488
3109
|
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
|
|
2489
3110
|
}
|
|
2490
|
-
const
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
};
|
|
2494
|
-
const normalizedObjects = normalizeDocument(ast).objects;
|
|
2495
|
-
validateDocument({
|
|
2496
|
-
format: "worldorbit",
|
|
2497
|
-
version: "1.0",
|
|
2498
|
-
system: null,
|
|
2499
|
-
objects: normalizedObjects
|
|
2500
|
-
});
|
|
2501
|
-
const diagnostics = schemaVersion === "2.0-draft" && outputVersion === "2.0" ? [
|
|
2502
|
-
{
|
|
2503
|
-
code: "load.schema.deprecatedDraft",
|
|
2504
|
-
severity: "warning",
|
|
2505
|
-
source: "upgrade",
|
|
2506
|
-
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
2507
|
-
}
|
|
2508
|
-
] : [];
|
|
2509
|
-
return {
|
|
3111
|
+
const objects = objectNodes.map((node) => normalizeDraftObject(node, sourceSchemaVersion, diagnostics));
|
|
3112
|
+
const outputVersion = forcedOutputVersion ?? (sourceSchemaVersion === "2.0-draft" ? "2.0" : sourceSchemaVersion);
|
|
3113
|
+
const baseDocument = {
|
|
2510
3114
|
format: "worldorbit",
|
|
2511
|
-
version: outputVersion,
|
|
2512
3115
|
sourceVersion: "1.0",
|
|
2513
3116
|
system,
|
|
2514
|
-
|
|
3117
|
+
groups,
|
|
3118
|
+
relations,
|
|
3119
|
+
objects,
|
|
2515
3120
|
diagnostics
|
|
2516
3121
|
};
|
|
2517
|
-
|
|
3122
|
+
if (outputVersion === "2.0-draft") {
|
|
3123
|
+
const document3 = {
|
|
3124
|
+
...baseDocument,
|
|
3125
|
+
version: "2.0-draft",
|
|
3126
|
+
schemaVersion: "2.0-draft"
|
|
3127
|
+
};
|
|
3128
|
+
document3.diagnostics.push(...collectAtlasDiagnostics(document3, sourceSchemaVersion));
|
|
3129
|
+
return document3;
|
|
3130
|
+
}
|
|
3131
|
+
const document2 = {
|
|
3132
|
+
...baseDocument,
|
|
3133
|
+
version: outputVersion,
|
|
3134
|
+
schemaVersion: outputVersion
|
|
3135
|
+
};
|
|
3136
|
+
if (sourceSchemaVersion === "2.0-draft") {
|
|
3137
|
+
document2.diagnostics.push({
|
|
3138
|
+
code: "load.schema.deprecatedDraft",
|
|
3139
|
+
severity: "warning",
|
|
3140
|
+
source: "upgrade",
|
|
3141
|
+
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
3142
|
+
});
|
|
3143
|
+
}
|
|
3144
|
+
document2.diagnostics.push(...collectAtlasDiagnostics(document2, sourceSchemaVersion));
|
|
3145
|
+
return document2;
|
|
3146
|
+
}
|
|
2518
3147
|
function assertDraftSchemaHeader(tokens, line) {
|
|
2519
|
-
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" ||
|
|
2520
|
-
throw new WorldOrbitError('Expected atlas header "schema 2.0" or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
3148
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1"].includes(tokens[1].value.toLowerCase())) {
|
|
3149
|
+
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
2521
3150
|
}
|
|
2522
|
-
|
|
3151
|
+
const version = tokens[1].value.toLowerCase();
|
|
3152
|
+
return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
2523
3153
|
}
|
|
2524
|
-
function startTopLevelSection(tokens, line, system, objectNodes, viewpointIds, annotationIds, flags) {
|
|
3154
|
+
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, flags) {
|
|
2525
3155
|
const keyword = tokens[0]?.value.toLowerCase();
|
|
2526
3156
|
switch (keyword) {
|
|
2527
3157
|
case "system":
|
|
2528
3158
|
if (system) {
|
|
2529
3159
|
throw new WorldOrbitError('Atlas section "system" may only appear once', line, tokens[0].column);
|
|
2530
3160
|
}
|
|
2531
|
-
return startSystemSection(tokens, line);
|
|
3161
|
+
return startSystemSection(tokens, line, sourceSchemaVersion, diagnostics);
|
|
2532
3162
|
case "defaults":
|
|
2533
3163
|
if (!system) {
|
|
2534
3164
|
throw new WorldOrbitError('Atlas section "defaults" requires a preceding system declaration', line, tokens[0].column);
|
|
@@ -2564,13 +3194,19 @@ var WorldOrbit = (() => {
|
|
|
2564
3194
|
throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
|
|
2565
3195
|
}
|
|
2566
3196
|
return startAnnotationSection(tokens, line, system, annotationIds);
|
|
3197
|
+
case "group":
|
|
3198
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "group", { line, column: tokens[0].column });
|
|
3199
|
+
return startGroupSection(tokens, line, groups, groupIds);
|
|
3200
|
+
case "relation":
|
|
3201
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "relation", { line, column: tokens[0].column });
|
|
3202
|
+
return startRelationSection(tokens, line, relations, relationIds);
|
|
2567
3203
|
case "object":
|
|
2568
|
-
return startObjectSection(tokens, line, objectNodes);
|
|
3204
|
+
return startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes);
|
|
2569
3205
|
default:
|
|
2570
3206
|
throw new WorldOrbitError(`Unknown atlas section "${tokens[0]?.value ?? ""}"`, line, tokens[0]?.column ?? 1);
|
|
2571
3207
|
}
|
|
2572
3208
|
}
|
|
2573
|
-
function startSystemSection(tokens, line) {
|
|
3209
|
+
function startSystemSection(tokens, line, sourceSchemaVersion, diagnostics) {
|
|
2574
3210
|
if (tokens.length !== 2) {
|
|
2575
3211
|
throw new WorldOrbitError("Invalid atlas system declaration", line, tokens[0]?.column ?? 1);
|
|
2576
3212
|
}
|
|
@@ -2578,6 +3214,9 @@ var WorldOrbit = (() => {
|
|
|
2578
3214
|
type: "system",
|
|
2579
3215
|
id: tokens[1].value,
|
|
2580
3216
|
title: null,
|
|
3217
|
+
description: null,
|
|
3218
|
+
epoch: null,
|
|
3219
|
+
referencePlane: null,
|
|
2581
3220
|
defaults: {
|
|
2582
3221
|
view: "topdown",
|
|
2583
3222
|
scale: null,
|
|
@@ -2592,6 +3231,8 @@ var WorldOrbit = (() => {
|
|
|
2592
3231
|
return {
|
|
2593
3232
|
kind: "system",
|
|
2594
3233
|
system,
|
|
3234
|
+
sourceSchemaVersion,
|
|
3235
|
+
diagnostics,
|
|
2595
3236
|
seenFields: /* @__PURE__ */ new Set()
|
|
2596
3237
|
};
|
|
2597
3238
|
}
|
|
@@ -2657,7 +3298,64 @@ var WorldOrbit = (() => {
|
|
|
2657
3298
|
seenFields: /* @__PURE__ */ new Set()
|
|
2658
3299
|
};
|
|
2659
3300
|
}
|
|
2660
|
-
function
|
|
3301
|
+
function startGroupSection(tokens, line, groups, groupIds) {
|
|
3302
|
+
if (tokens.length !== 2) {
|
|
3303
|
+
throw new WorldOrbitError("Invalid group declaration", line, tokens[0]?.column ?? 1);
|
|
3304
|
+
}
|
|
3305
|
+
const id = normalizeIdentifier(tokens[1].value);
|
|
3306
|
+
if (!id) {
|
|
3307
|
+
throw new WorldOrbitError("Group id must not be empty", line, tokens[1].column);
|
|
3308
|
+
}
|
|
3309
|
+
if (groupIds.has(id)) {
|
|
3310
|
+
throw new WorldOrbitError(`Duplicate group id "${id}"`, line, tokens[1].column);
|
|
3311
|
+
}
|
|
3312
|
+
const group = {
|
|
3313
|
+
id,
|
|
3314
|
+
label: humanizeIdentifier2(id),
|
|
3315
|
+
summary: "",
|
|
3316
|
+
color: null,
|
|
3317
|
+
tags: [],
|
|
3318
|
+
hidden: false
|
|
3319
|
+
};
|
|
3320
|
+
groups.push(group);
|
|
3321
|
+
groupIds.add(id);
|
|
3322
|
+
return {
|
|
3323
|
+
kind: "group",
|
|
3324
|
+
group,
|
|
3325
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
3326
|
+
};
|
|
3327
|
+
}
|
|
3328
|
+
function startRelationSection(tokens, line, relations, relationIds) {
|
|
3329
|
+
if (tokens.length !== 2) {
|
|
3330
|
+
throw new WorldOrbitError("Invalid relation declaration", line, tokens[0]?.column ?? 1);
|
|
3331
|
+
}
|
|
3332
|
+
const id = normalizeIdentifier(tokens[1].value);
|
|
3333
|
+
if (!id) {
|
|
3334
|
+
throw new WorldOrbitError("Relation id must not be empty", line, tokens[1].column);
|
|
3335
|
+
}
|
|
3336
|
+
if (relationIds.has(id)) {
|
|
3337
|
+
throw new WorldOrbitError(`Duplicate relation id "${id}"`, line, tokens[1].column);
|
|
3338
|
+
}
|
|
3339
|
+
const relation = {
|
|
3340
|
+
id,
|
|
3341
|
+
from: "",
|
|
3342
|
+
to: "",
|
|
3343
|
+
kind: "",
|
|
3344
|
+
label: null,
|
|
3345
|
+
summary: null,
|
|
3346
|
+
tags: [],
|
|
3347
|
+
color: null,
|
|
3348
|
+
hidden: false
|
|
3349
|
+
};
|
|
3350
|
+
relations.push(relation);
|
|
3351
|
+
relationIds.add(id);
|
|
3352
|
+
return {
|
|
3353
|
+
kind: "relation",
|
|
3354
|
+
relation,
|
|
3355
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
3356
|
+
};
|
|
3357
|
+
}
|
|
3358
|
+
function startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes) {
|
|
2661
3359
|
if (tokens.length < 3) {
|
|
2662
3360
|
throw new WorldOrbitError("Invalid atlas object declaration", line, tokens[0]?.column ?? 1);
|
|
2663
3361
|
}
|
|
@@ -2668,12 +3366,11 @@ var WorldOrbit = (() => {
|
|
|
2668
3366
|
throw new WorldOrbitError(`Unknown object type "${objectTypeToken.value}"`, line, objectTypeToken.column);
|
|
2669
3367
|
}
|
|
2670
3368
|
const objectNode = {
|
|
2671
|
-
type: "object",
|
|
2672
3369
|
objectType,
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
blockFields: [],
|
|
3370
|
+
id: idToken.value,
|
|
3371
|
+
fields: parseInlineObjectFields(tokens.slice(3), line, objectType, sourceSchemaVersion, diagnostics),
|
|
2676
3372
|
infoEntries: [],
|
|
3373
|
+
typedBlockEntries: {},
|
|
2677
3374
|
location: {
|
|
2678
3375
|
line,
|
|
2679
3376
|
column: objectTypeToken.column
|
|
@@ -2683,8 +3380,12 @@ var WorldOrbit = (() => {
|
|
|
2683
3380
|
return {
|
|
2684
3381
|
kind: "object",
|
|
2685
3382
|
objectNode,
|
|
2686
|
-
|
|
2687
|
-
|
|
3383
|
+
sourceSchemaVersion,
|
|
3384
|
+
diagnostics,
|
|
3385
|
+
activeBlock: null,
|
|
3386
|
+
blockIndent: null,
|
|
3387
|
+
seenInfoKeys: /* @__PURE__ */ new Set(),
|
|
3388
|
+
seenTypedBlockKeys: {}
|
|
2688
3389
|
};
|
|
2689
3390
|
}
|
|
2690
3391
|
function handleSectionLine(section, indent, tokens, line) {
|
|
@@ -2704,6 +3405,12 @@ var WorldOrbit = (() => {
|
|
|
2704
3405
|
case "annotation":
|
|
2705
3406
|
applyAnnotationField(section, tokens, line);
|
|
2706
3407
|
return;
|
|
3408
|
+
case "group":
|
|
3409
|
+
applyGroupField(section, tokens, line);
|
|
3410
|
+
return;
|
|
3411
|
+
case "relation":
|
|
3412
|
+
applyRelationField(section, tokens, line);
|
|
3413
|
+
return;
|
|
2707
3414
|
case "object":
|
|
2708
3415
|
applyObjectField(section, indent, tokens, line);
|
|
2709
3416
|
return;
|
|
@@ -2711,10 +3418,35 @@ var WorldOrbit = (() => {
|
|
|
2711
3418
|
}
|
|
2712
3419
|
function applySystemField(section, tokens, line) {
|
|
2713
3420
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
2714
|
-
|
|
2715
|
-
|
|
3421
|
+
const value = joinFieldValue(tokens, line);
|
|
3422
|
+
switch (key) {
|
|
3423
|
+
case "title":
|
|
3424
|
+
section.system.title = value;
|
|
3425
|
+
return;
|
|
3426
|
+
case "description":
|
|
3427
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
3428
|
+
line,
|
|
3429
|
+
column: tokens[0].column
|
|
3430
|
+
});
|
|
3431
|
+
section.system.description = value;
|
|
3432
|
+
return;
|
|
3433
|
+
case "epoch":
|
|
3434
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
3435
|
+
line,
|
|
3436
|
+
column: tokens[0].column
|
|
3437
|
+
});
|
|
3438
|
+
section.system.epoch = value;
|
|
3439
|
+
return;
|
|
3440
|
+
case "referenceplane":
|
|
3441
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, "referencePlane", {
|
|
3442
|
+
line,
|
|
3443
|
+
column: tokens[0].column
|
|
3444
|
+
});
|
|
3445
|
+
section.system.referencePlane = value;
|
|
3446
|
+
return;
|
|
3447
|
+
default:
|
|
3448
|
+
throw new WorldOrbitError(`Unknown system atlas field "${tokens[0].value}"`, line, tokens[0].column);
|
|
2716
3449
|
}
|
|
2717
|
-
section.system.title = joinFieldValue(tokens, line);
|
|
2718
3450
|
}
|
|
2719
3451
|
function applyDefaultsField(section, tokens, line) {
|
|
2720
3452
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
@@ -2745,14 +3477,11 @@ var WorldOrbit = (() => {
|
|
|
2745
3477
|
section.metadataIndent = null;
|
|
2746
3478
|
}
|
|
2747
3479
|
if (section.inMetadata) {
|
|
2748
|
-
|
|
2749
|
-
|
|
3480
|
+
const entry = parseInfoLikeEntry(tokens, line, "Invalid atlas metadata entry");
|
|
3481
|
+
if (entry.key in section.system.atlasMetadata) {
|
|
3482
|
+
throw new WorldOrbitError(`Duplicate atlas metadata key "${entry.key}"`, line, tokens[0].column);
|
|
2750
3483
|
}
|
|
2751
|
-
|
|
2752
|
-
if (key in section.system.atlasMetadata) {
|
|
2753
|
-
throw new WorldOrbitError(`Duplicate atlas metadata key "${key}"`, line, tokens[0].column);
|
|
2754
|
-
}
|
|
2755
|
-
section.system.atlasMetadata[key] = joinFieldValue(tokens, line);
|
|
3484
|
+
section.system.atlasMetadata[entry.key] = entry.value;
|
|
2756
3485
|
return;
|
|
2757
3486
|
}
|
|
2758
3487
|
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "metadata") {
|
|
@@ -2854,21 +3583,102 @@ var WorldOrbit = (() => {
|
|
|
2854
3583
|
throw new WorldOrbitError(`Unknown annotation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
2855
3584
|
}
|
|
2856
3585
|
}
|
|
2857
|
-
function
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
3586
|
+
function applyGroupField(section, tokens, line) {
|
|
3587
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
3588
|
+
switch (key) {
|
|
3589
|
+
case "label":
|
|
3590
|
+
section.group.label = joinFieldValue(tokens, line);
|
|
3591
|
+
return;
|
|
3592
|
+
case "summary":
|
|
3593
|
+
section.group.summary = joinFieldValue(tokens, line);
|
|
3594
|
+
return;
|
|
3595
|
+
case "color":
|
|
3596
|
+
section.group.color = joinFieldValue(tokens, line);
|
|
3597
|
+
return;
|
|
3598
|
+
case "tags":
|
|
3599
|
+
section.group.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
3600
|
+
return;
|
|
3601
|
+
case "hidden":
|
|
3602
|
+
section.group.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
3603
|
+
line,
|
|
3604
|
+
column: tokens[0].column
|
|
3605
|
+
});
|
|
3606
|
+
return;
|
|
3607
|
+
default:
|
|
3608
|
+
throw new WorldOrbitError(`Unknown group field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3609
|
+
}
|
|
3610
|
+
}
|
|
3611
|
+
function applyRelationField(section, tokens, line) {
|
|
3612
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
3613
|
+
switch (key) {
|
|
3614
|
+
case "from":
|
|
3615
|
+
section.relation.from = joinFieldValue(tokens, line);
|
|
3616
|
+
return;
|
|
3617
|
+
case "to":
|
|
3618
|
+
section.relation.to = joinFieldValue(tokens, line);
|
|
3619
|
+
return;
|
|
3620
|
+
case "kind":
|
|
3621
|
+
section.relation.kind = joinFieldValue(tokens, line);
|
|
3622
|
+
return;
|
|
3623
|
+
case "label":
|
|
3624
|
+
section.relation.label = joinFieldValue(tokens, line);
|
|
3625
|
+
return;
|
|
3626
|
+
case "summary":
|
|
3627
|
+
section.relation.summary = joinFieldValue(tokens, line);
|
|
3628
|
+
return;
|
|
3629
|
+
case "tags":
|
|
3630
|
+
section.relation.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
3631
|
+
return;
|
|
3632
|
+
case "color":
|
|
3633
|
+
section.relation.color = joinFieldValue(tokens, line);
|
|
3634
|
+
return;
|
|
3635
|
+
case "hidden":
|
|
3636
|
+
section.relation.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
3637
|
+
line,
|
|
3638
|
+
column: tokens[0].column
|
|
3639
|
+
});
|
|
3640
|
+
return;
|
|
3641
|
+
default:
|
|
3642
|
+
throw new WorldOrbitError(`Unknown relation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
2862
3643
|
}
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
3644
|
+
}
|
|
3645
|
+
function applyObjectField(section, indent, tokens, line) {
|
|
3646
|
+
if (section.activeBlock && indent <= (section.blockIndent ?? 0)) {
|
|
3647
|
+
section.activeBlock = null;
|
|
3648
|
+
section.blockIndent = null;
|
|
3649
|
+
}
|
|
3650
|
+
if (tokens.length === 1) {
|
|
3651
|
+
const blockName = tokens[0].value.toLowerCase();
|
|
3652
|
+
if (blockName === "info" || STRUCTURED_TYPED_BLOCKS.has(blockName)) {
|
|
3653
|
+
if (blockName !== "info") {
|
|
3654
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, blockName, { line, column: tokens[0].column });
|
|
3655
|
+
}
|
|
3656
|
+
section.activeBlock = blockName;
|
|
3657
|
+
section.blockIndent = indent;
|
|
3658
|
+
return;
|
|
3659
|
+
}
|
|
2866
3660
|
}
|
|
2867
|
-
if (section.
|
|
2868
|
-
|
|
3661
|
+
if (section.activeBlock) {
|
|
3662
|
+
const entry = parseInfoLikeEntry(tokens, line, `Invalid ${section.activeBlock} entry`);
|
|
3663
|
+
if (section.activeBlock === "info") {
|
|
3664
|
+
if (section.seenInfoKeys.has(entry.key)) {
|
|
3665
|
+
throw new WorldOrbitError(`Duplicate info key "${entry.key}"`, line, tokens[0].column);
|
|
3666
|
+
}
|
|
3667
|
+
section.seenInfoKeys.add(entry.key);
|
|
3668
|
+
section.objectNode.infoEntries.push(entry);
|
|
3669
|
+
return;
|
|
3670
|
+
}
|
|
3671
|
+
const typedBlock = section.activeBlock;
|
|
3672
|
+
const seenKeys = section.seenTypedBlockKeys[typedBlock] ?? (section.seenTypedBlockKeys[typedBlock] = /* @__PURE__ */ new Set());
|
|
3673
|
+
if (seenKeys.has(entry.key)) {
|
|
3674
|
+
throw new WorldOrbitError(`Duplicate ${typedBlock} key "${entry.key}"`, line, tokens[0].column);
|
|
3675
|
+
}
|
|
3676
|
+
seenKeys.add(entry.key);
|
|
3677
|
+
const entries = section.objectNode.typedBlockEntries[typedBlock] ?? (section.objectNode.typedBlockEntries[typedBlock] = []);
|
|
3678
|
+
entries.push(entry);
|
|
2869
3679
|
return;
|
|
2870
3680
|
}
|
|
2871
|
-
section.objectNode.
|
|
3681
|
+
section.objectNode.fields.push(parseObjectField(tokens, line, section.objectNode.objectType, section.sourceSchemaVersion, section.diagnostics));
|
|
2872
3682
|
}
|
|
2873
3683
|
function requireUniqueField(tokens, seenFields, line) {
|
|
2874
3684
|
if (tokens.length < 2) {
|
|
@@ -2888,50 +3698,40 @@ var WorldOrbit = (() => {
|
|
|
2888
3698
|
return tokens.slice(1).map((token) => token.value).join(" ").trim();
|
|
2889
3699
|
}
|
|
2890
3700
|
function parseObjectTypeTokens(tokens, line) {
|
|
2891
|
-
|
|
2892
|
-
throw new WorldOrbitError("Missing value for atlas field", line);
|
|
2893
|
-
}
|
|
2894
|
-
return tokens.map((token) => {
|
|
2895
|
-
const value = token.value;
|
|
2896
|
-
if (value !== "star" && value !== "planet" && value !== "moon" && value !== "belt" && value !== "asteroid" && value !== "comet" && value !== "ring" && value !== "structure" && value !== "phenomenon") {
|
|
2897
|
-
throw new WorldOrbitError(`Unknown viewpoint object type "${token.value}"`, line, token.column);
|
|
2898
|
-
}
|
|
2899
|
-
return value;
|
|
2900
|
-
});
|
|
2901
|
-
}
|
|
2902
|
-
function parseTokenList(tokens, line, field) {
|
|
2903
|
-
if (tokens.length === 0) {
|
|
2904
|
-
throw new WorldOrbitError(`Missing value for field "${field}"`, line);
|
|
2905
|
-
}
|
|
2906
|
-
return tokens.map((token) => token.value);
|
|
3701
|
+
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");
|
|
2907
3702
|
}
|
|
2908
3703
|
function parseLayerTokens(tokens, line) {
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
if (rawLayer === "orbits") {
|
|
2917
|
-
next["orbits-back"] = enabled;
|
|
2918
|
-
next["orbits-front"] = enabled;
|
|
3704
|
+
const layers = {};
|
|
3705
|
+
for (const token of parseTokenList(tokens, line, "layers")) {
|
|
3706
|
+
const enabled = !token.startsWith("-") && !token.startsWith("!");
|
|
3707
|
+
const raw = token.replace(/^[-!]+/, "").toLowerCase();
|
|
3708
|
+
if (raw === "orbits") {
|
|
3709
|
+
layers["orbits-back"] = enabled;
|
|
3710
|
+
layers["orbits-front"] = enabled;
|
|
2919
3711
|
continue;
|
|
2920
3712
|
}
|
|
2921
|
-
if (
|
|
2922
|
-
|
|
2923
|
-
continue;
|
|
3713
|
+
if (raw === "background" || raw === "guides" || raw === "orbits-back" || raw === "orbits-front" || raw === "relations" || raw === "objects" || raw === "labels" || raw === "metadata") {
|
|
3714
|
+
layers[raw] = enabled;
|
|
2924
3715
|
}
|
|
2925
|
-
throw new WorldOrbitError(`Unknown layer token "${token.value}"`, line, token.column);
|
|
2926
3716
|
}
|
|
2927
|
-
return
|
|
3717
|
+
return layers;
|
|
3718
|
+
}
|
|
3719
|
+
function parseTokenList(tokens, line, fieldName) {
|
|
3720
|
+
if (tokens.length === 0) {
|
|
3721
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, 1);
|
|
3722
|
+
}
|
|
3723
|
+
const values = tokens.map((token) => token.value).filter(Boolean);
|
|
3724
|
+
if (values.length === 0) {
|
|
3725
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, tokens[0]?.column ?? 1);
|
|
3726
|
+
}
|
|
3727
|
+
return values;
|
|
2928
3728
|
}
|
|
2929
3729
|
function parseProjectionValue(value, line, column) {
|
|
2930
3730
|
const normalized = value.toLowerCase();
|
|
2931
|
-
if (normalized
|
|
2932
|
-
|
|
3731
|
+
if (normalized !== "topdown" && normalized !== "isometric") {
|
|
3732
|
+
throw new WorldOrbitError(`Unknown projection "${value}"`, line, column);
|
|
2933
3733
|
}
|
|
2934
|
-
|
|
3734
|
+
return normalized;
|
|
2935
3735
|
}
|
|
2936
3736
|
function parsePresetValue(value, line, column) {
|
|
2937
3737
|
const normalized = value.toLowerCase();
|
|
@@ -2941,16 +3741,16 @@ var WorldOrbit = (() => {
|
|
|
2941
3741
|
throw new WorldOrbitError(`Unknown render preset "${value}"`, line, column);
|
|
2942
3742
|
}
|
|
2943
3743
|
function parsePositiveNumber2(value, line, column, field) {
|
|
2944
|
-
const parsed =
|
|
2945
|
-
if (
|
|
2946
|
-
throw new WorldOrbitError(`Field "${field}"
|
|
3744
|
+
const parsed = parseFiniteNumber2(value, line, column, field);
|
|
3745
|
+
if (parsed <= 0) {
|
|
3746
|
+
throw new WorldOrbitError(`Field "${field}" must be greater than zero`, line, column);
|
|
2947
3747
|
}
|
|
2948
3748
|
return parsed;
|
|
2949
3749
|
}
|
|
2950
3750
|
function parseFiniteNumber2(value, line, column, field) {
|
|
2951
3751
|
const parsed = Number(value);
|
|
2952
3752
|
if (!Number.isFinite(parsed)) {
|
|
2953
|
-
throw new WorldOrbitError(`
|
|
3753
|
+
throw new WorldOrbitError(`Invalid numeric value "${value}" for "${field}"`, line, column);
|
|
2954
3754
|
}
|
|
2955
3755
|
return parsed;
|
|
2956
3756
|
}
|
|
@@ -2962,28 +3762,43 @@ var WorldOrbit = (() => {
|
|
|
2962
3762
|
groupIds: []
|
|
2963
3763
|
};
|
|
2964
3764
|
}
|
|
2965
|
-
function
|
|
3765
|
+
function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
2966
3766
|
const fields = [];
|
|
2967
3767
|
let index = 0;
|
|
2968
3768
|
while (index < tokens.length) {
|
|
2969
3769
|
const keyToken = tokens[index];
|
|
2970
|
-
const
|
|
2971
|
-
if (!
|
|
3770
|
+
const spec = getDraftObjectFieldSpec(keyToken.value);
|
|
3771
|
+
if (!spec) {
|
|
2972
3772
|
throw new WorldOrbitError(`Unknown field "${keyToken.value}"`, line, keyToken.column);
|
|
2973
3773
|
}
|
|
3774
|
+
if (spec.version === "2.1") {
|
|
3775
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, keyToken.value, {
|
|
3776
|
+
line,
|
|
3777
|
+
column: keyToken.column
|
|
3778
|
+
});
|
|
3779
|
+
}
|
|
2974
3780
|
index++;
|
|
2975
3781
|
const valueTokens = [];
|
|
2976
|
-
if (
|
|
2977
|
-
while (index < tokens.length && !isKnownFieldKey(tokens[index].value)) {
|
|
2978
|
-
valueTokens.push(tokens[index]);
|
|
2979
|
-
index++;
|
|
2980
|
-
}
|
|
2981
|
-
} else {
|
|
3782
|
+
if (spec.inlineMode === "single") {
|
|
2982
3783
|
const nextToken = tokens[index];
|
|
2983
3784
|
if (nextToken) {
|
|
2984
3785
|
valueTokens.push(nextToken);
|
|
2985
3786
|
index++;
|
|
2986
3787
|
}
|
|
3788
|
+
} else if (spec.inlineMode === "pair") {
|
|
3789
|
+
for (let count = 0; count < 2; count++) {
|
|
3790
|
+
const nextToken = tokens[index];
|
|
3791
|
+
if (!nextToken) {
|
|
3792
|
+
break;
|
|
3793
|
+
}
|
|
3794
|
+
valueTokens.push(nextToken);
|
|
3795
|
+
index++;
|
|
3796
|
+
}
|
|
3797
|
+
} else {
|
|
3798
|
+
while (index < tokens.length && !DRAFT_OBJECT_FIELD_KEYS.has(tokens[index].value)) {
|
|
3799
|
+
valueTokens.push(tokens[index]);
|
|
3800
|
+
index++;
|
|
3801
|
+
}
|
|
2987
3802
|
}
|
|
2988
3803
|
if (valueTokens.length === 0) {
|
|
2989
3804
|
throw new WorldOrbitError(`Missing value for field "${keyToken.value}"`, line, keyToken.column);
|
|
@@ -2995,25 +3810,35 @@ var WorldOrbit = (() => {
|
|
|
2995
3810
|
location: { line, column: keyToken.column }
|
|
2996
3811
|
});
|
|
2997
3812
|
}
|
|
3813
|
+
validateDraftObjectFieldCompatibility(fields, objectType);
|
|
2998
3814
|
return fields;
|
|
2999
3815
|
}
|
|
3000
|
-
function
|
|
3816
|
+
function parseObjectField(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
3001
3817
|
if (tokens.length < 2) {
|
|
3002
3818
|
throw new WorldOrbitError("Invalid field line", line, tokens[0]?.column ?? 1);
|
|
3003
3819
|
}
|
|
3004
|
-
|
|
3820
|
+
const spec = getDraftObjectFieldSpec(tokens[0].value);
|
|
3821
|
+
if (!spec) {
|
|
3005
3822
|
throw new WorldOrbitError(`Unknown field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3006
3823
|
}
|
|
3007
|
-
|
|
3824
|
+
if (spec.version === "2.1") {
|
|
3825
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, tokens[0].value, {
|
|
3826
|
+
line,
|
|
3827
|
+
column: tokens[0].column
|
|
3828
|
+
});
|
|
3829
|
+
}
|
|
3830
|
+
const field = {
|
|
3008
3831
|
type: "field",
|
|
3009
3832
|
key: tokens[0].value,
|
|
3010
3833
|
values: tokens.slice(1).map((token) => token.value),
|
|
3011
3834
|
location: { line, column: tokens[0].column }
|
|
3012
3835
|
};
|
|
3836
|
+
validateDraftObjectFieldCompatibility([field], objectType);
|
|
3837
|
+
return field;
|
|
3013
3838
|
}
|
|
3014
|
-
function
|
|
3839
|
+
function parseInfoLikeEntry(tokens, line, errorMessage) {
|
|
3015
3840
|
if (tokens.length < 2) {
|
|
3016
|
-
throw new WorldOrbitError(
|
|
3841
|
+
throw new WorldOrbitError(errorMessage, line, tokens[0]?.column ?? 1);
|
|
3017
3842
|
}
|
|
3018
3843
|
return {
|
|
3019
3844
|
type: "info-entry",
|
|
@@ -3022,18 +3847,348 @@ var WorldOrbit = (() => {
|
|
|
3022
3847
|
location: { line, column: tokens[0].column }
|
|
3023
3848
|
};
|
|
3024
3849
|
}
|
|
3025
|
-
function
|
|
3026
|
-
|
|
3850
|
+
function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
|
|
3851
|
+
const fieldMap = collectDraftFields(node.fields);
|
|
3852
|
+
const placement = extractDraftPlacement(node.objectType, fieldMap);
|
|
3853
|
+
const properties = normalizeDraftProperties(node.objectType, fieldMap);
|
|
3854
|
+
const groups = parseOptionalTokenList(fieldMap.get("groups")?.[0]);
|
|
3855
|
+
const epoch = parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]);
|
|
3856
|
+
const referencePlane = parseOptionalJoinedValue(fieldMap.get("referencePlane")?.[0]);
|
|
3857
|
+
const tidalLock = fieldMap.has("tidalLock") ? parseAtlasBoolean(singleFieldValue2(fieldMap.get("tidalLock")[0]), "tidalLock", fieldMap.get("tidalLock")[0].location) : void 0;
|
|
3858
|
+
const resonance = fieldMap.has("resonance") ? parseResonanceField(fieldMap.get("resonance")[0]) : void 0;
|
|
3859
|
+
const renderHints = extractRenderHints(fieldMap);
|
|
3860
|
+
const deriveRules = fieldMap.get("derive")?.map((field) => parseDeriveField(field));
|
|
3861
|
+
const validationRules = fieldMap.get("validate")?.map((field) => ({
|
|
3862
|
+
rule: singleFieldValue2(field)
|
|
3863
|
+
}));
|
|
3864
|
+
const lockedFields = fieldMap.has("locked") ? [...new Set(fieldMap.get("locked").flatMap((field) => field.values))] : void 0;
|
|
3865
|
+
const tolerances = fieldMap.get("tolerance")?.map((field) => parseToleranceField(field));
|
|
3866
|
+
const typedBlocks = normalizeTypedBlocks(node.typedBlockEntries);
|
|
3867
|
+
const info2 = normalizeInfoEntries(node.infoEntries, "info");
|
|
3868
|
+
const object = {
|
|
3869
|
+
type: node.objectType,
|
|
3870
|
+
id: node.id,
|
|
3871
|
+
properties,
|
|
3872
|
+
placement,
|
|
3873
|
+
info: info2
|
|
3874
|
+
};
|
|
3875
|
+
if (groups.length > 0)
|
|
3876
|
+
object.groups = groups;
|
|
3877
|
+
if (epoch)
|
|
3878
|
+
object.epoch = epoch;
|
|
3879
|
+
if (referencePlane)
|
|
3880
|
+
object.referencePlane = referencePlane;
|
|
3881
|
+
if (tidalLock !== void 0)
|
|
3882
|
+
object.tidalLock = tidalLock;
|
|
3883
|
+
if (resonance)
|
|
3884
|
+
object.resonance = resonance;
|
|
3885
|
+
if (renderHints)
|
|
3886
|
+
object.renderHints = renderHints;
|
|
3887
|
+
if (deriveRules?.length)
|
|
3888
|
+
object.deriveRules = deriveRules;
|
|
3889
|
+
if (validationRules?.length)
|
|
3890
|
+
object.validationRules = validationRules;
|
|
3891
|
+
if (lockedFields?.length)
|
|
3892
|
+
object.lockedFields = lockedFields;
|
|
3893
|
+
if (tolerances?.length)
|
|
3894
|
+
object.tolerances = tolerances;
|
|
3895
|
+
if (typedBlocks && Object.keys(typedBlocks).length > 0)
|
|
3896
|
+
object.typedBlocks = typedBlocks;
|
|
3897
|
+
if (sourceSchemaVersion !== "2.1") {
|
|
3898
|
+
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) {
|
|
3899
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, node.id, node.location);
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
return object;
|
|
3027
3903
|
}
|
|
3028
|
-
function
|
|
3029
|
-
|
|
3904
|
+
function collectDraftFields(fields) {
|
|
3905
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
3906
|
+
for (const field of fields) {
|
|
3907
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
3908
|
+
if (!spec) {
|
|
3909
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
3910
|
+
}
|
|
3911
|
+
if (!spec.allowRepeat && grouped.has(field.key)) {
|
|
3912
|
+
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
3913
|
+
}
|
|
3914
|
+
const existing = grouped.get(field.key) ?? [];
|
|
3915
|
+
existing.push(field);
|
|
3916
|
+
grouped.set(field.key, existing);
|
|
3917
|
+
}
|
|
3918
|
+
return grouped;
|
|
3919
|
+
}
|
|
3920
|
+
function extractDraftPlacement(objectType, fieldMap) {
|
|
3921
|
+
const orbitField = fieldMap.get("orbit")?.[0];
|
|
3922
|
+
const atField = fieldMap.get("at")?.[0];
|
|
3923
|
+
const surfaceField = fieldMap.get("surface")?.[0];
|
|
3924
|
+
const freeField = fieldMap.get("free")?.[0];
|
|
3925
|
+
const count = [orbitField, atField, surfaceField, freeField].filter(Boolean).length;
|
|
3926
|
+
if (count > 1) {
|
|
3927
|
+
const conflictingField = orbitField ?? atField ?? surfaceField ?? freeField;
|
|
3928
|
+
throw WorldOrbitError.fromLocation("Object has multiple placement modes", conflictingField?.location);
|
|
3929
|
+
}
|
|
3930
|
+
if (orbitField) {
|
|
3931
|
+
return {
|
|
3932
|
+
mode: "orbit",
|
|
3933
|
+
target: singleFieldValue2(orbitField),
|
|
3934
|
+
distance: parseOptionalUnitField(fieldMap.get("distance")?.[0], "distance"),
|
|
3935
|
+
semiMajor: parseOptionalUnitField(fieldMap.get("semiMajor")?.[0], "semiMajor"),
|
|
3936
|
+
eccentricity: parseOptionalNumberField(fieldMap.get("eccentricity")?.[0], "eccentricity"),
|
|
3937
|
+
period: parseOptionalUnitField(fieldMap.get("period")?.[0], "period"),
|
|
3938
|
+
angle: parseOptionalUnitField(fieldMap.get("angle")?.[0], "angle"),
|
|
3939
|
+
inclination: parseOptionalUnitField(fieldMap.get("inclination")?.[0], "inclination"),
|
|
3940
|
+
phase: parseOptionalUnitField(fieldMap.get("phase")?.[0], "phase")
|
|
3941
|
+
};
|
|
3942
|
+
}
|
|
3943
|
+
if (atField) {
|
|
3944
|
+
const target = singleFieldValue2(atField);
|
|
3945
|
+
return {
|
|
3946
|
+
mode: "at",
|
|
3947
|
+
target,
|
|
3948
|
+
reference: parseAtlasAtReference(target, atField.location)
|
|
3949
|
+
};
|
|
3950
|
+
}
|
|
3951
|
+
if (surfaceField) {
|
|
3952
|
+
return {
|
|
3953
|
+
mode: "surface",
|
|
3954
|
+
target: singleFieldValue2(surfaceField)
|
|
3955
|
+
};
|
|
3956
|
+
}
|
|
3957
|
+
if (freeField) {
|
|
3958
|
+
const raw = singleFieldValue2(freeField);
|
|
3959
|
+
const distance = tryParseAtlasUnitValue(raw);
|
|
3960
|
+
return {
|
|
3961
|
+
mode: "free",
|
|
3962
|
+
distance: distance ?? void 0,
|
|
3963
|
+
descriptor: distance ? void 0 : raw
|
|
3964
|
+
};
|
|
3965
|
+
}
|
|
3966
|
+
return null;
|
|
3967
|
+
}
|
|
3968
|
+
function normalizeDraftProperties(objectType, fieldMap) {
|
|
3969
|
+
const properties = {};
|
|
3970
|
+
for (const [key, fields] of fieldMap.entries()) {
|
|
3971
|
+
const field = fields[0];
|
|
3972
|
+
const spec = getDraftObjectFieldSpec(key);
|
|
3973
|
+
if (!field || !spec?.legacySchema || spec.legacySchema.placement) {
|
|
3974
|
+
continue;
|
|
3975
|
+
}
|
|
3976
|
+
ensureAtlasFieldSupported(key, objectType, field.location);
|
|
3977
|
+
properties[key] = normalizeLegacyScalarValue(key, field.values, field.location);
|
|
3978
|
+
}
|
|
3979
|
+
return properties;
|
|
3980
|
+
}
|
|
3981
|
+
function normalizeInfoEntries(entries, label) {
|
|
3982
|
+
const normalized = {};
|
|
3983
|
+
for (const entry of entries) {
|
|
3984
|
+
if (entry.key in normalized) {
|
|
3985
|
+
throw WorldOrbitError.fromLocation(`Duplicate ${label} key "${entry.key}"`, entry.location);
|
|
3986
|
+
}
|
|
3987
|
+
normalized[entry.key] = entry.value;
|
|
3988
|
+
}
|
|
3989
|
+
return normalized;
|
|
3990
|
+
}
|
|
3991
|
+
function normalizeTypedBlocks(typedBlockEntries) {
|
|
3992
|
+
const typedBlocks = {};
|
|
3993
|
+
for (const blockName of Object.keys(typedBlockEntries)) {
|
|
3994
|
+
const entries = typedBlockEntries[blockName];
|
|
3995
|
+
if (entries?.length) {
|
|
3996
|
+
typedBlocks[blockName] = normalizeInfoEntries(entries, blockName);
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
return typedBlocks;
|
|
4000
|
+
}
|
|
4001
|
+
function extractRenderHints(fieldMap) {
|
|
4002
|
+
const renderHints = {};
|
|
4003
|
+
const renderLabelField = fieldMap.get("renderLabel")?.[0];
|
|
4004
|
+
const renderOrbitField = fieldMap.get("renderOrbit")?.[0];
|
|
4005
|
+
const renderPriorityField = fieldMap.get("renderPriority")?.[0];
|
|
4006
|
+
if (renderLabelField) {
|
|
4007
|
+
renderHints.renderLabel = parseAtlasBoolean(singleFieldValue2(renderLabelField), "renderLabel", renderLabelField.location);
|
|
4008
|
+
}
|
|
4009
|
+
if (renderOrbitField) {
|
|
4010
|
+
renderHints.renderOrbit = parseAtlasBoolean(singleFieldValue2(renderOrbitField), "renderOrbit", renderOrbitField.location);
|
|
4011
|
+
}
|
|
4012
|
+
if (renderPriorityField) {
|
|
4013
|
+
renderHints.renderPriority = parseAtlasNumber(singleFieldValue2(renderPriorityField), "renderPriority", renderPriorityField.location);
|
|
4014
|
+
}
|
|
4015
|
+
return Object.keys(renderHints).length > 0 ? renderHints : void 0;
|
|
4016
|
+
}
|
|
4017
|
+
function parseResonanceField(field) {
|
|
4018
|
+
if (field.values.length !== 2) {
|
|
4019
|
+
throw WorldOrbitError.fromLocation('Field "resonance" expects "<targetObjectId> <ratio>"', field.location);
|
|
4020
|
+
}
|
|
4021
|
+
const ratio = field.values[1];
|
|
4022
|
+
if (!/^\d+:\d+$/.test(ratio)) {
|
|
4023
|
+
throw WorldOrbitError.fromLocation(`Invalid resonance ratio "${ratio}"`, field.location);
|
|
4024
|
+
}
|
|
4025
|
+
return {
|
|
4026
|
+
targetObjectId: field.values[0],
|
|
4027
|
+
ratio
|
|
4028
|
+
};
|
|
4029
|
+
}
|
|
4030
|
+
function parseDeriveField(field) {
|
|
4031
|
+
if (field.values.length !== 2) {
|
|
4032
|
+
throw WorldOrbitError.fromLocation('Field "derive" expects "<field> <strategy>"', field.location);
|
|
4033
|
+
}
|
|
4034
|
+
return {
|
|
4035
|
+
field: field.values[0],
|
|
4036
|
+
strategy: field.values[1]
|
|
4037
|
+
};
|
|
4038
|
+
}
|
|
4039
|
+
function parseToleranceField(field) {
|
|
4040
|
+
if (field.values.length !== 2) {
|
|
4041
|
+
throw WorldOrbitError.fromLocation('Field "tolerance" expects "<field> <value>"', field.location);
|
|
4042
|
+
}
|
|
4043
|
+
const rawValue = field.values[1];
|
|
4044
|
+
const unitValue = tryParseAtlasUnitValue(rawValue);
|
|
4045
|
+
const numericValue2 = Number(rawValue);
|
|
4046
|
+
return {
|
|
4047
|
+
field: field.values[0],
|
|
4048
|
+
value: unitValue ?? (Number.isFinite(numericValue2) ? numericValue2 : rawValue)
|
|
4049
|
+
};
|
|
4050
|
+
}
|
|
4051
|
+
function parseOptionalTokenList(field) {
|
|
4052
|
+
return field ? [...new Set(field.values)] : [];
|
|
4053
|
+
}
|
|
4054
|
+
function parseOptionalJoinedValue(field) {
|
|
4055
|
+
if (!field) {
|
|
4056
|
+
return null;
|
|
4057
|
+
}
|
|
4058
|
+
return field.values.join(" ").trim() || null;
|
|
4059
|
+
}
|
|
4060
|
+
function parseOptionalUnitField(field, key) {
|
|
4061
|
+
return field ? parseAtlasUnitValue(singleFieldValue2(field), field.location, key) : void 0;
|
|
4062
|
+
}
|
|
4063
|
+
function parseOptionalNumberField(field, key) {
|
|
4064
|
+
return field ? parseAtlasNumber(singleFieldValue2(field), key, field.location) : void 0;
|
|
4065
|
+
}
|
|
4066
|
+
function singleFieldValue2(field) {
|
|
4067
|
+
return singleAtlasValue(field.values, field.key, field.location);
|
|
4068
|
+
}
|
|
4069
|
+
function getDraftObjectFieldSpec(key) {
|
|
4070
|
+
return DRAFT_OBJECT_FIELD_SPECS.get(key);
|
|
4071
|
+
}
|
|
4072
|
+
function validateDraftObjectFieldCompatibility(fields, objectType) {
|
|
4073
|
+
for (const field of fields) {
|
|
4074
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
4075
|
+
if (!spec) {
|
|
4076
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4077
|
+
}
|
|
4078
|
+
if (spec.legacySchema) {
|
|
4079
|
+
ensureAtlasFieldSupported(field.key, objectType, field.location);
|
|
4080
|
+
continue;
|
|
4081
|
+
}
|
|
4082
|
+
if ((field.key === "renderLabel" || field.key === "renderOrbit" || field.key === "tidalLock") && field.values.length !== 1) {
|
|
4083
|
+
throw WorldOrbitError.fromLocation(`Field "${field.key}" expects exactly one value`, field.location);
|
|
4084
|
+
}
|
|
4085
|
+
}
|
|
4086
|
+
}
|
|
4087
|
+
function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
4088
|
+
if (sourceSchemaVersion === "2.1") {
|
|
4089
|
+
return;
|
|
4090
|
+
}
|
|
4091
|
+
diagnostics.push({
|
|
4092
|
+
code: "parse.schema21.featureCompatibility",
|
|
4093
|
+
severity: "warning",
|
|
4094
|
+
source: "parse",
|
|
4095
|
+
message: `Feature "${featureName}" requires schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
4096
|
+
line: location.line,
|
|
4097
|
+
column: location.column
|
|
4098
|
+
});
|
|
4099
|
+
}
|
|
4100
|
+
function preprocessAtlasSource(source) {
|
|
4101
|
+
const chars = [...source];
|
|
4102
|
+
const comments = [];
|
|
4103
|
+
let inString = false;
|
|
4104
|
+
let inBlockComment = false;
|
|
4105
|
+
let blockCommentStart = null;
|
|
4106
|
+
let line = 1;
|
|
4107
|
+
let column = 1;
|
|
4108
|
+
for (let index = 0; index < chars.length; index++) {
|
|
4109
|
+
const ch = chars[index];
|
|
4110
|
+
const next = chars[index + 1];
|
|
4111
|
+
if (inBlockComment) {
|
|
4112
|
+
if (ch === "*" && next === "/") {
|
|
4113
|
+
chars[index] = " ";
|
|
4114
|
+
chars[index + 1] = " ";
|
|
4115
|
+
inBlockComment = false;
|
|
4116
|
+
blockCommentStart = null;
|
|
4117
|
+
index++;
|
|
4118
|
+
column += 2;
|
|
4119
|
+
continue;
|
|
4120
|
+
}
|
|
4121
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
4122
|
+
chars[index] = " ";
|
|
4123
|
+
}
|
|
4124
|
+
if (ch === "\n") {
|
|
4125
|
+
line++;
|
|
4126
|
+
column = 1;
|
|
4127
|
+
} else {
|
|
4128
|
+
column++;
|
|
4129
|
+
}
|
|
4130
|
+
continue;
|
|
4131
|
+
}
|
|
4132
|
+
if (!inString && ch === "/" && next === "*") {
|
|
4133
|
+
comments.push({ kind: "block", line, column });
|
|
4134
|
+
chars[index] = " ";
|
|
4135
|
+
chars[index + 1] = " ";
|
|
4136
|
+
inBlockComment = true;
|
|
4137
|
+
blockCommentStart = { line, column };
|
|
4138
|
+
index++;
|
|
4139
|
+
column += 2;
|
|
4140
|
+
continue;
|
|
4141
|
+
}
|
|
4142
|
+
if (!inString && ch === "#" && !isHexColorLiteral(chars, index)) {
|
|
4143
|
+
comments.push({ kind: "line", line, column });
|
|
4144
|
+
chars[index] = " ";
|
|
4145
|
+
let inner = index + 1;
|
|
4146
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
4147
|
+
chars[inner] = " ";
|
|
4148
|
+
inner++;
|
|
4149
|
+
}
|
|
4150
|
+
column += inner - index;
|
|
4151
|
+
index = inner - 1;
|
|
4152
|
+
continue;
|
|
4153
|
+
}
|
|
4154
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
4155
|
+
inString = !inString;
|
|
4156
|
+
}
|
|
4157
|
+
if (ch === "\n") {
|
|
4158
|
+
line++;
|
|
4159
|
+
column = 1;
|
|
4160
|
+
} else {
|
|
4161
|
+
column++;
|
|
4162
|
+
}
|
|
4163
|
+
}
|
|
4164
|
+
if (inBlockComment) {
|
|
4165
|
+
throw WorldOrbitError.fromLocation("Unclosed block comment", blockCommentStart ?? void 0);
|
|
4166
|
+
}
|
|
4167
|
+
return {
|
|
4168
|
+
source: chars.join(""),
|
|
4169
|
+
comments
|
|
4170
|
+
};
|
|
4171
|
+
}
|
|
4172
|
+
function isHexColorLiteral(chars, start) {
|
|
4173
|
+
let index = start + 1;
|
|
4174
|
+
let length = 0;
|
|
4175
|
+
while (index < chars.length && /[0-9a-f]/i.test(chars[index] ?? "")) {
|
|
4176
|
+
index++;
|
|
4177
|
+
length++;
|
|
4178
|
+
}
|
|
4179
|
+
if (![3, 4, 6, 8].includes(length)) {
|
|
4180
|
+
return false;
|
|
4181
|
+
}
|
|
4182
|
+
const next = chars[index];
|
|
4183
|
+
return next === void 0 || next === " " || next === " " || next === "\r" || next === "\n";
|
|
3030
4184
|
}
|
|
3031
4185
|
|
|
3032
4186
|
// packages/core/dist/load.js
|
|
3033
|
-
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0)?$/i;
|
|
4187
|
+
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;
|
|
4188
|
+
var ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
|
|
3034
4189
|
var LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
|
|
3035
4190
|
function detectWorldOrbitSchemaVersion(source) {
|
|
3036
|
-
for (const line of source.split(/\r?\n/)) {
|
|
4191
|
+
for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
|
|
3037
4192
|
const trimmed = line.trim();
|
|
3038
4193
|
if (!trimmed) {
|
|
3039
4194
|
continue;
|
|
@@ -3041,6 +4196,9 @@ var WorldOrbit = (() => {
|
|
|
3041
4196
|
if (LEGACY_DRAFT_SCHEMA_PATTERN.test(trimmed)) {
|
|
3042
4197
|
return "2.0-draft";
|
|
3043
4198
|
}
|
|
4199
|
+
if (ATLAS_SCHEMA_21_PATTERN.test(trimmed)) {
|
|
4200
|
+
return "2.1";
|
|
4201
|
+
}
|
|
3044
4202
|
if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
|
|
3045
4203
|
return "2.0";
|
|
3046
4204
|
}
|
|
@@ -3048,6 +4206,49 @@ var WorldOrbit = (() => {
|
|
|
3048
4206
|
}
|
|
3049
4207
|
return "1.0";
|
|
3050
4208
|
}
|
|
4209
|
+
function stripCommentsForSchemaDetection(source) {
|
|
4210
|
+
const chars = [...source];
|
|
4211
|
+
let inString = false;
|
|
4212
|
+
let inBlockComment = false;
|
|
4213
|
+
for (let index = 0; index < chars.length; index++) {
|
|
4214
|
+
const ch = chars[index];
|
|
4215
|
+
const next = chars[index + 1];
|
|
4216
|
+
if (inBlockComment) {
|
|
4217
|
+
if (ch === "*" && next === "/") {
|
|
4218
|
+
chars[index] = " ";
|
|
4219
|
+
chars[index + 1] = " ";
|
|
4220
|
+
inBlockComment = false;
|
|
4221
|
+
index++;
|
|
4222
|
+
continue;
|
|
4223
|
+
}
|
|
4224
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
4225
|
+
chars[index] = " ";
|
|
4226
|
+
}
|
|
4227
|
+
continue;
|
|
4228
|
+
}
|
|
4229
|
+
if (!inString && ch === "/" && next === "*") {
|
|
4230
|
+
chars[index] = " ";
|
|
4231
|
+
chars[index + 1] = " ";
|
|
4232
|
+
inBlockComment = true;
|
|
4233
|
+
index++;
|
|
4234
|
+
continue;
|
|
4235
|
+
}
|
|
4236
|
+
if (!inString && ch === "#") {
|
|
4237
|
+
chars[index] = " ";
|
|
4238
|
+
let inner = index + 1;
|
|
4239
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
4240
|
+
chars[inner] = " ";
|
|
4241
|
+
inner++;
|
|
4242
|
+
}
|
|
4243
|
+
index = inner - 1;
|
|
4244
|
+
continue;
|
|
4245
|
+
}
|
|
4246
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
4247
|
+
inString = !inString;
|
|
4248
|
+
}
|
|
4249
|
+
}
|
|
4250
|
+
return chars.join("");
|
|
4251
|
+
}
|
|
3051
4252
|
function loadWorldOrbitSource(source) {
|
|
3052
4253
|
const result = loadWorldOrbitSourceWithDiagnostics(source);
|
|
3053
4254
|
if (!result.ok || !result.value) {
|
|
@@ -3058,36 +4259,36 @@ var WorldOrbit = (() => {
|
|
|
3058
4259
|
}
|
|
3059
4260
|
function loadWorldOrbitSourceWithDiagnostics(source) {
|
|
3060
4261
|
const schemaVersion = detectWorldOrbitSchemaVersion(source);
|
|
3061
|
-
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft") {
|
|
4262
|
+
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1") {
|
|
3062
4263
|
return loadAtlasSourceWithDiagnostics(source, schemaVersion);
|
|
3063
4264
|
}
|
|
3064
4265
|
let ast;
|
|
3065
4266
|
try {
|
|
3066
4267
|
ast = parseWorldOrbit(source);
|
|
3067
|
-
} catch (
|
|
4268
|
+
} catch (error2) {
|
|
3068
4269
|
return {
|
|
3069
4270
|
ok: false,
|
|
3070
4271
|
value: null,
|
|
3071
|
-
diagnostics: [diagnosticFromError(
|
|
4272
|
+
diagnostics: [diagnosticFromError(error2, "parse")]
|
|
3072
4273
|
};
|
|
3073
4274
|
}
|
|
3074
4275
|
let document2;
|
|
3075
4276
|
try {
|
|
3076
4277
|
document2 = normalizeDocument(ast);
|
|
3077
|
-
} catch (
|
|
4278
|
+
} catch (error2) {
|
|
3078
4279
|
return {
|
|
3079
4280
|
ok: false,
|
|
3080
4281
|
value: null,
|
|
3081
|
-
diagnostics: [diagnosticFromError(
|
|
4282
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
3082
4283
|
};
|
|
3083
4284
|
}
|
|
3084
4285
|
try {
|
|
3085
4286
|
validateDocument(document2);
|
|
3086
|
-
} catch (
|
|
4287
|
+
} catch (error2) {
|
|
3087
4288
|
return {
|
|
3088
4289
|
ok: false,
|
|
3089
4290
|
value: null,
|
|
3090
|
-
diagnostics: [diagnosticFromError(
|
|
4291
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
3091
4292
|
};
|
|
3092
4293
|
}
|
|
3093
4294
|
return {
|
|
@@ -3107,30 +4308,29 @@ var WorldOrbit = (() => {
|
|
|
3107
4308
|
let atlasDocument;
|
|
3108
4309
|
try {
|
|
3109
4310
|
atlasDocument = parseWorldOrbitAtlas(source);
|
|
3110
|
-
} catch (
|
|
4311
|
+
} catch (error2) {
|
|
3111
4312
|
return {
|
|
3112
4313
|
ok: false,
|
|
3113
4314
|
value: null,
|
|
3114
|
-
diagnostics: [diagnosticFromError(
|
|
4315
|
+
diagnostics: [diagnosticFromError(error2, "parse", "load.atlas.failed")]
|
|
3115
4316
|
};
|
|
3116
4317
|
}
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
document2 = materializeAtlasDocument(atlasDocument);
|
|
3120
|
-
} catch (error) {
|
|
4318
|
+
const atlasDiagnostics = [...atlasDocument.diagnostics];
|
|
4319
|
+
if (atlasDiagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
3121
4320
|
return {
|
|
3122
4321
|
ok: false,
|
|
3123
4322
|
value: null,
|
|
3124
|
-
diagnostics:
|
|
4323
|
+
diagnostics: atlasDiagnostics
|
|
3125
4324
|
};
|
|
3126
4325
|
}
|
|
4326
|
+
let document2;
|
|
3127
4327
|
try {
|
|
3128
|
-
|
|
3129
|
-
} catch (
|
|
4328
|
+
document2 = materializeAtlasDocument(atlasDocument);
|
|
4329
|
+
} catch (error2) {
|
|
3130
4330
|
return {
|
|
3131
4331
|
ok: false,
|
|
3132
4332
|
value: null,
|
|
3133
|
-
diagnostics: [diagnosticFromError(
|
|
4333
|
+
diagnostics: [diagnosticFromError(error2, "normalize", "load.atlas.materialize.failed")]
|
|
3134
4334
|
};
|
|
3135
4335
|
}
|
|
3136
4336
|
const loaded = {
|
|
@@ -3139,12 +4339,12 @@ var WorldOrbit = (() => {
|
|
|
3139
4339
|
document: document2,
|
|
3140
4340
|
atlasDocument,
|
|
3141
4341
|
draftDocument: atlasDocument,
|
|
3142
|
-
diagnostics:
|
|
4342
|
+
diagnostics: atlasDiagnostics
|
|
3143
4343
|
};
|
|
3144
4344
|
return {
|
|
3145
4345
|
ok: true,
|
|
3146
4346
|
value: loaded,
|
|
3147
|
-
diagnostics:
|
|
4347
|
+
diagnostics: atlasDiagnostics
|
|
3148
4348
|
};
|
|
3149
4349
|
}
|
|
3150
4350
|
|
|
@@ -3152,6 +4352,7 @@ var WorldOrbit = (() => {
|
|
|
3152
4352
|
var DEFAULT_LAYERS = {
|
|
3153
4353
|
background: true,
|
|
3154
4354
|
guides: true,
|
|
4355
|
+
relations: true,
|
|
3155
4356
|
orbits: true,
|
|
3156
4357
|
objects: true,
|
|
3157
4358
|
labels: true,
|
|
@@ -3166,6 +4367,7 @@ var WorldOrbit = (() => {
|
|
|
3166
4367
|
backgroundGlow: "rgba(240, 180, 100, 0.18)",
|
|
3167
4368
|
panel: "rgba(7, 17, 27, 0.9)",
|
|
3168
4369
|
panelLine: "rgba(168, 207, 242, 0.18)",
|
|
4370
|
+
relation: "rgba(240, 180, 100, 0.42)",
|
|
3169
4371
|
orbit: "rgba(163, 209, 255, 0.24)",
|
|
3170
4372
|
orbitBand: "rgba(255, 190, 120, 0.28)",
|
|
3171
4373
|
guide: "rgba(255, 255, 255, 0.04)",
|
|
@@ -3188,6 +4390,7 @@ var WorldOrbit = (() => {
|
|
|
3188
4390
|
backgroundGlow: "rgba(120, 255, 215, 0.16)",
|
|
3189
4391
|
panel: "rgba(7, 20, 30, 0.9)",
|
|
3190
4392
|
panelLine: "rgba(120, 255, 215, 0.16)",
|
|
4393
|
+
relation: "rgba(156, 231, 255, 0.42)",
|
|
3191
4394
|
orbit: "rgba(120, 255, 215, 0.2)",
|
|
3192
4395
|
orbitBand: "rgba(137, 185, 255, 0.24)",
|
|
3193
4396
|
guide: "rgba(255, 255, 255, 0.035)",
|
|
@@ -3210,6 +4413,7 @@ var WorldOrbit = (() => {
|
|
|
3210
4413
|
backgroundGlow: "rgba(255, 127, 95, 0.18)",
|
|
3211
4414
|
panel: "rgba(24, 9, 13, 0.9)",
|
|
3212
4415
|
panelLine: "rgba(255, 166, 149, 0.16)",
|
|
4416
|
+
relation: "rgba(255, 178, 125, 0.42)",
|
|
3213
4417
|
orbit: "rgba(255, 188, 164, 0.22)",
|
|
3214
4418
|
orbitBand: "rgba(255, 214, 139, 0.24)",
|
|
3215
4419
|
guide: "rgba(255, 255, 255, 0.03)",
|
|
@@ -3291,7 +4495,11 @@ var WorldOrbit = (() => {
|
|
|
3291
4495
|
return false;
|
|
3292
4496
|
}
|
|
3293
4497
|
if (filter.groupIds?.length && (!object.groupId || !filter.groupIds.includes(object.groupId))) {
|
|
3294
|
-
|
|
4498
|
+
const hasSemanticMatch = object.semanticGroupIds.length > 0 && filter.groupIds.some((groupId) => object.semanticGroupIds.includes(groupId));
|
|
4499
|
+
const hasLegacyMatch = Boolean(object.groupId && filter.groupIds.includes(object.groupId));
|
|
4500
|
+
if (!hasSemanticMatch && !hasLegacyMatch) {
|
|
4501
|
+
return false;
|
|
4502
|
+
}
|
|
3295
4503
|
}
|
|
3296
4504
|
if (filter.tags?.length) {
|
|
3297
4505
|
const objectTags = Array.isArray(object.object.properties.tags) ? object.object.properties.tags.filter((entry) => typeof entry === "string") : [];
|
|
@@ -3347,6 +4555,7 @@ var WorldOrbit = (() => {
|
|
|
3347
4555
|
const imageDefinitions = buildImageDefinitions(visibleObjects);
|
|
3348
4556
|
const orbitMarkup = layers.orbits ? renderOrbitLayer(scene, visibleObjectIds, layers.structures) : { back: "", front: "" };
|
|
3349
4557
|
const leaderMarkup = layers.guides ? scene.leaders.filter((leader) => !leader.hidden).filter((leader) => visibleObjectIds.has(leader.objectId)).filter((leader) => layers.structures || !isStructureLike(leader.object)).map((leader) => `<line class="wo-leader wo-leader-${leader.mode}" x1="${leader.x1}" y1="${leader.y1}" x2="${leader.x2}" y2="${leader.y2}" data-render-id="${escapeXml(leader.renderId)}" data-group-id="${escapeAttribute(leader.groupId ?? "")}" />`).join("") : "";
|
|
4558
|
+
const relationMarkup = layers.relations ? scene.relations.filter((relation) => !relation.hidden).filter((relation) => visibleObjectIds.has(relation.fromObjectId) && visibleObjectIds.has(relation.toObjectId)).map((relation) => `<line class="wo-relation" x1="${relation.x1}" y1="${relation.y1}" x2="${relation.x2}" y2="${relation.y2}" data-render-id="${escapeXml(relation.renderId)}" data-relation-id="${escapeAttribute(relation.relationId)}" />`).join("") : "";
|
|
3350
4559
|
const objectMarkup = layers.objects ? visibleObjects.map((object) => renderSceneObject(object, options.selectedObjectId ?? null, theme)).join("") : "";
|
|
3351
4560
|
const labelMarkup = layers.labels ? visibleLabels.map((label) => renderSceneLabel(scene, label, options.selectedObjectId ?? null)).join("") : "";
|
|
3352
4561
|
const metadataMarkup = layers.metadata ? `<text class="wo-title" x="56" y="64">${escapeXml(scene.title)}</text>
|
|
@@ -3381,6 +4590,7 @@ var WorldOrbit = (() => {
|
|
|
3381
4590
|
.wo-orbit-back { opacity: 0.38; stroke-dasharray: 8 6; }
|
|
3382
4591
|
.wo-orbit-front { opacity: 0.9; }
|
|
3383
4592
|
.wo-orbit-band { stroke: ${theme.orbitBand}; stroke-linecap: round; }
|
|
4593
|
+
.wo-relation { stroke: ${theme.relation}; stroke-width: 2; stroke-dasharray: 10 6; }
|
|
3384
4594
|
.wo-leader { stroke: ${theme.leader}; stroke-width: 1.5; stroke-dasharray: 6 5; }
|
|
3385
4595
|
.wo-label { fill: ${theme.ink}; font-family: ${theme.fontFamily}; font-weight: 600; letter-spacing: 0.02em; }
|
|
3386
4596
|
.wo-label-secondary { fill: ${theme.muted}; font-family: ${theme.fontFamily}; font-weight: 500; }
|
|
@@ -3414,6 +4624,7 @@ var WorldOrbit = (() => {
|
|
|
3414
4624
|
<g data-worldorbit-world-content="true">
|
|
3415
4625
|
${layers.orbits ? `<g data-layer-id="orbits-back">${orbitMarkup.back}</g>` : ""}
|
|
3416
4626
|
${layers.guides ? `<g data-layer-id="guides">${leaderMarkup}</g>` : ""}
|
|
4627
|
+
${layers.relations ? `<g data-layer-id="relations">${relationMarkup}</g>` : ""}
|
|
3417
4628
|
${layers.objects ? `<g data-layer-id="objects">${objectMarkup}</g>` : ""}
|
|
3418
4629
|
${layers.orbits ? `<g data-layer-id="orbits-front">${orbitMarkup.front}</g>` : ""}
|
|
3419
4630
|
${layers.labels ? `<g data-layer-id="labels">${labelMarkup}</g>` : ""}
|
|
@@ -3457,10 +4668,11 @@ var WorldOrbit = (() => {
|
|
|
3457
4668
|
function renderSceneObject(sceneObject, selectedObjectId, theme) {
|
|
3458
4669
|
const { object, x, y, radius, visualRadius } = sceneObject;
|
|
3459
4670
|
const selectionClass = selectedObjectId === sceneObject.objectId ? " wo-object-selected" : "";
|
|
4671
|
+
const kindClass = object.properties.kind ? ` wo-kind-${String(object.properties.kind).toLowerCase().replace(/[^a-z0-9-]/g, "-")}` : "";
|
|
3460
4672
|
const palette = resolveObjectPalette(sceneObject, theme);
|
|
3461
4673
|
const imageMarkup = renderObjectImage(sceneObject);
|
|
3462
4674
|
const outlineMarkup = imageMarkup ? renderObjectBody(object, x, y, radius, palette, { outlineOnly: true }) : "";
|
|
3463
|
-
return `<g class="wo-object wo-object-${object.type}${selectionClass}" data-object-id="${escapeXml(sceneObject.objectId)}" data-parent-id="${escapeAttribute(sceneObject.parentId ?? "")}" data-group-id="${escapeAttribute(sceneObject.groupId ?? "")}" data-render-id="${escapeXml(sceneObject.renderId)}" tabindex="0" role="button" aria-label="${escapeXml(`${object.type} ${sceneObject.objectId}`)}">
|
|
4675
|
+
return `<g class="wo-object wo-object-${object.type}${kindClass}${selectionClass}" data-object-id="${escapeXml(sceneObject.objectId)}" data-parent-id="${escapeAttribute(sceneObject.parentId ?? "")}" data-group-id="${escapeAttribute(sceneObject.groupId ?? "")}" data-render-id="${escapeXml(sceneObject.renderId)}" tabindex="0" role="button" aria-label="${escapeXml(`${object.type} ${sceneObject.objectId}`)}">
|
|
3464
4676
|
<circle class="wo-selection-ring" cx="${x}" cy="${y}" r="${visualRadius + 8}" />
|
|
3465
4677
|
${renderAtmosphere(sceneObject, palette)}
|
|
3466
4678
|
${renderObjectBody(object, x, y, radius, palette)}
|
|
@@ -3494,8 +4706,33 @@ var WorldOrbit = (() => {
|
|
|
3494
4706
|
<circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
3495
4707
|
case "structure":
|
|
3496
4708
|
return `<polygon points="${diamondPoints(x, y, radius)}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
3497
|
-
case "phenomenon":
|
|
4709
|
+
case "phenomenon": {
|
|
4710
|
+
const kind = String(object.properties.kind ?? "").toLowerCase().replace(/_/g, "-");
|
|
4711
|
+
if (options.outlineOnly) {
|
|
4712
|
+
if (kind === "black-hole" || kind === "nebula" || kind === "galaxy" || kind === "dwarf-galaxy") {
|
|
4713
|
+
return `<circle cx="${x}" cy="${y}" r="${radius}" fill="transparent" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
4714
|
+
}
|
|
4715
|
+
return `<polygon points="${phenomenonPoints(x, y, radius)}" fill="transparent" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
4716
|
+
}
|
|
4717
|
+
if (kind === "black-hole") {
|
|
4718
|
+
return `<ellipse cx="${x}" cy="${y}" rx="${radius * 2.4}" ry="${radius * 0.55}" fill="none" stroke="${palette.accentRing ?? palette.stroke}" stroke-width="3.5" />
|
|
4719
|
+
<circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" stroke="${palette.stroke}" stroke-width="2" />`;
|
|
4720
|
+
}
|
|
4721
|
+
if (kind === "galaxy") {
|
|
4722
|
+
return `<ellipse cx="${x}" cy="${y}" rx="${radius * 2.6}" ry="${radius}" fill="${palette.halo ?? "none"}" stroke="none" />
|
|
4723
|
+
<ellipse cx="${x}" cy="${y}" rx="${radius * 1.5}" ry="${radius * 0.42}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.2" />
|
|
4724
|
+
<circle cx="${x}" cy="${y}" r="${radius * 0.28}" fill="${palette.core ?? "#fff"}" stroke="none" />`;
|
|
4725
|
+
}
|
|
4726
|
+
if (kind === "dwarf-galaxy") {
|
|
4727
|
+
return `<ellipse cx="${x}" cy="${y}" rx="${radius * 1.6}" ry="${radius * 0.55}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1" />
|
|
4728
|
+
<circle cx="${x}" cy="${y}" r="${radius * 0.25}" fill="${palette.core ?? "#fff"}" stroke="none" />`;
|
|
4729
|
+
}
|
|
4730
|
+
if (kind === "nebula") {
|
|
4731
|
+
return `<circle cx="${x}" cy="${y}" r="${radius * 2.2}" fill="${palette.halo ?? "none"}" stroke="none" />
|
|
4732
|
+
<circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1" />`;
|
|
4733
|
+
}
|
|
3498
4734
|
return `<polygon points="${phenomenonPoints(x, y, radius)}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
4735
|
+
}
|
|
3499
4736
|
}
|
|
3500
4737
|
}
|
|
3501
4738
|
function renderAtmosphere(sceneObject, palette) {
|
|
@@ -3564,7 +4801,8 @@ var WorldOrbit = (() => {
|
|
|
3564
4801
|
}
|
|
3565
4802
|
}
|
|
3566
4803
|
function resolveObjectPalette(sceneObject, theme) {
|
|
3567
|
-
const
|
|
4804
|
+
const kind = String(sceneObject.object.properties.kind ?? "").toLowerCase().replace(/_/g, "-");
|
|
4805
|
+
const base = basePaletteForType(sceneObject.object.type, kind, theme);
|
|
3568
4806
|
const customFill = sceneObject.fillColor && isColorLike(sceneObject.fillColor) ? sceneObject.fillColor : base.fill;
|
|
3569
4807
|
const albedo = numericValue(sceneObject.object.properties.albedo);
|
|
3570
4808
|
const temperature = numericValue(sceneObject.object.properties.temperature);
|
|
@@ -3580,7 +4818,7 @@ var WorldOrbit = (() => {
|
|
|
3580
4818
|
tail: sceneObject.object.type === "comet" ? rgbaString(mixColors(fill, "#ffffff", 0.5) ?? fill, 0.72) : void 0
|
|
3581
4819
|
};
|
|
3582
4820
|
}
|
|
3583
|
-
function basePaletteForType(type, theme) {
|
|
4821
|
+
function basePaletteForType(type, kind, theme) {
|
|
3584
4822
|
switch (type) {
|
|
3585
4823
|
case "star":
|
|
3586
4824
|
return {
|
|
@@ -3602,8 +4840,26 @@ var WorldOrbit = (() => {
|
|
|
3602
4840
|
case "structure":
|
|
3603
4841
|
return { fill: theme.accentStrong, stroke: "#fff2ea" };
|
|
3604
4842
|
case "phenomenon":
|
|
3605
|
-
return
|
|
4843
|
+
return kindPhenomenonPalette(kind);
|
|
4844
|
+
}
|
|
4845
|
+
}
|
|
4846
|
+
function kindPhenomenonPalette(kind) {
|
|
4847
|
+
if (kind === "galaxy") {
|
|
4848
|
+
return { fill: "rgba(165,125,255,0.55)", stroke: "rgba(210,185,255,0.75)", halo: "rgba(160,120,255,0.10)", core: "#ede0ff" };
|
|
4849
|
+
}
|
|
4850
|
+
if (kind === "dwarf-galaxy") {
|
|
4851
|
+
return { fill: "rgba(190,165,255,0.45)", stroke: "rgba(220,205,255,0.75)", core: "#ddd0ff" };
|
|
4852
|
+
}
|
|
4853
|
+
if (kind === "black-hole") {
|
|
4854
|
+
return { fill: "#040408", stroke: "#ff6a00", accentRing: "rgba(255,140,20,0.72)" };
|
|
4855
|
+
}
|
|
4856
|
+
if (kind === "nebula") {
|
|
4857
|
+
return { fill: "rgba(105,205,255,0.45)", stroke: "rgba(180,235,255,0.72)", halo: "rgba(100,200,255,0.08)" };
|
|
4858
|
+
}
|
|
4859
|
+
if (kind === "void") {
|
|
4860
|
+
return { fill: "#05080f", stroke: "rgba(130,160,255,0.4)" };
|
|
3606
4861
|
}
|
|
4862
|
+
return { fill: "#78ffd7", stroke: "#e9fff7" };
|
|
3607
4863
|
}
|
|
3608
4864
|
function applyTemperatureAndAlbedo(baseColor, temperature, albedo, type) {
|
|
3609
4865
|
let nextColor = baseColor;
|
|
@@ -3870,11 +5126,11 @@ var WorldOrbit = (() => {
|
|
|
3870
5126
|
});
|
|
3871
5127
|
}
|
|
3872
5128
|
return `<figure class="${escapeAttribute3(options.className ?? "worldorbit-block worldorbit-static")}">${renderSceneToSvg(scene, options)}</figure>`;
|
|
3873
|
-
} catch (
|
|
5129
|
+
} catch (error2) {
|
|
3874
5130
|
if (options.strict) {
|
|
3875
|
-
throw
|
|
5131
|
+
throw error2;
|
|
3876
5132
|
}
|
|
3877
|
-
return renderWorldOrbitError(
|
|
5133
|
+
return renderWorldOrbitError(error2 instanceof Error ? error2.message : String(error2));
|
|
3878
5134
|
}
|
|
3879
5135
|
}
|
|
3880
5136
|
function renderWorldOrbitError(message) {
|
|
@@ -3975,5 +5231,5 @@ var WorldOrbit = (() => {
|
|
|
3975
5231
|
}
|
|
3976
5232
|
return (node.children ?? []).map((child) => collectText(child)).join("");
|
|
3977
5233
|
}
|
|
3978
|
-
return __toCommonJS(
|
|
5234
|
+
return __toCommonJS(index_exports);
|
|
3979
5235
|
})();
|