worldorbit 2.5.17 → 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 +502 -61
- package/dist/browser/editor/dist/index.js +619 -71
- package/dist/browser/markdown/dist/index.js +464 -51
- package/dist/browser/viewer/dist/index.js +495 -62
- package/dist/unpkg/core/dist/index.js +502 -61
- package/dist/unpkg/editor/dist/index.js +619 -71
- package/dist/unpkg/markdown/dist/index.js +464 -51
- package/dist/unpkg/viewer/dist/index.js +495 -62
- package/dist/unpkg/worldorbit-core.min.js +12 -12
- package/dist/unpkg/worldorbit-editor.min.js +208 -198
- package/dist/unpkg/worldorbit-markdown.min.js +38 -38
- package/dist/unpkg/worldorbit-viewer.min.js +56 -56
- package/dist/unpkg/worldorbit.js +533 -72
- package/dist/unpkg/worldorbit.min.js +60 -60
- package/package.json +1 -1
- package/packages/core/dist/atlas-edit.js +1 -1
- package/packages/core/dist/atlas-validate.js +99 -10
- package/packages/core/dist/draft-parse.js +190 -15
- package/packages/core/dist/draft.js +50 -11
- package/packages/core/dist/format.js +36 -5
- package/packages/core/dist/load.js +9 -2
- package/packages/core/dist/scene.js +158 -24
- package/packages/core/dist/types.d.ts +19 -2
- package/packages/editor/dist/editor.js +105 -4
- package/packages/viewer/dist/atlas-state.js +5 -2
- package/packages/viewer/dist/atlas-viewer.js +19 -8
- package/packages/viewer/dist/types.d.ts +5 -2
- package/packages/viewer/dist/viewer.js +16 -0
|
@@ -155,13 +155,14 @@
|
|
|
155
155
|
}
|
|
156
156
|
function createAtlasStateSnapshot(viewerState, renderOptions, filter, viewpointId) {
|
|
157
157
|
return {
|
|
158
|
-
version: "2.
|
|
158
|
+
version: "2.5",
|
|
159
159
|
viewpointId,
|
|
160
160
|
activeEventId: renderOptions.activeEventId ?? null,
|
|
161
161
|
viewerState: { ...viewerState },
|
|
162
162
|
renderOptions: {
|
|
163
163
|
preset: renderOptions.preset,
|
|
164
164
|
projection: renderOptions.projection,
|
|
165
|
+
camera: renderOptions.camera ? { ...renderOptions.camera } : null,
|
|
165
166
|
layers: renderOptions.layers ? { ...renderOptions.layers } : void 0,
|
|
166
167
|
scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : void 0,
|
|
167
168
|
activeEventId: renderOptions.activeEventId ?? null
|
|
@@ -175,7 +176,7 @@
|
|
|
175
176
|
function deserializeViewerAtlasState(serialized) {
|
|
176
177
|
const raw = JSON.parse(decodeURIComponent(serialized));
|
|
177
178
|
return {
|
|
178
|
-
version: "2.0",
|
|
179
|
+
version: raw.version === "2.0" ? "2.0" : "2.5",
|
|
179
180
|
viewpointId: raw.viewpointId ?? null,
|
|
180
181
|
activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null,
|
|
181
182
|
viewerState: {
|
|
@@ -188,6 +189,7 @@
|
|
|
188
189
|
renderOptions: {
|
|
189
190
|
preset: raw.renderOptions?.preset,
|
|
190
191
|
projection: raw.renderOptions?.projection,
|
|
192
|
+
camera: raw.renderOptions?.camera ? { ...raw.renderOptions.camera } : null,
|
|
191
193
|
layers: raw.renderOptions?.layers ? { ...raw.renderOptions.layers } : void 0,
|
|
192
194
|
scaleModel: raw.renderOptions?.scaleModel ? { ...raw.renderOptions.scaleModel } : void 0,
|
|
193
195
|
activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null
|
|
@@ -205,6 +207,7 @@
|
|
|
205
207
|
viewerState: { ...atlasState.viewerState },
|
|
206
208
|
renderOptions: {
|
|
207
209
|
...atlasState.renderOptions,
|
|
210
|
+
camera: atlasState.renderOptions.camera ? { ...atlasState.renderOptions.camera } : null,
|
|
208
211
|
layers: atlasState.renderOptions.layers ? { ...atlasState.renderOptions.layers } : void 0,
|
|
209
212
|
scaleModel: atlasState.renderOptions.scaleModel ? { ...atlasState.renderOptions.scaleModel } : void 0,
|
|
210
213
|
activeEventId: atlasState.renderOptions.activeEventId ?? null
|
|
@@ -1253,7 +1256,9 @@
|
|
|
1253
1256
|
const height = frame.height;
|
|
1254
1257
|
const padding = frame.padding;
|
|
1255
1258
|
const layoutPreset = resolveLayoutPreset(document2);
|
|
1256
|
-
const
|
|
1259
|
+
const schemaProjection = resolveProjection(document2, options.projection);
|
|
1260
|
+
const camera = normalizeViewCamera(options.camera ?? null);
|
|
1261
|
+
const renderProjection = resolveRenderProjection(schemaProjection, camera);
|
|
1257
1262
|
const scaleModel = resolveScaleModel(layoutPreset, options.scaleModel);
|
|
1258
1263
|
const spacingFactor = layoutPresetSpacing(layoutPreset);
|
|
1259
1264
|
const systemId = document2.system?.id ?? null;
|
|
@@ -1296,7 +1301,7 @@
|
|
|
1296
1301
|
surfaceChildren,
|
|
1297
1302
|
objectMap,
|
|
1298
1303
|
spacingFactor,
|
|
1299
|
-
projection,
|
|
1304
|
+
projection: renderProjection,
|
|
1300
1305
|
scaleModel
|
|
1301
1306
|
};
|
|
1302
1307
|
const primaryRoot = rootObjects.find((object) => object.type === "star") ?? rootObjects[0] ?? null;
|
|
@@ -1308,7 +1313,7 @@
|
|
|
1308
1313
|
const rootRingRadius = Math.min(width, height) * 0.28 * spacingFactor * scaleModel.orbitDistanceMultiplier;
|
|
1309
1314
|
secondaryRoots.forEach((object, index) => {
|
|
1310
1315
|
const angle = angleForIndex(index, secondaryRoots.length, -Math.PI / 2);
|
|
1311
|
-
const offset = projectPolarOffset(angle, rootRingRadius,
|
|
1316
|
+
const offset = projectPolarOffset(angle, rootRingRadius, renderProjection, 1);
|
|
1312
1317
|
placeObject(object, centerX + offset.x, centerY + offset.y, 0, positions, orbitDrafts, leaderDrafts, context);
|
|
1313
1318
|
});
|
|
1314
1319
|
}
|
|
@@ -1370,27 +1375,34 @@
|
|
|
1370
1375
|
const layers = createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels);
|
|
1371
1376
|
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, scaleModel.labelMultiplier);
|
|
1372
1377
|
const semanticGroups = createSceneSemanticGroups(document2, objects);
|
|
1373
|
-
const viewpoints = createSceneViewpoints(document2,
|
|
1378
|
+
const viewpoints = createSceneViewpoints(document2, schemaProjection, frame.preset, relationships, objectMap);
|
|
1374
1379
|
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, scaleModel.labelMultiplier);
|
|
1375
1380
|
return {
|
|
1376
1381
|
width,
|
|
1377
1382
|
height,
|
|
1378
1383
|
padding,
|
|
1379
1384
|
renderPreset: frame.preset,
|
|
1380
|
-
projection,
|
|
1385
|
+
projection: schemaProjection,
|
|
1386
|
+
renderProjection,
|
|
1387
|
+
camera,
|
|
1381
1388
|
scaleModel,
|
|
1382
1389
|
title: String(document2.system?.title ?? document2.system?.properties.title ?? document2.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1383
|
-
subtitle:
|
|
1390
|
+
subtitle: buildSceneSubtitle(schemaProjection, renderProjection, layoutPreset, camera),
|
|
1384
1391
|
systemId,
|
|
1385
|
-
viewMode:
|
|
1392
|
+
viewMode: schemaProjection,
|
|
1386
1393
|
layoutPreset,
|
|
1387
1394
|
metadata: {
|
|
1388
1395
|
format: document2.format,
|
|
1389
1396
|
version: document2.version,
|
|
1390
|
-
view:
|
|
1397
|
+
view: schemaProjection,
|
|
1398
|
+
renderProjection,
|
|
1391
1399
|
scale: String(document2.system?.properties.scale ?? layoutPreset),
|
|
1392
1400
|
units: String(document2.system?.properties.units ?? "mixed"),
|
|
1393
|
-
preset: frame.preset ?? "custom"
|
|
1401
|
+
preset: frame.preset ?? "custom",
|
|
1402
|
+
...camera?.azimuth !== null ? { "camera.azimuth": String(camera?.azimuth) } : {},
|
|
1403
|
+
...camera?.elevation !== null ? { "camera.elevation": String(camera?.elevation) } : {},
|
|
1404
|
+
...camera?.roll !== null ? { "camera.roll": String(camera?.roll) } : {},
|
|
1405
|
+
...camera?.distance !== null ? { "camera.distance": String(camera?.distance) } : {}
|
|
1394
1406
|
},
|
|
1395
1407
|
contentBounds,
|
|
1396
1408
|
layers,
|
|
@@ -1427,21 +1439,42 @@
|
|
|
1427
1439
|
return cloned;
|
|
1428
1440
|
}
|
|
1429
1441
|
const objectMap = new Map(cloned.map((object) => [object.id, object]));
|
|
1442
|
+
const referencedIds = /* @__PURE__ */ new Set([
|
|
1443
|
+
...activeEvent.targetObjectId ? [activeEvent.targetObjectId] : [],
|
|
1444
|
+
...activeEvent.participantObjectIds,
|
|
1445
|
+
...activeEvent.positions.map((pose) => pose.objectId)
|
|
1446
|
+
]);
|
|
1447
|
+
for (const objectId of referencedIds) {
|
|
1448
|
+
const object = objectMap.get(objectId);
|
|
1449
|
+
if (!object) {
|
|
1450
|
+
continue;
|
|
1451
|
+
}
|
|
1452
|
+
if (activeEvent.epoch) {
|
|
1453
|
+
object.epoch = activeEvent.epoch;
|
|
1454
|
+
}
|
|
1455
|
+
if (activeEvent.referencePlane) {
|
|
1456
|
+
object.referencePlane = activeEvent.referencePlane;
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1430
1459
|
for (const pose of activeEvent.positions) {
|
|
1431
1460
|
const object = objectMap.get(pose.objectId);
|
|
1432
1461
|
if (!object) {
|
|
1433
1462
|
continue;
|
|
1434
1463
|
}
|
|
1435
|
-
|
|
1464
|
+
if (pose.placement) {
|
|
1465
|
+
object.placement = structuredClone(pose.placement);
|
|
1466
|
+
}
|
|
1436
1467
|
if (pose.inner) {
|
|
1437
1468
|
object.properties.inner = { ...pose.inner };
|
|
1438
|
-
} else {
|
|
1439
|
-
delete object.properties.inner;
|
|
1440
1469
|
}
|
|
1441
1470
|
if (pose.outer) {
|
|
1442
1471
|
object.properties.outer = { ...pose.outer };
|
|
1443
|
-
}
|
|
1444
|
-
|
|
1472
|
+
}
|
|
1473
|
+
if (pose.epoch) {
|
|
1474
|
+
object.epoch = pose.epoch;
|
|
1475
|
+
}
|
|
1476
|
+
if (pose.referencePlane) {
|
|
1477
|
+
object.referencePlane = pose.referencePlane;
|
|
1445
1478
|
}
|
|
1446
1479
|
}
|
|
1447
1480
|
return cloned;
|
|
@@ -1482,10 +1515,59 @@
|
|
|
1482
1515
|
}
|
|
1483
1516
|
}
|
|
1484
1517
|
function resolveProjection(document2, projection) {
|
|
1485
|
-
if (projection === "topdown" || projection === "isometric") {
|
|
1518
|
+
if (projection === "topdown" || projection === "isometric" || projection === "orthographic" || projection === "perspective") {
|
|
1486
1519
|
return projection;
|
|
1487
1520
|
}
|
|
1488
|
-
|
|
1521
|
+
const documentView = String(document2.system?.properties.view ?? "topdown").toLowerCase();
|
|
1522
|
+
return parseViewProjection(documentView) ?? "topdown";
|
|
1523
|
+
}
|
|
1524
|
+
function resolveRenderProjection(projection, camera) {
|
|
1525
|
+
switch (projection) {
|
|
1526
|
+
case "topdown":
|
|
1527
|
+
return "topdown";
|
|
1528
|
+
case "isometric":
|
|
1529
|
+
return "isometric";
|
|
1530
|
+
case "orthographic":
|
|
1531
|
+
return camera && (camera.azimuth !== null || camera.elevation !== null || camera.roll !== null) ? "isometric" : "topdown";
|
|
1532
|
+
case "perspective":
|
|
1533
|
+
return "isometric";
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
function normalizeViewCamera(camera) {
|
|
1537
|
+
if (!camera) {
|
|
1538
|
+
return null;
|
|
1539
|
+
}
|
|
1540
|
+
const normalized = {
|
|
1541
|
+
azimuth: normalizeFiniteCameraValue(camera.azimuth),
|
|
1542
|
+
elevation: normalizeFiniteCameraValue(camera.elevation),
|
|
1543
|
+
roll: normalizeFiniteCameraValue(camera.roll),
|
|
1544
|
+
distance: normalizePositiveCameraDistance(camera.distance)
|
|
1545
|
+
};
|
|
1546
|
+
return normalized.azimuth !== null || normalized.elevation !== null || normalized.roll !== null || normalized.distance !== null ? normalized : null;
|
|
1547
|
+
}
|
|
1548
|
+
function normalizeFiniteCameraValue(value) {
|
|
1549
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1550
|
+
}
|
|
1551
|
+
function normalizePositiveCameraDistance(value) {
|
|
1552
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
1553
|
+
}
|
|
1554
|
+
function buildSceneSubtitle(projection, renderProjection, layoutPreset, camera) {
|
|
1555
|
+
const parts = [`${capitalizeLabel(projection)} view`, `${capitalizeLabel(layoutPreset)} layout`];
|
|
1556
|
+
if (projection !== renderProjection) {
|
|
1557
|
+
parts.push(`2D ${renderProjection} fallback`);
|
|
1558
|
+
}
|
|
1559
|
+
if (camera) {
|
|
1560
|
+
const cameraParts = [
|
|
1561
|
+
camera.azimuth !== null ? `az ${camera.azimuth}` : null,
|
|
1562
|
+
camera.elevation !== null ? `el ${camera.elevation}` : null,
|
|
1563
|
+
camera.roll !== null ? `roll ${camera.roll}` : null,
|
|
1564
|
+
camera.distance !== null ? `dist ${camera.distance}` : null
|
|
1565
|
+
].filter(Boolean);
|
|
1566
|
+
if (cameraParts.length > 0) {
|
|
1567
|
+
parts.push(`camera ${cameraParts.join(" / ")}`);
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
return parts.join(" - ");
|
|
1489
1571
|
}
|
|
1490
1572
|
function resolveScaleModel(layoutPreset, overrides) {
|
|
1491
1573
|
const defaults = defaultScaleModel(layoutPreset);
|
|
@@ -1921,6 +2003,8 @@
|
|
|
1921
2003
|
function createGeneratedOverviewViewpoint(document2, projection, preset) {
|
|
1922
2004
|
const title = document2.system?.title ?? document2.system?.properties.title;
|
|
1923
2005
|
const label = title ? `${String(title)} Overview` : "Overview";
|
|
2006
|
+
const camera = normalizeViewCamera(null);
|
|
2007
|
+
const renderProjection = resolveRenderProjection(projection, camera);
|
|
1924
2008
|
return {
|
|
1925
2009
|
id: "overview",
|
|
1926
2010
|
label,
|
|
@@ -1929,6 +2013,8 @@
|
|
|
1929
2013
|
selectedObjectId: null,
|
|
1930
2014
|
eventIds: [],
|
|
1931
2015
|
projection,
|
|
2016
|
+
renderProjection,
|
|
2017
|
+
camera,
|
|
1932
2018
|
preset,
|
|
1933
2019
|
rotationDeg: 0,
|
|
1934
2020
|
scale: null,
|
|
@@ -1978,6 +2064,30 @@
|
|
|
1978
2064
|
case "angle":
|
|
1979
2065
|
draft.rotationDeg = parseFiniteNumber(normalizedValue) ?? draft.rotationDeg ?? 0;
|
|
1980
2066
|
return;
|
|
2067
|
+
case "camera.azimuth":
|
|
2068
|
+
draft.camera = {
|
|
2069
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
2070
|
+
azimuth: parseFiniteNumber(normalizedValue)
|
|
2071
|
+
};
|
|
2072
|
+
return;
|
|
2073
|
+
case "camera.elevation":
|
|
2074
|
+
draft.camera = {
|
|
2075
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
2076
|
+
elevation: parseFiniteNumber(normalizedValue)
|
|
2077
|
+
};
|
|
2078
|
+
return;
|
|
2079
|
+
case "camera.roll":
|
|
2080
|
+
draft.camera = {
|
|
2081
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
2082
|
+
roll: parseFiniteNumber(normalizedValue)
|
|
2083
|
+
};
|
|
2084
|
+
return;
|
|
2085
|
+
case "camera.distance":
|
|
2086
|
+
draft.camera = {
|
|
2087
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
2088
|
+
distance: parsePositiveNumber(normalizedValue)
|
|
2089
|
+
};
|
|
2090
|
+
return;
|
|
1981
2091
|
case "zoom":
|
|
1982
2092
|
case "scale":
|
|
1983
2093
|
draft.scale = parsePositiveNumber(normalizedValue);
|
|
@@ -2017,6 +2127,9 @@
|
|
|
2017
2127
|
const selectedObjectId = draft.select && objectMap.has(draft.select) ? draft.select : objectId;
|
|
2018
2128
|
const filter = normalizeViewpointFilter(draft.filter);
|
|
2019
2129
|
const label = draft.label?.trim() || humanizeIdentifier(draft.id);
|
|
2130
|
+
const resolvedProjection = draft.projection ?? projection;
|
|
2131
|
+
const camera = normalizeViewCamera(draft.camera ?? null);
|
|
2132
|
+
const renderProjection = resolveRenderProjection(resolvedProjection, camera);
|
|
2020
2133
|
return {
|
|
2021
2134
|
id: draft.id,
|
|
2022
2135
|
label,
|
|
@@ -2024,7 +2137,9 @@
|
|
|
2024
2137
|
objectId,
|
|
2025
2138
|
selectedObjectId,
|
|
2026
2139
|
eventIds: [...new Set(draft.eventIds ?? [])],
|
|
2027
|
-
projection:
|
|
2140
|
+
projection: resolvedProjection,
|
|
2141
|
+
renderProjection,
|
|
2142
|
+
camera,
|
|
2028
2143
|
preset: draft.preset ?? preset,
|
|
2029
2144
|
rotationDeg: draft.rotationDeg ?? 0,
|
|
2030
2145
|
scale: draft.scale ?? null,
|
|
@@ -2041,6 +2156,14 @@
|
|
|
2041
2156
|
groupIds: []
|
|
2042
2157
|
};
|
|
2043
2158
|
}
|
|
2159
|
+
function createEmptyViewCamera() {
|
|
2160
|
+
return {
|
|
2161
|
+
azimuth: null,
|
|
2162
|
+
elevation: null,
|
|
2163
|
+
roll: null,
|
|
2164
|
+
distance: null
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2044
2167
|
function normalizeViewpointFilter(filter) {
|
|
2045
2168
|
if (!filter) {
|
|
2046
2169
|
return null;
|
|
@@ -2054,7 +2177,18 @@
|
|
|
2054
2177
|
return normalized.query || normalized.objectTypes.length > 0 || normalized.tags.length > 0 || normalized.groupIds.length > 0 ? normalized : null;
|
|
2055
2178
|
}
|
|
2056
2179
|
function parseViewProjection(value) {
|
|
2057
|
-
|
|
2180
|
+
switch (value.toLowerCase()) {
|
|
2181
|
+
case "topdown":
|
|
2182
|
+
return "topdown";
|
|
2183
|
+
case "isometric":
|
|
2184
|
+
return "isometric";
|
|
2185
|
+
case "orthographic":
|
|
2186
|
+
return "orthographic";
|
|
2187
|
+
case "perspective":
|
|
2188
|
+
return "perspective";
|
|
2189
|
+
default:
|
|
2190
|
+
return null;
|
|
2191
|
+
}
|
|
2058
2192
|
}
|
|
2059
2193
|
function parseRenderPreset(value) {
|
|
2060
2194
|
const normalized = value.toLowerCase();
|
|
@@ -2092,7 +2226,7 @@
|
|
|
2092
2226
|
}
|
|
2093
2227
|
function parseViewpointGroups(value, document2, relationships, objectMap) {
|
|
2094
2228
|
return splitListValue(value).map((entry) => {
|
|
2095
|
-
if (document2.schemaVersion === "2.1" || document2.groups.some((group) => group.id === entry)) {
|
|
2229
|
+
if (document2.schemaVersion === "2.1" || document2.schemaVersion === "2.5" || document2.groups.some((group) => group.id === entry)) {
|
|
2096
2230
|
return entry;
|
|
2097
2231
|
}
|
|
2098
2232
|
if (entry.startsWith("wo-") && entry.endsWith("-group")) {
|
|
@@ -2924,7 +3058,9 @@
|
|
|
2924
3058
|
objectId: pose.objectId,
|
|
2925
3059
|
placement: clonePlacement(pose.placement),
|
|
2926
3060
|
inner: pose.inner ? { ...pose.inner } : void 0,
|
|
2927
|
-
outer: pose.outer ? { ...pose.outer } : void 0
|
|
3061
|
+
outer: pose.outer ? { ...pose.outer } : void 0,
|
|
3062
|
+
epoch: pose.epoch ?? null,
|
|
3063
|
+
referencePlane: pose.referencePlane ?? null
|
|
2928
3064
|
};
|
|
2929
3065
|
}
|
|
2930
3066
|
function clonePlacement(placement) {
|
|
@@ -2939,21 +3075,42 @@
|
|
|
2939
3075
|
return;
|
|
2940
3076
|
}
|
|
2941
3077
|
const objectMap = new Map(objects.map((object) => [object.id, object]));
|
|
3078
|
+
const referencedIds = /* @__PURE__ */ new Set([
|
|
3079
|
+
...event.targetObjectId ? [event.targetObjectId] : [],
|
|
3080
|
+
...event.participantObjectIds,
|
|
3081
|
+
...event.positions.map((pose) => pose.objectId)
|
|
3082
|
+
]);
|
|
3083
|
+
for (const objectId of referencedIds) {
|
|
3084
|
+
const object = objectMap.get(objectId);
|
|
3085
|
+
if (!object) {
|
|
3086
|
+
continue;
|
|
3087
|
+
}
|
|
3088
|
+
if (event.epoch) {
|
|
3089
|
+
object.epoch = event.epoch;
|
|
3090
|
+
}
|
|
3091
|
+
if (event.referencePlane) {
|
|
3092
|
+
object.referencePlane = event.referencePlane;
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
2942
3095
|
for (const pose of event.positions) {
|
|
2943
3096
|
const object = objectMap.get(pose.objectId);
|
|
2944
3097
|
if (!object) {
|
|
2945
3098
|
continue;
|
|
2946
3099
|
}
|
|
2947
|
-
|
|
3100
|
+
if (pose.placement) {
|
|
3101
|
+
object.placement = clonePlacement(pose.placement);
|
|
3102
|
+
}
|
|
2948
3103
|
if (pose.inner) {
|
|
2949
3104
|
object.properties.inner = { ...pose.inner };
|
|
2950
|
-
} else {
|
|
2951
|
-
delete object.properties.inner;
|
|
2952
3105
|
}
|
|
2953
3106
|
if (pose.outer) {
|
|
2954
3107
|
object.properties.outer = { ...pose.outer };
|
|
2955
|
-
}
|
|
2956
|
-
|
|
3108
|
+
}
|
|
3109
|
+
if (pose.epoch) {
|
|
3110
|
+
object.epoch = pose.epoch;
|
|
3111
|
+
}
|
|
3112
|
+
if (pose.referencePlane) {
|
|
3113
|
+
object.referencePlane = pose.referencePlane;
|
|
2957
3114
|
}
|
|
2958
3115
|
}
|
|
2959
3116
|
}
|
|
@@ -3029,6 +3186,18 @@
|
|
|
3029
3186
|
if (viewpoint.rotationDeg !== 0) {
|
|
3030
3187
|
info2[`${prefix}.rotation`] = String(viewpoint.rotationDeg);
|
|
3031
3188
|
}
|
|
3189
|
+
if (viewpoint.camera?.azimuth !== null) {
|
|
3190
|
+
info2[`${prefix}.camera.azimuth`] = String(viewpoint.camera?.azimuth);
|
|
3191
|
+
}
|
|
3192
|
+
if (viewpoint.camera?.elevation !== null) {
|
|
3193
|
+
info2[`${prefix}.camera.elevation`] = String(viewpoint.camera?.elevation);
|
|
3194
|
+
}
|
|
3195
|
+
if (viewpoint.camera?.roll !== null) {
|
|
3196
|
+
info2[`${prefix}.camera.roll`] = String(viewpoint.camera?.roll);
|
|
3197
|
+
}
|
|
3198
|
+
if (viewpoint.camera?.distance !== null) {
|
|
3199
|
+
info2[`${prefix}.camera.distance`] = String(viewpoint.camera?.distance);
|
|
3200
|
+
}
|
|
3032
3201
|
const serializedLayers = serializeViewpointLayers(viewpoint.layers);
|
|
3033
3202
|
if (serializedLayers) {
|
|
3034
3203
|
info2[`${prefix}.layers`] = serializedLayers;
|
|
@@ -3272,13 +3441,13 @@
|
|
|
3272
3441
|
validateRelation(relation, objectMap, diagnostics);
|
|
3273
3442
|
}
|
|
3274
3443
|
for (const viewpoint of document2.system?.viewpoints ?? []) {
|
|
3275
|
-
validateViewpoint(viewpoint
|
|
3444
|
+
validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, diagnostics, objectMap);
|
|
3276
3445
|
}
|
|
3277
3446
|
for (const object of document2.objects) {
|
|
3278
3447
|
validateObject(object, document2.system, objectMap, groupIds, diagnostics);
|
|
3279
3448
|
}
|
|
3280
3449
|
for (const event of document2.events) {
|
|
3281
|
-
validateEvent(event, objectMap, diagnostics);
|
|
3450
|
+
validateEvent(event, document2.system, objectMap, diagnostics);
|
|
3282
3451
|
}
|
|
3283
3452
|
return diagnostics;
|
|
3284
3453
|
}
|
|
@@ -3297,21 +3466,24 @@
|
|
|
3297
3466
|
diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
|
|
3298
3467
|
}
|
|
3299
3468
|
}
|
|
3300
|
-
function validateViewpoint(
|
|
3301
|
-
|
|
3469
|
+
function validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, diagnostics, objectMap) {
|
|
3470
|
+
const filter = viewpoint.filter;
|
|
3471
|
+
if (sourceSchemaVersion === "2.1" || sourceSchemaVersion === "2.5") {
|
|
3302
3472
|
if (filter) {
|
|
3303
3473
|
for (const groupId of filter.groupIds) {
|
|
3304
3474
|
if (!groupIds.has(groupId)) {
|
|
3305
|
-
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${
|
|
3475
|
+
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpoint.id}".`, void 0, `viewpoint.${viewpoint.id}.groups`));
|
|
3306
3476
|
}
|
|
3307
3477
|
}
|
|
3308
3478
|
}
|
|
3309
|
-
for (const eventId of
|
|
3479
|
+
for (const eventId of viewpoint.events ?? []) {
|
|
3310
3480
|
if (!eventIds.has(eventId)) {
|
|
3311
|
-
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${
|
|
3481
|
+
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${viewpoint.id}".`, void 0, `viewpoint.${viewpoint.id}.events`));
|
|
3312
3482
|
}
|
|
3313
3483
|
}
|
|
3314
3484
|
}
|
|
3485
|
+
validateProjection(viewpoint.projection, diagnostics, `viewpoint.${viewpoint.id}.projection`, viewpoint.id);
|
|
3486
|
+
validateCamera(viewpoint.camera, viewpoint.projection, viewpoint.rotationDeg, diagnostics, viewpoint.id, viewpoint.focusObjectId, viewpoint.selectedObjectId, filter, objectMap);
|
|
3315
3487
|
}
|
|
3316
3488
|
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
3317
3489
|
const placement = object.placement;
|
|
@@ -3324,6 +3496,12 @@
|
|
|
3324
3496
|
}
|
|
3325
3497
|
}
|
|
3326
3498
|
}
|
|
3499
|
+
if (typeof object.epoch === "string" && !object.epoch.trim()) {
|
|
3500
|
+
diagnostics.push(warn("validate.epoch.empty", `Object "${object.id}" defines an empty epoch string.`, object.id, "epoch"));
|
|
3501
|
+
}
|
|
3502
|
+
if (typeof object.referencePlane === "string" && !object.referencePlane.trim()) {
|
|
3503
|
+
diagnostics.push(warn("validate.referencePlane.empty", `Object "${object.id}" defines an empty reference plane string.`, object.id, "referencePlane"));
|
|
3504
|
+
}
|
|
3327
3505
|
if (orbitPlacement) {
|
|
3328
3506
|
if (!objectMap.has(orbitPlacement.target)) {
|
|
3329
3507
|
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
@@ -3395,12 +3573,18 @@
|
|
|
3395
3573
|
}
|
|
3396
3574
|
}
|
|
3397
3575
|
}
|
|
3398
|
-
function validateEvent(event, objectMap, diagnostics) {
|
|
3576
|
+
function validateEvent(event, system, objectMap, diagnostics) {
|
|
3399
3577
|
const fieldPrefix = `event.${event.id}`;
|
|
3400
3578
|
const referencedIds = /* @__PURE__ */ new Set();
|
|
3401
3579
|
if (!event.kind.trim()) {
|
|
3402
3580
|
diagnostics.push(error("validate.event.kind.required", `Event "${event.id}" is missing a "kind" value.`, void 0, `${fieldPrefix}.kind`));
|
|
3403
3581
|
}
|
|
3582
|
+
if (typeof event.epoch === "string" && !event.epoch.trim()) {
|
|
3583
|
+
diagnostics.push(warn("validate.event.epoch.empty", `Event "${event.id}" defines an empty epoch string.`, void 0, `${fieldPrefix}.epoch`));
|
|
3584
|
+
}
|
|
3585
|
+
if (typeof event.referencePlane === "string" && !event.referencePlane.trim()) {
|
|
3586
|
+
diagnostics.push(warn("validate.event.referencePlane.empty", `Event "${event.id}" defines an empty reference plane string.`, void 0, `${fieldPrefix}.referencePlane`));
|
|
3587
|
+
}
|
|
3404
3588
|
if (!event.targetObjectId && event.participantObjectIds.length === 0) {
|
|
3405
3589
|
diagnostics.push(error("validate.event.references.required", `Event "${event.id}" must define a "target" or at least one participant.`, void 0, `${fieldPrefix}.participants`));
|
|
3406
3590
|
}
|
|
@@ -3447,10 +3631,14 @@
|
|
|
3447
3631
|
if (!referencedIds.has(pose.objectId)) {
|
|
3448
3632
|
diagnostics.push(warn("validate.event.pose.unreferenced", `Event pose "${pose.objectId}" on "${event.id}" is not listed in target/participants.`, void 0, poseFieldPrefix));
|
|
3449
3633
|
}
|
|
3450
|
-
validateEventPose(pose, object, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
3634
|
+
validateEventPose(pose, object, event, system, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
3635
|
+
}
|
|
3636
|
+
const missingPoseIds = [...referencedIds].filter((objectId) => !poseIds.has(objectId));
|
|
3637
|
+
if (event.positions.length > 0 && missingPoseIds.length > 0) {
|
|
3638
|
+
diagnostics.push(warn("validate.event.positions.partial", `Event "${event.id}" leaves ${missingPoseIds.length} referenced object(s) on their base placement.`, void 0, `${fieldPrefix}.positions`));
|
|
3451
3639
|
}
|
|
3452
3640
|
}
|
|
3453
|
-
function validateEventPose(pose, object, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
3641
|
+
function validateEventPose(pose, object, event, system, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
3454
3642
|
const placement = pose.placement;
|
|
3455
3643
|
if (!placement) {
|
|
3456
3644
|
diagnostics.push(error("validate.event.pose.placement.required", `Event "${eventId}" pose "${pose.objectId}" is missing a placement mode.`, void 0, fieldPrefix));
|
|
@@ -3463,6 +3651,15 @@
|
|
|
3463
3651
|
if (placement.distance && placement.semiMajor) {
|
|
3464
3652
|
diagnostics.push(error("validate.event.pose.orbit.distanceConflict", `Event "${eventId}" pose "${pose.objectId}" cannot declare both "distance" and "semiMajor".`, void 0, `${fieldPrefix}.distance`));
|
|
3465
3653
|
}
|
|
3654
|
+
if (placement.phase && !resolveEffectiveEpoch(system, object, event, pose)) {
|
|
3655
|
+
diagnostics.push(warn("validate.event.pose.phase.epochMissing", `Event "${eventId}" pose "${pose.objectId}" sets "phase" without an effective epoch.`, void 0, `${fieldPrefix}.phase`));
|
|
3656
|
+
}
|
|
3657
|
+
if (placement.inclination && !resolveEffectiveReferencePlane(system, object, event, pose)) {
|
|
3658
|
+
diagnostics.push(warn("validate.event.pose.inclination.referencePlaneMissing", `Event "${eventId}" pose "${pose.objectId}" sets "inclination" without an effective reference plane.`, void 0, `${fieldPrefix}.inclination`));
|
|
3659
|
+
}
|
|
3660
|
+
if (placement.period && !massInSolar(objectMap.get(placement.target)?.properties.mass)) {
|
|
3661
|
+
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`));
|
|
3662
|
+
}
|
|
3466
3663
|
return;
|
|
3467
3664
|
}
|
|
3468
3665
|
if (placement.mode === "surface") {
|
|
@@ -3597,6 +3794,52 @@
|
|
|
3597
3794
|
return null;
|
|
3598
3795
|
}
|
|
3599
3796
|
}
|
|
3797
|
+
function validateProjection(projection, diagnostics, field, viewpointId) {
|
|
3798
|
+
if (projection !== "topdown" && projection !== "isometric" && projection !== "orthographic" && projection !== "perspective") {
|
|
3799
|
+
diagnostics.push(error("validate.viewpoint.projection.invalid", `Unknown projection "${String(projection)}" in viewpoint "${viewpointId}".`, void 0, field));
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
function validateCamera(camera, projection, rotationDeg, diagnostics, viewpointId, focusObjectId, selectedObjectId, filter, objectMap) {
|
|
3803
|
+
if (!camera) {
|
|
3804
|
+
return;
|
|
3805
|
+
}
|
|
3806
|
+
const prefix = `viewpoint.${viewpointId}.camera`;
|
|
3807
|
+
for (const [key, value] of [
|
|
3808
|
+
["azimuth", camera.azimuth],
|
|
3809
|
+
["elevation", camera.elevation],
|
|
3810
|
+
["roll", camera.roll],
|
|
3811
|
+
["distance", camera.distance]
|
|
3812
|
+
]) {
|
|
3813
|
+
if (value !== null && (!Number.isFinite(value) || key === "distance" && value <= 0)) {
|
|
3814
|
+
diagnostics.push(error("validate.viewpoint.camera.invalid", `Invalid camera ${key} "${String(value)}" in viewpoint "${viewpointId}".`, void 0, `${prefix}.${key}`));
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3817
|
+
if (camera.distance !== null && projection !== "perspective") {
|
|
3818
|
+
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`));
|
|
3819
|
+
}
|
|
3820
|
+
if (projection === "topdown" && (camera.elevation !== null || camera.roll !== null)) {
|
|
3821
|
+
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));
|
|
3822
|
+
}
|
|
3823
|
+
if (projection === "isometric" && camera.elevation !== null) {
|
|
3824
|
+
diagnostics.push(info("validate.viewpoint.camera.isometricStored", `Camera elevation on isometric viewpoint "${viewpointId}" is preserved semantically for future 3D rendering.`, void 0, `${prefix}.elevation`));
|
|
3825
|
+
}
|
|
3826
|
+
if (camera.azimuth !== null && camera.azimuth !== 0 && rotationDeg !== 0) {
|
|
3827
|
+
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`));
|
|
3828
|
+
}
|
|
3829
|
+
const hasAnchor = focusObjectId !== null && objectMap.has(focusObjectId) || selectedObjectId !== null && objectMap.has(selectedObjectId) || !!filter;
|
|
3830
|
+
if (!hasAnchor) {
|
|
3831
|
+
diagnostics.push(info("validate.viewpoint.camera.anchorMissing", `Viewpoint "${viewpointId}" stores camera settings without a focus object, selection, or filter anchor.`, void 0, prefix));
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
function resolveEffectiveEpoch(system, object, event, pose) {
|
|
3835
|
+
return normalizeOptionalContextString(pose?.epoch) ?? normalizeOptionalContextString(event?.epoch) ?? normalizeOptionalContextString(object.epoch) ?? normalizeOptionalContextString(system?.epoch) ?? null;
|
|
3836
|
+
}
|
|
3837
|
+
function resolveEffectiveReferencePlane(system, object, event, pose) {
|
|
3838
|
+
return normalizeOptionalContextString(pose?.referencePlane) ?? normalizeOptionalContextString(event?.referencePlane) ?? normalizeOptionalContextString(object.referencePlane) ?? normalizeOptionalContextString(system?.referencePlane) ?? null;
|
|
3839
|
+
}
|
|
3840
|
+
function normalizeOptionalContextString(value) {
|
|
3841
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
3842
|
+
}
|
|
3600
3843
|
function toleranceForField(object, field) {
|
|
3601
3844
|
const tolerance = object.tolerances?.find((entry) => entry.field === field)?.value;
|
|
3602
3845
|
if (typeof tolerance === "number") {
|
|
@@ -3705,7 +3948,9 @@
|
|
|
3705
3948
|
"surface",
|
|
3706
3949
|
"free",
|
|
3707
3950
|
"inner",
|
|
3708
|
-
"outer"
|
|
3951
|
+
"outer",
|
|
3952
|
+
"epoch",
|
|
3953
|
+
"referencePlane"
|
|
3709
3954
|
]);
|
|
3710
3955
|
function parseWorldOrbitAtlas(source) {
|
|
3711
3956
|
return parseAtlasSource(source);
|
|
@@ -3747,7 +3992,7 @@
|
|
|
3747
3992
|
if (!sawSchemaHeader) {
|
|
3748
3993
|
sourceSchemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
3749
3994
|
sawSchemaHeader = true;
|
|
3750
|
-
if (prepared.comments.length > 0 && sourceSchemaVersion
|
|
3995
|
+
if (prepared.comments.length > 0 && isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
3751
3996
|
diagnostics.push({
|
|
3752
3997
|
code: "parse.schema21.commentCompatibility",
|
|
3753
3998
|
severity: "warning",
|
|
@@ -3817,11 +4062,11 @@
|
|
|
3817
4062
|
return document2;
|
|
3818
4063
|
}
|
|
3819
4064
|
function assertDraftSchemaHeader(tokens, line) {
|
|
3820
|
-
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1"].includes(tokens[1].value.toLowerCase())) {
|
|
3821
|
-
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
4065
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1", "2.5"].includes(tokens[1].value.toLowerCase())) {
|
|
4066
|
+
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);
|
|
3822
4067
|
}
|
|
3823
4068
|
const version = tokens[1].value.toLowerCase();
|
|
3824
|
-
return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
4069
|
+
return version === "2.5" ? "2.5" : version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
3825
4070
|
}
|
|
3826
4071
|
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, flags) {
|
|
3827
4072
|
const keyword = tokens[0]?.value.toLowerCase();
|
|
@@ -3841,6 +4086,8 @@
|
|
|
3841
4086
|
return {
|
|
3842
4087
|
kind: "defaults",
|
|
3843
4088
|
system,
|
|
4089
|
+
sourceSchemaVersion,
|
|
4090
|
+
diagnostics,
|
|
3844
4091
|
seenFields: /* @__PURE__ */ new Set()
|
|
3845
4092
|
};
|
|
3846
4093
|
case "atlas":
|
|
@@ -3933,6 +4180,7 @@
|
|
|
3933
4180
|
preset: system.defaults.preset,
|
|
3934
4181
|
zoom: null,
|
|
3935
4182
|
rotationDeg: 0,
|
|
4183
|
+
camera: null,
|
|
3936
4184
|
layers: {},
|
|
3937
4185
|
filter: null
|
|
3938
4186
|
};
|
|
@@ -3946,7 +4194,10 @@
|
|
|
3946
4194
|
seenFields: /* @__PURE__ */ new Set(),
|
|
3947
4195
|
inFilter: false,
|
|
3948
4196
|
filterIndent: null,
|
|
3949
|
-
seenFilterFields: /* @__PURE__ */ new Set()
|
|
4197
|
+
seenFilterFields: /* @__PURE__ */ new Set(),
|
|
4198
|
+
inCamera: false,
|
|
4199
|
+
cameraIndent: null,
|
|
4200
|
+
seenCameraFields: /* @__PURE__ */ new Set()
|
|
3950
4201
|
};
|
|
3951
4202
|
}
|
|
3952
4203
|
function startAnnotationSection(tokens, line, system, annotationIds) {
|
|
@@ -4053,6 +4304,8 @@
|
|
|
4053
4304
|
participantObjectIds: [],
|
|
4054
4305
|
timing: null,
|
|
4055
4306
|
visibility: null,
|
|
4307
|
+
epoch: null,
|
|
4308
|
+
referencePlane: null,
|
|
4056
4309
|
tags: [],
|
|
4057
4310
|
color: null,
|
|
4058
4311
|
hidden: false,
|
|
@@ -4177,6 +4430,12 @@
|
|
|
4177
4430
|
const value = joinFieldValue(tokens, line);
|
|
4178
4431
|
switch (key) {
|
|
4179
4432
|
case "view":
|
|
4433
|
+
if (isSchema25Projection(value)) {
|
|
4434
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "defaults.view", {
|
|
4435
|
+
line,
|
|
4436
|
+
column: tokens[0].column
|
|
4437
|
+
});
|
|
4438
|
+
}
|
|
4180
4439
|
section.system.defaults.view = parseProjectionValue(value, line, tokens[0].column);
|
|
4181
4440
|
return;
|
|
4182
4441
|
case "scale":
|
|
@@ -4216,14 +4475,36 @@
|
|
|
4216
4475
|
throw new WorldOrbitError(`Unknown atlas field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4217
4476
|
}
|
|
4218
4477
|
function applyViewpointField2(section, indent, tokens, line) {
|
|
4478
|
+
if (section.inCamera && indent <= (section.cameraIndent ?? 0)) {
|
|
4479
|
+
section.inCamera = false;
|
|
4480
|
+
section.cameraIndent = null;
|
|
4481
|
+
}
|
|
4219
4482
|
if (section.inFilter && indent <= (section.filterIndent ?? 0)) {
|
|
4220
4483
|
section.inFilter = false;
|
|
4221
4484
|
section.filterIndent = null;
|
|
4222
4485
|
}
|
|
4486
|
+
if (section.inCamera) {
|
|
4487
|
+
applyViewpointCameraField(section, tokens, line);
|
|
4488
|
+
return;
|
|
4489
|
+
}
|
|
4223
4490
|
if (section.inFilter) {
|
|
4224
4491
|
applyViewpointFilterField(section, tokens, line);
|
|
4225
4492
|
return;
|
|
4226
4493
|
}
|
|
4494
|
+
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "camera") {
|
|
4495
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.camera", {
|
|
4496
|
+
line,
|
|
4497
|
+
column: tokens[0].column
|
|
4498
|
+
});
|
|
4499
|
+
if (section.seenFields.has("camera")) {
|
|
4500
|
+
throw new WorldOrbitError('Duplicate viewpoint field "camera"', line, tokens[0].column);
|
|
4501
|
+
}
|
|
4502
|
+
section.seenFields.add("camera");
|
|
4503
|
+
section.inCamera = true;
|
|
4504
|
+
section.cameraIndent = indent;
|
|
4505
|
+
section.viewpoint.camera = section.viewpoint.camera ?? createEmptyViewCamera2();
|
|
4506
|
+
return;
|
|
4507
|
+
}
|
|
4227
4508
|
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "filter") {
|
|
4228
4509
|
if (section.seenFields.has("filter")) {
|
|
4229
4510
|
throw new WorldOrbitError('Duplicate viewpoint field "filter"', line, tokens[0].column);
|
|
@@ -4249,6 +4530,12 @@
|
|
|
4249
4530
|
section.viewpoint.selectedObjectId = value;
|
|
4250
4531
|
return;
|
|
4251
4532
|
case "projection":
|
|
4533
|
+
if (isSchema25Projection(value)) {
|
|
4534
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "projection", {
|
|
4535
|
+
line,
|
|
4536
|
+
column: tokens[0].column
|
|
4537
|
+
});
|
|
4538
|
+
}
|
|
4252
4539
|
section.viewpoint.projection = parseProjectionValue(value, line, tokens[0].column);
|
|
4253
4540
|
return;
|
|
4254
4541
|
case "preset":
|
|
@@ -4260,6 +4547,13 @@
|
|
|
4260
4547
|
case "rotation":
|
|
4261
4548
|
section.viewpoint.rotationDeg = parseFiniteNumber2(value, line, tokens[0].column, "rotation");
|
|
4262
4549
|
return;
|
|
4550
|
+
case "camera":
|
|
4551
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.camera", {
|
|
4552
|
+
line,
|
|
4553
|
+
column: tokens[0].column
|
|
4554
|
+
});
|
|
4555
|
+
section.viewpoint.camera = parseInlineViewCamera(tokens.slice(1), line, section.viewpoint.camera);
|
|
4556
|
+
return;
|
|
4263
4557
|
case "layers":
|
|
4264
4558
|
section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line, section.sourceSchemaVersion, section.diagnostics);
|
|
4265
4559
|
return;
|
|
@@ -4274,6 +4568,28 @@
|
|
|
4274
4568
|
throw new WorldOrbitError(`Unknown viewpoint field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4275
4569
|
}
|
|
4276
4570
|
}
|
|
4571
|
+
function applyViewpointCameraField(section, tokens, line) {
|
|
4572
|
+
const key = requireUniqueField(tokens, section.seenCameraFields, line);
|
|
4573
|
+
const value = joinFieldValue(tokens, line);
|
|
4574
|
+
const camera = section.viewpoint.camera ?? createEmptyViewCamera2();
|
|
4575
|
+
switch (key) {
|
|
4576
|
+
case "azimuth":
|
|
4577
|
+
camera.azimuth = parseFiniteNumber2(value, line, tokens[0].column, "camera.azimuth");
|
|
4578
|
+
break;
|
|
4579
|
+
case "elevation":
|
|
4580
|
+
camera.elevation = parseFiniteNumber2(value, line, tokens[0].column, "camera.elevation");
|
|
4581
|
+
break;
|
|
4582
|
+
case "roll":
|
|
4583
|
+
camera.roll = parseFiniteNumber2(value, line, tokens[0].column, "camera.roll");
|
|
4584
|
+
break;
|
|
4585
|
+
case "distance":
|
|
4586
|
+
camera.distance = parsePositiveNumber2(value, line, tokens[0].column, "camera.distance");
|
|
4587
|
+
break;
|
|
4588
|
+
default:
|
|
4589
|
+
throw new WorldOrbitError(`Unknown viewpoint camera field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4590
|
+
}
|
|
4591
|
+
section.viewpoint.camera = camera;
|
|
4592
|
+
}
|
|
4277
4593
|
function applyViewpointFilterField(section, tokens, line) {
|
|
4278
4594
|
const key = requireUniqueField(tokens, section.seenFilterFields, line);
|
|
4279
4595
|
const filter = section.viewpoint.filter ?? createEmptyViewpointFilter2();
|
|
@@ -4384,6 +4700,12 @@
|
|
|
4384
4700
|
section.positionsIndent = null;
|
|
4385
4701
|
}
|
|
4386
4702
|
if (section.activePose) {
|
|
4703
|
+
if (tokens[0]?.value === "epoch" || tokens[0]?.value === "referencePlane") {
|
|
4704
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, `pose.${tokens[0].value}`, {
|
|
4705
|
+
line,
|
|
4706
|
+
column: tokens[0]?.column ?? 1
|
|
4707
|
+
});
|
|
4708
|
+
}
|
|
4387
4709
|
section.activePose.fields.push(parseEventPoseField(tokens, line, section.activePoseSeenFields));
|
|
4388
4710
|
return;
|
|
4389
4711
|
}
|
|
@@ -4438,6 +4760,20 @@
|
|
|
4438
4760
|
case "visibility":
|
|
4439
4761
|
section.event.visibility = joinFieldValue(tokens, line);
|
|
4440
4762
|
return;
|
|
4763
|
+
case "epoch":
|
|
4764
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "event.epoch", {
|
|
4765
|
+
line,
|
|
4766
|
+
column: tokens[0].column
|
|
4767
|
+
});
|
|
4768
|
+
section.event.epoch = joinFieldValue(tokens, line);
|
|
4769
|
+
return;
|
|
4770
|
+
case "referenceplane":
|
|
4771
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "event.referencePlane", {
|
|
4772
|
+
line,
|
|
4773
|
+
column: tokens[0].column
|
|
4774
|
+
});
|
|
4775
|
+
section.event.referencePlane = joinFieldValue(tokens, line);
|
|
4776
|
+
return;
|
|
4441
4777
|
case "tags":
|
|
4442
4778
|
section.event.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4443
4779
|
return;
|
|
@@ -4565,11 +4901,15 @@
|
|
|
4565
4901
|
}
|
|
4566
4902
|
function parseProjectionValue(value, line, column) {
|
|
4567
4903
|
const normalized = value.toLowerCase();
|
|
4568
|
-
if (normalized !== "topdown" && normalized !== "isometric") {
|
|
4904
|
+
if (normalized !== "topdown" && normalized !== "isometric" && normalized !== "orthographic" && normalized !== "perspective") {
|
|
4569
4905
|
throw new WorldOrbitError(`Unknown projection "${value}"`, line, column);
|
|
4570
4906
|
}
|
|
4571
4907
|
return normalized;
|
|
4572
4908
|
}
|
|
4909
|
+
function isSchema25Projection(value) {
|
|
4910
|
+
const normalized = value.toLowerCase();
|
|
4911
|
+
return normalized === "orthographic" || normalized === "perspective";
|
|
4912
|
+
}
|
|
4573
4913
|
function parsePresetValue(value, line, column) {
|
|
4574
4914
|
const normalized = value.toLowerCase();
|
|
4575
4915
|
if (normalized === "diagram" || normalized === "presentation" || normalized === "atlas-card" || normalized === "markdown") {
|
|
@@ -4599,6 +4939,48 @@
|
|
|
4599
4939
|
groupIds: []
|
|
4600
4940
|
};
|
|
4601
4941
|
}
|
|
4942
|
+
function createEmptyViewCamera2() {
|
|
4943
|
+
return {
|
|
4944
|
+
azimuth: null,
|
|
4945
|
+
elevation: null,
|
|
4946
|
+
roll: null,
|
|
4947
|
+
distance: null
|
|
4948
|
+
};
|
|
4949
|
+
}
|
|
4950
|
+
function parseInlineViewCamera(tokens, line, current) {
|
|
4951
|
+
if (tokens.length === 0 || tokens.length % 2 !== 0) {
|
|
4952
|
+
throw new WorldOrbitError('Field "camera" expects "<field> <value>" pairs', line, tokens[0]?.column ?? 1);
|
|
4953
|
+
}
|
|
4954
|
+
const camera = current ? { ...current } : createEmptyViewCamera2();
|
|
4955
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4956
|
+
for (let index = 0; index < tokens.length; index += 2) {
|
|
4957
|
+
const fieldToken = tokens[index];
|
|
4958
|
+
const valueToken = tokens[index + 1];
|
|
4959
|
+
const key = fieldToken.value.toLowerCase();
|
|
4960
|
+
if (seen.has(key)) {
|
|
4961
|
+
throw new WorldOrbitError(`Duplicate viewpoint camera field "${fieldToken.value}"`, line, fieldToken.column);
|
|
4962
|
+
}
|
|
4963
|
+
seen.add(key);
|
|
4964
|
+
const value = valueToken.value;
|
|
4965
|
+
switch (key) {
|
|
4966
|
+
case "azimuth":
|
|
4967
|
+
camera.azimuth = parseFiniteNumber2(value, line, fieldToken.column, "camera.azimuth");
|
|
4968
|
+
break;
|
|
4969
|
+
case "elevation":
|
|
4970
|
+
camera.elevation = parseFiniteNumber2(value, line, fieldToken.column, "camera.elevation");
|
|
4971
|
+
break;
|
|
4972
|
+
case "roll":
|
|
4973
|
+
camera.roll = parseFiniteNumber2(value, line, fieldToken.column, "camera.roll");
|
|
4974
|
+
break;
|
|
4975
|
+
case "distance":
|
|
4976
|
+
camera.distance = parsePositiveNumber2(value, line, fieldToken.column, "camera.distance");
|
|
4977
|
+
break;
|
|
4978
|
+
default:
|
|
4979
|
+
throw new WorldOrbitError(`Unknown viewpoint camera field "${fieldToken.value}"`, line, fieldToken.column);
|
|
4980
|
+
}
|
|
4981
|
+
}
|
|
4982
|
+
return camera;
|
|
4983
|
+
}
|
|
4602
4984
|
function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
4603
4985
|
const fields = [];
|
|
4604
4986
|
let index = 0;
|
|
@@ -4731,7 +5113,7 @@
|
|
|
4731
5113
|
object.tolerances = tolerances;
|
|
4732
5114
|
if (typedBlocks && Object.keys(typedBlocks).length > 0)
|
|
4733
5115
|
object.typedBlocks = typedBlocks;
|
|
4734
|
-
if (sourceSchemaVersion
|
|
5116
|
+
if (isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
4735
5117
|
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) {
|
|
4736
5118
|
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, node.id, node.location);
|
|
4737
5119
|
}
|
|
@@ -4747,23 +5129,25 @@
|
|
|
4747
5129
|
};
|
|
4748
5130
|
}
|
|
4749
5131
|
function normalizeDraftEventPose(rawPose) {
|
|
4750
|
-
const fieldMap = collectDraftFields(rawPose.fields);
|
|
5132
|
+
const fieldMap = collectDraftFields(rawPose.fields, "event-pose");
|
|
4751
5133
|
const placement = extractPlacementFromFieldMap(fieldMap);
|
|
4752
5134
|
return {
|
|
4753
5135
|
objectId: rawPose.objectId,
|
|
4754
5136
|
placement,
|
|
4755
5137
|
inner: parseOptionalUnitField(fieldMap.get("inner")?.[0], "inner"),
|
|
4756
|
-
outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer")
|
|
5138
|
+
outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer"),
|
|
5139
|
+
epoch: parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]),
|
|
5140
|
+
referencePlane: parseOptionalJoinedValue(fieldMap.get("referencePlane")?.[0])
|
|
4757
5141
|
};
|
|
4758
5142
|
}
|
|
4759
|
-
function collectDraftFields(fields) {
|
|
5143
|
+
function collectDraftFields(fields, _mode = "object") {
|
|
4760
5144
|
const grouped = /* @__PURE__ */ new Map();
|
|
4761
5145
|
for (const field of fields) {
|
|
4762
5146
|
const spec = getDraftObjectFieldSpec(field.key);
|
|
4763
|
-
if (!spec) {
|
|
5147
|
+
if (!spec && !EVENT_POSE_FIELD_KEYS.has(field.key)) {
|
|
4764
5148
|
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4765
5149
|
}
|
|
4766
|
-
if (!spec
|
|
5150
|
+
if (!spec?.allowRepeat && grouped.has(field.key)) {
|
|
4767
5151
|
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
4768
5152
|
}
|
|
4769
5153
|
const existing = grouped.get(field.key) ?? [];
|
|
@@ -4940,7 +5324,7 @@
|
|
|
4940
5324
|
}
|
|
4941
5325
|
}
|
|
4942
5326
|
function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
4943
|
-
if (sourceSchemaVersion
|
|
5327
|
+
if (!isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
4944
5328
|
return;
|
|
4945
5329
|
}
|
|
4946
5330
|
diagnostics.push({
|
|
@@ -4952,6 +5336,34 @@
|
|
|
4952
5336
|
column: location.column
|
|
4953
5337
|
});
|
|
4954
5338
|
}
|
|
5339
|
+
function warnIfSchema25Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
5340
|
+
if (!isSchemaOlderThan(sourceSchemaVersion, "2.5")) {
|
|
5341
|
+
return;
|
|
5342
|
+
}
|
|
5343
|
+
diagnostics.push({
|
|
5344
|
+
code: "parse.schema25.featureCompatibility",
|
|
5345
|
+
severity: "warning",
|
|
5346
|
+
source: "parse",
|
|
5347
|
+
message: `Feature "${featureName}" requires schema 2.5; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
5348
|
+
line: location.line,
|
|
5349
|
+
column: location.column
|
|
5350
|
+
});
|
|
5351
|
+
}
|
|
5352
|
+
function isSchemaOlderThan(sourceSchemaVersion, requiredVersion) {
|
|
5353
|
+
return schemaVersionRank(sourceSchemaVersion) < schemaVersionRank(requiredVersion);
|
|
5354
|
+
}
|
|
5355
|
+
function schemaVersionRank(version) {
|
|
5356
|
+
switch (version) {
|
|
5357
|
+
case "2.0-draft":
|
|
5358
|
+
return 0;
|
|
5359
|
+
case "2.0":
|
|
5360
|
+
return 1;
|
|
5361
|
+
case "2.1":
|
|
5362
|
+
return 2;
|
|
5363
|
+
case "2.5":
|
|
5364
|
+
return 3;
|
|
5365
|
+
}
|
|
5366
|
+
}
|
|
4955
5367
|
function preprocessAtlasSource(source) {
|
|
4956
5368
|
const chars = [...source];
|
|
4957
5369
|
const comments = [];
|
|
@@ -5039,8 +5451,9 @@
|
|
|
5039
5451
|
}
|
|
5040
5452
|
|
|
5041
5453
|
// packages/core/dist/load.js
|
|
5042
|
-
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;
|
|
5454
|
+
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1|\.5)?$/i;
|
|
5043
5455
|
var ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
|
|
5456
|
+
var ATLAS_SCHEMA_25_PATTERN = /^schema\s+2\.5$/i;
|
|
5044
5457
|
var LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
|
|
5045
5458
|
function detectWorldOrbitSchemaVersion(source) {
|
|
5046
5459
|
for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
|
|
@@ -5054,6 +5467,9 @@
|
|
|
5054
5467
|
if (ATLAS_SCHEMA_21_PATTERN.test(trimmed)) {
|
|
5055
5468
|
return "2.1";
|
|
5056
5469
|
}
|
|
5470
|
+
if (ATLAS_SCHEMA_25_PATTERN.test(trimmed)) {
|
|
5471
|
+
return "2.5";
|
|
5472
|
+
}
|
|
5057
5473
|
if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
|
|
5058
5474
|
return "2.0";
|
|
5059
5475
|
}
|
|
@@ -5114,7 +5530,7 @@
|
|
|
5114
5530
|
}
|
|
5115
5531
|
function loadWorldOrbitSourceWithDiagnostics(source) {
|
|
5116
5532
|
const schemaVersion = detectWorldOrbitSchemaVersion(source);
|
|
5117
|
-
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1") {
|
|
5533
|
+
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1" || schemaVersion === "2.5") {
|
|
5118
5534
|
return loadAtlasSourceWithDiagnostics(source, schemaVersion);
|
|
5119
5535
|
}
|
|
5120
5536
|
let ast;
|
|
@@ -6188,6 +6604,7 @@
|
|
|
6188
6604
|
padding: options.padding,
|
|
6189
6605
|
preset: options.preset,
|
|
6190
6606
|
projection: options.projection,
|
|
6607
|
+
camera: options.camera ? { ...options.camera } : null,
|
|
6191
6608
|
scaleModel: options.scaleModel ? { ...options.scaleModel } : void 0,
|
|
6192
6609
|
theme: options.theme,
|
|
6193
6610
|
layers: options.layers,
|
|
@@ -6476,6 +6893,11 @@
|
|
|
6476
6893
|
if (currentInput.kind !== "scene" && viewpoint.projection !== scene.projection) {
|
|
6477
6894
|
nextRenderOptions.projection = viewpoint.projection;
|
|
6478
6895
|
}
|
|
6896
|
+
if (viewpoint.camera) {
|
|
6897
|
+
nextRenderOptions.camera = { ...viewpoint.camera };
|
|
6898
|
+
} else if (renderOptions.camera) {
|
|
6899
|
+
nextRenderOptions.camera = null;
|
|
6900
|
+
}
|
|
6479
6901
|
if (viewpointLayers) {
|
|
6480
6902
|
nextRenderOptions.layers = viewpointLayers;
|
|
6481
6903
|
}
|
|
@@ -7082,6 +7504,7 @@
|
|
|
7082
7504
|
function cloneRenderOptions(renderOptions) {
|
|
7083
7505
|
return {
|
|
7084
7506
|
...renderOptions,
|
|
7507
|
+
camera: renderOptions.camera ? { ...renderOptions.camera } : null,
|
|
7085
7508
|
filter: renderOptions.filter ? { ...renderOptions.filter } : void 0,
|
|
7086
7509
|
scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : void 0,
|
|
7087
7510
|
layers: renderOptions.layers ? { ...renderOptions.layers } : void 0,
|
|
@@ -7093,6 +7516,7 @@
|
|
|
7093
7516
|
return {
|
|
7094
7517
|
...current,
|
|
7095
7518
|
...next,
|
|
7519
|
+
camera: next.camera !== void 0 ? next.camera ? { ...next.camera } : null : current.camera ? { ...current.camera } : null,
|
|
7096
7520
|
filter: next.filter !== void 0 ? normalizeViewerFilter(next.filter) : current.filter ? { ...current.filter } : void 0,
|
|
7097
7521
|
scaleModel: next.scaleModel ? {
|
|
7098
7522
|
...current.scaleModel ?? {},
|
|
@@ -7106,7 +7530,7 @@
|
|
|
7106
7530
|
};
|
|
7107
7531
|
}
|
|
7108
7532
|
function hasSceneAffectingRenderOptions(options) {
|
|
7109
|
-
return options.width !== void 0 || options.height !== void 0 || options.padding !== void 0 || options.preset !== void 0 || options.projection !== void 0 || options.scaleModel !== void 0 || options.activeEventId !== void 0;
|
|
7533
|
+
return options.width !== void 0 || options.height !== void 0 || options.padding !== void 0 || options.preset !== void 0 || options.projection !== void 0 || options.camera !== void 0 || options.scaleModel !== void 0 || options.activeEventId !== void 0;
|
|
7110
7534
|
}
|
|
7111
7535
|
function resolveSourceRenderOptions2(loaded, renderOptions) {
|
|
7112
7536
|
const atlasDocument = loaded.atlasDocument ?? loaded.draftDocument;
|
|
@@ -7703,6 +8127,8 @@
|
|
|
7703
8127
|
}
|
|
7704
8128
|
function buildInspectorSnapshot() {
|
|
7705
8129
|
const activeViewer = requireViewer();
|
|
8130
|
+
const scene = activeViewer.getScene();
|
|
8131
|
+
const camera = scene.camera;
|
|
7706
8132
|
return {
|
|
7707
8133
|
selection: activeViewer.getSelectionDetails(),
|
|
7708
8134
|
activeViewpoint: activeViewer.getActiveViewpoint(),
|
|
@@ -7710,14 +8136,21 @@
|
|
|
7710
8136
|
atlasState: activeViewer.getAtlasState(),
|
|
7711
8137
|
visibleObjectIds: activeViewer.getVisibleObjects().map((object) => object.objectId),
|
|
7712
8138
|
scene: {
|
|
7713
|
-
title:
|
|
7714
|
-
projection:
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
8139
|
+
title: scene.title,
|
|
8140
|
+
projection: scene.projection,
|
|
8141
|
+
renderProjection: scene.renderProjection,
|
|
8142
|
+
camera: camera ? {
|
|
8143
|
+
azimuth: camera.azimuth,
|
|
8144
|
+
elevation: camera.elevation,
|
|
8145
|
+
roll: camera.roll,
|
|
8146
|
+
distance: camera.distance
|
|
8147
|
+
} : null,
|
|
8148
|
+
renderPreset: scene.renderPreset,
|
|
8149
|
+
groupCount: scene.groups.length,
|
|
8150
|
+
semanticGroupCount: scene.semanticGroups.length,
|
|
8151
|
+
relationCount: scene.relations.length,
|
|
8152
|
+
eventCount: scene.events.length,
|
|
8153
|
+
viewpointCount: scene.viewpoints.length
|
|
7721
8154
|
}
|
|
7722
8155
|
};
|
|
7723
8156
|
}
|