worldorbit 2.5.16 → 2.6.0
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 +81 -15
- package/dist/browser/core/dist/index.js +1228 -110
- package/dist/browser/editor/dist/index.js +1896 -180
- package/dist/browser/markdown/dist/index.js +1071 -99
- package/dist/browser/viewer/dist/index.js +1127 -113
- package/dist/unpkg/core/dist/index.js +1228 -110
- package/dist/unpkg/editor/dist/index.js +1896 -180
- package/dist/unpkg/markdown/dist/index.js +1071 -99
- package/dist/unpkg/viewer/dist/index.js +1127 -113
- package/dist/unpkg/worldorbit-core.min.js +12 -12
- package/dist/unpkg/worldorbit-editor.min.js +295 -203
- package/dist/unpkg/worldorbit-markdown.min.js +66 -58
- package/dist/unpkg/worldorbit-viewer.min.js +84 -76
- package/dist/unpkg/worldorbit.js +1304 -124
- package/dist/unpkg/worldorbit.min.js +88 -80
- package/package.json +1 -1
- package/packages/core/dist/atlas-edit.js +75 -1
- package/packages/core/dist/atlas-validate.js +211 -8
- package/packages/core/dist/draft-parse.js +401 -22
- package/packages/core/dist/draft.d.ts +5 -2
- package/packages/core/dist/draft.js +103 -8
- package/packages/core/dist/format.js +99 -6
- package/packages/core/dist/load.js +9 -2
- package/packages/core/dist/normalize.js +1 -0
- package/packages/core/dist/scene.js +400 -64
- package/packages/core/dist/types.d.ts +60 -4
- package/packages/editor/dist/editor.js +702 -65
- package/packages/editor/dist/types.d.ts +3 -1
- package/packages/viewer/dist/atlas-state.js +11 -2
- package/packages/viewer/dist/atlas-viewer.js +19 -7
- 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 +12 -2
- package/packages/viewer/dist/viewer.js +28 -1
|
@@ -615,6 +615,7 @@ var WorldOrbit = (() => {
|
|
|
615
615
|
system,
|
|
616
616
|
groups: [],
|
|
617
617
|
relations: [],
|
|
618
|
+
events: [],
|
|
618
619
|
objects
|
|
619
620
|
};
|
|
620
621
|
}
|
|
@@ -1068,12 +1069,16 @@ var WorldOrbit = (() => {
|
|
|
1068
1069
|
const height = frame.height;
|
|
1069
1070
|
const padding = frame.padding;
|
|
1070
1071
|
const layoutPreset = resolveLayoutPreset(document);
|
|
1071
|
-
const
|
|
1072
|
+
const schemaProjection = resolveProjection(document, options.projection);
|
|
1073
|
+
const camera = normalizeViewCamera(options.camera ?? null);
|
|
1074
|
+
const renderProjection = resolveRenderProjection(schemaProjection, camera);
|
|
1072
1075
|
const scaleModel = resolveScaleModel(layoutPreset, options.scaleModel);
|
|
1073
1076
|
const spacingFactor = layoutPresetSpacing(layoutPreset);
|
|
1074
1077
|
const systemId = document.system?.id ?? null;
|
|
1075
|
-
const
|
|
1076
|
-
const
|
|
1078
|
+
const activeEventId = options.activeEventId ?? null;
|
|
1079
|
+
const effectiveObjects = createEffectiveObjects(document.objects, document.events ?? [], activeEventId);
|
|
1080
|
+
const objectMap = new Map(effectiveObjects.map((object) => [object.id, object]));
|
|
1081
|
+
const relationships = buildSceneRelationships(effectiveObjects, objectMap);
|
|
1077
1082
|
const positions = /* @__PURE__ */ new Map();
|
|
1078
1083
|
const orbitDrafts = [];
|
|
1079
1084
|
const leaderDrafts = [];
|
|
@@ -1082,7 +1087,7 @@ var WorldOrbit = (() => {
|
|
|
1082
1087
|
const atObjects = [];
|
|
1083
1088
|
const surfaceChildren = /* @__PURE__ */ new Map();
|
|
1084
1089
|
const orbitChildren = /* @__PURE__ */ new Map();
|
|
1085
|
-
for (const object of
|
|
1090
|
+
for (const object of effectiveObjects) {
|
|
1086
1091
|
const placement = object.placement;
|
|
1087
1092
|
if (!placement) {
|
|
1088
1093
|
rootObjects.push(object);
|
|
@@ -1109,7 +1114,7 @@ var WorldOrbit = (() => {
|
|
|
1109
1114
|
surfaceChildren,
|
|
1110
1115
|
objectMap,
|
|
1111
1116
|
spacingFactor,
|
|
1112
|
-
projection,
|
|
1117
|
+
projection: renderProjection,
|
|
1113
1118
|
scaleModel
|
|
1114
1119
|
};
|
|
1115
1120
|
const primaryRoot = rootObjects.find((object) => object.type === "star") ?? rootObjects[0] ?? null;
|
|
@@ -1121,7 +1126,7 @@ var WorldOrbit = (() => {
|
|
|
1121
1126
|
const rootRingRadius = Math.min(width, height) * 0.28 * spacingFactor * scaleModel.orbitDistanceMultiplier;
|
|
1122
1127
|
secondaryRoots.forEach((object, index) => {
|
|
1123
1128
|
const angle = angleForIndex(index, secondaryRoots.length, -Math.PI / 2);
|
|
1124
|
-
const offset = projectPolarOffset(angle, rootRingRadius,
|
|
1129
|
+
const offset = projectPolarOffset(angle, rootRingRadius, renderProjection, 1);
|
|
1125
1130
|
placeObject(object, centerX + offset.x, centerY + offset.y, 0, positions, orbitDrafts, leaderDrafts, context);
|
|
1126
1131
|
});
|
|
1127
1132
|
}
|
|
@@ -1177,38 +1182,48 @@ var WorldOrbit = (() => {
|
|
|
1177
1182
|
const objects = [...positions.values()].map((position) => createSceneObject(position, scaleModel, relationships));
|
|
1178
1183
|
const orbitVisuals = orbitDrafts.map((draft) => createOrbitVisual(draft, relationships.groupIds.get(draft.object.id) ?? null));
|
|
1179
1184
|
const leaders = leaderDrafts.map((draft) => createLeaderLine(draft));
|
|
1180
|
-
const labels = createSceneLabels(objects, height, scaleModel.labelMultiplier);
|
|
1185
|
+
const labels = createSceneLabels(objects, width, height, scaleModel.labelMultiplier);
|
|
1181
1186
|
const relations = createSceneRelations(document, objects);
|
|
1182
|
-
const
|
|
1183
|
-
const
|
|
1187
|
+
const events = createSceneEvents(document.events ?? [], objects, activeEventId);
|
|
1188
|
+
const layers = createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels);
|
|
1189
|
+
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, scaleModel.labelMultiplier);
|
|
1184
1190
|
const semanticGroups = createSceneSemanticGroups(document, objects);
|
|
1185
|
-
const viewpoints = createSceneViewpoints(document,
|
|
1186
|
-
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels);
|
|
1191
|
+
const viewpoints = createSceneViewpoints(document, schemaProjection, frame.preset, relationships, objectMap);
|
|
1192
|
+
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, scaleModel.labelMultiplier);
|
|
1187
1193
|
return {
|
|
1188
1194
|
width,
|
|
1189
1195
|
height,
|
|
1190
1196
|
padding,
|
|
1191
1197
|
renderPreset: frame.preset,
|
|
1192
|
-
projection,
|
|
1198
|
+
projection: schemaProjection,
|
|
1199
|
+
renderProjection,
|
|
1200
|
+
camera,
|
|
1193
1201
|
scaleModel,
|
|
1194
1202
|
title: String(document.system?.title ?? document.system?.properties.title ?? document.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1195
|
-
subtitle:
|
|
1203
|
+
subtitle: buildSceneSubtitle(schemaProjection, renderProjection, layoutPreset, camera),
|
|
1196
1204
|
systemId,
|
|
1197
|
-
viewMode:
|
|
1205
|
+
viewMode: schemaProjection,
|
|
1198
1206
|
layoutPreset,
|
|
1199
1207
|
metadata: {
|
|
1200
1208
|
format: document.format,
|
|
1201
1209
|
version: document.version,
|
|
1202
|
-
view:
|
|
1210
|
+
view: schemaProjection,
|
|
1211
|
+
renderProjection,
|
|
1203
1212
|
scale: String(document.system?.properties.scale ?? layoutPreset),
|
|
1204
1213
|
units: String(document.system?.properties.units ?? "mixed"),
|
|
1205
|
-
preset: frame.preset ?? "custom"
|
|
1214
|
+
preset: frame.preset ?? "custom",
|
|
1215
|
+
...camera?.azimuth !== null ? { "camera.azimuth": String(camera?.azimuth) } : {},
|
|
1216
|
+
...camera?.elevation !== null ? { "camera.elevation": String(camera?.elevation) } : {},
|
|
1217
|
+
...camera?.roll !== null ? { "camera.roll": String(camera?.roll) } : {},
|
|
1218
|
+
...camera?.distance !== null ? { "camera.distance": String(camera?.distance) } : {}
|
|
1206
1219
|
},
|
|
1207
1220
|
contentBounds,
|
|
1208
1221
|
layers,
|
|
1209
1222
|
groups,
|
|
1210
1223
|
semanticGroups,
|
|
1211
1224
|
viewpoints,
|
|
1225
|
+
events,
|
|
1226
|
+
activeEventId,
|
|
1212
1227
|
objects,
|
|
1213
1228
|
orbitVisuals,
|
|
1214
1229
|
relations,
|
|
@@ -1227,6 +1242,56 @@ var WorldOrbit = (() => {
|
|
|
1227
1242
|
y: center.y + dx * sin + dy * cos
|
|
1228
1243
|
};
|
|
1229
1244
|
}
|
|
1245
|
+
function createEffectiveObjects(objects, events, activeEventId) {
|
|
1246
|
+
const cloned = objects.map((object) => structuredClone(object));
|
|
1247
|
+
if (!activeEventId) {
|
|
1248
|
+
return cloned;
|
|
1249
|
+
}
|
|
1250
|
+
const activeEvent = events.find((event) => event.id === activeEventId);
|
|
1251
|
+
if (!activeEvent) {
|
|
1252
|
+
return cloned;
|
|
1253
|
+
}
|
|
1254
|
+
const objectMap = new Map(cloned.map((object) => [object.id, object]));
|
|
1255
|
+
const referencedIds = /* @__PURE__ */ new Set([
|
|
1256
|
+
...activeEvent.targetObjectId ? [activeEvent.targetObjectId] : [],
|
|
1257
|
+
...activeEvent.participantObjectIds,
|
|
1258
|
+
...activeEvent.positions.map((pose) => pose.objectId)
|
|
1259
|
+
]);
|
|
1260
|
+
for (const objectId of referencedIds) {
|
|
1261
|
+
const object = objectMap.get(objectId);
|
|
1262
|
+
if (!object) {
|
|
1263
|
+
continue;
|
|
1264
|
+
}
|
|
1265
|
+
if (activeEvent.epoch) {
|
|
1266
|
+
object.epoch = activeEvent.epoch;
|
|
1267
|
+
}
|
|
1268
|
+
if (activeEvent.referencePlane) {
|
|
1269
|
+
object.referencePlane = activeEvent.referencePlane;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
for (const pose of activeEvent.positions) {
|
|
1273
|
+
const object = objectMap.get(pose.objectId);
|
|
1274
|
+
if (!object) {
|
|
1275
|
+
continue;
|
|
1276
|
+
}
|
|
1277
|
+
if (pose.placement) {
|
|
1278
|
+
object.placement = structuredClone(pose.placement);
|
|
1279
|
+
}
|
|
1280
|
+
if (pose.inner) {
|
|
1281
|
+
object.properties.inner = { ...pose.inner };
|
|
1282
|
+
}
|
|
1283
|
+
if (pose.outer) {
|
|
1284
|
+
object.properties.outer = { ...pose.outer };
|
|
1285
|
+
}
|
|
1286
|
+
if (pose.epoch) {
|
|
1287
|
+
object.epoch = pose.epoch;
|
|
1288
|
+
}
|
|
1289
|
+
if (pose.referencePlane) {
|
|
1290
|
+
object.referencePlane = pose.referencePlane;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
return cloned;
|
|
1294
|
+
}
|
|
1230
1295
|
function resolveLayoutPreset(document) {
|
|
1231
1296
|
const rawScale = String(document.system?.properties.scale ?? "balanced").toLowerCase();
|
|
1232
1297
|
switch (rawScale) {
|
|
@@ -1263,10 +1328,59 @@ var WorldOrbit = (() => {
|
|
|
1263
1328
|
}
|
|
1264
1329
|
}
|
|
1265
1330
|
function resolveProjection(document, projection) {
|
|
1266
|
-
if (projection === "topdown" || projection === "isometric") {
|
|
1331
|
+
if (projection === "topdown" || projection === "isometric" || projection === "orthographic" || projection === "perspective") {
|
|
1267
1332
|
return projection;
|
|
1268
1333
|
}
|
|
1269
|
-
|
|
1334
|
+
const documentView = String(document.system?.properties.view ?? "topdown").toLowerCase();
|
|
1335
|
+
return parseViewProjection(documentView) ?? "topdown";
|
|
1336
|
+
}
|
|
1337
|
+
function resolveRenderProjection(projection, camera) {
|
|
1338
|
+
switch (projection) {
|
|
1339
|
+
case "topdown":
|
|
1340
|
+
return "topdown";
|
|
1341
|
+
case "isometric":
|
|
1342
|
+
return "isometric";
|
|
1343
|
+
case "orthographic":
|
|
1344
|
+
return camera && (camera.azimuth !== null || camera.elevation !== null || camera.roll !== null) ? "isometric" : "topdown";
|
|
1345
|
+
case "perspective":
|
|
1346
|
+
return "isometric";
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
function normalizeViewCamera(camera) {
|
|
1350
|
+
if (!camera) {
|
|
1351
|
+
return null;
|
|
1352
|
+
}
|
|
1353
|
+
const normalized = {
|
|
1354
|
+
azimuth: normalizeFiniteCameraValue(camera.azimuth),
|
|
1355
|
+
elevation: normalizeFiniteCameraValue(camera.elevation),
|
|
1356
|
+
roll: normalizeFiniteCameraValue(camera.roll),
|
|
1357
|
+
distance: normalizePositiveCameraDistance(camera.distance)
|
|
1358
|
+
};
|
|
1359
|
+
return normalized.azimuth !== null || normalized.elevation !== null || normalized.roll !== null || normalized.distance !== null ? normalized : null;
|
|
1360
|
+
}
|
|
1361
|
+
function normalizeFiniteCameraValue(value) {
|
|
1362
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1363
|
+
}
|
|
1364
|
+
function normalizePositiveCameraDistance(value) {
|
|
1365
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
1366
|
+
}
|
|
1367
|
+
function buildSceneSubtitle(projection, renderProjection, layoutPreset, camera) {
|
|
1368
|
+
const parts = [`${capitalizeLabel(projection)} view`, `${capitalizeLabel(layoutPreset)} layout`];
|
|
1369
|
+
if (projection !== renderProjection) {
|
|
1370
|
+
parts.push(`2D ${renderProjection} fallback`);
|
|
1371
|
+
}
|
|
1372
|
+
if (camera) {
|
|
1373
|
+
const cameraParts = [
|
|
1374
|
+
camera.azimuth !== null ? `az ${camera.azimuth}` : null,
|
|
1375
|
+
camera.elevation !== null ? `el ${camera.elevation}` : null,
|
|
1376
|
+
camera.roll !== null ? `roll ${camera.roll}` : null,
|
|
1377
|
+
camera.distance !== null ? `dist ${camera.distance}` : null
|
|
1378
|
+
].filter(Boolean);
|
|
1379
|
+
if (cameraParts.length > 0) {
|
|
1380
|
+
parts.push(`camera ${cameraParts.join(" / ")}`);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
return parts.join(" - ");
|
|
1270
1384
|
}
|
|
1271
1385
|
function resolveScaleModel(layoutPreset, overrides) {
|
|
1272
1386
|
const defaults = defaultScaleModel(layoutPreset);
|
|
@@ -1382,24 +1496,14 @@ var WorldOrbit = (() => {
|
|
|
1382
1496
|
hidden: draft.object.properties.hidden === true
|
|
1383
1497
|
};
|
|
1384
1498
|
}
|
|
1385
|
-
function createSceneLabels(objects, sceneHeight, labelMultiplier) {
|
|
1499
|
+
function createSceneLabels(objects, sceneWidth, sceneHeight, labelMultiplier) {
|
|
1386
1500
|
const labels = [];
|
|
1387
1501
|
const occupied = [];
|
|
1388
|
-
const
|
|
1502
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1503
|
+
const visibleObjects = [...objects].filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false).sort(compareLabelPlacementOrder);
|
|
1389
1504
|
for (const object of visibleObjects) {
|
|
1390
|
-
const
|
|
1391
|
-
|
|
1392
|
-
let labelY = object.y + direction * (object.radius + 18 * labelMultiplier);
|
|
1393
|
-
let secondaryY = labelY + direction * (16 * labelMultiplier);
|
|
1394
|
-
let bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
|
|
1395
|
-
let attempts = 0;
|
|
1396
|
-
while (occupied.some((entry) => rectsOverlap(entry, bounds)) && attempts < 10) {
|
|
1397
|
-
labelY += direction * 14 * labelMultiplier;
|
|
1398
|
-
secondaryY += direction * 14 * labelMultiplier;
|
|
1399
|
-
bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
|
|
1400
|
-
attempts += 1;
|
|
1401
|
-
}
|
|
1402
|
-
occupied.push(bounds);
|
|
1505
|
+
const placement = selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) ?? createLabelPlacement(object, defaultVerticalDirection(object, objectMap.get(object.parentId ?? "") ?? null, sceneHeight), 0, labelMultiplier);
|
|
1506
|
+
occupied.push(createLabelRect(object, placement, labelMultiplier));
|
|
1403
1507
|
labels.push({
|
|
1404
1508
|
renderId: `${object.renderId}-label`,
|
|
1405
1509
|
objectId: object.objectId,
|
|
@@ -1408,17 +1512,128 @@ var WorldOrbit = (() => {
|
|
|
1408
1512
|
semanticGroupIds: [...object.semanticGroupIds],
|
|
1409
1513
|
label: object.label,
|
|
1410
1514
|
secondaryLabel: object.secondaryLabel,
|
|
1411
|
-
x:
|
|
1412
|
-
y: labelY,
|
|
1413
|
-
secondaryY,
|
|
1414
|
-
textAnchor:
|
|
1415
|
-
direction: direction
|
|
1515
|
+
x: placement.x,
|
|
1516
|
+
y: placement.labelY,
|
|
1517
|
+
secondaryY: placement.secondaryY,
|
|
1518
|
+
textAnchor: placement.textAnchor,
|
|
1519
|
+
direction: placement.direction,
|
|
1416
1520
|
hidden: object.hidden
|
|
1417
1521
|
});
|
|
1418
1522
|
}
|
|
1419
1523
|
return labels;
|
|
1420
1524
|
}
|
|
1421
|
-
function
|
|
1525
|
+
function compareLabelPlacementOrder(left, right) {
|
|
1526
|
+
const priorityDiff = labelPlacementPriority(left) - labelPlacementPriority(right);
|
|
1527
|
+
if (priorityDiff !== 0) {
|
|
1528
|
+
return priorityDiff;
|
|
1529
|
+
}
|
|
1530
|
+
const renderPriorityDiff = (right.object.renderHints?.renderPriority ?? 0) - (left.object.renderHints?.renderPriority ?? 0);
|
|
1531
|
+
if (renderPriorityDiff !== 0) {
|
|
1532
|
+
return renderPriorityDiff;
|
|
1533
|
+
}
|
|
1534
|
+
return left.sortKey - right.sortKey;
|
|
1535
|
+
}
|
|
1536
|
+
function labelPlacementPriority(object) {
|
|
1537
|
+
switch (object.object.type) {
|
|
1538
|
+
case "star":
|
|
1539
|
+
return 0;
|
|
1540
|
+
case "planet":
|
|
1541
|
+
return 1;
|
|
1542
|
+
case "moon":
|
|
1543
|
+
return 2;
|
|
1544
|
+
case "belt":
|
|
1545
|
+
case "ring":
|
|
1546
|
+
return 3;
|
|
1547
|
+
case "asteroid":
|
|
1548
|
+
case "comet":
|
|
1549
|
+
return 4;
|
|
1550
|
+
case "structure":
|
|
1551
|
+
case "phenomenon":
|
|
1552
|
+
return 5;
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
function selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) {
|
|
1556
|
+
for (const direction of preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight)) {
|
|
1557
|
+
const maxAttempts = direction === "left" || direction === "right" ? 4 : 6;
|
|
1558
|
+
for (let attempt = 0; attempt <= maxAttempts; attempt += 1) {
|
|
1559
|
+
const placement = createLabelPlacement(object, direction, attempt, labelMultiplier);
|
|
1560
|
+
const rect = createLabelRect(object, placement, labelMultiplier);
|
|
1561
|
+
if (!occupied.some((entry) => rectsOverlap(entry, rect))) {
|
|
1562
|
+
return placement;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
return null;
|
|
1567
|
+
}
|
|
1568
|
+
function preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight) {
|
|
1569
|
+
const parent = object.parentId ? objectMap.get(object.parentId) ?? null : null;
|
|
1570
|
+
const vertical = defaultVerticalDirection(object, parent, sceneHeight);
|
|
1571
|
+
const oppositeVertical = vertical === "below" ? "above" : "below";
|
|
1572
|
+
const horizontal = defaultHorizontalDirection(object, parent, sceneWidth);
|
|
1573
|
+
const oppositeHorizontal = horizontal === "right" ? "left" : "right";
|
|
1574
|
+
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";
|
|
1575
|
+
return preferHorizontal ? [horizontal, vertical, oppositeHorizontal, oppositeVertical] : [vertical, horizontal, oppositeVertical, oppositeHorizontal];
|
|
1576
|
+
}
|
|
1577
|
+
function defaultVerticalDirection(object, parent, sceneHeight) {
|
|
1578
|
+
if (parent && Math.abs(object.y - parent.y) > 6) {
|
|
1579
|
+
return object.y >= parent.y ? "below" : "above";
|
|
1580
|
+
}
|
|
1581
|
+
return object.y > sceneHeight * 0.62 ? "above" : "below";
|
|
1582
|
+
}
|
|
1583
|
+
function defaultHorizontalDirection(object, parent, sceneWidth) {
|
|
1584
|
+
if (parent && Math.abs(object.x - parent.x) > 6) {
|
|
1585
|
+
return object.x >= parent.x ? "right" : "left";
|
|
1586
|
+
}
|
|
1587
|
+
return object.x >= sceneWidth / 2 ? "right" : "left";
|
|
1588
|
+
}
|
|
1589
|
+
function createLabelPlacement(object, direction, attempt, labelMultiplier) {
|
|
1590
|
+
const step = 14 * labelMultiplier;
|
|
1591
|
+
switch (direction) {
|
|
1592
|
+
case "above": {
|
|
1593
|
+
const labelY = object.y - (object.radius + 18 * labelMultiplier + attempt * step);
|
|
1594
|
+
return {
|
|
1595
|
+
x: object.x,
|
|
1596
|
+
labelY,
|
|
1597
|
+
secondaryY: labelY - 16 * labelMultiplier,
|
|
1598
|
+
textAnchor: "middle",
|
|
1599
|
+
direction
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
case "below": {
|
|
1603
|
+
const labelY = object.y + object.radius + 18 * labelMultiplier + attempt * step;
|
|
1604
|
+
return {
|
|
1605
|
+
x: object.x,
|
|
1606
|
+
labelY,
|
|
1607
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
1608
|
+
textAnchor: "middle",
|
|
1609
|
+
direction
|
|
1610
|
+
};
|
|
1611
|
+
}
|
|
1612
|
+
case "left": {
|
|
1613
|
+
const x = object.x - (object.visualRadius + 16 * labelMultiplier + attempt * step);
|
|
1614
|
+
const labelY = object.y - 4 * labelMultiplier;
|
|
1615
|
+
return {
|
|
1616
|
+
x,
|
|
1617
|
+
labelY,
|
|
1618
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
1619
|
+
textAnchor: "end",
|
|
1620
|
+
direction
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
case "right": {
|
|
1624
|
+
const x = object.x + object.visualRadius + 16 * labelMultiplier + attempt * step;
|
|
1625
|
+
const labelY = object.y - 4 * labelMultiplier;
|
|
1626
|
+
return {
|
|
1627
|
+
x,
|
|
1628
|
+
labelY,
|
|
1629
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
1630
|
+
textAnchor: "start",
|
|
1631
|
+
direction
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
function createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels) {
|
|
1422
1637
|
const backOrbitIds = orbitVisuals.filter((visual) => !visual.hidden && Boolean(visual.backArcPath)).map((visual) => visual.renderId);
|
|
1423
1638
|
const frontOrbitIds = orbitVisuals.filter((visual) => !visual.hidden).map((visual) => visual.renderId);
|
|
1424
1639
|
return [
|
|
@@ -1433,6 +1648,10 @@ var WorldOrbit = (() => {
|
|
|
1433
1648
|
id: "relations",
|
|
1434
1649
|
renderIds: relations.filter((relation) => !relation.hidden).map((relation) => relation.renderId)
|
|
1435
1650
|
},
|
|
1651
|
+
{
|
|
1652
|
+
id: "events",
|
|
1653
|
+
renderIds: events.filter((event) => !event.hidden).map((event) => event.renderId)
|
|
1654
|
+
},
|
|
1436
1655
|
{
|
|
1437
1656
|
id: "objects",
|
|
1438
1657
|
renderIds: objects.filter((object) => !object.hidden).map((object) => object.renderId)
|
|
@@ -1444,7 +1663,7 @@ var WorldOrbit = (() => {
|
|
|
1444
1663
|
{ id: "metadata", renderIds: ["wo-title", "wo-subtitle", "wo-meta"] }
|
|
1445
1664
|
];
|
|
1446
1665
|
}
|
|
1447
|
-
function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships) {
|
|
1666
|
+
function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, labelMultiplier) {
|
|
1448
1667
|
const groups = /* @__PURE__ */ new Map();
|
|
1449
1668
|
const ensureGroup = (groupId) => {
|
|
1450
1669
|
if (!groupId) {
|
|
@@ -1493,7 +1712,7 @@ var WorldOrbit = (() => {
|
|
|
1493
1712
|
}
|
|
1494
1713
|
}
|
|
1495
1714
|
for (const group of groups.values()) {
|
|
1496
|
-
group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels);
|
|
1715
|
+
group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier);
|
|
1497
1716
|
}
|
|
1498
1717
|
return [...groups.values()].sort((left, right) => left.label.localeCompare(right.label));
|
|
1499
1718
|
}
|
|
@@ -1527,6 +1746,29 @@ var WorldOrbit = (() => {
|
|
|
1527
1746
|
};
|
|
1528
1747
|
}).sort((left, right) => left.relation.id.localeCompare(right.relation.id));
|
|
1529
1748
|
}
|
|
1749
|
+
function createSceneEvents(events, objects, activeEventId) {
|
|
1750
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
1751
|
+
return events.map((event) => {
|
|
1752
|
+
const objectIds = [.../* @__PURE__ */ new Set([
|
|
1753
|
+
...event.targetObjectId ? [event.targetObjectId] : [],
|
|
1754
|
+
...event.participantObjectIds
|
|
1755
|
+
])];
|
|
1756
|
+
const positions = objectIds.map((objectId) => objectMap.get(objectId)).filter(Boolean);
|
|
1757
|
+
const centroidX = positions.length > 0 ? positions.reduce((sum, object) => sum + object.x, 0) / positions.length : 0;
|
|
1758
|
+
const centroidY = positions.length > 0 ? positions.reduce((sum, object) => sum + object.y, 0) / positions.length : 0;
|
|
1759
|
+
return {
|
|
1760
|
+
renderId: `${createRenderId(event.id)}-event`,
|
|
1761
|
+
eventId: event.id,
|
|
1762
|
+
event,
|
|
1763
|
+
objectIds,
|
|
1764
|
+
participantIds: [...event.participantObjectIds],
|
|
1765
|
+
targetObjectId: event.targetObjectId,
|
|
1766
|
+
x: centroidX,
|
|
1767
|
+
y: centroidY,
|
|
1768
|
+
hidden: event.hidden || positions.length === 0 || positions.every((object) => object.hidden) || activeEventId !== null && event.id !== activeEventId
|
|
1769
|
+
};
|
|
1770
|
+
}).sort((left, right) => left.event.id.localeCompare(right.event.id));
|
|
1771
|
+
}
|
|
1530
1772
|
function createSceneViewpoints(document, projection, preset, relationships, objectMap) {
|
|
1531
1773
|
const generatedOverview = createGeneratedOverviewViewpoint(document, projection, preset);
|
|
1532
1774
|
const drafts = /* @__PURE__ */ new Map();
|
|
@@ -1574,13 +1816,18 @@ var WorldOrbit = (() => {
|
|
|
1574
1816
|
function createGeneratedOverviewViewpoint(document, projection, preset) {
|
|
1575
1817
|
const title = document.system?.title ?? document.system?.properties.title;
|
|
1576
1818
|
const label = title ? `${String(title)} Overview` : "Overview";
|
|
1819
|
+
const camera = normalizeViewCamera(null);
|
|
1820
|
+
const renderProjection = resolveRenderProjection(projection, camera);
|
|
1577
1821
|
return {
|
|
1578
1822
|
id: "overview",
|
|
1579
1823
|
label,
|
|
1580
1824
|
summary: "Fit the whole system with the current atlas defaults.",
|
|
1581
1825
|
objectId: null,
|
|
1582
1826
|
selectedObjectId: null,
|
|
1827
|
+
eventIds: [],
|
|
1583
1828
|
projection,
|
|
1829
|
+
renderProjection,
|
|
1830
|
+
camera,
|
|
1584
1831
|
preset,
|
|
1585
1832
|
rotationDeg: 0,
|
|
1586
1833
|
scale: null,
|
|
@@ -1616,6 +1863,9 @@ var WorldOrbit = (() => {
|
|
|
1616
1863
|
draft.select = normalizedValue;
|
|
1617
1864
|
}
|
|
1618
1865
|
return;
|
|
1866
|
+
case "events":
|
|
1867
|
+
draft.eventIds = splitListValue(normalizedValue);
|
|
1868
|
+
return;
|
|
1619
1869
|
case "projection":
|
|
1620
1870
|
case "view":
|
|
1621
1871
|
draft.projection = parseViewProjection(normalizedValue) ?? projection;
|
|
@@ -1627,6 +1877,30 @@ var WorldOrbit = (() => {
|
|
|
1627
1877
|
case "angle":
|
|
1628
1878
|
draft.rotationDeg = parseFiniteNumber(normalizedValue) ?? draft.rotationDeg ?? 0;
|
|
1629
1879
|
return;
|
|
1880
|
+
case "camera.azimuth":
|
|
1881
|
+
draft.camera = {
|
|
1882
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1883
|
+
azimuth: parseFiniteNumber(normalizedValue)
|
|
1884
|
+
};
|
|
1885
|
+
return;
|
|
1886
|
+
case "camera.elevation":
|
|
1887
|
+
draft.camera = {
|
|
1888
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1889
|
+
elevation: parseFiniteNumber(normalizedValue)
|
|
1890
|
+
};
|
|
1891
|
+
return;
|
|
1892
|
+
case "camera.roll":
|
|
1893
|
+
draft.camera = {
|
|
1894
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1895
|
+
roll: parseFiniteNumber(normalizedValue)
|
|
1896
|
+
};
|
|
1897
|
+
return;
|
|
1898
|
+
case "camera.distance":
|
|
1899
|
+
draft.camera = {
|
|
1900
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1901
|
+
distance: parsePositiveNumber(normalizedValue)
|
|
1902
|
+
};
|
|
1903
|
+
return;
|
|
1630
1904
|
case "zoom":
|
|
1631
1905
|
case "scale":
|
|
1632
1906
|
draft.scale = parsePositiveNumber(normalizedValue);
|
|
@@ -1666,13 +1940,19 @@ var WorldOrbit = (() => {
|
|
|
1666
1940
|
const selectedObjectId = draft.select && objectMap.has(draft.select) ? draft.select : objectId;
|
|
1667
1941
|
const filter = normalizeViewpointFilter(draft.filter);
|
|
1668
1942
|
const label = draft.label?.trim() || humanizeIdentifier(draft.id);
|
|
1943
|
+
const resolvedProjection = draft.projection ?? projection;
|
|
1944
|
+
const camera = normalizeViewCamera(draft.camera ?? null);
|
|
1945
|
+
const renderProjection = resolveRenderProjection(resolvedProjection, camera);
|
|
1669
1946
|
return {
|
|
1670
1947
|
id: draft.id,
|
|
1671
1948
|
label,
|
|
1672
1949
|
summary: draft.summary?.trim() || createViewpointSummary(label, objectId, filter),
|
|
1673
1950
|
objectId,
|
|
1674
1951
|
selectedObjectId,
|
|
1675
|
-
|
|
1952
|
+
eventIds: [...new Set(draft.eventIds ?? [])],
|
|
1953
|
+
projection: resolvedProjection,
|
|
1954
|
+
renderProjection,
|
|
1955
|
+
camera,
|
|
1676
1956
|
preset: draft.preset ?? preset,
|
|
1677
1957
|
rotationDeg: draft.rotationDeg ?? 0,
|
|
1678
1958
|
scale: draft.scale ?? null,
|
|
@@ -1689,6 +1969,14 @@ var WorldOrbit = (() => {
|
|
|
1689
1969
|
groupIds: []
|
|
1690
1970
|
};
|
|
1691
1971
|
}
|
|
1972
|
+
function createEmptyViewCamera() {
|
|
1973
|
+
return {
|
|
1974
|
+
azimuth: null,
|
|
1975
|
+
elevation: null,
|
|
1976
|
+
roll: null,
|
|
1977
|
+
distance: null
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1692
1980
|
function normalizeViewpointFilter(filter) {
|
|
1693
1981
|
if (!filter) {
|
|
1694
1982
|
return null;
|
|
@@ -1702,7 +1990,18 @@ var WorldOrbit = (() => {
|
|
|
1702
1990
|
return normalized.query || normalized.objectTypes.length > 0 || normalized.tags.length > 0 || normalized.groupIds.length > 0 ? normalized : null;
|
|
1703
1991
|
}
|
|
1704
1992
|
function parseViewProjection(value) {
|
|
1705
|
-
|
|
1993
|
+
switch (value.toLowerCase()) {
|
|
1994
|
+
case "topdown":
|
|
1995
|
+
return "topdown";
|
|
1996
|
+
case "isometric":
|
|
1997
|
+
return "isometric";
|
|
1998
|
+
case "orthographic":
|
|
1999
|
+
return "orthographic";
|
|
2000
|
+
case "perspective":
|
|
2001
|
+
return "perspective";
|
|
2002
|
+
default:
|
|
2003
|
+
return null;
|
|
2004
|
+
}
|
|
1706
2005
|
}
|
|
1707
2006
|
function parseRenderPreset(value) {
|
|
1708
2007
|
const normalized = value.toLowerCase();
|
|
@@ -1729,7 +2028,7 @@ var WorldOrbit = (() => {
|
|
|
1729
2028
|
next["orbits-front"] = enabled;
|
|
1730
2029
|
continue;
|
|
1731
2030
|
}
|
|
1732
|
-
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "relations" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
2031
|
+
if (rawLayer === "background" || rawLayer === "guides" || rawLayer === "orbits-back" || rawLayer === "orbits-front" || rawLayer === "relations" || rawLayer === "events" || rawLayer === "objects" || rawLayer === "labels" || rawLayer === "metadata") {
|
|
1733
2032
|
next[rawLayer] = enabled;
|
|
1734
2033
|
}
|
|
1735
2034
|
}
|
|
@@ -1740,7 +2039,7 @@ var WorldOrbit = (() => {
|
|
|
1740
2039
|
}
|
|
1741
2040
|
function parseViewpointGroups(value, document, relationships, objectMap) {
|
|
1742
2041
|
return splitListValue(value).map((entry) => {
|
|
1743
|
-
if (document.schemaVersion === "2.1" || document.groups.some((group) => group.id === entry)) {
|
|
2042
|
+
if (document.schemaVersion === "2.1" || document.schemaVersion === "2.5" || document.groups.some((group) => group.id === entry)) {
|
|
1744
2043
|
return entry;
|
|
1745
2044
|
}
|
|
1746
2045
|
if (entry.startsWith("wo-") && entry.endsWith("-group")) {
|
|
@@ -1777,7 +2076,7 @@ var WorldOrbit = (() => {
|
|
|
1777
2076
|
}
|
|
1778
2077
|
return parts.join(" - ");
|
|
1779
2078
|
}
|
|
1780
|
-
function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels) {
|
|
2079
|
+
function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, labelMultiplier) {
|
|
1781
2080
|
let minX = Number.POSITIVE_INFINITY;
|
|
1782
2081
|
let minY = Number.POSITIVE_INFINITY;
|
|
1783
2082
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
@@ -1807,7 +2106,7 @@ var WorldOrbit = (() => {
|
|
|
1807
2106
|
for (const label of labels) {
|
|
1808
2107
|
if (label.hidden)
|
|
1809
2108
|
continue;
|
|
1810
|
-
includeLabelBounds(label, include);
|
|
2109
|
+
includeLabelBounds(label, include, labelMultiplier);
|
|
1811
2110
|
}
|
|
1812
2111
|
if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
|
|
1813
2112
|
return createBounds(0, 0, width, height);
|
|
@@ -1845,13 +2144,10 @@ var WorldOrbit = (() => {
|
|
|
1845
2144
|
include(object.x - object.visualRadius - 24, object.y - object.visualRadius - 16);
|
|
1846
2145
|
include(object.x + object.visualRadius + 24, object.y + object.visualRadius + 36);
|
|
1847
2146
|
}
|
|
1848
|
-
function includeLabelBounds(label, include) {
|
|
1849
|
-
const
|
|
1850
|
-
|
|
1851
|
-
include(
|
|
1852
|
-
include(label.x + labelHalfWidth, label.y + 8);
|
|
1853
|
-
include(label.x - labelHalfWidth, label.secondaryY - 14);
|
|
1854
|
-
include(label.x + labelHalfWidth, label.secondaryY + 8);
|
|
2147
|
+
function includeLabelBounds(label, include, labelMultiplier) {
|
|
2148
|
+
const bounds = createLabelRectFromText(label.x, label.y, label.secondaryY, label.textAnchor, label.direction, label.label, label.secondaryLabel, labelMultiplier);
|
|
2149
|
+
include(bounds.left, bounds.top);
|
|
2150
|
+
include(bounds.right, bounds.bottom);
|
|
1855
2151
|
}
|
|
1856
2152
|
function placeObject(object, x, y, depth, positions, orbitDrafts, leaderDrafts, context) {
|
|
1857
2153
|
if (positions.has(object.id)) {
|
|
@@ -2241,7 +2537,7 @@ var WorldOrbit = (() => {
|
|
|
2241
2537
|
return null;
|
|
2242
2538
|
}
|
|
2243
2539
|
}
|
|
2244
|
-
function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels) {
|
|
2540
|
+
function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier) {
|
|
2245
2541
|
let minX = Number.POSITIVE_INFINITY;
|
|
2246
2542
|
let minY = Number.POSITIVE_INFINITY;
|
|
2247
2543
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
@@ -2270,7 +2566,7 @@ var WorldOrbit = (() => {
|
|
|
2270
2566
|
}
|
|
2271
2567
|
for (const label of labels) {
|
|
2272
2568
|
if (!label.hidden && group.labelIds.includes(label.objectId)) {
|
|
2273
|
-
includeLabelBounds(label, include);
|
|
2569
|
+
includeLabelBounds(label, include, labelMultiplier);
|
|
2274
2570
|
}
|
|
2275
2571
|
}
|
|
2276
2572
|
if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
|
|
@@ -2295,12 +2591,28 @@ var WorldOrbit = (() => {
|
|
|
2295
2591
|
}
|
|
2296
2592
|
return current.id;
|
|
2297
2593
|
}
|
|
2298
|
-
function createLabelRect(
|
|
2594
|
+
function createLabelRect(object, placement, labelMultiplier) {
|
|
2595
|
+
return createLabelRectFromText(placement.x, placement.labelY, placement.secondaryY, placement.textAnchor, placement.direction, object.label, object.secondaryLabel, labelMultiplier);
|
|
2596
|
+
}
|
|
2597
|
+
function createLabelRectFromText(x, labelY, secondaryY, textAnchor, direction, label, secondaryLabel, labelMultiplier) {
|
|
2598
|
+
const labelHalfWidth = estimateLabelHalfWidthFromText(label, secondaryLabel, labelMultiplier);
|
|
2599
|
+
const labelWidth = labelHalfWidth * 2;
|
|
2600
|
+
const topPadding = direction === "above" ? 18 : 12;
|
|
2601
|
+
const bottomPadding = direction === "above" ? 8 : 12;
|
|
2602
|
+
let left = x - labelHalfWidth;
|
|
2603
|
+
let right = x + labelHalfWidth;
|
|
2604
|
+
if (textAnchor === "start") {
|
|
2605
|
+
left = x;
|
|
2606
|
+
right = x + labelWidth;
|
|
2607
|
+
} else if (textAnchor === "end") {
|
|
2608
|
+
left = x - labelWidth;
|
|
2609
|
+
right = x;
|
|
2610
|
+
}
|
|
2299
2611
|
return {
|
|
2300
|
-
left
|
|
2301
|
-
right
|
|
2302
|
-
top: Math.min(labelY, secondaryY) -
|
|
2303
|
-
bottom: Math.max(labelY, secondaryY) +
|
|
2612
|
+
left,
|
|
2613
|
+
right,
|
|
2614
|
+
top: Math.min(labelY, secondaryY) - topPadding,
|
|
2615
|
+
bottom: Math.max(labelY, secondaryY) + bottomPadding
|
|
2304
2616
|
};
|
|
2305
2617
|
}
|
|
2306
2618
|
function rectsOverlap(left, right) {
|
|
@@ -2487,11 +2799,6 @@ var WorldOrbit = (() => {
|
|
|
2487
2799
|
function customColorFor(value) {
|
|
2488
2800
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
2489
2801
|
}
|
|
2490
|
-
function estimateLabelHalfWidth(object, labelMultiplier) {
|
|
2491
|
-
const primaryWidth = object.label.length * 4.6 * labelMultiplier + 18;
|
|
2492
|
-
const secondaryWidth = object.secondaryLabel.length * 3.9 * labelMultiplier + 18;
|
|
2493
|
-
return Math.max(primaryWidth, secondaryWidth, object.visualRadius + 18);
|
|
2494
|
-
}
|
|
2495
2802
|
function estimateLabelHalfWidthFromText(label, secondaryLabel, labelMultiplier) {
|
|
2496
2803
|
const primaryWidth = label.length * 4.6 * labelMultiplier + 18;
|
|
2497
2804
|
const secondaryWidth = secondaryLabel.length * 3.9 * labelMultiplier + 18;
|
|
@@ -2525,12 +2832,13 @@ var WorldOrbit = (() => {
|
|
|
2525
2832
|
}
|
|
2526
2833
|
return {
|
|
2527
2834
|
format: "worldorbit",
|
|
2528
|
-
version: "2.
|
|
2529
|
-
schemaVersion: "2.
|
|
2835
|
+
version: "2.5",
|
|
2836
|
+
schemaVersion: "2.5",
|
|
2530
2837
|
sourceVersion: document.version,
|
|
2531
2838
|
system,
|
|
2532
2839
|
groups: structuredClone(document.groups ?? []),
|
|
2533
2840
|
relations: structuredClone(document.relations ?? []),
|
|
2841
|
+
events: structuredClone(document.events ?? []),
|
|
2534
2842
|
objects: document.objects.map(cloneWorldOrbitObject),
|
|
2535
2843
|
diagnostics
|
|
2536
2844
|
};
|
|
@@ -2538,7 +2846,7 @@ var WorldOrbit = (() => {
|
|
|
2538
2846
|
function upgradeDocumentToDraftV2(document, options = {}) {
|
|
2539
2847
|
return convertAtlasDocumentToLegacyDraft(upgradeDocumentToV2(document, options));
|
|
2540
2848
|
}
|
|
2541
|
-
function materializeAtlasDocument(document) {
|
|
2849
|
+
function materializeAtlasDocument(document, options = {}) {
|
|
2542
2850
|
const system = document.system ? {
|
|
2543
2851
|
type: "system",
|
|
2544
2852
|
id: document.system.id,
|
|
@@ -2549,6 +2857,8 @@ var WorldOrbit = (() => {
|
|
|
2549
2857
|
properties: materializeDraftSystemProperties(document.system),
|
|
2550
2858
|
info: materializeDraftSystemInfo(document.system)
|
|
2551
2859
|
} : null;
|
|
2860
|
+
const objects = document.objects.map(cloneWorldOrbitObject);
|
|
2861
|
+
applyEventPoseOverrides(objects, document.events ?? [], options.activeEventId ?? null);
|
|
2552
2862
|
return {
|
|
2553
2863
|
format: "worldorbit",
|
|
2554
2864
|
version: "1.0",
|
|
@@ -2556,7 +2866,8 @@ var WorldOrbit = (() => {
|
|
|
2556
2866
|
system,
|
|
2557
2867
|
groups: structuredClone(document.groups ?? []),
|
|
2558
2868
|
relations: structuredClone(document.relations ?? []),
|
|
2559
|
-
|
|
2869
|
+
events: document.events.map(cloneWorldOrbitEvent),
|
|
2870
|
+
objects
|
|
2560
2871
|
};
|
|
2561
2872
|
}
|
|
2562
2873
|
function materializeDraftDocument(document) {
|
|
@@ -2581,8 +2892,9 @@ var WorldOrbit = (() => {
|
|
|
2581
2892
|
};
|
|
2582
2893
|
}
|
|
2583
2894
|
function createDraftDefaults(document, preset, projection) {
|
|
2895
|
+
const rawView = typeof document.system?.properties.view === "string" ? document.system.properties.view.toLowerCase() : null;
|
|
2584
2896
|
return {
|
|
2585
|
-
view:
|
|
2897
|
+
view: rawView === "topdown" || rawView === "isometric" || rawView === "orthographic" || rawView === "perspective" ? rawView : projection,
|
|
2586
2898
|
scale: typeof document.system?.properties.scale === "string" ? document.system.properties.scale : null,
|
|
2587
2899
|
units: typeof document.system?.properties.units === "string" ? document.system.properties.units : null,
|
|
2588
2900
|
preset,
|
|
@@ -2684,10 +2996,12 @@ var WorldOrbit = (() => {
|
|
|
2684
2996
|
summary: viewpoint.summary,
|
|
2685
2997
|
focusObjectId: viewpoint.objectId,
|
|
2686
2998
|
selectedObjectId: viewpoint.selectedObjectId,
|
|
2999
|
+
events: [...viewpoint.eventIds],
|
|
2687
3000
|
projection: viewpoint.projection,
|
|
2688
3001
|
preset: viewpoint.preset,
|
|
2689
3002
|
zoom: viewpoint.scale,
|
|
2690
3003
|
rotationDeg: viewpoint.rotationDeg,
|
|
3004
|
+
camera: viewpoint.camera ? { ...viewpoint.camera } : null,
|
|
2691
3005
|
layers: { ...viewpoint.layers },
|
|
2692
3006
|
filter: viewpoint.filter ? {
|
|
2693
3007
|
query: viewpoint.filter.query,
|
|
@@ -2716,6 +3030,75 @@ var WorldOrbit = (() => {
|
|
|
2716
3030
|
info: { ...object.info }
|
|
2717
3031
|
};
|
|
2718
3032
|
}
|
|
3033
|
+
function cloneWorldOrbitEvent(event) {
|
|
3034
|
+
return {
|
|
3035
|
+
...event,
|
|
3036
|
+
participantObjectIds: [...event.participantObjectIds],
|
|
3037
|
+
tags: [...event.tags],
|
|
3038
|
+
positions: event.positions.map(cloneWorldOrbitEventPose)
|
|
3039
|
+
};
|
|
3040
|
+
}
|
|
3041
|
+
function cloneWorldOrbitEventPose(pose) {
|
|
3042
|
+
return {
|
|
3043
|
+
objectId: pose.objectId,
|
|
3044
|
+
placement: clonePlacement(pose.placement),
|
|
3045
|
+
inner: pose.inner ? { ...pose.inner } : void 0,
|
|
3046
|
+
outer: pose.outer ? { ...pose.outer } : void 0,
|
|
3047
|
+
epoch: pose.epoch ?? null,
|
|
3048
|
+
referencePlane: pose.referencePlane ?? null
|
|
3049
|
+
};
|
|
3050
|
+
}
|
|
3051
|
+
function clonePlacement(placement) {
|
|
3052
|
+
return placement ? structuredClone(placement) : null;
|
|
3053
|
+
}
|
|
3054
|
+
function applyEventPoseOverrides(objects, events, activeEventId) {
|
|
3055
|
+
if (!activeEventId) {
|
|
3056
|
+
return;
|
|
3057
|
+
}
|
|
3058
|
+
const event = events.find((entry) => entry.id === activeEventId);
|
|
3059
|
+
if (!event) {
|
|
3060
|
+
return;
|
|
3061
|
+
}
|
|
3062
|
+
const objectMap = new Map(objects.map((object) => [object.id, object]));
|
|
3063
|
+
const referencedIds = /* @__PURE__ */ new Set([
|
|
3064
|
+
...event.targetObjectId ? [event.targetObjectId] : [],
|
|
3065
|
+
...event.participantObjectIds,
|
|
3066
|
+
...event.positions.map((pose) => pose.objectId)
|
|
3067
|
+
]);
|
|
3068
|
+
for (const objectId of referencedIds) {
|
|
3069
|
+
const object = objectMap.get(objectId);
|
|
3070
|
+
if (!object) {
|
|
3071
|
+
continue;
|
|
3072
|
+
}
|
|
3073
|
+
if (event.epoch) {
|
|
3074
|
+
object.epoch = event.epoch;
|
|
3075
|
+
}
|
|
3076
|
+
if (event.referencePlane) {
|
|
3077
|
+
object.referencePlane = event.referencePlane;
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
for (const pose of event.positions) {
|
|
3081
|
+
const object = objectMap.get(pose.objectId);
|
|
3082
|
+
if (!object) {
|
|
3083
|
+
continue;
|
|
3084
|
+
}
|
|
3085
|
+
if (pose.placement) {
|
|
3086
|
+
object.placement = clonePlacement(pose.placement);
|
|
3087
|
+
}
|
|
3088
|
+
if (pose.inner) {
|
|
3089
|
+
object.properties.inner = { ...pose.inner };
|
|
3090
|
+
}
|
|
3091
|
+
if (pose.outer) {
|
|
3092
|
+
object.properties.outer = { ...pose.outer };
|
|
3093
|
+
}
|
|
3094
|
+
if (pose.epoch) {
|
|
3095
|
+
object.epoch = pose.epoch;
|
|
3096
|
+
}
|
|
3097
|
+
if (pose.referencePlane) {
|
|
3098
|
+
object.referencePlane = pose.referencePlane;
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
2719
3102
|
function cloneProperties(properties) {
|
|
2720
3103
|
const next = {};
|
|
2721
3104
|
for (const [key, value] of Object.entries(properties)) {
|
|
@@ -2797,6 +3180,18 @@ var WorldOrbit = (() => {
|
|
|
2797
3180
|
if (viewpoint.rotationDeg !== 0) {
|
|
2798
3181
|
info2[`${prefix}.rotation`] = String(viewpoint.rotationDeg);
|
|
2799
3182
|
}
|
|
3183
|
+
if (viewpoint.camera?.azimuth !== null) {
|
|
3184
|
+
info2[`${prefix}.camera.azimuth`] = String(viewpoint.camera?.azimuth);
|
|
3185
|
+
}
|
|
3186
|
+
if (viewpoint.camera?.elevation !== null) {
|
|
3187
|
+
info2[`${prefix}.camera.elevation`] = String(viewpoint.camera?.elevation);
|
|
3188
|
+
}
|
|
3189
|
+
if (viewpoint.camera?.roll !== null) {
|
|
3190
|
+
info2[`${prefix}.camera.roll`] = String(viewpoint.camera?.roll);
|
|
3191
|
+
}
|
|
3192
|
+
if (viewpoint.camera?.distance !== null) {
|
|
3193
|
+
info2[`${prefix}.camera.distance`] = String(viewpoint.camera?.distance);
|
|
3194
|
+
}
|
|
2800
3195
|
const serializedLayers = serializeViewpointLayers(viewpoint.layers);
|
|
2801
3196
|
if (serializedLayers) {
|
|
2802
3197
|
info2[`${prefix}.layers`] = serializedLayers;
|
|
@@ -2813,6 +3208,9 @@ var WorldOrbit = (() => {
|
|
|
2813
3208
|
if ((viewpoint.filter?.groupIds.length ?? 0) > 0) {
|
|
2814
3209
|
info2[`${prefix}.groups`] = viewpoint.filter?.groupIds.join(" ") ?? "";
|
|
2815
3210
|
}
|
|
3211
|
+
if (viewpoint.events.length > 0) {
|
|
3212
|
+
info2[`${prefix}.events`] = viewpoint.events.join(" ");
|
|
3213
|
+
}
|
|
2816
3214
|
}
|
|
2817
3215
|
for (const annotation of system.annotations) {
|
|
2818
3216
|
const prefix = `annotation.${annotation.id}`;
|
|
@@ -2837,7 +3235,7 @@ var WorldOrbit = (() => {
|
|
|
2837
3235
|
if (orbitFront !== void 0 || orbitBack !== void 0) {
|
|
2838
3236
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
2839
3237
|
}
|
|
2840
|
-
for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
|
|
3238
|
+
for (const key of ["background", "guides", "relations", "events", "objects", "labels", "metadata"]) {
|
|
2841
3239
|
if (layers[key] !== void 0) {
|
|
2842
3240
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
2843
3241
|
}
|
|
@@ -2890,26 +3288,26 @@ var WorldOrbit = (() => {
|
|
|
2890
3288
|
];
|
|
2891
3289
|
function formatDocument(document, options = {}) {
|
|
2892
3290
|
const schema = options.schema ?? "auto";
|
|
2893
|
-
const useDraft = schema === "2.0" || schema === "2.1" || schema === "2.0-draft" || document.version === "2.0" || document.version === "2.1" || document.version === "2.0-draft";
|
|
3291
|
+
const useDraft = schema === "2.0" || schema === "2.1" || schema === "2.5" || schema === "2.0-draft" || document.version === "2.0" || document.version === "2.1" || document.version === "2.5" || document.version === "2.0-draft";
|
|
2894
3292
|
if (useDraft) {
|
|
2895
3293
|
if (schema === "2.0-draft") {
|
|
2896
|
-
const legacyDraftDocument = document.version === "2.0-draft" ? document : document.version === "2.0" || document.version === "2.1" ? {
|
|
3294
|
+
const legacyDraftDocument = document.version === "2.0-draft" ? document : document.version === "2.0" || document.version === "2.1" || document.version === "2.5" ? {
|
|
2897
3295
|
...document,
|
|
2898
3296
|
version: "2.0-draft",
|
|
2899
3297
|
schemaVersion: "2.0-draft"
|
|
2900
3298
|
} : upgradeDocumentToDraftV2(document);
|
|
2901
3299
|
return formatDraftDocument(legacyDraftDocument);
|
|
2902
3300
|
}
|
|
2903
|
-
const atlasDocument = document.version === "2.0" || document.version === "2.1" ? document : document.version === "2.0-draft" ? {
|
|
3301
|
+
const atlasDocument = document.version === "2.0" || document.version === "2.1" || document.version === "2.5" ? document : document.version === "2.0-draft" ? {
|
|
2904
3302
|
...document,
|
|
2905
3303
|
version: "2.0",
|
|
2906
3304
|
schemaVersion: "2.0"
|
|
2907
3305
|
} : upgradeDocumentToV2(document);
|
|
2908
|
-
if (schema === "2.1" && atlasDocument.version !==
|
|
3306
|
+
if ((schema === "2.0" || schema === "2.1" || schema === "2.5") && atlasDocument.version !== schema) {
|
|
2909
3307
|
return formatAtlasDocument({
|
|
2910
3308
|
...atlasDocument,
|
|
2911
|
-
version:
|
|
2912
|
-
schemaVersion:
|
|
3309
|
+
version: schema,
|
|
3310
|
+
schemaVersion: schema
|
|
2913
3311
|
});
|
|
2914
3312
|
}
|
|
2915
3313
|
return formatAtlasDocument(atlasDocument);
|
|
@@ -2941,6 +3339,10 @@ var WorldOrbit = (() => {
|
|
|
2941
3339
|
lines.push("");
|
|
2942
3340
|
lines.push(...formatAtlasRelation(relation));
|
|
2943
3341
|
}
|
|
3342
|
+
for (const event of [...document.events].sort(compareIdLike)) {
|
|
3343
|
+
lines.push("");
|
|
3344
|
+
lines.push(...formatAtlasEvent(event));
|
|
3345
|
+
}
|
|
2944
3346
|
const sortedObjects = [...document.objects].sort(compareObjects);
|
|
2945
3347
|
if (sortedObjects.length > 0 && lines.at(-1) !== "") {
|
|
2946
3348
|
lines.push("");
|
|
@@ -2971,6 +3373,10 @@ var WorldOrbit = (() => {
|
|
|
2971
3373
|
lines.push("");
|
|
2972
3374
|
lines.push(...formatAtlasRelation(relation));
|
|
2973
3375
|
}
|
|
3376
|
+
for (const event of [...legacy.events].sort(compareIdLike)) {
|
|
3377
|
+
lines.push("");
|
|
3378
|
+
lines.push(...formatAtlasEvent(event));
|
|
3379
|
+
}
|
|
2974
3380
|
const sortedObjects = [...legacy.objects].sort(compareObjects);
|
|
2975
3381
|
if (sortedObjects.length > 0 && lines.at(-1) !== "") {
|
|
2976
3382
|
lines.push("");
|
|
@@ -3178,10 +3584,28 @@ var WorldOrbit = (() => {
|
|
|
3178
3584
|
if (viewpoint.rotationDeg !== 0) {
|
|
3179
3585
|
lines.push(` rotation ${viewpoint.rotationDeg}`);
|
|
3180
3586
|
}
|
|
3587
|
+
if (viewpoint.camera && hasCameraValues(viewpoint.camera)) {
|
|
3588
|
+
lines.push(" camera");
|
|
3589
|
+
if (viewpoint.camera.azimuth !== null) {
|
|
3590
|
+
lines.push(` azimuth ${viewpoint.camera.azimuth}`);
|
|
3591
|
+
}
|
|
3592
|
+
if (viewpoint.camera.elevation !== null) {
|
|
3593
|
+
lines.push(` elevation ${viewpoint.camera.elevation}`);
|
|
3594
|
+
}
|
|
3595
|
+
if (viewpoint.camera.roll !== null) {
|
|
3596
|
+
lines.push(` roll ${viewpoint.camera.roll}`);
|
|
3597
|
+
}
|
|
3598
|
+
if (viewpoint.camera.distance !== null) {
|
|
3599
|
+
lines.push(` distance ${viewpoint.camera.distance}`);
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3181
3602
|
const layerTokens = formatDraftLayers(viewpoint.layers);
|
|
3182
3603
|
if (layerTokens.length > 0) {
|
|
3183
3604
|
lines.push(` layers ${layerTokens.join(" ")}`);
|
|
3184
3605
|
}
|
|
3606
|
+
if (viewpoint.events.length > 0) {
|
|
3607
|
+
lines.push(` events ${viewpoint.events.join(" ")}`);
|
|
3608
|
+
}
|
|
3185
3609
|
if (viewpoint.filter) {
|
|
3186
3610
|
lines.push(" filter");
|
|
3187
3611
|
if (viewpoint.filter.query) {
|
|
@@ -3254,6 +3678,65 @@ var WorldOrbit = (() => {
|
|
|
3254
3678
|
}
|
|
3255
3679
|
return lines;
|
|
3256
3680
|
}
|
|
3681
|
+
function formatAtlasEvent(event) {
|
|
3682
|
+
const lines = [`event ${event.id}`, ` kind ${quoteIfNeeded(event.kind)}`];
|
|
3683
|
+
if (event.label) {
|
|
3684
|
+
lines.push(` label ${quoteIfNeeded(event.label)}`);
|
|
3685
|
+
}
|
|
3686
|
+
if (event.summary) {
|
|
3687
|
+
lines.push(` summary ${quoteIfNeeded(event.summary)}`);
|
|
3688
|
+
}
|
|
3689
|
+
if (event.targetObjectId) {
|
|
3690
|
+
lines.push(` target ${event.targetObjectId}`);
|
|
3691
|
+
}
|
|
3692
|
+
if (event.participantObjectIds.length > 0) {
|
|
3693
|
+
lines.push(` participants ${event.participantObjectIds.join(" ")}`);
|
|
3694
|
+
}
|
|
3695
|
+
if (event.timing) {
|
|
3696
|
+
lines.push(` timing ${quoteIfNeeded(event.timing)}`);
|
|
3697
|
+
}
|
|
3698
|
+
if (event.visibility) {
|
|
3699
|
+
lines.push(` visibility ${quoteIfNeeded(event.visibility)}`);
|
|
3700
|
+
}
|
|
3701
|
+
if (event.epoch) {
|
|
3702
|
+
lines.push(` epoch ${quoteIfNeeded(event.epoch)}`);
|
|
3703
|
+
}
|
|
3704
|
+
if (event.referencePlane) {
|
|
3705
|
+
lines.push(` referencePlane ${quoteIfNeeded(event.referencePlane)}`);
|
|
3706
|
+
}
|
|
3707
|
+
if (event.tags.length > 0) {
|
|
3708
|
+
lines.push(` tags ${event.tags.map(quoteIfNeeded).join(" ")}`);
|
|
3709
|
+
}
|
|
3710
|
+
if (event.color) {
|
|
3711
|
+
lines.push(` color ${quoteIfNeeded(event.color)}`);
|
|
3712
|
+
}
|
|
3713
|
+
if (event.hidden) {
|
|
3714
|
+
lines.push(" hidden true");
|
|
3715
|
+
}
|
|
3716
|
+
if (event.positions.length > 0) {
|
|
3717
|
+
lines.push("");
|
|
3718
|
+
lines.push(" positions");
|
|
3719
|
+
for (const pose of [...event.positions].sort(comparePoseObjectId)) {
|
|
3720
|
+
lines.push(` pose ${pose.objectId}`);
|
|
3721
|
+
for (const fieldLine of formatEventPoseFields(pose)) {
|
|
3722
|
+
lines.push(` ${fieldLine}`);
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
return lines;
|
|
3727
|
+
}
|
|
3728
|
+
function formatEventPoseFields(pose) {
|
|
3729
|
+
return [
|
|
3730
|
+
...formatPlacement(pose.placement),
|
|
3731
|
+
...pose.epoch ? [`epoch ${quoteIfNeeded(pose.epoch)}`] : [],
|
|
3732
|
+
...pose.referencePlane ? [`referencePlane ${quoteIfNeeded(pose.referencePlane)}`] : [],
|
|
3733
|
+
...formatOptionalUnit("inner", pose.inner),
|
|
3734
|
+
...formatOptionalUnit("outer", pose.outer)
|
|
3735
|
+
];
|
|
3736
|
+
}
|
|
3737
|
+
function hasCameraValues(camera) {
|
|
3738
|
+
return camera.azimuth !== null || camera.elevation !== null || camera.roll !== null || camera.distance !== null;
|
|
3739
|
+
}
|
|
3257
3740
|
function formatValue(value) {
|
|
3258
3741
|
if (Array.isArray(value)) {
|
|
3259
3742
|
return value.map((item) => quoteIfNeeded(item)).join(" ");
|
|
@@ -3295,7 +3778,7 @@ var WorldOrbit = (() => {
|
|
|
3295
3778
|
if (orbitFront !== void 0 || orbitBack !== void 0) {
|
|
3296
3779
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
3297
3780
|
}
|
|
3298
|
-
for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
|
|
3781
|
+
for (const key of ["background", "guides", "relations", "events", "objects", "labels", "metadata"]) {
|
|
3299
3782
|
if (layers[key] !== void 0) {
|
|
3300
3783
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
3301
3784
|
}
|
|
@@ -3323,6 +3806,9 @@ var WorldOrbit = (() => {
|
|
|
3323
3806
|
function compareIdLike(left, right) {
|
|
3324
3807
|
return left.id.localeCompare(right.id);
|
|
3325
3808
|
}
|
|
3809
|
+
function comparePoseObjectId(left, right) {
|
|
3810
|
+
return left.objectId.localeCompare(right.objectId);
|
|
3811
|
+
}
|
|
3326
3812
|
function objectTypeIndex(objectType) {
|
|
3327
3813
|
switch (objectType) {
|
|
3328
3814
|
case "star":
|
|
@@ -3518,6 +4004,7 @@ var WorldOrbit = (() => {
|
|
|
3518
4004
|
const diagnostics = [];
|
|
3519
4005
|
const objectMap = new Map(document.objects.map((object) => [object.id, object]));
|
|
3520
4006
|
const groupIds = new Set(document.groups.map((group) => group.id));
|
|
4007
|
+
const eventIds = new Set(document.events.map((event) => event.id));
|
|
3521
4008
|
if (!document.system) {
|
|
3522
4009
|
diagnostics.push(error("validate.system.required", "Atlas documents must declare exactly one system."));
|
|
3523
4010
|
}
|
|
@@ -3527,6 +4014,7 @@ var WorldOrbit = (() => {
|
|
|
3527
4014
|
["viewpoint", document.system?.viewpoints.map((viewpoint) => viewpoint.id) ?? []],
|
|
3528
4015
|
["annotation", document.system?.annotations.map((annotation) => annotation.id) ?? []],
|
|
3529
4016
|
["relation", document.relations.map((relation) => relation.id)],
|
|
4017
|
+
["event", document.events.map((event) => event.id)],
|
|
3530
4018
|
["object", document.objects.map((object) => object.id)]
|
|
3531
4019
|
]) {
|
|
3532
4020
|
for (const id of ids) {
|
|
@@ -3542,11 +4030,14 @@ var WorldOrbit = (() => {
|
|
|
3542
4030
|
validateRelation(relation, objectMap, diagnostics);
|
|
3543
4031
|
}
|
|
3544
4032
|
for (const viewpoint of document.system?.viewpoints ?? []) {
|
|
3545
|
-
|
|
4033
|
+
validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, diagnostics, objectMap);
|
|
3546
4034
|
}
|
|
3547
4035
|
for (const object of document.objects) {
|
|
3548
4036
|
validateObject(object, document.system, objectMap, groupIds, diagnostics);
|
|
3549
4037
|
}
|
|
4038
|
+
for (const event of document.events) {
|
|
4039
|
+
validateEvent(event, document.system, objectMap, diagnostics);
|
|
4040
|
+
}
|
|
3550
4041
|
return diagnostics;
|
|
3551
4042
|
}
|
|
3552
4043
|
function validateRelation(relation, objectMap, diagnostics) {
|
|
@@ -3564,15 +4055,24 @@ var WorldOrbit = (() => {
|
|
|
3564
4055
|
diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
|
|
3565
4056
|
}
|
|
3566
4057
|
}
|
|
3567
|
-
function
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
4058
|
+
function validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, diagnostics, objectMap) {
|
|
4059
|
+
const filter = viewpoint.filter;
|
|
4060
|
+
if (sourceSchemaVersion === "2.1" || sourceSchemaVersion === "2.5") {
|
|
4061
|
+
if (filter) {
|
|
4062
|
+
for (const groupId of filter.groupIds) {
|
|
4063
|
+
if (!groupIds.has(groupId)) {
|
|
4064
|
+
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpoint.id}".`, void 0, `viewpoint.${viewpoint.id}.groups`));
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
for (const eventId of viewpoint.events ?? []) {
|
|
4069
|
+
if (!eventIds.has(eventId)) {
|
|
4070
|
+
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${viewpoint.id}".`, void 0, `viewpoint.${viewpoint.id}.events`));
|
|
4071
|
+
}
|
|
3574
4072
|
}
|
|
3575
4073
|
}
|
|
4074
|
+
validateProjection(viewpoint.projection, diagnostics, `viewpoint.${viewpoint.id}.projection`, viewpoint.id);
|
|
4075
|
+
validateCamera(viewpoint.camera, viewpoint.projection, viewpoint.rotationDeg, diagnostics, viewpoint.id, viewpoint.focusObjectId, viewpoint.selectedObjectId, filter, objectMap);
|
|
3576
4076
|
}
|
|
3577
4077
|
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
3578
4078
|
const placement = object.placement;
|
|
@@ -3585,6 +4085,12 @@ var WorldOrbit = (() => {
|
|
|
3585
4085
|
}
|
|
3586
4086
|
}
|
|
3587
4087
|
}
|
|
4088
|
+
if (typeof object.epoch === "string" && !object.epoch.trim()) {
|
|
4089
|
+
diagnostics.push(warn("validate.epoch.empty", `Object "${object.id}" defines an empty epoch string.`, object.id, "epoch"));
|
|
4090
|
+
}
|
|
4091
|
+
if (typeof object.referencePlane === "string" && !object.referencePlane.trim()) {
|
|
4092
|
+
diagnostics.push(warn("validate.referencePlane.empty", `Object "${object.id}" defines an empty reference plane string.`, object.id, "referencePlane"));
|
|
4093
|
+
}
|
|
3588
4094
|
if (orbitPlacement) {
|
|
3589
4095
|
if (!objectMap.has(orbitPlacement.target)) {
|
|
3590
4096
|
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
@@ -3656,6 +4162,122 @@ var WorldOrbit = (() => {
|
|
|
3656
4162
|
}
|
|
3657
4163
|
}
|
|
3658
4164
|
}
|
|
4165
|
+
function validateEvent(event, system, objectMap, diagnostics) {
|
|
4166
|
+
const fieldPrefix = `event.${event.id}`;
|
|
4167
|
+
const referencedIds = /* @__PURE__ */ new Set();
|
|
4168
|
+
if (!event.kind.trim()) {
|
|
4169
|
+
diagnostics.push(error("validate.event.kind.required", `Event "${event.id}" is missing a "kind" value.`, void 0, `${fieldPrefix}.kind`));
|
|
4170
|
+
}
|
|
4171
|
+
if (typeof event.epoch === "string" && !event.epoch.trim()) {
|
|
4172
|
+
diagnostics.push(warn("validate.event.epoch.empty", `Event "${event.id}" defines an empty epoch string.`, void 0, `${fieldPrefix}.epoch`));
|
|
4173
|
+
}
|
|
4174
|
+
if (typeof event.referencePlane === "string" && !event.referencePlane.trim()) {
|
|
4175
|
+
diagnostics.push(warn("validate.event.referencePlane.empty", `Event "${event.id}" defines an empty reference plane string.`, void 0, `${fieldPrefix}.referencePlane`));
|
|
4176
|
+
}
|
|
4177
|
+
if (!event.targetObjectId && event.participantObjectIds.length === 0) {
|
|
4178
|
+
diagnostics.push(error("validate.event.references.required", `Event "${event.id}" must define a "target" or at least one participant.`, void 0, `${fieldPrefix}.participants`));
|
|
4179
|
+
}
|
|
4180
|
+
if (event.targetObjectId) {
|
|
4181
|
+
referencedIds.add(event.targetObjectId);
|
|
4182
|
+
if (!objectMap.has(event.targetObjectId)) {
|
|
4183
|
+
diagnostics.push(error("validate.event.target.unknown", `Unknown event target "${event.targetObjectId}" on "${event.id}".`, void 0, `${fieldPrefix}.target`));
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
const seenParticipants = /* @__PURE__ */ new Set();
|
|
4187
|
+
for (const participantId of event.participantObjectIds) {
|
|
4188
|
+
referencedIds.add(participantId);
|
|
4189
|
+
if (seenParticipants.has(participantId)) {
|
|
4190
|
+
diagnostics.push(warn("validate.event.participants.duplicate", `Event "${event.id}" repeats participant "${participantId}".`, void 0, `${fieldPrefix}.participants`));
|
|
4191
|
+
continue;
|
|
4192
|
+
}
|
|
4193
|
+
seenParticipants.add(participantId);
|
|
4194
|
+
if (!objectMap.has(participantId)) {
|
|
4195
|
+
diagnostics.push(error("validate.event.participants.unknown", `Unknown event participant "${participantId}" on "${event.id}".`, void 0, `${fieldPrefix}.participants`));
|
|
4196
|
+
}
|
|
4197
|
+
}
|
|
4198
|
+
if (event.targetObjectId && event.participantObjectIds.length > 0 && !event.participantObjectIds.includes(event.targetObjectId)) {
|
|
4199
|
+
diagnostics.push(warn("validate.event.target.notParticipant", `Event "${event.id}" defines a target outside its participants list.`, void 0, `${fieldPrefix}.target`));
|
|
4200
|
+
}
|
|
4201
|
+
if (event.positions.length === 0) {
|
|
4202
|
+
diagnostics.push(warn("validate.event.positions.missing", `Event "${event.id}" has no positions block and cannot drive a scene snapshot.`, void 0, `${fieldPrefix}.positions`));
|
|
4203
|
+
}
|
|
4204
|
+
if (/(?:^|[-_])(solar-eclipse|lunar-eclipse|transit|occultation)(?:$|[-_])/.test(event.kind) && referencedIds.size < 3) {
|
|
4205
|
+
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`));
|
|
4206
|
+
}
|
|
4207
|
+
const poseIds = /* @__PURE__ */ new Set();
|
|
4208
|
+
for (const pose of event.positions) {
|
|
4209
|
+
const poseFieldPrefix = `${fieldPrefix}.pose.${pose.objectId}`;
|
|
4210
|
+
if (poseIds.has(pose.objectId)) {
|
|
4211
|
+
diagnostics.push(error("validate.event.pose.duplicate", `Event "${event.id}" defines "${pose.objectId}" more than once in positions.`, void 0, poseFieldPrefix));
|
|
4212
|
+
continue;
|
|
4213
|
+
}
|
|
4214
|
+
poseIds.add(pose.objectId);
|
|
4215
|
+
const object = objectMap.get(pose.objectId);
|
|
4216
|
+
if (!object) {
|
|
4217
|
+
diagnostics.push(error("validate.event.pose.object.unknown", `Unknown event pose object "${pose.objectId}" on "${event.id}".`, void 0, poseFieldPrefix));
|
|
4218
|
+
continue;
|
|
4219
|
+
}
|
|
4220
|
+
if (!referencedIds.has(pose.objectId)) {
|
|
4221
|
+
diagnostics.push(warn("validate.event.pose.unreferenced", `Event pose "${pose.objectId}" on "${event.id}" is not listed in target/participants.`, void 0, poseFieldPrefix));
|
|
4222
|
+
}
|
|
4223
|
+
validateEventPose(pose, object, event, system, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
4224
|
+
}
|
|
4225
|
+
const missingPoseIds = [...referencedIds].filter((objectId) => !poseIds.has(objectId));
|
|
4226
|
+
if (event.positions.length > 0 && missingPoseIds.length > 0) {
|
|
4227
|
+
diagnostics.push(warn("validate.event.positions.partial", `Event "${event.id}" leaves ${missingPoseIds.length} referenced object(s) on their base placement.`, void 0, `${fieldPrefix}.positions`));
|
|
4228
|
+
}
|
|
4229
|
+
}
|
|
4230
|
+
function validateEventPose(pose, object, event, system, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
4231
|
+
const placement = pose.placement;
|
|
4232
|
+
if (!placement) {
|
|
4233
|
+
diagnostics.push(error("validate.event.pose.placement.required", `Event "${eventId}" pose "${pose.objectId}" is missing a placement mode.`, void 0, fieldPrefix));
|
|
4234
|
+
return;
|
|
4235
|
+
}
|
|
4236
|
+
if (placement.mode === "orbit") {
|
|
4237
|
+
if (!objectMap.has(placement.target)) {
|
|
4238
|
+
diagnostics.push(error("validate.event.pose.orbit.target.unknown", `Unknown event orbit target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.orbit`));
|
|
4239
|
+
}
|
|
4240
|
+
if (placement.distance && placement.semiMajor) {
|
|
4241
|
+
diagnostics.push(error("validate.event.pose.orbit.distanceConflict", `Event "${eventId}" pose "${pose.objectId}" cannot declare both "distance" and "semiMajor".`, void 0, `${fieldPrefix}.distance`));
|
|
4242
|
+
}
|
|
4243
|
+
if (placement.phase && !resolveEffectiveEpoch(system, object, event, pose)) {
|
|
4244
|
+
diagnostics.push(warn("validate.event.pose.phase.epochMissing", `Event "${eventId}" pose "${pose.objectId}" sets "phase" without an effective epoch.`, void 0, `${fieldPrefix}.phase`));
|
|
4245
|
+
}
|
|
4246
|
+
if (placement.inclination && !resolveEffectiveReferencePlane(system, object, event, pose)) {
|
|
4247
|
+
diagnostics.push(warn("validate.event.pose.inclination.referencePlaneMissing", `Event "${eventId}" pose "${pose.objectId}" sets "inclination" without an effective reference plane.`, void 0, `${fieldPrefix}.inclination`));
|
|
4248
|
+
}
|
|
4249
|
+
if (placement.period && !massInSolar(objectMap.get(placement.target)?.properties.mass)) {
|
|
4250
|
+
diagnostics.push(warn("validate.event.pose.period.massMissing", `Event "${eventId}" pose "${pose.objectId}" sets "period" but its central mass cannot be derived.`, void 0, `${fieldPrefix}.period`));
|
|
4251
|
+
}
|
|
4252
|
+
return;
|
|
4253
|
+
}
|
|
4254
|
+
if (placement.mode === "surface") {
|
|
4255
|
+
const target = objectMap.get(placement.target);
|
|
4256
|
+
if (!target) {
|
|
4257
|
+
diagnostics.push(error("validate.event.pose.surface.target.unknown", `Unknown event surface target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.surface`));
|
|
4258
|
+
} else if (!SURFACE_TARGET_TYPES2.has(target.type)) {
|
|
4259
|
+
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`));
|
|
4260
|
+
}
|
|
4261
|
+
return;
|
|
4262
|
+
}
|
|
4263
|
+
if (placement.mode === "at") {
|
|
4264
|
+
if (object.type !== "structure" && object.type !== "phenomenon") {
|
|
4265
|
+
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`));
|
|
4266
|
+
}
|
|
4267
|
+
const reference = placement.reference;
|
|
4268
|
+
if (reference.kind === "named" && !objectMap.has(reference.name)) {
|
|
4269
|
+
diagnostics.push(error("validate.event.pose.at.target.unknown", `Unknown event at-reference target "${placement.target}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
4270
|
+
} else if (reference.kind === "anchor" && !objectMap.has(reference.objectId)) {
|
|
4271
|
+
diagnostics.push(error("validate.event.pose.anchor.target.unknown", `Unknown event anchor target "${reference.objectId}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
4272
|
+
} else if (reference.kind === "lagrange") {
|
|
4273
|
+
if (!objectMap.has(reference.primary)) {
|
|
4274
|
+
diagnostics.push(error("validate.event.pose.lagrange.primary.unknown", `Unknown event Lagrange target "${reference.primary}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
4275
|
+
} else if (reference.secondary && !objectMap.has(reference.secondary)) {
|
|
4276
|
+
diagnostics.push(error("validate.event.pose.lagrange.secondary.unknown", `Unknown event Lagrange target "${reference.secondary}" on "${eventId}:${pose.objectId}".`, void 0, `${fieldPrefix}.at`));
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
}
|
|
4280
|
+
}
|
|
3659
4281
|
function validateAtTarget(object, objectMap, diagnostics) {
|
|
3660
4282
|
const reference = object.placement?.mode === "at" ? object.placement.reference : null;
|
|
3661
4283
|
if (!reference) {
|
|
@@ -3761,6 +4383,52 @@ var WorldOrbit = (() => {
|
|
|
3761
4383
|
return null;
|
|
3762
4384
|
}
|
|
3763
4385
|
}
|
|
4386
|
+
function validateProjection(projection, diagnostics, field, viewpointId) {
|
|
4387
|
+
if (projection !== "topdown" && projection !== "isometric" && projection !== "orthographic" && projection !== "perspective") {
|
|
4388
|
+
diagnostics.push(error("validate.viewpoint.projection.invalid", `Unknown projection "${String(projection)}" in viewpoint "${viewpointId}".`, void 0, field));
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
function validateCamera(camera, projection, rotationDeg, diagnostics, viewpointId, focusObjectId, selectedObjectId, filter, objectMap) {
|
|
4392
|
+
if (!camera) {
|
|
4393
|
+
return;
|
|
4394
|
+
}
|
|
4395
|
+
const prefix = `viewpoint.${viewpointId}.camera`;
|
|
4396
|
+
for (const [key, value] of [
|
|
4397
|
+
["azimuth", camera.azimuth],
|
|
4398
|
+
["elevation", camera.elevation],
|
|
4399
|
+
["roll", camera.roll],
|
|
4400
|
+
["distance", camera.distance]
|
|
4401
|
+
]) {
|
|
4402
|
+
if (value !== null && (!Number.isFinite(value) || key === "distance" && value <= 0)) {
|
|
4403
|
+
diagnostics.push(error("validate.viewpoint.camera.invalid", `Invalid camera ${key} "${String(value)}" in viewpoint "${viewpointId}".`, void 0, `${prefix}.${key}`));
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
if (camera.distance !== null && projection !== "perspective") {
|
|
4407
|
+
diagnostics.push(warn("validate.viewpoint.camera.distance.partialEffect", `Camera "distance" only has a semantic effect in perspective viewpoints; "${viewpointId}" uses "${projection}".`, void 0, `${prefix}.distance`));
|
|
4408
|
+
}
|
|
4409
|
+
if (projection === "topdown" && (camera.elevation !== null || camera.roll !== null)) {
|
|
4410
|
+
diagnostics.push(warn("validate.viewpoint.camera.topdownPartial", `Camera elevation/roll on topdown viewpoint "${viewpointId}" are currently stored for future 3D use and only partially affect 2D rendering.`, void 0, prefix));
|
|
4411
|
+
}
|
|
4412
|
+
if (projection === "isometric" && camera.elevation !== null) {
|
|
4413
|
+
diagnostics.push(info("validate.viewpoint.camera.isometricStored", `Camera elevation on isometric viewpoint "${viewpointId}" is preserved semantically for future 3D rendering.`, void 0, `${prefix}.elevation`));
|
|
4414
|
+
}
|
|
4415
|
+
if (camera.azimuth !== null && camera.azimuth !== 0 && rotationDeg !== 0) {
|
|
4416
|
+
diagnostics.push(warn("validate.viewpoint.rotation.cameraOverlap", `Viewpoint "${viewpointId}" uses camera.azimuth; keep "rotation" only for 2D screen rotation to avoid ambiguity.`, void 0, `${prefix}.azimuth`));
|
|
4417
|
+
}
|
|
4418
|
+
const hasAnchor = focusObjectId !== null && objectMap.has(focusObjectId) || selectedObjectId !== null && objectMap.has(selectedObjectId) || !!filter;
|
|
4419
|
+
if (!hasAnchor) {
|
|
4420
|
+
diagnostics.push(info("validate.viewpoint.camera.anchorMissing", `Viewpoint "${viewpointId}" stores camera settings without a focus object, selection, or filter anchor.`, void 0, prefix));
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
function resolveEffectiveEpoch(system, object, event, pose) {
|
|
4424
|
+
return normalizeOptionalContextString(pose?.epoch) ?? normalizeOptionalContextString(event?.epoch) ?? normalizeOptionalContextString(object.epoch) ?? normalizeOptionalContextString(system?.epoch) ?? null;
|
|
4425
|
+
}
|
|
4426
|
+
function resolveEffectiveReferencePlane(system, object, event, pose) {
|
|
4427
|
+
return normalizeOptionalContextString(pose?.referencePlane) ?? normalizeOptionalContextString(event?.referencePlane) ?? normalizeOptionalContextString(object.referencePlane) ?? normalizeOptionalContextString(system?.referencePlane) ?? null;
|
|
4428
|
+
}
|
|
4429
|
+
function normalizeOptionalContextString(value) {
|
|
4430
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
4431
|
+
}
|
|
3764
4432
|
function toleranceForField(object, field) {
|
|
3765
4433
|
const tolerance = object.tolerances?.find((entry) => entry.field === field)?.value;
|
|
3766
4434
|
if (typeof tolerance === "number") {
|
|
@@ -3856,6 +4524,23 @@ var WorldOrbit = (() => {
|
|
|
3856
4524
|
});
|
|
3857
4525
|
}
|
|
3858
4526
|
var DRAFT_OBJECT_FIELD_KEYS = new Set(DRAFT_OBJECT_FIELD_SPECS.keys());
|
|
4527
|
+
var EVENT_POSE_FIELD_KEYS = /* @__PURE__ */ new Set([
|
|
4528
|
+
"orbit",
|
|
4529
|
+
"distance",
|
|
4530
|
+
"semiMajor",
|
|
4531
|
+
"eccentricity",
|
|
4532
|
+
"period",
|
|
4533
|
+
"angle",
|
|
4534
|
+
"inclination",
|
|
4535
|
+
"phase",
|
|
4536
|
+
"at",
|
|
4537
|
+
"surface",
|
|
4538
|
+
"free",
|
|
4539
|
+
"inner",
|
|
4540
|
+
"outer",
|
|
4541
|
+
"epoch",
|
|
4542
|
+
"referencePlane"
|
|
4543
|
+
]);
|
|
3859
4544
|
function parseWorldOrbitAtlas(source) {
|
|
3860
4545
|
return parseAtlasSource(source);
|
|
3861
4546
|
}
|
|
@@ -3873,12 +4558,15 @@ var WorldOrbit = (() => {
|
|
|
3873
4558
|
const objectNodes = [];
|
|
3874
4559
|
const groups = [];
|
|
3875
4560
|
const relations = [];
|
|
4561
|
+
const events = [];
|
|
4562
|
+
const eventPoseNodes = /* @__PURE__ */ new Map();
|
|
3876
4563
|
let sawDefaults = false;
|
|
3877
4564
|
let sawAtlas = false;
|
|
3878
4565
|
const viewpointIds = /* @__PURE__ */ new Set();
|
|
3879
4566
|
const annotationIds = /* @__PURE__ */ new Set();
|
|
3880
4567
|
const groupIds = /* @__PURE__ */ new Set();
|
|
3881
4568
|
const relationIds = /* @__PURE__ */ new Set();
|
|
4569
|
+
const eventIds = /* @__PURE__ */ new Set();
|
|
3882
4570
|
for (let index = 0; index < lines.length; index++) {
|
|
3883
4571
|
const rawLine = lines[index];
|
|
3884
4572
|
const lineNumber = index + 1;
|
|
@@ -3896,7 +4584,7 @@ var WorldOrbit = (() => {
|
|
|
3896
4584
|
if (!sawSchemaHeader) {
|
|
3897
4585
|
sourceSchemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
3898
4586
|
sawSchemaHeader = true;
|
|
3899
|
-
if (prepared.comments.length > 0 && sourceSchemaVersion
|
|
4587
|
+
if (prepared.comments.length > 0 && isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
3900
4588
|
diagnostics.push({
|
|
3901
4589
|
code: "parse.schema21.commentCompatibility",
|
|
3902
4590
|
severity: "warning",
|
|
@@ -3909,7 +4597,7 @@ var WorldOrbit = (() => {
|
|
|
3909
4597
|
continue;
|
|
3910
4598
|
}
|
|
3911
4599
|
if (indent === 0) {
|
|
3912
|
-
section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, { sawDefaults, sawAtlas });
|
|
4600
|
+
section = startTopLevelSection(tokens, lineNumber, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, { sawDefaults, sawAtlas });
|
|
3913
4601
|
if (section.kind === "system") {
|
|
3914
4602
|
system = section.system;
|
|
3915
4603
|
} else if (section.kind === "defaults") {
|
|
@@ -3928,6 +4616,7 @@ var WorldOrbit = (() => {
|
|
|
3928
4616
|
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0"');
|
|
3929
4617
|
}
|
|
3930
4618
|
const objects = objectNodes.map((node) => normalizeDraftObject(node, sourceSchemaVersion, diagnostics));
|
|
4619
|
+
const normalizedEvents = events.map((event) => normalizeDraftEvent(event, eventPoseNodes.get(event.id) ?? []));
|
|
3931
4620
|
const outputVersion = forcedOutputVersion ?? (sourceSchemaVersion === "2.0-draft" ? "2.0" : sourceSchemaVersion);
|
|
3932
4621
|
const baseDocument = {
|
|
3933
4622
|
format: "worldorbit",
|
|
@@ -3935,6 +4624,7 @@ var WorldOrbit = (() => {
|
|
|
3935
4624
|
system,
|
|
3936
4625
|
groups,
|
|
3937
4626
|
relations,
|
|
4627
|
+
events: normalizedEvents,
|
|
3938
4628
|
objects,
|
|
3939
4629
|
diagnostics
|
|
3940
4630
|
};
|
|
@@ -3964,13 +4654,13 @@ var WorldOrbit = (() => {
|
|
|
3964
4654
|
return document;
|
|
3965
4655
|
}
|
|
3966
4656
|
function assertDraftSchemaHeader(tokens, line) {
|
|
3967
|
-
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1"].includes(tokens[1].value.toLowerCase())) {
|
|
3968
|
-
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
4657
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1", "2.5"].includes(tokens[1].value.toLowerCase())) {
|
|
4658
|
+
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", "schema 2.5", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
3969
4659
|
}
|
|
3970
4660
|
const version = tokens[1].value.toLowerCase();
|
|
3971
|
-
return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
4661
|
+
return version === "2.5" ? "2.5" : version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
3972
4662
|
}
|
|
3973
|
-
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, viewpointIds, annotationIds, groupIds, relationIds, flags) {
|
|
4663
|
+
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, flags) {
|
|
3974
4664
|
const keyword = tokens[0]?.value.toLowerCase();
|
|
3975
4665
|
switch (keyword) {
|
|
3976
4666
|
case "system":
|
|
@@ -3988,6 +4678,8 @@ var WorldOrbit = (() => {
|
|
|
3988
4678
|
return {
|
|
3989
4679
|
kind: "defaults",
|
|
3990
4680
|
system,
|
|
4681
|
+
sourceSchemaVersion,
|
|
4682
|
+
diagnostics,
|
|
3991
4683
|
seenFields: /* @__PURE__ */ new Set()
|
|
3992
4684
|
};
|
|
3993
4685
|
case "atlas":
|
|
@@ -4007,7 +4699,7 @@ var WorldOrbit = (() => {
|
|
|
4007
4699
|
if (!system) {
|
|
4008
4700
|
throw new WorldOrbitError('Atlas section "viewpoint" requires a preceding system declaration', line, tokens[0].column);
|
|
4009
4701
|
}
|
|
4010
|
-
return startViewpointSection(tokens, line, system, viewpointIds);
|
|
4702
|
+
return startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaVersion, diagnostics);
|
|
4011
4703
|
case "annotation":
|
|
4012
4704
|
if (!system) {
|
|
4013
4705
|
throw new WorldOrbitError('Atlas section "annotation" requires a preceding system declaration', line, tokens[0].column);
|
|
@@ -4019,6 +4711,9 @@ var WorldOrbit = (() => {
|
|
|
4019
4711
|
case "relation":
|
|
4020
4712
|
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "relation", { line, column: tokens[0].column });
|
|
4021
4713
|
return startRelationSection(tokens, line, relations, relationIds);
|
|
4714
|
+
case "event":
|
|
4715
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "event", { line, column: tokens[0].column });
|
|
4716
|
+
return startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourceSchemaVersion, diagnostics);
|
|
4022
4717
|
case "object":
|
|
4023
4718
|
return startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes);
|
|
4024
4719
|
default:
|
|
@@ -4055,7 +4750,7 @@ var WorldOrbit = (() => {
|
|
|
4055
4750
|
seenFields: /* @__PURE__ */ new Set()
|
|
4056
4751
|
};
|
|
4057
4752
|
}
|
|
4058
|
-
function startViewpointSection(tokens, line, system, viewpointIds) {
|
|
4753
|
+
function startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaVersion, diagnostics) {
|
|
4059
4754
|
if (tokens.length !== 2) {
|
|
4060
4755
|
throw new WorldOrbitError("Invalid viewpoint declaration", line, tokens[0]?.column ?? 1);
|
|
4061
4756
|
}
|
|
@@ -4072,10 +4767,12 @@ var WorldOrbit = (() => {
|
|
|
4072
4767
|
summary: "",
|
|
4073
4768
|
focusObjectId: null,
|
|
4074
4769
|
selectedObjectId: null,
|
|
4770
|
+
events: [],
|
|
4075
4771
|
projection: system.defaults.view,
|
|
4076
4772
|
preset: system.defaults.preset,
|
|
4077
4773
|
zoom: null,
|
|
4078
4774
|
rotationDeg: 0,
|
|
4775
|
+
camera: null,
|
|
4079
4776
|
layers: {},
|
|
4080
4777
|
filter: null
|
|
4081
4778
|
};
|
|
@@ -4084,10 +4781,15 @@ var WorldOrbit = (() => {
|
|
|
4084
4781
|
return {
|
|
4085
4782
|
kind: "viewpoint",
|
|
4086
4783
|
viewpoint,
|
|
4784
|
+
sourceSchemaVersion,
|
|
4785
|
+
diagnostics,
|
|
4087
4786
|
seenFields: /* @__PURE__ */ new Set(),
|
|
4088
4787
|
inFilter: false,
|
|
4089
4788
|
filterIndent: null,
|
|
4090
|
-
seenFilterFields: /* @__PURE__ */ new Set()
|
|
4789
|
+
seenFilterFields: /* @__PURE__ */ new Set(),
|
|
4790
|
+
inCamera: false,
|
|
4791
|
+
cameraIndent: null,
|
|
4792
|
+
seenCameraFields: /* @__PURE__ */ new Set()
|
|
4091
4793
|
};
|
|
4092
4794
|
}
|
|
4093
4795
|
function startAnnotationSection(tokens, line, system, annotationIds) {
|
|
@@ -4174,6 +4876,51 @@ var WorldOrbit = (() => {
|
|
|
4174
4876
|
seenFields: /* @__PURE__ */ new Set()
|
|
4175
4877
|
};
|
|
4176
4878
|
}
|
|
4879
|
+
function startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourceSchemaVersion, diagnostics) {
|
|
4880
|
+
if (tokens.length !== 2) {
|
|
4881
|
+
throw new WorldOrbitError("Invalid event declaration", line, tokens[0]?.column ?? 1);
|
|
4882
|
+
}
|
|
4883
|
+
const id = normalizeIdentifier2(tokens[1].value);
|
|
4884
|
+
if (!id) {
|
|
4885
|
+
throw new WorldOrbitError("Event id must not be empty", line, tokens[1].column);
|
|
4886
|
+
}
|
|
4887
|
+
if (eventIds.has(id)) {
|
|
4888
|
+
throw new WorldOrbitError(`Duplicate event id "${id}"`, line, tokens[1].column);
|
|
4889
|
+
}
|
|
4890
|
+
const event = {
|
|
4891
|
+
id,
|
|
4892
|
+
kind: "",
|
|
4893
|
+
label: humanizeIdentifier3(id),
|
|
4894
|
+
summary: null,
|
|
4895
|
+
targetObjectId: null,
|
|
4896
|
+
participantObjectIds: [],
|
|
4897
|
+
timing: null,
|
|
4898
|
+
visibility: null,
|
|
4899
|
+
epoch: null,
|
|
4900
|
+
referencePlane: null,
|
|
4901
|
+
tags: [],
|
|
4902
|
+
color: null,
|
|
4903
|
+
hidden: false,
|
|
4904
|
+
positions: []
|
|
4905
|
+
};
|
|
4906
|
+
const rawPoses = [];
|
|
4907
|
+
events.push(event);
|
|
4908
|
+
eventPoseNodes.set(id, rawPoses);
|
|
4909
|
+
eventIds.add(id);
|
|
4910
|
+
return {
|
|
4911
|
+
kind: "event",
|
|
4912
|
+
event,
|
|
4913
|
+
sourceSchemaVersion,
|
|
4914
|
+
diagnostics,
|
|
4915
|
+
seenFields: /* @__PURE__ */ new Set(),
|
|
4916
|
+
rawPoses,
|
|
4917
|
+
inPositions: false,
|
|
4918
|
+
positionsIndent: null,
|
|
4919
|
+
activePose: null,
|
|
4920
|
+
poseIndent: null,
|
|
4921
|
+
activePoseSeenFields: /* @__PURE__ */ new Set()
|
|
4922
|
+
};
|
|
4923
|
+
}
|
|
4177
4924
|
function startObjectSection(tokens, line, sourceSchemaVersion, diagnostics, objectNodes) {
|
|
4178
4925
|
if (tokens.length < 3) {
|
|
4179
4926
|
throw new WorldOrbitError("Invalid atlas object declaration", line, tokens[0]?.column ?? 1);
|
|
@@ -4230,6 +4977,9 @@ var WorldOrbit = (() => {
|
|
|
4230
4977
|
case "relation":
|
|
4231
4978
|
applyRelationField(section, tokens, line);
|
|
4232
4979
|
return;
|
|
4980
|
+
case "event":
|
|
4981
|
+
applyEventField(section, indent, tokens, line);
|
|
4982
|
+
return;
|
|
4233
4983
|
case "object":
|
|
4234
4984
|
applyObjectField(section, indent, tokens, line);
|
|
4235
4985
|
return;
|
|
@@ -4272,6 +5022,12 @@ var WorldOrbit = (() => {
|
|
|
4272
5022
|
const value = joinFieldValue(tokens, line);
|
|
4273
5023
|
switch (key) {
|
|
4274
5024
|
case "view":
|
|
5025
|
+
if (isSchema25Projection(value)) {
|
|
5026
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "defaults.view", {
|
|
5027
|
+
line,
|
|
5028
|
+
column: tokens[0].column
|
|
5029
|
+
});
|
|
5030
|
+
}
|
|
4275
5031
|
section.system.defaults.view = parseProjectionValue(value, line, tokens[0].column);
|
|
4276
5032
|
return;
|
|
4277
5033
|
case "scale":
|
|
@@ -4311,14 +5067,36 @@ var WorldOrbit = (() => {
|
|
|
4311
5067
|
throw new WorldOrbitError(`Unknown atlas field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4312
5068
|
}
|
|
4313
5069
|
function applyViewpointField2(section, indent, tokens, line) {
|
|
5070
|
+
if (section.inCamera && indent <= (section.cameraIndent ?? 0)) {
|
|
5071
|
+
section.inCamera = false;
|
|
5072
|
+
section.cameraIndent = null;
|
|
5073
|
+
}
|
|
4314
5074
|
if (section.inFilter && indent <= (section.filterIndent ?? 0)) {
|
|
4315
5075
|
section.inFilter = false;
|
|
4316
5076
|
section.filterIndent = null;
|
|
4317
5077
|
}
|
|
5078
|
+
if (section.inCamera) {
|
|
5079
|
+
applyViewpointCameraField(section, tokens, line);
|
|
5080
|
+
return;
|
|
5081
|
+
}
|
|
4318
5082
|
if (section.inFilter) {
|
|
4319
5083
|
applyViewpointFilterField(section, tokens, line);
|
|
4320
5084
|
return;
|
|
4321
5085
|
}
|
|
5086
|
+
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "camera") {
|
|
5087
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.camera", {
|
|
5088
|
+
line,
|
|
5089
|
+
column: tokens[0].column
|
|
5090
|
+
});
|
|
5091
|
+
if (section.seenFields.has("camera")) {
|
|
5092
|
+
throw new WorldOrbitError('Duplicate viewpoint field "camera"', line, tokens[0].column);
|
|
5093
|
+
}
|
|
5094
|
+
section.seenFields.add("camera");
|
|
5095
|
+
section.inCamera = true;
|
|
5096
|
+
section.cameraIndent = indent;
|
|
5097
|
+
section.viewpoint.camera = section.viewpoint.camera ?? createEmptyViewCamera2();
|
|
5098
|
+
return;
|
|
5099
|
+
}
|
|
4322
5100
|
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "filter") {
|
|
4323
5101
|
if (section.seenFields.has("filter")) {
|
|
4324
5102
|
throw new WorldOrbitError('Duplicate viewpoint field "filter"', line, tokens[0].column);
|
|
@@ -4344,6 +5122,12 @@ var WorldOrbit = (() => {
|
|
|
4344
5122
|
section.viewpoint.selectedObjectId = value;
|
|
4345
5123
|
return;
|
|
4346
5124
|
case "projection":
|
|
5125
|
+
if (isSchema25Projection(value)) {
|
|
5126
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "projection", {
|
|
5127
|
+
line,
|
|
5128
|
+
column: tokens[0].column
|
|
5129
|
+
});
|
|
5130
|
+
}
|
|
4347
5131
|
section.viewpoint.projection = parseProjectionValue(value, line, tokens[0].column);
|
|
4348
5132
|
return;
|
|
4349
5133
|
case "preset":
|
|
@@ -4355,13 +5139,49 @@ var WorldOrbit = (() => {
|
|
|
4355
5139
|
case "rotation":
|
|
4356
5140
|
section.viewpoint.rotationDeg = parseFiniteNumber2(value, line, tokens[0].column, "rotation");
|
|
4357
5141
|
return;
|
|
5142
|
+
case "camera":
|
|
5143
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.camera", {
|
|
5144
|
+
line,
|
|
5145
|
+
column: tokens[0].column
|
|
5146
|
+
});
|
|
5147
|
+
section.viewpoint.camera = parseInlineViewCamera(tokens.slice(1), line, section.viewpoint.camera);
|
|
5148
|
+
return;
|
|
4358
5149
|
case "layers":
|
|
4359
|
-
section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line);
|
|
5150
|
+
section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line, section.sourceSchemaVersion, section.diagnostics);
|
|
5151
|
+
return;
|
|
5152
|
+
case "events":
|
|
5153
|
+
warnIfSchema21Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.events", {
|
|
5154
|
+
line,
|
|
5155
|
+
column: tokens[0].column
|
|
5156
|
+
});
|
|
5157
|
+
section.viewpoint.events = parseTokenList(tokens.slice(1), line, "events");
|
|
4360
5158
|
return;
|
|
4361
5159
|
default:
|
|
4362
5160
|
throw new WorldOrbitError(`Unknown viewpoint field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4363
5161
|
}
|
|
4364
5162
|
}
|
|
5163
|
+
function applyViewpointCameraField(section, tokens, line) {
|
|
5164
|
+
const key = requireUniqueField(tokens, section.seenCameraFields, line);
|
|
5165
|
+
const value = joinFieldValue(tokens, line);
|
|
5166
|
+
const camera = section.viewpoint.camera ?? createEmptyViewCamera2();
|
|
5167
|
+
switch (key) {
|
|
5168
|
+
case "azimuth":
|
|
5169
|
+
camera.azimuth = parseFiniteNumber2(value, line, tokens[0].column, "camera.azimuth");
|
|
5170
|
+
break;
|
|
5171
|
+
case "elevation":
|
|
5172
|
+
camera.elevation = parseFiniteNumber2(value, line, tokens[0].column, "camera.elevation");
|
|
5173
|
+
break;
|
|
5174
|
+
case "roll":
|
|
5175
|
+
camera.roll = parseFiniteNumber2(value, line, tokens[0].column, "camera.roll");
|
|
5176
|
+
break;
|
|
5177
|
+
case "distance":
|
|
5178
|
+
camera.distance = parsePositiveNumber2(value, line, tokens[0].column, "camera.distance");
|
|
5179
|
+
break;
|
|
5180
|
+
default:
|
|
5181
|
+
throw new WorldOrbitError(`Unknown viewpoint camera field "${tokens[0].value}"`, line, tokens[0].column);
|
|
5182
|
+
}
|
|
5183
|
+
section.viewpoint.camera = camera;
|
|
5184
|
+
}
|
|
4365
5185
|
function applyViewpointFilterField(section, tokens, line) {
|
|
4366
5186
|
const key = requireUniqueField(tokens, section.seenFilterFields, line);
|
|
4367
5187
|
const filter = section.viewpoint.filter ?? createEmptyViewpointFilter2();
|
|
@@ -4461,6 +5281,126 @@ var WorldOrbit = (() => {
|
|
|
4461
5281
|
throw new WorldOrbitError(`Unknown relation field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4462
5282
|
}
|
|
4463
5283
|
}
|
|
5284
|
+
function applyEventField(section, indent, tokens, line) {
|
|
5285
|
+
if (section.activePose && indent <= (section.poseIndent ?? 0)) {
|
|
5286
|
+
section.activePose = null;
|
|
5287
|
+
section.poseIndent = null;
|
|
5288
|
+
section.activePoseSeenFields.clear();
|
|
5289
|
+
}
|
|
5290
|
+
if (!section.activePose && section.inPositions && indent <= (section.positionsIndent ?? 0)) {
|
|
5291
|
+
section.inPositions = false;
|
|
5292
|
+
section.positionsIndent = null;
|
|
5293
|
+
}
|
|
5294
|
+
if (section.activePose) {
|
|
5295
|
+
if (tokens[0]?.value === "epoch" || tokens[0]?.value === "referencePlane") {
|
|
5296
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, `pose.${tokens[0].value}`, {
|
|
5297
|
+
line,
|
|
5298
|
+
column: tokens[0]?.column ?? 1
|
|
5299
|
+
});
|
|
5300
|
+
}
|
|
5301
|
+
section.activePose.fields.push(parseEventPoseField(tokens, line, section.activePoseSeenFields));
|
|
5302
|
+
return;
|
|
5303
|
+
}
|
|
5304
|
+
if (section.inPositions) {
|
|
5305
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "pose") {
|
|
5306
|
+
throw new WorldOrbitError(`Unknown event positions field "${tokens[0].value}"`, line, tokens[0]?.column ?? 1);
|
|
5307
|
+
}
|
|
5308
|
+
const objectId = tokens[1].value;
|
|
5309
|
+
if (!objectId.trim()) {
|
|
5310
|
+
throw new WorldOrbitError("Event pose object id must not be empty", line, tokens[1].column);
|
|
5311
|
+
}
|
|
5312
|
+
const rawPose = {
|
|
5313
|
+
objectId,
|
|
5314
|
+
fields: [],
|
|
5315
|
+
location: { line, column: tokens[0].column }
|
|
5316
|
+
};
|
|
5317
|
+
section.rawPoses.push(rawPose);
|
|
5318
|
+
section.activePose = rawPose;
|
|
5319
|
+
section.poseIndent = indent;
|
|
5320
|
+
section.activePoseSeenFields = /* @__PURE__ */ new Set();
|
|
5321
|
+
return;
|
|
5322
|
+
}
|
|
5323
|
+
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "positions") {
|
|
5324
|
+
if (section.seenFields.has("positions")) {
|
|
5325
|
+
throw new WorldOrbitError('Duplicate event field "positions"', line, tokens[0].column);
|
|
5326
|
+
}
|
|
5327
|
+
section.seenFields.add("positions");
|
|
5328
|
+
section.inPositions = true;
|
|
5329
|
+
section.positionsIndent = indent;
|
|
5330
|
+
return;
|
|
5331
|
+
}
|
|
5332
|
+
const key = requireUniqueField(tokens, section.seenFields, line);
|
|
5333
|
+
switch (key) {
|
|
5334
|
+
case "kind":
|
|
5335
|
+
section.event.kind = joinFieldValue(tokens, line);
|
|
5336
|
+
return;
|
|
5337
|
+
case "label":
|
|
5338
|
+
section.event.label = joinFieldValue(tokens, line);
|
|
5339
|
+
return;
|
|
5340
|
+
case "summary":
|
|
5341
|
+
section.event.summary = joinFieldValue(tokens, line);
|
|
5342
|
+
return;
|
|
5343
|
+
case "target":
|
|
5344
|
+
section.event.targetObjectId = joinFieldValue(tokens, line);
|
|
5345
|
+
return;
|
|
5346
|
+
case "participants":
|
|
5347
|
+
section.event.participantObjectIds = parseTokenList(tokens.slice(1), line, "participants");
|
|
5348
|
+
return;
|
|
5349
|
+
case "timing":
|
|
5350
|
+
section.event.timing = joinFieldValue(tokens, line);
|
|
5351
|
+
return;
|
|
5352
|
+
case "visibility":
|
|
5353
|
+
section.event.visibility = joinFieldValue(tokens, line);
|
|
5354
|
+
return;
|
|
5355
|
+
case "epoch":
|
|
5356
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "event.epoch", {
|
|
5357
|
+
line,
|
|
5358
|
+
column: tokens[0].column
|
|
5359
|
+
});
|
|
5360
|
+
section.event.epoch = joinFieldValue(tokens, line);
|
|
5361
|
+
return;
|
|
5362
|
+
case "referenceplane":
|
|
5363
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "event.referencePlane", {
|
|
5364
|
+
line,
|
|
5365
|
+
column: tokens[0].column
|
|
5366
|
+
});
|
|
5367
|
+
section.event.referencePlane = joinFieldValue(tokens, line);
|
|
5368
|
+
return;
|
|
5369
|
+
case "tags":
|
|
5370
|
+
section.event.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
5371
|
+
return;
|
|
5372
|
+
case "color":
|
|
5373
|
+
section.event.color = joinFieldValue(tokens, line);
|
|
5374
|
+
return;
|
|
5375
|
+
case "hidden":
|
|
5376
|
+
section.event.hidden = parseAtlasBoolean(joinFieldValue(tokens, line), "hidden", {
|
|
5377
|
+
line,
|
|
5378
|
+
column: tokens[0].column
|
|
5379
|
+
});
|
|
5380
|
+
return;
|
|
5381
|
+
default:
|
|
5382
|
+
throw new WorldOrbitError(`Unknown event field "${tokens[0].value}"`, line, tokens[0].column);
|
|
5383
|
+
}
|
|
5384
|
+
}
|
|
5385
|
+
function parseEventPoseField(tokens, line, seenFields) {
|
|
5386
|
+
if (tokens.length < 2) {
|
|
5387
|
+
throw new WorldOrbitError("Invalid event pose field line", line, tokens[0]?.column ?? 1);
|
|
5388
|
+
}
|
|
5389
|
+
const key = tokens[0].value;
|
|
5390
|
+
if (!EVENT_POSE_FIELD_KEYS.has(key)) {
|
|
5391
|
+
throw new WorldOrbitError(`Unknown event pose field "${key}"`, line, tokens[0].column);
|
|
5392
|
+
}
|
|
5393
|
+
if (seenFields.has(key)) {
|
|
5394
|
+
throw new WorldOrbitError(`Duplicate event pose field "${key}"`, line, tokens[0].column);
|
|
5395
|
+
}
|
|
5396
|
+
seenFields.add(key);
|
|
5397
|
+
return {
|
|
5398
|
+
type: "field",
|
|
5399
|
+
key,
|
|
5400
|
+
values: tokens.slice(1).map((token) => token.value),
|
|
5401
|
+
location: { line, column: tokens[0].column }
|
|
5402
|
+
};
|
|
5403
|
+
}
|
|
4464
5404
|
function applyObjectField(section, indent, tokens, line) {
|
|
4465
5405
|
if (section.activeBlock && indent <= (section.blockIndent ?? 0)) {
|
|
4466
5406
|
section.activeBlock = null;
|
|
@@ -4519,7 +5459,7 @@ var WorldOrbit = (() => {
|
|
|
4519
5459
|
function parseObjectTypeTokens(tokens, line) {
|
|
4520
5460
|
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");
|
|
4521
5461
|
}
|
|
4522
|
-
function parseLayerTokens(tokens, line) {
|
|
5462
|
+
function parseLayerTokens(tokens, line, sourceSchemaVersion, diagnostics) {
|
|
4523
5463
|
const layers = {};
|
|
4524
5464
|
for (const token of parseTokenList(tokens, line, "layers")) {
|
|
4525
5465
|
const enabled = !token.startsWith("-") && !token.startsWith("!");
|
|
@@ -4529,7 +5469,13 @@ var WorldOrbit = (() => {
|
|
|
4529
5469
|
layers["orbits-front"] = enabled;
|
|
4530
5470
|
continue;
|
|
4531
5471
|
}
|
|
4532
|
-
if (raw === "background" || raw === "guides" || raw === "orbits-back" || raw === "orbits-front" || raw === "relations" || raw === "objects" || raw === "labels" || raw === "metadata") {
|
|
5472
|
+
if (raw === "background" || raw === "guides" || raw === "orbits-back" || raw === "orbits-front" || raw === "relations" || raw === "events" || raw === "objects" || raw === "labels" || raw === "metadata") {
|
|
5473
|
+
if (raw === "events" && sourceSchemaVersion && diagnostics) {
|
|
5474
|
+
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "layers.events", {
|
|
5475
|
+
line,
|
|
5476
|
+
column: tokens[0]?.column ?? 1
|
|
5477
|
+
});
|
|
5478
|
+
}
|
|
4533
5479
|
layers[raw] = enabled;
|
|
4534
5480
|
}
|
|
4535
5481
|
}
|
|
@@ -4547,11 +5493,15 @@ var WorldOrbit = (() => {
|
|
|
4547
5493
|
}
|
|
4548
5494
|
function parseProjectionValue(value, line, column) {
|
|
4549
5495
|
const normalized = value.toLowerCase();
|
|
4550
|
-
if (normalized !== "topdown" && normalized !== "isometric") {
|
|
5496
|
+
if (normalized !== "topdown" && normalized !== "isometric" && normalized !== "orthographic" && normalized !== "perspective") {
|
|
4551
5497
|
throw new WorldOrbitError(`Unknown projection "${value}"`, line, column);
|
|
4552
5498
|
}
|
|
4553
5499
|
return normalized;
|
|
4554
5500
|
}
|
|
5501
|
+
function isSchema25Projection(value) {
|
|
5502
|
+
const normalized = value.toLowerCase();
|
|
5503
|
+
return normalized === "orthographic" || normalized === "perspective";
|
|
5504
|
+
}
|
|
4555
5505
|
function parsePresetValue(value, line, column) {
|
|
4556
5506
|
const normalized = value.toLowerCase();
|
|
4557
5507
|
if (normalized === "diagram" || normalized === "presentation" || normalized === "atlas-card" || normalized === "markdown") {
|
|
@@ -4581,6 +5531,48 @@ var WorldOrbit = (() => {
|
|
|
4581
5531
|
groupIds: []
|
|
4582
5532
|
};
|
|
4583
5533
|
}
|
|
5534
|
+
function createEmptyViewCamera2() {
|
|
5535
|
+
return {
|
|
5536
|
+
azimuth: null,
|
|
5537
|
+
elevation: null,
|
|
5538
|
+
roll: null,
|
|
5539
|
+
distance: null
|
|
5540
|
+
};
|
|
5541
|
+
}
|
|
5542
|
+
function parseInlineViewCamera(tokens, line, current) {
|
|
5543
|
+
if (tokens.length === 0 || tokens.length % 2 !== 0) {
|
|
5544
|
+
throw new WorldOrbitError('Field "camera" expects "<field> <value>" pairs', line, tokens[0]?.column ?? 1);
|
|
5545
|
+
}
|
|
5546
|
+
const camera = current ? { ...current } : createEmptyViewCamera2();
|
|
5547
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5548
|
+
for (let index = 0; index < tokens.length; index += 2) {
|
|
5549
|
+
const fieldToken = tokens[index];
|
|
5550
|
+
const valueToken = tokens[index + 1];
|
|
5551
|
+
const key = fieldToken.value.toLowerCase();
|
|
5552
|
+
if (seen.has(key)) {
|
|
5553
|
+
throw new WorldOrbitError(`Duplicate viewpoint camera field "${fieldToken.value}"`, line, fieldToken.column);
|
|
5554
|
+
}
|
|
5555
|
+
seen.add(key);
|
|
5556
|
+
const value = valueToken.value;
|
|
5557
|
+
switch (key) {
|
|
5558
|
+
case "azimuth":
|
|
5559
|
+
camera.azimuth = parseFiniteNumber2(value, line, fieldToken.column, "camera.azimuth");
|
|
5560
|
+
break;
|
|
5561
|
+
case "elevation":
|
|
5562
|
+
camera.elevation = parseFiniteNumber2(value, line, fieldToken.column, "camera.elevation");
|
|
5563
|
+
break;
|
|
5564
|
+
case "roll":
|
|
5565
|
+
camera.roll = parseFiniteNumber2(value, line, fieldToken.column, "camera.roll");
|
|
5566
|
+
break;
|
|
5567
|
+
case "distance":
|
|
5568
|
+
camera.distance = parsePositiveNumber2(value, line, fieldToken.column, "camera.distance");
|
|
5569
|
+
break;
|
|
5570
|
+
default:
|
|
5571
|
+
throw new WorldOrbitError(`Unknown viewpoint camera field "${fieldToken.value}"`, line, fieldToken.column);
|
|
5572
|
+
}
|
|
5573
|
+
}
|
|
5574
|
+
return camera;
|
|
5575
|
+
}
|
|
4584
5576
|
function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
4585
5577
|
const fields = [];
|
|
4586
5578
|
let index = 0;
|
|
@@ -4668,7 +5660,7 @@ var WorldOrbit = (() => {
|
|
|
4668
5660
|
}
|
|
4669
5661
|
function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
|
|
4670
5662
|
const fieldMap = collectDraftFields(node.fields);
|
|
4671
|
-
const placement =
|
|
5663
|
+
const placement = extractPlacementFromFieldMap(fieldMap);
|
|
4672
5664
|
const properties = normalizeDraftProperties(node.objectType, fieldMap);
|
|
4673
5665
|
const groups = parseOptionalTokenList(fieldMap.get("groups")?.[0]);
|
|
4674
5666
|
const epoch = parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]);
|
|
@@ -4713,21 +5705,41 @@ var WorldOrbit = (() => {
|
|
|
4713
5705
|
object.tolerances = tolerances;
|
|
4714
5706
|
if (typedBlocks && Object.keys(typedBlocks).length > 0)
|
|
4715
5707
|
object.typedBlocks = typedBlocks;
|
|
4716
|
-
if (sourceSchemaVersion
|
|
5708
|
+
if (isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
4717
5709
|
if (object.groups || object.epoch || object.referencePlane || object.tidalLock !== void 0 || object.resonance || object.renderHints || object.deriveRules?.length || object.validationRules?.length || object.lockedFields?.length || object.tolerances?.length || object.typedBlocks) {
|
|
4718
5710
|
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, node.id, node.location);
|
|
4719
5711
|
}
|
|
4720
5712
|
}
|
|
4721
5713
|
return object;
|
|
4722
5714
|
}
|
|
4723
|
-
function
|
|
5715
|
+
function normalizeDraftEvent(event, rawPoses) {
|
|
5716
|
+
return {
|
|
5717
|
+
...event,
|
|
5718
|
+
participantObjectIds: [...new Set(event.participantObjectIds)],
|
|
5719
|
+
tags: [...new Set(event.tags)],
|
|
5720
|
+
positions: rawPoses.map((pose) => normalizeDraftEventPose(pose))
|
|
5721
|
+
};
|
|
5722
|
+
}
|
|
5723
|
+
function normalizeDraftEventPose(rawPose) {
|
|
5724
|
+
const fieldMap = collectDraftFields(rawPose.fields, "event-pose");
|
|
5725
|
+
const placement = extractPlacementFromFieldMap(fieldMap);
|
|
5726
|
+
return {
|
|
5727
|
+
objectId: rawPose.objectId,
|
|
5728
|
+
placement,
|
|
5729
|
+
inner: parseOptionalUnitField(fieldMap.get("inner")?.[0], "inner"),
|
|
5730
|
+
outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer"),
|
|
5731
|
+
epoch: parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]),
|
|
5732
|
+
referencePlane: parseOptionalJoinedValue(fieldMap.get("referencePlane")?.[0])
|
|
5733
|
+
};
|
|
5734
|
+
}
|
|
5735
|
+
function collectDraftFields(fields, _mode = "object") {
|
|
4724
5736
|
const grouped = /* @__PURE__ */ new Map();
|
|
4725
5737
|
for (const field of fields) {
|
|
4726
5738
|
const spec = getDraftObjectFieldSpec(field.key);
|
|
4727
|
-
if (!spec) {
|
|
5739
|
+
if (!spec && !EVENT_POSE_FIELD_KEYS.has(field.key)) {
|
|
4728
5740
|
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4729
5741
|
}
|
|
4730
|
-
if (!spec
|
|
5742
|
+
if (!spec?.allowRepeat && grouped.has(field.key)) {
|
|
4731
5743
|
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
4732
5744
|
}
|
|
4733
5745
|
const existing = grouped.get(field.key) ?? [];
|
|
@@ -4736,7 +5748,7 @@ var WorldOrbit = (() => {
|
|
|
4736
5748
|
}
|
|
4737
5749
|
return grouped;
|
|
4738
5750
|
}
|
|
4739
|
-
function
|
|
5751
|
+
function extractPlacementFromFieldMap(fieldMap) {
|
|
4740
5752
|
const orbitField = fieldMap.get("orbit")?.[0];
|
|
4741
5753
|
const atField = fieldMap.get("at")?.[0];
|
|
4742
5754
|
const surfaceField = fieldMap.get("surface")?.[0];
|
|
@@ -4904,7 +5916,7 @@ var WorldOrbit = (() => {
|
|
|
4904
5916
|
}
|
|
4905
5917
|
}
|
|
4906
5918
|
function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
4907
|
-
if (sourceSchemaVersion
|
|
5919
|
+
if (!isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
4908
5920
|
return;
|
|
4909
5921
|
}
|
|
4910
5922
|
diagnostics.push({
|
|
@@ -4916,6 +5928,34 @@ var WorldOrbit = (() => {
|
|
|
4916
5928
|
column: location.column
|
|
4917
5929
|
});
|
|
4918
5930
|
}
|
|
5931
|
+
function warnIfSchema25Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
5932
|
+
if (!isSchemaOlderThan(sourceSchemaVersion, "2.5")) {
|
|
5933
|
+
return;
|
|
5934
|
+
}
|
|
5935
|
+
diagnostics.push({
|
|
5936
|
+
code: "parse.schema25.featureCompatibility",
|
|
5937
|
+
severity: "warning",
|
|
5938
|
+
source: "parse",
|
|
5939
|
+
message: `Feature "${featureName}" requires schema 2.5; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
5940
|
+
line: location.line,
|
|
5941
|
+
column: location.column
|
|
5942
|
+
});
|
|
5943
|
+
}
|
|
5944
|
+
function isSchemaOlderThan(sourceSchemaVersion, requiredVersion) {
|
|
5945
|
+
return schemaVersionRank(sourceSchemaVersion) < schemaVersionRank(requiredVersion);
|
|
5946
|
+
}
|
|
5947
|
+
function schemaVersionRank(version) {
|
|
5948
|
+
switch (version) {
|
|
5949
|
+
case "2.0-draft":
|
|
5950
|
+
return 0;
|
|
5951
|
+
case "2.0":
|
|
5952
|
+
return 1;
|
|
5953
|
+
case "2.1":
|
|
5954
|
+
return 2;
|
|
5955
|
+
case "2.5":
|
|
5956
|
+
return 3;
|
|
5957
|
+
}
|
|
5958
|
+
}
|
|
4919
5959
|
function preprocessAtlasSource(source) {
|
|
4920
5960
|
const chars = [...source];
|
|
4921
5961
|
const comments = [];
|
|
@@ -5003,7 +6043,7 @@ var WorldOrbit = (() => {
|
|
|
5003
6043
|
}
|
|
5004
6044
|
|
|
5005
6045
|
// packages/core/dist/atlas-edit.js
|
|
5006
|
-
function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "2.
|
|
6046
|
+
function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "2.5") {
|
|
5007
6047
|
return {
|
|
5008
6048
|
format: "worldorbit",
|
|
5009
6049
|
version,
|
|
@@ -5029,6 +6069,7 @@ var WorldOrbit = (() => {
|
|
|
5029
6069
|
},
|
|
5030
6070
|
groups: [],
|
|
5031
6071
|
relations: [],
|
|
6072
|
+
events: [],
|
|
5032
6073
|
objects: [],
|
|
5033
6074
|
diagnostics: []
|
|
5034
6075
|
};
|
|
@@ -5055,6 +6096,12 @@ var WorldOrbit = (() => {
|
|
|
5055
6096
|
for (const relation of [...document.relations].sort(compareIdLike2)) {
|
|
5056
6097
|
paths.push({ kind: "relation", id: relation.id });
|
|
5057
6098
|
}
|
|
6099
|
+
for (const event of [...document.events].sort(compareIdLike2)) {
|
|
6100
|
+
paths.push({ kind: "event", id: event.id });
|
|
6101
|
+
for (const pose of [...event.positions].sort(comparePoseObjectId2)) {
|
|
6102
|
+
paths.push({ kind: "event-pose", id: event.id, key: pose.objectId });
|
|
6103
|
+
}
|
|
6104
|
+
}
|
|
5058
6105
|
for (const object of [...document.objects].sort(compareIdLike2)) {
|
|
5059
6106
|
paths.push({ kind: "object", id: object.id });
|
|
5060
6107
|
}
|
|
@@ -5070,6 +6117,10 @@ var WorldOrbit = (() => {
|
|
|
5070
6117
|
return path.key ? document.system?.atlasMetadata[path.key] ?? null : null;
|
|
5071
6118
|
case "group":
|
|
5072
6119
|
return path.id ? findGroup(document, path.id) : null;
|
|
6120
|
+
case "event":
|
|
6121
|
+
return path.id ? findEvent(document, path.id) : null;
|
|
6122
|
+
case "event-pose":
|
|
6123
|
+
return path.id && path.key ? findEventPose(document, path.id, path.key) : null;
|
|
5073
6124
|
case "object":
|
|
5074
6125
|
return path.id ? findObject(document, path.id) : null;
|
|
5075
6126
|
case "viewpoint":
|
|
@@ -5109,6 +6160,18 @@ var WorldOrbit = (() => {
|
|
|
5109
6160
|
}
|
|
5110
6161
|
upsertById(next.groups, value);
|
|
5111
6162
|
return next;
|
|
6163
|
+
case "event":
|
|
6164
|
+
if (!path.id) {
|
|
6165
|
+
throw new Error('Event updates require an "id" value.');
|
|
6166
|
+
}
|
|
6167
|
+
upsertById(next.events, value);
|
|
6168
|
+
return next;
|
|
6169
|
+
case "event-pose":
|
|
6170
|
+
if (!path.id || !path.key) {
|
|
6171
|
+
throw new Error('Event pose updates require an event "id" and pose "key" value.');
|
|
6172
|
+
}
|
|
6173
|
+
upsertEventPose(next.events, path.id, value);
|
|
6174
|
+
return next;
|
|
5112
6175
|
case "object":
|
|
5113
6176
|
if (!path.id) {
|
|
5114
6177
|
throw new Error('Object updates require an "id" value.');
|
|
@@ -5157,6 +6220,19 @@ var WorldOrbit = (() => {
|
|
|
5157
6220
|
next.groups = next.groups.filter((group) => group.id !== path.id);
|
|
5158
6221
|
}
|
|
5159
6222
|
return next;
|
|
6223
|
+
case "event":
|
|
6224
|
+
if (path.id) {
|
|
6225
|
+
next.events = next.events.filter((event) => event.id !== path.id);
|
|
6226
|
+
}
|
|
6227
|
+
return next;
|
|
6228
|
+
case "event-pose":
|
|
6229
|
+
if (path.id && path.key) {
|
|
6230
|
+
const event = findEvent(next, path.id);
|
|
6231
|
+
if (event) {
|
|
6232
|
+
event.positions = event.positions.filter((pose) => pose.objectId !== path.key);
|
|
6233
|
+
}
|
|
6234
|
+
}
|
|
6235
|
+
return next;
|
|
5160
6236
|
case "viewpoint":
|
|
5161
6237
|
if (path.id) {
|
|
5162
6238
|
system.viewpoints = system.viewpoints.filter((viewpoint) => viewpoint.id !== path.id);
|
|
@@ -5225,6 +6301,22 @@ var WorldOrbit = (() => {
|
|
|
5225
6301
|
};
|
|
5226
6302
|
}
|
|
5227
6303
|
}
|
|
6304
|
+
if (diagnostic.field?.startsWith("event.")) {
|
|
6305
|
+
const parts = diagnostic.field.split(".");
|
|
6306
|
+
if (parts[1] && findEvent(document, parts[1])) {
|
|
6307
|
+
if (parts[2] === "pose" && parts[3] && findEventPose(document, parts[1], parts[3])) {
|
|
6308
|
+
return {
|
|
6309
|
+
kind: "event-pose",
|
|
6310
|
+
id: parts[1],
|
|
6311
|
+
key: parts[3]
|
|
6312
|
+
};
|
|
6313
|
+
}
|
|
6314
|
+
return {
|
|
6315
|
+
kind: "event",
|
|
6316
|
+
id: parts[1]
|
|
6317
|
+
};
|
|
6318
|
+
}
|
|
6319
|
+
}
|
|
5228
6320
|
if (diagnostic.field && diagnostic.field in ensureSystem(document).atlasMetadata) {
|
|
5229
6321
|
return {
|
|
5230
6322
|
kind: "metadata",
|
|
@@ -5256,6 +6348,12 @@ var WorldOrbit = (() => {
|
|
|
5256
6348
|
function findRelation(document, relationId) {
|
|
5257
6349
|
return document.relations.find((relation) => relation.id === relationId) ?? null;
|
|
5258
6350
|
}
|
|
6351
|
+
function findEvent(document, eventId) {
|
|
6352
|
+
return document.events.find((event) => event.id === eventId) ?? null;
|
|
6353
|
+
}
|
|
6354
|
+
function findEventPose(document, eventId, objectId) {
|
|
6355
|
+
return findEvent(document, eventId)?.positions.find((pose) => pose.objectId === objectId) ?? null;
|
|
6356
|
+
}
|
|
5259
6357
|
function findViewpoint(system, viewpointId) {
|
|
5260
6358
|
return system?.viewpoints.find((viewpoint) => viewpoint.id === viewpointId) ?? null;
|
|
5261
6359
|
}
|
|
@@ -5271,13 +6369,30 @@ var WorldOrbit = (() => {
|
|
|
5271
6369
|
}
|
|
5272
6370
|
items[index] = value;
|
|
5273
6371
|
}
|
|
6372
|
+
function upsertEventPose(events, eventId, value) {
|
|
6373
|
+
const event = events.find((entry) => entry.id === eventId);
|
|
6374
|
+
if (!event) {
|
|
6375
|
+
throw new Error(`Unknown event "${eventId}" for pose update.`);
|
|
6376
|
+
}
|
|
6377
|
+
const index = event.positions.findIndex((entry) => entry.objectId === value.objectId);
|
|
6378
|
+
if (index === -1) {
|
|
6379
|
+
event.positions.push(value);
|
|
6380
|
+
event.positions.sort(comparePoseObjectId2);
|
|
6381
|
+
return;
|
|
6382
|
+
}
|
|
6383
|
+
event.positions[index] = value;
|
|
6384
|
+
}
|
|
5274
6385
|
function compareIdLike2(left, right) {
|
|
5275
6386
|
return left.id.localeCompare(right.id);
|
|
5276
6387
|
}
|
|
6388
|
+
function comparePoseObjectId2(left, right) {
|
|
6389
|
+
return left.objectId.localeCompare(right.objectId);
|
|
6390
|
+
}
|
|
5277
6391
|
|
|
5278
6392
|
// packages/core/dist/load.js
|
|
5279
|
-
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;
|
|
6393
|
+
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1|\.5)?$/i;
|
|
5280
6394
|
var ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
|
|
6395
|
+
var ATLAS_SCHEMA_25_PATTERN = /^schema\s+2\.5$/i;
|
|
5281
6396
|
var LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
|
|
5282
6397
|
function detectWorldOrbitSchemaVersion(source) {
|
|
5283
6398
|
for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
|
|
@@ -5291,6 +6406,9 @@ var WorldOrbit = (() => {
|
|
|
5291
6406
|
if (ATLAS_SCHEMA_21_PATTERN.test(trimmed)) {
|
|
5292
6407
|
return "2.1";
|
|
5293
6408
|
}
|
|
6409
|
+
if (ATLAS_SCHEMA_25_PATTERN.test(trimmed)) {
|
|
6410
|
+
return "2.5";
|
|
6411
|
+
}
|
|
5294
6412
|
if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
|
|
5295
6413
|
return "2.0";
|
|
5296
6414
|
}
|
|
@@ -5351,7 +6469,7 @@ var WorldOrbit = (() => {
|
|
|
5351
6469
|
}
|
|
5352
6470
|
function loadWorldOrbitSourceWithDiagnostics(source) {
|
|
5353
6471
|
const schemaVersion = detectWorldOrbitSchemaVersion(source);
|
|
5354
|
-
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1") {
|
|
6472
|
+
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1" || schemaVersion === "2.5") {
|
|
5355
6473
|
return loadAtlasSourceWithDiagnostics(source, schemaVersion);
|
|
5356
6474
|
}
|
|
5357
6475
|
let ast;
|