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
|
@@ -998,7 +998,9 @@
|
|
|
998
998
|
const height = frame.height;
|
|
999
999
|
const padding = frame.padding;
|
|
1000
1000
|
const layoutPreset = resolveLayoutPreset(document);
|
|
1001
|
-
const
|
|
1001
|
+
const schemaProjection = resolveProjection(document, options.projection);
|
|
1002
|
+
const camera = normalizeViewCamera(options.camera ?? null);
|
|
1003
|
+
const renderProjection = resolveRenderProjection(schemaProjection, camera);
|
|
1002
1004
|
const scaleModel = resolveScaleModel(layoutPreset, options.scaleModel);
|
|
1003
1005
|
const spacingFactor = layoutPresetSpacing(layoutPreset);
|
|
1004
1006
|
const systemId = document.system?.id ?? null;
|
|
@@ -1041,7 +1043,7 @@
|
|
|
1041
1043
|
surfaceChildren,
|
|
1042
1044
|
objectMap,
|
|
1043
1045
|
spacingFactor,
|
|
1044
|
-
projection,
|
|
1046
|
+
projection: renderProjection,
|
|
1045
1047
|
scaleModel
|
|
1046
1048
|
};
|
|
1047
1049
|
const primaryRoot = rootObjects.find((object) => object.type === "star") ?? rootObjects[0] ?? null;
|
|
@@ -1053,7 +1055,7 @@
|
|
|
1053
1055
|
const rootRingRadius = Math.min(width, height) * 0.28 * spacingFactor * scaleModel.orbitDistanceMultiplier;
|
|
1054
1056
|
secondaryRoots.forEach((object, index) => {
|
|
1055
1057
|
const angle = angleForIndex(index, secondaryRoots.length, -Math.PI / 2);
|
|
1056
|
-
const offset = projectPolarOffset(angle, rootRingRadius,
|
|
1058
|
+
const offset = projectPolarOffset(angle, rootRingRadius, renderProjection, 1);
|
|
1057
1059
|
placeObject(object, centerX + offset.x, centerY + offset.y, 0, positions, orbitDrafts, leaderDrafts, context);
|
|
1058
1060
|
});
|
|
1059
1061
|
}
|
|
@@ -1115,27 +1117,34 @@
|
|
|
1115
1117
|
const layers = createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels);
|
|
1116
1118
|
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, scaleModel.labelMultiplier);
|
|
1117
1119
|
const semanticGroups = createSceneSemanticGroups(document, objects);
|
|
1118
|
-
const viewpoints = createSceneViewpoints(document,
|
|
1120
|
+
const viewpoints = createSceneViewpoints(document, schemaProjection, frame.preset, relationships, objectMap);
|
|
1119
1121
|
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, scaleModel.labelMultiplier);
|
|
1120
1122
|
return {
|
|
1121
1123
|
width,
|
|
1122
1124
|
height,
|
|
1123
1125
|
padding,
|
|
1124
1126
|
renderPreset: frame.preset,
|
|
1125
|
-
projection,
|
|
1127
|
+
projection: schemaProjection,
|
|
1128
|
+
renderProjection,
|
|
1129
|
+
camera,
|
|
1126
1130
|
scaleModel,
|
|
1127
1131
|
title: String(document.system?.title ?? document.system?.properties.title ?? document.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1128
|
-
subtitle:
|
|
1132
|
+
subtitle: buildSceneSubtitle(schemaProjection, renderProjection, layoutPreset, camera),
|
|
1129
1133
|
systemId,
|
|
1130
|
-
viewMode:
|
|
1134
|
+
viewMode: schemaProjection,
|
|
1131
1135
|
layoutPreset,
|
|
1132
1136
|
metadata: {
|
|
1133
1137
|
format: document.format,
|
|
1134
1138
|
version: document.version,
|
|
1135
|
-
view:
|
|
1139
|
+
view: schemaProjection,
|
|
1140
|
+
renderProjection,
|
|
1136
1141
|
scale: String(document.system?.properties.scale ?? layoutPreset),
|
|
1137
1142
|
units: String(document.system?.properties.units ?? "mixed"),
|
|
1138
|
-
preset: frame.preset ?? "custom"
|
|
1143
|
+
preset: frame.preset ?? "custom",
|
|
1144
|
+
...camera?.azimuth !== null ? { "camera.azimuth": String(camera?.azimuth) } : {},
|
|
1145
|
+
...camera?.elevation !== null ? { "camera.elevation": String(camera?.elevation) } : {},
|
|
1146
|
+
...camera?.roll !== null ? { "camera.roll": String(camera?.roll) } : {},
|
|
1147
|
+
...camera?.distance !== null ? { "camera.distance": String(camera?.distance) } : {}
|
|
1139
1148
|
},
|
|
1140
1149
|
contentBounds,
|
|
1141
1150
|
layers,
|
|
@@ -1172,21 +1181,42 @@
|
|
|
1172
1181
|
return cloned;
|
|
1173
1182
|
}
|
|
1174
1183
|
const objectMap = new Map(cloned.map((object) => [object.id, object]));
|
|
1184
|
+
const referencedIds = /* @__PURE__ */ new Set([
|
|
1185
|
+
...activeEvent.targetObjectId ? [activeEvent.targetObjectId] : [],
|
|
1186
|
+
...activeEvent.participantObjectIds,
|
|
1187
|
+
...activeEvent.positions.map((pose) => pose.objectId)
|
|
1188
|
+
]);
|
|
1189
|
+
for (const objectId of referencedIds) {
|
|
1190
|
+
const object = objectMap.get(objectId);
|
|
1191
|
+
if (!object) {
|
|
1192
|
+
continue;
|
|
1193
|
+
}
|
|
1194
|
+
if (activeEvent.epoch) {
|
|
1195
|
+
object.epoch = activeEvent.epoch;
|
|
1196
|
+
}
|
|
1197
|
+
if (activeEvent.referencePlane) {
|
|
1198
|
+
object.referencePlane = activeEvent.referencePlane;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1175
1201
|
for (const pose of activeEvent.positions) {
|
|
1176
1202
|
const object = objectMap.get(pose.objectId);
|
|
1177
1203
|
if (!object) {
|
|
1178
1204
|
continue;
|
|
1179
1205
|
}
|
|
1180
|
-
|
|
1206
|
+
if (pose.placement) {
|
|
1207
|
+
object.placement = structuredClone(pose.placement);
|
|
1208
|
+
}
|
|
1181
1209
|
if (pose.inner) {
|
|
1182
1210
|
object.properties.inner = { ...pose.inner };
|
|
1183
|
-
} else {
|
|
1184
|
-
delete object.properties.inner;
|
|
1185
1211
|
}
|
|
1186
1212
|
if (pose.outer) {
|
|
1187
1213
|
object.properties.outer = { ...pose.outer };
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1214
|
+
}
|
|
1215
|
+
if (pose.epoch) {
|
|
1216
|
+
object.epoch = pose.epoch;
|
|
1217
|
+
}
|
|
1218
|
+
if (pose.referencePlane) {
|
|
1219
|
+
object.referencePlane = pose.referencePlane;
|
|
1190
1220
|
}
|
|
1191
1221
|
}
|
|
1192
1222
|
return cloned;
|
|
@@ -1227,10 +1257,59 @@
|
|
|
1227
1257
|
}
|
|
1228
1258
|
}
|
|
1229
1259
|
function resolveProjection(document, projection) {
|
|
1230
|
-
if (projection === "topdown" || projection === "isometric") {
|
|
1260
|
+
if (projection === "topdown" || projection === "isometric" || projection === "orthographic" || projection === "perspective") {
|
|
1231
1261
|
return projection;
|
|
1232
1262
|
}
|
|
1233
|
-
|
|
1263
|
+
const documentView = String(document.system?.properties.view ?? "topdown").toLowerCase();
|
|
1264
|
+
return parseViewProjection(documentView) ?? "topdown";
|
|
1265
|
+
}
|
|
1266
|
+
function resolveRenderProjection(projection, camera) {
|
|
1267
|
+
switch (projection) {
|
|
1268
|
+
case "topdown":
|
|
1269
|
+
return "topdown";
|
|
1270
|
+
case "isometric":
|
|
1271
|
+
return "isometric";
|
|
1272
|
+
case "orthographic":
|
|
1273
|
+
return camera && (camera.azimuth !== null || camera.elevation !== null || camera.roll !== null) ? "isometric" : "topdown";
|
|
1274
|
+
case "perspective":
|
|
1275
|
+
return "isometric";
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
function normalizeViewCamera(camera) {
|
|
1279
|
+
if (!camera) {
|
|
1280
|
+
return null;
|
|
1281
|
+
}
|
|
1282
|
+
const normalized = {
|
|
1283
|
+
azimuth: normalizeFiniteCameraValue(camera.azimuth),
|
|
1284
|
+
elevation: normalizeFiniteCameraValue(camera.elevation),
|
|
1285
|
+
roll: normalizeFiniteCameraValue(camera.roll),
|
|
1286
|
+
distance: normalizePositiveCameraDistance(camera.distance)
|
|
1287
|
+
};
|
|
1288
|
+
return normalized.azimuth !== null || normalized.elevation !== null || normalized.roll !== null || normalized.distance !== null ? normalized : null;
|
|
1289
|
+
}
|
|
1290
|
+
function normalizeFiniteCameraValue(value) {
|
|
1291
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1292
|
+
}
|
|
1293
|
+
function normalizePositiveCameraDistance(value) {
|
|
1294
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
1295
|
+
}
|
|
1296
|
+
function buildSceneSubtitle(projection, renderProjection, layoutPreset, camera) {
|
|
1297
|
+
const parts = [`${capitalizeLabel(projection)} view`, `${capitalizeLabel(layoutPreset)} layout`];
|
|
1298
|
+
if (projection !== renderProjection) {
|
|
1299
|
+
parts.push(`2D ${renderProjection} fallback`);
|
|
1300
|
+
}
|
|
1301
|
+
if (camera) {
|
|
1302
|
+
const cameraParts = [
|
|
1303
|
+
camera.azimuth !== null ? `az ${camera.azimuth}` : null,
|
|
1304
|
+
camera.elevation !== null ? `el ${camera.elevation}` : null,
|
|
1305
|
+
camera.roll !== null ? `roll ${camera.roll}` : null,
|
|
1306
|
+
camera.distance !== null ? `dist ${camera.distance}` : null
|
|
1307
|
+
].filter(Boolean);
|
|
1308
|
+
if (cameraParts.length > 0) {
|
|
1309
|
+
parts.push(`camera ${cameraParts.join(" / ")}`);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
return parts.join(" - ");
|
|
1234
1313
|
}
|
|
1235
1314
|
function resolveScaleModel(layoutPreset, overrides) {
|
|
1236
1315
|
const defaults = defaultScaleModel(layoutPreset);
|
|
@@ -1666,6 +1745,8 @@
|
|
|
1666
1745
|
function createGeneratedOverviewViewpoint(document, projection, preset) {
|
|
1667
1746
|
const title = document.system?.title ?? document.system?.properties.title;
|
|
1668
1747
|
const label = title ? `${String(title)} Overview` : "Overview";
|
|
1748
|
+
const camera = normalizeViewCamera(null);
|
|
1749
|
+
const renderProjection = resolveRenderProjection(projection, camera);
|
|
1669
1750
|
return {
|
|
1670
1751
|
id: "overview",
|
|
1671
1752
|
label,
|
|
@@ -1674,6 +1755,8 @@
|
|
|
1674
1755
|
selectedObjectId: null,
|
|
1675
1756
|
eventIds: [],
|
|
1676
1757
|
projection,
|
|
1758
|
+
renderProjection,
|
|
1759
|
+
camera,
|
|
1677
1760
|
preset,
|
|
1678
1761
|
rotationDeg: 0,
|
|
1679
1762
|
scale: null,
|
|
@@ -1723,6 +1806,30 @@
|
|
|
1723
1806
|
case "angle":
|
|
1724
1807
|
draft.rotationDeg = parseFiniteNumber(normalizedValue) ?? draft.rotationDeg ?? 0;
|
|
1725
1808
|
return;
|
|
1809
|
+
case "camera.azimuth":
|
|
1810
|
+
draft.camera = {
|
|
1811
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1812
|
+
azimuth: parseFiniteNumber(normalizedValue)
|
|
1813
|
+
};
|
|
1814
|
+
return;
|
|
1815
|
+
case "camera.elevation":
|
|
1816
|
+
draft.camera = {
|
|
1817
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1818
|
+
elevation: parseFiniteNumber(normalizedValue)
|
|
1819
|
+
};
|
|
1820
|
+
return;
|
|
1821
|
+
case "camera.roll":
|
|
1822
|
+
draft.camera = {
|
|
1823
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1824
|
+
roll: parseFiniteNumber(normalizedValue)
|
|
1825
|
+
};
|
|
1826
|
+
return;
|
|
1827
|
+
case "camera.distance":
|
|
1828
|
+
draft.camera = {
|
|
1829
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1830
|
+
distance: parsePositiveNumber(normalizedValue)
|
|
1831
|
+
};
|
|
1832
|
+
return;
|
|
1726
1833
|
case "zoom":
|
|
1727
1834
|
case "scale":
|
|
1728
1835
|
draft.scale = parsePositiveNumber(normalizedValue);
|
|
@@ -1762,6 +1869,9 @@
|
|
|
1762
1869
|
const selectedObjectId = draft.select && objectMap.has(draft.select) ? draft.select : objectId;
|
|
1763
1870
|
const filter = normalizeViewpointFilter(draft.filter);
|
|
1764
1871
|
const label = draft.label?.trim() || humanizeIdentifier(draft.id);
|
|
1872
|
+
const resolvedProjection = draft.projection ?? projection;
|
|
1873
|
+
const camera = normalizeViewCamera(draft.camera ?? null);
|
|
1874
|
+
const renderProjection = resolveRenderProjection(resolvedProjection, camera);
|
|
1765
1875
|
return {
|
|
1766
1876
|
id: draft.id,
|
|
1767
1877
|
label,
|
|
@@ -1769,7 +1879,9 @@
|
|
|
1769
1879
|
objectId,
|
|
1770
1880
|
selectedObjectId,
|
|
1771
1881
|
eventIds: [...new Set(draft.eventIds ?? [])],
|
|
1772
|
-
projection:
|
|
1882
|
+
projection: resolvedProjection,
|
|
1883
|
+
renderProjection,
|
|
1884
|
+
camera,
|
|
1773
1885
|
preset: draft.preset ?? preset,
|
|
1774
1886
|
rotationDeg: draft.rotationDeg ?? 0,
|
|
1775
1887
|
scale: draft.scale ?? null,
|
|
@@ -1786,6 +1898,14 @@
|
|
|
1786
1898
|
groupIds: []
|
|
1787
1899
|
};
|
|
1788
1900
|
}
|
|
1901
|
+
function createEmptyViewCamera() {
|
|
1902
|
+
return {
|
|
1903
|
+
azimuth: null,
|
|
1904
|
+
elevation: null,
|
|
1905
|
+
roll: null,
|
|
1906
|
+
distance: null
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1789
1909
|
function normalizeViewpointFilter(filter) {
|
|
1790
1910
|
if (!filter) {
|
|
1791
1911
|
return null;
|
|
@@ -1799,7 +1919,18 @@
|
|
|
1799
1919
|
return normalized.query || normalized.objectTypes.length > 0 || normalized.tags.length > 0 || normalized.groupIds.length > 0 ? normalized : null;
|
|
1800
1920
|
}
|
|
1801
1921
|
function parseViewProjection(value) {
|
|
1802
|
-
|
|
1922
|
+
switch (value.toLowerCase()) {
|
|
1923
|
+
case "topdown":
|
|
1924
|
+
return "topdown";
|
|
1925
|
+
case "isometric":
|
|
1926
|
+
return "isometric";
|
|
1927
|
+
case "orthographic":
|
|
1928
|
+
return "orthographic";
|
|
1929
|
+
case "perspective":
|
|
1930
|
+
return "perspective";
|
|
1931
|
+
default:
|
|
1932
|
+
return null;
|
|
1933
|
+
}
|
|
1803
1934
|
}
|
|
1804
1935
|
function parseRenderPreset(value) {
|
|
1805
1936
|
const normalized = value.toLowerCase();
|
|
@@ -1837,7 +1968,7 @@
|
|
|
1837
1968
|
}
|
|
1838
1969
|
function parseViewpointGroups(value, document, relationships, objectMap) {
|
|
1839
1970
|
return splitListValue(value).map((entry) => {
|
|
1840
|
-
if (document.schemaVersion === "2.1" || document.groups.some((group) => group.id === entry)) {
|
|
1971
|
+
if (document.schemaVersion === "2.1" || document.schemaVersion === "2.5" || document.groups.some((group) => group.id === entry)) {
|
|
1841
1972
|
return entry;
|
|
1842
1973
|
}
|
|
1843
1974
|
if (entry.startsWith("wo-") && entry.endsWith("-group")) {
|
|
@@ -2630,8 +2761,8 @@
|
|
|
2630
2761
|
}
|
|
2631
2762
|
return {
|
|
2632
2763
|
format: "worldorbit",
|
|
2633
|
-
version: "2.
|
|
2634
|
-
schemaVersion: "2.
|
|
2764
|
+
version: "2.5",
|
|
2765
|
+
schemaVersion: "2.5",
|
|
2635
2766
|
sourceVersion: document.version,
|
|
2636
2767
|
system,
|
|
2637
2768
|
groups: structuredClone(document.groups ?? []),
|
|
@@ -2690,8 +2821,9 @@
|
|
|
2690
2821
|
};
|
|
2691
2822
|
}
|
|
2692
2823
|
function createDraftDefaults(document, preset, projection) {
|
|
2824
|
+
const rawView = typeof document.system?.properties.view === "string" ? document.system.properties.view.toLowerCase() : null;
|
|
2693
2825
|
return {
|
|
2694
|
-
view:
|
|
2826
|
+
view: rawView === "topdown" || rawView === "isometric" || rawView === "orthographic" || rawView === "perspective" ? rawView : projection,
|
|
2695
2827
|
scale: typeof document.system?.properties.scale === "string" ? document.system.properties.scale : null,
|
|
2696
2828
|
units: typeof document.system?.properties.units === "string" ? document.system.properties.units : null,
|
|
2697
2829
|
preset,
|
|
@@ -2798,6 +2930,7 @@
|
|
|
2798
2930
|
preset: viewpoint.preset,
|
|
2799
2931
|
zoom: viewpoint.scale,
|
|
2800
2932
|
rotationDeg: viewpoint.rotationDeg,
|
|
2933
|
+
camera: viewpoint.camera ? { ...viewpoint.camera } : null,
|
|
2801
2934
|
layers: { ...viewpoint.layers },
|
|
2802
2935
|
filter: viewpoint.filter ? {
|
|
2803
2936
|
query: viewpoint.filter.query,
|
|
@@ -2839,7 +2972,9 @@
|
|
|
2839
2972
|
objectId: pose.objectId,
|
|
2840
2973
|
placement: clonePlacement(pose.placement),
|
|
2841
2974
|
inner: pose.inner ? { ...pose.inner } : void 0,
|
|
2842
|
-
outer: pose.outer ? { ...pose.outer } : void 0
|
|
2975
|
+
outer: pose.outer ? { ...pose.outer } : void 0,
|
|
2976
|
+
epoch: pose.epoch ?? null,
|
|
2977
|
+
referencePlane: pose.referencePlane ?? null
|
|
2843
2978
|
};
|
|
2844
2979
|
}
|
|
2845
2980
|
function clonePlacement(placement) {
|
|
@@ -2854,21 +2989,42 @@
|
|
|
2854
2989
|
return;
|
|
2855
2990
|
}
|
|
2856
2991
|
const objectMap = new Map(objects.map((object) => [object.id, object]));
|
|
2992
|
+
const referencedIds = /* @__PURE__ */ new Set([
|
|
2993
|
+
...event.targetObjectId ? [event.targetObjectId] : [],
|
|
2994
|
+
...event.participantObjectIds,
|
|
2995
|
+
...event.positions.map((pose) => pose.objectId)
|
|
2996
|
+
]);
|
|
2997
|
+
for (const objectId of referencedIds) {
|
|
2998
|
+
const object = objectMap.get(objectId);
|
|
2999
|
+
if (!object) {
|
|
3000
|
+
continue;
|
|
3001
|
+
}
|
|
3002
|
+
if (event.epoch) {
|
|
3003
|
+
object.epoch = event.epoch;
|
|
3004
|
+
}
|
|
3005
|
+
if (event.referencePlane) {
|
|
3006
|
+
object.referencePlane = event.referencePlane;
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
2857
3009
|
for (const pose of event.positions) {
|
|
2858
3010
|
const object = objectMap.get(pose.objectId);
|
|
2859
3011
|
if (!object) {
|
|
2860
3012
|
continue;
|
|
2861
3013
|
}
|
|
2862
|
-
|
|
3014
|
+
if (pose.placement) {
|
|
3015
|
+
object.placement = clonePlacement(pose.placement);
|
|
3016
|
+
}
|
|
2863
3017
|
if (pose.inner) {
|
|
2864
3018
|
object.properties.inner = { ...pose.inner };
|
|
2865
|
-
} else {
|
|
2866
|
-
delete object.properties.inner;
|
|
2867
3019
|
}
|
|
2868
3020
|
if (pose.outer) {
|
|
2869
3021
|
object.properties.outer = { ...pose.outer };
|
|
2870
|
-
}
|
|
2871
|
-
|
|
3022
|
+
}
|
|
3023
|
+
if (pose.epoch) {
|
|
3024
|
+
object.epoch = pose.epoch;
|
|
3025
|
+
}
|
|
3026
|
+
if (pose.referencePlane) {
|
|
3027
|
+
object.referencePlane = pose.referencePlane;
|
|
2872
3028
|
}
|
|
2873
3029
|
}
|
|
2874
3030
|
}
|
|
@@ -2953,6 +3109,18 @@
|
|
|
2953
3109
|
if (viewpoint.rotationDeg !== 0) {
|
|
2954
3110
|
info2[`${prefix}.rotation`] = String(viewpoint.rotationDeg);
|
|
2955
3111
|
}
|
|
3112
|
+
if (viewpoint.camera?.azimuth !== null) {
|
|
3113
|
+
info2[`${prefix}.camera.azimuth`] = String(viewpoint.camera?.azimuth);
|
|
3114
|
+
}
|
|
3115
|
+
if (viewpoint.camera?.elevation !== null) {
|
|
3116
|
+
info2[`${prefix}.camera.elevation`] = String(viewpoint.camera?.elevation);
|
|
3117
|
+
}
|
|
3118
|
+
if (viewpoint.camera?.roll !== null) {
|
|
3119
|
+
info2[`${prefix}.camera.roll`] = String(viewpoint.camera?.roll);
|
|
3120
|
+
}
|
|
3121
|
+
if (viewpoint.camera?.distance !== null) {
|
|
3122
|
+
info2[`${prefix}.camera.distance`] = String(viewpoint.camera?.distance);
|
|
3123
|
+
}
|
|
2956
3124
|
const serializedLayers = serializeViewpointLayers(viewpoint.layers);
|
|
2957
3125
|
if (serializedLayers) {
|
|
2958
3126
|
info2[`${prefix}.layers`] = serializedLayers;
|
|
@@ -3049,26 +3217,26 @@
|
|
|
3049
3217
|
];
|
|
3050
3218
|
function formatDocument(document, options = {}) {
|
|
3051
3219
|
const schema = options.schema ?? "auto";
|
|
3052
|
-
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";
|
|
3220
|
+
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";
|
|
3053
3221
|
if (useDraft) {
|
|
3054
3222
|
if (schema === "2.0-draft") {
|
|
3055
|
-
const legacyDraftDocument = document.version === "2.0-draft" ? document : document.version === "2.0" || document.version === "2.1" ? {
|
|
3223
|
+
const legacyDraftDocument = document.version === "2.0-draft" ? document : document.version === "2.0" || document.version === "2.1" || document.version === "2.5" ? {
|
|
3056
3224
|
...document,
|
|
3057
3225
|
version: "2.0-draft",
|
|
3058
3226
|
schemaVersion: "2.0-draft"
|
|
3059
3227
|
} : upgradeDocumentToDraftV2(document);
|
|
3060
3228
|
return formatDraftDocument(legacyDraftDocument);
|
|
3061
3229
|
}
|
|
3062
|
-
const atlasDocument = document.version === "2.0" || document.version === "2.1" ? document : document.version === "2.0-draft" ? {
|
|
3230
|
+
const atlasDocument = document.version === "2.0" || document.version === "2.1" || document.version === "2.5" ? document : document.version === "2.0-draft" ? {
|
|
3063
3231
|
...document,
|
|
3064
3232
|
version: "2.0",
|
|
3065
3233
|
schemaVersion: "2.0"
|
|
3066
3234
|
} : upgradeDocumentToV2(document);
|
|
3067
|
-
if (schema === "2.1" && atlasDocument.version !==
|
|
3235
|
+
if ((schema === "2.0" || schema === "2.1" || schema === "2.5") && atlasDocument.version !== schema) {
|
|
3068
3236
|
return formatAtlasDocument({
|
|
3069
3237
|
...atlasDocument,
|
|
3070
|
-
version:
|
|
3071
|
-
schemaVersion:
|
|
3238
|
+
version: schema,
|
|
3239
|
+
schemaVersion: schema
|
|
3072
3240
|
});
|
|
3073
3241
|
}
|
|
3074
3242
|
return formatAtlasDocument(atlasDocument);
|
|
@@ -3345,6 +3513,21 @@
|
|
|
3345
3513
|
if (viewpoint.rotationDeg !== 0) {
|
|
3346
3514
|
lines.push(` rotation ${viewpoint.rotationDeg}`);
|
|
3347
3515
|
}
|
|
3516
|
+
if (viewpoint.camera && hasCameraValues(viewpoint.camera)) {
|
|
3517
|
+
lines.push(" camera");
|
|
3518
|
+
if (viewpoint.camera.azimuth !== null) {
|
|
3519
|
+
lines.push(` azimuth ${viewpoint.camera.azimuth}`);
|
|
3520
|
+
}
|
|
3521
|
+
if (viewpoint.camera.elevation !== null) {
|
|
3522
|
+
lines.push(` elevation ${viewpoint.camera.elevation}`);
|
|
3523
|
+
}
|
|
3524
|
+
if (viewpoint.camera.roll !== null) {
|
|
3525
|
+
lines.push(` roll ${viewpoint.camera.roll}`);
|
|
3526
|
+
}
|
|
3527
|
+
if (viewpoint.camera.distance !== null) {
|
|
3528
|
+
lines.push(` distance ${viewpoint.camera.distance}`);
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3348
3531
|
const layerTokens = formatDraftLayers(viewpoint.layers);
|
|
3349
3532
|
if (layerTokens.length > 0) {
|
|
3350
3533
|
lines.push(` layers ${layerTokens.join(" ")}`);
|
|
@@ -3444,6 +3627,12 @@
|
|
|
3444
3627
|
if (event.visibility) {
|
|
3445
3628
|
lines.push(` visibility ${quoteIfNeeded(event.visibility)}`);
|
|
3446
3629
|
}
|
|
3630
|
+
if (event.epoch) {
|
|
3631
|
+
lines.push(` epoch ${quoteIfNeeded(event.epoch)}`);
|
|
3632
|
+
}
|
|
3633
|
+
if (event.referencePlane) {
|
|
3634
|
+
lines.push(` referencePlane ${quoteIfNeeded(event.referencePlane)}`);
|
|
3635
|
+
}
|
|
3447
3636
|
if (event.tags.length > 0) {
|
|
3448
3637
|
lines.push(` tags ${event.tags.map(quoteIfNeeded).join(" ")}`);
|
|
3449
3638
|
}
|
|
@@ -3468,10 +3657,15 @@
|
|
|
3468
3657
|
function formatEventPoseFields(pose) {
|
|
3469
3658
|
return [
|
|
3470
3659
|
...formatPlacement(pose.placement),
|
|
3660
|
+
...pose.epoch ? [`epoch ${quoteIfNeeded(pose.epoch)}`] : [],
|
|
3661
|
+
...pose.referencePlane ? [`referencePlane ${quoteIfNeeded(pose.referencePlane)}`] : [],
|
|
3471
3662
|
...formatOptionalUnit("inner", pose.inner),
|
|
3472
3663
|
...formatOptionalUnit("outer", pose.outer)
|
|
3473
3664
|
];
|
|
3474
3665
|
}
|
|
3666
|
+
function hasCameraValues(camera) {
|
|
3667
|
+
return camera.azimuth !== null || camera.elevation !== null || camera.roll !== null || camera.distance !== null;
|
|
3668
|
+
}
|
|
3475
3669
|
function formatValue(value) {
|
|
3476
3670
|
if (Array.isArray(value)) {
|
|
3477
3671
|
return value.map((item) => quoteIfNeeded(item)).join(" ");
|
|
@@ -3765,13 +3959,13 @@
|
|
|
3765
3959
|
validateRelation(relation, objectMap, diagnostics);
|
|
3766
3960
|
}
|
|
3767
3961
|
for (const viewpoint of document.system?.viewpoints ?? []) {
|
|
3768
|
-
validateViewpoint(viewpoint
|
|
3962
|
+
validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, diagnostics, objectMap);
|
|
3769
3963
|
}
|
|
3770
3964
|
for (const object of document.objects) {
|
|
3771
3965
|
validateObject(object, document.system, objectMap, groupIds, diagnostics);
|
|
3772
3966
|
}
|
|
3773
3967
|
for (const event of document.events) {
|
|
3774
|
-
validateEvent(event, objectMap, diagnostics);
|
|
3968
|
+
validateEvent(event, document.system, objectMap, diagnostics);
|
|
3775
3969
|
}
|
|
3776
3970
|
return diagnostics;
|
|
3777
3971
|
}
|
|
@@ -3790,21 +3984,24 @@
|
|
|
3790
3984
|
diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
|
|
3791
3985
|
}
|
|
3792
3986
|
}
|
|
3793
|
-
function validateViewpoint(
|
|
3794
|
-
|
|
3987
|
+
function validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, diagnostics, objectMap) {
|
|
3988
|
+
const filter = viewpoint.filter;
|
|
3989
|
+
if (sourceSchemaVersion === "2.1" || sourceSchemaVersion === "2.5") {
|
|
3795
3990
|
if (filter) {
|
|
3796
3991
|
for (const groupId of filter.groupIds) {
|
|
3797
3992
|
if (!groupIds.has(groupId)) {
|
|
3798
|
-
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${
|
|
3993
|
+
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpoint.id}".`, void 0, `viewpoint.${viewpoint.id}.groups`));
|
|
3799
3994
|
}
|
|
3800
3995
|
}
|
|
3801
3996
|
}
|
|
3802
|
-
for (const eventId of
|
|
3997
|
+
for (const eventId of viewpoint.events ?? []) {
|
|
3803
3998
|
if (!eventIds.has(eventId)) {
|
|
3804
|
-
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${
|
|
3999
|
+
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${viewpoint.id}".`, void 0, `viewpoint.${viewpoint.id}.events`));
|
|
3805
4000
|
}
|
|
3806
4001
|
}
|
|
3807
4002
|
}
|
|
4003
|
+
validateProjection(viewpoint.projection, diagnostics, `viewpoint.${viewpoint.id}.projection`, viewpoint.id);
|
|
4004
|
+
validateCamera(viewpoint.camera, viewpoint.projection, viewpoint.rotationDeg, diagnostics, viewpoint.id, viewpoint.focusObjectId, viewpoint.selectedObjectId, filter, objectMap);
|
|
3808
4005
|
}
|
|
3809
4006
|
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
3810
4007
|
const placement = object.placement;
|
|
@@ -3817,6 +4014,12 @@
|
|
|
3817
4014
|
}
|
|
3818
4015
|
}
|
|
3819
4016
|
}
|
|
4017
|
+
if (typeof object.epoch === "string" && !object.epoch.trim()) {
|
|
4018
|
+
diagnostics.push(warn("validate.epoch.empty", `Object "${object.id}" defines an empty epoch string.`, object.id, "epoch"));
|
|
4019
|
+
}
|
|
4020
|
+
if (typeof object.referencePlane === "string" && !object.referencePlane.trim()) {
|
|
4021
|
+
diagnostics.push(warn("validate.referencePlane.empty", `Object "${object.id}" defines an empty reference plane string.`, object.id, "referencePlane"));
|
|
4022
|
+
}
|
|
3820
4023
|
if (orbitPlacement) {
|
|
3821
4024
|
if (!objectMap.has(orbitPlacement.target)) {
|
|
3822
4025
|
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
@@ -3888,12 +4091,18 @@
|
|
|
3888
4091
|
}
|
|
3889
4092
|
}
|
|
3890
4093
|
}
|
|
3891
|
-
function validateEvent(event, objectMap, diagnostics) {
|
|
4094
|
+
function validateEvent(event, system, objectMap, diagnostics) {
|
|
3892
4095
|
const fieldPrefix = `event.${event.id}`;
|
|
3893
4096
|
const referencedIds = /* @__PURE__ */ new Set();
|
|
3894
4097
|
if (!event.kind.trim()) {
|
|
3895
4098
|
diagnostics.push(error("validate.event.kind.required", `Event "${event.id}" is missing a "kind" value.`, void 0, `${fieldPrefix}.kind`));
|
|
3896
4099
|
}
|
|
4100
|
+
if (typeof event.epoch === "string" && !event.epoch.trim()) {
|
|
4101
|
+
diagnostics.push(warn("validate.event.epoch.empty", `Event "${event.id}" defines an empty epoch string.`, void 0, `${fieldPrefix}.epoch`));
|
|
4102
|
+
}
|
|
4103
|
+
if (typeof event.referencePlane === "string" && !event.referencePlane.trim()) {
|
|
4104
|
+
diagnostics.push(warn("validate.event.referencePlane.empty", `Event "${event.id}" defines an empty reference plane string.`, void 0, `${fieldPrefix}.referencePlane`));
|
|
4105
|
+
}
|
|
3897
4106
|
if (!event.targetObjectId && event.participantObjectIds.length === 0) {
|
|
3898
4107
|
diagnostics.push(error("validate.event.references.required", `Event "${event.id}" must define a "target" or at least one participant.`, void 0, `${fieldPrefix}.participants`));
|
|
3899
4108
|
}
|
|
@@ -3940,10 +4149,14 @@
|
|
|
3940
4149
|
if (!referencedIds.has(pose.objectId)) {
|
|
3941
4150
|
diagnostics.push(warn("validate.event.pose.unreferenced", `Event pose "${pose.objectId}" on "${event.id}" is not listed in target/participants.`, void 0, poseFieldPrefix));
|
|
3942
4151
|
}
|
|
3943
|
-
validateEventPose(pose, object, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
4152
|
+
validateEventPose(pose, object, event, system, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
4153
|
+
}
|
|
4154
|
+
const missingPoseIds = [...referencedIds].filter((objectId) => !poseIds.has(objectId));
|
|
4155
|
+
if (event.positions.length > 0 && missingPoseIds.length > 0) {
|
|
4156
|
+
diagnostics.push(warn("validate.event.positions.partial", `Event "${event.id}" leaves ${missingPoseIds.length} referenced object(s) on their base placement.`, void 0, `${fieldPrefix}.positions`));
|
|
3944
4157
|
}
|
|
3945
4158
|
}
|
|
3946
|
-
function validateEventPose(pose, object, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
4159
|
+
function validateEventPose(pose, object, event, system, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
3947
4160
|
const placement = pose.placement;
|
|
3948
4161
|
if (!placement) {
|
|
3949
4162
|
diagnostics.push(error("validate.event.pose.placement.required", `Event "${eventId}" pose "${pose.objectId}" is missing a placement mode.`, void 0, fieldPrefix));
|
|
@@ -3956,6 +4169,15 @@
|
|
|
3956
4169
|
if (placement.distance && placement.semiMajor) {
|
|
3957
4170
|
diagnostics.push(error("validate.event.pose.orbit.distanceConflict", `Event "${eventId}" pose "${pose.objectId}" cannot declare both "distance" and "semiMajor".`, void 0, `${fieldPrefix}.distance`));
|
|
3958
4171
|
}
|
|
4172
|
+
if (placement.phase && !resolveEffectiveEpoch(system, object, event, pose)) {
|
|
4173
|
+
diagnostics.push(warn("validate.event.pose.phase.epochMissing", `Event "${eventId}" pose "${pose.objectId}" sets "phase" without an effective epoch.`, void 0, `${fieldPrefix}.phase`));
|
|
4174
|
+
}
|
|
4175
|
+
if (placement.inclination && !resolveEffectiveReferencePlane(system, object, event, pose)) {
|
|
4176
|
+
diagnostics.push(warn("validate.event.pose.inclination.referencePlaneMissing", `Event "${eventId}" pose "${pose.objectId}" sets "inclination" without an effective reference plane.`, void 0, `${fieldPrefix}.inclination`));
|
|
4177
|
+
}
|
|
4178
|
+
if (placement.period && !massInSolar(objectMap.get(placement.target)?.properties.mass)) {
|
|
4179
|
+
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`));
|
|
4180
|
+
}
|
|
3959
4181
|
return;
|
|
3960
4182
|
}
|
|
3961
4183
|
if (placement.mode === "surface") {
|
|
@@ -4090,6 +4312,52 @@
|
|
|
4090
4312
|
return null;
|
|
4091
4313
|
}
|
|
4092
4314
|
}
|
|
4315
|
+
function validateProjection(projection, diagnostics, field, viewpointId) {
|
|
4316
|
+
if (projection !== "topdown" && projection !== "isometric" && projection !== "orthographic" && projection !== "perspective") {
|
|
4317
|
+
diagnostics.push(error("validate.viewpoint.projection.invalid", `Unknown projection "${String(projection)}" in viewpoint "${viewpointId}".`, void 0, field));
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
function validateCamera(camera, projection, rotationDeg, diagnostics, viewpointId, focusObjectId, selectedObjectId, filter, objectMap) {
|
|
4321
|
+
if (!camera) {
|
|
4322
|
+
return;
|
|
4323
|
+
}
|
|
4324
|
+
const prefix = `viewpoint.${viewpointId}.camera`;
|
|
4325
|
+
for (const [key, value] of [
|
|
4326
|
+
["azimuth", camera.azimuth],
|
|
4327
|
+
["elevation", camera.elevation],
|
|
4328
|
+
["roll", camera.roll],
|
|
4329
|
+
["distance", camera.distance]
|
|
4330
|
+
]) {
|
|
4331
|
+
if (value !== null && (!Number.isFinite(value) || key === "distance" && value <= 0)) {
|
|
4332
|
+
diagnostics.push(error("validate.viewpoint.camera.invalid", `Invalid camera ${key} "${String(value)}" in viewpoint "${viewpointId}".`, void 0, `${prefix}.${key}`));
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
if (camera.distance !== null && projection !== "perspective") {
|
|
4336
|
+
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`));
|
|
4337
|
+
}
|
|
4338
|
+
if (projection === "topdown" && (camera.elevation !== null || camera.roll !== null)) {
|
|
4339
|
+
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));
|
|
4340
|
+
}
|
|
4341
|
+
if (projection === "isometric" && camera.elevation !== null) {
|
|
4342
|
+
diagnostics.push(info("validate.viewpoint.camera.isometricStored", `Camera elevation on isometric viewpoint "${viewpointId}" is preserved semantically for future 3D rendering.`, void 0, `${prefix}.elevation`));
|
|
4343
|
+
}
|
|
4344
|
+
if (camera.azimuth !== null && camera.azimuth !== 0 && rotationDeg !== 0) {
|
|
4345
|
+
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`));
|
|
4346
|
+
}
|
|
4347
|
+
const hasAnchor = focusObjectId !== null && objectMap.has(focusObjectId) || selectedObjectId !== null && objectMap.has(selectedObjectId) || !!filter;
|
|
4348
|
+
if (!hasAnchor) {
|
|
4349
|
+
diagnostics.push(info("validate.viewpoint.camera.anchorMissing", `Viewpoint "${viewpointId}" stores camera settings without a focus object, selection, or filter anchor.`, void 0, prefix));
|
|
4350
|
+
}
|
|
4351
|
+
}
|
|
4352
|
+
function resolveEffectiveEpoch(system, object, event, pose) {
|
|
4353
|
+
return normalizeOptionalContextString(pose?.epoch) ?? normalizeOptionalContextString(event?.epoch) ?? normalizeOptionalContextString(object.epoch) ?? normalizeOptionalContextString(system?.epoch) ?? null;
|
|
4354
|
+
}
|
|
4355
|
+
function resolveEffectiveReferencePlane(system, object, event, pose) {
|
|
4356
|
+
return normalizeOptionalContextString(pose?.referencePlane) ?? normalizeOptionalContextString(event?.referencePlane) ?? normalizeOptionalContextString(object.referencePlane) ?? normalizeOptionalContextString(system?.referencePlane) ?? null;
|
|
4357
|
+
}
|
|
4358
|
+
function normalizeOptionalContextString(value) {
|
|
4359
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
4360
|
+
}
|
|
4093
4361
|
function toleranceForField(object, field) {
|
|
4094
4362
|
const tolerance = object.tolerances?.find((entry) => entry.field === field)?.value;
|
|
4095
4363
|
if (typeof tolerance === "number") {
|
|
@@ -4198,7 +4466,9 @@
|
|
|
4198
4466
|
"surface",
|
|
4199
4467
|
"free",
|
|
4200
4468
|
"inner",
|
|
4201
|
-
"outer"
|
|
4469
|
+
"outer",
|
|
4470
|
+
"epoch",
|
|
4471
|
+
"referencePlane"
|
|
4202
4472
|
]);
|
|
4203
4473
|
function parseWorldOrbitAtlas(source) {
|
|
4204
4474
|
return parseAtlasSource(source);
|
|
@@ -4243,7 +4513,7 @@
|
|
|
4243
4513
|
if (!sawSchemaHeader) {
|
|
4244
4514
|
sourceSchemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
4245
4515
|
sawSchemaHeader = true;
|
|
4246
|
-
if (prepared.comments.length > 0 && sourceSchemaVersion
|
|
4516
|
+
if (prepared.comments.length > 0 && isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
4247
4517
|
diagnostics.push({
|
|
4248
4518
|
code: "parse.schema21.commentCompatibility",
|
|
4249
4519
|
severity: "warning",
|
|
@@ -4313,11 +4583,11 @@
|
|
|
4313
4583
|
return document;
|
|
4314
4584
|
}
|
|
4315
4585
|
function assertDraftSchemaHeader(tokens, line) {
|
|
4316
|
-
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1"].includes(tokens[1].value.toLowerCase())) {
|
|
4317
|
-
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
4586
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1", "2.5"].includes(tokens[1].value.toLowerCase())) {
|
|
4587
|
+
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);
|
|
4318
4588
|
}
|
|
4319
4589
|
const version = tokens[1].value.toLowerCase();
|
|
4320
|
-
return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
4590
|
+
return version === "2.5" ? "2.5" : version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
4321
4591
|
}
|
|
4322
4592
|
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, flags) {
|
|
4323
4593
|
const keyword = tokens[0]?.value.toLowerCase();
|
|
@@ -4337,6 +4607,8 @@
|
|
|
4337
4607
|
return {
|
|
4338
4608
|
kind: "defaults",
|
|
4339
4609
|
system,
|
|
4610
|
+
sourceSchemaVersion,
|
|
4611
|
+
diagnostics,
|
|
4340
4612
|
seenFields: /* @__PURE__ */ new Set()
|
|
4341
4613
|
};
|
|
4342
4614
|
case "atlas":
|
|
@@ -4429,6 +4701,7 @@
|
|
|
4429
4701
|
preset: system.defaults.preset,
|
|
4430
4702
|
zoom: null,
|
|
4431
4703
|
rotationDeg: 0,
|
|
4704
|
+
camera: null,
|
|
4432
4705
|
layers: {},
|
|
4433
4706
|
filter: null
|
|
4434
4707
|
};
|
|
@@ -4442,7 +4715,10 @@
|
|
|
4442
4715
|
seenFields: /* @__PURE__ */ new Set(),
|
|
4443
4716
|
inFilter: false,
|
|
4444
4717
|
filterIndent: null,
|
|
4445
|
-
seenFilterFields: /* @__PURE__ */ new Set()
|
|
4718
|
+
seenFilterFields: /* @__PURE__ */ new Set(),
|
|
4719
|
+
inCamera: false,
|
|
4720
|
+
cameraIndent: null,
|
|
4721
|
+
seenCameraFields: /* @__PURE__ */ new Set()
|
|
4446
4722
|
};
|
|
4447
4723
|
}
|
|
4448
4724
|
function startAnnotationSection(tokens, line, system, annotationIds) {
|
|
@@ -4549,6 +4825,8 @@
|
|
|
4549
4825
|
participantObjectIds: [],
|
|
4550
4826
|
timing: null,
|
|
4551
4827
|
visibility: null,
|
|
4828
|
+
epoch: null,
|
|
4829
|
+
referencePlane: null,
|
|
4552
4830
|
tags: [],
|
|
4553
4831
|
color: null,
|
|
4554
4832
|
hidden: false,
|
|
@@ -4673,6 +4951,12 @@
|
|
|
4673
4951
|
const value = joinFieldValue(tokens, line);
|
|
4674
4952
|
switch (key) {
|
|
4675
4953
|
case "view":
|
|
4954
|
+
if (isSchema25Projection(value)) {
|
|
4955
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "defaults.view", {
|
|
4956
|
+
line,
|
|
4957
|
+
column: tokens[0].column
|
|
4958
|
+
});
|
|
4959
|
+
}
|
|
4676
4960
|
section.system.defaults.view = parseProjectionValue(value, line, tokens[0].column);
|
|
4677
4961
|
return;
|
|
4678
4962
|
case "scale":
|
|
@@ -4712,14 +4996,36 @@
|
|
|
4712
4996
|
throw new WorldOrbitError(`Unknown atlas field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4713
4997
|
}
|
|
4714
4998
|
function applyViewpointField2(section, indent, tokens, line) {
|
|
4999
|
+
if (section.inCamera && indent <= (section.cameraIndent ?? 0)) {
|
|
5000
|
+
section.inCamera = false;
|
|
5001
|
+
section.cameraIndent = null;
|
|
5002
|
+
}
|
|
4715
5003
|
if (section.inFilter && indent <= (section.filterIndent ?? 0)) {
|
|
4716
5004
|
section.inFilter = false;
|
|
4717
5005
|
section.filterIndent = null;
|
|
4718
5006
|
}
|
|
5007
|
+
if (section.inCamera) {
|
|
5008
|
+
applyViewpointCameraField(section, tokens, line);
|
|
5009
|
+
return;
|
|
5010
|
+
}
|
|
4719
5011
|
if (section.inFilter) {
|
|
4720
5012
|
applyViewpointFilterField(section, tokens, line);
|
|
4721
5013
|
return;
|
|
4722
5014
|
}
|
|
5015
|
+
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "camera") {
|
|
5016
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.camera", {
|
|
5017
|
+
line,
|
|
5018
|
+
column: tokens[0].column
|
|
5019
|
+
});
|
|
5020
|
+
if (section.seenFields.has("camera")) {
|
|
5021
|
+
throw new WorldOrbitError('Duplicate viewpoint field "camera"', line, tokens[0].column);
|
|
5022
|
+
}
|
|
5023
|
+
section.seenFields.add("camera");
|
|
5024
|
+
section.inCamera = true;
|
|
5025
|
+
section.cameraIndent = indent;
|
|
5026
|
+
section.viewpoint.camera = section.viewpoint.camera ?? createEmptyViewCamera2();
|
|
5027
|
+
return;
|
|
5028
|
+
}
|
|
4723
5029
|
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "filter") {
|
|
4724
5030
|
if (section.seenFields.has("filter")) {
|
|
4725
5031
|
throw new WorldOrbitError('Duplicate viewpoint field "filter"', line, tokens[0].column);
|
|
@@ -4745,6 +5051,12 @@
|
|
|
4745
5051
|
section.viewpoint.selectedObjectId = value;
|
|
4746
5052
|
return;
|
|
4747
5053
|
case "projection":
|
|
5054
|
+
if (isSchema25Projection(value)) {
|
|
5055
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "projection", {
|
|
5056
|
+
line,
|
|
5057
|
+
column: tokens[0].column
|
|
5058
|
+
});
|
|
5059
|
+
}
|
|
4748
5060
|
section.viewpoint.projection = parseProjectionValue(value, line, tokens[0].column);
|
|
4749
5061
|
return;
|
|
4750
5062
|
case "preset":
|
|
@@ -4756,6 +5068,13 @@
|
|
|
4756
5068
|
case "rotation":
|
|
4757
5069
|
section.viewpoint.rotationDeg = parseFiniteNumber2(value, line, tokens[0].column, "rotation");
|
|
4758
5070
|
return;
|
|
5071
|
+
case "camera":
|
|
5072
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.camera", {
|
|
5073
|
+
line,
|
|
5074
|
+
column: tokens[0].column
|
|
5075
|
+
});
|
|
5076
|
+
section.viewpoint.camera = parseInlineViewCamera(tokens.slice(1), line, section.viewpoint.camera);
|
|
5077
|
+
return;
|
|
4759
5078
|
case "layers":
|
|
4760
5079
|
section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line, section.sourceSchemaVersion, section.diagnostics);
|
|
4761
5080
|
return;
|
|
@@ -4770,6 +5089,28 @@
|
|
|
4770
5089
|
throw new WorldOrbitError(`Unknown viewpoint field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4771
5090
|
}
|
|
4772
5091
|
}
|
|
5092
|
+
function applyViewpointCameraField(section, tokens, line) {
|
|
5093
|
+
const key = requireUniqueField(tokens, section.seenCameraFields, line);
|
|
5094
|
+
const value = joinFieldValue(tokens, line);
|
|
5095
|
+
const camera = section.viewpoint.camera ?? createEmptyViewCamera2();
|
|
5096
|
+
switch (key) {
|
|
5097
|
+
case "azimuth":
|
|
5098
|
+
camera.azimuth = parseFiniteNumber2(value, line, tokens[0].column, "camera.azimuth");
|
|
5099
|
+
break;
|
|
5100
|
+
case "elevation":
|
|
5101
|
+
camera.elevation = parseFiniteNumber2(value, line, tokens[0].column, "camera.elevation");
|
|
5102
|
+
break;
|
|
5103
|
+
case "roll":
|
|
5104
|
+
camera.roll = parseFiniteNumber2(value, line, tokens[0].column, "camera.roll");
|
|
5105
|
+
break;
|
|
5106
|
+
case "distance":
|
|
5107
|
+
camera.distance = parsePositiveNumber2(value, line, tokens[0].column, "camera.distance");
|
|
5108
|
+
break;
|
|
5109
|
+
default:
|
|
5110
|
+
throw new WorldOrbitError(`Unknown viewpoint camera field "${tokens[0].value}"`, line, tokens[0].column);
|
|
5111
|
+
}
|
|
5112
|
+
section.viewpoint.camera = camera;
|
|
5113
|
+
}
|
|
4773
5114
|
function applyViewpointFilterField(section, tokens, line) {
|
|
4774
5115
|
const key = requireUniqueField(tokens, section.seenFilterFields, line);
|
|
4775
5116
|
const filter = section.viewpoint.filter ?? createEmptyViewpointFilter2();
|
|
@@ -4880,6 +5221,12 @@
|
|
|
4880
5221
|
section.positionsIndent = null;
|
|
4881
5222
|
}
|
|
4882
5223
|
if (section.activePose) {
|
|
5224
|
+
if (tokens[0]?.value === "epoch" || tokens[0]?.value === "referencePlane") {
|
|
5225
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, `pose.${tokens[0].value}`, {
|
|
5226
|
+
line,
|
|
5227
|
+
column: tokens[0]?.column ?? 1
|
|
5228
|
+
});
|
|
5229
|
+
}
|
|
4883
5230
|
section.activePose.fields.push(parseEventPoseField(tokens, line, section.activePoseSeenFields));
|
|
4884
5231
|
return;
|
|
4885
5232
|
}
|
|
@@ -4934,6 +5281,20 @@
|
|
|
4934
5281
|
case "visibility":
|
|
4935
5282
|
section.event.visibility = joinFieldValue(tokens, line);
|
|
4936
5283
|
return;
|
|
5284
|
+
case "epoch":
|
|
5285
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "event.epoch", {
|
|
5286
|
+
line,
|
|
5287
|
+
column: tokens[0].column
|
|
5288
|
+
});
|
|
5289
|
+
section.event.epoch = joinFieldValue(tokens, line);
|
|
5290
|
+
return;
|
|
5291
|
+
case "referenceplane":
|
|
5292
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "event.referencePlane", {
|
|
5293
|
+
line,
|
|
5294
|
+
column: tokens[0].column
|
|
5295
|
+
});
|
|
5296
|
+
section.event.referencePlane = joinFieldValue(tokens, line);
|
|
5297
|
+
return;
|
|
4937
5298
|
case "tags":
|
|
4938
5299
|
section.event.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4939
5300
|
return;
|
|
@@ -5061,11 +5422,15 @@
|
|
|
5061
5422
|
}
|
|
5062
5423
|
function parseProjectionValue(value, line, column) {
|
|
5063
5424
|
const normalized = value.toLowerCase();
|
|
5064
|
-
if (normalized !== "topdown" && normalized !== "isometric") {
|
|
5425
|
+
if (normalized !== "topdown" && normalized !== "isometric" && normalized !== "orthographic" && normalized !== "perspective") {
|
|
5065
5426
|
throw new WorldOrbitError(`Unknown projection "${value}"`, line, column);
|
|
5066
5427
|
}
|
|
5067
5428
|
return normalized;
|
|
5068
5429
|
}
|
|
5430
|
+
function isSchema25Projection(value) {
|
|
5431
|
+
const normalized = value.toLowerCase();
|
|
5432
|
+
return normalized === "orthographic" || normalized === "perspective";
|
|
5433
|
+
}
|
|
5069
5434
|
function parsePresetValue(value, line, column) {
|
|
5070
5435
|
const normalized = value.toLowerCase();
|
|
5071
5436
|
if (normalized === "diagram" || normalized === "presentation" || normalized === "atlas-card" || normalized === "markdown") {
|
|
@@ -5095,6 +5460,48 @@
|
|
|
5095
5460
|
groupIds: []
|
|
5096
5461
|
};
|
|
5097
5462
|
}
|
|
5463
|
+
function createEmptyViewCamera2() {
|
|
5464
|
+
return {
|
|
5465
|
+
azimuth: null,
|
|
5466
|
+
elevation: null,
|
|
5467
|
+
roll: null,
|
|
5468
|
+
distance: null
|
|
5469
|
+
};
|
|
5470
|
+
}
|
|
5471
|
+
function parseInlineViewCamera(tokens, line, current) {
|
|
5472
|
+
if (tokens.length === 0 || tokens.length % 2 !== 0) {
|
|
5473
|
+
throw new WorldOrbitError('Field "camera" expects "<field> <value>" pairs', line, tokens[0]?.column ?? 1);
|
|
5474
|
+
}
|
|
5475
|
+
const camera = current ? { ...current } : createEmptyViewCamera2();
|
|
5476
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5477
|
+
for (let index = 0; index < tokens.length; index += 2) {
|
|
5478
|
+
const fieldToken = tokens[index];
|
|
5479
|
+
const valueToken = tokens[index + 1];
|
|
5480
|
+
const key = fieldToken.value.toLowerCase();
|
|
5481
|
+
if (seen.has(key)) {
|
|
5482
|
+
throw new WorldOrbitError(`Duplicate viewpoint camera field "${fieldToken.value}"`, line, fieldToken.column);
|
|
5483
|
+
}
|
|
5484
|
+
seen.add(key);
|
|
5485
|
+
const value = valueToken.value;
|
|
5486
|
+
switch (key) {
|
|
5487
|
+
case "azimuth":
|
|
5488
|
+
camera.azimuth = parseFiniteNumber2(value, line, fieldToken.column, "camera.azimuth");
|
|
5489
|
+
break;
|
|
5490
|
+
case "elevation":
|
|
5491
|
+
camera.elevation = parseFiniteNumber2(value, line, fieldToken.column, "camera.elevation");
|
|
5492
|
+
break;
|
|
5493
|
+
case "roll":
|
|
5494
|
+
camera.roll = parseFiniteNumber2(value, line, fieldToken.column, "camera.roll");
|
|
5495
|
+
break;
|
|
5496
|
+
case "distance":
|
|
5497
|
+
camera.distance = parsePositiveNumber2(value, line, fieldToken.column, "camera.distance");
|
|
5498
|
+
break;
|
|
5499
|
+
default:
|
|
5500
|
+
throw new WorldOrbitError(`Unknown viewpoint camera field "${fieldToken.value}"`, line, fieldToken.column);
|
|
5501
|
+
}
|
|
5502
|
+
}
|
|
5503
|
+
return camera;
|
|
5504
|
+
}
|
|
5098
5505
|
function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
5099
5506
|
const fields = [];
|
|
5100
5507
|
let index = 0;
|
|
@@ -5227,7 +5634,7 @@
|
|
|
5227
5634
|
object.tolerances = tolerances;
|
|
5228
5635
|
if (typedBlocks && Object.keys(typedBlocks).length > 0)
|
|
5229
5636
|
object.typedBlocks = typedBlocks;
|
|
5230
|
-
if (sourceSchemaVersion
|
|
5637
|
+
if (isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
5231
5638
|
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) {
|
|
5232
5639
|
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, node.id, node.location);
|
|
5233
5640
|
}
|
|
@@ -5243,23 +5650,25 @@
|
|
|
5243
5650
|
};
|
|
5244
5651
|
}
|
|
5245
5652
|
function normalizeDraftEventPose(rawPose) {
|
|
5246
|
-
const fieldMap = collectDraftFields(rawPose.fields);
|
|
5653
|
+
const fieldMap = collectDraftFields(rawPose.fields, "event-pose");
|
|
5247
5654
|
const placement = extractPlacementFromFieldMap(fieldMap);
|
|
5248
5655
|
return {
|
|
5249
5656
|
objectId: rawPose.objectId,
|
|
5250
5657
|
placement,
|
|
5251
5658
|
inner: parseOptionalUnitField(fieldMap.get("inner")?.[0], "inner"),
|
|
5252
|
-
outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer")
|
|
5659
|
+
outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer"),
|
|
5660
|
+
epoch: parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]),
|
|
5661
|
+
referencePlane: parseOptionalJoinedValue(fieldMap.get("referencePlane")?.[0])
|
|
5253
5662
|
};
|
|
5254
5663
|
}
|
|
5255
|
-
function collectDraftFields(fields) {
|
|
5664
|
+
function collectDraftFields(fields, _mode = "object") {
|
|
5256
5665
|
const grouped = /* @__PURE__ */ new Map();
|
|
5257
5666
|
for (const field of fields) {
|
|
5258
5667
|
const spec = getDraftObjectFieldSpec(field.key);
|
|
5259
|
-
if (!spec) {
|
|
5668
|
+
if (!spec && !EVENT_POSE_FIELD_KEYS.has(field.key)) {
|
|
5260
5669
|
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
5261
5670
|
}
|
|
5262
|
-
if (!spec
|
|
5671
|
+
if (!spec?.allowRepeat && grouped.has(field.key)) {
|
|
5263
5672
|
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
5264
5673
|
}
|
|
5265
5674
|
const existing = grouped.get(field.key) ?? [];
|
|
@@ -5436,7 +5845,7 @@
|
|
|
5436
5845
|
}
|
|
5437
5846
|
}
|
|
5438
5847
|
function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
5439
|
-
if (sourceSchemaVersion
|
|
5848
|
+
if (!isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
5440
5849
|
return;
|
|
5441
5850
|
}
|
|
5442
5851
|
diagnostics.push({
|
|
@@ -5448,6 +5857,34 @@
|
|
|
5448
5857
|
column: location.column
|
|
5449
5858
|
});
|
|
5450
5859
|
}
|
|
5860
|
+
function warnIfSchema25Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
5861
|
+
if (!isSchemaOlderThan(sourceSchemaVersion, "2.5")) {
|
|
5862
|
+
return;
|
|
5863
|
+
}
|
|
5864
|
+
diagnostics.push({
|
|
5865
|
+
code: "parse.schema25.featureCompatibility",
|
|
5866
|
+
severity: "warning",
|
|
5867
|
+
source: "parse",
|
|
5868
|
+
message: `Feature "${featureName}" requires schema 2.5; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
5869
|
+
line: location.line,
|
|
5870
|
+
column: location.column
|
|
5871
|
+
});
|
|
5872
|
+
}
|
|
5873
|
+
function isSchemaOlderThan(sourceSchemaVersion, requiredVersion) {
|
|
5874
|
+
return schemaVersionRank(sourceSchemaVersion) < schemaVersionRank(requiredVersion);
|
|
5875
|
+
}
|
|
5876
|
+
function schemaVersionRank(version) {
|
|
5877
|
+
switch (version) {
|
|
5878
|
+
case "2.0-draft":
|
|
5879
|
+
return 0;
|
|
5880
|
+
case "2.0":
|
|
5881
|
+
return 1;
|
|
5882
|
+
case "2.1":
|
|
5883
|
+
return 2;
|
|
5884
|
+
case "2.5":
|
|
5885
|
+
return 3;
|
|
5886
|
+
}
|
|
5887
|
+
}
|
|
5451
5888
|
function preprocessAtlasSource(source) {
|
|
5452
5889
|
const chars = [...source];
|
|
5453
5890
|
const comments = [];
|
|
@@ -5535,7 +5972,7 @@
|
|
|
5535
5972
|
}
|
|
5536
5973
|
|
|
5537
5974
|
// packages/core/dist/atlas-edit.js
|
|
5538
|
-
function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "2.
|
|
5975
|
+
function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "2.5") {
|
|
5539
5976
|
return {
|
|
5540
5977
|
format: "worldorbit",
|
|
5541
5978
|
version,
|
|
@@ -5882,8 +6319,9 @@
|
|
|
5882
6319
|
}
|
|
5883
6320
|
|
|
5884
6321
|
// packages/core/dist/load.js
|
|
5885
|
-
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;
|
|
6322
|
+
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1|\.5)?$/i;
|
|
5886
6323
|
var ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
|
|
6324
|
+
var ATLAS_SCHEMA_25_PATTERN = /^schema\s+2\.5$/i;
|
|
5887
6325
|
var LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
|
|
5888
6326
|
function detectWorldOrbitSchemaVersion(source) {
|
|
5889
6327
|
for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
|
|
@@ -5897,6 +6335,9 @@
|
|
|
5897
6335
|
if (ATLAS_SCHEMA_21_PATTERN.test(trimmed)) {
|
|
5898
6336
|
return "2.1";
|
|
5899
6337
|
}
|
|
6338
|
+
if (ATLAS_SCHEMA_25_PATTERN.test(trimmed)) {
|
|
6339
|
+
return "2.5";
|
|
6340
|
+
}
|
|
5900
6341
|
if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
|
|
5901
6342
|
return "2.0";
|
|
5902
6343
|
}
|
|
@@ -5957,7 +6398,7 @@
|
|
|
5957
6398
|
}
|
|
5958
6399
|
function loadWorldOrbitSourceWithDiagnostics(source) {
|
|
5959
6400
|
const schemaVersion = detectWorldOrbitSchemaVersion(source);
|
|
5960
|
-
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1") {
|
|
6401
|
+
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1" || schemaVersion === "2.5") {
|
|
5961
6402
|
return loadAtlasSourceWithDiagnostics(source, schemaVersion);
|
|
5962
6403
|
}
|
|
5963
6404
|
let ast;
|