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,
|
|
@@ -1095,21 +1104,42 @@
|
|
|
1095
1104
|
return cloned;
|
|
1096
1105
|
}
|
|
1097
1106
|
const objectMap = new Map(cloned.map((object) => [object.id, object]));
|
|
1107
|
+
const referencedIds = /* @__PURE__ */ new Set([
|
|
1108
|
+
...activeEvent.targetObjectId ? [activeEvent.targetObjectId] : [],
|
|
1109
|
+
...activeEvent.participantObjectIds,
|
|
1110
|
+
...activeEvent.positions.map((pose) => pose.objectId)
|
|
1111
|
+
]);
|
|
1112
|
+
for (const objectId of referencedIds) {
|
|
1113
|
+
const object = objectMap.get(objectId);
|
|
1114
|
+
if (!object) {
|
|
1115
|
+
continue;
|
|
1116
|
+
}
|
|
1117
|
+
if (activeEvent.epoch) {
|
|
1118
|
+
object.epoch = activeEvent.epoch;
|
|
1119
|
+
}
|
|
1120
|
+
if (activeEvent.referencePlane) {
|
|
1121
|
+
object.referencePlane = activeEvent.referencePlane;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1098
1124
|
for (const pose of activeEvent.positions) {
|
|
1099
1125
|
const object = objectMap.get(pose.objectId);
|
|
1100
1126
|
if (!object) {
|
|
1101
1127
|
continue;
|
|
1102
1128
|
}
|
|
1103
|
-
|
|
1129
|
+
if (pose.placement) {
|
|
1130
|
+
object.placement = structuredClone(pose.placement);
|
|
1131
|
+
}
|
|
1104
1132
|
if (pose.inner) {
|
|
1105
1133
|
object.properties.inner = { ...pose.inner };
|
|
1106
|
-
} else {
|
|
1107
|
-
delete object.properties.inner;
|
|
1108
1134
|
}
|
|
1109
1135
|
if (pose.outer) {
|
|
1110
1136
|
object.properties.outer = { ...pose.outer };
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1137
|
+
}
|
|
1138
|
+
if (pose.epoch) {
|
|
1139
|
+
object.epoch = pose.epoch;
|
|
1140
|
+
}
|
|
1141
|
+
if (pose.referencePlane) {
|
|
1142
|
+
object.referencePlane = pose.referencePlane;
|
|
1113
1143
|
}
|
|
1114
1144
|
}
|
|
1115
1145
|
return cloned;
|
|
@@ -1150,10 +1180,59 @@
|
|
|
1150
1180
|
}
|
|
1151
1181
|
}
|
|
1152
1182
|
function resolveProjection(document2, projection) {
|
|
1153
|
-
if (projection === "topdown" || projection === "isometric") {
|
|
1183
|
+
if (projection === "topdown" || projection === "isometric" || projection === "orthographic" || projection === "perspective") {
|
|
1154
1184
|
return projection;
|
|
1155
1185
|
}
|
|
1156
|
-
|
|
1186
|
+
const documentView = String(document2.system?.properties.view ?? "topdown").toLowerCase();
|
|
1187
|
+
return parseViewProjection(documentView) ?? "topdown";
|
|
1188
|
+
}
|
|
1189
|
+
function resolveRenderProjection(projection, camera) {
|
|
1190
|
+
switch (projection) {
|
|
1191
|
+
case "topdown":
|
|
1192
|
+
return "topdown";
|
|
1193
|
+
case "isometric":
|
|
1194
|
+
return "isometric";
|
|
1195
|
+
case "orthographic":
|
|
1196
|
+
return camera && (camera.azimuth !== null || camera.elevation !== null || camera.roll !== null) ? "isometric" : "topdown";
|
|
1197
|
+
case "perspective":
|
|
1198
|
+
return "isometric";
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
function normalizeViewCamera(camera) {
|
|
1202
|
+
if (!camera) {
|
|
1203
|
+
return null;
|
|
1204
|
+
}
|
|
1205
|
+
const normalized = {
|
|
1206
|
+
azimuth: normalizeFiniteCameraValue(camera.azimuth),
|
|
1207
|
+
elevation: normalizeFiniteCameraValue(camera.elevation),
|
|
1208
|
+
roll: normalizeFiniteCameraValue(camera.roll),
|
|
1209
|
+
distance: normalizePositiveCameraDistance(camera.distance)
|
|
1210
|
+
};
|
|
1211
|
+
return normalized.azimuth !== null || normalized.elevation !== null || normalized.roll !== null || normalized.distance !== null ? normalized : null;
|
|
1212
|
+
}
|
|
1213
|
+
function normalizeFiniteCameraValue(value) {
|
|
1214
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1215
|
+
}
|
|
1216
|
+
function normalizePositiveCameraDistance(value) {
|
|
1217
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
1218
|
+
}
|
|
1219
|
+
function buildSceneSubtitle(projection, renderProjection, layoutPreset, camera) {
|
|
1220
|
+
const parts = [`${capitalizeLabel(projection)} view`, `${capitalizeLabel(layoutPreset)} layout`];
|
|
1221
|
+
if (projection !== renderProjection) {
|
|
1222
|
+
parts.push(`2D ${renderProjection} fallback`);
|
|
1223
|
+
}
|
|
1224
|
+
if (camera) {
|
|
1225
|
+
const cameraParts = [
|
|
1226
|
+
camera.azimuth !== null ? `az ${camera.azimuth}` : null,
|
|
1227
|
+
camera.elevation !== null ? `el ${camera.elevation}` : null,
|
|
1228
|
+
camera.roll !== null ? `roll ${camera.roll}` : null,
|
|
1229
|
+
camera.distance !== null ? `dist ${camera.distance}` : null
|
|
1230
|
+
].filter(Boolean);
|
|
1231
|
+
if (cameraParts.length > 0) {
|
|
1232
|
+
parts.push(`camera ${cameraParts.join(" / ")}`);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
return parts.join(" - ");
|
|
1157
1236
|
}
|
|
1158
1237
|
function resolveScaleModel(layoutPreset, overrides) {
|
|
1159
1238
|
const defaults = defaultScaleModel(layoutPreset);
|
|
@@ -1589,6 +1668,8 @@
|
|
|
1589
1668
|
function createGeneratedOverviewViewpoint(document2, projection, preset) {
|
|
1590
1669
|
const title = document2.system?.title ?? document2.system?.properties.title;
|
|
1591
1670
|
const label = title ? `${String(title)} Overview` : "Overview";
|
|
1671
|
+
const camera = normalizeViewCamera(null);
|
|
1672
|
+
const renderProjection = resolveRenderProjection(projection, camera);
|
|
1592
1673
|
return {
|
|
1593
1674
|
id: "overview",
|
|
1594
1675
|
label,
|
|
@@ -1597,6 +1678,8 @@
|
|
|
1597
1678
|
selectedObjectId: null,
|
|
1598
1679
|
eventIds: [],
|
|
1599
1680
|
projection,
|
|
1681
|
+
renderProjection,
|
|
1682
|
+
camera,
|
|
1600
1683
|
preset,
|
|
1601
1684
|
rotationDeg: 0,
|
|
1602
1685
|
scale: null,
|
|
@@ -1646,6 +1729,30 @@
|
|
|
1646
1729
|
case "angle":
|
|
1647
1730
|
draft.rotationDeg = parseFiniteNumber(normalizedValue) ?? draft.rotationDeg ?? 0;
|
|
1648
1731
|
return;
|
|
1732
|
+
case "camera.azimuth":
|
|
1733
|
+
draft.camera = {
|
|
1734
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1735
|
+
azimuth: parseFiniteNumber(normalizedValue)
|
|
1736
|
+
};
|
|
1737
|
+
return;
|
|
1738
|
+
case "camera.elevation":
|
|
1739
|
+
draft.camera = {
|
|
1740
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1741
|
+
elevation: parseFiniteNumber(normalizedValue)
|
|
1742
|
+
};
|
|
1743
|
+
return;
|
|
1744
|
+
case "camera.roll":
|
|
1745
|
+
draft.camera = {
|
|
1746
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1747
|
+
roll: parseFiniteNumber(normalizedValue)
|
|
1748
|
+
};
|
|
1749
|
+
return;
|
|
1750
|
+
case "camera.distance":
|
|
1751
|
+
draft.camera = {
|
|
1752
|
+
...draft.camera ?? createEmptyViewCamera(),
|
|
1753
|
+
distance: parsePositiveNumber(normalizedValue)
|
|
1754
|
+
};
|
|
1755
|
+
return;
|
|
1649
1756
|
case "zoom":
|
|
1650
1757
|
case "scale":
|
|
1651
1758
|
draft.scale = parsePositiveNumber(normalizedValue);
|
|
@@ -1685,6 +1792,9 @@
|
|
|
1685
1792
|
const selectedObjectId = draft.select && objectMap.has(draft.select) ? draft.select : objectId;
|
|
1686
1793
|
const filter = normalizeViewpointFilter(draft.filter);
|
|
1687
1794
|
const label = draft.label?.trim() || humanizeIdentifier(draft.id);
|
|
1795
|
+
const resolvedProjection = draft.projection ?? projection;
|
|
1796
|
+
const camera = normalizeViewCamera(draft.camera ?? null);
|
|
1797
|
+
const renderProjection = resolveRenderProjection(resolvedProjection, camera);
|
|
1688
1798
|
return {
|
|
1689
1799
|
id: draft.id,
|
|
1690
1800
|
label,
|
|
@@ -1692,7 +1802,9 @@
|
|
|
1692
1802
|
objectId,
|
|
1693
1803
|
selectedObjectId,
|
|
1694
1804
|
eventIds: [...new Set(draft.eventIds ?? [])],
|
|
1695
|
-
projection:
|
|
1805
|
+
projection: resolvedProjection,
|
|
1806
|
+
renderProjection,
|
|
1807
|
+
camera,
|
|
1696
1808
|
preset: draft.preset ?? preset,
|
|
1697
1809
|
rotationDeg: draft.rotationDeg ?? 0,
|
|
1698
1810
|
scale: draft.scale ?? null,
|
|
@@ -1709,6 +1821,14 @@
|
|
|
1709
1821
|
groupIds: []
|
|
1710
1822
|
};
|
|
1711
1823
|
}
|
|
1824
|
+
function createEmptyViewCamera() {
|
|
1825
|
+
return {
|
|
1826
|
+
azimuth: null,
|
|
1827
|
+
elevation: null,
|
|
1828
|
+
roll: null,
|
|
1829
|
+
distance: null
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1712
1832
|
function normalizeViewpointFilter(filter) {
|
|
1713
1833
|
if (!filter) {
|
|
1714
1834
|
return null;
|
|
@@ -1722,7 +1842,18 @@
|
|
|
1722
1842
|
return normalized.query || normalized.objectTypes.length > 0 || normalized.tags.length > 0 || normalized.groupIds.length > 0 ? normalized : null;
|
|
1723
1843
|
}
|
|
1724
1844
|
function parseViewProjection(value) {
|
|
1725
|
-
|
|
1845
|
+
switch (value.toLowerCase()) {
|
|
1846
|
+
case "topdown":
|
|
1847
|
+
return "topdown";
|
|
1848
|
+
case "isometric":
|
|
1849
|
+
return "isometric";
|
|
1850
|
+
case "orthographic":
|
|
1851
|
+
return "orthographic";
|
|
1852
|
+
case "perspective":
|
|
1853
|
+
return "perspective";
|
|
1854
|
+
default:
|
|
1855
|
+
return null;
|
|
1856
|
+
}
|
|
1726
1857
|
}
|
|
1727
1858
|
function parseRenderPreset(value) {
|
|
1728
1859
|
const normalized = value.toLowerCase();
|
|
@@ -1760,7 +1891,7 @@
|
|
|
1760
1891
|
}
|
|
1761
1892
|
function parseViewpointGroups(value, document2, relationships, objectMap) {
|
|
1762
1893
|
return splitListValue(value).map((entry) => {
|
|
1763
|
-
if (document2.schemaVersion === "2.1" || document2.groups.some((group) => group.id === entry)) {
|
|
1894
|
+
if (document2.schemaVersion === "2.1" || document2.schemaVersion === "2.5" || document2.groups.some((group) => group.id === entry)) {
|
|
1764
1895
|
return entry;
|
|
1765
1896
|
}
|
|
1766
1897
|
if (entry.startsWith("wo-") && entry.endsWith("-group")) {
|
|
@@ -2553,8 +2684,8 @@
|
|
|
2553
2684
|
}
|
|
2554
2685
|
return {
|
|
2555
2686
|
format: "worldorbit",
|
|
2556
|
-
version: "2.
|
|
2557
|
-
schemaVersion: "2.
|
|
2687
|
+
version: "2.5",
|
|
2688
|
+
schemaVersion: "2.5",
|
|
2558
2689
|
sourceVersion: document2.version,
|
|
2559
2690
|
system,
|
|
2560
2691
|
groups: structuredClone(document2.groups ?? []),
|
|
@@ -2610,8 +2741,9 @@
|
|
|
2610
2741
|
};
|
|
2611
2742
|
}
|
|
2612
2743
|
function createDraftDefaults(document2, preset, projection) {
|
|
2744
|
+
const rawView = typeof document2.system?.properties.view === "string" ? document2.system.properties.view.toLowerCase() : null;
|
|
2613
2745
|
return {
|
|
2614
|
-
view:
|
|
2746
|
+
view: rawView === "topdown" || rawView === "isometric" || rawView === "orthographic" || rawView === "perspective" ? rawView : projection,
|
|
2615
2747
|
scale: typeof document2.system?.properties.scale === "string" ? document2.system.properties.scale : null,
|
|
2616
2748
|
units: typeof document2.system?.properties.units === "string" ? document2.system.properties.units : null,
|
|
2617
2749
|
preset,
|
|
@@ -2718,6 +2850,7 @@
|
|
|
2718
2850
|
preset: viewpoint.preset,
|
|
2719
2851
|
zoom: viewpoint.scale,
|
|
2720
2852
|
rotationDeg: viewpoint.rotationDeg,
|
|
2853
|
+
camera: viewpoint.camera ? { ...viewpoint.camera } : null,
|
|
2721
2854
|
layers: { ...viewpoint.layers },
|
|
2722
2855
|
filter: viewpoint.filter ? {
|
|
2723
2856
|
query: viewpoint.filter.query,
|
|
@@ -2759,7 +2892,9 @@
|
|
|
2759
2892
|
objectId: pose.objectId,
|
|
2760
2893
|
placement: clonePlacement(pose.placement),
|
|
2761
2894
|
inner: pose.inner ? { ...pose.inner } : void 0,
|
|
2762
|
-
outer: pose.outer ? { ...pose.outer } : void 0
|
|
2895
|
+
outer: pose.outer ? { ...pose.outer } : void 0,
|
|
2896
|
+
epoch: pose.epoch ?? null,
|
|
2897
|
+
referencePlane: pose.referencePlane ?? null
|
|
2763
2898
|
};
|
|
2764
2899
|
}
|
|
2765
2900
|
function clonePlacement(placement) {
|
|
@@ -2774,21 +2909,42 @@
|
|
|
2774
2909
|
return;
|
|
2775
2910
|
}
|
|
2776
2911
|
const objectMap = new Map(objects.map((object) => [object.id, object]));
|
|
2912
|
+
const referencedIds = /* @__PURE__ */ new Set([
|
|
2913
|
+
...event.targetObjectId ? [event.targetObjectId] : [],
|
|
2914
|
+
...event.participantObjectIds,
|
|
2915
|
+
...event.positions.map((pose) => pose.objectId)
|
|
2916
|
+
]);
|
|
2917
|
+
for (const objectId of referencedIds) {
|
|
2918
|
+
const object = objectMap.get(objectId);
|
|
2919
|
+
if (!object) {
|
|
2920
|
+
continue;
|
|
2921
|
+
}
|
|
2922
|
+
if (event.epoch) {
|
|
2923
|
+
object.epoch = event.epoch;
|
|
2924
|
+
}
|
|
2925
|
+
if (event.referencePlane) {
|
|
2926
|
+
object.referencePlane = event.referencePlane;
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2777
2929
|
for (const pose of event.positions) {
|
|
2778
2930
|
const object = objectMap.get(pose.objectId);
|
|
2779
2931
|
if (!object) {
|
|
2780
2932
|
continue;
|
|
2781
2933
|
}
|
|
2782
|
-
|
|
2934
|
+
if (pose.placement) {
|
|
2935
|
+
object.placement = clonePlacement(pose.placement);
|
|
2936
|
+
}
|
|
2783
2937
|
if (pose.inner) {
|
|
2784
2938
|
object.properties.inner = { ...pose.inner };
|
|
2785
|
-
} else {
|
|
2786
|
-
delete object.properties.inner;
|
|
2787
2939
|
}
|
|
2788
2940
|
if (pose.outer) {
|
|
2789
2941
|
object.properties.outer = { ...pose.outer };
|
|
2790
|
-
}
|
|
2791
|
-
|
|
2942
|
+
}
|
|
2943
|
+
if (pose.epoch) {
|
|
2944
|
+
object.epoch = pose.epoch;
|
|
2945
|
+
}
|
|
2946
|
+
if (pose.referencePlane) {
|
|
2947
|
+
object.referencePlane = pose.referencePlane;
|
|
2792
2948
|
}
|
|
2793
2949
|
}
|
|
2794
2950
|
}
|
|
@@ -2873,6 +3029,18 @@
|
|
|
2873
3029
|
if (viewpoint.rotationDeg !== 0) {
|
|
2874
3030
|
info2[`${prefix}.rotation`] = String(viewpoint.rotationDeg);
|
|
2875
3031
|
}
|
|
3032
|
+
if (viewpoint.camera?.azimuth !== null) {
|
|
3033
|
+
info2[`${prefix}.camera.azimuth`] = String(viewpoint.camera?.azimuth);
|
|
3034
|
+
}
|
|
3035
|
+
if (viewpoint.camera?.elevation !== null) {
|
|
3036
|
+
info2[`${prefix}.camera.elevation`] = String(viewpoint.camera?.elevation);
|
|
3037
|
+
}
|
|
3038
|
+
if (viewpoint.camera?.roll !== null) {
|
|
3039
|
+
info2[`${prefix}.camera.roll`] = String(viewpoint.camera?.roll);
|
|
3040
|
+
}
|
|
3041
|
+
if (viewpoint.camera?.distance !== null) {
|
|
3042
|
+
info2[`${prefix}.camera.distance`] = String(viewpoint.camera?.distance);
|
|
3043
|
+
}
|
|
2876
3044
|
const serializedLayers = serializeViewpointLayers(viewpoint.layers);
|
|
2877
3045
|
if (serializedLayers) {
|
|
2878
3046
|
info2[`${prefix}.layers`] = serializedLayers;
|
|
@@ -2969,26 +3137,26 @@
|
|
|
2969
3137
|
];
|
|
2970
3138
|
function formatDocument(document2, options = {}) {
|
|
2971
3139
|
const schema = options.schema ?? "auto";
|
|
2972
|
-
const useDraft = schema === "2.0" || schema === "2.1" || schema === "2.0-draft" || document2.version === "2.0" || document2.version === "2.1" || document2.version === "2.0-draft";
|
|
3140
|
+
const useDraft = schema === "2.0" || schema === "2.1" || schema === "2.5" || schema === "2.0-draft" || document2.version === "2.0" || document2.version === "2.1" || document2.version === "2.5" || document2.version === "2.0-draft";
|
|
2973
3141
|
if (useDraft) {
|
|
2974
3142
|
if (schema === "2.0-draft") {
|
|
2975
|
-
const legacyDraftDocument = document2.version === "2.0-draft" ? document2 : document2.version === "2.0" || document2.version === "2.1" ? {
|
|
3143
|
+
const legacyDraftDocument = document2.version === "2.0-draft" ? document2 : document2.version === "2.0" || document2.version === "2.1" || document2.version === "2.5" ? {
|
|
2976
3144
|
...document2,
|
|
2977
3145
|
version: "2.0-draft",
|
|
2978
3146
|
schemaVersion: "2.0-draft"
|
|
2979
3147
|
} : upgradeDocumentToDraftV2(document2);
|
|
2980
3148
|
return formatDraftDocument(legacyDraftDocument);
|
|
2981
3149
|
}
|
|
2982
|
-
const atlasDocument = document2.version === "2.0" || document2.version === "2.1" ? document2 : document2.version === "2.0-draft" ? {
|
|
3150
|
+
const atlasDocument = document2.version === "2.0" || document2.version === "2.1" || document2.version === "2.5" ? document2 : document2.version === "2.0-draft" ? {
|
|
2983
3151
|
...document2,
|
|
2984
3152
|
version: "2.0",
|
|
2985
3153
|
schemaVersion: "2.0"
|
|
2986
3154
|
} : upgradeDocumentToV2(document2);
|
|
2987
|
-
if (schema === "2.1" && atlasDocument.version !==
|
|
3155
|
+
if ((schema === "2.0" || schema === "2.1" || schema === "2.5") && atlasDocument.version !== schema) {
|
|
2988
3156
|
return formatAtlasDocument({
|
|
2989
3157
|
...atlasDocument,
|
|
2990
|
-
version:
|
|
2991
|
-
schemaVersion:
|
|
3158
|
+
version: schema,
|
|
3159
|
+
schemaVersion: schema
|
|
2992
3160
|
});
|
|
2993
3161
|
}
|
|
2994
3162
|
return formatAtlasDocument(atlasDocument);
|
|
@@ -3265,6 +3433,21 @@
|
|
|
3265
3433
|
if (viewpoint.rotationDeg !== 0) {
|
|
3266
3434
|
lines.push(` rotation ${viewpoint.rotationDeg}`);
|
|
3267
3435
|
}
|
|
3436
|
+
if (viewpoint.camera && hasCameraValues(viewpoint.camera)) {
|
|
3437
|
+
lines.push(" camera");
|
|
3438
|
+
if (viewpoint.camera.azimuth !== null) {
|
|
3439
|
+
lines.push(` azimuth ${viewpoint.camera.azimuth}`);
|
|
3440
|
+
}
|
|
3441
|
+
if (viewpoint.camera.elevation !== null) {
|
|
3442
|
+
lines.push(` elevation ${viewpoint.camera.elevation}`);
|
|
3443
|
+
}
|
|
3444
|
+
if (viewpoint.camera.roll !== null) {
|
|
3445
|
+
lines.push(` roll ${viewpoint.camera.roll}`);
|
|
3446
|
+
}
|
|
3447
|
+
if (viewpoint.camera.distance !== null) {
|
|
3448
|
+
lines.push(` distance ${viewpoint.camera.distance}`);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3268
3451
|
const layerTokens = formatDraftLayers(viewpoint.layers);
|
|
3269
3452
|
if (layerTokens.length > 0) {
|
|
3270
3453
|
lines.push(` layers ${layerTokens.join(" ")}`);
|
|
@@ -3364,6 +3547,12 @@
|
|
|
3364
3547
|
if (event.visibility) {
|
|
3365
3548
|
lines.push(` visibility ${quoteIfNeeded(event.visibility)}`);
|
|
3366
3549
|
}
|
|
3550
|
+
if (event.epoch) {
|
|
3551
|
+
lines.push(` epoch ${quoteIfNeeded(event.epoch)}`);
|
|
3552
|
+
}
|
|
3553
|
+
if (event.referencePlane) {
|
|
3554
|
+
lines.push(` referencePlane ${quoteIfNeeded(event.referencePlane)}`);
|
|
3555
|
+
}
|
|
3367
3556
|
if (event.tags.length > 0) {
|
|
3368
3557
|
lines.push(` tags ${event.tags.map(quoteIfNeeded).join(" ")}`);
|
|
3369
3558
|
}
|
|
@@ -3388,10 +3577,15 @@
|
|
|
3388
3577
|
function formatEventPoseFields(pose) {
|
|
3389
3578
|
return [
|
|
3390
3579
|
...formatPlacement(pose.placement),
|
|
3580
|
+
...pose.epoch ? [`epoch ${quoteIfNeeded(pose.epoch)}`] : [],
|
|
3581
|
+
...pose.referencePlane ? [`referencePlane ${quoteIfNeeded(pose.referencePlane)}`] : [],
|
|
3391
3582
|
...formatOptionalUnit("inner", pose.inner),
|
|
3392
3583
|
...formatOptionalUnit("outer", pose.outer)
|
|
3393
3584
|
];
|
|
3394
3585
|
}
|
|
3586
|
+
function hasCameraValues(camera) {
|
|
3587
|
+
return camera.azimuth !== null || camera.elevation !== null || camera.roll !== null || camera.distance !== null;
|
|
3588
|
+
}
|
|
3395
3589
|
function formatValue(value) {
|
|
3396
3590
|
if (Array.isArray(value)) {
|
|
3397
3591
|
return value.map((item) => quoteIfNeeded(item)).join(" ");
|
|
@@ -3685,13 +3879,13 @@
|
|
|
3685
3879
|
validateRelation(relation, objectMap, diagnostics);
|
|
3686
3880
|
}
|
|
3687
3881
|
for (const viewpoint of document2.system?.viewpoints ?? []) {
|
|
3688
|
-
validateViewpoint(viewpoint
|
|
3882
|
+
validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, diagnostics, objectMap);
|
|
3689
3883
|
}
|
|
3690
3884
|
for (const object of document2.objects) {
|
|
3691
3885
|
validateObject(object, document2.system, objectMap, groupIds, diagnostics);
|
|
3692
3886
|
}
|
|
3693
3887
|
for (const event of document2.events) {
|
|
3694
|
-
validateEvent(event, objectMap, diagnostics);
|
|
3888
|
+
validateEvent(event, document2.system, objectMap, diagnostics);
|
|
3695
3889
|
}
|
|
3696
3890
|
return diagnostics;
|
|
3697
3891
|
}
|
|
@@ -3710,21 +3904,24 @@
|
|
|
3710
3904
|
diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
|
|
3711
3905
|
}
|
|
3712
3906
|
}
|
|
3713
|
-
function validateViewpoint(
|
|
3714
|
-
|
|
3907
|
+
function validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, diagnostics, objectMap) {
|
|
3908
|
+
const filter = viewpoint.filter;
|
|
3909
|
+
if (sourceSchemaVersion === "2.1" || sourceSchemaVersion === "2.5") {
|
|
3715
3910
|
if (filter) {
|
|
3716
3911
|
for (const groupId of filter.groupIds) {
|
|
3717
3912
|
if (!groupIds.has(groupId)) {
|
|
3718
|
-
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${
|
|
3913
|
+
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpoint.id}".`, void 0, `viewpoint.${viewpoint.id}.groups`));
|
|
3719
3914
|
}
|
|
3720
3915
|
}
|
|
3721
3916
|
}
|
|
3722
|
-
for (const eventId of
|
|
3917
|
+
for (const eventId of viewpoint.events ?? []) {
|
|
3723
3918
|
if (!eventIds.has(eventId)) {
|
|
3724
|
-
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${
|
|
3919
|
+
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${viewpoint.id}".`, void 0, `viewpoint.${viewpoint.id}.events`));
|
|
3725
3920
|
}
|
|
3726
3921
|
}
|
|
3727
3922
|
}
|
|
3923
|
+
validateProjection(viewpoint.projection, diagnostics, `viewpoint.${viewpoint.id}.projection`, viewpoint.id);
|
|
3924
|
+
validateCamera(viewpoint.camera, viewpoint.projection, viewpoint.rotationDeg, diagnostics, viewpoint.id, viewpoint.focusObjectId, viewpoint.selectedObjectId, filter, objectMap);
|
|
3728
3925
|
}
|
|
3729
3926
|
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
3730
3927
|
const placement = object.placement;
|
|
@@ -3737,6 +3934,12 @@
|
|
|
3737
3934
|
}
|
|
3738
3935
|
}
|
|
3739
3936
|
}
|
|
3937
|
+
if (typeof object.epoch === "string" && !object.epoch.trim()) {
|
|
3938
|
+
diagnostics.push(warn("validate.epoch.empty", `Object "${object.id}" defines an empty epoch string.`, object.id, "epoch"));
|
|
3939
|
+
}
|
|
3940
|
+
if (typeof object.referencePlane === "string" && !object.referencePlane.trim()) {
|
|
3941
|
+
diagnostics.push(warn("validate.referencePlane.empty", `Object "${object.id}" defines an empty reference plane string.`, object.id, "referencePlane"));
|
|
3942
|
+
}
|
|
3740
3943
|
if (orbitPlacement) {
|
|
3741
3944
|
if (!objectMap.has(orbitPlacement.target)) {
|
|
3742
3945
|
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
@@ -3808,12 +4011,18 @@
|
|
|
3808
4011
|
}
|
|
3809
4012
|
}
|
|
3810
4013
|
}
|
|
3811
|
-
function validateEvent(event, objectMap, diagnostics) {
|
|
4014
|
+
function validateEvent(event, system, objectMap, diagnostics) {
|
|
3812
4015
|
const fieldPrefix = `event.${event.id}`;
|
|
3813
4016
|
const referencedIds = /* @__PURE__ */ new Set();
|
|
3814
4017
|
if (!event.kind.trim()) {
|
|
3815
4018
|
diagnostics.push(error("validate.event.kind.required", `Event "${event.id}" is missing a "kind" value.`, void 0, `${fieldPrefix}.kind`));
|
|
3816
4019
|
}
|
|
4020
|
+
if (typeof event.epoch === "string" && !event.epoch.trim()) {
|
|
4021
|
+
diagnostics.push(warn("validate.event.epoch.empty", `Event "${event.id}" defines an empty epoch string.`, void 0, `${fieldPrefix}.epoch`));
|
|
4022
|
+
}
|
|
4023
|
+
if (typeof event.referencePlane === "string" && !event.referencePlane.trim()) {
|
|
4024
|
+
diagnostics.push(warn("validate.event.referencePlane.empty", `Event "${event.id}" defines an empty reference plane string.`, void 0, `${fieldPrefix}.referencePlane`));
|
|
4025
|
+
}
|
|
3817
4026
|
if (!event.targetObjectId && event.participantObjectIds.length === 0) {
|
|
3818
4027
|
diagnostics.push(error("validate.event.references.required", `Event "${event.id}" must define a "target" or at least one participant.`, void 0, `${fieldPrefix}.participants`));
|
|
3819
4028
|
}
|
|
@@ -3860,10 +4069,14 @@
|
|
|
3860
4069
|
if (!referencedIds.has(pose.objectId)) {
|
|
3861
4070
|
diagnostics.push(warn("validate.event.pose.unreferenced", `Event pose "${pose.objectId}" on "${event.id}" is not listed in target/participants.`, void 0, poseFieldPrefix));
|
|
3862
4071
|
}
|
|
3863
|
-
validateEventPose(pose, object, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
4072
|
+
validateEventPose(pose, object, event, system, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
4073
|
+
}
|
|
4074
|
+
const missingPoseIds = [...referencedIds].filter((objectId) => !poseIds.has(objectId));
|
|
4075
|
+
if (event.positions.length > 0 && missingPoseIds.length > 0) {
|
|
4076
|
+
diagnostics.push(warn("validate.event.positions.partial", `Event "${event.id}" leaves ${missingPoseIds.length} referenced object(s) on their base placement.`, void 0, `${fieldPrefix}.positions`));
|
|
3864
4077
|
}
|
|
3865
4078
|
}
|
|
3866
|
-
function validateEventPose(pose, object, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
4079
|
+
function validateEventPose(pose, object, event, system, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
3867
4080
|
const placement = pose.placement;
|
|
3868
4081
|
if (!placement) {
|
|
3869
4082
|
diagnostics.push(error("validate.event.pose.placement.required", `Event "${eventId}" pose "${pose.objectId}" is missing a placement mode.`, void 0, fieldPrefix));
|
|
@@ -3876,6 +4089,15 @@
|
|
|
3876
4089
|
if (placement.distance && placement.semiMajor) {
|
|
3877
4090
|
diagnostics.push(error("validate.event.pose.orbit.distanceConflict", `Event "${eventId}" pose "${pose.objectId}" cannot declare both "distance" and "semiMajor".`, void 0, `${fieldPrefix}.distance`));
|
|
3878
4091
|
}
|
|
4092
|
+
if (placement.phase && !resolveEffectiveEpoch(system, object, event, pose)) {
|
|
4093
|
+
diagnostics.push(warn("validate.event.pose.phase.epochMissing", `Event "${eventId}" pose "${pose.objectId}" sets "phase" without an effective epoch.`, void 0, `${fieldPrefix}.phase`));
|
|
4094
|
+
}
|
|
4095
|
+
if (placement.inclination && !resolveEffectiveReferencePlane(system, object, event, pose)) {
|
|
4096
|
+
diagnostics.push(warn("validate.event.pose.inclination.referencePlaneMissing", `Event "${eventId}" pose "${pose.objectId}" sets "inclination" without an effective reference plane.`, void 0, `${fieldPrefix}.inclination`));
|
|
4097
|
+
}
|
|
4098
|
+
if (placement.period && !massInSolar(objectMap.get(placement.target)?.properties.mass)) {
|
|
4099
|
+
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`));
|
|
4100
|
+
}
|
|
3879
4101
|
return;
|
|
3880
4102
|
}
|
|
3881
4103
|
if (placement.mode === "surface") {
|
|
@@ -4010,6 +4232,52 @@
|
|
|
4010
4232
|
return null;
|
|
4011
4233
|
}
|
|
4012
4234
|
}
|
|
4235
|
+
function validateProjection(projection, diagnostics, field, viewpointId) {
|
|
4236
|
+
if (projection !== "topdown" && projection !== "isometric" && projection !== "orthographic" && projection !== "perspective") {
|
|
4237
|
+
diagnostics.push(error("validate.viewpoint.projection.invalid", `Unknown projection "${String(projection)}" in viewpoint "${viewpointId}".`, void 0, field));
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4240
|
+
function validateCamera(camera, projection, rotationDeg, diagnostics, viewpointId, focusObjectId, selectedObjectId, filter, objectMap) {
|
|
4241
|
+
if (!camera) {
|
|
4242
|
+
return;
|
|
4243
|
+
}
|
|
4244
|
+
const prefix = `viewpoint.${viewpointId}.camera`;
|
|
4245
|
+
for (const [key, value] of [
|
|
4246
|
+
["azimuth", camera.azimuth],
|
|
4247
|
+
["elevation", camera.elevation],
|
|
4248
|
+
["roll", camera.roll],
|
|
4249
|
+
["distance", camera.distance]
|
|
4250
|
+
]) {
|
|
4251
|
+
if (value !== null && (!Number.isFinite(value) || key === "distance" && value <= 0)) {
|
|
4252
|
+
diagnostics.push(error("validate.viewpoint.camera.invalid", `Invalid camera ${key} "${String(value)}" in viewpoint "${viewpointId}".`, void 0, `${prefix}.${key}`));
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
if (camera.distance !== null && projection !== "perspective") {
|
|
4256
|
+
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`));
|
|
4257
|
+
}
|
|
4258
|
+
if (projection === "topdown" && (camera.elevation !== null || camera.roll !== null)) {
|
|
4259
|
+
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));
|
|
4260
|
+
}
|
|
4261
|
+
if (projection === "isometric" && camera.elevation !== null) {
|
|
4262
|
+
diagnostics.push(info("validate.viewpoint.camera.isometricStored", `Camera elevation on isometric viewpoint "${viewpointId}" is preserved semantically for future 3D rendering.`, void 0, `${prefix}.elevation`));
|
|
4263
|
+
}
|
|
4264
|
+
if (camera.azimuth !== null && camera.azimuth !== 0 && rotationDeg !== 0) {
|
|
4265
|
+
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`));
|
|
4266
|
+
}
|
|
4267
|
+
const hasAnchor = focusObjectId !== null && objectMap.has(focusObjectId) || selectedObjectId !== null && objectMap.has(selectedObjectId) || !!filter;
|
|
4268
|
+
if (!hasAnchor) {
|
|
4269
|
+
diagnostics.push(info("validate.viewpoint.camera.anchorMissing", `Viewpoint "${viewpointId}" stores camera settings without a focus object, selection, or filter anchor.`, void 0, prefix));
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
function resolveEffectiveEpoch(system, object, event, pose) {
|
|
4273
|
+
return normalizeOptionalContextString(pose?.epoch) ?? normalizeOptionalContextString(event?.epoch) ?? normalizeOptionalContextString(object.epoch) ?? normalizeOptionalContextString(system?.epoch) ?? null;
|
|
4274
|
+
}
|
|
4275
|
+
function resolveEffectiveReferencePlane(system, object, event, pose) {
|
|
4276
|
+
return normalizeOptionalContextString(pose?.referencePlane) ?? normalizeOptionalContextString(event?.referencePlane) ?? normalizeOptionalContextString(object.referencePlane) ?? normalizeOptionalContextString(system?.referencePlane) ?? null;
|
|
4277
|
+
}
|
|
4278
|
+
function normalizeOptionalContextString(value) {
|
|
4279
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
4280
|
+
}
|
|
4013
4281
|
function toleranceForField(object, field) {
|
|
4014
4282
|
const tolerance = object.tolerances?.find((entry) => entry.field === field)?.value;
|
|
4015
4283
|
if (typeof tolerance === "number") {
|
|
@@ -4118,7 +4386,9 @@
|
|
|
4118
4386
|
"surface",
|
|
4119
4387
|
"free",
|
|
4120
4388
|
"inner",
|
|
4121
|
-
"outer"
|
|
4389
|
+
"outer",
|
|
4390
|
+
"epoch",
|
|
4391
|
+
"referencePlane"
|
|
4122
4392
|
]);
|
|
4123
4393
|
function parseWorldOrbitAtlas(source) {
|
|
4124
4394
|
return parseAtlasSource(source);
|
|
@@ -4160,7 +4430,7 @@
|
|
|
4160
4430
|
if (!sawSchemaHeader) {
|
|
4161
4431
|
sourceSchemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
4162
4432
|
sawSchemaHeader = true;
|
|
4163
|
-
if (prepared.comments.length > 0 && sourceSchemaVersion
|
|
4433
|
+
if (prepared.comments.length > 0 && isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
4164
4434
|
diagnostics.push({
|
|
4165
4435
|
code: "parse.schema21.commentCompatibility",
|
|
4166
4436
|
severity: "warning",
|
|
@@ -4230,11 +4500,11 @@
|
|
|
4230
4500
|
return document2;
|
|
4231
4501
|
}
|
|
4232
4502
|
function assertDraftSchemaHeader(tokens, line) {
|
|
4233
|
-
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1"].includes(tokens[1].value.toLowerCase())) {
|
|
4234
|
-
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
4503
|
+
if (tokens.length !== 2 || tokens[0].value.toLowerCase() !== "schema" || !["2.0-draft", "2.0", "2.1", "2.5"].includes(tokens[1].value.toLowerCase())) {
|
|
4504
|
+
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);
|
|
4235
4505
|
}
|
|
4236
4506
|
const version = tokens[1].value.toLowerCase();
|
|
4237
|
-
return version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
4507
|
+
return version === "2.5" ? "2.5" : version === "2.1" ? "2.1" : version === "2.0-draft" ? "2.0-draft" : "2.0";
|
|
4238
4508
|
}
|
|
4239
4509
|
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, flags) {
|
|
4240
4510
|
const keyword = tokens[0]?.value.toLowerCase();
|
|
@@ -4254,6 +4524,8 @@
|
|
|
4254
4524
|
return {
|
|
4255
4525
|
kind: "defaults",
|
|
4256
4526
|
system,
|
|
4527
|
+
sourceSchemaVersion,
|
|
4528
|
+
diagnostics,
|
|
4257
4529
|
seenFields: /* @__PURE__ */ new Set()
|
|
4258
4530
|
};
|
|
4259
4531
|
case "atlas":
|
|
@@ -4346,6 +4618,7 @@
|
|
|
4346
4618
|
preset: system.defaults.preset,
|
|
4347
4619
|
zoom: null,
|
|
4348
4620
|
rotationDeg: 0,
|
|
4621
|
+
camera: null,
|
|
4349
4622
|
layers: {},
|
|
4350
4623
|
filter: null
|
|
4351
4624
|
};
|
|
@@ -4359,7 +4632,10 @@
|
|
|
4359
4632
|
seenFields: /* @__PURE__ */ new Set(),
|
|
4360
4633
|
inFilter: false,
|
|
4361
4634
|
filterIndent: null,
|
|
4362
|
-
seenFilterFields: /* @__PURE__ */ new Set()
|
|
4635
|
+
seenFilterFields: /* @__PURE__ */ new Set(),
|
|
4636
|
+
inCamera: false,
|
|
4637
|
+
cameraIndent: null,
|
|
4638
|
+
seenCameraFields: /* @__PURE__ */ new Set()
|
|
4363
4639
|
};
|
|
4364
4640
|
}
|
|
4365
4641
|
function startAnnotationSection(tokens, line, system, annotationIds) {
|
|
@@ -4466,6 +4742,8 @@
|
|
|
4466
4742
|
participantObjectIds: [],
|
|
4467
4743
|
timing: null,
|
|
4468
4744
|
visibility: null,
|
|
4745
|
+
epoch: null,
|
|
4746
|
+
referencePlane: null,
|
|
4469
4747
|
tags: [],
|
|
4470
4748
|
color: null,
|
|
4471
4749
|
hidden: false,
|
|
@@ -4590,6 +4868,12 @@
|
|
|
4590
4868
|
const value = joinFieldValue(tokens, line);
|
|
4591
4869
|
switch (key) {
|
|
4592
4870
|
case "view":
|
|
4871
|
+
if (isSchema25Projection(value)) {
|
|
4872
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "defaults.view", {
|
|
4873
|
+
line,
|
|
4874
|
+
column: tokens[0].column
|
|
4875
|
+
});
|
|
4876
|
+
}
|
|
4593
4877
|
section.system.defaults.view = parseProjectionValue(value, line, tokens[0].column);
|
|
4594
4878
|
return;
|
|
4595
4879
|
case "scale":
|
|
@@ -4629,14 +4913,36 @@
|
|
|
4629
4913
|
throw new WorldOrbitError(`Unknown atlas field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4630
4914
|
}
|
|
4631
4915
|
function applyViewpointField2(section, indent, tokens, line) {
|
|
4916
|
+
if (section.inCamera && indent <= (section.cameraIndent ?? 0)) {
|
|
4917
|
+
section.inCamera = false;
|
|
4918
|
+
section.cameraIndent = null;
|
|
4919
|
+
}
|
|
4632
4920
|
if (section.inFilter && indent <= (section.filterIndent ?? 0)) {
|
|
4633
4921
|
section.inFilter = false;
|
|
4634
4922
|
section.filterIndent = null;
|
|
4635
4923
|
}
|
|
4924
|
+
if (section.inCamera) {
|
|
4925
|
+
applyViewpointCameraField(section, tokens, line);
|
|
4926
|
+
return;
|
|
4927
|
+
}
|
|
4636
4928
|
if (section.inFilter) {
|
|
4637
4929
|
applyViewpointFilterField(section, tokens, line);
|
|
4638
4930
|
return;
|
|
4639
4931
|
}
|
|
4932
|
+
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "camera") {
|
|
4933
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.camera", {
|
|
4934
|
+
line,
|
|
4935
|
+
column: tokens[0].column
|
|
4936
|
+
});
|
|
4937
|
+
if (section.seenFields.has("camera")) {
|
|
4938
|
+
throw new WorldOrbitError('Duplicate viewpoint field "camera"', line, tokens[0].column);
|
|
4939
|
+
}
|
|
4940
|
+
section.seenFields.add("camera");
|
|
4941
|
+
section.inCamera = true;
|
|
4942
|
+
section.cameraIndent = indent;
|
|
4943
|
+
section.viewpoint.camera = section.viewpoint.camera ?? createEmptyViewCamera2();
|
|
4944
|
+
return;
|
|
4945
|
+
}
|
|
4640
4946
|
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "filter") {
|
|
4641
4947
|
if (section.seenFields.has("filter")) {
|
|
4642
4948
|
throw new WorldOrbitError('Duplicate viewpoint field "filter"', line, tokens[0].column);
|
|
@@ -4662,6 +4968,12 @@
|
|
|
4662
4968
|
section.viewpoint.selectedObjectId = value;
|
|
4663
4969
|
return;
|
|
4664
4970
|
case "projection":
|
|
4971
|
+
if (isSchema25Projection(value)) {
|
|
4972
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "projection", {
|
|
4973
|
+
line,
|
|
4974
|
+
column: tokens[0].column
|
|
4975
|
+
});
|
|
4976
|
+
}
|
|
4665
4977
|
section.viewpoint.projection = parseProjectionValue(value, line, tokens[0].column);
|
|
4666
4978
|
return;
|
|
4667
4979
|
case "preset":
|
|
@@ -4673,6 +4985,13 @@
|
|
|
4673
4985
|
case "rotation":
|
|
4674
4986
|
section.viewpoint.rotationDeg = parseFiniteNumber2(value, line, tokens[0].column, "rotation");
|
|
4675
4987
|
return;
|
|
4988
|
+
case "camera":
|
|
4989
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.camera", {
|
|
4990
|
+
line,
|
|
4991
|
+
column: tokens[0].column
|
|
4992
|
+
});
|
|
4993
|
+
section.viewpoint.camera = parseInlineViewCamera(tokens.slice(1), line, section.viewpoint.camera);
|
|
4994
|
+
return;
|
|
4676
4995
|
case "layers":
|
|
4677
4996
|
section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line, section.sourceSchemaVersion, section.diagnostics);
|
|
4678
4997
|
return;
|
|
@@ -4687,6 +5006,28 @@
|
|
|
4687
5006
|
throw new WorldOrbitError(`Unknown viewpoint field "${tokens[0].value}"`, line, tokens[0].column);
|
|
4688
5007
|
}
|
|
4689
5008
|
}
|
|
5009
|
+
function applyViewpointCameraField(section, tokens, line) {
|
|
5010
|
+
const key = requireUniqueField(tokens, section.seenCameraFields, line);
|
|
5011
|
+
const value = joinFieldValue(tokens, line);
|
|
5012
|
+
const camera = section.viewpoint.camera ?? createEmptyViewCamera2();
|
|
5013
|
+
switch (key) {
|
|
5014
|
+
case "azimuth":
|
|
5015
|
+
camera.azimuth = parseFiniteNumber2(value, line, tokens[0].column, "camera.azimuth");
|
|
5016
|
+
break;
|
|
5017
|
+
case "elevation":
|
|
5018
|
+
camera.elevation = parseFiniteNumber2(value, line, tokens[0].column, "camera.elevation");
|
|
5019
|
+
break;
|
|
5020
|
+
case "roll":
|
|
5021
|
+
camera.roll = parseFiniteNumber2(value, line, tokens[0].column, "camera.roll");
|
|
5022
|
+
break;
|
|
5023
|
+
case "distance":
|
|
5024
|
+
camera.distance = parsePositiveNumber2(value, line, tokens[0].column, "camera.distance");
|
|
5025
|
+
break;
|
|
5026
|
+
default:
|
|
5027
|
+
throw new WorldOrbitError(`Unknown viewpoint camera field "${tokens[0].value}"`, line, tokens[0].column);
|
|
5028
|
+
}
|
|
5029
|
+
section.viewpoint.camera = camera;
|
|
5030
|
+
}
|
|
4690
5031
|
function applyViewpointFilterField(section, tokens, line) {
|
|
4691
5032
|
const key = requireUniqueField(tokens, section.seenFilterFields, line);
|
|
4692
5033
|
const filter = section.viewpoint.filter ?? createEmptyViewpointFilter2();
|
|
@@ -4797,6 +5138,12 @@
|
|
|
4797
5138
|
section.positionsIndent = null;
|
|
4798
5139
|
}
|
|
4799
5140
|
if (section.activePose) {
|
|
5141
|
+
if (tokens[0]?.value === "epoch" || tokens[0]?.value === "referencePlane") {
|
|
5142
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, `pose.${tokens[0].value}`, {
|
|
5143
|
+
line,
|
|
5144
|
+
column: tokens[0]?.column ?? 1
|
|
5145
|
+
});
|
|
5146
|
+
}
|
|
4800
5147
|
section.activePose.fields.push(parseEventPoseField(tokens, line, section.activePoseSeenFields));
|
|
4801
5148
|
return;
|
|
4802
5149
|
}
|
|
@@ -4851,6 +5198,20 @@
|
|
|
4851
5198
|
case "visibility":
|
|
4852
5199
|
section.event.visibility = joinFieldValue(tokens, line);
|
|
4853
5200
|
return;
|
|
5201
|
+
case "epoch":
|
|
5202
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "event.epoch", {
|
|
5203
|
+
line,
|
|
5204
|
+
column: tokens[0].column
|
|
5205
|
+
});
|
|
5206
|
+
section.event.epoch = joinFieldValue(tokens, line);
|
|
5207
|
+
return;
|
|
5208
|
+
case "referenceplane":
|
|
5209
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "event.referencePlane", {
|
|
5210
|
+
line,
|
|
5211
|
+
column: tokens[0].column
|
|
5212
|
+
});
|
|
5213
|
+
section.event.referencePlane = joinFieldValue(tokens, line);
|
|
5214
|
+
return;
|
|
4854
5215
|
case "tags":
|
|
4855
5216
|
section.event.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
4856
5217
|
return;
|
|
@@ -4978,11 +5339,15 @@
|
|
|
4978
5339
|
}
|
|
4979
5340
|
function parseProjectionValue(value, line, column) {
|
|
4980
5341
|
const normalized = value.toLowerCase();
|
|
4981
|
-
if (normalized !== "topdown" && normalized !== "isometric") {
|
|
5342
|
+
if (normalized !== "topdown" && normalized !== "isometric" && normalized !== "orthographic" && normalized !== "perspective") {
|
|
4982
5343
|
throw new WorldOrbitError(`Unknown projection "${value}"`, line, column);
|
|
4983
5344
|
}
|
|
4984
5345
|
return normalized;
|
|
4985
5346
|
}
|
|
5347
|
+
function isSchema25Projection(value) {
|
|
5348
|
+
const normalized = value.toLowerCase();
|
|
5349
|
+
return normalized === "orthographic" || normalized === "perspective";
|
|
5350
|
+
}
|
|
4986
5351
|
function parsePresetValue(value, line, column) {
|
|
4987
5352
|
const normalized = value.toLowerCase();
|
|
4988
5353
|
if (normalized === "diagram" || normalized === "presentation" || normalized === "atlas-card" || normalized === "markdown") {
|
|
@@ -5012,6 +5377,48 @@
|
|
|
5012
5377
|
groupIds: []
|
|
5013
5378
|
};
|
|
5014
5379
|
}
|
|
5380
|
+
function createEmptyViewCamera2() {
|
|
5381
|
+
return {
|
|
5382
|
+
azimuth: null,
|
|
5383
|
+
elevation: null,
|
|
5384
|
+
roll: null,
|
|
5385
|
+
distance: null
|
|
5386
|
+
};
|
|
5387
|
+
}
|
|
5388
|
+
function parseInlineViewCamera(tokens, line, current) {
|
|
5389
|
+
if (tokens.length === 0 || tokens.length % 2 !== 0) {
|
|
5390
|
+
throw new WorldOrbitError('Field "camera" expects "<field> <value>" pairs', line, tokens[0]?.column ?? 1);
|
|
5391
|
+
}
|
|
5392
|
+
const camera = current ? { ...current } : createEmptyViewCamera2();
|
|
5393
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5394
|
+
for (let index = 0; index < tokens.length; index += 2) {
|
|
5395
|
+
const fieldToken = tokens[index];
|
|
5396
|
+
const valueToken = tokens[index + 1];
|
|
5397
|
+
const key = fieldToken.value.toLowerCase();
|
|
5398
|
+
if (seen.has(key)) {
|
|
5399
|
+
throw new WorldOrbitError(`Duplicate viewpoint camera field "${fieldToken.value}"`, line, fieldToken.column);
|
|
5400
|
+
}
|
|
5401
|
+
seen.add(key);
|
|
5402
|
+
const value = valueToken.value;
|
|
5403
|
+
switch (key) {
|
|
5404
|
+
case "azimuth":
|
|
5405
|
+
camera.azimuth = parseFiniteNumber2(value, line, fieldToken.column, "camera.azimuth");
|
|
5406
|
+
break;
|
|
5407
|
+
case "elevation":
|
|
5408
|
+
camera.elevation = parseFiniteNumber2(value, line, fieldToken.column, "camera.elevation");
|
|
5409
|
+
break;
|
|
5410
|
+
case "roll":
|
|
5411
|
+
camera.roll = parseFiniteNumber2(value, line, fieldToken.column, "camera.roll");
|
|
5412
|
+
break;
|
|
5413
|
+
case "distance":
|
|
5414
|
+
camera.distance = parsePositiveNumber2(value, line, fieldToken.column, "camera.distance");
|
|
5415
|
+
break;
|
|
5416
|
+
default:
|
|
5417
|
+
throw new WorldOrbitError(`Unknown viewpoint camera field "${fieldToken.value}"`, line, fieldToken.column);
|
|
5418
|
+
}
|
|
5419
|
+
}
|
|
5420
|
+
return camera;
|
|
5421
|
+
}
|
|
5015
5422
|
function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
5016
5423
|
const fields = [];
|
|
5017
5424
|
let index = 0;
|
|
@@ -5144,7 +5551,7 @@
|
|
|
5144
5551
|
object.tolerances = tolerances;
|
|
5145
5552
|
if (typedBlocks && Object.keys(typedBlocks).length > 0)
|
|
5146
5553
|
object.typedBlocks = typedBlocks;
|
|
5147
|
-
if (sourceSchemaVersion
|
|
5554
|
+
if (isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
5148
5555
|
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) {
|
|
5149
5556
|
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, node.id, node.location);
|
|
5150
5557
|
}
|
|
@@ -5160,23 +5567,25 @@
|
|
|
5160
5567
|
};
|
|
5161
5568
|
}
|
|
5162
5569
|
function normalizeDraftEventPose(rawPose) {
|
|
5163
|
-
const fieldMap = collectDraftFields(rawPose.fields);
|
|
5570
|
+
const fieldMap = collectDraftFields(rawPose.fields, "event-pose");
|
|
5164
5571
|
const placement = extractPlacementFromFieldMap(fieldMap);
|
|
5165
5572
|
return {
|
|
5166
5573
|
objectId: rawPose.objectId,
|
|
5167
5574
|
placement,
|
|
5168
5575
|
inner: parseOptionalUnitField(fieldMap.get("inner")?.[0], "inner"),
|
|
5169
|
-
outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer")
|
|
5576
|
+
outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer"),
|
|
5577
|
+
epoch: parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]),
|
|
5578
|
+
referencePlane: parseOptionalJoinedValue(fieldMap.get("referencePlane")?.[0])
|
|
5170
5579
|
};
|
|
5171
5580
|
}
|
|
5172
|
-
function collectDraftFields(fields) {
|
|
5581
|
+
function collectDraftFields(fields, _mode = "object") {
|
|
5173
5582
|
const grouped = /* @__PURE__ */ new Map();
|
|
5174
5583
|
for (const field of fields) {
|
|
5175
5584
|
const spec = getDraftObjectFieldSpec(field.key);
|
|
5176
|
-
if (!spec) {
|
|
5585
|
+
if (!spec && !EVENT_POSE_FIELD_KEYS.has(field.key)) {
|
|
5177
5586
|
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
5178
5587
|
}
|
|
5179
|
-
if (!spec
|
|
5588
|
+
if (!spec?.allowRepeat && grouped.has(field.key)) {
|
|
5180
5589
|
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
5181
5590
|
}
|
|
5182
5591
|
const existing = grouped.get(field.key) ?? [];
|
|
@@ -5353,7 +5762,7 @@
|
|
|
5353
5762
|
}
|
|
5354
5763
|
}
|
|
5355
5764
|
function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
5356
|
-
if (sourceSchemaVersion
|
|
5765
|
+
if (!isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
5357
5766
|
return;
|
|
5358
5767
|
}
|
|
5359
5768
|
diagnostics.push({
|
|
@@ -5365,6 +5774,34 @@
|
|
|
5365
5774
|
column: location.column
|
|
5366
5775
|
});
|
|
5367
5776
|
}
|
|
5777
|
+
function warnIfSchema25Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
5778
|
+
if (!isSchemaOlderThan(sourceSchemaVersion, "2.5")) {
|
|
5779
|
+
return;
|
|
5780
|
+
}
|
|
5781
|
+
diagnostics.push({
|
|
5782
|
+
code: "parse.schema25.featureCompatibility",
|
|
5783
|
+
severity: "warning",
|
|
5784
|
+
source: "parse",
|
|
5785
|
+
message: `Feature "${featureName}" requires schema 2.5; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
5786
|
+
line: location.line,
|
|
5787
|
+
column: location.column
|
|
5788
|
+
});
|
|
5789
|
+
}
|
|
5790
|
+
function isSchemaOlderThan(sourceSchemaVersion, requiredVersion) {
|
|
5791
|
+
return schemaVersionRank(sourceSchemaVersion) < schemaVersionRank(requiredVersion);
|
|
5792
|
+
}
|
|
5793
|
+
function schemaVersionRank(version) {
|
|
5794
|
+
switch (version) {
|
|
5795
|
+
case "2.0-draft":
|
|
5796
|
+
return 0;
|
|
5797
|
+
case "2.0":
|
|
5798
|
+
return 1;
|
|
5799
|
+
case "2.1":
|
|
5800
|
+
return 2;
|
|
5801
|
+
case "2.5":
|
|
5802
|
+
return 3;
|
|
5803
|
+
}
|
|
5804
|
+
}
|
|
5368
5805
|
function preprocessAtlasSource(source) {
|
|
5369
5806
|
const chars = [...source];
|
|
5370
5807
|
const comments = [];
|
|
@@ -5452,7 +5889,7 @@
|
|
|
5452
5889
|
}
|
|
5453
5890
|
|
|
5454
5891
|
// packages/core/dist/atlas-edit.js
|
|
5455
|
-
function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "2.
|
|
5892
|
+
function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "2.5") {
|
|
5456
5893
|
return {
|
|
5457
5894
|
format: "worldorbit",
|
|
5458
5895
|
version,
|
|
@@ -5671,8 +6108,9 @@
|
|
|
5671
6108
|
}
|
|
5672
6109
|
|
|
5673
6110
|
// packages/core/dist/load.js
|
|
5674
|
-
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;
|
|
6111
|
+
var ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1|\.5)?$/i;
|
|
5675
6112
|
var ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
|
|
6113
|
+
var ATLAS_SCHEMA_25_PATTERN = /^schema\s+2\.5$/i;
|
|
5676
6114
|
var LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
|
|
5677
6115
|
function detectWorldOrbitSchemaVersion(source) {
|
|
5678
6116
|
for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
|
|
@@ -5686,6 +6124,9 @@
|
|
|
5686
6124
|
if (ATLAS_SCHEMA_21_PATTERN.test(trimmed)) {
|
|
5687
6125
|
return "2.1";
|
|
5688
6126
|
}
|
|
6127
|
+
if (ATLAS_SCHEMA_25_PATTERN.test(trimmed)) {
|
|
6128
|
+
return "2.5";
|
|
6129
|
+
}
|
|
5689
6130
|
if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
|
|
5690
6131
|
return "2.0";
|
|
5691
6132
|
}
|
|
@@ -5746,7 +6187,7 @@
|
|
|
5746
6187
|
}
|
|
5747
6188
|
function loadWorldOrbitSourceWithDiagnostics(source) {
|
|
5748
6189
|
const schemaVersion = detectWorldOrbitSchemaVersion(source);
|
|
5749
|
-
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1") {
|
|
6190
|
+
if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1" || schemaVersion === "2.5") {
|
|
5750
6191
|
return loadAtlasSourceWithDiagnostics(source, schemaVersion);
|
|
5751
6192
|
}
|
|
5752
6193
|
let ast;
|
|
@@ -5987,13 +6428,14 @@
|
|
|
5987
6428
|
}
|
|
5988
6429
|
function createAtlasStateSnapshot(viewerState, renderOptions, filter, viewpointId) {
|
|
5989
6430
|
return {
|
|
5990
|
-
version: "2.
|
|
6431
|
+
version: "2.5",
|
|
5991
6432
|
viewpointId,
|
|
5992
6433
|
activeEventId: renderOptions.activeEventId ?? null,
|
|
5993
6434
|
viewerState: { ...viewerState },
|
|
5994
6435
|
renderOptions: {
|
|
5995
6436
|
preset: renderOptions.preset,
|
|
5996
6437
|
projection: renderOptions.projection,
|
|
6438
|
+
camera: renderOptions.camera ? { ...renderOptions.camera } : null,
|
|
5997
6439
|
layers: renderOptions.layers ? { ...renderOptions.layers } : void 0,
|
|
5998
6440
|
scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : void 0,
|
|
5999
6441
|
activeEventId: renderOptions.activeEventId ?? null
|
|
@@ -6007,7 +6449,7 @@
|
|
|
6007
6449
|
function deserializeViewerAtlasState(serialized) {
|
|
6008
6450
|
const raw = JSON.parse(decodeURIComponent(serialized));
|
|
6009
6451
|
return {
|
|
6010
|
-
version: "2.0",
|
|
6452
|
+
version: raw.version === "2.0" ? "2.0" : "2.5",
|
|
6011
6453
|
viewpointId: raw.viewpointId ?? null,
|
|
6012
6454
|
activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null,
|
|
6013
6455
|
viewerState: {
|
|
@@ -6020,6 +6462,7 @@
|
|
|
6020
6462
|
renderOptions: {
|
|
6021
6463
|
preset: raw.renderOptions?.preset,
|
|
6022
6464
|
projection: raw.renderOptions?.projection,
|
|
6465
|
+
camera: raw.renderOptions?.camera ? { ...raw.renderOptions.camera } : null,
|
|
6023
6466
|
layers: raw.renderOptions?.layers ? { ...raw.renderOptions.layers } : void 0,
|
|
6024
6467
|
scaleModel: raw.renderOptions?.scaleModel ? { ...raw.renderOptions.scaleModel } : void 0,
|
|
6025
6468
|
activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null
|
|
@@ -6037,6 +6480,7 @@
|
|
|
6037
6480
|
viewerState: { ...atlasState.viewerState },
|
|
6038
6481
|
renderOptions: {
|
|
6039
6482
|
...atlasState.renderOptions,
|
|
6483
|
+
camera: atlasState.renderOptions.camera ? { ...atlasState.renderOptions.camera } : null,
|
|
6040
6484
|
layers: atlasState.renderOptions.layers ? { ...atlasState.renderOptions.layers } : void 0,
|
|
6041
6485
|
scaleModel: atlasState.renderOptions.scaleModel ? { ...atlasState.renderOptions.scaleModel } : void 0,
|
|
6042
6486
|
activeEventId: atlasState.renderOptions.activeEventId ?? null
|
|
@@ -7132,6 +7576,7 @@
|
|
|
7132
7576
|
padding: options.padding,
|
|
7133
7577
|
preset: options.preset,
|
|
7134
7578
|
projection: options.projection,
|
|
7579
|
+
camera: options.camera ? { ...options.camera } : null,
|
|
7135
7580
|
scaleModel: options.scaleModel ? { ...options.scaleModel } : void 0,
|
|
7136
7581
|
theme: options.theme,
|
|
7137
7582
|
layers: options.layers,
|
|
@@ -7420,6 +7865,11 @@
|
|
|
7420
7865
|
if (currentInput.kind !== "scene" && viewpoint.projection !== scene.projection) {
|
|
7421
7866
|
nextRenderOptions.projection = viewpoint.projection;
|
|
7422
7867
|
}
|
|
7868
|
+
if (viewpoint.camera) {
|
|
7869
|
+
nextRenderOptions.camera = { ...viewpoint.camera };
|
|
7870
|
+
} else if (renderOptions.camera) {
|
|
7871
|
+
nextRenderOptions.camera = null;
|
|
7872
|
+
}
|
|
7423
7873
|
if (viewpointLayers) {
|
|
7424
7874
|
nextRenderOptions.layers = viewpointLayers;
|
|
7425
7875
|
}
|
|
@@ -8026,6 +8476,7 @@
|
|
|
8026
8476
|
function cloneRenderOptions(renderOptions) {
|
|
8027
8477
|
return {
|
|
8028
8478
|
...renderOptions,
|
|
8479
|
+
camera: renderOptions.camera ? { ...renderOptions.camera } : null,
|
|
8029
8480
|
filter: renderOptions.filter ? { ...renderOptions.filter } : void 0,
|
|
8030
8481
|
scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : void 0,
|
|
8031
8482
|
layers: renderOptions.layers ? { ...renderOptions.layers } : void 0,
|
|
@@ -8037,6 +8488,7 @@
|
|
|
8037
8488
|
return {
|
|
8038
8489
|
...current,
|
|
8039
8490
|
...next,
|
|
8491
|
+
camera: next.camera !== void 0 ? next.camera ? { ...next.camera } : null : current.camera ? { ...current.camera } : null,
|
|
8040
8492
|
filter: next.filter !== void 0 ? normalizeViewerFilter(next.filter) : current.filter ? { ...current.filter } : void 0,
|
|
8041
8493
|
scaleModel: next.scaleModel ? {
|
|
8042
8494
|
...current.scaleModel ?? {},
|
|
@@ -8050,7 +8502,7 @@
|
|
|
8050
8502
|
};
|
|
8051
8503
|
}
|
|
8052
8504
|
function hasSceneAffectingRenderOptions(options) {
|
|
8053
|
-
return options.width !== void 0 || options.height !== void 0 || options.padding !== void 0 || options.preset !== void 0 || options.projection !== void 0 || options.scaleModel !== void 0 || options.activeEventId !== void 0;
|
|
8505
|
+
return options.width !== void 0 || options.height !== void 0 || options.padding !== void 0 || options.preset !== void 0 || options.projection !== void 0 || options.camera !== void 0 || options.scaleModel !== void 0 || options.activeEventId !== void 0;
|
|
8054
8506
|
}
|
|
8055
8507
|
function resolveSourceRenderOptions(loaded, renderOptions) {
|
|
8056
8508
|
const atlasDocument = loaded.atlasDocument ?? loaded.draftDocument;
|
|
@@ -8361,7 +8813,11 @@
|
|
|
8361
8813
|
var FIELD_HELP = {
|
|
8362
8814
|
"defaults-view": {
|
|
8363
8815
|
description: "Sets the default camera projection for the atlas.",
|
|
8364
|
-
references: [
|
|
8816
|
+
references: [
|
|
8817
|
+
"Topdown = map-like",
|
|
8818
|
+
"Isometric = angled overview",
|
|
8819
|
+
"Orthographic/Perspective = 3D-ready semantic views"
|
|
8820
|
+
]
|
|
8365
8821
|
},
|
|
8366
8822
|
"defaults-scale": {
|
|
8367
8823
|
description: "Chooses the overall spacing/style preset used by the renderer.",
|
|
@@ -8373,15 +8829,35 @@
|
|
|
8373
8829
|
},
|
|
8374
8830
|
"viewpoint-projection": {
|
|
8375
8831
|
description: "Overrides the projection for this saved viewpoint.",
|
|
8376
|
-
references: [
|
|
8832
|
+
references: [
|
|
8833
|
+
"Topdown = flat orbital map",
|
|
8834
|
+
"Isometric = angled scene",
|
|
8835
|
+
"Orthographic/Perspective = stored with current 2D fallback"
|
|
8836
|
+
]
|
|
8377
8837
|
},
|
|
8378
8838
|
"viewpoint-zoom": {
|
|
8379
8839
|
description: "Controls how closely this viewpoint frames the system.",
|
|
8380
8840
|
references: ["1 = scene fit", "2+ = close-up"]
|
|
8381
8841
|
},
|
|
8382
8842
|
"viewpoint-rotation": {
|
|
8383
|
-
description: "
|
|
8384
|
-
references: ["90deg = quarter turn", "
|
|
8843
|
+
description: "Legacy 2D screen rotation. This is separate from the Schema 2.5 camera block.",
|
|
8844
|
+
references: ["90deg = quarter turn", "Use camera.azimuth for semantic view direction"]
|
|
8845
|
+
},
|
|
8846
|
+
"viewpoint-camera-azimuth": {
|
|
8847
|
+
description: "Horizontal camera direction in degrees for Schema 2.5 viewpoints.",
|
|
8848
|
+
references: ["0 = forward/default", "90 = quarter orbit around the scene"]
|
|
8849
|
+
},
|
|
8850
|
+
"viewpoint-camera-elevation": {
|
|
8851
|
+
description: "Vertical camera tilt in degrees for 3D-ready viewpoints.",
|
|
8852
|
+
references: ["0 = level", "30 = gentle look down"]
|
|
8853
|
+
},
|
|
8854
|
+
"viewpoint-camera-roll": {
|
|
8855
|
+
description: "Rolls the camera around its forward axis.",
|
|
8856
|
+
references: ["0 = upright", "15 = slight bank"]
|
|
8857
|
+
},
|
|
8858
|
+
"viewpoint-camera-distance": {
|
|
8859
|
+
description: "Semantic camera distance for perspective viewpoints.",
|
|
8860
|
+
references: ["4 = close", "12 = wide framing"]
|
|
8385
8861
|
},
|
|
8386
8862
|
"viewpoint-events": {
|
|
8387
8863
|
description: "Lists event IDs that this viewpoint should feature in its detail panel.",
|
|
@@ -8407,6 +8883,14 @@
|
|
|
8407
8883
|
description: "Notes where or how the event is visible.",
|
|
8408
8884
|
references: ['"Visible from Naar"', '"Southern hemisphere only"']
|
|
8409
8885
|
},
|
|
8886
|
+
"event-epoch": {
|
|
8887
|
+
description: "Optional event-wide epoch that event poses inherit unless they override it.",
|
|
8888
|
+
references: ['"JY-0001.0"', '"Naar bloom cycle year 18"']
|
|
8889
|
+
},
|
|
8890
|
+
"event-referencePlane": {
|
|
8891
|
+
description: "Optional event-wide reference plane for all poses in this snapshot.",
|
|
8892
|
+
references: ["ecliptic", "naar-equatorial"]
|
|
8893
|
+
},
|
|
8410
8894
|
"event-viewpoints": {
|
|
8411
8895
|
description: "Viewpoint IDs that should list this event prominently.",
|
|
8412
8896
|
references: ["naar-system", "overview inner-system"]
|
|
@@ -8447,6 +8931,14 @@
|
|
|
8447
8931
|
description: "Starting position of the object along its orbit.",
|
|
8448
8932
|
references: ["0deg = start position", "180deg = opposite side"]
|
|
8449
8933
|
},
|
|
8934
|
+
"pose-epoch": {
|
|
8935
|
+
description: "Overrides the effective epoch for this pose only.",
|
|
8936
|
+
references: ['"JY-0001.0"', "Falls back to event, object, then system"]
|
|
8937
|
+
},
|
|
8938
|
+
"pose-referencePlane": {
|
|
8939
|
+
description: "Overrides the effective reference plane for this pose only.",
|
|
8940
|
+
references: ["naar-equatorial", "Falls back to event, object, then system"]
|
|
8941
|
+
},
|
|
8450
8942
|
"prop-radius": {
|
|
8451
8943
|
description: "Visual body size or real-world-inspired radius value.",
|
|
8452
8944
|
references: ["1re = Earth radius", "1sol = Sun radius"]
|
|
@@ -8641,6 +9133,8 @@
|
|
|
8641
9133
|
participantObjectIds: [],
|
|
8642
9134
|
timing: null,
|
|
8643
9135
|
visibility: null,
|
|
9136
|
+
epoch: null,
|
|
9137
|
+
referencePlane: null,
|
|
8644
9138
|
tags: [],
|
|
8645
9139
|
color: null,
|
|
8646
9140
|
hidden: false,
|
|
@@ -8665,6 +9159,7 @@
|
|
|
8665
9159
|
preset: atlasDocument.system?.defaults.preset ?? null,
|
|
8666
9160
|
zoom: null,
|
|
8667
9161
|
rotationDeg: 0,
|
|
9162
|
+
camera: null,
|
|
8668
9163
|
layers: {},
|
|
8669
9164
|
filter: null
|
|
8670
9165
|
};
|
|
@@ -9556,6 +10051,7 @@
|
|
|
9556
10051
|
preset: readOptionalTextInput(form, "viewpoint-preset") ?? null,
|
|
9557
10052
|
zoom: parseNullableNumber(readOptionalTextInput(form, "viewpoint-zoom")),
|
|
9558
10053
|
rotationDeg: parseNullableNumber(readOptionalTextInput(form, "viewpoint-rotation")) ?? 0,
|
|
10054
|
+
camera: buildViewCameraFromForm(form),
|
|
9559
10055
|
layers: {
|
|
9560
10056
|
background: readCheckbox(form, "layer-background"),
|
|
9561
10057
|
guides: readCheckbox(form, "layer-guides"),
|
|
@@ -9598,6 +10094,8 @@
|
|
|
9598
10094
|
participantObjectIds: splitTokens(readOptionalTextInput(form, "event-participants")),
|
|
9599
10095
|
timing: readOptionalTextInput(form, "event-timing"),
|
|
9600
10096
|
visibility: readOptionalTextInput(form, "event-visibility"),
|
|
10097
|
+
epoch: readOptionalTextInput(form, "event-epoch"),
|
|
10098
|
+
referencePlane: readOptionalTextInput(form, "event-referencePlane"),
|
|
9601
10099
|
tags: splitTokens(readOptionalTextInput(form, "event-tags")),
|
|
9602
10100
|
color: readOptionalTextInput(form, "event-color"),
|
|
9603
10101
|
hidden: readCheckbox(form, "event-hidden")
|
|
@@ -9620,7 +10118,9 @@
|
|
|
9620
10118
|
const nextObjectId = readTextInput(form, "pose-object-id") || currentPose.objectId;
|
|
9621
10119
|
const replacement = {
|
|
9622
10120
|
objectId: nextObjectId,
|
|
9623
|
-
placement: buildPlacementFromPoseForm(form, currentPose)
|
|
10121
|
+
placement: buildPlacementFromPoseForm(form, currentPose),
|
|
10122
|
+
epoch: readOptionalTextInput(form, "pose-epoch"),
|
|
10123
|
+
referencePlane: readOptionalTextInput(form, "pose-referencePlane")
|
|
9624
10124
|
};
|
|
9625
10125
|
const inner = parseOptionalUnit(readOptionalTextInput(form, "prop-inner"));
|
|
9626
10126
|
const outer = parseOptionalUnit(readOptionalTextInput(form, "prop-outer"));
|
|
@@ -9990,7 +10490,9 @@
|
|
|
9990
10490
|
<h2>Defaults</h2>
|
|
9991
10491
|
${renderInspectorSection("defaults", "basics", "Basics", `${renderSelectField("Projection", "defaults-view", [
|
|
9992
10492
|
["topdown", "Topdown"],
|
|
9993
|
-
["isometric", "Isometric"]
|
|
10493
|
+
["isometric", "Isometric"],
|
|
10494
|
+
["orthographic", "Orthographic"],
|
|
10495
|
+
["perspective", "Perspective"]
|
|
9994
10496
|
], defaults?.view ?? "topdown")}
|
|
9995
10497
|
${renderTextField("Scale preset", "defaults-scale", defaults?.scale ?? "")}
|
|
9996
10498
|
${renderTextField("Units", "defaults-units", defaults?.units ?? "")}
|
|
@@ -10026,7 +10528,9 @@
|
|
|
10026
10528
|
${renderTextField("Selected object", "viewpoint-select", viewpoint.selectedObjectId ?? "")}
|
|
10027
10529
|
${renderSelectField("Projection", "viewpoint-projection", [
|
|
10028
10530
|
["topdown", "Topdown"],
|
|
10029
|
-
["isometric", "Isometric"]
|
|
10531
|
+
["isometric", "Isometric"],
|
|
10532
|
+
["orthographic", "Orthographic"],
|
|
10533
|
+
["perspective", "Perspective"]
|
|
10030
10534
|
], viewpoint.projection)}
|
|
10031
10535
|
${renderSelectField("Preset", "viewpoint-preset", [
|
|
10032
10536
|
["", "Document default"],
|
|
@@ -10037,6 +10541,11 @@
|
|
|
10037
10541
|
], viewpoint.preset ?? "")}
|
|
10038
10542
|
${renderTextField("Zoom", "viewpoint-zoom", viewpoint.zoom === null ? "" : String(viewpoint.zoom))}
|
|
10039
10543
|
${renderTextField("Rotation", "viewpoint-rotation", String(viewpoint.rotationDeg))}`, true)}
|
|
10544
|
+
${renderInspectorSection("viewpoint", "camera", "Camera", `${renderTextField("Azimuth", "viewpoint-camera-azimuth", viewpoint.camera?.azimuth === null || viewpoint.camera?.azimuth === void 0 ? "" : String(viewpoint.camera.azimuth))}
|
|
10545
|
+
${renderTextField("Elevation", "viewpoint-camera-elevation", viewpoint.camera?.elevation === null || viewpoint.camera?.elevation === void 0 ? "" : String(viewpoint.camera.elevation))}
|
|
10546
|
+
${renderTextField("Roll", "viewpoint-camera-roll", viewpoint.camera?.roll === null || viewpoint.camera?.roll === void 0 ? "" : String(viewpoint.camera.roll))}
|
|
10547
|
+
${renderTextField("Distance", "viewpoint-camera-distance", viewpoint.camera?.distance === null || viewpoint.camera?.distance === void 0 ? "" : String(viewpoint.camera.distance))}
|
|
10548
|
+
<p class="wo-editor-inline-note">Rotation stays a 2D screen-rotation hint. The camera block stores Schema 2.5 view direction and framing.</p>`)}
|
|
10040
10549
|
${renderInspectorSection("viewpoint", "layers", "Layers", `<fieldset class="wo-editor-fieldset">
|
|
10041
10550
|
<legend>Layers</legend>
|
|
10042
10551
|
${renderCheckboxField("Background", "layer-background", viewpoint.layers.background !== false)}
|
|
@@ -10071,6 +10580,8 @@
|
|
|
10071
10580
|
${renderTextField("Participants", "event-participants", eventEntry.participantObjectIds.join(" "))}
|
|
10072
10581
|
${renderTextField("Timing", "event-timing", eventEntry.timing ?? "")}
|
|
10073
10582
|
${renderTextField("Visibility", "event-visibility", eventEntry.visibility ?? "")}
|
|
10583
|
+
${renderTextField("Epoch", "event-epoch", eventEntry.epoch ?? "")}
|
|
10584
|
+
${renderTextField("Reference plane", "event-referencePlane", eventEntry.referencePlane ?? "")}
|
|
10074
10585
|
${renderTextField("Tags", "event-tags", eventEntry.tags.join(" "))}
|
|
10075
10586
|
${renderTextField("Color", "event-color", eventEntry.color ?? "")}
|
|
10076
10587
|
${renderCheckboxField("Hidden", "event-hidden", eventEntry.hidden === true)}`, true)}
|
|
@@ -10115,6 +10626,9 @@
|
|
|
10115
10626
|
${renderTextField("Phase", "placement-phase", pose.placement?.mode === "orbit" && pose.placement.phase ? formatUnitValue3(pose.placement.phase) : "")}
|
|
10116
10627
|
${renderTextField("Inner", "prop-inner", pose.inner ? formatUnitValue3(pose.inner) : "")}
|
|
10117
10628
|
${renderTextField("Outer", "prop-outer", pose.outer ? formatUnitValue3(pose.outer) : "")}`, true)}
|
|
10629
|
+
${renderInspectorSection("event-pose", "context", "Context", `${renderTextField("Epoch", "pose-epoch", pose.epoch ?? "")}
|
|
10630
|
+
${renderTextField("Reference plane", "pose-referencePlane", pose.referencePlane ?? "")}
|
|
10631
|
+
<p class="wo-editor-inline-note">Falls back to event, then object, then system context when left empty.</p>`)}
|
|
10118
10632
|
</form>`;
|
|
10119
10633
|
}
|
|
10120
10634
|
function renderAnnotationInspector(formState, id) {
|
|
@@ -10299,6 +10813,15 @@
|
|
|
10299
10813
|
const parsed = Number(value);
|
|
10300
10814
|
return Number.isFinite(parsed) ? parsed : null;
|
|
10301
10815
|
}
|
|
10816
|
+
function buildViewCameraFromForm(form) {
|
|
10817
|
+
const camera = {
|
|
10818
|
+
azimuth: parseNullableNumber(readOptionalTextInput(form, "viewpoint-camera-azimuth")),
|
|
10819
|
+
elevation: parseNullableNumber(readOptionalTextInput(form, "viewpoint-camera-elevation")),
|
|
10820
|
+
roll: parseNullableNumber(readOptionalTextInput(form, "viewpoint-camera-roll")),
|
|
10821
|
+
distance: parseNullableNumber(readOptionalTextInput(form, "viewpoint-camera-distance"))
|
|
10822
|
+
};
|
|
10823
|
+
return camera.azimuth !== null || camera.elevation !== null || camera.roll !== null || camera.distance !== null ? camera : null;
|
|
10824
|
+
}
|
|
10302
10825
|
function parseObjectTypes(value) {
|
|
10303
10826
|
const tokens = splitTokens(value);
|
|
10304
10827
|
return tokens.filter((token) => OBJECT_TYPES.includes(token));
|
|
@@ -10881,6 +11404,21 @@
|
|
|
10881
11404
|
return ["viewpoint-zoom"];
|
|
10882
11405
|
case "rotationDeg":
|
|
10883
11406
|
return ["viewpoint-rotation"];
|
|
11407
|
+
case "camera":
|
|
11408
|
+
return [
|
|
11409
|
+
"viewpoint-camera-azimuth",
|
|
11410
|
+
"viewpoint-camera-elevation",
|
|
11411
|
+
"viewpoint-camera-roll",
|
|
11412
|
+
"viewpoint-camera-distance"
|
|
11413
|
+
];
|
|
11414
|
+
case "camera.azimuth":
|
|
11415
|
+
return ["viewpoint-camera-azimuth"];
|
|
11416
|
+
case "camera.elevation":
|
|
11417
|
+
return ["viewpoint-camera-elevation"];
|
|
11418
|
+
case "camera.roll":
|
|
11419
|
+
return ["viewpoint-camera-roll"];
|
|
11420
|
+
case "camera.distance":
|
|
11421
|
+
return ["viewpoint-camera-distance"];
|
|
10884
11422
|
case "events":
|
|
10885
11423
|
return ["viewpoint-events"];
|
|
10886
11424
|
default:
|
|
@@ -10906,6 +11444,10 @@
|
|
|
10906
11444
|
return ["event-timing"];
|
|
10907
11445
|
case "visibility":
|
|
10908
11446
|
return ["event-visibility"];
|
|
11447
|
+
case "epoch":
|
|
11448
|
+
return ["event-epoch"];
|
|
11449
|
+
case "referencePlane":
|
|
11450
|
+
return ["event-referencePlane"];
|
|
10909
11451
|
case "tags":
|
|
10910
11452
|
return ["event-tags"];
|
|
10911
11453
|
case "color":
|
|
@@ -10934,6 +11476,12 @@
|
|
|
10934
11476
|
if (field === "inner" || field === "outer") {
|
|
10935
11477
|
return [`prop-${field}`];
|
|
10936
11478
|
}
|
|
11479
|
+
if (field === "epoch") {
|
|
11480
|
+
return ["pose-epoch"];
|
|
11481
|
+
}
|
|
11482
|
+
if (field === "referencePlane") {
|
|
11483
|
+
return ["pose-referencePlane"];
|
|
11484
|
+
}
|
|
10937
11485
|
return [];
|
|
10938
11486
|
case "annotation":
|
|
10939
11487
|
switch (field) {
|