worldorbit 2.5.15 → 2.5.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/browser/core/dist/index.js +2444 -342
- package/dist/browser/editor/dist/index.js +11702 -0
- package/dist/browser/markdown/dist/index.js +2207 -392
- package/dist/browser/viewer/dist/index.js +2302 -382
- package/dist/unpkg/core/dist/index.js +2447 -345
- package/dist/unpkg/editor/dist/index.js +11727 -0
- package/dist/unpkg/markdown/dist/index.js +2210 -395
- package/dist/unpkg/viewer/dist/index.js +2305 -385
- package/dist/unpkg/worldorbit-core.min.js +12 -12
- package/dist/unpkg/worldorbit-editor.min.js +894 -0
- package/dist/unpkg/worldorbit-markdown.min.js +66 -58
- package/dist/unpkg/worldorbit-viewer.min.js +76 -68
- package/dist/unpkg/worldorbit.js +797 -78
- package/dist/unpkg/worldorbit.min.js +80 -72
- package/package.json +3 -2
- package/packages/core/dist/atlas-edit.js +74 -0
- package/packages/core/dist/atlas-validate.js +122 -8
- package/packages/core/dist/draft-parse.js +212 -8
- package/packages/core/dist/draft.d.ts +5 -2
- package/packages/core/dist/draft.js +59 -3
- package/packages/core/dist/format.js +63 -1
- package/packages/core/dist/normalize.js +1 -0
- package/packages/core/dist/scene.js +248 -46
- package/packages/core/dist/types.d.ts +41 -2
- package/packages/editor/dist/editor.d.ts +2 -0
- package/packages/editor/dist/editor.js +3578 -0
- package/packages/editor/dist/index.d.ts +2 -0
- package/packages/editor/dist/index.js +1 -0
- package/packages/editor/dist/types.d.ts +55 -0
- package/packages/editor/dist/types.js +1 -0
- package/packages/markdown/dist/html.d.ts +3 -0
- package/packages/markdown/dist/html.js +57 -0
- package/packages/markdown/dist/index.d.ts +4 -0
- package/packages/markdown/dist/index.js +3 -0
- package/packages/markdown/dist/rehype.d.ts +10 -0
- package/packages/markdown/dist/rehype.js +49 -0
- package/packages/markdown/dist/remark.d.ts +9 -0
- package/packages/markdown/dist/remark.js +28 -0
- package/packages/markdown/dist/types.d.ts +11 -0
- package/packages/markdown/dist/types.js +1 -0
- package/packages/viewer/dist/atlas-state.js +6 -0
- package/packages/viewer/dist/atlas-viewer.js +1 -0
- package/packages/viewer/dist/render.js +31 -2
- package/packages/viewer/dist/theme.js +1 -0
- package/packages/viewer/dist/tooltip.js +9 -0
- package/packages/viewer/dist/types.d.ts +8 -1
- package/packages/viewer/dist/viewer.js +12 -1
|
@@ -19,8 +19,8 @@ var WorldOrbit = (() => {
|
|
|
19
19
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
20
|
|
|
21
21
|
// packages/viewer/dist/index.js
|
|
22
|
-
var
|
|
23
|
-
__export(
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
24
|
DEFAULT_VIEWER_STATE: () => DEFAULT_VIEWER_STATE,
|
|
25
25
|
WORLD_LAYER_ID: () => WORLD_LAYER_ID,
|
|
26
26
|
clampScale: () => clampScale,
|
|
@@ -60,6 +60,8 @@ var WorldOrbit = (() => {
|
|
|
60
60
|
var DEFAULT_LAYERS = {
|
|
61
61
|
background: true,
|
|
62
62
|
guides: true,
|
|
63
|
+
relations: true,
|
|
64
|
+
events: true,
|
|
63
65
|
orbits: true,
|
|
64
66
|
objects: true,
|
|
65
67
|
labels: true,
|
|
@@ -74,6 +76,7 @@ var WorldOrbit = (() => {
|
|
|
74
76
|
backgroundGlow: "rgba(240, 180, 100, 0.18)",
|
|
75
77
|
panel: "rgba(7, 17, 27, 0.9)",
|
|
76
78
|
panelLine: "rgba(168, 207, 242, 0.18)",
|
|
79
|
+
relation: "rgba(240, 180, 100, 0.42)",
|
|
77
80
|
orbit: "rgba(163, 209, 255, 0.24)",
|
|
78
81
|
orbitBand: "rgba(255, 190, 120, 0.28)",
|
|
79
82
|
guide: "rgba(255, 255, 255, 0.04)",
|
|
@@ -96,6 +99,7 @@ var WorldOrbit = (() => {
|
|
|
96
99
|
backgroundGlow: "rgba(120, 255, 215, 0.16)",
|
|
97
100
|
panel: "rgba(7, 20, 30, 0.9)",
|
|
98
101
|
panelLine: "rgba(120, 255, 215, 0.16)",
|
|
102
|
+
relation: "rgba(156, 231, 255, 0.42)",
|
|
99
103
|
orbit: "rgba(120, 255, 215, 0.2)",
|
|
100
104
|
orbitBand: "rgba(137, 185, 255, 0.24)",
|
|
101
105
|
guide: "rgba(255, 255, 255, 0.035)",
|
|
@@ -118,6 +122,7 @@ var WorldOrbit = (() => {
|
|
|
118
122
|
backgroundGlow: "rgba(255, 127, 95, 0.18)",
|
|
119
123
|
panel: "rgba(24, 9, 13, 0.9)",
|
|
120
124
|
panelLine: "rgba(255, 166, 149, 0.16)",
|
|
125
|
+
relation: "rgba(255, 178, 125, 0.42)",
|
|
121
126
|
orbit: "rgba(255, 188, 164, 0.22)",
|
|
122
127
|
orbitBand: "rgba(255, 214, 139, 0.24)",
|
|
123
128
|
guide: "rgba(255, 255, 255, 0.03)",
|
|
@@ -208,12 +213,14 @@ var WorldOrbit = (() => {
|
|
|
208
213
|
return {
|
|
209
214
|
version: "2.0",
|
|
210
215
|
viewpointId,
|
|
216
|
+
activeEventId: renderOptions.activeEventId ?? null,
|
|
211
217
|
viewerState: { ...viewerState },
|
|
212
218
|
renderOptions: {
|
|
213
219
|
preset: renderOptions.preset,
|
|
214
220
|
projection: renderOptions.projection,
|
|
215
221
|
layers: renderOptions.layers ? { ...renderOptions.layers } : void 0,
|
|
216
|
-
scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : void 0
|
|
222
|
+
scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : void 0,
|
|
223
|
+
activeEventId: renderOptions.activeEventId ?? null
|
|
217
224
|
},
|
|
218
225
|
filter: normalizeViewerFilter(filter)
|
|
219
226
|
};
|
|
@@ -226,6 +233,7 @@ var WorldOrbit = (() => {
|
|
|
226
233
|
return {
|
|
227
234
|
version: "2.0",
|
|
228
235
|
viewpointId: raw.viewpointId ?? null,
|
|
236
|
+
activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null,
|
|
229
237
|
viewerState: {
|
|
230
238
|
scale: raw.viewerState?.scale ?? 1,
|
|
231
239
|
rotationDeg: raw.viewerState?.rotationDeg ?? 0,
|
|
@@ -237,7 +245,8 @@ var WorldOrbit = (() => {
|
|
|
237
245
|
preset: raw.renderOptions?.preset,
|
|
238
246
|
projection: raw.renderOptions?.projection,
|
|
239
247
|
layers: raw.renderOptions?.layers ? { ...raw.renderOptions.layers } : void 0,
|
|
240
|
-
scaleModel: raw.renderOptions?.scaleModel ? { ...raw.renderOptions.scaleModel } : void 0
|
|
248
|
+
scaleModel: raw.renderOptions?.scaleModel ? { ...raw.renderOptions.scaleModel } : void 0,
|
|
249
|
+
activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null
|
|
241
250
|
},
|
|
242
251
|
filter: normalizeViewerFilter(raw.filter ?? null)
|
|
243
252
|
};
|
|
@@ -253,7 +262,8 @@ var WorldOrbit = (() => {
|
|
|
253
262
|
renderOptions: {
|
|
254
263
|
...atlasState.renderOptions,
|
|
255
264
|
layers: atlasState.renderOptions.layers ? { ...atlasState.renderOptions.layers } : void 0,
|
|
256
|
-
scaleModel: atlasState.renderOptions.scaleModel ? { ...atlasState.renderOptions.scaleModel } : void 0
|
|
265
|
+
scaleModel: atlasState.renderOptions.scaleModel ? { ...atlasState.renderOptions.scaleModel } : void 0,
|
|
266
|
+
activeEventId: atlasState.renderOptions.activeEventId ?? null
|
|
257
267
|
},
|
|
258
268
|
filter: atlasState.filter ? { ...atlasState.filter } : null
|
|
259
269
|
}
|
|
@@ -270,6 +280,8 @@ var WorldOrbit = (() => {
|
|
|
270
280
|
return {
|
|
271
281
|
background: viewpoint.layers.background,
|
|
272
282
|
guides: viewpoint.layers.guides,
|
|
283
|
+
relations: viewpoint.layers.relations,
|
|
284
|
+
events: viewpoint.layers.events,
|
|
273
285
|
orbits: viewpoint.layers["orbits-front"] === void 0 && viewpoint.layers["orbits-back"] === void 0 ? void 0 : viewpoint.layers["orbits-front"] !== false || viewpoint.layers["orbits-back"] !== false,
|
|
274
286
|
objects: viewpoint.layers.objects,
|
|
275
287
|
labels: viewpoint.layers.labels,
|
|
@@ -307,7 +319,11 @@ var WorldOrbit = (() => {
|
|
|
307
319
|
return false;
|
|
308
320
|
}
|
|
309
321
|
if (filter.groupIds?.length && (!object.groupId || !filter.groupIds.includes(object.groupId))) {
|
|
310
|
-
|
|
322
|
+
const hasSemanticMatch = object.semanticGroupIds.length > 0 && filter.groupIds.some((groupId) => object.semanticGroupIds.includes(groupId));
|
|
323
|
+
const hasLegacyMatch = Boolean(object.groupId && filter.groupIds.includes(object.groupId));
|
|
324
|
+
if (!hasSemanticMatch && !hasLegacyMatch) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
311
327
|
}
|
|
312
328
|
if (filter.tags?.length) {
|
|
313
329
|
const objectTags = Array.isArray(object.object.properties.tags) ? object.object.properties.tags.filter((entry) => typeof entry === "string") : [];
|
|
@@ -674,13 +690,13 @@ var WorldOrbit = (() => {
|
|
|
674
690
|
function unitFamilyAllowsUnit(family, unit) {
|
|
675
691
|
switch (family) {
|
|
676
692
|
case "distance":
|
|
677
|
-
return unit === null || ["au", "km", "re", "sol"].includes(unit);
|
|
693
|
+
return unit === null || ["au", "km", "m", "ly", "pc", "kpc", "re", "sol"].includes(unit);
|
|
678
694
|
case "radius":
|
|
679
|
-
return unit === null || ["km", "re", "sol"].includes(unit);
|
|
695
|
+
return unit === null || ["km", "m", "re", "rj", "sol"].includes(unit);
|
|
680
696
|
case "mass":
|
|
681
|
-
return unit === null || ["me", "sol"].includes(unit);
|
|
697
|
+
return unit === null || ["me", "mj", "sol"].includes(unit);
|
|
682
698
|
case "duration":
|
|
683
|
-
return unit === null || ["h", "d", "y"].includes(unit);
|
|
699
|
+
return unit === null || ["s", "min", "h", "d", "y", "ky", "my", "gy"].includes(unit);
|
|
684
700
|
case "angle":
|
|
685
701
|
return unit === null || unit === "deg";
|
|
686
702
|
case "generic":
|
|
@@ -884,7 +900,7 @@ var WorldOrbit = (() => {
|
|
|
884
900
|
}
|
|
885
901
|
|
|
886
902
|
// packages/core/dist/normalize.js
|
|
887
|
-
var UNIT_PATTERN = /^(-?\d+(?:\.\d+)?)(au|km|re|sol|
|
|
903
|
+
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)?$/;
|
|
888
904
|
var BOOLEAN_VALUES = /* @__PURE__ */ new Map([
|
|
889
905
|
["true", true],
|
|
890
906
|
["false", false],
|
|
@@ -909,7 +925,11 @@ var WorldOrbit = (() => {
|
|
|
909
925
|
return {
|
|
910
926
|
format: "worldorbit",
|
|
911
927
|
version: "1.0",
|
|
928
|
+
schemaVersion: "1.0",
|
|
912
929
|
system,
|
|
930
|
+
groups: [],
|
|
931
|
+
relations: [],
|
|
932
|
+
events: [],
|
|
913
933
|
objects
|
|
914
934
|
};
|
|
915
935
|
}
|
|
@@ -919,13 +939,17 @@ var WorldOrbit = (() => {
|
|
|
919
939
|
const fieldMap = collectFields(mergedFields);
|
|
920
940
|
const placement = extractPlacement(node.objectType, fieldMap);
|
|
921
941
|
const properties = normalizeProperties(fieldMap);
|
|
922
|
-
const
|
|
942
|
+
const info2 = normalizeInfo(node.infoEntries);
|
|
923
943
|
if (node.objectType === "system") {
|
|
924
944
|
return {
|
|
925
945
|
type: "system",
|
|
926
946
|
id: node.name,
|
|
947
|
+
title: typeof properties.title === "string" ? properties.title : null,
|
|
948
|
+
description: null,
|
|
949
|
+
epoch: null,
|
|
950
|
+
referencePlane: null,
|
|
927
951
|
properties,
|
|
928
|
-
info
|
|
952
|
+
info: info2
|
|
929
953
|
};
|
|
930
954
|
}
|
|
931
955
|
return {
|
|
@@ -933,7 +957,7 @@ var WorldOrbit = (() => {
|
|
|
933
957
|
id: node.name,
|
|
934
958
|
properties,
|
|
935
959
|
placement,
|
|
936
|
-
info
|
|
960
|
+
info: info2
|
|
937
961
|
};
|
|
938
962
|
}
|
|
939
963
|
function validateFieldCompatibility(objectType, fields) {
|
|
@@ -1063,14 +1087,14 @@ var WorldOrbit = (() => {
|
|
|
1063
1087
|
}
|
|
1064
1088
|
}
|
|
1065
1089
|
function normalizeInfo(entries) {
|
|
1066
|
-
const
|
|
1090
|
+
const info2 = {};
|
|
1067
1091
|
for (const entry of entries) {
|
|
1068
|
-
if (entry.key in
|
|
1092
|
+
if (entry.key in info2) {
|
|
1069
1093
|
throw WorldOrbitError.fromLocation(`Duplicate info key "${entry.key}"`, entry.location);
|
|
1070
1094
|
}
|
|
1071
|
-
|
|
1095
|
+
info2[entry.key] = entry.value;
|
|
1072
1096
|
}
|
|
1073
|
-
return
|
|
1097
|
+
return info2;
|
|
1074
1098
|
}
|
|
1075
1099
|
function parseAtReference(target, location) {
|
|
1076
1100
|
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
@@ -1241,37 +1265,41 @@ var WorldOrbit = (() => {
|
|
|
1241
1265
|
}
|
|
1242
1266
|
|
|
1243
1267
|
// packages/core/dist/diagnostics.js
|
|
1244
|
-
function diagnosticFromError(
|
|
1245
|
-
if (
|
|
1268
|
+
function diagnosticFromError(error2, source, code = `${source}.failed`) {
|
|
1269
|
+
if (error2 instanceof WorldOrbitError) {
|
|
1246
1270
|
return {
|
|
1247
1271
|
code,
|
|
1248
1272
|
severity: "error",
|
|
1249
1273
|
source,
|
|
1250
|
-
message:
|
|
1251
|
-
line:
|
|
1252
|
-
column:
|
|
1274
|
+
message: error2.message,
|
|
1275
|
+
line: error2.line,
|
|
1276
|
+
column: error2.column
|
|
1253
1277
|
};
|
|
1254
1278
|
}
|
|
1255
|
-
if (
|
|
1279
|
+
if (error2 instanceof Error) {
|
|
1256
1280
|
return {
|
|
1257
1281
|
code,
|
|
1258
1282
|
severity: "error",
|
|
1259
1283
|
source,
|
|
1260
|
-
message:
|
|
1284
|
+
message: error2.message
|
|
1261
1285
|
};
|
|
1262
1286
|
}
|
|
1263
1287
|
return {
|
|
1264
1288
|
code,
|
|
1265
1289
|
severity: "error",
|
|
1266
1290
|
source,
|
|
1267
|
-
message: String(
|
|
1291
|
+
message: String(error2)
|
|
1268
1292
|
};
|
|
1269
1293
|
}
|
|
1270
1294
|
|
|
1271
1295
|
// packages/core/dist/scene.js
|
|
1272
1296
|
var AU_IN_KM = 1495978707e-1;
|
|
1273
1297
|
var EARTH_RADIUS_IN_KM = 6371;
|
|
1298
|
+
var JUPITER_RADIUS_IN_KM = 71492;
|
|
1274
1299
|
var SOLAR_RADIUS_IN_KM = 695700;
|
|
1300
|
+
var LY_IN_AU = 63241.077;
|
|
1301
|
+
var PC_IN_AU = 206264.806;
|
|
1302
|
+
var KPC_IN_AU = 206264806;
|
|
1275
1303
|
var ISO_FLATTENING = 0.68;
|
|
1276
1304
|
var MIN_ISO_MINOR_SCALE = 0.2;
|
|
1277
1305
|
var ARC_SAMPLE_COUNT = 28;
|
|
@@ -1285,8 +1313,10 @@ var WorldOrbit = (() => {
|
|
|
1285
1313
|
const scaleModel = resolveScaleModel(layoutPreset, options.scaleModel);
|
|
1286
1314
|
const spacingFactor = layoutPresetSpacing(layoutPreset);
|
|
1287
1315
|
const systemId = document2.system?.id ?? null;
|
|
1288
|
-
const
|
|
1289
|
-
const
|
|
1316
|
+
const activeEventId = options.activeEventId ?? null;
|
|
1317
|
+
const effectiveObjects = createEffectiveObjects(document2.objects, document2.events ?? [], activeEventId);
|
|
1318
|
+
const objectMap = new Map(effectiveObjects.map((object) => [object.id, object]));
|
|
1319
|
+
const relationships = buildSceneRelationships(effectiveObjects, objectMap);
|
|
1290
1320
|
const positions = /* @__PURE__ */ new Map();
|
|
1291
1321
|
const orbitDrafts = [];
|
|
1292
1322
|
const leaderDrafts = [];
|
|
@@ -1295,7 +1325,7 @@ var WorldOrbit = (() => {
|
|
|
1295
1325
|
const atObjects = [];
|
|
1296
1326
|
const surfaceChildren = /* @__PURE__ */ new Map();
|
|
1297
1327
|
const orbitChildren = /* @__PURE__ */ new Map();
|
|
1298
|
-
for (const object of
|
|
1328
|
+
for (const object of effectiveObjects) {
|
|
1299
1329
|
const placement = object.placement;
|
|
1300
1330
|
if (!placement) {
|
|
1301
1331
|
rootObjects.push(object);
|
|
@@ -1390,11 +1420,14 @@ var WorldOrbit = (() => {
|
|
|
1390
1420
|
const objects = [...positions.values()].map((position) => createSceneObject(position, scaleModel, relationships));
|
|
1391
1421
|
const orbitVisuals = orbitDrafts.map((draft) => createOrbitVisual(draft, relationships.groupIds.get(draft.object.id) ?? null));
|
|
1392
1422
|
const leaders = leaderDrafts.map((draft) => createLeaderLine(draft));
|
|
1393
|
-
const labels = createSceneLabels(objects, height, scaleModel.labelMultiplier);
|
|
1394
|
-
const
|
|
1395
|
-
const
|
|
1423
|
+
const labels = createSceneLabels(objects, width, height, scaleModel.labelMultiplier);
|
|
1424
|
+
const relations = createSceneRelations(document2, objects);
|
|
1425
|
+
const events = createSceneEvents(document2.events ?? [], objects, activeEventId);
|
|
1426
|
+
const layers = createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels);
|
|
1427
|
+
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, scaleModel.labelMultiplier);
|
|
1428
|
+
const semanticGroups = createSceneSemanticGroups(document2, objects);
|
|
1396
1429
|
const viewpoints = createSceneViewpoints(document2, projection, frame.preset, relationships, objectMap);
|
|
1397
|
-
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels);
|
|
1430
|
+
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, scaleModel.labelMultiplier);
|
|
1398
1431
|
return {
|
|
1399
1432
|
width,
|
|
1400
1433
|
height,
|
|
@@ -1402,7 +1435,7 @@ var WorldOrbit = (() => {
|
|
|
1402
1435
|
renderPreset: frame.preset,
|
|
1403
1436
|
projection,
|
|
1404
1437
|
scaleModel,
|
|
1405
|
-
title: String(document2.system?.properties.title ?? document2.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1438
|
+
title: String(document2.system?.title ?? document2.system?.properties.title ?? document2.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1406
1439
|
subtitle: `${capitalizeLabel(projection)} view - ${capitalizeLabel(layoutPreset)} layout`,
|
|
1407
1440
|
systemId,
|
|
1408
1441
|
viewMode: projection,
|
|
@@ -1418,9 +1451,13 @@ var WorldOrbit = (() => {
|
|
|
1418
1451
|
contentBounds,
|
|
1419
1452
|
layers,
|
|
1420
1453
|
groups,
|
|
1454
|
+
semanticGroups,
|
|
1421
1455
|
viewpoints,
|
|
1456
|
+
events,
|
|
1457
|
+
activeEventId,
|
|
1422
1458
|
objects,
|
|
1423
1459
|
orbitVisuals,
|
|
1460
|
+
relations,
|
|
1424
1461
|
leaders,
|
|
1425
1462
|
labels
|
|
1426
1463
|
};
|
|
@@ -1436,6 +1473,35 @@ var WorldOrbit = (() => {
|
|
|
1436
1473
|
y: center.y + dx * sin + dy * cos
|
|
1437
1474
|
};
|
|
1438
1475
|
}
|
|
1476
|
+
function createEffectiveObjects(objects, events, activeEventId) {
|
|
1477
|
+
const cloned = objects.map((object) => structuredClone(object));
|
|
1478
|
+
if (!activeEventId) {
|
|
1479
|
+
return cloned;
|
|
1480
|
+
}
|
|
1481
|
+
const activeEvent = events.find((event) => event.id === activeEventId);
|
|
1482
|
+
if (!activeEvent) {
|
|
1483
|
+
return cloned;
|
|
1484
|
+
}
|
|
1485
|
+
const objectMap = new Map(cloned.map((object) => [object.id, object]));
|
|
1486
|
+
for (const pose of activeEvent.positions) {
|
|
1487
|
+
const object = objectMap.get(pose.objectId);
|
|
1488
|
+
if (!object) {
|
|
1489
|
+
continue;
|
|
1490
|
+
}
|
|
1491
|
+
object.placement = pose.placement ? structuredClone(pose.placement) : null;
|
|
1492
|
+
if (pose.inner) {
|
|
1493
|
+
object.properties.inner = { ...pose.inner };
|
|
1494
|
+
} else {
|
|
1495
|
+
delete object.properties.inner;
|
|
1496
|
+
}
|
|
1497
|
+
if (pose.outer) {
|
|
1498
|
+
object.properties.outer = { ...pose.outer };
|
|
1499
|
+
} else {
|
|
1500
|
+
delete object.properties.outer;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
return cloned;
|
|
1504
|
+
}
|
|
1439
1505
|
function resolveLayoutPreset(document2) {
|
|
1440
1506
|
const rawScale = String(document2.system?.properties.scale ?? "balanced").toLowerCase();
|
|
1441
1507
|
switch (rawScale) {
|
|
@@ -1530,6 +1596,7 @@ var WorldOrbit = (() => {
|
|
|
1530
1596
|
}
|
|
1531
1597
|
function createSceneObject(position, scaleModel, relationships) {
|
|
1532
1598
|
const { object, x, y, radius, sortKey, anchorX, anchorY } = position;
|
|
1599
|
+
const renderPriority = object.renderHints?.renderPriority ?? 0;
|
|
1533
1600
|
return {
|
|
1534
1601
|
renderId: createRenderId(object.id),
|
|
1535
1602
|
objectId: object.id,
|
|
@@ -1538,11 +1605,12 @@ var WorldOrbit = (() => {
|
|
|
1538
1605
|
ancestorIds: relationships.ancestorIds.get(object.id) ?? [],
|
|
1539
1606
|
childIds: relationships.childIds.get(object.id) ?? [],
|
|
1540
1607
|
groupId: relationships.groupIds.get(object.id) ?? null,
|
|
1608
|
+
semanticGroupIds: [...object.groups ?? []],
|
|
1541
1609
|
x,
|
|
1542
1610
|
y,
|
|
1543
1611
|
radius,
|
|
1544
1612
|
visualRadius: visualExtentForObject(object, radius, scaleModel),
|
|
1545
|
-
sortKey,
|
|
1613
|
+
sortKey: sortKey + renderPriority * 1e-3,
|
|
1546
1614
|
anchorX,
|
|
1547
1615
|
anchorY,
|
|
1548
1616
|
label: object.id,
|
|
@@ -1559,6 +1627,7 @@ var WorldOrbit = (() => {
|
|
|
1559
1627
|
object: draft.object,
|
|
1560
1628
|
parentId: draft.parentId,
|
|
1561
1629
|
groupId,
|
|
1630
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1562
1631
|
kind: draft.kind,
|
|
1563
1632
|
cx: draft.cx,
|
|
1564
1633
|
cy: draft.cy,
|
|
@@ -1570,7 +1639,7 @@ var WorldOrbit = (() => {
|
|
|
1570
1639
|
bandThickness: draft.bandThickness,
|
|
1571
1640
|
frontArcPath: draft.frontArcPath,
|
|
1572
1641
|
backArcPath: draft.backArcPath,
|
|
1573
|
-
hidden: draft.object.properties.hidden === true
|
|
1642
|
+
hidden: draft.object.properties.hidden === true || draft.object.renderHints?.renderOrbit === false
|
|
1574
1643
|
};
|
|
1575
1644
|
}
|
|
1576
1645
|
function createLeaderLine(draft) {
|
|
@@ -1579,6 +1648,7 @@ var WorldOrbit = (() => {
|
|
|
1579
1648
|
objectId: draft.object.id,
|
|
1580
1649
|
object: draft.object,
|
|
1581
1650
|
groupId: draft.groupId,
|
|
1651
|
+
semanticGroupIds: [...draft.object.groups ?? []],
|
|
1582
1652
|
x1: draft.x1,
|
|
1583
1653
|
y1: draft.y1,
|
|
1584
1654
|
x2: draft.x2,
|
|
@@ -1587,42 +1657,144 @@ var WorldOrbit = (() => {
|
|
|
1587
1657
|
hidden: draft.object.properties.hidden === true
|
|
1588
1658
|
};
|
|
1589
1659
|
}
|
|
1590
|
-
function createSceneLabels(objects, sceneHeight, labelMultiplier) {
|
|
1660
|
+
function createSceneLabels(objects, sceneWidth, sceneHeight, labelMultiplier) {
|
|
1591
1661
|
const labels = [];
|
|
1592
1662
|
const occupied = [];
|
|
1593
|
-
const
|
|
1663
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1664
|
+
const visibleObjects = [...objects].filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false).sort(compareLabelPlacementOrder);
|
|
1594
1665
|
for (const object of visibleObjects) {
|
|
1595
|
-
const
|
|
1596
|
-
|
|
1597
|
-
let labelY = object.y + direction * (object.radius + 18 * labelMultiplier);
|
|
1598
|
-
let secondaryY = labelY + direction * (16 * labelMultiplier);
|
|
1599
|
-
let bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
|
|
1600
|
-
let attempts = 0;
|
|
1601
|
-
while (occupied.some((entry) => rectsOverlap(entry, bounds)) && attempts < 10) {
|
|
1602
|
-
labelY += direction * 14 * labelMultiplier;
|
|
1603
|
-
secondaryY += direction * 14 * labelMultiplier;
|
|
1604
|
-
bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
|
|
1605
|
-
attempts += 1;
|
|
1606
|
-
}
|
|
1607
|
-
occupied.push(bounds);
|
|
1666
|
+
const placement = selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) ?? createLabelPlacement(object, defaultVerticalDirection(object, objectMap.get(object.parentId ?? "") ?? null, sceneHeight), 0, labelMultiplier);
|
|
1667
|
+
occupied.push(createLabelRect(object, placement, labelMultiplier));
|
|
1608
1668
|
labels.push({
|
|
1609
1669
|
renderId: `${object.renderId}-label`,
|
|
1610
1670
|
objectId: object.objectId,
|
|
1611
1671
|
object: object.object,
|
|
1612
1672
|
groupId: object.groupId,
|
|
1673
|
+
semanticGroupIds: [...object.semanticGroupIds],
|
|
1613
1674
|
label: object.label,
|
|
1614
1675
|
secondaryLabel: object.secondaryLabel,
|
|
1615
|
-
x:
|
|
1616
|
-
y: labelY,
|
|
1617
|
-
secondaryY,
|
|
1618
|
-
textAnchor:
|
|
1619
|
-
direction: direction
|
|
1676
|
+
x: placement.x,
|
|
1677
|
+
y: placement.labelY,
|
|
1678
|
+
secondaryY: placement.secondaryY,
|
|
1679
|
+
textAnchor: placement.textAnchor,
|
|
1680
|
+
direction: placement.direction,
|
|
1620
1681
|
hidden: object.hidden
|
|
1621
1682
|
});
|
|
1622
1683
|
}
|
|
1623
1684
|
return labels;
|
|
1624
1685
|
}
|
|
1625
|
-
function
|
|
1686
|
+
function compareLabelPlacementOrder(left, right) {
|
|
1687
|
+
const priorityDiff = labelPlacementPriority(left) - labelPlacementPriority(right);
|
|
1688
|
+
if (priorityDiff !== 0) {
|
|
1689
|
+
return priorityDiff;
|
|
1690
|
+
}
|
|
1691
|
+
const renderPriorityDiff = (right.object.renderHints?.renderPriority ?? 0) - (left.object.renderHints?.renderPriority ?? 0);
|
|
1692
|
+
if (renderPriorityDiff !== 0) {
|
|
1693
|
+
return renderPriorityDiff;
|
|
1694
|
+
}
|
|
1695
|
+
return left.sortKey - right.sortKey;
|
|
1696
|
+
}
|
|
1697
|
+
function labelPlacementPriority(object) {
|
|
1698
|
+
switch (object.object.type) {
|
|
1699
|
+
case "star":
|
|
1700
|
+
return 0;
|
|
1701
|
+
case "planet":
|
|
1702
|
+
return 1;
|
|
1703
|
+
case "moon":
|
|
1704
|
+
return 2;
|
|
1705
|
+
case "belt":
|
|
1706
|
+
case "ring":
|
|
1707
|
+
return 3;
|
|
1708
|
+
case "asteroid":
|
|
1709
|
+
case "comet":
|
|
1710
|
+
return 4;
|
|
1711
|
+
case "structure":
|
|
1712
|
+
case "phenomenon":
|
|
1713
|
+
return 5;
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
function selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) {
|
|
1717
|
+
for (const direction of preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight)) {
|
|
1718
|
+
const maxAttempts = direction === "left" || direction === "right" ? 4 : 6;
|
|
1719
|
+
for (let attempt = 0; attempt <= maxAttempts; attempt += 1) {
|
|
1720
|
+
const placement = createLabelPlacement(object, direction, attempt, labelMultiplier);
|
|
1721
|
+
const rect = createLabelRect(object, placement, labelMultiplier);
|
|
1722
|
+
if (!occupied.some((entry) => rectsOverlap(entry, rect))) {
|
|
1723
|
+
return placement;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
return null;
|
|
1728
|
+
}
|
|
1729
|
+
function preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight) {
|
|
1730
|
+
const parent = object.parentId ? objectMap.get(object.parentId) ?? null : null;
|
|
1731
|
+
const vertical = defaultVerticalDirection(object, parent, sceneHeight);
|
|
1732
|
+
const oppositeVertical = vertical === "below" ? "above" : "below";
|
|
1733
|
+
const horizontal = defaultHorizontalDirection(object, parent, sceneWidth);
|
|
1734
|
+
const oppositeHorizontal = horizontal === "right" ? "left" : "right";
|
|
1735
|
+
const preferHorizontal = object.object.type === "structure" || object.object.type === "phenomenon" || object.object.placement?.mode === "at" || object.object.placement?.mode === "surface" || object.object.placement?.mode === "free";
|
|
1736
|
+
return preferHorizontal ? [horizontal, vertical, oppositeHorizontal, oppositeVertical] : [vertical, horizontal, oppositeVertical, oppositeHorizontal];
|
|
1737
|
+
}
|
|
1738
|
+
function defaultVerticalDirection(object, parent, sceneHeight) {
|
|
1739
|
+
if (parent && Math.abs(object.y - parent.y) > 6) {
|
|
1740
|
+
return object.y >= parent.y ? "below" : "above";
|
|
1741
|
+
}
|
|
1742
|
+
return object.y > sceneHeight * 0.62 ? "above" : "below";
|
|
1743
|
+
}
|
|
1744
|
+
function defaultHorizontalDirection(object, parent, sceneWidth) {
|
|
1745
|
+
if (parent && Math.abs(object.x - parent.x) > 6) {
|
|
1746
|
+
return object.x >= parent.x ? "right" : "left";
|
|
1747
|
+
}
|
|
1748
|
+
return object.x >= sceneWidth / 2 ? "right" : "left";
|
|
1749
|
+
}
|
|
1750
|
+
function createLabelPlacement(object, direction, attempt, labelMultiplier) {
|
|
1751
|
+
const step = 14 * labelMultiplier;
|
|
1752
|
+
switch (direction) {
|
|
1753
|
+
case "above": {
|
|
1754
|
+
const labelY = object.y - (object.radius + 18 * labelMultiplier + attempt * step);
|
|
1755
|
+
return {
|
|
1756
|
+
x: object.x,
|
|
1757
|
+
labelY,
|
|
1758
|
+
secondaryY: labelY - 16 * labelMultiplier,
|
|
1759
|
+
textAnchor: "middle",
|
|
1760
|
+
direction
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
case "below": {
|
|
1764
|
+
const labelY = object.y + object.radius + 18 * labelMultiplier + attempt * step;
|
|
1765
|
+
return {
|
|
1766
|
+
x: object.x,
|
|
1767
|
+
labelY,
|
|
1768
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
1769
|
+
textAnchor: "middle",
|
|
1770
|
+
direction
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
case "left": {
|
|
1774
|
+
const x = object.x - (object.visualRadius + 16 * labelMultiplier + attempt * step);
|
|
1775
|
+
const labelY = object.y - 4 * labelMultiplier;
|
|
1776
|
+
return {
|
|
1777
|
+
x,
|
|
1778
|
+
labelY,
|
|
1779
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
1780
|
+
textAnchor: "end",
|
|
1781
|
+
direction
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1784
|
+
case "right": {
|
|
1785
|
+
const x = object.x + object.visualRadius + 16 * labelMultiplier + attempt * step;
|
|
1786
|
+
const labelY = object.y - 4 * labelMultiplier;
|
|
1787
|
+
return {
|
|
1788
|
+
x,
|
|
1789
|
+
labelY,
|
|
1790
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
1791
|
+
textAnchor: "start",
|
|
1792
|
+
direction
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
function createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels) {
|
|
1626
1798
|
const backOrbitIds = orbitVisuals.filter((visual) => !visual.hidden && Boolean(visual.backArcPath)).map((visual) => visual.renderId);
|
|
1627
1799
|
const frontOrbitIds = orbitVisuals.filter((visual) => !visual.hidden).map((visual) => visual.renderId);
|
|
1628
1800
|
return [
|
|
@@ -1633,6 +1805,14 @@ var WorldOrbit = (() => {
|
|
|
1633
1805
|
},
|
|
1634
1806
|
{ id: "orbits-back", renderIds: backOrbitIds },
|
|
1635
1807
|
{ id: "orbits-front", renderIds: frontOrbitIds },
|
|
1808
|
+
{
|
|
1809
|
+
id: "relations",
|
|
1810
|
+
renderIds: relations.filter((relation) => !relation.hidden).map((relation) => relation.renderId)
|
|
1811
|
+
},
|
|
1812
|
+
{
|
|
1813
|
+
id: "events",
|
|
1814
|
+
renderIds: events.filter((event) => !event.hidden).map((event) => event.renderId)
|
|
1815
|
+
},
|
|
1636
1816
|
{
|
|
1637
1817
|
id: "objects",
|
|
1638
1818
|
renderIds: objects.filter((object) => !object.hidden).map((object) => object.renderId)
|
|
@@ -1644,7 +1824,7 @@ var WorldOrbit = (() => {
|
|
|
1644
1824
|
{ id: "metadata", renderIds: ["wo-title", "wo-subtitle", "wo-meta"] }
|
|
1645
1825
|
];
|
|
1646
1826
|
}
|
|
1647
|
-
function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships) {
|
|
1827
|
+
function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, labelMultiplier) {
|
|
1648
1828
|
const groups = /* @__PURE__ */ new Map();
|
|
1649
1829
|
const ensureGroup = (groupId) => {
|
|
1650
1830
|
if (!groupId) {
|
|
@@ -1693,10 +1873,63 @@ var WorldOrbit = (() => {
|
|
|
1693
1873
|
}
|
|
1694
1874
|
}
|
|
1695
1875
|
for (const group of groups.values()) {
|
|
1696
|
-
group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels);
|
|
1876
|
+
group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier);
|
|
1697
1877
|
}
|
|
1698
1878
|
return [...groups.values()].sort((left, right) => left.label.localeCompare(right.label));
|
|
1699
1879
|
}
|
|
1880
|
+
function createSceneSemanticGroups(document2, objects) {
|
|
1881
|
+
return [...document2.groups].map((group) => ({
|
|
1882
|
+
id: group.id,
|
|
1883
|
+
label: group.label,
|
|
1884
|
+
summary: group.summary,
|
|
1885
|
+
color: group.color,
|
|
1886
|
+
tags: [...group.tags],
|
|
1887
|
+
hidden: group.hidden,
|
|
1888
|
+
objectIds: objects.filter((object) => !object.hidden && object.semanticGroupIds.includes(group.id)).map((object) => object.objectId)
|
|
1889
|
+
})).sort((left, right) => left.label.localeCompare(right.label));
|
|
1890
|
+
}
|
|
1891
|
+
function createSceneRelations(document2, objects) {
|
|
1892
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1893
|
+
return document2.relations.map((relation) => {
|
|
1894
|
+
const from = objectMap.get(relation.from);
|
|
1895
|
+
const to = objectMap.get(relation.to);
|
|
1896
|
+
return {
|
|
1897
|
+
renderId: `${createRenderId(relation.id)}-relation`,
|
|
1898
|
+
relationId: relation.id,
|
|
1899
|
+
relation,
|
|
1900
|
+
fromObjectId: relation.from,
|
|
1901
|
+
toObjectId: relation.to,
|
|
1902
|
+
x1: from?.x ?? 0,
|
|
1903
|
+
y1: from?.y ?? 0,
|
|
1904
|
+
x2: to?.x ?? 0,
|
|
1905
|
+
y2: to?.y ?? 0,
|
|
1906
|
+
hidden: relation.hidden || !from || !to || from.hidden || to.hidden
|
|
1907
|
+
};
|
|
1908
|
+
}).sort((left, right) => left.relation.id.localeCompare(right.relation.id));
|
|
1909
|
+
}
|
|
1910
|
+
function createSceneEvents(events, objects, activeEventId) {
|
|
1911
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1912
|
+
return events.map((event) => {
|
|
1913
|
+
const objectIds = [.../* @__PURE__ */ new Set([
|
|
1914
|
+
...event.targetObjectId ? [event.targetObjectId] : [],
|
|
1915
|
+
...event.participantObjectIds
|
|
1916
|
+
])];
|
|
1917
|
+
const positions = objectIds.map((objectId) => objectMap.get(objectId)).filter(Boolean);
|
|
1918
|
+
const centroidX = positions.length > 0 ? positions.reduce((sum, object) => sum + object.x, 0) / positions.length : 0;
|
|
1919
|
+
const centroidY = positions.length > 0 ? positions.reduce((sum, object) => sum + object.y, 0) / positions.length : 0;
|
|
1920
|
+
return {
|
|
1921
|
+
renderId: `${createRenderId(event.id)}-event`,
|
|
1922
|
+
eventId: event.id,
|
|
1923
|
+
event,
|
|
1924
|
+
objectIds,
|
|
1925
|
+
participantIds: [...event.participantObjectIds],
|
|
1926
|
+
targetObjectId: event.targetObjectId,
|
|
1927
|
+
x: centroidX,
|
|
1928
|
+
y: centroidY,
|
|
1929
|
+
hidden: event.hidden || positions.length === 0 || positions.every((object) => object.hidden) || activeEventId !== null && event.id !== activeEventId
|
|
1930
|
+
};
|
|
1931
|
+
}).sort((left, right) => left.event.id.localeCompare(right.event.id));
|
|
1932
|
+
}
|
|
1700
1933
|
function createSceneViewpoints(document2, projection, preset, relationships, objectMap) {
|
|
1701
1934
|
const generatedOverview = createGeneratedOverviewViewpoint(document2, projection, preset);
|
|
1702
1935
|
const drafts = /* @__PURE__ */ new Map();
|
|
@@ -1714,7 +1947,7 @@ var WorldOrbit = (() => {
|
|
|
1714
1947
|
}
|
|
1715
1948
|
const field = fieldParts.join(".").toLowerCase();
|
|
1716
1949
|
const draft = drafts.get(id) ?? { id };
|
|
1717
|
-
applyViewpointField(draft, field, value, projection, preset, relationships, objectMap);
|
|
1950
|
+
applyViewpointField(draft, field, value, document2, projection, preset, relationships, objectMap);
|
|
1718
1951
|
drafts.set(id, draft);
|
|
1719
1952
|
}
|
|
1720
1953
|
const viewpoints = [...drafts.values()].map((draft) => finalizeViewpointDraft(draft, projection, preset, objectMap)).filter(Boolean);
|
|
@@ -1742,13 +1975,15 @@ var WorldOrbit = (() => {
|
|
|
1742
1975
|
});
|
|
1743
1976
|
}
|
|
1744
1977
|
function createGeneratedOverviewViewpoint(document2, projection, preset) {
|
|
1745
|
-
const
|
|
1978
|
+
const title = document2.system?.title ?? document2.system?.properties.title;
|
|
1979
|
+
const label = title ? `${String(title)} Overview` : "Overview";
|
|
1746
1980
|
return {
|
|
1747
1981
|
id: "overview",
|
|
1748
1982
|
label,
|
|
1749
1983
|
summary: "Fit the whole system with the current atlas defaults.",
|
|
1750
1984
|
objectId: null,
|
|
1751
1985
|
selectedObjectId: null,
|
|
1986
|
+
eventIds: [],
|
|
1752
1987
|
projection,
|
|
1753
1988
|
preset,
|
|
1754
1989
|
rotationDeg: 0,
|
|
@@ -1758,7 +1993,7 @@ var WorldOrbit = (() => {
|
|
|
1758
1993
|
generated: true
|
|
1759
1994
|
};
|
|
1760
1995
|
}
|
|
1761
|
-
function applyViewpointField(draft, field, value, projection, preset, relationships, objectMap) {
|
|
1996
|
+
function applyViewpointField(draft, field, value, document2, projection, preset, relationships, objectMap) {
|
|
1762
1997
|
const normalizedValue = value.trim();
|
|
1763
1998
|
switch (field) {
|
|
1764
1999
|
case "label":
|
|
@@ -1785,6 +2020,9 @@ var WorldOrbit = (() => {
|
|
|
1785
2020
|
draft.select = normalizedValue;
|
|
1786
2021
|
}
|
|
1787
2022
|
return;
|
|
2023
|
+
case "events":
|
|
2024
|
+
draft.eventIds = splitListValue(normalizedValue);
|
|
2025
|
+
return;
|
|
1788
2026
|
case "projection":
|
|
1789
2027
|
case "view":
|
|
1790
2028
|
draft.projection = parseViewProjection(normalizedValue) ?? projection;
|
|
@@ -1825,7 +2063,7 @@ var WorldOrbit = (() => {
|
|
|
1825
2063
|
case "groups":
|
|
1826
2064
|
draft.filter = {
|
|
1827
2065
|
...draft.filter ?? createEmptyViewpointFilter(),
|
|
1828
|
-
groupIds: parseViewpointGroups(normalizedValue, relationships, objectMap)
|
|
2066
|
+
groupIds: parseViewpointGroups(normalizedValue, document2, relationships, objectMap)
|
|
1829
2067
|
};
|
|
1830
2068
|
return;
|
|
1831
2069
|
}
|
|
@@ -1841,6 +2079,7 @@ var WorldOrbit = (() => {
|
|
|
1841
2079
|
summary: draft.summary?.trim() || createViewpointSummary(label, objectId, filter),
|
|
1842
2080
|
objectId,
|
|
1843
2081
|
selectedObjectId,
|
|
2082
|
+
eventIds: [...new Set(draft.eventIds ?? [])],
|
|
1844
2083
|
projection: draft.projection ?? projection,
|
|
1845
2084
|
preset: draft.preset ?? preset,
|
|
1846
2085
|
rotationDeg: draft.rotationDeg ?? 0,
|
|
@@ -1898,7 +2137,7 @@ var WorldOrbit = (() => {
|
|
|
1898
2137
|
next["orbits-front"] = enabled;
|
|
1899
2138
|
continue;
|
|
1900
2139
|
}
|
|
1901
|
-
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
2140
|
+
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "relations" || rawLayer === "events" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1902
2141
|
next[rawLayer] = enabled;
|
|
1903
2142
|
}
|
|
1904
2143
|
}
|
|
@@ -1907,8 +2146,11 @@ var WorldOrbit = (() => {
|
|
|
1907
2146
|
function parseViewpointObjectTypes(value) {
|
|
1908
2147
|
return splitListValue(value).filter((entry) => entry === "star" || entry === "planet" || entry === "moon" || entry === "belt" || entry === "asteroid" || entry === "comet" || entry === "ring" || entry === "structure" || entry === "phenomenon");
|
|
1909
2148
|
}
|
|
1910
|
-
function parseViewpointGroups(value, relationships, objectMap) {
|
|
2149
|
+
function parseViewpointGroups(value, document2, relationships, objectMap) {
|
|
1911
2150
|
return splitListValue(value).map((entry) => {
|
|
2151
|
+
if (document2.schemaVersion === "2.1" || document2.groups.some((group) => group.id === entry)) {
|
|
2152
|
+
return entry;
|
|
2153
|
+
}
|
|
1912
2154
|
if (entry.startsWith("wo-") && entry.endsWith("-group")) {
|
|
1913
2155
|
return entry;
|
|
1914
2156
|
}
|
|
@@ -1943,7 +2185,7 @@ var WorldOrbit = (() => {
|
|
|
1943
2185
|
}
|
|
1944
2186
|
return parts.join(" - ");
|
|
1945
2187
|
}
|
|
1946
|
-
function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels) {
|
|
2188
|
+
function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, labelMultiplier) {
|
|
1947
2189
|
let minX = Number.POSITIVE_INFINITY;
|
|
1948
2190
|
let minY = Number.POSITIVE_INFINITY;
|
|
1949
2191
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
@@ -1973,7 +2215,7 @@ var WorldOrbit = (() => {
|
|
|
1973
2215
|
for (const label of labels) {
|
|
1974
2216
|
if (label.hidden)
|
|
1975
2217
|
continue;
|
|
1976
|
-
includeLabelBounds(label, include);
|
|
2218
|
+
includeLabelBounds(label, include, labelMultiplier);
|
|
1977
2219
|
}
|
|
1978
2220
|
if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
|
|
1979
2221
|
return createBounds(0, 0, width, height);
|
|
@@ -2011,13 +2253,10 @@ var WorldOrbit = (() => {
|
|
|
2011
2253
|
include(object.x - object.visualRadius - 24, object.y - object.visualRadius - 16);
|
|
2012
2254
|
include(object.x + object.visualRadius + 24, object.y + object.visualRadius + 36);
|
|
2013
2255
|
}
|
|
2014
|
-
function includeLabelBounds(label, include) {
|
|
2015
|
-
const
|
|
2016
|
-
|
|
2017
|
-
include(
|
|
2018
|
-
include(label.x + labelHalfWidth, label.y + 8);
|
|
2019
|
-
include(label.x - labelHalfWidth, label.secondaryY - 14);
|
|
2020
|
-
include(label.x + labelHalfWidth, label.secondaryY + 8);
|
|
2256
|
+
function includeLabelBounds(label, include, labelMultiplier) {
|
|
2257
|
+
const bounds = createLabelRectFromText(label.x, label.y, label.secondaryY, label.textAnchor, label.direction, label.label, label.secondaryLabel, labelMultiplier);
|
|
2258
|
+
include(bounds.left, bounds.top);
|
|
2259
|
+
include(bounds.right, bounds.bottom);
|
|
2021
2260
|
}
|
|
2022
2261
|
function placeObject(object, x, y, depth, positions, orbitDrafts, leaderDrafts, context) {
|
|
2023
2262
|
if (positions.has(object.id)) {
|
|
@@ -2039,8 +2278,9 @@ var WorldOrbit = (() => {
|
|
|
2039
2278
|
}
|
|
2040
2279
|
const orbiting = [...context.orbitChildren.get(object.id) ?? []].sort(compareOrbiting);
|
|
2041
2280
|
const orbitMetricContext = computeOrbitMetricContext(orbiting, parent.radius, context.spacingFactor, context.scaleModel);
|
|
2281
|
+
const orbitRadiiPx = resolveOrbitRadiiPx(orbiting, orbitMetricContext);
|
|
2042
2282
|
orbiting.forEach((child, index) => {
|
|
2043
|
-
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, context);
|
|
2283
|
+
const orbitGeometry = resolveOrbitGeometry(child, index, orbiting.length, parent, orbitMetricContext, orbitRadiiPx[index] ?? orbitMetricContext.innerPx, context);
|
|
2044
2284
|
orbitDrafts.push({
|
|
2045
2285
|
object: child,
|
|
2046
2286
|
parentId: object.id,
|
|
@@ -2114,7 +2354,8 @@ var WorldOrbit = (() => {
|
|
|
2114
2354
|
metricSpread: 0,
|
|
2115
2355
|
innerPx,
|
|
2116
2356
|
stepPx,
|
|
2117
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
2357
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
2358
|
+
minimumGapPx: stepPx * 0.42
|
|
2118
2359
|
};
|
|
2119
2360
|
}
|
|
2120
2361
|
const minMetric = Math.min(...presentMetrics);
|
|
@@ -2127,10 +2368,11 @@ var WorldOrbit = (() => {
|
|
|
2127
2368
|
metricSpread,
|
|
2128
2369
|
innerPx,
|
|
2129
2370
|
stepPx,
|
|
2130
|
-
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx)
|
|
2371
|
+
pixelSpread: Math.max(stepPx * Math.max(objects.length - 1, 1), stepPx),
|
|
2372
|
+
minimumGapPx: stepPx * 0.42
|
|
2131
2373
|
};
|
|
2132
2374
|
}
|
|
2133
|
-
function resolveOrbitGeometry(object, index, count, parent, metricContext, context) {
|
|
2375
|
+
function resolveOrbitGeometry(object, index, count, parent, metricContext, orbitRadiusPx, context) {
|
|
2134
2376
|
const placement = object.placement;
|
|
2135
2377
|
const band = object.type === "belt" || object.type === "ring";
|
|
2136
2378
|
if (!placement || placement.mode !== "orbit") {
|
|
@@ -2148,7 +2390,7 @@ var WorldOrbit = (() => {
|
|
|
2148
2390
|
};
|
|
2149
2391
|
}
|
|
2150
2392
|
const eccentricity = clampNumber(typeof placement.eccentricity === "number" ? placement.eccentricity : 0, 0, 0.92);
|
|
2151
|
-
const semiMajor =
|
|
2393
|
+
const semiMajor = orbitRadiusPx;
|
|
2152
2394
|
const baseMinor = Math.max(semiMajor * Math.sqrt(1 - eccentricity * eccentricity), semiMajor * 0.18);
|
|
2153
2395
|
const inclinationDeg = unitValueToDegrees(placement.inclination) ?? 0;
|
|
2154
2396
|
const inclinationScale = context.projection === "isometric" ? Math.max(MIN_ISO_MINOR_SCALE, Math.cos(degreesToRadians(inclinationDeg))) * ISO_FLATTENING : 1;
|
|
@@ -2178,15 +2420,19 @@ var WorldOrbit = (() => {
|
|
|
2178
2420
|
objectY: objectPoint.y
|
|
2179
2421
|
};
|
|
2180
2422
|
}
|
|
2181
|
-
function resolveOrbitRadiusPx(
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2423
|
+
function resolveOrbitRadiusPx(metric, metricContext) {
|
|
2424
|
+
return metricContext.innerPx + metricContext.stepPx * log2(Math.max(metric, 0) + 1);
|
|
2425
|
+
}
|
|
2426
|
+
function resolveOrbitRadiiPx(objects, metricContext) {
|
|
2427
|
+
const radii = [];
|
|
2428
|
+
objects.forEach((object, index) => {
|
|
2429
|
+
const metric = orbitMetric(object);
|
|
2430
|
+
const fallbackRadius = metricContext.innerPx + index * metricContext.stepPx;
|
|
2431
|
+
const baseRadius = metric === null ? fallbackRadius : resolveOrbitRadiusPx(metric, metricContext);
|
|
2432
|
+
const minimumRadius = index === 0 ? metricContext.innerPx : (radii[index - 1] ?? metricContext.innerPx) + metricContext.minimumGapPx;
|
|
2433
|
+
radii.push(Math.max(baseRadius, minimumRadius));
|
|
2434
|
+
});
|
|
2435
|
+
return radii;
|
|
2190
2436
|
}
|
|
2191
2437
|
function orbitMetric(object) {
|
|
2192
2438
|
if (!object.placement || object.placement.mode !== "orbit") {
|
|
@@ -2194,6 +2440,9 @@ var WorldOrbit = (() => {
|
|
|
2194
2440
|
}
|
|
2195
2441
|
return toDistanceMetric(object.placement.semiMajor ?? object.placement.distance ?? null);
|
|
2196
2442
|
}
|
|
2443
|
+
function log2(value) {
|
|
2444
|
+
return Math.log(value) / Math.log(2);
|
|
2445
|
+
}
|
|
2197
2446
|
function resolveOrbitPhase(phase, index, count) {
|
|
2198
2447
|
const degreeValue = phase ? unitValueToDegrees(phase) : null;
|
|
2199
2448
|
if (degreeValue !== null) {
|
|
@@ -2397,7 +2646,7 @@ var WorldOrbit = (() => {
|
|
|
2397
2646
|
return null;
|
|
2398
2647
|
}
|
|
2399
2648
|
}
|
|
2400
|
-
function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels) {
|
|
2649
|
+
function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier) {
|
|
2401
2650
|
let minX = Number.POSITIVE_INFINITY;
|
|
2402
2651
|
let minY = Number.POSITIVE_INFINITY;
|
|
2403
2652
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
@@ -2426,7 +2675,7 @@ var WorldOrbit = (() => {
|
|
|
2426
2675
|
}
|
|
2427
2676
|
for (const label of labels) {
|
|
2428
2677
|
if (!label.hidden && group.labelIds.includes(label.objectId)) {
|
|
2429
|
-
includeLabelBounds(label, include);
|
|
2678
|
+
includeLabelBounds(label, include, labelMultiplier);
|
|
2430
2679
|
}
|
|
2431
2680
|
}
|
|
2432
2681
|
if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
|
|
@@ -2451,12 +2700,28 @@ var WorldOrbit = (() => {
|
|
|
2451
2700
|
}
|
|
2452
2701
|
return current.id;
|
|
2453
2702
|
}
|
|
2454
|
-
function createLabelRect(
|
|
2703
|
+
function createLabelRect(object, placement, labelMultiplier) {
|
|
2704
|
+
return createLabelRectFromText(placement.x, placement.labelY, placement.secondaryY, placement.textAnchor, placement.direction, object.label, object.secondaryLabel, labelMultiplier);
|
|
2705
|
+
}
|
|
2706
|
+
function createLabelRectFromText(x, labelY, secondaryY, textAnchor, direction, label, secondaryLabel, labelMultiplier) {
|
|
2707
|
+
const labelHalfWidth = estimateLabelHalfWidthFromText(label, secondaryLabel, labelMultiplier);
|
|
2708
|
+
const labelWidth = labelHalfWidth * 2;
|
|
2709
|
+
const topPadding = direction === "above" ? 18 : 12;
|
|
2710
|
+
const bottomPadding = direction === "above" ? 8 : 12;
|
|
2711
|
+
let left = x - labelHalfWidth;
|
|
2712
|
+
let right = x + labelHalfWidth;
|
|
2713
|
+
if (textAnchor === "start") {
|
|
2714
|
+
left = x;
|
|
2715
|
+
right = x + labelWidth;
|
|
2716
|
+
} else if (textAnchor === "end") {
|
|
2717
|
+
left = x - labelWidth;
|
|
2718
|
+
right = x;
|
|
2719
|
+
}
|
|
2455
2720
|
return {
|
|
2456
|
-
left
|
|
2457
|
-
right
|
|
2458
|
-
top: Math.min(labelY, secondaryY) -
|
|
2459
|
-
bottom: Math.max(labelY, secondaryY) +
|
|
2721
|
+
left,
|
|
2722
|
+
right,
|
|
2723
|
+
top: Math.min(labelY, secondaryY) - topPadding,
|
|
2724
|
+
bottom: Math.max(labelY, secondaryY) + bottomPadding
|
|
2460
2725
|
};
|
|
2461
2726
|
}
|
|
2462
2727
|
function rectsOverlap(left, right) {
|
|
@@ -2517,8 +2782,18 @@ var WorldOrbit = (() => {
|
|
|
2517
2782
|
return value.value;
|
|
2518
2783
|
case "km":
|
|
2519
2784
|
return value.value / AU_IN_KM;
|
|
2785
|
+
case "m":
|
|
2786
|
+
return value.value / 1e3 / AU_IN_KM;
|
|
2787
|
+
case "ly":
|
|
2788
|
+
return value.value * LY_IN_AU;
|
|
2789
|
+
case "pc":
|
|
2790
|
+
return value.value * PC_IN_AU;
|
|
2791
|
+
case "kpc":
|
|
2792
|
+
return value.value * KPC_IN_AU;
|
|
2520
2793
|
case "re":
|
|
2521
2794
|
return value.value * EARTH_RADIUS_IN_KM / AU_IN_KM;
|
|
2795
|
+
case "rj":
|
|
2796
|
+
return value.value * JUPITER_RADIUS_IN_KM / AU_IN_KM;
|
|
2522
2797
|
case "sol":
|
|
2523
2798
|
return value.value * SOLAR_RADIUS_IN_KM / AU_IN_KM;
|
|
2524
2799
|
default:
|
|
@@ -2633,11 +2908,6 @@ var WorldOrbit = (() => {
|
|
|
2633
2908
|
function customColorFor(value) {
|
|
2634
2909
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
2635
2910
|
}
|
|
2636
|
-
function estimateLabelHalfWidth(object, labelMultiplier) {
|
|
2637
|
-
const primaryWidth = object.label.length * 4.6 * labelMultiplier + 18;
|
|
2638
|
-
const secondaryWidth = object.secondaryLabel.length * 3.9 * labelMultiplier + 18;
|
|
2639
|
-
return Math.max(primaryWidth, secondaryWidth, object.visualRadius + 18);
|
|
2640
|
-
}
|
|
2641
2911
|
function estimateLabelHalfWidthFromText(label, secondaryLabel, labelMultiplier) {
|
|
2642
2912
|
const primaryWidth = label.length * 4.6 * labelMultiplier + 18;
|
|
2643
2913
|
const secondaryWidth = secondaryLabel.length * 3.9 * labelMultiplier + 18;
|
|
@@ -2654,28 +2924,95 @@ var WorldOrbit = (() => {
|
|
|
2654
2924
|
}
|
|
2655
2925
|
|
|
2656
2926
|
// packages/core/dist/draft.js
|
|
2657
|
-
function materializeAtlasDocument(document2) {
|
|
2927
|
+
function materializeAtlasDocument(document2, options = {}) {
|
|
2658
2928
|
const system = document2.system ? {
|
|
2659
2929
|
type: "system",
|
|
2660
2930
|
id: document2.system.id,
|
|
2931
|
+
title: document2.system.title,
|
|
2932
|
+
description: document2.system.description,
|
|
2933
|
+
epoch: document2.system.epoch,
|
|
2934
|
+
referencePlane: document2.system.referencePlane,
|
|
2661
2935
|
properties: materializeDraftSystemProperties(document2.system),
|
|
2662
2936
|
info: materializeDraftSystemInfo(document2.system)
|
|
2663
2937
|
} : null;
|
|
2938
|
+
const objects = document2.objects.map(cloneWorldOrbitObject);
|
|
2939
|
+
applyEventPoseOverrides(objects, document2.events ?? [], options.activeEventId ?? null);
|
|
2664
2940
|
return {
|
|
2665
2941
|
format: "worldorbit",
|
|
2666
2942
|
version: "1.0",
|
|
2943
|
+
schemaVersion: document2.version,
|
|
2667
2944
|
system,
|
|
2668
|
-
|
|
2945
|
+
groups: structuredClone(document2.groups ?? []),
|
|
2946
|
+
relations: structuredClone(document2.relations ?? []),
|
|
2947
|
+
events: document2.events.map(cloneWorldOrbitEvent),
|
|
2948
|
+
objects
|
|
2669
2949
|
};
|
|
2670
2950
|
}
|
|
2671
2951
|
function cloneWorldOrbitObject(object) {
|
|
2672
2952
|
return {
|
|
2673
2953
|
...object,
|
|
2954
|
+
groups: object.groups ? [...object.groups] : void 0,
|
|
2955
|
+
resonance: object.resonance ? { ...object.resonance } : object.resonance,
|
|
2956
|
+
renderHints: object.renderHints ? { ...object.renderHints } : object.renderHints,
|
|
2957
|
+
deriveRules: object.deriveRules ? object.deriveRules.map((rule) => ({ ...rule })) : void 0,
|
|
2958
|
+
validationRules: object.validationRules ? object.validationRules.map((rule) => ({ ...rule })) : void 0,
|
|
2959
|
+
lockedFields: object.lockedFields ? [...object.lockedFields] : void 0,
|
|
2960
|
+
tolerances: object.tolerances ? object.tolerances.map((entry) => ({
|
|
2961
|
+
field: entry.field,
|
|
2962
|
+
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
|
|
2963
|
+
})) : void 0,
|
|
2964
|
+
typedBlocks: object.typedBlocks ? Object.fromEntries(Object.entries(object.typedBlocks).map(([key, block]) => [key, { ...block ?? {} }])) : void 0,
|
|
2674
2965
|
properties: cloneProperties(object.properties),
|
|
2675
2966
|
placement: object.placement ? structuredClone(object.placement) : null,
|
|
2676
2967
|
info: { ...object.info }
|
|
2677
2968
|
};
|
|
2678
2969
|
}
|
|
2970
|
+
function cloneWorldOrbitEvent(event) {
|
|
2971
|
+
return {
|
|
2972
|
+
...event,
|
|
2973
|
+
participantObjectIds: [...event.participantObjectIds],
|
|
2974
|
+
tags: [...event.tags],
|
|
2975
|
+
positions: event.positions.map(cloneWorldOrbitEventPose)
|
|
2976
|
+
};
|
|
2977
|
+
}
|
|
2978
|
+
function cloneWorldOrbitEventPose(pose) {
|
|
2979
|
+
return {
|
|
2980
|
+
objectId: pose.objectId,
|
|
2981
|
+
placement: clonePlacement(pose.placement),
|
|
2982
|
+
inner: pose.inner ? { ...pose.inner } : void 0,
|
|
2983
|
+
outer: pose.outer ? { ...pose.outer } : void 0
|
|
2984
|
+
};
|
|
2985
|
+
}
|
|
2986
|
+
function clonePlacement(placement) {
|
|
2987
|
+
return placement ? structuredClone(placement) : null;
|
|
2988
|
+
}
|
|
2989
|
+
function applyEventPoseOverrides(objects, events, activeEventId) {
|
|
2990
|
+
if (!activeEventId) {
|
|
2991
|
+
return;
|
|
2992
|
+
}
|
|
2993
|
+
const event = events.find((entry) => entry.id === activeEventId);
|
|
2994
|
+
if (!event) {
|
|
2995
|
+
return;
|
|
2996
|
+
}
|
|
2997
|
+
const objectMap = new Map(objects.map((object) => [object.id, object]));
|
|
2998
|
+
for (const pose of event.positions) {
|
|
2999
|
+
const object = objectMap.get(pose.objectId);
|
|
3000
|
+
if (!object) {
|
|
3001
|
+
continue;
|
|
3002
|
+
}
|
|
3003
|
+
object.placement = clonePlacement(pose.placement);
|
|
3004
|
+
if (pose.inner) {
|
|
3005
|
+
object.properties.inner = { ...pose.inner };
|
|
3006
|
+
} else {
|
|
3007
|
+
delete object.properties.inner;
|
|
3008
|
+
}
|
|
3009
|
+
if (pose.outer) {
|
|
3010
|
+
object.properties.outer = { ...pose.outer };
|
|
3011
|
+
} else {
|
|
3012
|
+
delete object.properties.outer;
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
2679
3016
|
function cloneProperties(properties) {
|
|
2680
3017
|
const next = {};
|
|
2681
3018
|
for (const [key, value] of Object.entries(properties)) {
|
|
@@ -2706,71 +3043,83 @@ var WorldOrbit = (() => {
|
|
|
2706
3043
|
if (system.defaults.units) {
|
|
2707
3044
|
properties.units = system.defaults.units;
|
|
2708
3045
|
}
|
|
3046
|
+
if (system.description) {
|
|
3047
|
+
properties.description = system.description;
|
|
3048
|
+
}
|
|
3049
|
+
if (system.epoch) {
|
|
3050
|
+
properties.epoch = system.epoch;
|
|
3051
|
+
}
|
|
3052
|
+
if (system.referencePlane) {
|
|
3053
|
+
properties.referencePlane = system.referencePlane;
|
|
3054
|
+
}
|
|
2709
3055
|
return properties;
|
|
2710
3056
|
}
|
|
2711
3057
|
function materializeDraftSystemInfo(system) {
|
|
2712
|
-
const
|
|
3058
|
+
const info2 = {
|
|
2713
3059
|
...system.atlasMetadata
|
|
2714
3060
|
};
|
|
2715
3061
|
if (system.defaults.theme) {
|
|
2716
|
-
|
|
3062
|
+
info2["atlas.theme"] = system.defaults.theme;
|
|
2717
3063
|
}
|
|
2718
3064
|
for (const viewpoint of system.viewpoints) {
|
|
2719
3065
|
const prefix = `viewpoint.${viewpoint.id}`;
|
|
2720
|
-
|
|
3066
|
+
info2[`${prefix}.label`] = viewpoint.label;
|
|
2721
3067
|
if (viewpoint.summary) {
|
|
2722
|
-
|
|
3068
|
+
info2[`${prefix}.summary`] = viewpoint.summary;
|
|
2723
3069
|
}
|
|
2724
3070
|
if (viewpoint.focusObjectId) {
|
|
2725
|
-
|
|
3071
|
+
info2[`${prefix}.focus`] = viewpoint.focusObjectId;
|
|
2726
3072
|
}
|
|
2727
3073
|
if (viewpoint.selectedObjectId) {
|
|
2728
|
-
|
|
3074
|
+
info2[`${prefix}.select`] = viewpoint.selectedObjectId;
|
|
2729
3075
|
}
|
|
2730
3076
|
if (viewpoint.projection) {
|
|
2731
|
-
|
|
3077
|
+
info2[`${prefix}.projection`] = viewpoint.projection;
|
|
2732
3078
|
}
|
|
2733
3079
|
if (viewpoint.preset) {
|
|
2734
|
-
|
|
3080
|
+
info2[`${prefix}.preset`] = viewpoint.preset;
|
|
2735
3081
|
}
|
|
2736
3082
|
if (viewpoint.zoom !== null) {
|
|
2737
|
-
|
|
3083
|
+
info2[`${prefix}.zoom`] = String(viewpoint.zoom);
|
|
2738
3084
|
}
|
|
2739
3085
|
if (viewpoint.rotationDeg !== 0) {
|
|
2740
|
-
|
|
3086
|
+
info2[`${prefix}.rotation`] = String(viewpoint.rotationDeg);
|
|
2741
3087
|
}
|
|
2742
3088
|
const serializedLayers = serializeViewpointLayers(viewpoint.layers);
|
|
2743
3089
|
if (serializedLayers) {
|
|
2744
|
-
|
|
3090
|
+
info2[`${prefix}.layers`] = serializedLayers;
|
|
2745
3091
|
}
|
|
2746
3092
|
if (viewpoint.filter?.query) {
|
|
2747
|
-
|
|
3093
|
+
info2[`${prefix}.query`] = viewpoint.filter.query;
|
|
2748
3094
|
}
|
|
2749
3095
|
if ((viewpoint.filter?.objectTypes.length ?? 0) > 0) {
|
|
2750
|
-
|
|
3096
|
+
info2[`${prefix}.types`] = viewpoint.filter?.objectTypes.join(" ") ?? "";
|
|
2751
3097
|
}
|
|
2752
3098
|
if ((viewpoint.filter?.tags.length ?? 0) > 0) {
|
|
2753
|
-
|
|
3099
|
+
info2[`${prefix}.tags`] = viewpoint.filter?.tags.join(" ") ?? "";
|
|
2754
3100
|
}
|
|
2755
3101
|
if ((viewpoint.filter?.groupIds.length ?? 0) > 0) {
|
|
2756
|
-
|
|
3102
|
+
info2[`${prefix}.groups`] = viewpoint.filter?.groupIds.join(" ") ?? "";
|
|
3103
|
+
}
|
|
3104
|
+
if (viewpoint.events.length > 0) {
|
|
3105
|
+
info2[`${prefix}.events`] = viewpoint.events.join(" ");
|
|
2757
3106
|
}
|
|
2758
3107
|
}
|
|
2759
3108
|
for (const annotation of system.annotations) {
|
|
2760
3109
|
const prefix = `annotation.${annotation.id}`;
|
|
2761
|
-
|
|
3110
|
+
info2[`${prefix}.label`] = annotation.label;
|
|
2762
3111
|
if (annotation.targetObjectId) {
|
|
2763
|
-
|
|
3112
|
+
info2[`${prefix}.target`] = annotation.targetObjectId;
|
|
2764
3113
|
}
|
|
2765
|
-
|
|
3114
|
+
info2[`${prefix}.body`] = annotation.body;
|
|
2766
3115
|
if (annotation.tags.length > 0) {
|
|
2767
|
-
|
|
3116
|
+
info2[`${prefix}.tags`] = annotation.tags.join(" ");
|
|
2768
3117
|
}
|
|
2769
3118
|
if (annotation.sourceObjectId) {
|
|
2770
|
-
|
|
3119
|
+
info2[`${prefix}.source`] = annotation.sourceObjectId;
|
|
2771
3120
|
}
|
|
2772
3121
|
}
|
|
2773
|
-
return
|
|
3122
|
+
return info2;
|
|
2774
3123
|
}
|
|
2775
3124
|
function serializeViewpointLayers(layers) {
|
|
2776
3125
|
const tokens = [];
|
|
@@ -2779,7 +3128,7 @@ var WorldOrbit = (() => {
|
|
|
2779
3128
|
if (orbitFront !== void 0 || orbitBack !== void 0) {
|
|
2780
3129
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
2781
3130
|
}
|
|
2782
|
-
for (const key of ["background", "guides", "objects", "labels", "metadata"]) {
|
|
3131
|
+
for (const key of ["background", "guides", "relations", "events", "objects", "labels", "metadata"]) {
|
|
2783
3132
|
if (layers[key] !== void 0) {
|
|
2784
3133
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
2785
3134
|
}
|
|
@@ -2787,171 +3136,838 @@ var WorldOrbit = (() => {
|
|
|
2787
3136
|
return tokens.join(" ");
|
|
2788
3137
|
}
|
|
2789
3138
|
|
|
2790
|
-
// packages/core/dist/
|
|
2791
|
-
|
|
2792
|
-
|
|
3139
|
+
// packages/core/dist/atlas-utils.js
|
|
3140
|
+
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)?$/;
|
|
3141
|
+
var BOOLEAN_VALUES2 = /* @__PURE__ */ new Map([
|
|
3142
|
+
["true", true],
|
|
3143
|
+
["false", false],
|
|
3144
|
+
["yes", true],
|
|
3145
|
+
["no", false]
|
|
3146
|
+
]);
|
|
3147
|
+
var URL_SCHEME_PATTERN2 = /^[A-Za-z][A-Za-z0-9+.-]*:/;
|
|
3148
|
+
function normalizeIdentifier(value) {
|
|
3149
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2793
3150
|
}
|
|
2794
|
-
function
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
let sawDefaults = false;
|
|
2802
|
-
let sawAtlas = false;
|
|
2803
|
-
const viewpointIds = /* @__PURE__ */ new Set();
|
|
2804
|
-
const annotationIds = /* @__PURE__ */ new Set();
|
|
2805
|
-
for (let index = 0; index < lines.length; index++) {
|
|
2806
|
-
const rawLine = lines[index];
|
|
2807
|
-
const lineNumber = index + 1;
|
|
2808
|
-
if (!rawLine.trim()) {
|
|
2809
|
-
continue;
|
|
2810
|
-
}
|
|
2811
|
-
const indent = getIndent(rawLine);
|
|
2812
|
-
const tokens = tokenizeLineDetailed(rawLine.slice(indent), {
|
|
2813
|
-
line: lineNumber,
|
|
2814
|
-
columnOffset: indent
|
|
2815
|
-
});
|
|
2816
|
-
if (tokens.length === 0) {
|
|
2817
|
-
continue;
|
|
2818
|
-
}
|
|
2819
|
-
if (!sawSchemaHeader) {
|
|
2820
|
-
schemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
2821
|
-
sawSchemaHeader = true;
|
|
2822
|
-
continue;
|
|
2823
|
-
}
|
|
2824
|
-
if (indent === 0) {
|
|
2825
|
-
section = startTopLevelSection(tokens, lineNumber, system, objectNodes, viewpointIds, annotationIds, {
|
|
2826
|
-
sawDefaults,
|
|
2827
|
-
sawAtlas
|
|
2828
|
-
});
|
|
2829
|
-
if (section.kind === "system") {
|
|
2830
|
-
system = section.system;
|
|
2831
|
-
} else if (section.kind === "defaults") {
|
|
2832
|
-
sawDefaults = true;
|
|
2833
|
-
} else if (section.kind === "atlas") {
|
|
2834
|
-
sawAtlas = true;
|
|
2835
|
-
}
|
|
2836
|
-
continue;
|
|
2837
|
-
}
|
|
2838
|
-
if (!section) {
|
|
2839
|
-
throw new WorldOrbitError("Indented line without parent atlas section", lineNumber, indent + 1);
|
|
2840
|
-
}
|
|
2841
|
-
handleSectionLine(section, indent, tokens, lineNumber);
|
|
2842
|
-
}
|
|
2843
|
-
if (!sawSchemaHeader) {
|
|
2844
|
-
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
|
|
3151
|
+
function humanizeIdentifier2(value) {
|
|
3152
|
+
return value.split(/[-_]+/).filter(Boolean).map((segment) => segment[0].toUpperCase() + segment.slice(1)).join(" ");
|
|
3153
|
+
}
|
|
3154
|
+
function parseAtlasUnitValue(input, location, fieldKey) {
|
|
3155
|
+
const match = input.match(UNIT_PATTERN2);
|
|
3156
|
+
if (!match) {
|
|
3157
|
+
throw WorldOrbitError.fromLocation(`Invalid unit value "${input}"`, location);
|
|
2845
3158
|
}
|
|
2846
|
-
const
|
|
2847
|
-
|
|
2848
|
-
|
|
3159
|
+
const unitValue = {
|
|
3160
|
+
value: Number(match[1]),
|
|
3161
|
+
unit: match[2] ?? null
|
|
2849
3162
|
};
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
system: null,
|
|
2855
|
-
objects: normalizedObjects
|
|
2856
|
-
});
|
|
2857
|
-
const diagnostics = schemaVersion === "2.0-draft" && outputVersion === "2.0" ? [
|
|
2858
|
-
{
|
|
2859
|
-
code: "load.schema.deprecatedDraft",
|
|
2860
|
-
severity: "warning",
|
|
2861
|
-
source: "upgrade",
|
|
2862
|
-
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
3163
|
+
if (fieldKey) {
|
|
3164
|
+
const schema = getFieldSchema(fieldKey);
|
|
3165
|
+
if (schema?.unitFamily && !unitFamilyAllowsUnit(schema.unitFamily, unitValue.unit)) {
|
|
3166
|
+
throw WorldOrbitError.fromLocation(`Unit "${unitValue.unit ?? "none"}" is not valid for "${fieldKey}"`, location);
|
|
2863
3167
|
}
|
|
2864
|
-
|
|
3168
|
+
}
|
|
3169
|
+
return unitValue;
|
|
3170
|
+
}
|
|
3171
|
+
function tryParseAtlasUnitValue(input) {
|
|
3172
|
+
const match = input.match(UNIT_PATTERN2);
|
|
3173
|
+
if (!match) {
|
|
3174
|
+
return null;
|
|
3175
|
+
}
|
|
2865
3176
|
return {
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
sourceVersion: "1.0",
|
|
2869
|
-
system,
|
|
2870
|
-
objects: normalizedObjects,
|
|
2871
|
-
diagnostics
|
|
3177
|
+
value: Number(match[1]),
|
|
3178
|
+
unit: match[2] ?? null
|
|
2872
3179
|
};
|
|
2873
3180
|
}
|
|
2874
|
-
function
|
|
2875
|
-
|
|
2876
|
-
|
|
3181
|
+
function parseAtlasNumber(input, key, location) {
|
|
3182
|
+
const value = Number(input);
|
|
3183
|
+
if (!Number.isFinite(value)) {
|
|
3184
|
+
throw WorldOrbitError.fromLocation(`Invalid numeric value "${input}" for "${key}"`, location);
|
|
2877
3185
|
}
|
|
2878
|
-
return
|
|
3186
|
+
return value;
|
|
2879
3187
|
}
|
|
2880
|
-
function
|
|
2881
|
-
const
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
if (system) {
|
|
2885
|
-
throw new WorldOrbitError('Atlas section "system" may only appear once', line, tokens[0].column);
|
|
2886
|
-
}
|
|
2887
|
-
return startSystemSection(tokens, line);
|
|
2888
|
-
case "defaults":
|
|
2889
|
-
if (!system) {
|
|
2890
|
-
throw new WorldOrbitError('Atlas section "defaults" requires a preceding system declaration', line, tokens[0].column);
|
|
2891
|
-
}
|
|
2892
|
-
if (flags.sawDefaults) {
|
|
2893
|
-
throw new WorldOrbitError('Atlas section "defaults" may only appear once', line, tokens[0].column);
|
|
2894
|
-
}
|
|
2895
|
-
return {
|
|
2896
|
-
kind: "defaults",
|
|
2897
|
-
system,
|
|
2898
|
-
seenFields: /* @__PURE__ */ new Set()
|
|
2899
|
-
};
|
|
2900
|
-
case "atlas":
|
|
2901
|
-
if (!system) {
|
|
2902
|
-
throw new WorldOrbitError('Atlas section "atlas" requires a preceding system declaration', line, tokens[0].column);
|
|
2903
|
-
}
|
|
2904
|
-
if (flags.sawAtlas) {
|
|
2905
|
-
throw new WorldOrbitError('Atlas section "atlas" may only appear once', line, tokens[0].column);
|
|
2906
|
-
}
|
|
2907
|
-
return {
|
|
2908
|
-
kind: "atlas",
|
|
2909
|
-
system,
|
|
2910
|
-
inMetadata: false,
|
|
2911
|
-
metadataIndent: null
|
|
2912
|
-
};
|
|
2913
|
-
case "viewpoint":
|
|
2914
|
-
if (!system) {
|
|
2915
|
-
throw new WorldOrbitError('Atlas section "viewpoint" requires a preceding system declaration', line, tokens[0].column);
|
|
2916
|
-
}
|
|
2917
|
-
return startViewpointSection(tokens, line, system, viewpointIds);
|
|
2918
|
-
case "annotation":
|
|
2919
|
-
if (!system) {
|
|
2920
|
-
throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
|
|
2921
|
-
}
|
|
2922
|
-
return startAnnotationSection(tokens, line, system, annotationIds);
|
|
2923
|
-
case "object":
|
|
2924
|
-
return startObjectSection(tokens, line, objectNodes);
|
|
2925
|
-
default:
|
|
2926
|
-
throw new WorldOrbitError(`Unknown atlas section "${tokens[0]?.value ?? ""}"`, line, tokens[0]?.column ?? 1);
|
|
3188
|
+
function parseAtlasBoolean(input, key, location) {
|
|
3189
|
+
const parsed = BOOLEAN_VALUES2.get(input.toLowerCase());
|
|
3190
|
+
if (parsed === void 0) {
|
|
3191
|
+
throw WorldOrbitError.fromLocation(`Invalid boolean value "${input}" for "${key}"`, location);
|
|
2927
3192
|
}
|
|
3193
|
+
return parsed;
|
|
2928
3194
|
}
|
|
2929
|
-
function
|
|
2930
|
-
if (
|
|
2931
|
-
throw
|
|
3195
|
+
function parseAtlasAtReference(target, location) {
|
|
3196
|
+
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
3197
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
2932
3198
|
}
|
|
2933
|
-
const
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
3199
|
+
const pairedMatch = target.match(/^([A-Za-z0-9._-]+)-([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
3200
|
+
if (pairedMatch) {
|
|
3201
|
+
return {
|
|
3202
|
+
kind: "lagrange",
|
|
3203
|
+
primary: pairedMatch[1],
|
|
3204
|
+
secondary: pairedMatch[2],
|
|
3205
|
+
point: pairedMatch[3]
|
|
3206
|
+
};
|
|
3207
|
+
}
|
|
3208
|
+
const simpleMatch = target.match(/^([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
3209
|
+
if (simpleMatch) {
|
|
3210
|
+
return {
|
|
3211
|
+
kind: "lagrange",
|
|
3212
|
+
primary: simpleMatch[1],
|
|
3213
|
+
secondary: null,
|
|
3214
|
+
point: simpleMatch[2]
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
if (/^[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
3218
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
3219
|
+
}
|
|
3220
|
+
const anchorMatch = target.match(/^([A-Za-z0-9._-]+):([A-Za-z0-9._-]+)$/);
|
|
3221
|
+
if (anchorMatch) {
|
|
3222
|
+
return {
|
|
3223
|
+
kind: "anchor",
|
|
3224
|
+
objectId: anchorMatch[1],
|
|
3225
|
+
anchor: anchorMatch[2]
|
|
3226
|
+
};
|
|
3227
|
+
}
|
|
3228
|
+
return {
|
|
3229
|
+
kind: "named",
|
|
3230
|
+
name: target
|
|
3231
|
+
};
|
|
3232
|
+
}
|
|
3233
|
+
function validateAtlasImageSource(value, location) {
|
|
3234
|
+
if (!value) {
|
|
3235
|
+
throw WorldOrbitError.fromLocation('Field "image" must not be empty', location);
|
|
3236
|
+
}
|
|
3237
|
+
if (value.startsWith("//")) {
|
|
3238
|
+
throw WorldOrbitError.fromLocation('Field "image" must use a relative path, root-relative path, or an http/https URL', location);
|
|
3239
|
+
}
|
|
3240
|
+
const schemeMatch = value.match(URL_SCHEME_PATTERN2);
|
|
3241
|
+
if (!schemeMatch) {
|
|
3242
|
+
return;
|
|
3243
|
+
}
|
|
3244
|
+
const scheme = schemeMatch[0].slice(0, -1).toLowerCase();
|
|
3245
|
+
if (scheme !== "http" && scheme !== "https") {
|
|
3246
|
+
throw WorldOrbitError.fromLocation(`Field "image" does not support the "${scheme}" scheme`, location);
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
function normalizeLegacyScalarValue(key, values, location) {
|
|
3250
|
+
const schema = getFieldSchema(key);
|
|
3251
|
+
if (!schema) {
|
|
3252
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
3253
|
+
}
|
|
3254
|
+
if (schema.arity === "single" && values.length !== 1) {
|
|
3255
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
3256
|
+
}
|
|
3257
|
+
switch (schema.kind) {
|
|
3258
|
+
case "list":
|
|
3259
|
+
return values;
|
|
3260
|
+
case "boolean":
|
|
3261
|
+
return parseAtlasBoolean(singleAtlasValue(values, key, location), key, location);
|
|
3262
|
+
case "number":
|
|
3263
|
+
return parseAtlasNumber(singleAtlasValue(values, key, location), key, location);
|
|
3264
|
+
case "unit":
|
|
3265
|
+
return parseAtlasUnitValue(singleAtlasValue(values, key, location), location, key);
|
|
3266
|
+
case "string": {
|
|
3267
|
+
const value = values.join(" ").trim();
|
|
3268
|
+
if (key === "image") {
|
|
3269
|
+
validateAtlasImageSource(value, location);
|
|
3270
|
+
}
|
|
3271
|
+
return value;
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
function ensureAtlasFieldSupported(key, objectType, location) {
|
|
3276
|
+
const schema = getFieldSchema(key);
|
|
3277
|
+
if (!schema) {
|
|
3278
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${key}"`, location);
|
|
3279
|
+
}
|
|
3280
|
+
if (!schema.objectTypes.includes(objectType)) {
|
|
3281
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" is not valid on "${objectType}"`, location);
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
function singleAtlasValue(values, key, location) {
|
|
3285
|
+
if (values.length !== 1) {
|
|
3286
|
+
throw WorldOrbitError.fromLocation(`Field "${key}" expects exactly one value`, location);
|
|
3287
|
+
}
|
|
3288
|
+
return values[0];
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
// packages/core/dist/atlas-validate.js
|
|
3292
|
+
var SURFACE_TARGET_TYPES2 = /* @__PURE__ */ new Set(["star", "planet", "moon", "asteroid", "comet"]);
|
|
3293
|
+
var EARTH_MASSES_PER_SOLAR = 332946.0487;
|
|
3294
|
+
var JUPITER_MASSES_PER_SOLAR = 1047.3486;
|
|
3295
|
+
var AU_IN_KM2 = 1495978707e-1;
|
|
3296
|
+
var EARTH_RADIUS_IN_KM2 = 6371;
|
|
3297
|
+
var SOLAR_RADIUS_IN_KM2 = 695700;
|
|
3298
|
+
var LY_IN_AU2 = 63241.077;
|
|
3299
|
+
var PC_IN_AU2 = 206264.806;
|
|
3300
|
+
var KPC_IN_AU2 = 206264806;
|
|
3301
|
+
function collectAtlasDiagnostics(document2, sourceSchemaVersion) {
|
|
3302
|
+
const diagnostics = [];
|
|
3303
|
+
const objectMap = new Map(document2.objects.map((object) => [object.id, object]));
|
|
3304
|
+
const groupIds = new Set(document2.groups.map((group) => group.id));
|
|
3305
|
+
const eventIds = new Set(document2.events.map((event) => event.id));
|
|
3306
|
+
if (!document2.system) {
|
|
3307
|
+
diagnostics.push(error("validate.system.required", "Atlas documents must declare exactly one system."));
|
|
3308
|
+
}
|
|
3309
|
+
const knownIds = /* @__PURE__ */ new Map();
|
|
3310
|
+
for (const [kind, ids] of [
|
|
3311
|
+
["group", document2.groups.map((group) => group.id)],
|
|
3312
|
+
["viewpoint", document2.system?.viewpoints.map((viewpoint) => viewpoint.id) ?? []],
|
|
3313
|
+
["annotation", document2.system?.annotations.map((annotation) => annotation.id) ?? []],
|
|
3314
|
+
["relation", document2.relations.map((relation) => relation.id)],
|
|
3315
|
+
["event", document2.events.map((event) => event.id)],
|
|
3316
|
+
["object", document2.objects.map((object) => object.id)]
|
|
3317
|
+
]) {
|
|
3318
|
+
for (const id of ids) {
|
|
3319
|
+
const previous = knownIds.get(id);
|
|
3320
|
+
if (previous) {
|
|
3321
|
+
diagnostics.push(error("validate.id.duplicate", `Duplicate ${kind} id "${id}" already used by ${previous}.`));
|
|
3322
|
+
} else {
|
|
3323
|
+
knownIds.set(id, kind);
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
for (const relation of document2.relations) {
|
|
3328
|
+
validateRelation(relation, objectMap, diagnostics);
|
|
3329
|
+
}
|
|
3330
|
+
for (const viewpoint of document2.system?.viewpoints ?? []) {
|
|
3331
|
+
validateViewpoint(viewpoint.filter, viewpoint.events ?? [], groupIds, eventIds, sourceSchemaVersion, diagnostics, viewpoint.id);
|
|
3332
|
+
}
|
|
3333
|
+
for (const object of document2.objects) {
|
|
3334
|
+
validateObject(object, document2.system, objectMap, groupIds, diagnostics);
|
|
3335
|
+
}
|
|
3336
|
+
for (const event of document2.events) {
|
|
3337
|
+
validateEvent(event, objectMap, diagnostics);
|
|
3338
|
+
}
|
|
3339
|
+
return diagnostics;
|
|
3340
|
+
}
|
|
3341
|
+
function validateRelation(relation, objectMap, diagnostics) {
|
|
3342
|
+
if (!relation.from) {
|
|
3343
|
+
diagnostics.push(error("validate.relation.from.required", `Relation "${relation.id}" is missing a "from" target.`));
|
|
3344
|
+
} else if (!objectMap.has(relation.from)) {
|
|
3345
|
+
diagnostics.push(error("validate.relation.from.unknown", `Unknown relation source "${relation.from}" on "${relation.id}".`));
|
|
3346
|
+
}
|
|
3347
|
+
if (!relation.to) {
|
|
3348
|
+
diagnostics.push(error("validate.relation.to.required", `Relation "${relation.id}" is missing a "to" target.`));
|
|
3349
|
+
} else if (!objectMap.has(relation.to)) {
|
|
3350
|
+
diagnostics.push(error("validate.relation.to.unknown", `Unknown relation target "${relation.to}" on "${relation.id}".`));
|
|
3351
|
+
}
|
|
3352
|
+
if (!relation.kind) {
|
|
3353
|
+
diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
function validateViewpoint(filter, eventRefs, groupIds, eventIds, sourceSchemaVersion, diagnostics, viewpointId) {
|
|
3357
|
+
if (sourceSchemaVersion === "2.1") {
|
|
3358
|
+
if (filter) {
|
|
3359
|
+
for (const groupId of filter.groupIds) {
|
|
3360
|
+
if (!groupIds.has(groupId)) {
|
|
3361
|
+
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpointId}".`, void 0, `viewpoint.${viewpointId}.groups`));
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
for (const eventId of eventRefs) {
|
|
3366
|
+
if (!eventIds.has(eventId)) {
|
|
3367
|
+
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${viewpointId}".`, void 0, `viewpoint.${viewpointId}.events`));
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
3373
|
+
const placement = object.placement;
|
|
3374
|
+
const orbitPlacement = placement?.mode === "orbit" ? placement : null;
|
|
3375
|
+
const parentObject = placement?.mode === "orbit" ? objectMap.get(placement.target) ?? null : null;
|
|
3376
|
+
if (object.groups) {
|
|
3377
|
+
for (const groupId of object.groups) {
|
|
3378
|
+
if (!groupIds.has(groupId)) {
|
|
3379
|
+
diagnostics.push(warn("validate.group.unknown", `Unknown group "${groupId}" on "${object.id}".`, object.id, "groups"));
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
if (orbitPlacement) {
|
|
3384
|
+
if (!objectMap.has(orbitPlacement.target)) {
|
|
3385
|
+
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
3386
|
+
}
|
|
3387
|
+
if (orbitPlacement.distance && orbitPlacement.semiMajor) {
|
|
3388
|
+
diagnostics.push(error("validate.orbit.distanceConflict", `Object "${object.id}" cannot declare both "distance" and "semiMajor".`, object.id, "distance"));
|
|
3389
|
+
}
|
|
3390
|
+
if (orbitPlacement.phase && !object.epoch && !system?.epoch) {
|
|
3391
|
+
diagnostics.push(warn("validate.phase.epochMissing", `Object "${object.id}" sets "phase" without an object or system epoch.`, object.id, "phase"));
|
|
3392
|
+
}
|
|
3393
|
+
if (orbitPlacement.inclination && !object.referencePlane && !system?.referencePlane) {
|
|
3394
|
+
diagnostics.push(warn("validate.inclination.referencePlaneMissing", `Object "${object.id}" sets "inclination" without an object or system reference plane.`, object.id, "inclination"));
|
|
3395
|
+
}
|
|
3396
|
+
if (orbitPlacement.period && !massInSolar(parentObject?.properties.mass)) {
|
|
3397
|
+
diagnostics.push(warn("validate.period.massMissing", `Object "${object.id}" sets "period" but its central mass cannot be derived.`, object.id, "period"));
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
if (placement?.mode === "surface") {
|
|
3401
|
+
const target = objectMap.get(placement.target);
|
|
3402
|
+
if (!target) {
|
|
3403
|
+
diagnostics.push(error("validate.surface.target.unknown", `Unknown placement target "${placement.target}" on "${object.id}".`, object.id, "surface"));
|
|
3404
|
+
} else if (!SURFACE_TARGET_TYPES2.has(target.type)) {
|
|
3405
|
+
diagnostics.push(error("validate.surface.target.invalid", `Surface target "${placement.target}" on "${object.id}" is not surface-capable.`, object.id, "surface"));
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
if (placement?.mode === "at") {
|
|
3409
|
+
if (object.type !== "structure" && object.type !== "phenomenon") {
|
|
3410
|
+
diagnostics.push(error("validate.at.objectType", `Only structures and phenomena may use "at" placement; found "${object.type}" on "${object.id}".`, object.id, "at"));
|
|
3411
|
+
}
|
|
3412
|
+
if (!validateAtTarget(object, objectMap, diagnostics)) {
|
|
3413
|
+
diagnostics.push(error("validate.at.target.unknown", `Unknown at-reference target "${placement.target}" on "${object.id}".`, object.id, "at"));
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
if (object.resonance) {
|
|
3417
|
+
const target = objectMap.get(object.resonance.targetObjectId);
|
|
3418
|
+
if (!target) {
|
|
3419
|
+
diagnostics.push(error("validate.resonance.target.unknown", `Unknown resonance target "${object.resonance.targetObjectId}" on "${object.id}".`, object.id, "resonance"));
|
|
3420
|
+
} else if (object.placement?.mode !== "orbit" || target.placement?.mode !== "orbit" || object.placement.target !== target.placement.target) {
|
|
3421
|
+
diagnostics.push(warn("validate.resonance.orbitMismatch", `Resonance target "${object.resonance.targetObjectId}" on "${object.id}" does not share a compatible orbital parent.`, object.id, "resonance"));
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
for (const rule of object.deriveRules ?? []) {
|
|
3425
|
+
if (rule.field !== "period" || rule.strategy !== "kepler") {
|
|
3426
|
+
diagnostics.push(warn("validate.derive.unsupported", `Unsupported derive rule "${rule.field} ${rule.strategy}" on "${object.id}".`, object.id, "derive"));
|
|
3427
|
+
continue;
|
|
3428
|
+
}
|
|
3429
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
3430
|
+
if (derivedPeriodDays === null) {
|
|
3431
|
+
diagnostics.push(warn("validate.derive.inputsMissing", `Object "${object.id}" requests "derive period kepler" but lacks enough input data.`, object.id, "derive"));
|
|
3432
|
+
continue;
|
|
3433
|
+
}
|
|
3434
|
+
if (!orbitPlacement?.period) {
|
|
3435
|
+
diagnostics.push(info("validate.derive.period.available", `Object "${object.id}" can derive a Kepler period of ${formatDays(derivedPeriodDays)}.`, object.id, "derive"));
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
for (const rule of object.validationRules ?? []) {
|
|
3439
|
+
if (rule.rule !== "kepler") {
|
|
3440
|
+
diagnostics.push(warn("validate.rule.unsupported", `Unsupported validation rule "${rule.rule}" on "${object.id}".`, object.id, "validate"));
|
|
3441
|
+
continue;
|
|
3442
|
+
}
|
|
3443
|
+
const actualPeriodDays = durationInDays(orbitPlacement?.period);
|
|
3444
|
+
const derivedPeriodDays = keplerPeriodDays(object, parentObject);
|
|
3445
|
+
if (actualPeriodDays === null || derivedPeriodDays === null) {
|
|
3446
|
+
continue;
|
|
3447
|
+
}
|
|
3448
|
+
const toleranceDays = toleranceForField(object, "period");
|
|
3449
|
+
if (Math.abs(actualPeriodDays - derivedPeriodDays) > toleranceDays) {
|
|
3450
|
+
diagnostics.push(error("validate.kepler.mismatch", `Object "${object.id}" fails Kepler validation for "period".`, object.id, "validate"));
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
function validateEvent(event, objectMap, diagnostics) {
|
|
3455
|
+
const fieldPrefix = `event.${event.id}`;
|
|
3456
|
+
const referencedIds = /* @__PURE__ */ new Set();
|
|
3457
|
+
if (!event.kind.trim()) {
|
|
3458
|
+
diagnostics.push(error("validate.event.kind.required", `Event "${event.id}" is missing a "kind" value.`, void 0, `${fieldPrefix}.kind`));
|
|
3459
|
+
}
|
|
3460
|
+
if (!event.targetObjectId && event.participantObjectIds.length === 0) {
|
|
3461
|
+
diagnostics.push(error("validate.event.references.required", `Event "${event.id}" must define a "target" or at least one participant.`, void 0, `${fieldPrefix}.participants`));
|
|
3462
|
+
}
|
|
3463
|
+
if (event.targetObjectId) {
|
|
3464
|
+
referencedIds.add(event.targetObjectId);
|
|
3465
|
+
if (!objectMap.has(event.targetObjectId)) {
|
|
3466
|
+
diagnostics.push(error("validate.event.target.unknown", `Unknown event target "${event.targetObjectId}" on "${event.id}".`, void 0, `${fieldPrefix}.target`));
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
const seenParticipants = /* @__PURE__ */ new Set();
|
|
3470
|
+
for (const participantId of event.participantObjectIds) {
|
|
3471
|
+
referencedIds.add(participantId);
|
|
3472
|
+
if (seenParticipants.has(participantId)) {
|
|
3473
|
+
diagnostics.push(warn("validate.event.participants.duplicate", `Event "${event.id}" repeats participant "${participantId}".`, void 0, `${fieldPrefix}.participants`));
|
|
3474
|
+
continue;
|
|
3475
|
+
}
|
|
3476
|
+
seenParticipants.add(participantId);
|
|
3477
|
+
if (!objectMap.has(participantId)) {
|
|
3478
|
+
diagnostics.push(error("validate.event.participants.unknown", `Unknown event participant "${participantId}" on "${event.id}".`, void 0, `${fieldPrefix}.participants`));
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
if (event.targetObjectId && event.participantObjectIds.length > 0 && !event.participantObjectIds.includes(event.targetObjectId)) {
|
|
3482
|
+
diagnostics.push(warn("validate.event.target.notParticipant", `Event "${event.id}" defines a target outside its participants list.`, void 0, `${fieldPrefix}.target`));
|
|
3483
|
+
}
|
|
3484
|
+
if (event.positions.length === 0) {
|
|
3485
|
+
diagnostics.push(warn("validate.event.positions.missing", `Event "${event.id}" has no positions block and cannot drive a scene snapshot.`, void 0, `${fieldPrefix}.positions`));
|
|
3486
|
+
}
|
|
3487
|
+
if (/(?:^|[-_])(solar-eclipse|lunar-eclipse|transit|occultation)(?:$|[-_])/.test(event.kind) && referencedIds.size < 3) {
|
|
3488
|
+
diagnostics.push(warn("validate.event.kind.participants", `Event "${event.id}" looks like an eclipse or transit but references fewer than three bodies.`, void 0, `${fieldPrefix}.participants`));
|
|
3489
|
+
}
|
|
3490
|
+
const poseIds = /* @__PURE__ */ new Set();
|
|
3491
|
+
for (const pose of event.positions) {
|
|
3492
|
+
const poseFieldPrefix = `${fieldPrefix}.pose.${pose.objectId}`;
|
|
3493
|
+
if (poseIds.has(pose.objectId)) {
|
|
3494
|
+
diagnostics.push(error("validate.event.pose.duplicate", `Event "${event.id}" defines "${pose.objectId}" more than once in positions.`, void 0, poseFieldPrefix));
|
|
3495
|
+
continue;
|
|
3496
|
+
}
|
|
3497
|
+
poseIds.add(pose.objectId);
|
|
3498
|
+
const object = objectMap.get(pose.objectId);
|
|
3499
|
+
if (!object) {
|
|
3500
|
+
diagnostics.push(error("validate.event.pose.object.unknown", `Unknown event pose object "${pose.objectId}" on "${event.id}".`, void 0, poseFieldPrefix));
|
|
3501
|
+
continue;
|
|
3502
|
+
}
|
|
3503
|
+
if (!referencedIds.has(pose.objectId)) {
|
|
3504
|
+
diagnostics.push(warn("validate.event.pose.unreferenced", `Event pose "${pose.objectId}" on "${event.id}" is not listed in target/participants.`, void 0, poseFieldPrefix));
|
|
3505
|
+
}
|
|
3506
|
+
validateEventPose(pose, object, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
function validateEventPose(pose, object, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
3510
|
+
const placement = pose.placement;
|
|
3511
|
+
if (!placement) {
|
|
3512
|
+
diagnostics.push(error("validate.event.pose.placement.required", `Event "${eventId}" pose "${pose.objectId}" is missing a placement mode.`, void 0, fieldPrefix));
|
|
3513
|
+
return;
|
|
3514
|
+
}
|
|
3515
|
+
if (placement.mode === "orbit") {
|
|
3516
|
+
if (!objectMap.has(placement.target)) {
|
|
3517
|
+
diagnostics.push(error("validate.event.pose.orbit.target.unknown", `Unknown event orbit target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.orbit`));
|
|
3518
|
+
}
|
|
3519
|
+
if (placement.distance && placement.semiMajor) {
|
|
3520
|
+
diagnostics.push(error("validate.event.pose.orbit.distanceConflict", `Event "${eventId}" pose "${pose.objectId}" cannot declare both "distance" and "semiMajor".`, void 0, `${fieldPrefix}.distance`));
|
|
3521
|
+
}
|
|
3522
|
+
return;
|
|
3523
|
+
}
|
|
3524
|
+
if (placement.mode === "surface") {
|
|
3525
|
+
const target = objectMap.get(placement.target);
|
|
3526
|
+
if (!target) {
|
|
3527
|
+
diagnostics.push(error("validate.event.pose.surface.target.unknown", `Unknown event surface target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.surface`));
|
|
3528
|
+
} else if (!SURFACE_TARGET_TYPES2.has(target.type)) {
|
|
3529
|
+
diagnostics.push(error("validate.event.pose.surface.target.invalid", `Event surface target "${placement.target}" on "${eventId}:${pose.objectId}" is not surface-capable.`, void 0, `${fieldPrefix}.surface`));
|
|
3530
|
+
}
|
|
3531
|
+
return;
|
|
3532
|
+
}
|
|
3533
|
+
if (placement.mode === "at") {
|
|
3534
|
+
if (object.type !== "structure" && object.type !== "phenomenon") {
|
|
3535
|
+
diagnostics.push(error("validate.event.pose.at.objectType", `Only structures and phenomena may use "at" placement in events; found "${object.type}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
3536
|
+
}
|
|
3537
|
+
const reference = placement.reference;
|
|
3538
|
+
if (reference.kind === "named" && !objectMap.has(reference.name)) {
|
|
3539
|
+
diagnostics.push(error("validate.event.pose.at.target.unknown", `Unknown event at-reference target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
3540
|
+
} else if (reference.kind === "anchor" && !objectMap.has(reference.objectId)) {
|
|
3541
|
+
diagnostics.push(error("validate.event.pose.anchor.target.unknown", `Unknown event anchor target "${reference.objectId}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
3542
|
+
} else if (reference.kind === "lagrange") {
|
|
3543
|
+
if (!objectMap.has(reference.primary)) {
|
|
3544
|
+
diagnostics.push(error("validate.event.pose.lagrange.primary.unknown", `Unknown event Lagrange target "${reference.primary}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
3545
|
+
} else if (reference.secondary && !objectMap.has(reference.secondary)) {
|
|
3546
|
+
diagnostics.push(error("validate.event.pose.lagrange.secondary.unknown", `Unknown event Lagrange target "${reference.secondary}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
function validateAtTarget(object, objectMap, diagnostics) {
|
|
3552
|
+
const reference = object.placement?.mode === "at" ? object.placement.reference : null;
|
|
3553
|
+
if (!reference) {
|
|
3554
|
+
return true;
|
|
3555
|
+
}
|
|
3556
|
+
if (reference.kind === "named") {
|
|
3557
|
+
return objectMap.has(reference.name);
|
|
3558
|
+
}
|
|
3559
|
+
if (reference.kind === "anchor") {
|
|
3560
|
+
if (!objectMap.has(reference.objectId)) {
|
|
3561
|
+
diagnostics.push(error("validate.anchor.target.unknown", `Unknown anchor target "${reference.objectId}" on "${object.id}".`, object.id, "at"));
|
|
3562
|
+
return false;
|
|
3563
|
+
}
|
|
3564
|
+
return true;
|
|
3565
|
+
}
|
|
3566
|
+
if (!objectMap.has(reference.primary)) {
|
|
3567
|
+
diagnostics.push(error("validate.lagrange.primary.unknown", `Unknown Lagrange reference "${reference.primary}" on "${object.id}".`, object.id, "at"));
|
|
3568
|
+
return false;
|
|
3569
|
+
}
|
|
3570
|
+
if (reference.secondary && !objectMap.has(reference.secondary)) {
|
|
3571
|
+
diagnostics.push(error("validate.lagrange.secondary.unknown", `Unknown Lagrange reference "${reference.secondary}" on "${object.id}".`, object.id, "at"));
|
|
3572
|
+
return false;
|
|
3573
|
+
}
|
|
3574
|
+
return true;
|
|
3575
|
+
}
|
|
3576
|
+
function keplerPeriodDays(object, parentObject) {
|
|
3577
|
+
const placement = object.placement;
|
|
3578
|
+
if (!placement || placement.mode !== "orbit") {
|
|
3579
|
+
return null;
|
|
3580
|
+
}
|
|
3581
|
+
const semiMajorAu = distanceInAu(placement.semiMajor ?? placement.distance);
|
|
3582
|
+
const centralMassSolar = massInSolar(parentObject?.properties.mass);
|
|
3583
|
+
if (semiMajorAu === null || centralMassSolar === null || centralMassSolar <= 0) {
|
|
3584
|
+
return null;
|
|
3585
|
+
}
|
|
3586
|
+
const periodYears = Math.sqrt(semiMajorAu ** 3 / centralMassSolar);
|
|
3587
|
+
return periodYears * 365.25;
|
|
3588
|
+
}
|
|
3589
|
+
function distanceInAu(value) {
|
|
3590
|
+
if (!value)
|
|
3591
|
+
return null;
|
|
3592
|
+
switch (value.unit) {
|
|
3593
|
+
case null:
|
|
3594
|
+
case "au":
|
|
3595
|
+
return value.value;
|
|
3596
|
+
case "km":
|
|
3597
|
+
return value.value / AU_IN_KM2;
|
|
3598
|
+
case "m":
|
|
3599
|
+
return value.value / (AU_IN_KM2 * 1e3);
|
|
3600
|
+
case "ly":
|
|
3601
|
+
return value.value * LY_IN_AU2;
|
|
3602
|
+
case "pc":
|
|
3603
|
+
return value.value * PC_IN_AU2;
|
|
3604
|
+
case "kpc":
|
|
3605
|
+
return value.value * KPC_IN_AU2;
|
|
3606
|
+
case "re":
|
|
3607
|
+
return value.value * EARTH_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
3608
|
+
case "sol":
|
|
3609
|
+
return value.value * SOLAR_RADIUS_IN_KM2 / AU_IN_KM2;
|
|
3610
|
+
default:
|
|
3611
|
+
return null;
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
function massInSolar(value) {
|
|
3615
|
+
if (!value || typeof value !== "object" || !("value" in value)) {
|
|
3616
|
+
return null;
|
|
3617
|
+
}
|
|
3618
|
+
const unitValue = value;
|
|
3619
|
+
switch (unitValue.unit) {
|
|
3620
|
+
case null:
|
|
3621
|
+
case "sol":
|
|
3622
|
+
return unitValue.value;
|
|
3623
|
+
case "me":
|
|
3624
|
+
return unitValue.value / EARTH_MASSES_PER_SOLAR;
|
|
3625
|
+
case "mj":
|
|
3626
|
+
return unitValue.value / JUPITER_MASSES_PER_SOLAR;
|
|
3627
|
+
default:
|
|
3628
|
+
return null;
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
function durationInDays(value) {
|
|
3632
|
+
if (!value)
|
|
3633
|
+
return null;
|
|
3634
|
+
switch (value.unit) {
|
|
3635
|
+
case null:
|
|
3636
|
+
case "d":
|
|
3637
|
+
return value.value;
|
|
3638
|
+
case "s":
|
|
3639
|
+
return value.value / 86400;
|
|
3640
|
+
case "min":
|
|
3641
|
+
return value.value / 1440;
|
|
3642
|
+
case "h":
|
|
3643
|
+
return value.value / 24;
|
|
3644
|
+
case "y":
|
|
3645
|
+
return value.value * 365.25;
|
|
3646
|
+
case "ky":
|
|
3647
|
+
return value.value * 365250;
|
|
3648
|
+
case "my":
|
|
3649
|
+
return value.value * 36525e4;
|
|
3650
|
+
case "gy":
|
|
3651
|
+
return value.value * 36525e7;
|
|
3652
|
+
default:
|
|
3653
|
+
return null;
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
function toleranceForField(object, field) {
|
|
3657
|
+
const tolerance = object.tolerances?.find((entry) => entry.field === field)?.value;
|
|
3658
|
+
if (typeof tolerance === "number") {
|
|
3659
|
+
return tolerance;
|
|
3660
|
+
}
|
|
3661
|
+
if (tolerance && typeof tolerance === "object" && "value" in tolerance) {
|
|
3662
|
+
return durationInDays(tolerance) ?? 0;
|
|
3663
|
+
}
|
|
3664
|
+
return 0;
|
|
3665
|
+
}
|
|
3666
|
+
function formatDays(days) {
|
|
3667
|
+
return `${Math.round(days * 100) / 100}d`;
|
|
3668
|
+
}
|
|
3669
|
+
function error(code, message, objectId, field) {
|
|
3670
|
+
return { code, severity: "error", source: "validate", message, objectId, field };
|
|
3671
|
+
}
|
|
3672
|
+
function warn(code, message, objectId, field) {
|
|
3673
|
+
return { code, severity: "warning", source: "validate", message, objectId, field };
|
|
3674
|
+
}
|
|
3675
|
+
function info(code, message, objectId, field) {
|
|
3676
|
+
return { code, severity: "info", source: "validate", message, objectId, field };
|
|
3677
|
+
}
|
|
3678
|
+
|
|
3679
|
+
// packages/core/dist/draft-parse.js
|
|
3680
|
+
var STRUCTURED_TYPED_BLOCKS = /* @__PURE__ */ new Set([
|
|
3681
|
+
"climate",
|
|
3682
|
+
"habitability",
|
|
3683
|
+
"settlement"
|
|
3684
|
+
]);
|
|
3685
|
+
var DRAFT_OBJECT_FIELD_SPECS = /* @__PURE__ */ new Map();
|
|
3686
|
+
for (const key of [
|
|
3687
|
+
"orbit",
|
|
3688
|
+
"distance",
|
|
3689
|
+
"semiMajor",
|
|
3690
|
+
"eccentricity",
|
|
3691
|
+
"period",
|
|
3692
|
+
"angle",
|
|
3693
|
+
"inclination",
|
|
3694
|
+
"phase",
|
|
3695
|
+
"at",
|
|
3696
|
+
"surface",
|
|
3697
|
+
"free",
|
|
3698
|
+
"kind",
|
|
3699
|
+
"class",
|
|
3700
|
+
"culture",
|
|
3701
|
+
"tags",
|
|
3702
|
+
"color",
|
|
3703
|
+
"image",
|
|
3704
|
+
"hidden",
|
|
3705
|
+
"radius",
|
|
3706
|
+
"mass",
|
|
3707
|
+
"density",
|
|
3708
|
+
"gravity",
|
|
3709
|
+
"temperature",
|
|
3710
|
+
"albedo",
|
|
3711
|
+
"atmosphere",
|
|
3712
|
+
"inner",
|
|
3713
|
+
"outer",
|
|
3714
|
+
"on",
|
|
3715
|
+
"source",
|
|
3716
|
+
"cycle"
|
|
3717
|
+
]) {
|
|
3718
|
+
const schema = getFieldSchema(key);
|
|
3719
|
+
if (schema) {
|
|
3720
|
+
DRAFT_OBJECT_FIELD_SPECS.set(key, {
|
|
3721
|
+
key,
|
|
3722
|
+
version: "2.0",
|
|
3723
|
+
inlineMode: schema.arity === "multiple" ? "multiple" : "single",
|
|
3724
|
+
allowRepeat: false,
|
|
3725
|
+
legacySchema: schema
|
|
3726
|
+
});
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
for (const spec of [
|
|
3730
|
+
{ key: "groups", inlineMode: "multiple", allowRepeat: false },
|
|
3731
|
+
{ key: "epoch", inlineMode: "single", allowRepeat: false },
|
|
3732
|
+
{ key: "referencePlane", inlineMode: "single", allowRepeat: false },
|
|
3733
|
+
{ key: "tidalLock", inlineMode: "single", allowRepeat: false },
|
|
3734
|
+
{ key: "renderLabel", inlineMode: "single", allowRepeat: false },
|
|
3735
|
+
{ key: "renderOrbit", inlineMode: "single", allowRepeat: false },
|
|
3736
|
+
{ key: "renderPriority", inlineMode: "single", allowRepeat: false },
|
|
3737
|
+
{ key: "resonance", inlineMode: "pair", allowRepeat: false },
|
|
3738
|
+
{ key: "derive", inlineMode: "pair", allowRepeat: true },
|
|
3739
|
+
{ key: "validate", inlineMode: "single", allowRepeat: true },
|
|
3740
|
+
{ key: "locked", inlineMode: "multiple", allowRepeat: false },
|
|
3741
|
+
{ key: "tolerance", inlineMode: "pair", allowRepeat: true }
|
|
3742
|
+
]) {
|
|
3743
|
+
DRAFT_OBJECT_FIELD_SPECS.set(spec.key, {
|
|
3744
|
+
key: spec.key,
|
|
3745
|
+
version: "2.1",
|
|
3746
|
+
inlineMode: spec.inlineMode,
|
|
3747
|
+
allowRepeat: spec.allowRepeat
|
|
3748
|
+
});
|
|
3749
|
+
}
|
|
3750
|
+
var DRAFT_OBJECT_FIELD_KEYS = new Set(DRAFT_OBJECT_FIELD_SPECS.keys());
|
|
3751
|
+
var EVENT_POSE_FIELD_KEYS = /* @__PURE__ */ new Set([
|
|
3752
|
+
"orbit",
|
|
3753
|
+
"distance",
|
|
3754
|
+
"semiMajor",
|
|
3755
|
+
"eccentricity",
|
|
3756
|
+
"period",
|
|
3757
|
+
"angle",
|
|
3758
|
+
"inclination",
|
|
3759
|
+
"phase",
|
|
3760
|
+
"at",
|
|
3761
|
+
"surface",
|
|
3762
|
+
"free",
|
|
3763
|
+
"inner",
|
|
3764
|
+
"outer"
|
|
3765
|
+
]);
|
|
3766
|
+
function parseWorldOrbitAtlas(source) {
|
|
3767
|
+
return parseAtlasSource(source);
|
|
3768
|
+
}
|
|
3769
|
+
function parseAtlasSource(source, forcedOutputVersion) {
|
|
3770
|
+
const prepared = preprocessAtlasSource(source);
|
|
3771
|
+
const lines = prepared.source.split(/\r?\n/);
|
|
3772
|
+
const diagnostics = [];
|
|
3773
|
+
let sawSchemaHeader = false;
|
|
3774
|
+
let sourceSchemaVersion = "2.0";
|
|
3775
|
+
let system = null;
|
|
3776
|
+
let section = null;
|
|
3777
|
+
const objectNodes = [];
|
|
3778
|
+
const groups = [];
|
|
3779
|
+
const relations = [];
|
|
3780
|
+
const events = [];
|
|
3781
|
+
const eventPoseNodes = /* @__PURE__ */ new Map();
|
|
3782
|
+
let sawDefaults = false;
|
|
3783
|
+
let sawAtlas = false;
|
|
3784
|
+
const viewpointIds = /* @__PURE__ */ new Set();
|
|
3785
|
+
const annotationIds = /* @__PURE__ */ new Set();
|
|
3786
|
+
const groupIds = /* @__PURE__ */ new Set();
|
|
3787
|
+
const relationIds = /* @__PURE__ */ new Set();
|
|
3788
|
+
const eventIds = /* @__PURE__ */ new Set();
|
|
3789
|
+
for (let index = 0; index < lines.length; index++) {
|
|
3790
|
+
const rawLine = lines[index];
|
|
3791
|
+
const lineNumber = index + 1;
|
|
3792
|
+
if (!rawLine.trim()) {
|
|
3793
|
+
continue;
|
|
3794
|
+
}
|
|
3795
|
+
const indent = getIndent(rawLine);
|
|
3796
|
+
const tokens = tokenizeLineDetailed(rawLine.slice(indent), {
|
|
3797
|
+
line: lineNumber,
|
|
3798
|
+
columnOffset: indent
|
|
3799
|
+
});
|
|
3800
|
+
if (tokens.length === 0) {
|
|
3801
|
+
continue;
|
|
3802
|
+
}
|
|
3803
|
+
if (!sawSchemaHeader) {
|
|
3804
|
+
sourceSchemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
3805
|
+
sawSchemaHeader = true;
|
|
3806
|
+
if (prepared.comments.length > 0 && sourceSchemaVersion !== "2.1") {
|
|
3807
|
+
diagnostics.push({
|
|
3808
|
+
code: "parse.schema21.commentCompatibility",
|
|
3809
|
+
severity: "warning",
|
|
3810
|
+
source: "parse",
|
|
3811
|
+
message: `Comments require schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
3812
|
+
line: prepared.comments[0].line,
|
|
3813
|
+
column: prepared.comments[0].column
|
|
3814
|
+
});
|
|
3815
|
+
}
|
|
3816
|
+
continue;
|
|
3817
|
+
}
|
|
3818
|
+
if (indent === 0) {
|
|
3819
|
+
section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, { sawDefaults, sawAtlas });
|
|
3820
|
+
if (section.kind === "system") {
|
|
3821
|
+
system = section.system;
|
|
3822
|
+
} else if (section.kind === "defaults") {
|
|
3823
|
+
sawDefaults = true;
|
|
3824
|
+
} else if (section.kind === "atlas") {
|
|
3825
|
+
sawAtlas = true;
|
|
3826
|
+
}
|
|
3827
|
+
continue;
|
|
3828
|
+
}
|
|
3829
|
+
if (!section) {
|
|
3830
|
+
throw new WorldOrbitError("Indented line without parent atlas section", lineNumber, indent + 1);
|
|
3831
|
+
}
|
|
3832
|
+
handleSectionLine(section, indent, tokens, lineNumber);
|
|
3833
|
+
}
|
|
3834
|
+
if (!sawSchemaHeader) {
|
|
3835
|
+
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
|
|
3836
|
+
}
|
|
3837
|
+
const objects = objectNodes.map((node) => normalizeDraftObject(node, sourceSchemaVersion, diagnostics));
|
|
3838
|
+
const normalizedEvents = events.map((event) => normalizeDraftEvent(event, eventPoseNodes.get(event.id) ?? []));
|
|
3839
|
+
const outputVersion = forcedOutputVersion ?? (sourceSchemaVersion === "2.0-draft" ? "2.0" : sourceSchemaVersion);
|
|
3840
|
+
const baseDocument = {
|
|
3841
|
+
format: "worldorbit",
|
|
3842
|
+
sourceVersion: "1.0",
|
|
3843
|
+
system,
|
|
3844
|
+
groups,
|
|
3845
|
+
relations,
|
|
3846
|
+
events: normalizedEvents,
|
|
3847
|
+
objects,
|
|
3848
|
+
diagnostics
|
|
3849
|
+
};
|
|
3850
|
+
if (outputVersion === "2.0-draft") {
|
|
3851
|
+
const document3 = {
|
|
3852
|
+
...baseDocument,
|
|
3853
|
+
version: "2.0-draft",
|
|
3854
|
+
schemaVersion: "2.0-draft"
|
|
3855
|
+
};
|
|
3856
|
+
document3.diagnostics.push(...collectAtlasDiagnostics(document3, sourceSchemaVersion));
|
|
3857
|
+
return document3;
|
|
3858
|
+
}
|
|
3859
|
+
const document2 = {
|
|
3860
|
+
...baseDocument,
|
|
3861
|
+
version: outputVersion,
|
|
3862
|
+
schemaVersion: outputVersion
|
|
3863
|
+
};
|
|
3864
|
+
if (sourceSchemaVersion === "2.0-draft") {
|
|
3865
|
+
document2.diagnostics.push({
|
|
3866
|
+
code: "load.schema.deprecatedDraft",
|
|
3867
|
+
severity: "warning",
|
|
3868
|
+
source: "upgrade",
|
|
3869
|
+
message: 'Source header "schema 2.0-draft" is deprecated; canonical v2 documents now use "schema 2.0".'
|
|
3870
|
+
});
|
|
3871
|
+
}
|
|
3872
|
+
document2.diagnostics.push(...collectAtlasDiagnostics(document2, sourceSchemaVersion));
|
|
3873
|
+
return document2;
|
|
3874
|
+
}
|
|
3875
|
+
function assertDraftSchemaHeader(tokens, line) {
|
|
3876
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1"].includes(tokens[1].value.toLowerCase())) {
|
|
3877
|
+
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
3878
|
+
}
|
|
3879
|
+
const version = tokens[1].value.toLowerCase();
|
|
3880
|
+
return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
3881
|
+
}
|
|
3882
|
+
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, flags) {
|
|
3883
|
+
const keyword = tokens[0]?.value.toLowerCase();
|
|
3884
|
+
switch (keyword) {
|
|
3885
|
+
case "system":
|
|
3886
|
+
if (system) {
|
|
3887
|
+
throw new WorldOrbitError('Atlas section "system" may only appear once', line, tokens[0].column);
|
|
3888
|
+
}
|
|
3889
|
+
return startSystemSection(tokens, line, sourceSchemaVersion, diagnostics);
|
|
3890
|
+
case "defaults":
|
|
3891
|
+
if (!system) {
|
|
3892
|
+
throw new WorldOrbitError('Atlas section "defaults" requires a preceding system declaration', line, tokens[0].column);
|
|
3893
|
+
}
|
|
3894
|
+
if (flags.sawDefaults) {
|
|
3895
|
+
throw new WorldOrbitError('Atlas section "defaults" may only appear once', line, tokens[0].column);
|
|
3896
|
+
}
|
|
3897
|
+
return {
|
|
3898
|
+
kind: "defaults",
|
|
3899
|
+
system,
|
|
3900
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
3901
|
+
};
|
|
3902
|
+
case "atlas":
|
|
3903
|
+
if (!system) {
|
|
3904
|
+
throw new WorldOrbitError('Atlas section "atlas" requires a preceding system declaration', line, tokens[0].column);
|
|
3905
|
+
}
|
|
3906
|
+
if (flags.sawAtlas) {
|
|
3907
|
+
throw new WorldOrbitError('Atlas section "atlas" may only appear once', line, tokens[0].column);
|
|
3908
|
+
}
|
|
3909
|
+
return {
|
|
3910
|
+
kind: "atlas",
|
|
3911
|
+
system,
|
|
3912
|
+
inMetadata: false,
|
|
3913
|
+
metadataIndent: null
|
|
3914
|
+
};
|
|
3915
|
+
case "viewpoint":
|
|
3916
|
+
if (!system) {
|
|
3917
|
+
throw new WorldOrbitError('Atlas section "viewpoint" requires a preceding system declaration', line, tokens[0].column);
|
|
3918
|
+
}
|
|
3919
|
+
return startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaVersion, diagnostics);
|
|
3920
|
+
case "annotation":
|
|
3921
|
+
if (!system) {
|
|
3922
|
+
throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
|
|
3923
|
+
}
|
|
3924
|
+
return startAnnotationSection(tokens, line, system, annotationIds);
|
|
3925
|
+
case "group":
|
|
3926
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "group", { line, column: tokens[0].column });
|
|
3927
|
+
return startGroupSection(tokens, line, groups, groupIds);
|
|
3928
|
+
case "relation":
|
|
3929
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "relation", { line, column: tokens[0].column });
|
|
3930
|
+
return startRelationSection(tokens, line, relations, relationIds);
|
|
3931
|
+
case "event":
|
|
3932
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "event", { line, column: tokens[0].column });
|
|
3933
|
+
return startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourceSchemaVersion, diagnostics);
|
|
3934
|
+
case "object":
|
|
3935
|
+
return startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes);
|
|
3936
|
+
default:
|
|
3937
|
+
throw new WorldOrbitError(`Unknown atlas section "${tokens[0]?.value ?? ""}"`, line, tokens[0]?.column ?? 1);
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
function startSystemSection(tokens, line, sourceSchemaVersion, diagnostics) {
|
|
3941
|
+
if (tokens.length !== 2) {
|
|
3942
|
+
throw new WorldOrbitError("Invalid atlas system declaration", line, tokens[0]?.column ?? 1);
|
|
3943
|
+
}
|
|
3944
|
+
const system = {
|
|
3945
|
+
type: "system",
|
|
3946
|
+
id: tokens[1].value,
|
|
3947
|
+
title: null,
|
|
3948
|
+
description: null,
|
|
3949
|
+
epoch: null,
|
|
3950
|
+
referencePlane: null,
|
|
3951
|
+
defaults: {
|
|
3952
|
+
view: "topdown",
|
|
3953
|
+
scale: null,
|
|
3954
|
+
units: null,
|
|
3955
|
+
preset: null,
|
|
3956
|
+
theme: null
|
|
3957
|
+
},
|
|
3958
|
+
atlasMetadata: {},
|
|
3959
|
+
viewpoints: [],
|
|
2946
3960
|
annotations: []
|
|
2947
3961
|
};
|
|
2948
3962
|
return {
|
|
2949
3963
|
kind: "system",
|
|
2950
3964
|
system,
|
|
3965
|
+
sourceSchemaVersion,
|
|
3966
|
+
diagnostics,
|
|
2951
3967
|
seenFields: /* @__PURE__ */ new Set()
|
|
2952
3968
|
};
|
|
2953
3969
|
}
|
|
2954
|
-
function startViewpointSection(tokens, line, system, viewpointIds) {
|
|
3970
|
+
function startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaVersion, diagnostics) {
|
|
2955
3971
|
if (tokens.length !== 2) {
|
|
2956
3972
|
throw new WorldOrbitError("Invalid viewpoint declaration", line, tokens[0]?.column ?? 1);
|
|
2957
3973
|
}
|
|
@@ -2968,6 +3984,7 @@ var WorldOrbit = (() => {
|
|
|
2968
3984
|
summary: "",
|
|
2969
3985
|
focusObjectId: null,
|
|
2970
3986
|
selectedObjectId: null,
|
|
3987
|
+
events: [],
|
|
2971
3988
|
projection: system.defaults.view,
|
|
2972
3989
|
preset: system.defaults.preset,
|
|
2973
3990
|
zoom: null,
|
|
@@ -2980,6 +3997,8 @@ var WorldOrbit = (() => {
|
|
|
2980
3997
|
return {
|
|
2981
3998
|
kind: "viewpoint",
|
|
2982
3999
|
viewpoint,
|
|
4000
|
+
sourceSchemaVersion,
|
|
4001
|
+
diagnostics,
|
|
2983
4002
|
seenFields: /* @__PURE__ */ new Set(),
|
|
2984
4003
|
inFilter: false,
|
|
2985
4004
|
filterIndent: null,
|
|
@@ -3013,7 +4032,107 @@ var WorldOrbit = (() => {
|
|
|
3013
4032
|
seenFields: /* @__PURE__ */ new Set()
|
|
3014
4033
|
};
|
|
3015
4034
|
}
|
|
3016
|
-
function
|
|
4035
|
+
function startGroupSection(tokens, line, groups, groupIds) {
|
|
4036
|
+
if (tokens.length !== 2) {
|
|
4037
|
+
throw new WorldOrbitError("Invalid group declaration", line, tokens[0]?.column ?? 1);
|
|
4038
|
+
}
|
|
4039
|
+
const id = normalizeIdentifier(tokens[1].value);
|
|
4040
|
+
if (!id) {
|
|
4041
|
+
throw new WorldOrbitError("Group id must not be empty", line, tokens[1].column);
|
|
4042
|
+
}
|
|
4043
|
+
if (groupIds.has(id)) {
|
|
4044
|
+
throw new WorldOrbitError(`Duplicate group id "${id}"`, line, tokens[1].column);
|
|
4045
|
+
}
|
|
4046
|
+
const group = {
|
|
4047
|
+
id,
|
|
4048
|
+
label: humanizeIdentifier2(id),
|
|
4049
|
+
summary: "",
|
|
4050
|
+
color: null,
|
|
4051
|
+
tags: [],
|
|
4052
|
+
hidden: false
|
|
4053
|
+
};
|
|
4054
|
+
groups.push(group);
|
|
4055
|
+
groupIds.add(id);
|
|
4056
|
+
return {
|
|
4057
|
+
kind: "group",
|
|
4058
|
+
group,
|
|
4059
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
4060
|
+
};
|
|
4061
|
+
}
|
|
4062
|
+
function startRelationSection(tokens, line, relations, relationIds) {
|
|
4063
|
+
if (tokens.length !== 2) {
|
|
4064
|
+
throw new WorldOrbitError("Invalid relation declaration", line, tokens[0]?.column ?? 1);
|
|
4065
|
+
}
|
|
4066
|
+
const id = normalizeIdentifier(tokens[1].value);
|
|
4067
|
+
if (!id) {
|
|
4068
|
+
throw new WorldOrbitError("Relation id must not be empty", line, tokens[1].column);
|
|
4069
|
+
}
|
|
4070
|
+
if (relationIds.has(id)) {
|
|
4071
|
+
throw new WorldOrbitError(`Duplicate relation id "${id}"`, line, tokens[1].column);
|
|
4072
|
+
}
|
|
4073
|
+
const relation = {
|
|
4074
|
+
id,
|
|
4075
|
+
from: "",
|
|
4076
|
+
to: "",
|
|
4077
|
+
kind: "",
|
|
4078
|
+
label: null,
|
|
4079
|
+
summary: null,
|
|
4080
|
+
tags: [],
|
|
4081
|
+
color: null,
|
|
4082
|
+
hidden: false
|
|
4083
|
+
};
|
|
4084
|
+
relations.push(relation);
|
|
4085
|
+
relationIds.add(id);
|
|
4086
|
+
return {
|
|
4087
|
+
kind: "relation",
|
|
4088
|
+
relation,
|
|
4089
|
+
seenFields: /* @__PURE__ */ new Set()
|
|
4090
|
+
};
|
|
4091
|
+
}
|
|
4092
|
+
function startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourceSchemaVersion, diagnostics) {
|
|
4093
|
+
if (tokens.length !== 2) {
|
|
4094
|
+
throw new WorldOrbitError("Invalid event declaration", line, tokens[0]?.column ?? 1);
|
|
4095
|
+
}
|
|
4096
|
+
const id = normalizeIdentifier(tokens[1].value);
|
|
4097
|
+
if (!id) {
|
|
4098
|
+
throw new WorldOrbitError("Event id must not be empty", line, tokens[1].column);
|
|
4099
|
+
}
|
|
4100
|
+
if (eventIds.has(id)) {
|
|
4101
|
+
throw new WorldOrbitError(`Duplicate event id "${id}"`, line, tokens[1].column);
|
|
4102
|
+
}
|
|
4103
|
+
const event = {
|
|
4104
|
+
id,
|
|
4105
|
+
kind: "",
|
|
4106
|
+
label: humanizeIdentifier2(id),
|
|
4107
|
+
summary: null,
|
|
4108
|
+
targetObjectId: null,
|
|
4109
|
+
participantObjectIds: [],
|
|
4110
|
+
timing: null,
|
|
4111
|
+
visibility: null,
|
|
4112
|
+
tags: [],
|
|
4113
|
+
color: null,
|
|
4114
|
+
hidden: false,
|
|
4115
|
+
positions: []
|
|
4116
|
+
};
|
|
4117
|
+
const rawPoses = [];
|
|
4118
|
+
events.push(event);
|
|
4119
|
+
eventPoseNodes.set(id, rawPoses);
|
|
4120
|
+
eventIds.add(id);
|
|
4121
|
+
return {
|
|
4122
|
+
kind: "event",
|
|
4123
|
+
event,
|
|
4124
|
+
sourceSchemaVersion,
|
|
4125
|
+
diagnostics,
|
|
4126
|
+
seenFields: /* @__PURE__ */ new Set(),
|
|
4127
|
+
rawPoses,
|
|
4128
|
+
inPositions: false,
|
|
4129
|
+
positionsIndent: null,
|
|
4130
|
+
activePose: null,
|
|
4131
|
+
poseIndent: null,
|
|
4132
|
+
activePoseSeenFields: /* @__PURE__ */ new Set()
|
|
4133
|
+
};
|
|
4134
|
+
}
|
|
4135
|
+
function startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes) {
|
|
3017
4136
|
if (tokens.length < 3) {
|
|
3018
4137
|
throw new WorldOrbitError("Invalid atlas object declaration", line, tokens[0]?.column ?? 1);
|
|
3019
4138
|
}
|
|
@@ -3024,12 +4143,11 @@ var WorldOrbit = (() => {
|
|
|
3024
4143
|
throw new WorldOrbitError(`Unknown object type "${objectTypeToken.value}"`, line, objectTypeToken.column);
|
|
3025
4144
|
}
|
|
3026
4145
|
const objectNode = {
|
|
3027
|
-
type: "object",
|
|
3028
4146
|
objectType,
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
blockFields: [],
|
|
4147
|
+
id: idToken.value,
|
|
4148
|
+
fields: parseInlineObjectFields(tokens.slice(3), line, objectType, sourceSchemaVersion, diagnostics),
|
|
3032
4149
|
infoEntries: [],
|
|
4150
|
+
typedBlockEntries: {},
|
|
3033
4151
|
location: {
|
|
3034
4152
|
line,
|
|
3035
4153
|
column: objectTypeToken.column
|
|
@@ -3039,8 +4157,12 @@ var WorldOrbit = (() => {
|
|
|
3039
4157
|
return {
|
|
3040
4158
|
kind: "object",
|
|
3041
4159
|
objectNode,
|
|
3042
|
-
|
|
3043
|
-
|
|
4160
|
+
sourceSchemaVersion,
|
|
4161
|
+
diagnostics,
|
|
4162
|
+
activeBlock: null,
|
|
4163
|
+
blockIndent: null,
|
|
4164
|
+
seenInfoKeys: /* @__PURE__ */ new Set(),
|
|
4165
|
+
seenTypedBlockKeys: {}
|
|
3044
4166
|
};
|
|
3045
4167
|
}
|
|
3046
4168
|
function handleSectionLine(section, indent, tokens, line) {
|
|
@@ -3060,6 +4182,15 @@ var WorldOrbit = (() => {
|
|
|
3060
4182
|
case "annotation":
|
|
3061
4183
|
applyAnnotationField(section, tokens, line);
|
|
3062
4184
|
return;
|
|
4185
|
+
case "group":
|
|
4186
|
+
applyGroupField(section, tokens, line);
|
|
4187
|
+
return;
|
|
4188
|
+
case "relation":
|
|
4189
|
+
applyRelationField(section, tokens, line);
|
|
4190
|
+
return;
|
|
4191
|
+
case "event":
|
|
4192
|
+
applyEventField(section, indent, tokens, line);
|
|
4193
|
+
return;
|
|
3063
4194
|
case "object":
|
|
3064
4195
|
applyObjectField(section, indent, tokens, line);
|
|
3065
4196
|
return;
|
|
@@ -3067,10 +4198,35 @@ var WorldOrbit = (() => {
|
|
|
3067
4198
|
}
|
|
3068
4199
|
function applySystemField(section, tokens, line) {
|
|
3069
4200
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
3070
|
-
|
|
3071
|
-
|
|
4201
|
+
const value = joinFieldValue(tokens, line);
|
|
4202
|
+
switch (key) {
|
|
4203
|
+
case "title":
|
|
4204
|
+
section.system.title = value;
|
|
4205
|
+
return;
|
|
4206
|
+
case "description":
|
|
4207
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
4208
|
+
line,
|
|
4209
|
+
column: tokens[0].column
|
|
4210
|
+
});
|
|
4211
|
+
section.system.description = value;
|
|
4212
|
+
return;
|
|
4213
|
+
case "epoch":
|
|
4214
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, key, {
|
|
4215
|
+
line,
|
|
4216
|
+
column: tokens[0].column
|
|
4217
|
+
});
|
|
4218
|
+
section.system.epoch = value;
|
|
4219
|
+
return;
|
|
4220
|
+
case "referenceplane":
|
|
4221
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, "referencePlane", {
|
|
4222
|
+
line,
|
|
4223
|
+
column: tokens[0].column
|
|
4224
|
+
});
|
|
4225
|
+
section.system.referencePlane = value;
|
|
4226
|
+
return;
|
|
4227
|
+
default:
|
|
4228
|
+
throw new WorldOrbitError(`Unknown system atlas field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3072
4229
|
}
|
|
3073
|
-
section.system.title = joinFieldValue(tokens, line);
|
|
3074
4230
|
}
|
|
3075
4231
|
function applyDefaultsField(section, tokens, line) {
|
|
3076
4232
|
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
@@ -3101,14 +4257,11 @@ var WorldOrbit = (() => {
|
|
|
3101
4257
|
section.metadataIndent = null;
|
|
3102
4258
|
}
|
|
3103
4259
|
if (section.inMetadata) {
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
const key = tokens[0].value;
|
|
3108
|
-
if (key in section.system.atlasMetadata) {
|
|
3109
|
-
throw new WorldOrbitError(`Duplicate atlas metadata key "${key}"`, line, tokens[0].column);
|
|
4260
|
+
const entry = parseInfoLikeEntry(tokens, line, "Invalid atlas metadata entry");
|
|
4261
|
+
if (entry.key in section.system.atlasMetadata) {
|
|
4262
|
+
throw new WorldOrbitError(`Duplicate atlas metadata key "${entry.key}"`, line, tokens[0].column);
|
|
3110
4263
|
}
|
|
3111
|
-
section.system.atlasMetadata[key] =
|
|
4264
|
+
section.system.atlasMetadata[entry.key] = entry.value;
|
|
3112
4265
|
return;
|
|
3113
4266
|
}
|
|
3114
4267
|
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "metadata") {
|
|
@@ -3164,7 +4317,14 @@ var WorldOrbit = (() => {
|
|
|
3164
4317
|
section.viewpoint.rotationDeg = parseFiniteNumber2(value, line, tokens[0].column, "rotation");
|
|
3165
4318
|
return;
|
|
3166
4319
|
case "layers":
|
|
3167
|
-
section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line);
|
|
4320
|
+
section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line, section.sourceSchemaVersion, section.diagnostics);
|
|
4321
|
+
return;
|
|
4322
|
+
case "events":
|
|
4323
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.events", {
|
|
4324
|
+
line,
|
|
4325
|
+
column: tokens[0].column
|
|
4326
|
+
});
|
|
4327
|
+
section.viewpoint.events = parseTokenList(tokens.slice(1), line, "events");
|
|
3168
4328
|
return;
|
|
3169
4329
|
default:
|
|
3170
4330
|
throw new WorldOrbitError(`Unknown viewpoint field "${tokens[0].value}"`, line, tokens[0].column);
|
|
@@ -3210,21 +4370,202 @@ var WorldOrbit = (() => {
|
|
|
3210
4370
|
throw new WorldOrbitError(`Unknown annotation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3211
4371
|
}
|
|
3212
4372
|
}
|
|
4373
|
+
function applyGroupField(section, tokens, line) {
|
|
4374
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
4375
|
+
switch (key) {
|
|
4376
|
+
case "label":
|
|
4377
|
+
section.group.label = joinFieldValue(tokens, line);
|
|
4378
|
+
return;
|
|
4379
|
+
case "summary":
|
|
4380
|
+
section.group.summary = joinFieldValue(tokens, line);
|
|
4381
|
+
return;
|
|
4382
|
+
case "color":
|
|
4383
|
+
section.group.color = joinFieldValue(tokens, line);
|
|
4384
|
+
return;
|
|
4385
|
+
case "tags":
|
|
4386
|
+
section.group.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4387
|
+
return;
|
|
4388
|
+
case "hidden":
|
|
4389
|
+
section.group.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4390
|
+
line,
|
|
4391
|
+
column: tokens[0].column
|
|
4392
|
+
});
|
|
4393
|
+
return;
|
|
4394
|
+
default:
|
|
4395
|
+
throw new WorldOrbitError(`Unknown group field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
function applyRelationField(section, tokens, line) {
|
|
4399
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
4400
|
+
switch (key) {
|
|
4401
|
+
case "from":
|
|
4402
|
+
section.relation.from = joinFieldValue(tokens, line);
|
|
4403
|
+
return;
|
|
4404
|
+
case "to":
|
|
4405
|
+
section.relation.to = joinFieldValue(tokens, line);
|
|
4406
|
+
return;
|
|
4407
|
+
case "kind":
|
|
4408
|
+
section.relation.kind = joinFieldValue(tokens, line);
|
|
4409
|
+
return;
|
|
4410
|
+
case "label":
|
|
4411
|
+
section.relation.label = joinFieldValue(tokens, line);
|
|
4412
|
+
return;
|
|
4413
|
+
case "summary":
|
|
4414
|
+
section.relation.summary = joinFieldValue(tokens, line);
|
|
4415
|
+
return;
|
|
4416
|
+
case "tags":
|
|
4417
|
+
section.relation.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4418
|
+
return;
|
|
4419
|
+
case "color":
|
|
4420
|
+
section.relation.color = joinFieldValue(tokens, line);
|
|
4421
|
+
return;
|
|
4422
|
+
case "hidden":
|
|
4423
|
+
section.relation.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4424
|
+
line,
|
|
4425
|
+
column: tokens[0].column
|
|
4426
|
+
});
|
|
4427
|
+
return;
|
|
4428
|
+
default:
|
|
4429
|
+
throw new WorldOrbitError(`Unknown relation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
function applyEventField(section, indent, tokens, line) {
|
|
4433
|
+
if (section.activePose && indent <= (section.poseIndent ?? 0)) {
|
|
4434
|
+
section.activePose = null;
|
|
4435
|
+
section.poseIndent = null;
|
|
4436
|
+
section.activePoseSeenFields.clear();
|
|
4437
|
+
}
|
|
4438
|
+
if (!section.activePose && section.inPositions && indent <= (section.positionsIndent ?? 0)) {
|
|
4439
|
+
section.inPositions = false;
|
|
4440
|
+
section.positionsIndent = null;
|
|
4441
|
+
}
|
|
4442
|
+
if (section.activePose) {
|
|
4443
|
+
section.activePose.fields.push(parseEventPoseField(tokens, line, section.activePoseSeenFields));
|
|
4444
|
+
return;
|
|
4445
|
+
}
|
|
4446
|
+
if (section.inPositions) {
|
|
4447
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "pose") {
|
|
4448
|
+
throw new WorldOrbitError(`Unknown event positions field "${tokens[0].value}"`, line, tokens[0]?.column ?? 1);
|
|
4449
|
+
}
|
|
4450
|
+
const objectId = tokens[1].value;
|
|
4451
|
+
if (!objectId.trim()) {
|
|
4452
|
+
throw new WorldOrbitError("Event pose object id must not be empty", line, tokens[1].column);
|
|
4453
|
+
}
|
|
4454
|
+
const rawPose = {
|
|
4455
|
+
objectId,
|
|
4456
|
+
fields: [],
|
|
4457
|
+
location: { line, column: tokens[0].column }
|
|
4458
|
+
};
|
|
4459
|
+
section.rawPoses.push(rawPose);
|
|
4460
|
+
section.activePose = rawPose;
|
|
4461
|
+
section.poseIndent = indent;
|
|
4462
|
+
section.activePoseSeenFields = /* @__PURE__ */ new Set();
|
|
4463
|
+
return;
|
|
4464
|
+
}
|
|
4465
|
+
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "positions") {
|
|
4466
|
+
if (section.seenFields.has("positions")) {
|
|
4467
|
+
throw new WorldOrbitError('Duplicate event field "positions"', line, tokens[0].column);
|
|
4468
|
+
}
|
|
4469
|
+
section.seenFields.add("positions");
|
|
4470
|
+
section.inPositions = true;
|
|
4471
|
+
section.positionsIndent = indent;
|
|
4472
|
+
return;
|
|
4473
|
+
}
|
|
4474
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
4475
|
+
switch (key) {
|
|
4476
|
+
case "kind":
|
|
4477
|
+
section.event.kind = joinFieldValue(tokens, line);
|
|
4478
|
+
return;
|
|
4479
|
+
case "label":
|
|
4480
|
+
section.event.label = joinFieldValue(tokens, line);
|
|
4481
|
+
return;
|
|
4482
|
+
case "summary":
|
|
4483
|
+
section.event.summary = joinFieldValue(tokens, line);
|
|
4484
|
+
return;
|
|
4485
|
+
case "target":
|
|
4486
|
+
section.event.targetObjectId = joinFieldValue(tokens, line);
|
|
4487
|
+
return;
|
|
4488
|
+
case "participants":
|
|
4489
|
+
section.event.participantObjectIds = parseTokenList(tokens.slice(1), line, "participants");
|
|
4490
|
+
return;
|
|
4491
|
+
case "timing":
|
|
4492
|
+
section.event.timing = joinFieldValue(tokens, line);
|
|
4493
|
+
return;
|
|
4494
|
+
case "visibility":
|
|
4495
|
+
section.event.visibility = joinFieldValue(tokens, line);
|
|
4496
|
+
return;
|
|
4497
|
+
case "tags":
|
|
4498
|
+
section.event.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4499
|
+
return;
|
|
4500
|
+
case "color":
|
|
4501
|
+
section.event.color = joinFieldValue(tokens, line);
|
|
4502
|
+
return;
|
|
4503
|
+
case "hidden":
|
|
4504
|
+
section.event.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
4505
|
+
line,
|
|
4506
|
+
column: tokens[0].column
|
|
4507
|
+
});
|
|
4508
|
+
return;
|
|
4509
|
+
default:
|
|
4510
|
+
throw new WorldOrbitError(`Unknown event field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4511
|
+
}
|
|
4512
|
+
}
|
|
4513
|
+
function parseEventPoseField(tokens, line, seenFields) {
|
|
4514
|
+
if (tokens.length < 2) {
|
|
4515
|
+
throw new WorldOrbitError("Invalid event pose field line", line, tokens[0]?.column ?? 1);
|
|
4516
|
+
}
|
|
4517
|
+
const key = tokens[0].value;
|
|
4518
|
+
if (!EVENT_POSE_FIELD_KEYS.has(key)) {
|
|
4519
|
+
throw new WorldOrbitError(`Unknown event pose field "${key}"`, line, tokens[0].column);
|
|
4520
|
+
}
|
|
4521
|
+
if (seenFields.has(key)) {
|
|
4522
|
+
throw new WorldOrbitError(`Duplicate event pose field "${key}"`, line, tokens[0].column);
|
|
4523
|
+
}
|
|
4524
|
+
seenFields.add(key);
|
|
4525
|
+
return {
|
|
4526
|
+
type: "field",
|
|
4527
|
+
key,
|
|
4528
|
+
values: tokens.slice(1).map((token) => token.value),
|
|
4529
|
+
location: { line, column: tokens[0].column }
|
|
4530
|
+
};
|
|
4531
|
+
}
|
|
3213
4532
|
function applyObjectField(section, indent, tokens, line) {
|
|
3214
|
-
if (
|
|
3215
|
-
section.
|
|
3216
|
-
section.
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
4533
|
+
if (section.activeBlock && indent <= (section.blockIndent ?? 0)) {
|
|
4534
|
+
section.activeBlock = null;
|
|
4535
|
+
section.blockIndent = null;
|
|
4536
|
+
}
|
|
4537
|
+
if (tokens.length === 1) {
|
|
4538
|
+
const blockName = tokens[0].value.toLowerCase();
|
|
4539
|
+
if (blockName === "info" || STRUCTURED_TYPED_BLOCKS.has(blockName)) {
|
|
4540
|
+
if (blockName !== "info") {
|
|
4541
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, blockName, { line, column: tokens[0].column });
|
|
4542
|
+
}
|
|
4543
|
+
section.activeBlock = blockName;
|
|
4544
|
+
section.blockIndent = indent;
|
|
4545
|
+
return;
|
|
4546
|
+
}
|
|
3222
4547
|
}
|
|
3223
|
-
if (section.
|
|
3224
|
-
|
|
4548
|
+
if (section.activeBlock) {
|
|
4549
|
+
const entry = parseInfoLikeEntry(tokens, line, `Invalid ${section.activeBlock} entry`);
|
|
4550
|
+
if (section.activeBlock === "info") {
|
|
4551
|
+
if (section.seenInfoKeys.has(entry.key)) {
|
|
4552
|
+
throw new WorldOrbitError(`Duplicate info key "${entry.key}"`, line, tokens[0].column);
|
|
4553
|
+
}
|
|
4554
|
+
section.seenInfoKeys.add(entry.key);
|
|
4555
|
+
section.objectNode.infoEntries.push(entry);
|
|
4556
|
+
return;
|
|
4557
|
+
}
|
|
4558
|
+
const typedBlock = section.activeBlock;
|
|
4559
|
+
const seenKeys = section.seenTypedBlockKeys[typedBlock] ?? (section.seenTypedBlockKeys[typedBlock] = /* @__PURE__ */ new Set());
|
|
4560
|
+
if (seenKeys.has(entry.key)) {
|
|
4561
|
+
throw new WorldOrbitError(`Duplicate ${typedBlock} key "${entry.key}"`, line, tokens[0].column);
|
|
4562
|
+
}
|
|
4563
|
+
seenKeys.add(entry.key);
|
|
4564
|
+
const entries = section.objectNode.typedBlockEntries[typedBlock] ?? (section.objectNode.typedBlockEntries[typedBlock] = []);
|
|
4565
|
+
entries.push(entry);
|
|
3225
4566
|
return;
|
|
3226
4567
|
}
|
|
3227
|
-
section.objectNode.
|
|
4568
|
+
section.objectNode.fields.push(parseObjectField(tokens, line, section.objectNode.objectType, section.sourceSchemaVersion, section.diagnostics));
|
|
3228
4569
|
}
|
|
3229
4570
|
function requireUniqueField(tokens, seenFields, line) {
|
|
3230
4571
|
if (tokens.length < 2) {
|
|
@@ -3244,50 +4585,46 @@ var WorldOrbit = (() => {
|
|
|
3244
4585
|
return tokens.slice(1).map((token) => token.value).join(" ").trim();
|
|
3245
4586
|
}
|
|
3246
4587
|
function parseObjectTypeTokens(tokens, line) {
|
|
3247
|
-
|
|
3248
|
-
throw new WorldOrbitError("Missing value for atlas field", line);
|
|
3249
|
-
}
|
|
3250
|
-
return tokens.map((token) => {
|
|
3251
|
-
const value = token.value;
|
|
3252
|
-
if (value !== "star" && value !== "planet" && value !== "moon" && value !== "belt" && value !== "asteroid" && value !== "comet" && value !== "ring" && value !== "structure" && value !== "phenomenon") {
|
|
3253
|
-
throw new WorldOrbitError(`Unknown viewpoint object type "${token.value}"`, line, token.column);
|
|
3254
|
-
}
|
|
3255
|
-
return value;
|
|
3256
|
-
});
|
|
4588
|
+
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");
|
|
3257
4589
|
}
|
|
3258
|
-
function
|
|
3259
|
-
|
|
3260
|
-
|
|
4590
|
+
function parseLayerTokens(tokens, line, sourceSchemaVersion, diagnostics) {
|
|
4591
|
+
const layers = {};
|
|
4592
|
+
for (const token of parseTokenList(tokens, line, "layers")) {
|
|
4593
|
+
const enabled = !token.startsWith("-") && !token.startsWith("!");
|
|
4594
|
+
const raw = token.replace(/^[-!]+/, "").toLowerCase();
|
|
4595
|
+
if (raw === "orbits") {
|
|
4596
|
+
layers["orbits-back"] = enabled;
|
|
4597
|
+
layers["orbits-front"] = enabled;
|
|
4598
|
+
continue;
|
|
4599
|
+
}
|
|
4600
|
+
if (raw === "background" || raw === "guides" || raw === "orbits-back" || raw === "orbits-front" || raw === "relations" || raw === "events" || raw === "objects" || raw === "labels" || raw === "metadata") {
|
|
4601
|
+
if (raw === "events" && sourceSchemaVersion && diagnostics) {
|
|
4602
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "layers.events", {
|
|
4603
|
+
line,
|
|
4604
|
+
column: tokens[0]?.column ?? 1
|
|
4605
|
+
});
|
|
4606
|
+
}
|
|
4607
|
+
layers[raw] = enabled;
|
|
4608
|
+
}
|
|
3261
4609
|
}
|
|
3262
|
-
return
|
|
4610
|
+
return layers;
|
|
3263
4611
|
}
|
|
3264
|
-
function
|
|
4612
|
+
function parseTokenList(tokens, line, fieldName) {
|
|
3265
4613
|
if (tokens.length === 0) {
|
|
3266
|
-
throw new WorldOrbitError(
|
|
4614
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, 1);
|
|
3267
4615
|
}
|
|
3268
|
-
const
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
const rawLayer = token.value.replace(/^[-!]+/, "").toLowerCase();
|
|
3272
|
-
if (rawLayer === "orbits") {
|
|
3273
|
-
next["orbits-back"] = enabled;
|
|
3274
|
-
next["orbits-front"] = enabled;
|
|
3275
|
-
continue;
|
|
3276
|
-
}
|
|
3277
|
-
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
3278
|
-
next[rawLayer] = enabled;
|
|
3279
|
-
continue;
|
|
3280
|
-
}
|
|
3281
|
-
throw new WorldOrbitError(`Unknown layer token "${token.value}"`, line, token.column);
|
|
4616
|
+
const values = tokens.map((token) => token.value).filter(Boolean);
|
|
4617
|
+
if (values.length === 0) {
|
|
4618
|
+
throw new WorldOrbitError(`Missing value for atlas field "${fieldName}"`, line, tokens[0]?.column ?? 1);
|
|
3282
4619
|
}
|
|
3283
|
-
return
|
|
4620
|
+
return values;
|
|
3284
4621
|
}
|
|
3285
4622
|
function parseProjectionValue(value, line, column) {
|
|
3286
4623
|
const normalized = value.toLowerCase();
|
|
3287
|
-
if (normalized
|
|
3288
|
-
|
|
4624
|
+
if (normalized !== "topdown" && normalized !== "isometric") {
|
|
4625
|
+
throw new WorldOrbitError(`Unknown projection "${value}"`, line, column);
|
|
3289
4626
|
}
|
|
3290
|
-
|
|
4627
|
+
return normalized;
|
|
3291
4628
|
}
|
|
3292
4629
|
function parsePresetValue(value, line, column) {
|
|
3293
4630
|
const normalized = value.toLowerCase();
|
|
@@ -3297,16 +4634,16 @@ var WorldOrbit = (() => {
|
|
|
3297
4634
|
throw new WorldOrbitError(`Unknown render preset "${value}"`, line, column);
|
|
3298
4635
|
}
|
|
3299
4636
|
function parsePositiveNumber2(value, line, column, field) {
|
|
3300
|
-
const parsed =
|
|
3301
|
-
if (
|
|
3302
|
-
throw new WorldOrbitError(`Field "${field}"
|
|
4637
|
+
const parsed = parseFiniteNumber2(value, line, column, field);
|
|
4638
|
+
if (parsed <= 0) {
|
|
4639
|
+
throw new WorldOrbitError(`Field "${field}" must be greater than zero`, line, column);
|
|
3303
4640
|
}
|
|
3304
4641
|
return parsed;
|
|
3305
4642
|
}
|
|
3306
4643
|
function parseFiniteNumber2(value, line, column, field) {
|
|
3307
4644
|
const parsed = Number(value);
|
|
3308
4645
|
if (!Number.isFinite(parsed)) {
|
|
3309
|
-
throw new WorldOrbitError(`
|
|
4646
|
+
throw new WorldOrbitError(`Invalid numeric value "${value}" for "${field}"`, line, column);
|
|
3310
4647
|
}
|
|
3311
4648
|
return parsed;
|
|
3312
4649
|
}
|
|
@@ -3318,28 +4655,43 @@ var WorldOrbit = (() => {
|
|
|
3318
4655
|
groupIds: []
|
|
3319
4656
|
};
|
|
3320
4657
|
}
|
|
3321
|
-
function
|
|
4658
|
+
function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
3322
4659
|
const fields = [];
|
|
3323
4660
|
let index = 0;
|
|
3324
4661
|
while (index < tokens.length) {
|
|
3325
4662
|
const keyToken = tokens[index];
|
|
3326
|
-
const
|
|
3327
|
-
if (!
|
|
4663
|
+
const spec = getDraftObjectFieldSpec(keyToken.value);
|
|
4664
|
+
if (!spec) {
|
|
3328
4665
|
throw new WorldOrbitError(`Unknown field "${keyToken.value}"`, line, keyToken.column);
|
|
3329
4666
|
}
|
|
4667
|
+
if (spec.version === "2.1") {
|
|
4668
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, keyToken.value, {
|
|
4669
|
+
line,
|
|
4670
|
+
column: keyToken.column
|
|
4671
|
+
});
|
|
4672
|
+
}
|
|
3330
4673
|
index++;
|
|
3331
4674
|
const valueTokens = [];
|
|
3332
|
-
if (
|
|
3333
|
-
while (index < tokens.length && !isKnownFieldKey(tokens[index].value)) {
|
|
3334
|
-
valueTokens.push(tokens[index]);
|
|
3335
|
-
index++;
|
|
3336
|
-
}
|
|
3337
|
-
} else {
|
|
4675
|
+
if (spec.inlineMode === "single") {
|
|
3338
4676
|
const nextToken = tokens[index];
|
|
3339
4677
|
if (nextToken) {
|
|
3340
4678
|
valueTokens.push(nextToken);
|
|
3341
4679
|
index++;
|
|
3342
4680
|
}
|
|
4681
|
+
} else if (spec.inlineMode === "pair") {
|
|
4682
|
+
for (let count = 0; count < 2; count++) {
|
|
4683
|
+
const nextToken = tokens[index];
|
|
4684
|
+
if (!nextToken) {
|
|
4685
|
+
break;
|
|
4686
|
+
}
|
|
4687
|
+
valueTokens.push(nextToken);
|
|
4688
|
+
index++;
|
|
4689
|
+
}
|
|
4690
|
+
} else {
|
|
4691
|
+
while (index < tokens.length && !DRAFT_OBJECT_FIELD_KEYS.has(tokens[index].value)) {
|
|
4692
|
+
valueTokens.push(tokens[index]);
|
|
4693
|
+
index++;
|
|
4694
|
+
}
|
|
3343
4695
|
}
|
|
3344
4696
|
if (valueTokens.length === 0) {
|
|
3345
4697
|
throw new WorldOrbitError(`Missing value for field "${keyToken.value}"`, line, keyToken.column);
|
|
@@ -3351,25 +4703,35 @@ var WorldOrbit = (() => {
|
|
|
3351
4703
|
location: { line, column: keyToken.column }
|
|
3352
4704
|
});
|
|
3353
4705
|
}
|
|
4706
|
+
validateDraftObjectFieldCompatibility(fields, objectType);
|
|
3354
4707
|
return fields;
|
|
3355
4708
|
}
|
|
3356
|
-
function
|
|
4709
|
+
function parseObjectField(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
3357
4710
|
if (tokens.length < 2) {
|
|
3358
4711
|
throw new WorldOrbitError("Invalid field line", line, tokens[0]?.column ?? 1);
|
|
3359
4712
|
}
|
|
3360
|
-
|
|
4713
|
+
const spec = getDraftObjectFieldSpec(tokens[0].value);
|
|
4714
|
+
if (!spec) {
|
|
3361
4715
|
throw new WorldOrbitError(`Unknown field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3362
4716
|
}
|
|
3363
|
-
|
|
4717
|
+
if (spec.version === "2.1") {
|
|
4718
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, tokens[0].value, {
|
|
4719
|
+
line,
|
|
4720
|
+
column: tokens[0].column
|
|
4721
|
+
});
|
|
4722
|
+
}
|
|
4723
|
+
const field = {
|
|
3364
4724
|
type: "field",
|
|
3365
4725
|
key: tokens[0].value,
|
|
3366
4726
|
values: tokens.slice(1).map((token) => token.value),
|
|
3367
4727
|
location: { line, column: tokens[0].column }
|
|
3368
4728
|
};
|
|
4729
|
+
validateDraftObjectFieldCompatibility([field], objectType);
|
|
4730
|
+
return field;
|
|
3369
4731
|
}
|
|
3370
|
-
function
|
|
4732
|
+
function parseInfoLikeEntry(tokens, line, errorMessage) {
|
|
3371
4733
|
if (tokens.length < 2) {
|
|
3372
|
-
throw new WorldOrbitError(
|
|
4734
|
+
throw new WorldOrbitError(errorMessage, line, tokens[0]?.column ?? 1);
|
|
3373
4735
|
}
|
|
3374
4736
|
return {
|
|
3375
4737
|
type: "info-entry",
|
|
@@ -3378,18 +4740,366 @@ var WorldOrbit = (() => {
|
|
|
3378
4740
|
location: { line, column: tokens[0].column }
|
|
3379
4741
|
};
|
|
3380
4742
|
}
|
|
3381
|
-
function
|
|
3382
|
-
|
|
4743
|
+
function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
|
|
4744
|
+
const fieldMap = collectDraftFields(node.fields);
|
|
4745
|
+
const placement = extractPlacementFromFieldMap(fieldMap);
|
|
4746
|
+
const properties = normalizeDraftProperties(node.objectType, fieldMap);
|
|
4747
|
+
const groups = parseOptionalTokenList(fieldMap.get("groups")?.[0]);
|
|
4748
|
+
const epoch = parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]);
|
|
4749
|
+
const referencePlane = parseOptionalJoinedValue(fieldMap.get("referencePlane")?.[0]);
|
|
4750
|
+
const tidalLock = fieldMap.has("tidalLock") ? parseAtlasBoolean(singleFieldValue2(fieldMap.get("tidalLock")[0]), "tidalLock", fieldMap.get("tidalLock")[0].location) : void 0;
|
|
4751
|
+
const resonance = fieldMap.has("resonance") ? parseResonanceField(fieldMap.get("resonance")[0]) : void 0;
|
|
4752
|
+
const renderHints = extractRenderHints(fieldMap);
|
|
4753
|
+
const deriveRules = fieldMap.get("derive")?.map((field) => parseDeriveField(field));
|
|
4754
|
+
const validationRules = fieldMap.get("validate")?.map((field) => ({
|
|
4755
|
+
rule: singleFieldValue2(field)
|
|
4756
|
+
}));
|
|
4757
|
+
const lockedFields = fieldMap.has("locked") ? [...new Set(fieldMap.get("locked").flatMap((field) => field.values))] : void 0;
|
|
4758
|
+
const tolerances = fieldMap.get("tolerance")?.map((field) => parseToleranceField(field));
|
|
4759
|
+
const typedBlocks = normalizeTypedBlocks(node.typedBlockEntries);
|
|
4760
|
+
const info2 = normalizeInfoEntries(node.infoEntries, "info");
|
|
4761
|
+
const object = {
|
|
4762
|
+
type: node.objectType,
|
|
4763
|
+
id: node.id,
|
|
4764
|
+
properties,
|
|
4765
|
+
placement,
|
|
4766
|
+
info: info2
|
|
4767
|
+
};
|
|
4768
|
+
if (groups.length > 0)
|
|
4769
|
+
object.groups = groups;
|
|
4770
|
+
if (epoch)
|
|
4771
|
+
object.epoch = epoch;
|
|
4772
|
+
if (referencePlane)
|
|
4773
|
+
object.referencePlane = referencePlane;
|
|
4774
|
+
if (tidalLock !== void 0)
|
|
4775
|
+
object.tidalLock = tidalLock;
|
|
4776
|
+
if (resonance)
|
|
4777
|
+
object.resonance = resonance;
|
|
4778
|
+
if (renderHints)
|
|
4779
|
+
object.renderHints = renderHints;
|
|
4780
|
+
if (deriveRules?.length)
|
|
4781
|
+
object.deriveRules = deriveRules;
|
|
4782
|
+
if (validationRules?.length)
|
|
4783
|
+
object.validationRules = validationRules;
|
|
4784
|
+
if (lockedFields?.length)
|
|
4785
|
+
object.lockedFields = lockedFields;
|
|
4786
|
+
if (tolerances?.length)
|
|
4787
|
+
object.tolerances = tolerances;
|
|
4788
|
+
if (typedBlocks && Object.keys(typedBlocks).length > 0)
|
|
4789
|
+
object.typedBlocks = typedBlocks;
|
|
4790
|
+
if (sourceSchemaVersion !== "2.1") {
|
|
4791
|
+
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) {
|
|
4792
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, node.id, node.location);
|
|
4793
|
+
}
|
|
4794
|
+
}
|
|
4795
|
+
return object;
|
|
4796
|
+
}
|
|
4797
|
+
function normalizeDraftEvent(event, rawPoses) {
|
|
4798
|
+
return {
|
|
4799
|
+
...event,
|
|
4800
|
+
participantObjectIds: [...new Set(event.participantObjectIds)],
|
|
4801
|
+
tags: [...new Set(event.tags)],
|
|
4802
|
+
positions: rawPoses.map((pose) => normalizeDraftEventPose(pose))
|
|
4803
|
+
};
|
|
3383
4804
|
}
|
|
3384
|
-
function
|
|
3385
|
-
|
|
4805
|
+
function normalizeDraftEventPose(rawPose) {
|
|
4806
|
+
const fieldMap = collectDraftFields(rawPose.fields);
|
|
4807
|
+
const placement = extractPlacementFromFieldMap(fieldMap);
|
|
4808
|
+
return {
|
|
4809
|
+
objectId: rawPose.objectId,
|
|
4810
|
+
placement,
|
|
4811
|
+
inner: parseOptionalUnitField(fieldMap.get("inner")?.[0], "inner"),
|
|
4812
|
+
outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer")
|
|
4813
|
+
};
|
|
4814
|
+
}
|
|
4815
|
+
function collectDraftFields(fields) {
|
|
4816
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4817
|
+
for (const field of fields) {
|
|
4818
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
4819
|
+
if (!spec) {
|
|
4820
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4821
|
+
}
|
|
4822
|
+
if (!spec.allowRepeat && grouped.has(field.key)) {
|
|
4823
|
+
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
4824
|
+
}
|
|
4825
|
+
const existing = grouped.get(field.key) ?? [];
|
|
4826
|
+
existing.push(field);
|
|
4827
|
+
grouped.set(field.key, existing);
|
|
4828
|
+
}
|
|
4829
|
+
return grouped;
|
|
4830
|
+
}
|
|
4831
|
+
function extractPlacementFromFieldMap(fieldMap) {
|
|
4832
|
+
const orbitField = fieldMap.get("orbit")?.[0];
|
|
4833
|
+
const atField = fieldMap.get("at")?.[0];
|
|
4834
|
+
const surfaceField = fieldMap.get("surface")?.[0];
|
|
4835
|
+
const freeField = fieldMap.get("free")?.[0];
|
|
4836
|
+
const count = [orbitField, atField, surfaceField, freeField].filter(Boolean).length;
|
|
4837
|
+
if (count > 1) {
|
|
4838
|
+
const conflictingField = orbitField ?? atField ?? surfaceField ?? freeField;
|
|
4839
|
+
throw WorldOrbitError.fromLocation("Object has multiple placement modes", conflictingField?.location);
|
|
4840
|
+
}
|
|
4841
|
+
if (orbitField) {
|
|
4842
|
+
return {
|
|
4843
|
+
mode: "orbit",
|
|
4844
|
+
target: singleFieldValue2(orbitField),
|
|
4845
|
+
distance: parseOptionalUnitField(fieldMap.get("distance")?.[0], "distance"),
|
|
4846
|
+
semiMajor: parseOptionalUnitField(fieldMap.get("semiMajor")?.[0], "semiMajor"),
|
|
4847
|
+
eccentricity: parseOptionalNumberField(fieldMap.get("eccentricity")?.[0], "eccentricity"),
|
|
4848
|
+
period: parseOptionalUnitField(fieldMap.get("period")?.[0], "period"),
|
|
4849
|
+
angle: parseOptionalUnitField(fieldMap.get("angle")?.[0], "angle"),
|
|
4850
|
+
inclination: parseOptionalUnitField(fieldMap.get("inclination")?.[0], "inclination"),
|
|
4851
|
+
phase: parseOptionalUnitField(fieldMap.get("phase")?.[0], "phase")
|
|
4852
|
+
};
|
|
4853
|
+
}
|
|
4854
|
+
if (atField) {
|
|
4855
|
+
const target = singleFieldValue2(atField);
|
|
4856
|
+
return {
|
|
4857
|
+
mode: "at",
|
|
4858
|
+
target,
|
|
4859
|
+
reference: parseAtlasAtReference(target, atField.location)
|
|
4860
|
+
};
|
|
4861
|
+
}
|
|
4862
|
+
if (surfaceField) {
|
|
4863
|
+
return {
|
|
4864
|
+
mode: "surface",
|
|
4865
|
+
target: singleFieldValue2(surfaceField)
|
|
4866
|
+
};
|
|
4867
|
+
}
|
|
4868
|
+
if (freeField) {
|
|
4869
|
+
const raw = singleFieldValue2(freeField);
|
|
4870
|
+
const distance = tryParseAtlasUnitValue(raw);
|
|
4871
|
+
return {
|
|
4872
|
+
mode: "free",
|
|
4873
|
+
distance: distance ?? void 0,
|
|
4874
|
+
descriptor: distance ? void 0 : raw
|
|
4875
|
+
};
|
|
4876
|
+
}
|
|
4877
|
+
return null;
|
|
4878
|
+
}
|
|
4879
|
+
function normalizeDraftProperties(objectType, fieldMap) {
|
|
4880
|
+
const properties = {};
|
|
4881
|
+
for (const [key, fields] of fieldMap.entries()) {
|
|
4882
|
+
const field = fields[0];
|
|
4883
|
+
const spec = getDraftObjectFieldSpec(key);
|
|
4884
|
+
if (!field || !spec?.legacySchema || spec.legacySchema.placement) {
|
|
4885
|
+
continue;
|
|
4886
|
+
}
|
|
4887
|
+
ensureAtlasFieldSupported(key, objectType, field.location);
|
|
4888
|
+
properties[key] = normalizeLegacyScalarValue(key, field.values, field.location);
|
|
4889
|
+
}
|
|
4890
|
+
return properties;
|
|
4891
|
+
}
|
|
4892
|
+
function normalizeInfoEntries(entries, label) {
|
|
4893
|
+
const normalized = {};
|
|
4894
|
+
for (const entry of entries) {
|
|
4895
|
+
if (entry.key in normalized) {
|
|
4896
|
+
throw WorldOrbitError.fromLocation(`Duplicate ${label} key "${entry.key}"`, entry.location);
|
|
4897
|
+
}
|
|
4898
|
+
normalized[entry.key] = entry.value;
|
|
4899
|
+
}
|
|
4900
|
+
return normalized;
|
|
4901
|
+
}
|
|
4902
|
+
function normalizeTypedBlocks(typedBlockEntries) {
|
|
4903
|
+
const typedBlocks = {};
|
|
4904
|
+
for (const blockName of Object.keys(typedBlockEntries)) {
|
|
4905
|
+
const entries = typedBlockEntries[blockName];
|
|
4906
|
+
if (entries?.length) {
|
|
4907
|
+
typedBlocks[blockName] = normalizeInfoEntries(entries, blockName);
|
|
4908
|
+
}
|
|
4909
|
+
}
|
|
4910
|
+
return typedBlocks;
|
|
4911
|
+
}
|
|
4912
|
+
function extractRenderHints(fieldMap) {
|
|
4913
|
+
const renderHints = {};
|
|
4914
|
+
const renderLabelField = fieldMap.get("renderLabel")?.[0];
|
|
4915
|
+
const renderOrbitField = fieldMap.get("renderOrbit")?.[0];
|
|
4916
|
+
const renderPriorityField = fieldMap.get("renderPriority")?.[0];
|
|
4917
|
+
if (renderLabelField) {
|
|
4918
|
+
renderHints.renderLabel = parseAtlasBoolean(singleFieldValue2(renderLabelField), "renderLabel", renderLabelField.location);
|
|
4919
|
+
}
|
|
4920
|
+
if (renderOrbitField) {
|
|
4921
|
+
renderHints.renderOrbit = parseAtlasBoolean(singleFieldValue2(renderOrbitField), "renderOrbit", renderOrbitField.location);
|
|
4922
|
+
}
|
|
4923
|
+
if (renderPriorityField) {
|
|
4924
|
+
renderHints.renderPriority = parseAtlasNumber(singleFieldValue2(renderPriorityField), "renderPriority", renderPriorityField.location);
|
|
4925
|
+
}
|
|
4926
|
+
return Object.keys(renderHints).length > 0 ? renderHints : void 0;
|
|
4927
|
+
}
|
|
4928
|
+
function parseResonanceField(field) {
|
|
4929
|
+
if (field.values.length !== 2) {
|
|
4930
|
+
throw WorldOrbitError.fromLocation('Field "resonance" expects "<targetObjectId> <ratio>"', field.location);
|
|
4931
|
+
}
|
|
4932
|
+
const ratio = field.values[1];
|
|
4933
|
+
if (!/^\d+:\d+$/.test(ratio)) {
|
|
4934
|
+
throw WorldOrbitError.fromLocation(`Invalid resonance ratio "${ratio}"`, field.location);
|
|
4935
|
+
}
|
|
4936
|
+
return {
|
|
4937
|
+
targetObjectId: field.values[0],
|
|
4938
|
+
ratio
|
|
4939
|
+
};
|
|
4940
|
+
}
|
|
4941
|
+
function parseDeriveField(field) {
|
|
4942
|
+
if (field.values.length !== 2) {
|
|
4943
|
+
throw WorldOrbitError.fromLocation('Field "derive" expects "<field> <strategy>"', field.location);
|
|
4944
|
+
}
|
|
4945
|
+
return {
|
|
4946
|
+
field: field.values[0],
|
|
4947
|
+
strategy: field.values[1]
|
|
4948
|
+
};
|
|
4949
|
+
}
|
|
4950
|
+
function parseToleranceField(field) {
|
|
4951
|
+
if (field.values.length !== 2) {
|
|
4952
|
+
throw WorldOrbitError.fromLocation('Field "tolerance" expects "<field> <value>"', field.location);
|
|
4953
|
+
}
|
|
4954
|
+
const rawValue = field.values[1];
|
|
4955
|
+
const unitValue = tryParseAtlasUnitValue(rawValue);
|
|
4956
|
+
const numericValue2 = Number(rawValue);
|
|
4957
|
+
return {
|
|
4958
|
+
field: field.values[0],
|
|
4959
|
+
value: unitValue ?? (Number.isFinite(numericValue2) ? numericValue2 : rawValue)
|
|
4960
|
+
};
|
|
4961
|
+
}
|
|
4962
|
+
function parseOptionalTokenList(field) {
|
|
4963
|
+
return field ? [...new Set(field.values)] : [];
|
|
4964
|
+
}
|
|
4965
|
+
function parseOptionalJoinedValue(field) {
|
|
4966
|
+
if (!field) {
|
|
4967
|
+
return null;
|
|
4968
|
+
}
|
|
4969
|
+
return field.values.join(" ").trim() || null;
|
|
4970
|
+
}
|
|
4971
|
+
function parseOptionalUnitField(field, key) {
|
|
4972
|
+
return field ? parseAtlasUnitValue(singleFieldValue2(field), field.location, key) : void 0;
|
|
4973
|
+
}
|
|
4974
|
+
function parseOptionalNumberField(field, key) {
|
|
4975
|
+
return field ? parseAtlasNumber(singleFieldValue2(field), key, field.location) : void 0;
|
|
4976
|
+
}
|
|
4977
|
+
function singleFieldValue2(field) {
|
|
4978
|
+
return singleAtlasValue(field.values, field.key, field.location);
|
|
4979
|
+
}
|
|
4980
|
+
function getDraftObjectFieldSpec(key) {
|
|
4981
|
+
return DRAFT_OBJECT_FIELD_SPECS.get(key);
|
|
4982
|
+
}
|
|
4983
|
+
function validateDraftObjectFieldCompatibility(fields, objectType) {
|
|
4984
|
+
for (const field of fields) {
|
|
4985
|
+
const spec = getDraftObjectFieldSpec(field.key);
|
|
4986
|
+
if (!spec) {
|
|
4987
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4988
|
+
}
|
|
4989
|
+
if (spec.legacySchema) {
|
|
4990
|
+
ensureAtlasFieldSupported(field.key, objectType, field.location);
|
|
4991
|
+
continue;
|
|
4992
|
+
}
|
|
4993
|
+
if ((field.key === "renderLabel" || field.key === "renderOrbit" || field.key === "tidalLock") && field.values.length !== 1) {
|
|
4994
|
+
throw WorldOrbitError.fromLocation(`Field "${field.key}" expects exactly one value`, field.location);
|
|
4995
|
+
}
|
|
4996
|
+
}
|
|
4997
|
+
}
|
|
4998
|
+
function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
4999
|
+
if (sourceSchemaVersion === "2.1") {
|
|
5000
|
+
return;
|
|
5001
|
+
}
|
|
5002
|
+
diagnostics.push({
|
|
5003
|
+
code: "parse.schema21.featureCompatibility",
|
|
5004
|
+
severity: "warning",
|
|
5005
|
+
source: "parse",
|
|
5006
|
+
message: `Feature "${featureName}" requires schema 2.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
5007
|
+
line: location.line,
|
|
5008
|
+
column: location.column
|
|
5009
|
+
});
|
|
5010
|
+
}
|
|
5011
|
+
function preprocessAtlasSource(source) {
|
|
5012
|
+
const chars = [...source];
|
|
5013
|
+
const comments = [];
|
|
5014
|
+
let inString = false;
|
|
5015
|
+
let inBlockComment = false;
|
|
5016
|
+
let blockCommentStart = null;
|
|
5017
|
+
let line = 1;
|
|
5018
|
+
let column = 1;
|
|
5019
|
+
for (let index = 0; index < chars.length; index++) {
|
|
5020
|
+
const ch = chars[index];
|
|
5021
|
+
const next = chars[index + 1];
|
|
5022
|
+
if (inBlockComment) {
|
|
5023
|
+
if (ch === "*" && next === "/") {
|
|
5024
|
+
chars[index] = " ";
|
|
5025
|
+
chars[index + 1] = " ";
|
|
5026
|
+
inBlockComment = false;
|
|
5027
|
+
blockCommentStart = null;
|
|
5028
|
+
index++;
|
|
5029
|
+
column += 2;
|
|
5030
|
+
continue;
|
|
5031
|
+
}
|
|
5032
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
5033
|
+
chars[index] = " ";
|
|
5034
|
+
}
|
|
5035
|
+
if (ch === "\n") {
|
|
5036
|
+
line++;
|
|
5037
|
+
column = 1;
|
|
5038
|
+
} else {
|
|
5039
|
+
column++;
|
|
5040
|
+
}
|
|
5041
|
+
continue;
|
|
5042
|
+
}
|
|
5043
|
+
if (!inString && ch === "/" && next === "*") {
|
|
5044
|
+
comments.push({ kind: "block", line, column });
|
|
5045
|
+
chars[index] = " ";
|
|
5046
|
+
chars[index + 1] = " ";
|
|
5047
|
+
inBlockComment = true;
|
|
5048
|
+
blockCommentStart = { line, column };
|
|
5049
|
+
index++;
|
|
5050
|
+
column += 2;
|
|
5051
|
+
continue;
|
|
5052
|
+
}
|
|
5053
|
+
if (!inString && ch === "#" && !isHexColorLiteral(chars, index)) {
|
|
5054
|
+
comments.push({ kind: "line", line, column });
|
|
5055
|
+
chars[index] = " ";
|
|
5056
|
+
let inner = index + 1;
|
|
5057
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
5058
|
+
chars[inner] = " ";
|
|
5059
|
+
inner++;
|
|
5060
|
+
}
|
|
5061
|
+
column += inner - index;
|
|
5062
|
+
index = inner - 1;
|
|
5063
|
+
continue;
|
|
5064
|
+
}
|
|
5065
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
5066
|
+
inString = !inString;
|
|
5067
|
+
}
|
|
5068
|
+
if (ch === "\n") {
|
|
5069
|
+
line++;
|
|
5070
|
+
column = 1;
|
|
5071
|
+
} else {
|
|
5072
|
+
column++;
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
5075
|
+
if (inBlockComment) {
|
|
5076
|
+
throw WorldOrbitError.fromLocation("Unclosed block comment", blockCommentStart ?? void 0);
|
|
5077
|
+
}
|
|
5078
|
+
return {
|
|
5079
|
+
source: chars.join(""),
|
|
5080
|
+
comments
|
|
5081
|
+
};
|
|
5082
|
+
}
|
|
5083
|
+
function isHexColorLiteral(chars, start) {
|
|
5084
|
+
let index = start + 1;
|
|
5085
|
+
let length = 0;
|
|
5086
|
+
while (index < chars.length && /[0-9a-f]/i.test(chars[index] ?? "")) {
|
|
5087
|
+
index++;
|
|
5088
|
+
length++;
|
|
5089
|
+
}
|
|
5090
|
+
if (![3, 4, 6, 8].includes(length)) {
|
|
5091
|
+
return false;
|
|
5092
|
+
}
|
|
5093
|
+
const next = chars[index];
|
|
5094
|
+
return next === void 0 || next === " " || next === " " || next === "\r" || next === "\n";
|
|
3386
5095
|
}
|
|
3387
5096
|
|
|
3388
5097
|
// packages/core/dist/load.js
|
|
3389
|
-
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0)?$/i;
|
|
5098
|
+
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;
|
|
5099
|
+
var ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
|
|
3390
5100
|
var LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
|
|
3391
5101
|
function detectWorldOrbitSchemaVersion(source) {
|
|
3392
|
-
for (const line of source.split(/\r?\n/)) {
|
|
5102
|
+
for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
|
|
3393
5103
|
const trimmed = line.trim();
|
|
3394
5104
|
if (!trimmed) {
|
|
3395
5105
|
continue;
|
|
@@ -3397,6 +5107,9 @@ var WorldOrbit = (() => {
|
|
|
3397
5107
|
if (LEGACY_DRAFT_SCHEMA_PATTERN.test(trimmed)) {
|
|
3398
5108
|
return "2.0-draft";
|
|
3399
5109
|
}
|
|
5110
|
+
if (ATLAS_SCHEMA_21_PATTERN.test(trimmed)) {
|
|
5111
|
+
return "2.1";
|
|
5112
|
+
}
|
|
3400
5113
|
if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
|
|
3401
5114
|
return "2.0";
|
|
3402
5115
|
}
|
|
@@ -3404,6 +5117,49 @@ var WorldOrbit = (() => {
|
|
|
3404
5117
|
}
|
|
3405
5118
|
return "1.0";
|
|
3406
5119
|
}
|
|
5120
|
+
function stripCommentsForSchemaDetection(source) {
|
|
5121
|
+
const chars = [...source];
|
|
5122
|
+
let inString = false;
|
|
5123
|
+
let inBlockComment = false;
|
|
5124
|
+
for (let index = 0; index < chars.length; index++) {
|
|
5125
|
+
const ch = chars[index];
|
|
5126
|
+
const next = chars[index + 1];
|
|
5127
|
+
if (inBlockComment) {
|
|
5128
|
+
if (ch === "*" && next === "/") {
|
|
5129
|
+
chars[index] = " ";
|
|
5130
|
+
chars[index + 1] = " ";
|
|
5131
|
+
inBlockComment = false;
|
|
5132
|
+
index++;
|
|
5133
|
+
continue;
|
|
5134
|
+
}
|
|
5135
|
+
if (ch !== "\n" && ch !== "\r") {
|
|
5136
|
+
chars[index] = " ";
|
|
5137
|
+
}
|
|
5138
|
+
continue;
|
|
5139
|
+
}
|
|
5140
|
+
if (!inString && ch === "/" && next === "*") {
|
|
5141
|
+
chars[index] = " ";
|
|
5142
|
+
chars[index + 1] = " ";
|
|
5143
|
+
inBlockComment = true;
|
|
5144
|
+
index++;
|
|
5145
|
+
continue;
|
|
5146
|
+
}
|
|
5147
|
+
if (!inString && ch === "#") {
|
|
5148
|
+
chars[index] = " ";
|
|
5149
|
+
let inner = index + 1;
|
|
5150
|
+
while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
|
|
5151
|
+
chars[inner] = " ";
|
|
5152
|
+
inner++;
|
|
5153
|
+
}
|
|
5154
|
+
index = inner - 1;
|
|
5155
|
+
continue;
|
|
5156
|
+
}
|
|
5157
|
+
if (ch === '"' && chars[index - 1] !== "\\") {
|
|
5158
|
+
inString = !inString;
|
|
5159
|
+
}
|
|
5160
|
+
}
|
|
5161
|
+
return chars.join("");
|
|
5162
|
+
}
|
|
3407
5163
|
function loadWorldOrbitSource(source) {
|
|
3408
5164
|
const result = loadWorldOrbitSourceWithDiagnostics(source);
|
|
3409
5165
|
if (!result.ok || !result.value) {
|
|
@@ -3414,36 +5170,36 @@ var WorldOrbit = (() => {
|
|
|
3414
5170
|
}
|
|
3415
5171
|
function loadWorldOrbitSourceWithDiagnostics(source) {
|
|
3416
5172
|
const schemaVersion = detectWorldOrbitSchemaVersion(source);
|
|
3417
|
-
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft") {
|
|
5173
|
+
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1") {
|
|
3418
5174
|
return loadAtlasSourceWithDiagnostics(source, schemaVersion);
|
|
3419
5175
|
}
|
|
3420
5176
|
let ast;
|
|
3421
5177
|
try {
|
|
3422
5178
|
ast = parseWorldOrbit(source);
|
|
3423
|
-
} catch (
|
|
5179
|
+
} catch (error2) {
|
|
3424
5180
|
return {
|
|
3425
5181
|
ok: false,
|
|
3426
5182
|
value: null,
|
|
3427
|
-
diagnostics: [diagnosticFromError(
|
|
5183
|
+
diagnostics: [diagnosticFromError(error2, "parse")]
|
|
3428
5184
|
};
|
|
3429
5185
|
}
|
|
3430
5186
|
let document2;
|
|
3431
5187
|
try {
|
|
3432
5188
|
document2 = normalizeDocument(ast);
|
|
3433
|
-
} catch (
|
|
5189
|
+
} catch (error2) {
|
|
3434
5190
|
return {
|
|
3435
5191
|
ok: false,
|
|
3436
5192
|
value: null,
|
|
3437
|
-
diagnostics: [diagnosticFromError(
|
|
5193
|
+
diagnostics: [diagnosticFromError(error2, "normalize")]
|
|
3438
5194
|
};
|
|
3439
5195
|
}
|
|
3440
5196
|
try {
|
|
3441
5197
|
validateDocument(document2);
|
|
3442
|
-
} catch (
|
|
5198
|
+
} catch (error2) {
|
|
3443
5199
|
return {
|
|
3444
5200
|
ok: false,
|
|
3445
5201
|
value: null,
|
|
3446
|
-
diagnostics: [diagnosticFromError(
|
|
5202
|
+
diagnostics: [diagnosticFromError(error2, "validate")]
|
|
3447
5203
|
};
|
|
3448
5204
|
}
|
|
3449
5205
|
return {
|
|
@@ -3463,30 +5219,29 @@ var WorldOrbit = (() => {
|
|
|
3463
5219
|
let atlasDocument;
|
|
3464
5220
|
try {
|
|
3465
5221
|
atlasDocument = parseWorldOrbitAtlas(source);
|
|
3466
|
-
} catch (
|
|
5222
|
+
} catch (error2) {
|
|
3467
5223
|
return {
|
|
3468
5224
|
ok: false,
|
|
3469
5225
|
value: null,
|
|
3470
|
-
diagnostics: [diagnosticFromError(
|
|
5226
|
+
diagnostics: [diagnosticFromError(error2, "parse", "load.atlas.failed")]
|
|
3471
5227
|
};
|
|
3472
5228
|
}
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
document2 = materializeAtlasDocument(atlasDocument);
|
|
3476
|
-
} catch (error) {
|
|
5229
|
+
const atlasDiagnostics = [...atlasDocument.diagnostics];
|
|
5230
|
+
if (atlasDiagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
3477
5231
|
return {
|
|
3478
5232
|
ok: false,
|
|
3479
5233
|
value: null,
|
|
3480
|
-
diagnostics:
|
|
5234
|
+
diagnostics: atlasDiagnostics
|
|
3481
5235
|
};
|
|
3482
5236
|
}
|
|
5237
|
+
let document2;
|
|
3483
5238
|
try {
|
|
3484
|
-
|
|
3485
|
-
} catch (
|
|
5239
|
+
document2 = materializeAtlasDocument(atlasDocument);
|
|
5240
|
+
} catch (error2) {
|
|
3486
5241
|
return {
|
|
3487
5242
|
ok: false,
|
|
3488
5243
|
value: null,
|
|
3489
|
-
diagnostics: [diagnosticFromError(
|
|
5244
|
+
diagnostics: [diagnosticFromError(error2, "normalize", "load.atlas.materialize.failed")]
|
|
3490
5245
|
};
|
|
3491
5246
|
}
|
|
3492
5247
|
const loaded = {
|
|
@@ -3495,12 +5250,12 @@ var WorldOrbit = (() => {
|
|
|
3495
5250
|
document: document2,
|
|
3496
5251
|
atlasDocument,
|
|
3497
5252
|
draftDocument: atlasDocument,
|
|
3498
|
-
diagnostics:
|
|
5253
|
+
diagnostics: atlasDiagnostics
|
|
3499
5254
|
};
|
|
3500
5255
|
return {
|
|
3501
5256
|
ok: true,
|
|
3502
5257
|
value: loaded,
|
|
3503
|
-
diagnostics:
|
|
5258
|
+
diagnostics: atlasDiagnostics
|
|
3504
5259
|
};
|
|
3505
5260
|
}
|
|
3506
5261
|
|
|
@@ -3683,6 +5438,8 @@ var WorldOrbit = (() => {
|
|
|
3683
5438
|
const imageDefinitions = buildImageDefinitions(visibleObjects);
|
|
3684
5439
|
const orbitMarkup = layers.orbits ? renderOrbitLayer(scene, visibleObjectIds, layers.structures) : { back: "", front: "" };
|
|
3685
5440
|
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("") : "";
|
|
5441
|
+
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("") : "";
|
|
5442
|
+
const eventMarkup = layers.events ? scene.events.filter((event) => !event.hidden).map((event) => renderSceneEventOverlay(scene, event, visibleObjectIds, theme)).join("") : "";
|
|
3686
5443
|
const objectMarkup = layers.objects ? visibleObjects.map((object) => renderSceneObject(object, options.selectedObjectId ?? null, theme)).join("") : "";
|
|
3687
5444
|
const labelMarkup = layers.labels ? visibleLabels.map((label) => renderSceneLabel(scene, label, options.selectedObjectId ?? null)).join("") : "";
|
|
3688
5445
|
const metadataMarkup = layers.metadata ? `<text class="wo-title" x="56" y="64">${escapeXml(scene.title)}</text>
|
|
@@ -3717,6 +5474,10 @@ var WorldOrbit = (() => {
|
|
|
3717
5474
|
.wo-orbit-back { opacity: 0.38; stroke-dasharray: 8 6; }
|
|
3718
5475
|
.wo-orbit-front { opacity: 0.9; }
|
|
3719
5476
|
.wo-orbit-band { stroke: ${theme.orbitBand}; stroke-linecap: round; }
|
|
5477
|
+
.wo-relation { stroke: ${theme.relation}; stroke-width: 2; stroke-dasharray: 10 6; }
|
|
5478
|
+
.wo-event-line { stroke: ${theme.accent}; stroke-width: 1.6; stroke-dasharray: 5 5; opacity: 0.72; }
|
|
5479
|
+
.wo-event-node { fill: ${theme.accent}; stroke: ${theme.selected}; stroke-width: 1.4; opacity: 0.92; }
|
|
5480
|
+
.wo-event-label { fill: ${theme.accent}; font-family: ${theme.fontFamily}; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
|
|
3720
5481
|
.wo-leader { stroke: ${theme.leader}; stroke-width: 1.5; stroke-dasharray: 6 5; }
|
|
3721
5482
|
.wo-label { fill: ${theme.ink}; font-family: ${theme.fontFamily}; font-weight: 600; letter-spacing: 0.02em; }
|
|
3722
5483
|
.wo-label-secondary { fill: ${theme.muted}; font-family: ${theme.fontFamily}; font-weight: 500; }
|
|
@@ -3750,6 +5511,8 @@ var WorldOrbit = (() => {
|
|
|
3750
5511
|
<g data-worldorbit-world-content="true">
|
|
3751
5512
|
${layers.orbits ? `<g data-layer-id="orbits-back">${orbitMarkup.back}</g>` : ""}
|
|
3752
5513
|
${layers.guides ? `<g data-layer-id="guides">${leaderMarkup}</g>` : ""}
|
|
5514
|
+
${layers.relations ? `<g data-layer-id="relations">${relationMarkup}</g>` : ""}
|
|
5515
|
+
${layers.events ? `<g data-layer-id="events">${eventMarkup}</g>` : ""}
|
|
3753
5516
|
${layers.objects ? `<g data-layer-id="objects">${objectMarkup}</g>` : ""}
|
|
3754
5517
|
${layers.orbits ? `<g data-layer-id="orbits-front">${orbitMarkup.front}</g>` : ""}
|
|
3755
5518
|
${layers.labels ? `<g data-layer-id="labels">${labelMarkup}</g>` : ""}
|
|
@@ -3757,6 +5520,20 @@ var WorldOrbit = (() => {
|
|
|
3757
5520
|
</g>
|
|
3758
5521
|
</g>
|
|
3759
5522
|
</svg>`;
|
|
5523
|
+
}
|
|
5524
|
+
function renderSceneEventOverlay(scene, event, visibleObjectIds, theme) {
|
|
5525
|
+
const participants = event.objectIds.filter((objectId) => visibleObjectIds.has(objectId)).map((objectId) => scene.objects.find((object) => object.objectId === objectId && !object.hidden)).filter(Boolean);
|
|
5526
|
+
if (participants.length === 0) {
|
|
5527
|
+
return "";
|
|
5528
|
+
}
|
|
5529
|
+
const stroke = event.event.color || theme.accent;
|
|
5530
|
+
const label = event.event.label || event.event.id;
|
|
5531
|
+
const lineMarkup = participants.map((object) => `<line class="wo-event-line" x1="${event.x}" y1="${event.y}" x2="${object.x}" y2="${object.y}" stroke="${escapeAttribute(stroke)}" data-event-id="${escapeAttribute(event.eventId)}" data-object-id="${escapeAttribute(object.objectId)}" />`).join("");
|
|
5532
|
+
return `<g class="wo-event" data-render-id="${escapeXml(event.renderId)}" data-event-id="${escapeAttribute(event.eventId)}">
|
|
5533
|
+
${lineMarkup}
|
|
5534
|
+
<circle class="wo-event-node" cx="${event.x}" cy="${event.y}" r="5" fill="${escapeAttribute(stroke)}" />
|
|
5535
|
+
<text class="wo-event-label" x="${event.x}" y="${event.y - 10}" text-anchor="middle" font-size="10">${escapeXml(label)}</text>
|
|
5536
|
+
</g>`;
|
|
3760
5537
|
}
|
|
3761
5538
|
function renderDocumentToSvg(document2, options = {}) {
|
|
3762
5539
|
return renderSceneToSvg(renderDocumentToScene(document2, options), options);
|
|
@@ -3810,10 +5587,11 @@ var WorldOrbit = (() => {
|
|
|
3810
5587
|
function renderSceneObject(sceneObject, selectedObjectId, theme) {
|
|
3811
5588
|
const { object, x, y, radius, visualRadius } = sceneObject;
|
|
3812
5589
|
const selectionClass = selectedObjectId === sceneObject.objectId ? " wo-object-selected" : "";
|
|
5590
|
+
const kindClass = object.properties.kind ? ` wo-kind-${String(object.properties.kind).toLowerCase().replace(/[^a-z0-9-]/g, "-")}` : "";
|
|
3813
5591
|
const palette = resolveObjectPalette(sceneObject, theme);
|
|
3814
5592
|
const imageMarkup = renderObjectImage(sceneObject);
|
|
3815
5593
|
const outlineMarkup = imageMarkup ? renderObjectBody(object, x, y, radius, palette, { outlineOnly: true }) : "";
|
|
3816
|
-
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}`)}">
|
|
5594
|
+
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}`)}">
|
|
3817
5595
|
<circle class="wo-selection-ring" cx="${x}" cy="${y}" r="${visualRadius + 8}" />
|
|
3818
5596
|
${renderAtmosphere(sceneObject, palette)}
|
|
3819
5597
|
${renderObjectBody(object, x, y, radius, palette)}
|
|
@@ -3847,8 +5625,33 @@ var WorldOrbit = (() => {
|
|
|
3847
5625
|
<circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
3848
5626
|
case "structure":
|
|
3849
5627
|
return `<polygon points="${diamondPoints(x, y, radius)}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
3850
|
-
case "phenomenon":
|
|
5628
|
+
case "phenomenon": {
|
|
5629
|
+
const kind = String(object.properties.kind ?? "").toLowerCase().replace(/_/g, "-");
|
|
5630
|
+
if (options.outlineOnly) {
|
|
5631
|
+
if (kind === "black-hole" || kind === "nebula" || kind === "galaxy" || kind === "dwarf-galaxy") {
|
|
5632
|
+
return `<circle cx="${x}" cy="${y}" r="${radius}" fill="transparent" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
5633
|
+
}
|
|
5634
|
+
return `<polygon points="${phenomenonPoints(x, y, radius)}" fill="transparent" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
5635
|
+
}
|
|
5636
|
+
if (kind === "black-hole") {
|
|
5637
|
+
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" />
|
|
5638
|
+
<circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" stroke="${palette.stroke}" stroke-width="2" />`;
|
|
5639
|
+
}
|
|
5640
|
+
if (kind === "galaxy") {
|
|
5641
|
+
return `<ellipse cx="${x}" cy="${y}" rx="${radius * 2.6}" ry="${radius}" fill="${palette.halo ?? "none"}" stroke="none" />
|
|
5642
|
+
<ellipse cx="${x}" cy="${y}" rx="${radius * 1.5}" ry="${radius * 0.42}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.2" />
|
|
5643
|
+
<circle cx="${x}" cy="${y}" r="${radius * 0.28}" fill="${palette.core ?? "#fff"}" stroke="none" />`;
|
|
5644
|
+
}
|
|
5645
|
+
if (kind === "dwarf-galaxy") {
|
|
5646
|
+
return `<ellipse cx="${x}" cy="${y}" rx="${radius * 1.6}" ry="${radius * 0.55}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1" />
|
|
5647
|
+
<circle cx="${x}" cy="${y}" r="${radius * 0.25}" fill="${palette.core ?? "#fff"}" stroke="none" />`;
|
|
5648
|
+
}
|
|
5649
|
+
if (kind === "nebula") {
|
|
5650
|
+
return `<circle cx="${x}" cy="${y}" r="${radius * 2.2}" fill="${palette.halo ?? "none"}" stroke="none" />
|
|
5651
|
+
<circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1" />`;
|
|
5652
|
+
}
|
|
3851
5653
|
return `<polygon points="${phenomenonPoints(x, y, radius)}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.4" />`;
|
|
5654
|
+
}
|
|
3852
5655
|
}
|
|
3853
5656
|
}
|
|
3854
5657
|
function renderAtmosphere(sceneObject, palette) {
|
|
@@ -3917,7 +5720,8 @@ var WorldOrbit = (() => {
|
|
|
3917
5720
|
}
|
|
3918
5721
|
}
|
|
3919
5722
|
function resolveObjectPalette(sceneObject, theme) {
|
|
3920
|
-
const
|
|
5723
|
+
const kind = String(sceneObject.object.properties.kind ?? "").toLowerCase().replace(/_/g, "-");
|
|
5724
|
+
const base = basePaletteForType(sceneObject.object.type, kind, theme);
|
|
3921
5725
|
const customFill = sceneObject.fillColor && isColorLike(sceneObject.fillColor) ? sceneObject.fillColor : base.fill;
|
|
3922
5726
|
const albedo = numericValue(sceneObject.object.properties.albedo);
|
|
3923
5727
|
const temperature = numericValue(sceneObject.object.properties.temperature);
|
|
@@ -3933,7 +5737,7 @@ var WorldOrbit = (() => {
|
|
|
3933
5737
|
tail: sceneObject.object.type === "comet" ? rgbaString(mixColors(fill, "#ffffff", 0.5) ?? fill, 0.72) : void 0
|
|
3934
5738
|
};
|
|
3935
5739
|
}
|
|
3936
|
-
function basePaletteForType(type, theme) {
|
|
5740
|
+
function basePaletteForType(type, kind, theme) {
|
|
3937
5741
|
switch (type) {
|
|
3938
5742
|
case "star":
|
|
3939
5743
|
return {
|
|
@@ -3955,8 +5759,26 @@ var WorldOrbit = (() => {
|
|
|
3955
5759
|
case "structure":
|
|
3956
5760
|
return { fill: theme.accentStrong, stroke: "#fff2ea" };
|
|
3957
5761
|
case "phenomenon":
|
|
3958
|
-
return
|
|
5762
|
+
return kindPhenomenonPalette(kind);
|
|
5763
|
+
}
|
|
5764
|
+
}
|
|
5765
|
+
function kindPhenomenonPalette(kind) {
|
|
5766
|
+
if (kind === "galaxy") {
|
|
5767
|
+
return { fill: "rgba(165,125,255,0.55)", stroke: "rgba(210,185,255,0.75)", halo: "rgba(160,120,255,0.10)", core: "#ede0ff" };
|
|
5768
|
+
}
|
|
5769
|
+
if (kind === "dwarf-galaxy") {
|
|
5770
|
+
return { fill: "rgba(190,165,255,0.45)", stroke: "rgba(220,205,255,0.75)", core: "#ddd0ff" };
|
|
3959
5771
|
}
|
|
5772
|
+
if (kind === "black-hole") {
|
|
5773
|
+
return { fill: "#040408", stroke: "#ff6a00", accentRing: "rgba(255,140,20,0.72)" };
|
|
5774
|
+
}
|
|
5775
|
+
if (kind === "nebula") {
|
|
5776
|
+
return { fill: "rgba(105,205,255,0.45)", stroke: "rgba(180,235,255,0.72)", halo: "rgba(100,200,255,0.08)" };
|
|
5777
|
+
}
|
|
5778
|
+
if (kind === "void") {
|
|
5779
|
+
return { fill: "#05080f", stroke: "rgba(130,160,255,0.4)" };
|
|
5780
|
+
}
|
|
5781
|
+
return { fill: "#78ffd7", stroke: "#e9fff7" };
|
|
3960
5782
|
}
|
|
3961
5783
|
function applyTemperatureAndAlbedo(baseColor, temperature, albedo, type) {
|
|
3962
5784
|
let nextColor = baseColor;
|
|
@@ -4276,6 +6098,48 @@ var WorldOrbit = (() => {
|
|
|
4276
6098
|
});
|
|
4277
6099
|
}
|
|
4278
6100
|
const placement = details.object.placement;
|
|
6101
|
+
if (details.object.groups?.length) {
|
|
6102
|
+
fields.set("groups", {
|
|
6103
|
+
key: "groups",
|
|
6104
|
+
label: "Groups",
|
|
6105
|
+
value: details.object.groups.join(", ")
|
|
6106
|
+
});
|
|
6107
|
+
}
|
|
6108
|
+
if (details.object.epoch) {
|
|
6109
|
+
fields.set("epoch", {
|
|
6110
|
+
key: "epoch",
|
|
6111
|
+
label: "Epoch",
|
|
6112
|
+
value: details.object.epoch
|
|
6113
|
+
});
|
|
6114
|
+
}
|
|
6115
|
+
if (details.object.referencePlane) {
|
|
6116
|
+
fields.set("referencePlane", {
|
|
6117
|
+
key: "referencePlane",
|
|
6118
|
+
label: "Reference Plane",
|
|
6119
|
+
value: details.object.referencePlane
|
|
6120
|
+
});
|
|
6121
|
+
}
|
|
6122
|
+
if (details.object.tidalLock !== void 0) {
|
|
6123
|
+
fields.set("tidalLock", {
|
|
6124
|
+
key: "tidalLock",
|
|
6125
|
+
label: "Tidal Lock",
|
|
6126
|
+
value: details.object.tidalLock ? "true" : "false"
|
|
6127
|
+
});
|
|
6128
|
+
}
|
|
6129
|
+
if (details.object.resonance) {
|
|
6130
|
+
fields.set("resonance", {
|
|
6131
|
+
key: "resonance",
|
|
6132
|
+
label: "Resonance",
|
|
6133
|
+
value: `${details.object.resonance.targetObjectId} ${details.object.resonance.ratio}`
|
|
6134
|
+
});
|
|
6135
|
+
}
|
|
6136
|
+
if (details.relatedEvents.length > 0) {
|
|
6137
|
+
fields.set("events", {
|
|
6138
|
+
key: "events",
|
|
6139
|
+
label: "Events",
|
|
6140
|
+
value: details.relatedEvents.map((event) => event.event.label || event.event.id).join(", ")
|
|
6141
|
+
});
|
|
6142
|
+
}
|
|
4279
6143
|
if (placement?.mode === "at") {
|
|
4280
6144
|
fields.set("placement", {
|
|
4281
6145
|
key: "placement",
|
|
@@ -4445,6 +6309,9 @@ var WorldOrbit = (() => {
|
|
|
4445
6309
|
touchPoints.set(event.pointerId, point);
|
|
4446
6310
|
if (touchPoints.size === 2) {
|
|
4447
6311
|
touchGesture = createTouchGestureState(scene, state, touchPoints);
|
|
6312
|
+
} else if (touchPoints.size === 1) {
|
|
6313
|
+
dragDistance = 0;
|
|
6314
|
+
suppressClick = false;
|
|
4448
6315
|
}
|
|
4449
6316
|
return;
|
|
4450
6317
|
}
|
|
@@ -4462,7 +6329,9 @@ var WorldOrbit = (() => {
|
|
|
4462
6329
|
if (!behavior.touch || !touchPoints.has(event.pointerId)) {
|
|
4463
6330
|
return;
|
|
4464
6331
|
}
|
|
4465
|
-
touchPoints.
|
|
6332
|
+
const prevPoint = touchPoints.get(event.pointerId);
|
|
6333
|
+
const nextPoint2 = getViewportPointFromClient(event.clientX, event.clientY);
|
|
6334
|
+
touchPoints.set(event.pointerId, nextPoint2);
|
|
4466
6335
|
if (touchPoints.size === 2) {
|
|
4467
6336
|
if (!touchGesture) {
|
|
4468
6337
|
touchGesture = createTouchGestureState(scene, state, touchPoints);
|
|
@@ -4473,6 +6342,14 @@ var WorldOrbit = (() => {
|
|
|
4473
6342
|
const deltaX2 = current.center.x - touchGesture.startViewportCenter.x;
|
|
4474
6343
|
const deltaY2 = current.center.y - touchGesture.startViewportCenter.y;
|
|
4475
6344
|
updateState(panViewerState(zoomedState, deltaX2, deltaY2));
|
|
6345
|
+
} else if (touchPoints.size === 1) {
|
|
6346
|
+
const deltaX2 = nextPoint2.x - prevPoint.x;
|
|
6347
|
+
const deltaY2 = nextPoint2.y - prevPoint.y;
|
|
6348
|
+
dragDistance += Math.abs(deltaX2) + Math.abs(deltaY2);
|
|
6349
|
+
if (dragDistance > 2) {
|
|
6350
|
+
suppressClick = true;
|
|
6351
|
+
}
|
|
6352
|
+
updateState(panViewerState(state, deltaX2, deltaY2));
|
|
4476
6353
|
}
|
|
4477
6354
|
return;
|
|
4478
6355
|
}
|
|
@@ -4677,6 +6554,12 @@ var WorldOrbit = (() => {
|
|
|
4677
6554
|
emitAtlasStateChange();
|
|
4678
6555
|
return true;
|
|
4679
6556
|
},
|
|
6557
|
+
getActiveEventId() {
|
|
6558
|
+
return renderOptions.activeEventId ?? null;
|
|
6559
|
+
},
|
|
6560
|
+
setActiveEvent(id) {
|
|
6561
|
+
api.setRenderOptions({ activeEventId: id });
|
|
6562
|
+
},
|
|
4680
6563
|
search(query, limit = 12) {
|
|
4681
6564
|
return searchSceneObjects(scene, query, limit);
|
|
4682
6565
|
},
|
|
@@ -4937,8 +6820,11 @@ var WorldOrbit = (() => {
|
|
|
4937
6820
|
renderObject,
|
|
4938
6821
|
label: scene.labels.find((label) => label.objectId === renderObject.objectId && !label.hidden) ?? null,
|
|
4939
6822
|
group: scene.groups.find((group) => group.renderId === renderObject.groupId) ?? null,
|
|
6823
|
+
semanticGroups: scene.semanticGroups.filter((group) => renderObject.semanticGroupIds.includes(group.id)),
|
|
4940
6824
|
orbit: scene.orbitVisuals.find((orbit) => orbit.objectId === renderObject.objectId && !orbit.hidden) ?? null,
|
|
4941
6825
|
relatedOrbits: scene.orbitVisuals.filter((orbit) => !orbit.hidden && (orbit.objectId === renderObject.objectId || renderObject.ancestorIds.includes(orbit.objectId) || renderObject.childIds.includes(orbit.objectId))),
|
|
6826
|
+
relations: scene.relations.filter((relation) => !relation.hidden && (relation.fromObjectId === renderObject.objectId || relation.toObjectId === renderObject.objectId)),
|
|
6827
|
+
relatedEvents: scene.events.filter((event) => !event.hidden && (event.targetObjectId === renderObject.objectId || event.objectIds.includes(renderObject.objectId))),
|
|
4942
6828
|
parent: getObjectById(renderObject.parentId),
|
|
4943
6829
|
children: renderObject.childIds.map((childId) => getObjectById(childId)).filter(Boolean),
|
|
4944
6830
|
ancestors: renderObject.ancestorIds.map((ancestorId) => getObjectById(ancestorId)).filter(Boolean),
|
|
@@ -5255,7 +7141,8 @@ var WorldOrbit = (() => {
|
|
|
5255
7141
|
filter: renderOptions.filter ? { ...renderOptions.filter } : void 0,
|
|
5256
7142
|
scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : void 0,
|
|
5257
7143
|
layers: renderOptions.layers ? { ...renderOptions.layers } : void 0,
|
|
5258
|
-
theme: renderOptions.theme && typeof renderOptions.theme === "object" ? { ...renderOptions.theme } : renderOptions.theme
|
|
7144
|
+
theme: renderOptions.theme && typeof renderOptions.theme === "object" ? { ...renderOptions.theme } : renderOptions.theme,
|
|
7145
|
+
activeEventId: renderOptions.activeEventId ?? null
|
|
5259
7146
|
};
|
|
5260
7147
|
}
|
|
5261
7148
|
function mergeRenderOptions(current, next) {
|
|
@@ -5275,7 +7162,7 @@ var WorldOrbit = (() => {
|
|
|
5275
7162
|
};
|
|
5276
7163
|
}
|
|
5277
7164
|
function hasSceneAffectingRenderOptions(options) {
|
|
5278
|
-
return options.width !== void 0 || options.height !== void 0 || options.padding !== void 0 || options.preset !== void 0 || options.projection !== void 0 || options.scaleModel !== void 0;
|
|
7165
|
+
return options.width !== void 0 || options.height !== void 0 || options.padding !== void 0 || options.preset !== void 0 || options.projection !== void 0 || options.scaleModel !== void 0 || options.activeEventId !== void 0;
|
|
5279
7166
|
}
|
|
5280
7167
|
function resolveSourceRenderOptions2(loaded, renderOptions) {
|
|
5281
7168
|
const atlasDocument = loaded.atlasDocument ?? loaded.draftDocument;
|
|
@@ -5563,6 +7450,7 @@ var WorldOrbit = (() => {
|
|
|
5563
7450
|
const controls = {
|
|
5564
7451
|
search: options.controls?.search ?? true,
|
|
5565
7452
|
typeFilter: options.controls?.typeFilter ?? true,
|
|
7453
|
+
groupFilter: options.controls?.groupFilter ?? true,
|
|
5566
7454
|
viewpointSelect: options.controls?.viewpointSelect ?? true,
|
|
5567
7455
|
inspector: options.controls?.inspector ?? true,
|
|
5568
7456
|
bookmarks: options.controls?.bookmarks ?? true
|
|
@@ -5572,6 +7460,7 @@ var WorldOrbit = (() => {
|
|
|
5572
7460
|
const toolbar = container.querySelector("[data-atlas-toolbar]");
|
|
5573
7461
|
const searchInput = container.querySelector("[data-atlas-search]");
|
|
5574
7462
|
const typeFilterSelect = container.querySelector("[data-atlas-type-filter]");
|
|
7463
|
+
const groupFilterSelect = container.querySelector("[data-atlas-group-filter]");
|
|
5575
7464
|
const viewpointSelect = container.querySelector("[data-atlas-viewpoint]");
|
|
5576
7465
|
const bookmarkButton = container.querySelector("[data-atlas-bookmark]");
|
|
5577
7466
|
const bookmarkList = container.querySelector("[data-atlas-bookmarks]");
|
|
@@ -5584,6 +7473,7 @@ var WorldOrbit = (() => {
|
|
|
5584
7473
|
const baseFilter = normalizeViewerFilter(options.initialFilter ?? null);
|
|
5585
7474
|
let searchQuery = options.initialQuery?.trim() ?? baseFilter?.query ?? "";
|
|
5586
7475
|
let objectTypeFilter = options.initialObjectType ?? (baseFilter?.objectTypes?.length === 1 ? baseFilter.objectTypes[0] : null);
|
|
7476
|
+
let groupFilter = baseFilter?.groupIds?.[0] ?? null;
|
|
5587
7477
|
let bookmarks = [];
|
|
5588
7478
|
let viewer;
|
|
5589
7479
|
viewer = createInteractiveViewer(stage, {
|
|
@@ -5631,6 +7521,7 @@ var WorldOrbit = (() => {
|
|
|
5631
7521
|
});
|
|
5632
7522
|
applyCurrentFilter();
|
|
5633
7523
|
populateViewpoints();
|
|
7524
|
+
populateGroups();
|
|
5634
7525
|
syncControlsFromFilter(viewer.getFilter());
|
|
5635
7526
|
renderBookmarks();
|
|
5636
7527
|
updateSearchResults();
|
|
@@ -5643,6 +7534,10 @@ var WorldOrbit = (() => {
|
|
|
5643
7534
|
objectTypeFilter = typeFilterSelect.value || null;
|
|
5644
7535
|
applyCurrentFilter();
|
|
5645
7536
|
});
|
|
7537
|
+
groupFilterSelect?.addEventListener("change", () => {
|
|
7538
|
+
groupFilter = groupFilterSelect.value || null;
|
|
7539
|
+
applyCurrentFilter();
|
|
7540
|
+
});
|
|
5646
7541
|
viewpointSelect?.addEventListener("change", () => {
|
|
5647
7542
|
const activeViewer = requireViewer();
|
|
5648
7543
|
if (!viewpointSelect.value) {
|
|
@@ -5784,6 +7679,7 @@ var WorldOrbit = (() => {
|
|
|
5784
7679
|
return api;
|
|
5785
7680
|
function refreshAfterInputChange() {
|
|
5786
7681
|
populateViewpoints();
|
|
7682
|
+
populateGroups();
|
|
5787
7683
|
applyCurrentFilter();
|
|
5788
7684
|
renderBookmarks();
|
|
5789
7685
|
updateSearchResults();
|
|
@@ -5800,19 +7696,23 @@ var WorldOrbit = (() => {
|
|
|
5800
7696
|
query: searchQuery || void 0,
|
|
5801
7697
|
objectTypes: objectTypeFilter ? [objectTypeFilter] : void 0,
|
|
5802
7698
|
tags: baseFilter?.tags,
|
|
5803
|
-
groupIds: baseFilter?.groupIds,
|
|
7699
|
+
groupIds: groupFilter ? [groupFilter] : baseFilter?.groupIds,
|
|
5804
7700
|
includeAncestors: baseFilter?.includeAncestors ?? true
|
|
5805
7701
|
});
|
|
5806
7702
|
}
|
|
5807
7703
|
function syncControlsFromFilter(filter) {
|
|
5808
7704
|
searchQuery = filter?.query?.trim() ?? "";
|
|
5809
7705
|
objectTypeFilter = filter?.objectTypes?.length === 1 ? filter.objectTypes[0] : null;
|
|
7706
|
+
groupFilter = filter?.groupIds?.length === 1 ? filter.groupIds[0] : null;
|
|
5810
7707
|
if (searchInput && document.activeElement !== searchInput) {
|
|
5811
7708
|
searchInput.value = searchQuery;
|
|
5812
7709
|
}
|
|
5813
7710
|
if (typeFilterSelect) {
|
|
5814
7711
|
typeFilterSelect.value = objectTypeFilter ?? "";
|
|
5815
7712
|
}
|
|
7713
|
+
if (groupFilterSelect) {
|
|
7714
|
+
groupFilterSelect.value = groupFilter ?? "";
|
|
7715
|
+
}
|
|
5816
7716
|
}
|
|
5817
7717
|
function populateViewpoints() {
|
|
5818
7718
|
if (!viewpointSelect) {
|
|
@@ -5826,6 +7726,17 @@ var WorldOrbit = (() => {
|
|
|
5826
7726
|
].join("");
|
|
5827
7727
|
viewpointSelect.value = active;
|
|
5828
7728
|
}
|
|
7729
|
+
function populateGroups() {
|
|
7730
|
+
if (!groupFilterSelect) {
|
|
7731
|
+
return;
|
|
7732
|
+
}
|
|
7733
|
+
const activeViewer = requireViewer();
|
|
7734
|
+
groupFilterSelect.innerHTML = [
|
|
7735
|
+
`<option value="">All groups</option>`,
|
|
7736
|
+
...activeViewer.getScene().semanticGroups.map((group) => `<option value="${escapeHtml2(group.id)}">${escapeHtml2(group.label)}</option>`)
|
|
7737
|
+
].join("");
|
|
7738
|
+
groupFilterSelect.value = groupFilter ?? "";
|
|
7739
|
+
}
|
|
5829
7740
|
function syncViewpointControl() {
|
|
5830
7741
|
if (!viewpointSelect) {
|
|
5831
7742
|
return;
|
|
@@ -5859,6 +7770,9 @@ var WorldOrbit = (() => {
|
|
|
5859
7770
|
projection: activeViewer.getScene().projection,
|
|
5860
7771
|
renderPreset: activeViewer.getScene().renderPreset,
|
|
5861
7772
|
groupCount: activeViewer.getScene().groups.length,
|
|
7773
|
+
semanticGroupCount: activeViewer.getScene().semanticGroups.length,
|
|
7774
|
+
relationCount: activeViewer.getScene().relations.length,
|
|
7775
|
+
eventCount: activeViewer.getScene().events.length,
|
|
5862
7776
|
viewpointCount: activeViewer.getScene().viewpoints.length
|
|
5863
7777
|
}
|
|
5864
7778
|
};
|
|
@@ -5891,6 +7805,12 @@ var WorldOrbit = (() => {
|
|
|
5891
7805
|
<option value="phenomenon">Phenomenon</option>
|
|
5892
7806
|
</select>
|
|
5893
7807
|
</label>` : "",
|
|
7808
|
+
controls.groupFilter ? `<label class="wo-atlas-field">
|
|
7809
|
+
<span>Group</span>
|
|
7810
|
+
<select data-atlas-group-filter>
|
|
7811
|
+
<option value="">All groups</option>
|
|
7812
|
+
</select>
|
|
7813
|
+
</label>` : "",
|
|
5894
7814
|
controls.viewpointSelect ? `<label class="wo-atlas-field">
|
|
5895
7815
|
<span>Viewpoint</span>
|
|
5896
7816
|
<select data-atlas-viewpoint>
|
|
@@ -6034,5 +7954,5 @@ var WorldOrbit = (() => {
|
|
|
6034
7954
|
function parseSource(source) {
|
|
6035
7955
|
return loadWorldOrbitSource(source).document;
|
|
6036
7956
|
}
|
|
6037
|
-
return __toCommonJS(
|
|
7957
|
+
return __toCommonJS(index_exports);
|
|
6038
7958
|
})();
|