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
|
@@ -921,7 +921,9 @@
|
|
|
921
921
|
const height = frame.height;
|
|
922
922
|
const padding = frame.padding;
|
|
923
923
|
const layoutPreset = resolveLayoutPreset(document2);
|
|
924
|
-
const
|
|
924
|
+
const schemaProjection = resolveProjection(document2, options.projection);
|
|
925
|
+
const camera = normalizeViewCamera(options.camera ?? null);
|
|
926
|
+
const renderProjection = resolveRenderProjection(schemaProjection, camera);
|
|
925
927
|
const scaleModel = resolveScaleModel(layoutPreset, options.scaleModel);
|
|
926
928
|
const spacingFactor = layoutPresetSpacing(layoutPreset);
|
|
927
929
|
const systemId = document2.system?.id ?? null;
|
|
@@ -964,7 +966,7 @@
|
|
|
964
966
|
surfaceChildren,
|
|
965
967
|
objectMap,
|
|
966
968
|
spacingFactor,
|
|
967
|
-
projection,
|
|
969
|
+
projection: renderProjection,
|
|
968
970
|
scaleModel
|
|
969
971
|
};
|
|
970
972
|
const primaryRoot = rootObjects.find((object) => object.type === "star") ?? rootObjects[0] ?? null;
|
|
@@ -976,7 +978,7 @@
|
|
|
976
978
|
const rootRingRadius = Math.min(width, height) * 0.28 * spacingFactor * scaleModel.orbitDistanceMultiplier;
|
|
977
979
|
secondaryRoots.forEach((object, index) => {
|
|
978
980
|
const angle = angleForIndex(index, secondaryRoots.length, -Math.PI / 2);
|
|
979
|
-
const offset = projectPolarOffset(angle, rootRingRadius,
|
|
981
|
+
const offset = projectPolarOffset(angle, rootRingRadius, renderProjection, 1);
|
|
980
982
|
placeObject(object, centerX + offset.x, centerY + offset.y, 0, positions, orbitDrafts, leaderDrafts, context);
|
|
981
983
|
});
|
|
982
984
|
}
|
|
@@ -1038,27 +1040,34 @@
|
|
|
1038
1040
|
const layers = createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels);
|
|
1039
1041
|
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, scaleModel.labelMultiplier);
|
|
1040
1042
|
const semanticGroups = createSceneSemanticGroups(document2, objects);
|
|
1041
|
-
const viewpoints = createSceneViewpoints(document2,
|
|
1043
|
+
const viewpoints = createSceneViewpoints(document2, schemaProjection, frame.preset, relationships, objectMap);
|
|
1042
1044
|
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, scaleModel.labelMultiplier);
|
|
1043
1045
|
return {
|
|
1044
1046
|
width,
|
|
1045
1047
|
height,
|
|
1046
1048
|
padding,
|
|
1047
1049
|
renderPreset: frame.preset,
|
|
1048
|
-
projection,
|
|
1050
|
+
projection: schemaProjection,
|
|
1051
|
+
renderProjection,
|
|
1052
|
+
camera,
|
|
1049
1053
|
scaleModel,
|
|
1050
1054
|
title: String(document2.system?.title ?? document2.system?.properties.title ?? document2.system?.id ?? "WorldOrbit") || "WorldOrbit",
|
|
1051
|
-
subtitle:
|
|
1055
|
+
subtitle: buildSceneSubtitle(schemaProjection, renderProjection, layoutPreset, camera),
|
|
1052
1056
|
systemId,
|
|
1053
|
-
viewMode:
|
|
1057
|
+
viewMode: schemaProjection,
|
|
1054
1058
|
layoutPreset,
|
|
1055
1059
|
metadata: {
|
|
1056
1060
|
format: document2.format,
|
|
1057
1061
|
version: document2.version,
|
|
1058
|
-
view:
|
|
1062
|
+
view: schemaProjection,
|
|
1063
|
+
renderProjection,
|
|
1059
1064
|
scale: String(document2.system?.properties.scale ?? layoutPreset),
|
|
1060
1065
|
units: String(document2.system?.properties.units ?? "mixed"),
|
|
1061
|
-
preset: frame.preset ?? "custom"
|
|
1066
|
+
preset: frame.preset ?? "custom",
|
|
1067
|
+
...camera?.azimuth !== null ? { "camera.azimuth": String(camera?.azimuth) } : {},
|
|
1068
|
+
...camera?.elevation !== null ? { "camera.elevation": String(camera?.elevation) } : {},
|
|
1069
|
+
...camera?.roll !== null ? { "camera.roll": String(camera?.roll) } : {},
|
|
1070
|
+
...camera?.distance !== null ? { "camera.distance": String(camera?.distance) } : {}
|
|
1062
1071
|
},
|
|
1063
1072
|
contentBounds,
|
|
1064
1073
|
layers,
|
|
@@ -1084,21 +1093,42 @@
|
|
|
1084
1093
|
return cloned;
|
|
1085
1094
|
}
|
|
1086
1095
|
const objectMap = new Map(cloned.map((object) => [object.id, object]));
|
|
1096
|
+
const referencedIds = /* @__PURE__ */ new Set([
|
|
1097
|
+
...activeEvent.targetObjectId ? [activeEvent.targetObjectId] : [],
|
|
1098
|
+
...activeEvent.participantObjectIds,
|
|
1099
|
+
...activeEvent.positions.map((pose) => pose.objectId)
|
|
1100
|
+
]);
|
|
1101
|
+
for (const objectId of referencedIds) {
|
|
1102
|
+
const object = objectMap.get(objectId);
|
|
1103
|
+
if (!object) {
|
|
1104
|
+
continue;
|
|
1105
|
+
}
|
|
1106
|
+
if (activeEvent.epoch) {
|
|
1107
|
+
object.epoch = activeEvent.epoch;
|
|
1108
|
+
}
|
|
1109
|
+
if (activeEvent.referencePlane) {
|
|
1110
|
+
object.referencePlane = activeEvent.referencePlane;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1087
1113
|
for (const pose of activeEvent.positions) {
|
|
1088
1114
|
const object = objectMap.get(pose.objectId);
|
|
1089
1115
|
if (!object) {
|
|
1090
1116
|
continue;
|
|
1091
1117
|
}
|
|
1092
|
-
|
|
1118
|
+
if (pose.placement) {
|
|
1119
|
+
object.placement = structuredClone(pose.placement);
|
|
1120
|
+
}
|
|
1093
1121
|
if (pose.inner) {
|
|
1094
1122
|
object.properties.inner = { ...pose.inner };
|
|
1095
|
-
} else {
|
|
1096
|
-
delete object.properties.inner;
|
|
1097
1123
|
}
|
|
1098
1124
|
if (pose.outer) {
|
|
1099
1125
|
object.properties.outer = { ...pose.outer };
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1126
|
+
}
|
|
1127
|
+
if (pose.epoch) {
|
|
1128
|
+
object.epoch = pose.epoch;
|
|
1129
|
+
}
|
|
1130
|
+
if (pose.referencePlane) {
|
|
1131
|
+
object.referencePlane = pose.referencePlane;
|
|
1102
1132
|
}
|
|
1103
1133
|
}
|
|
1104
1134
|
return cloned;
|
|
@@ -1139,10 +1169,59 @@
|
|
|
1139
1169
|
}
|
|
1140
1170
|
}
|
|
1141
1171
|
function resolveProjection(document2, projection) {
|
|
1142
|
-
if (projection === "topdown" || projection === "isometric") {
|
|
1172
|
+
if (projection === "topdown" || projection === "isometric" || projection === "orthographic" || projection === "perspective") {
|
|
1143
1173
|
return projection;
|
|
1144
1174
|
}
|
|
1145
|
-
|
|
1175
|
+
const documentView = String(document2.system?.properties.view ?? "topdown").toLowerCase();
|
|
1176
|
+
return parseViewProjection(documentView) ?? "topdown";
|
|
1177
|
+
}
|
|
1178
|
+
function resolveRenderProjection(projection, camera) {
|
|
1179
|
+
switch (projection) {
|
|
1180
|
+
case "topdown":
|
|
1181
|
+
return "topdown";
|
|
1182
|
+
case "isometric":
|
|
1183
|
+
return "isometric";
|
|
1184
|
+
case "orthographic":
|
|
1185
|
+
return camera && (camera.azimuth !== null || camera.elevation !== null || camera.roll !== null) ? "isometric" : "topdown";
|
|
1186
|
+
case "perspective":
|
|
1187
|
+
return "isometric";
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
function normalizeViewCamera(camera) {
|
|
1191
|
+
if (!camera) {
|
|
1192
|
+
return null;
|
|
1193
|
+
}
|
|
1194
|
+
const normalized = {
|
|
1195
|
+
azimuth: normalizeFiniteCameraValue(camera.azimuth),
|
|
1196
|
+
elevation: normalizeFiniteCameraValue(camera.elevation),
|
|
1197
|
+
roll: normalizeFiniteCameraValue(camera.roll),
|
|
1198
|
+
distance: normalizePositiveCameraDistance(camera.distance)
|
|
1199
|
+
};
|
|
1200
|
+
return normalized.azimuth !== null || normalized.elevation !== null || normalized.roll !== null || normalized.distance !== null ? normalized : null;
|
|
1201
|
+
}
|
|
1202
|
+
function normalizeFiniteCameraValue(value) {
|
|
1203
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1204
|
+
}
|
|
1205
|
+
function normalizePositiveCameraDistance(value) {
|
|
1206
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
1207
|
+
}
|
|
1208
|
+
function buildSceneSubtitle(projection, renderProjection, layoutPreset, camera) {
|
|
1209
|
+
const parts = [`${capitalizeLabel(projection)} view`, `${capitalizeLabel(layoutPreset)} layout`];
|
|
1210
|
+
if (projection !== renderProjection) {
|
|
1211
|
+
parts.push(`2D ${renderProjection} fallback`);
|
|
1212
|
+
}
|
|
1213
|
+
if (camera) {
|
|
1214
|
+
const cameraParts = [
|
|
1215
|
+
camera.azimuth !== null ? `az ${camera.azimuth}` : null,
|
|
1216
|
+
camera.elevation !== null ? `el ${camera.elevation}` : null,
|
|
1217
|
+
camera.roll !== null ? `roll ${camera.roll}` : null,
|
|
1218
|
+
camera.distance !== null ? `dist ${camera.distance}` : null
|
|
1219
|
+
].filter(Boolean);
|
|
1220
|
+
if (cameraParts.length > 0) {
|
|
1221
|
+
parts.push(`camera ${cameraParts.join(" / ")}`);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
return parts.join(" - ");
|
|
1146
1225
|
}
|
|
1147
1226
|
function resolveScaleModel(layoutPreset, overrides) {
|
|
1148
1227
|
const defaults = defaultScaleModel(layoutPreset);
|
|
@@ -1578,6 +1657,8 @@
|
|
|
1578
1657
|
function createGeneratedOverviewViewpoint(document2, projection, preset) {
|
|
1579
1658
|
const title = document2.system?.title ?? document2.system?.properties.title;
|
|
1580
1659
|
const label = title ? `${String(title)} Overview` : "Overview";
|
|
1660
|
+
const camera = normalizeViewCamera(null);
|
|
1661
|
+
const renderProjection = resolveRenderProjection(projection, camera);
|
|
1581
1662
|
return {
|
|
1582
1663
|
id: "overview",
|
|
1583
1664
|
label,
|
|
@@ -1586,6 +1667,8 @@
|
|
|
1586
1667
|
selectedObjectId: null,
|
|
1587
1668
|
eventIds: [],
|
|
1588
1669
|
projection,
|
|
1670
|
+
renderProjection,
|
|
1671
|
+
camera,
|
|
1589
1672
|
preset,
|
|
1590
1673
|
rotationDeg: 0,
|
|
1591
1674
|
scale: null,
|
|
@@ -1635,6 +1718,30 @@
|
|
|
1635
1718
|
case "angle":
|
|
1636
1719
|
draft.rotationDeg = parseFiniteNumber(normalizedValue) ?? draft.rotationDeg ?? 0;
|
|
1637
1720
|
return;
|
|
1721
|
+
case "camera.azimuth":
|
|
1722
|
+
draft.camera = {
|
|
1723
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1724
|
+
azimuth: parseFiniteNumber(normalizedValue)
|
|
1725
|
+
};
|
|
1726
|
+
return;
|
|
1727
|
+
case "camera.elevation":
|
|
1728
|
+
draft.camera = {
|
|
1729
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1730
|
+
elevation: parseFiniteNumber(normalizedValue)
|
|
1731
|
+
};
|
|
1732
|
+
return;
|
|
1733
|
+
case "camera.roll":
|
|
1734
|
+
draft.camera = {
|
|
1735
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1736
|
+
roll: parseFiniteNumber(normalizedValue)
|
|
1737
|
+
};
|
|
1738
|
+
return;
|
|
1739
|
+
case "camera.distance":
|
|
1740
|
+
draft.camera = {
|
|
1741
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1742
|
+
distance: parsePositiveNumber(normalizedValue)
|
|
1743
|
+
};
|
|
1744
|
+
return;
|
|
1638
1745
|
case "zoom":
|
|
1639
1746
|
case "scale":
|
|
1640
1747
|
draft.scale = parsePositiveNumber(normalizedValue);
|
|
@@ -1674,6 +1781,9 @@
|
|
|
1674
1781
|
const selectedObjectId = draft.select && objectMap.has(draft.select) ? draft.select : objectId;
|
|
1675
1782
|
const filter = normalizeViewpointFilter(draft.filter);
|
|
1676
1783
|
const label = draft.label?.trim() || humanizeIdentifier(draft.id);
|
|
1784
|
+
const resolvedProjection = draft.projection ?? projection;
|
|
1785
|
+
const camera = normalizeViewCamera(draft.camera ?? null);
|
|
1786
|
+
const renderProjection = resolveRenderProjection(resolvedProjection, camera);
|
|
1677
1787
|
return {
|
|
1678
1788
|
id: draft.id,
|
|
1679
1789
|
label,
|
|
@@ -1681,7 +1791,9 @@
|
|
|
1681
1791
|
objectId,
|
|
1682
1792
|
selectedObjectId,
|
|
1683
1793
|
eventIds: [...new Set(draft.eventIds ?? [])],
|
|
1684
|
-
projection:
|
|
1794
|
+
projection: resolvedProjection,
|
|
1795
|
+
renderProjection,
|
|
1796
|
+
camera,
|
|
1685
1797
|
preset: draft.preset ?? preset,
|
|
1686
1798
|
rotationDeg: draft.rotationDeg ?? 0,
|
|
1687
1799
|
scale: draft.scale ?? null,
|
|
@@ -1698,6 +1810,14 @@
|
|
|
1698
1810
|
groupIds: []
|
|
1699
1811
|
};
|
|
1700
1812
|
}
|
|
1813
|
+
function createEmptyViewCamera() {
|
|
1814
|
+
return {
|
|
1815
|
+
azimuth: null,
|
|
1816
|
+
elevation: null,
|
|
1817
|
+
roll: null,
|
|
1818
|
+
distance: null
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1701
1821
|
function normalizeViewpointFilter(filter) {
|
|
1702
1822
|
if (!filter) {
|
|
1703
1823
|
return null;
|
|
@@ -1711,7 +1831,18 @@
|
|
|
1711
1831
|
return normalized.query || normalized.objectTypes.length > 0 || normalized.tags.length > 0 || normalized.groupIds.length > 0 ? normalized : null;
|
|
1712
1832
|
}
|
|
1713
1833
|
function parseViewProjection(value) {
|
|
1714
|
-
|
|
1834
|
+
switch (value.toLowerCase()) {
|
|
1835
|
+
case "topdown":
|
|
1836
|
+
return "topdown";
|
|
1837
|
+
case "isometric":
|
|
1838
|
+
return "isometric";
|
|
1839
|
+
case "orthographic":
|
|
1840
|
+
return "orthographic";
|
|
1841
|
+
case "perspective":
|
|
1842
|
+
return "perspective";
|
|
1843
|
+
default:
|
|
1844
|
+
return null;
|
|
1845
|
+
}
|
|
1715
1846
|
}
|
|
1716
1847
|
function parseRenderPreset(value) {
|
|
1717
1848
|
const normalized = value.toLowerCase();
|
|
@@ -1749,7 +1880,7 @@
|
|
|
1749
1880
|
}
|
|
1750
1881
|
function parseViewpointGroups(value, document2, relationships, objectMap) {
|
|
1751
1882
|
return splitListValue(value).map((entry) => {
|
|
1752
|
-
if (document2.schemaVersion === "2.1" || document2.groups.some((group) => group.id === entry)) {
|
|
1883
|
+
if (document2.schemaVersion === "2.1" || document2.schemaVersion === "2.5" || document2.groups.some((group) => group.id === entry)) {
|
|
1753
1884
|
return entry;
|
|
1754
1885
|
}
|
|
1755
1886
|
if (entry.startsWith("wo-") && entry.endsWith("-group")) {
|
|
@@ -2581,7 +2712,9 @@
|
|
|
2581
2712
|
objectId: pose.objectId,
|
|
2582
2713
|
placement: clonePlacement(pose.placement),
|
|
2583
2714
|
inner: pose.inner ? { ...pose.inner } : void 0,
|
|
2584
|
-
outer: pose.outer ? { ...pose.outer } : void 0
|
|
2715
|
+
outer: pose.outer ? { ...pose.outer } : void 0,
|
|
2716
|
+
epoch: pose.epoch ?? null,
|
|
2717
|
+
referencePlane: pose.referencePlane ?? null
|
|
2585
2718
|
};
|
|
2586
2719
|
}
|
|
2587
2720
|
function clonePlacement(placement) {
|
|
@@ -2596,21 +2729,42 @@
|
|
|
2596
2729
|
return;
|
|
2597
2730
|
}
|
|
2598
2731
|
const objectMap = new Map(objects.map((object) => [object.id, object]));
|
|
2732
|
+
const referencedIds = /* @__PURE__ */ new Set([
|
|
2733
|
+
...event.targetObjectId ? [event.targetObjectId] : [],
|
|
2734
|
+
...event.participantObjectIds,
|
|
2735
|
+
...event.positions.map((pose) => pose.objectId)
|
|
2736
|
+
]);
|
|
2737
|
+
for (const objectId of referencedIds) {
|
|
2738
|
+
const object = objectMap.get(objectId);
|
|
2739
|
+
if (!object) {
|
|
2740
|
+
continue;
|
|
2741
|
+
}
|
|
2742
|
+
if (event.epoch) {
|
|
2743
|
+
object.epoch = event.epoch;
|
|
2744
|
+
}
|
|
2745
|
+
if (event.referencePlane) {
|
|
2746
|
+
object.referencePlane = event.referencePlane;
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2599
2749
|
for (const pose of event.positions) {
|
|
2600
2750
|
const object = objectMap.get(pose.objectId);
|
|
2601
2751
|
if (!object) {
|
|
2602
2752
|
continue;
|
|
2603
2753
|
}
|
|
2604
|
-
|
|
2754
|
+
if (pose.placement) {
|
|
2755
|
+
object.placement = clonePlacement(pose.placement);
|
|
2756
|
+
}
|
|
2605
2757
|
if (pose.inner) {
|
|
2606
2758
|
object.properties.inner = { ...pose.inner };
|
|
2607
|
-
} else {
|
|
2608
|
-
delete object.properties.inner;
|
|
2609
2759
|
}
|
|
2610
2760
|
if (pose.outer) {
|
|
2611
2761
|
object.properties.outer = { ...pose.outer };
|
|
2612
|
-
}
|
|
2613
|
-
|
|
2762
|
+
}
|
|
2763
|
+
if (pose.epoch) {
|
|
2764
|
+
object.epoch = pose.epoch;
|
|
2765
|
+
}
|
|
2766
|
+
if (pose.referencePlane) {
|
|
2767
|
+
object.referencePlane = pose.referencePlane;
|
|
2614
2768
|
}
|
|
2615
2769
|
}
|
|
2616
2770
|
}
|
|
@@ -2686,6 +2840,18 @@
|
|
|
2686
2840
|
if (viewpoint.rotationDeg !== 0) {
|
|
2687
2841
|
info2[`${prefix}.rotation`] = String(viewpoint.rotationDeg);
|
|
2688
2842
|
}
|
|
2843
|
+
if (viewpoint.camera?.azimuth !== null) {
|
|
2844
|
+
info2[`${prefix}.camera.azimuth`] = String(viewpoint.camera?.azimuth);
|
|
2845
|
+
}
|
|
2846
|
+
if (viewpoint.camera?.elevation !== null) {
|
|
2847
|
+
info2[`${prefix}.camera.elevation`] = String(viewpoint.camera?.elevation);
|
|
2848
|
+
}
|
|
2849
|
+
if (viewpoint.camera?.roll !== null) {
|
|
2850
|
+
info2[`${prefix}.camera.roll`] = String(viewpoint.camera?.roll);
|
|
2851
|
+
}
|
|
2852
|
+
if (viewpoint.camera?.distance !== null) {
|
|
2853
|
+
info2[`${prefix}.camera.distance`] = String(viewpoint.camera?.distance);
|
|
2854
|
+
}
|
|
2689
2855
|
const serializedLayers = serializeViewpointLayers(viewpoint.layers);
|
|
2690
2856
|
if (serializedLayers) {
|
|
2691
2857
|
info2[`${prefix}.layers`] = serializedLayers;
|
|
@@ -2929,13 +3095,13 @@
|
|
|
2929
3095
|
validateRelation(relation, objectMap, diagnostics);
|
|
2930
3096
|
}
|
|
2931
3097
|
for (const viewpoint of document2.system?.viewpoints ?? []) {
|
|
2932
|
-
validateViewpoint(viewpoint
|
|
3098
|
+
validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, diagnostics, objectMap);
|
|
2933
3099
|
}
|
|
2934
3100
|
for (const object of document2.objects) {
|
|
2935
3101
|
validateObject(object, document2.system, objectMap, groupIds, diagnostics);
|
|
2936
3102
|
}
|
|
2937
3103
|
for (const event of document2.events) {
|
|
2938
|
-
validateEvent(event, objectMap, diagnostics);
|
|
3104
|
+
validateEvent(event, document2.system, objectMap, diagnostics);
|
|
2939
3105
|
}
|
|
2940
3106
|
return diagnostics;
|
|
2941
3107
|
}
|
|
@@ -2954,21 +3120,24 @@
|
|
|
2954
3120
|
diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
|
|
2955
3121
|
}
|
|
2956
3122
|
}
|
|
2957
|
-
function validateViewpoint(
|
|
2958
|
-
|
|
3123
|
+
function validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, diagnostics, objectMap) {
|
|
3124
|
+
const filter = viewpoint.filter;
|
|
3125
|
+
if (sourceSchemaVersion === "2.1" || sourceSchemaVersion === "2.5") {
|
|
2959
3126
|
if (filter) {
|
|
2960
3127
|
for (const groupId of filter.groupIds) {
|
|
2961
3128
|
if (!groupIds.has(groupId)) {
|
|
2962
|
-
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${
|
|
3129
|
+
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpoint.id}".`, void 0, `viewpoint.${viewpoint.id}.groups`));
|
|
2963
3130
|
}
|
|
2964
3131
|
}
|
|
2965
3132
|
}
|
|
2966
|
-
for (const eventId of
|
|
3133
|
+
for (const eventId of viewpoint.events ?? []) {
|
|
2967
3134
|
if (!eventIds.has(eventId)) {
|
|
2968
|
-
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${
|
|
3135
|
+
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${viewpoint.id}".`, void 0, `viewpoint.${viewpoint.id}.events`));
|
|
2969
3136
|
}
|
|
2970
3137
|
}
|
|
2971
3138
|
}
|
|
3139
|
+
validateProjection(viewpoint.projection, diagnostics, `viewpoint.${viewpoint.id}.projection`, viewpoint.id);
|
|
3140
|
+
validateCamera(viewpoint.camera, viewpoint.projection, viewpoint.rotationDeg, diagnostics, viewpoint.id, viewpoint.focusObjectId, viewpoint.selectedObjectId, filter, objectMap);
|
|
2972
3141
|
}
|
|
2973
3142
|
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
2974
3143
|
const placement = object.placement;
|
|
@@ -2981,6 +3150,12 @@
|
|
|
2981
3150
|
}
|
|
2982
3151
|
}
|
|
2983
3152
|
}
|
|
3153
|
+
if (typeof object.epoch === "string" && !object.epoch.trim()) {
|
|
3154
|
+
diagnostics.push(warn("validate.epoch.empty", `Object "${object.id}" defines an empty epoch string.`, object.id, "epoch"));
|
|
3155
|
+
}
|
|
3156
|
+
if (typeof object.referencePlane === "string" && !object.referencePlane.trim()) {
|
|
3157
|
+
diagnostics.push(warn("validate.referencePlane.empty", `Object "${object.id}" defines an empty reference plane string.`, object.id, "referencePlane"));
|
|
3158
|
+
}
|
|
2984
3159
|
if (orbitPlacement) {
|
|
2985
3160
|
if (!objectMap.has(orbitPlacement.target)) {
|
|
2986
3161
|
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
@@ -3052,12 +3227,18 @@
|
|
|
3052
3227
|
}
|
|
3053
3228
|
}
|
|
3054
3229
|
}
|
|
3055
|
-
function validateEvent(event, objectMap, diagnostics) {
|
|
3230
|
+
function validateEvent(event, system, objectMap, diagnostics) {
|
|
3056
3231
|
const fieldPrefix = `event.${event.id}`;
|
|
3057
3232
|
const referencedIds = /* @__PURE__ */ new Set();
|
|
3058
3233
|
if (!event.kind.trim()) {
|
|
3059
3234
|
diagnostics.push(error("validate.event.kind.required", `Event "${event.id}" is missing a "kind" value.`, void 0, `${fieldPrefix}.kind`));
|
|
3060
3235
|
}
|
|
3236
|
+
if (typeof event.epoch === "string" && !event.epoch.trim()) {
|
|
3237
|
+
diagnostics.push(warn("validate.event.epoch.empty", `Event "${event.id}" defines an empty epoch string.`, void 0, `${fieldPrefix}.epoch`));
|
|
3238
|
+
}
|
|
3239
|
+
if (typeof event.referencePlane === "string" && !event.referencePlane.trim()) {
|
|
3240
|
+
diagnostics.push(warn("validate.event.referencePlane.empty", `Event "${event.id}" defines an empty reference plane string.`, void 0, `${fieldPrefix}.referencePlane`));
|
|
3241
|
+
}
|
|
3061
3242
|
if (!event.targetObjectId && event.participantObjectIds.length === 0) {
|
|
3062
3243
|
diagnostics.push(error("validate.event.references.required", `Event "${event.id}" must define a "target" or at least one participant.`, void 0, `${fieldPrefix}.participants`));
|
|
3063
3244
|
}
|
|
@@ -3104,10 +3285,14 @@
|
|
|
3104
3285
|
if (!referencedIds.has(pose.objectId)) {
|
|
3105
3286
|
diagnostics.push(warn("validate.event.pose.unreferenced", `Event pose "${pose.objectId}" on "${event.id}" is not listed in target/participants.`, void 0, poseFieldPrefix));
|
|
3106
3287
|
}
|
|
3107
|
-
validateEventPose(pose, object, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
3288
|
+
validateEventPose(pose, object, event, system, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
3289
|
+
}
|
|
3290
|
+
const missingPoseIds = [...referencedIds].filter((objectId) => !poseIds.has(objectId));
|
|
3291
|
+
if (event.positions.length > 0 && missingPoseIds.length > 0) {
|
|
3292
|
+
diagnostics.push(warn("validate.event.positions.partial", `Event "${event.id}" leaves ${missingPoseIds.length} referenced object(s) on their base placement.`, void 0, `${fieldPrefix}.positions`));
|
|
3108
3293
|
}
|
|
3109
3294
|
}
|
|
3110
|
-
function validateEventPose(pose, object, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
3295
|
+
function validateEventPose(pose, object, event, system, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
3111
3296
|
const placement = pose.placement;
|
|
3112
3297
|
if (!placement) {
|
|
3113
3298
|
diagnostics.push(error("validate.event.pose.placement.required", `Event "${eventId}" pose "${pose.objectId}" is missing a placement mode.`, void 0, fieldPrefix));
|
|
@@ -3120,6 +3305,15 @@
|
|
|
3120
3305
|
if (placement.distance && placement.semiMajor) {
|
|
3121
3306
|
diagnostics.push(error("validate.event.pose.orbit.distanceConflict", `Event "${eventId}" pose "${pose.objectId}" cannot declare both "distance" and "semiMajor".`, void 0, `${fieldPrefix}.distance`));
|
|
3122
3307
|
}
|
|
3308
|
+
if (placement.phase && !resolveEffectiveEpoch(system, object, event, pose)) {
|
|
3309
|
+
diagnostics.push(warn("validate.event.pose.phase.epochMissing", `Event "${eventId}" pose "${pose.objectId}" sets "phase" without an effective epoch.`, void 0, `${fieldPrefix}.phase`));
|
|
3310
|
+
}
|
|
3311
|
+
if (placement.inclination && !resolveEffectiveReferencePlane(system, object, event, pose)) {
|
|
3312
|
+
diagnostics.push(warn("validate.event.pose.inclination.referencePlaneMissing", `Event "${eventId}" pose "${pose.objectId}" sets "inclination" without an effective reference plane.`, void 0, `${fieldPrefix}.inclination`));
|
|
3313
|
+
}
|
|
3314
|
+
if (placement.period && !massInSolar(objectMap.get(placement.target)?.properties.mass)) {
|
|
3315
|
+
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`));
|
|
3316
|
+
}
|
|
3123
3317
|
return;
|
|
3124
3318
|
}
|
|
3125
3319
|
if (placement.mode === "surface") {
|
|
@@ -3254,6 +3448,52 @@
|
|
|
3254
3448
|
return null;
|
|
3255
3449
|
}
|
|
3256
3450
|
}
|
|
3451
|
+
function validateProjection(projection, diagnostics, field, viewpointId) {
|
|
3452
|
+
if (projection !== "topdown" && projection !== "isometric" && projection !== "orthographic" && projection !== "perspective") {
|
|
3453
|
+
diagnostics.push(error("validate.viewpoint.projection.invalid", `Unknown projection "${String(projection)}" in viewpoint "${viewpointId}".`, void 0, field));
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
function validateCamera(camera, projection, rotationDeg, diagnostics, viewpointId, focusObjectId, selectedObjectId, filter, objectMap) {
|
|
3457
|
+
if (!camera) {
|
|
3458
|
+
return;
|
|
3459
|
+
}
|
|
3460
|
+
const prefix = `viewpoint.${viewpointId}.camera`;
|
|
3461
|
+
for (const [key, value] of [
|
|
3462
|
+
["azimuth", camera.azimuth],
|
|
3463
|
+
["elevation", camera.elevation],
|
|
3464
|
+
["roll", camera.roll],
|
|
3465
|
+
["distance", camera.distance]
|
|
3466
|
+
]) {
|
|
3467
|
+
if (value !== null && (!Number.isFinite(value) || key === "distance" && value <= 0)) {
|
|
3468
|
+
diagnostics.push(error("validate.viewpoint.camera.invalid", `Invalid camera ${key} "${String(value)}" in viewpoint "${viewpointId}".`, void 0, `${prefix}.${key}`));
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
if (camera.distance !== null && projection !== "perspective") {
|
|
3472
|
+
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`));
|
|
3473
|
+
}
|
|
3474
|
+
if (projection === "topdown" && (camera.elevation !== null || camera.roll !== null)) {
|
|
3475
|
+
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));
|
|
3476
|
+
}
|
|
3477
|
+
if (projection === "isometric" && camera.elevation !== null) {
|
|
3478
|
+
diagnostics.push(info("validate.viewpoint.camera.isometricStored", `Camera elevation on isometric viewpoint "${viewpointId}" is preserved semantically for future 3D rendering.`, void 0, `${prefix}.elevation`));
|
|
3479
|
+
}
|
|
3480
|
+
if (camera.azimuth !== null && camera.azimuth !== 0 && rotationDeg !== 0) {
|
|
3481
|
+
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`));
|
|
3482
|
+
}
|
|
3483
|
+
const hasAnchor = focusObjectId !== null && objectMap.has(focusObjectId) || selectedObjectId !== null && objectMap.has(selectedObjectId) || !!filter;
|
|
3484
|
+
if (!hasAnchor) {
|
|
3485
|
+
diagnostics.push(info("validate.viewpoint.camera.anchorMissing", `Viewpoint "${viewpointId}" stores camera settings without a focus object, selection, or filter anchor.`, void 0, prefix));
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
function resolveEffectiveEpoch(system, object, event, pose) {
|
|
3489
|
+
return normalizeOptionalContextString(pose?.epoch) ?? normalizeOptionalContextString(event?.epoch) ?? normalizeOptionalContextString(object.epoch) ?? normalizeOptionalContextString(system?.epoch) ?? null;
|
|
3490
|
+
}
|
|
3491
|
+
function resolveEffectiveReferencePlane(system, object, event, pose) {
|
|
3492
|
+
return normalizeOptionalContextString(pose?.referencePlane) ?? normalizeOptionalContextString(event?.referencePlane) ?? normalizeOptionalContextString(object.referencePlane) ?? normalizeOptionalContextString(system?.referencePlane) ?? null;
|
|
3493
|
+
}
|
|
3494
|
+
function normalizeOptionalContextString(value) {
|
|
3495
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
3496
|
+
}
|
|
3257
3497
|
function toleranceForField(object, field) {
|
|
3258
3498
|
const tolerance = object.tolerances?.find((entry) => entry.field === field)?.value;
|
|
3259
3499
|
if (typeof tolerance === "number") {
|
|
@@ -3362,7 +3602,9 @@
|
|
|
3362
3602
|
"surface",
|
|
3363
3603
|
"free",
|
|
3364
3604
|
"inner",
|
|
3365
|
-
"outer"
|
|
3605
|
+
"outer",
|
|
3606
|
+
"epoch",
|
|
3607
|
+
"referencePlane"
|
|
3366
3608
|
]);
|
|
3367
3609
|
function parseWorldOrbitAtlas(source) {
|
|
3368
3610
|
return parseAtlasSource(source);
|
|
@@ -3404,7 +3646,7 @@
|
|
|
3404
3646
|
if (!sawSchemaHeader) {
|
|
3405
3647
|
sourceSchemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
3406
3648
|
sawSchemaHeader = true;
|
|
3407
|
-
if (prepared.comments.length > 0 && sourceSchemaVersion
|
|
3649
|
+
if (prepared.comments.length > 0 && isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
3408
3650
|
diagnostics.push({
|
|
3409
3651
|
code: "parse.schema21.commentCompatibility",
|
|
3410
3652
|
severity: "warning",
|
|
@@ -3474,11 +3716,11 @@
|
|
|
3474
3716
|
return document2;
|
|
3475
3717
|
}
|
|
3476
3718
|
function assertDraftSchemaHeader(tokens, line) {
|
|
3477
|
-
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1"].includes(tokens[1].value.toLowerCase())) {
|
|
3478
|
-
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
3719
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1", "2.5"].includes(tokens[1].value.toLowerCase())) {
|
|
3720
|
+
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);
|
|
3479
3721
|
}
|
|
3480
3722
|
const version = tokens[1].value.toLowerCase();
|
|
3481
|
-
return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
3723
|
+
return version === "2.5" ? "2.5" : version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
3482
3724
|
}
|
|
3483
3725
|
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, flags) {
|
|
3484
3726
|
const keyword = tokens[0]?.value.toLowerCase();
|
|
@@ -3498,6 +3740,8 @@
|
|
|
3498
3740
|
return {
|
|
3499
3741
|
kind: "defaults",
|
|
3500
3742
|
system,
|
|
3743
|
+
sourceSchemaVersion,
|
|
3744
|
+
diagnostics,
|
|
3501
3745
|
seenFields: /* @__PURE__ */ new Set()
|
|
3502
3746
|
};
|
|
3503
3747
|
case "atlas":
|
|
@@ -3590,6 +3834,7 @@
|
|
|
3590
3834
|
preset: system.defaults.preset,
|
|
3591
3835
|
zoom: null,
|
|
3592
3836
|
rotationDeg: 0,
|
|
3837
|
+
camera: null,
|
|
3593
3838
|
layers: {},
|
|
3594
3839
|
filter: null
|
|
3595
3840
|
};
|
|
@@ -3603,7 +3848,10 @@
|
|
|
3603
3848
|
seenFields: /* @__PURE__ */ new Set(),
|
|
3604
3849
|
inFilter: false,
|
|
3605
3850
|
filterIndent: null,
|
|
3606
|
-
seenFilterFields: /* @__PURE__ */ new Set()
|
|
3851
|
+
seenFilterFields: /* @__PURE__ */ new Set(),
|
|
3852
|
+
inCamera: false,
|
|
3853
|
+
cameraIndent: null,
|
|
3854
|
+
seenCameraFields: /* @__PURE__ */ new Set()
|
|
3607
3855
|
};
|
|
3608
3856
|
}
|
|
3609
3857
|
function startAnnotationSection(tokens, line, system, annotationIds) {
|
|
@@ -3710,6 +3958,8 @@
|
|
|
3710
3958
|
participantObjectIds: [],
|
|
3711
3959
|
timing: null,
|
|
3712
3960
|
visibility: null,
|
|
3961
|
+
epoch: null,
|
|
3962
|
+
referencePlane: null,
|
|
3713
3963
|
tags: [],
|
|
3714
3964
|
color: null,
|
|
3715
3965
|
hidden: false,
|
|
@@ -3834,6 +4084,12 @@
|
|
|
3834
4084
|
const value = joinFieldValue(tokens, line);
|
|
3835
4085
|
switch (key) {
|
|
3836
4086
|
case "view":
|
|
4087
|
+
if (isSchema25Projection(value)) {
|
|
4088
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "defaults.view", {
|
|
4089
|
+
line,
|
|
4090
|
+
column: tokens[0].column
|
|
4091
|
+
});
|
|
4092
|
+
}
|
|
3837
4093
|
section.system.defaults.view = parseProjectionValue(value, line, tokens[0].column);
|
|
3838
4094
|
return;
|
|
3839
4095
|
case "scale":
|
|
@@ -3873,14 +4129,36 @@
|
|
|
3873
4129
|
throw new WorldOrbitError(`Unknown atlas field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3874
4130
|
}
|
|
3875
4131
|
function applyViewpointField2(section, indent, tokens, line) {
|
|
4132
|
+
if (section.inCamera && indent <= (section.cameraIndent ?? 0)) {
|
|
4133
|
+
section.inCamera = false;
|
|
4134
|
+
section.cameraIndent = null;
|
|
4135
|
+
}
|
|
3876
4136
|
if (section.inFilter && indent <= (section.filterIndent ?? 0)) {
|
|
3877
4137
|
section.inFilter = false;
|
|
3878
4138
|
section.filterIndent = null;
|
|
3879
4139
|
}
|
|
4140
|
+
if (section.inCamera) {
|
|
4141
|
+
applyViewpointCameraField(section, tokens, line);
|
|
4142
|
+
return;
|
|
4143
|
+
}
|
|
3880
4144
|
if (section.inFilter) {
|
|
3881
4145
|
applyViewpointFilterField(section, tokens, line);
|
|
3882
4146
|
return;
|
|
3883
4147
|
}
|
|
4148
|
+
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "camera") {
|
|
4149
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.camera", {
|
|
4150
|
+
line,
|
|
4151
|
+
column: tokens[0].column
|
|
4152
|
+
});
|
|
4153
|
+
if (section.seenFields.has("camera")) {
|
|
4154
|
+
throw new WorldOrbitError('Duplicate viewpoint field "camera"', line, tokens[0].column);
|
|
4155
|
+
}
|
|
4156
|
+
section.seenFields.add("camera");
|
|
4157
|
+
section.inCamera = true;
|
|
4158
|
+
section.cameraIndent = indent;
|
|
4159
|
+
section.viewpoint.camera = section.viewpoint.camera ?? createEmptyViewCamera2();
|
|
4160
|
+
return;
|
|
4161
|
+
}
|
|
3884
4162
|
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "filter") {
|
|
3885
4163
|
if (section.seenFields.has("filter")) {
|
|
3886
4164
|
throw new WorldOrbitError('Duplicate viewpoint field "filter"', line, tokens[0].column);
|
|
@@ -3906,6 +4184,12 @@
|
|
|
3906
4184
|
section.viewpoint.selectedObjectId = value;
|
|
3907
4185
|
return;
|
|
3908
4186
|
case "projection":
|
|
4187
|
+
if (isSchema25Projection(value)) {
|
|
4188
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "projection", {
|
|
4189
|
+
line,
|
|
4190
|
+
column: tokens[0].column
|
|
4191
|
+
});
|
|
4192
|
+
}
|
|
3909
4193
|
section.viewpoint.projection = parseProjectionValue(value, line, tokens[0].column);
|
|
3910
4194
|
return;
|
|
3911
4195
|
case "preset":
|
|
@@ -3917,6 +4201,13 @@
|
|
|
3917
4201
|
case "rotation":
|
|
3918
4202
|
section.viewpoint.rotationDeg = parseFiniteNumber2(value, line, tokens[0].column, "rotation");
|
|
3919
4203
|
return;
|
|
4204
|
+
case "camera":
|
|
4205
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.camera", {
|
|
4206
|
+
line,
|
|
4207
|
+
column: tokens[0].column
|
|
4208
|
+
});
|
|
4209
|
+
section.viewpoint.camera = parseInlineViewCamera(tokens.slice(1), line, section.viewpoint.camera);
|
|
4210
|
+
return;
|
|
3920
4211
|
case "layers":
|
|
3921
4212
|
section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line, section.sourceSchemaVersion, section.diagnostics);
|
|
3922
4213
|
return;
|
|
@@ -3931,6 +4222,28 @@
|
|
|
3931
4222
|
throw new WorldOrbitError(`Unknown viewpoint field "${tokens[0].value}"`, line, tokens[0].column);
|
|
3932
4223
|
}
|
|
3933
4224
|
}
|
|
4225
|
+
function applyViewpointCameraField(section, tokens, line) {
|
|
4226
|
+
const key = requireUniqueField(tokens, section.seenCameraFields, line);
|
|
4227
|
+
const value = joinFieldValue(tokens, line);
|
|
4228
|
+
const camera = section.viewpoint.camera ?? createEmptyViewCamera2();
|
|
4229
|
+
switch (key) {
|
|
4230
|
+
case "azimuth":
|
|
4231
|
+
camera.azimuth = parseFiniteNumber2(value, line, tokens[0].column, "camera.azimuth");
|
|
4232
|
+
break;
|
|
4233
|
+
case "elevation":
|
|
4234
|
+
camera.elevation = parseFiniteNumber2(value, line, tokens[0].column, "camera.elevation");
|
|
4235
|
+
break;
|
|
4236
|
+
case "roll":
|
|
4237
|
+
camera.roll = parseFiniteNumber2(value, line, tokens[0].column, "camera.roll");
|
|
4238
|
+
break;
|
|
4239
|
+
case "distance":
|
|
4240
|
+
camera.distance = parsePositiveNumber2(value, line, tokens[0].column, "camera.distance");
|
|
4241
|
+
break;
|
|
4242
|
+
default:
|
|
4243
|
+
throw new WorldOrbitError(`Unknown viewpoint camera field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4244
|
+
}
|
|
4245
|
+
section.viewpoint.camera = camera;
|
|
4246
|
+
}
|
|
3934
4247
|
function applyViewpointFilterField(section, tokens, line) {
|
|
3935
4248
|
const key = requireUniqueField(tokens, section.seenFilterFields, line);
|
|
3936
4249
|
const filter = section.viewpoint.filter ?? createEmptyViewpointFilter2();
|
|
@@ -4041,6 +4354,12 @@
|
|
|
4041
4354
|
section.positionsIndent = null;
|
|
4042
4355
|
}
|
|
4043
4356
|
if (section.activePose) {
|
|
4357
|
+
if (tokens[0]?.value === "epoch" || tokens[0]?.value === "referencePlane") {
|
|
4358
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, `pose.${tokens[0].value}`, {
|
|
4359
|
+
line,
|
|
4360
|
+
column: tokens[0]?.column ?? 1
|
|
4361
|
+
});
|
|
4362
|
+
}
|
|
4044
4363
|
section.activePose.fields.push(parseEventPoseField(tokens, line, section.activePoseSeenFields));
|
|
4045
4364
|
return;
|
|
4046
4365
|
}
|
|
@@ -4095,6 +4414,20 @@
|
|
|
4095
4414
|
case "visibility":
|
|
4096
4415
|
section.event.visibility = joinFieldValue(tokens, line);
|
|
4097
4416
|
return;
|
|
4417
|
+
case "epoch":
|
|
4418
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "event.epoch", {
|
|
4419
|
+
line,
|
|
4420
|
+
column: tokens[0].column
|
|
4421
|
+
});
|
|
4422
|
+
section.event.epoch = joinFieldValue(tokens, line);
|
|
4423
|
+
return;
|
|
4424
|
+
case "referenceplane":
|
|
4425
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "event.referencePlane", {
|
|
4426
|
+
line,
|
|
4427
|
+
column: tokens[0].column
|
|
4428
|
+
});
|
|
4429
|
+
section.event.referencePlane = joinFieldValue(tokens, line);
|
|
4430
|
+
return;
|
|
4098
4431
|
case "tags":
|
|
4099
4432
|
section.event.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4100
4433
|
return;
|
|
@@ -4222,11 +4555,15 @@
|
|
|
4222
4555
|
}
|
|
4223
4556
|
function parseProjectionValue(value, line, column) {
|
|
4224
4557
|
const normalized = value.toLowerCase();
|
|
4225
|
-
if (normalized !== "topdown" && normalized !== "isometric") {
|
|
4558
|
+
if (normalized !== "topdown" && normalized !== "isometric" && normalized !== "orthographic" && normalized !== "perspective") {
|
|
4226
4559
|
throw new WorldOrbitError(`Unknown projection "${value}"`, line, column);
|
|
4227
4560
|
}
|
|
4228
4561
|
return normalized;
|
|
4229
4562
|
}
|
|
4563
|
+
function isSchema25Projection(value) {
|
|
4564
|
+
const normalized = value.toLowerCase();
|
|
4565
|
+
return normalized === "orthographic" || normalized === "perspective";
|
|
4566
|
+
}
|
|
4230
4567
|
function parsePresetValue(value, line, column) {
|
|
4231
4568
|
const normalized = value.toLowerCase();
|
|
4232
4569
|
if (normalized === "diagram" || normalized === "presentation" || normalized === "atlas-card" || normalized === "markdown") {
|
|
@@ -4256,6 +4593,48 @@
|
|
|
4256
4593
|
groupIds: []
|
|
4257
4594
|
};
|
|
4258
4595
|
}
|
|
4596
|
+
function createEmptyViewCamera2() {
|
|
4597
|
+
return {
|
|
4598
|
+
azimuth: null,
|
|
4599
|
+
elevation: null,
|
|
4600
|
+
roll: null,
|
|
4601
|
+
distance: null
|
|
4602
|
+
};
|
|
4603
|
+
}
|
|
4604
|
+
function parseInlineViewCamera(tokens, line, current) {
|
|
4605
|
+
if (tokens.length === 0 || tokens.length % 2 !== 0) {
|
|
4606
|
+
throw new WorldOrbitError('Field "camera" expects "<field> <value>" pairs', line, tokens[0]?.column ?? 1);
|
|
4607
|
+
}
|
|
4608
|
+
const camera = current ? { ...current } : createEmptyViewCamera2();
|
|
4609
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4610
|
+
for (let index = 0; index < tokens.length; index += 2) {
|
|
4611
|
+
const fieldToken = tokens[index];
|
|
4612
|
+
const valueToken = tokens[index + 1];
|
|
4613
|
+
const key = fieldToken.value.toLowerCase();
|
|
4614
|
+
if (seen.has(key)) {
|
|
4615
|
+
throw new WorldOrbitError(`Duplicate viewpoint camera field "${fieldToken.value}"`, line, fieldToken.column);
|
|
4616
|
+
}
|
|
4617
|
+
seen.add(key);
|
|
4618
|
+
const value = valueToken.value;
|
|
4619
|
+
switch (key) {
|
|
4620
|
+
case "azimuth":
|
|
4621
|
+
camera.azimuth = parseFiniteNumber2(value, line, fieldToken.column, "camera.azimuth");
|
|
4622
|
+
break;
|
|
4623
|
+
case "elevation":
|
|
4624
|
+
camera.elevation = parseFiniteNumber2(value, line, fieldToken.column, "camera.elevation");
|
|
4625
|
+
break;
|
|
4626
|
+
case "roll":
|
|
4627
|
+
camera.roll = parseFiniteNumber2(value, line, fieldToken.column, "camera.roll");
|
|
4628
|
+
break;
|
|
4629
|
+
case "distance":
|
|
4630
|
+
camera.distance = parsePositiveNumber2(value, line, fieldToken.column, "camera.distance");
|
|
4631
|
+
break;
|
|
4632
|
+
default:
|
|
4633
|
+
throw new WorldOrbitError(`Unknown viewpoint camera field "${fieldToken.value}"`, line, fieldToken.column);
|
|
4634
|
+
}
|
|
4635
|
+
}
|
|
4636
|
+
return camera;
|
|
4637
|
+
}
|
|
4259
4638
|
function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
4260
4639
|
const fields = [];
|
|
4261
4640
|
let index = 0;
|
|
@@ -4388,7 +4767,7 @@
|
|
|
4388
4767
|
object.tolerances = tolerances;
|
|
4389
4768
|
if (typedBlocks && Object.keys(typedBlocks).length > 0)
|
|
4390
4769
|
object.typedBlocks = typedBlocks;
|
|
4391
|
-
if (sourceSchemaVersion
|
|
4770
|
+
if (isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
4392
4771
|
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) {
|
|
4393
4772
|
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, node.id, node.location);
|
|
4394
4773
|
}
|
|
@@ -4404,23 +4783,25 @@
|
|
|
4404
4783
|
};
|
|
4405
4784
|
}
|
|
4406
4785
|
function normalizeDraftEventPose(rawPose) {
|
|
4407
|
-
const fieldMap = collectDraftFields(rawPose.fields);
|
|
4786
|
+
const fieldMap = collectDraftFields(rawPose.fields, "event-pose");
|
|
4408
4787
|
const placement = extractPlacementFromFieldMap(fieldMap);
|
|
4409
4788
|
return {
|
|
4410
4789
|
objectId: rawPose.objectId,
|
|
4411
4790
|
placement,
|
|
4412
4791
|
inner: parseOptionalUnitField(fieldMap.get("inner")?.[0], "inner"),
|
|
4413
|
-
outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer")
|
|
4792
|
+
outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer"),
|
|
4793
|
+
epoch: parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]),
|
|
4794
|
+
referencePlane: parseOptionalJoinedValue(fieldMap.get("referencePlane")?.[0])
|
|
4414
4795
|
};
|
|
4415
4796
|
}
|
|
4416
|
-
function collectDraftFields(fields) {
|
|
4797
|
+
function collectDraftFields(fields, _mode = "object") {
|
|
4417
4798
|
const grouped = /* @__PURE__ */ new Map();
|
|
4418
4799
|
for (const field of fields) {
|
|
4419
4800
|
const spec = getDraftObjectFieldSpec(field.key);
|
|
4420
|
-
if (!spec) {
|
|
4801
|
+
if (!spec && !EVENT_POSE_FIELD_KEYS.has(field.key)) {
|
|
4421
4802
|
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
4422
4803
|
}
|
|
4423
|
-
if (!spec
|
|
4804
|
+
if (!spec?.allowRepeat && grouped.has(field.key)) {
|
|
4424
4805
|
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
4425
4806
|
}
|
|
4426
4807
|
const existing = grouped.get(field.key) ?? [];
|
|
@@ -4597,7 +4978,7 @@
|
|
|
4597
4978
|
}
|
|
4598
4979
|
}
|
|
4599
4980
|
function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
4600
|
-
if (sourceSchemaVersion
|
|
4981
|
+
if (!isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
4601
4982
|
return;
|
|
4602
4983
|
}
|
|
4603
4984
|
diagnostics.push({
|
|
@@ -4609,6 +4990,34 @@
|
|
|
4609
4990
|
column: location.column
|
|
4610
4991
|
});
|
|
4611
4992
|
}
|
|
4993
|
+
function warnIfSchema25Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
4994
|
+
if (!isSchemaOlderThan(sourceSchemaVersion, "2.5")) {
|
|
4995
|
+
return;
|
|
4996
|
+
}
|
|
4997
|
+
diagnostics.push({
|
|
4998
|
+
code: "parse.schema25.featureCompatibility",
|
|
4999
|
+
severity: "warning",
|
|
5000
|
+
source: "parse",
|
|
5001
|
+
message: `Feature "${featureName}" requires schema 2.5; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
5002
|
+
line: location.line,
|
|
5003
|
+
column: location.column
|
|
5004
|
+
});
|
|
5005
|
+
}
|
|
5006
|
+
function isSchemaOlderThan(sourceSchemaVersion, requiredVersion) {
|
|
5007
|
+
return schemaVersionRank(sourceSchemaVersion) < schemaVersionRank(requiredVersion);
|
|
5008
|
+
}
|
|
5009
|
+
function schemaVersionRank(version) {
|
|
5010
|
+
switch (version) {
|
|
5011
|
+
case "2.0-draft":
|
|
5012
|
+
return 0;
|
|
5013
|
+
case "2.0":
|
|
5014
|
+
return 1;
|
|
5015
|
+
case "2.1":
|
|
5016
|
+
return 2;
|
|
5017
|
+
case "2.5":
|
|
5018
|
+
return 3;
|
|
5019
|
+
}
|
|
5020
|
+
}
|
|
4612
5021
|
function preprocessAtlasSource(source) {
|
|
4613
5022
|
const chars = [...source];
|
|
4614
5023
|
const comments = [];
|
|
@@ -4696,8 +5105,9 @@
|
|
|
4696
5105
|
}
|
|
4697
5106
|
|
|
4698
5107
|
// packages/core/dist/load.js
|
|
4699
|
-
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;
|
|
5108
|
+
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1|\.5)?$/i;
|
|
4700
5109
|
var ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
|
|
5110
|
+
var ATLAS_SCHEMA_25_PATTERN = /^schema\s+2\.5$/i;
|
|
4701
5111
|
var LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
|
|
4702
5112
|
function detectWorldOrbitSchemaVersion(source) {
|
|
4703
5113
|
for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
|
|
@@ -4711,6 +5121,9 @@
|
|
|
4711
5121
|
if (ATLAS_SCHEMA_21_PATTERN.test(trimmed)) {
|
|
4712
5122
|
return "2.1";
|
|
4713
5123
|
}
|
|
5124
|
+
if (ATLAS_SCHEMA_25_PATTERN.test(trimmed)) {
|
|
5125
|
+
return "2.5";
|
|
5126
|
+
}
|
|
4714
5127
|
if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
|
|
4715
5128
|
return "2.0";
|
|
4716
5129
|
}
|
|
@@ -4771,7 +5184,7 @@
|
|
|
4771
5184
|
}
|
|
4772
5185
|
function loadWorldOrbitSourceWithDiagnostics(source) {
|
|
4773
5186
|
const schemaVersion = detectWorldOrbitSchemaVersion(source);
|
|
4774
|
-
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1") {
|
|
5187
|
+
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1" || schemaVersion === "2.5") {
|
|
4775
5188
|
return loadAtlasSourceWithDiagnostics(source, schemaVersion);
|
|
4776
5189
|
}
|
|
4777
5190
|
let ast;
|