worldorbit 2.5.17 → 3.0.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 +91 -18
- package/dist/browser/core/dist/atlas-edit.d.ts +11 -0
- package/dist/browser/core/dist/atlas-edit.js +347 -0
- package/dist/browser/core/dist/atlas-utils.d.ts +22 -0
- package/dist/browser/core/dist/atlas-utils.js +189 -0
- package/dist/browser/core/dist/atlas-validate.d.ts +2 -0
- package/dist/browser/core/dist/atlas-validate.js +488 -0
- package/dist/browser/core/dist/diagnostics.d.ts +10 -0
- package/dist/browser/core/dist/diagnostics.js +109 -0
- package/dist/browser/core/dist/draft-parse.d.ts +3 -0
- package/dist/browser/core/dist/draft-parse.js +1654 -0
- package/dist/browser/core/dist/draft.d.ts +21 -0
- package/dist/browser/core/dist/draft.js +482 -0
- package/dist/browser/core/dist/errors.d.ts +7 -0
- package/dist/browser/core/dist/errors.js +16 -0
- package/dist/browser/core/dist/format.d.ts +4 -0
- package/dist/browser/core/dist/format.js +613 -0
- package/dist/browser/core/dist/index.d.ts +29 -0
- package/dist/browser/core/dist/index.js +35 -6101
- package/dist/browser/core/dist/load.d.ts +4 -0
- package/dist/browser/core/dist/load.js +182 -0
- package/dist/browser/core/dist/markdown.d.ts +2 -0
- package/dist/browser/core/dist/markdown.js +37 -0
- package/dist/browser/core/dist/normalize.d.ts +2 -0
- package/dist/browser/core/dist/normalize.js +312 -0
- package/dist/browser/core/dist/parse.d.ts +2 -0
- package/dist/browser/core/dist/parse.js +133 -0
- package/dist/browser/core/dist/scene.d.ts +3 -0
- package/dist/browser/core/dist/scene.js +1901 -0
- package/dist/browser/core/dist/schema.d.ts +8 -0
- package/dist/browser/core/dist/schema.js +298 -0
- package/dist/browser/core/dist/spatial-scene.d.ts +3 -0
- package/dist/browser/core/dist/spatial-scene.js +420 -0
- package/dist/browser/core/dist/tokenize.d.ts +4 -0
- package/dist/browser/core/dist/tokenize.js +68 -0
- package/dist/browser/core/dist/types.d.ts +637 -0
- package/dist/browser/core/dist/types.js +1 -0
- package/dist/browser/core/dist/validate.d.ts +2 -0
- package/dist/browser/core/dist/validate.js +56 -0
- package/dist/browser/editor/dist/editor.d.ts +2 -0
- package/dist/browser/editor/dist/editor.js +3700 -0
- package/dist/browser/editor/dist/index.d.ts +2 -0
- package/dist/browser/editor/dist/index.js +1 -11702
- package/dist/browser/editor/dist/types.d.ts +59 -0
- package/dist/browser/editor/dist/types.js +1 -0
- package/dist/browser/markdown/dist/html.d.ts +3 -0
- package/dist/browser/markdown/dist/html.js +64 -0
- package/dist/browser/markdown/dist/index.d.ts +4 -0
- package/dist/browser/markdown/dist/index.js +3 -5766
- package/dist/browser/markdown/dist/rehype.d.ts +10 -0
- package/dist/browser/markdown/dist/rehype.js +49 -0
- package/dist/browser/markdown/dist/remark.d.ts +9 -0
- package/dist/browser/markdown/dist/remark.js +28 -0
- package/dist/browser/markdown/dist/types.d.ts +11 -0
- package/dist/browser/markdown/dist/types.js +1 -0
- package/dist/browser/viewer/dist/atlas-state.d.ts +12 -0
- package/dist/browser/viewer/dist/atlas-state.js +269 -0
- package/dist/browser/viewer/dist/atlas-viewer.d.ts +2 -0
- package/dist/browser/viewer/dist/atlas-viewer.js +495 -0
- package/dist/browser/viewer/dist/custom-element.d.ts +1 -0
- package/dist/browser/viewer/dist/custom-element.js +78 -0
- package/dist/browser/viewer/dist/embed.d.ts +24 -0
- package/dist/browser/viewer/dist/embed.js +172 -0
- package/dist/browser/viewer/dist/errors.d.ts +6 -0
- package/dist/browser/viewer/dist/errors.js +12 -0
- package/dist/browser/viewer/dist/index.d.ts +10 -0
- package/dist/browser/viewer/dist/index.js +9 -7901
- package/dist/browser/viewer/dist/minimap.d.ts +3 -0
- package/dist/browser/viewer/dist/minimap.js +63 -0
- package/dist/browser/viewer/dist/render.d.ts +6 -0
- package/dist/browser/viewer/dist/render.js +670 -0
- package/dist/browser/viewer/dist/runtime-3d.d.ts +19 -0
- package/dist/browser/viewer/dist/runtime-3d.js +494 -0
- package/dist/browser/viewer/dist/theme.d.ts +4 -0
- package/dist/browser/viewer/dist/theme.js +103 -0
- package/dist/browser/viewer/dist/tooltip.d.ts +3 -0
- package/dist/browser/viewer/dist/tooltip.js +198 -0
- package/dist/browser/viewer/dist/types.d.ts +292 -0
- package/dist/browser/viewer/dist/types.js +1 -0
- package/dist/browser/viewer/dist/vendor/three.module.js +53032 -0
- package/dist/browser/viewer/dist/viewer-state.d.ts +19 -0
- package/dist/browser/viewer/dist/viewer-state.js +162 -0
- package/dist/browser/viewer/dist/viewer.d.ts +2 -0
- package/dist/browser/viewer/dist/viewer.js +1662 -0
- package/dist/unpkg/core/dist/atlas-edit.d.ts +11 -0
- package/dist/unpkg/core/dist/atlas-edit.js +347 -0
- package/dist/unpkg/core/dist/atlas-utils.d.ts +22 -0
- package/dist/unpkg/core/dist/atlas-utils.js +189 -0
- package/dist/unpkg/core/dist/atlas-validate.d.ts +2 -0
- package/dist/unpkg/core/dist/atlas-validate.js +488 -0
- package/dist/unpkg/core/dist/diagnostics.d.ts +10 -0
- package/dist/unpkg/core/dist/diagnostics.js +109 -0
- package/dist/unpkg/core/dist/draft-parse.d.ts +3 -0
- package/dist/unpkg/core/dist/draft-parse.js +1654 -0
- package/dist/unpkg/core/dist/draft.d.ts +21 -0
- package/dist/unpkg/core/dist/draft.js +482 -0
- package/dist/unpkg/core/dist/errors.d.ts +7 -0
- package/dist/unpkg/core/dist/errors.js +16 -0
- package/dist/unpkg/core/dist/format.d.ts +4 -0
- package/dist/unpkg/core/dist/format.js +613 -0
- package/dist/unpkg/core/dist/index.d.ts +29 -0
- package/dist/unpkg/core/dist/index.js +35 -6173
- package/dist/unpkg/core/dist/load.d.ts +4 -0
- package/dist/unpkg/core/dist/load.js +182 -0
- package/dist/unpkg/core/dist/markdown.d.ts +2 -0
- package/dist/unpkg/core/dist/markdown.js +37 -0
- package/dist/unpkg/core/dist/normalize.d.ts +2 -0
- package/dist/unpkg/core/dist/normalize.js +312 -0
- package/dist/unpkg/core/dist/parse.d.ts +2 -0
- package/dist/unpkg/core/dist/parse.js +133 -0
- package/dist/unpkg/core/dist/scene.d.ts +3 -0
- package/dist/unpkg/core/dist/scene.js +1901 -0
- package/dist/unpkg/core/dist/schema.d.ts +8 -0
- package/dist/unpkg/core/dist/schema.js +298 -0
- package/dist/unpkg/core/dist/spatial-scene.d.ts +3 -0
- package/dist/unpkg/core/dist/spatial-scene.js +420 -0
- package/dist/unpkg/core/dist/tokenize.d.ts +4 -0
- package/dist/unpkg/core/dist/tokenize.js +68 -0
- package/dist/unpkg/core/dist/types.d.ts +637 -0
- package/dist/unpkg/core/dist/types.js +1 -0
- package/dist/unpkg/core/dist/validate.d.ts +2 -0
- package/dist/unpkg/core/dist/validate.js +56 -0
- package/dist/unpkg/editor/dist/editor.d.ts +2 -0
- package/dist/unpkg/editor/dist/editor.js +3700 -0
- package/dist/unpkg/editor/dist/index.d.ts +2 -0
- package/dist/unpkg/editor/dist/index.js +1 -11727
- package/dist/unpkg/editor/dist/types.d.ts +59 -0
- package/dist/unpkg/editor/dist/types.js +1 -0
- package/dist/unpkg/markdown/dist/html.d.ts +3 -0
- package/dist/unpkg/markdown/dist/html.js +64 -0
- package/dist/unpkg/markdown/dist/index.d.ts +4 -0
- package/dist/unpkg/markdown/dist/index.js +3 -5794
- package/dist/unpkg/markdown/dist/rehype.d.ts +10 -0
- package/dist/unpkg/markdown/dist/rehype.js +49 -0
- package/dist/unpkg/markdown/dist/remark.d.ts +9 -0
- package/dist/unpkg/markdown/dist/remark.js +28 -0
- package/dist/unpkg/markdown/dist/types.d.ts +11 -0
- package/dist/unpkg/markdown/dist/types.js +1 -0
- package/dist/unpkg/viewer/dist/atlas-state.d.ts +12 -0
- package/dist/unpkg/viewer/dist/atlas-state.js +269 -0
- package/dist/unpkg/viewer/dist/atlas-viewer.d.ts +2 -0
- package/dist/unpkg/viewer/dist/atlas-viewer.js +495 -0
- package/dist/unpkg/viewer/dist/custom-element.d.ts +1 -0
- package/dist/unpkg/viewer/dist/custom-element.js +78 -0
- package/dist/unpkg/viewer/dist/embed.d.ts +24 -0
- package/dist/unpkg/viewer/dist/embed.js +172 -0
- package/dist/unpkg/viewer/dist/errors.d.ts +6 -0
- package/dist/unpkg/viewer/dist/errors.js +12 -0
- package/dist/unpkg/viewer/dist/index.d.ts +10 -0
- package/dist/unpkg/viewer/dist/index.js +9 -7958
- package/dist/unpkg/viewer/dist/minimap.d.ts +3 -0
- package/dist/unpkg/viewer/dist/minimap.js +63 -0
- package/dist/unpkg/viewer/dist/render.d.ts +6 -0
- package/dist/unpkg/viewer/dist/render.js +670 -0
- package/dist/unpkg/viewer/dist/runtime-3d.d.ts +19 -0
- package/dist/unpkg/viewer/dist/runtime-3d.js +494 -0
- package/dist/unpkg/viewer/dist/theme.d.ts +4 -0
- package/dist/unpkg/viewer/dist/theme.js +103 -0
- package/dist/unpkg/viewer/dist/tooltip.d.ts +3 -0
- package/dist/unpkg/viewer/dist/tooltip.js +198 -0
- package/dist/unpkg/viewer/dist/types.d.ts +292 -0
- package/dist/unpkg/viewer/dist/types.js +1 -0
- package/dist/unpkg/viewer/dist/vendor/three.module.js +53032 -0
- package/dist/unpkg/viewer/dist/viewer-state.d.ts +19 -0
- package/dist/unpkg/viewer/dist/viewer-state.js +162 -0
- package/dist/unpkg/viewer/dist/viewer.d.ts +2 -0
- package/dist/unpkg/viewer/dist/viewer.js +1662 -0
- package/dist/unpkg/worldorbit-core.min.js +1 -12
- package/dist/unpkg/worldorbit-editor.min.js +1 -894
- package/dist/unpkg/worldorbit-markdown.min.js +1 -103
- package/dist/unpkg/worldorbit-viewer.min.js +1 -259
- package/dist/unpkg/worldorbit.js +2 -9243
- package/dist/unpkg/worldorbit.min.js +2 -263
- 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/index.d.ts +1 -0
- package/packages/core/dist/index.js +1 -0
- package/packages/core/dist/load.js +9 -2
- package/packages/core/dist/scene.js +158 -24
- package/packages/core/dist/spatial-scene.d.ts +3 -0
- package/packages/core/dist/spatial-scene.js +420 -0
- package/packages/core/dist/types.d.ts +124 -2
- package/packages/editor/dist/editor.js +130 -8
- package/packages/editor/dist/types.d.ts +4 -0
- package/packages/markdown/dist/html.js +10 -3
- package/packages/viewer/dist/atlas-state.js +8 -2
- package/packages/viewer/dist/atlas-viewer.js +20 -8
- package/packages/viewer/dist/custom-element.js +18 -4
- package/packages/viewer/dist/embed.d.ts +5 -1
- package/packages/viewer/dist/embed.js +58 -24
- package/packages/viewer/dist/errors.d.ts +6 -0
- package/packages/viewer/dist/errors.js +12 -0
- package/packages/viewer/dist/index.d.ts +1 -0
- package/packages/viewer/dist/index.js +1 -0
- package/packages/viewer/dist/runtime-3d.d.ts +19 -0
- package/packages/viewer/dist/runtime-3d.js +494 -0
- package/packages/viewer/dist/types.d.ts +25 -3
- package/packages/viewer/dist/vendor/three.module.js +53032 -0
- package/packages/viewer/dist/viewer.js +517 -41
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { collectAtlasDiagnostics } from "./atlas-validate.js";
|
|
2
|
-
export function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "2.
|
|
2
|
+
export function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "2.5") {
|
|
3
3
|
return {
|
|
4
4
|
format: "worldorbit",
|
|
5
5
|
version,
|
|
@@ -38,13 +38,13 @@ export function collectAtlasDiagnostics(document, sourceSchemaVersion) {
|
|
|
38
38
|
validateRelation(relation, objectMap, diagnostics);
|
|
39
39
|
}
|
|
40
40
|
for (const viewpoint of document.system?.viewpoints ?? []) {
|
|
41
|
-
validateViewpoint(viewpoint
|
|
41
|
+
validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, diagnostics, objectMap);
|
|
42
42
|
}
|
|
43
43
|
for (const object of document.objects) {
|
|
44
44
|
validateObject(object, document.system, objectMap, groupIds, diagnostics);
|
|
45
45
|
}
|
|
46
46
|
for (const event of document.events) {
|
|
47
|
-
validateEvent(event, objectMap, diagnostics);
|
|
47
|
+
validateEvent(event, document.system, objectMap, diagnostics);
|
|
48
48
|
}
|
|
49
49
|
return diagnostics;
|
|
50
50
|
}
|
|
@@ -65,21 +65,24 @@ function validateRelation(relation, objectMap, diagnostics) {
|
|
|
65
65
|
diagnostics.push(error("validate.relation.kind.required", `Relation "${relation.id}" is missing a "kind" value.`));
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
-
function validateViewpoint(
|
|
69
|
-
|
|
68
|
+
function validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, diagnostics, objectMap) {
|
|
69
|
+
const filter = viewpoint.filter;
|
|
70
|
+
if (sourceSchemaVersion === "2.1" || sourceSchemaVersion === "2.5") {
|
|
70
71
|
if (filter) {
|
|
71
72
|
for (const groupId of filter.groupIds) {
|
|
72
73
|
if (!groupIds.has(groupId)) {
|
|
73
|
-
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${
|
|
74
|
+
diagnostics.push(warn("validate.viewpoint.group.unknown", `Unknown group "${groupId}" in viewpoint "${viewpoint.id}".`, undefined, `viewpoint.${viewpoint.id}.groups`));
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
|
-
for (const eventId of
|
|
78
|
+
for (const eventId of viewpoint.events ?? []) {
|
|
78
79
|
if (!eventIds.has(eventId)) {
|
|
79
|
-
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${
|
|
80
|
+
diagnostics.push(warn("validate.viewpoint.event.unknown", `Unknown event "${eventId}" in viewpoint "${viewpoint.id}".`, undefined, `viewpoint.${viewpoint.id}.events`));
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
}
|
|
84
|
+
validateProjection(viewpoint.projection, diagnostics, `viewpoint.${viewpoint.id}.projection`, viewpoint.id);
|
|
85
|
+
validateCamera(viewpoint.camera, viewpoint.projection, viewpoint.rotationDeg, diagnostics, viewpoint.id, viewpoint.focusObjectId, viewpoint.selectedObjectId, filter, objectMap);
|
|
83
86
|
}
|
|
84
87
|
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
85
88
|
const placement = object.placement;
|
|
@@ -92,6 +95,12 @@ function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
97
|
}
|
|
98
|
+
if (typeof object.epoch === "string" && !object.epoch.trim()) {
|
|
99
|
+
diagnostics.push(warn("validate.epoch.empty", `Object "${object.id}" defines an empty epoch string.`, object.id, "epoch"));
|
|
100
|
+
}
|
|
101
|
+
if (typeof object.referencePlane === "string" && !object.referencePlane.trim()) {
|
|
102
|
+
diagnostics.push(warn("validate.referencePlane.empty", `Object "${object.id}" defines an empty reference plane string.`, object.id, "referencePlane"));
|
|
103
|
+
}
|
|
95
104
|
if (orbitPlacement) {
|
|
96
105
|
if (!objectMap.has(orbitPlacement.target)) {
|
|
97
106
|
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
@@ -167,12 +176,18 @@ function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
|
167
176
|
}
|
|
168
177
|
}
|
|
169
178
|
}
|
|
170
|
-
function validateEvent(event, objectMap, diagnostics) {
|
|
179
|
+
function validateEvent(event, system, objectMap, diagnostics) {
|
|
171
180
|
const fieldPrefix = `event.${event.id}`;
|
|
172
181
|
const referencedIds = new Set();
|
|
173
182
|
if (!event.kind.trim()) {
|
|
174
183
|
diagnostics.push(error("validate.event.kind.required", `Event "${event.id}" is missing a "kind" value.`, undefined, `${fieldPrefix}.kind`));
|
|
175
184
|
}
|
|
185
|
+
if (typeof event.epoch === "string" && !event.epoch.trim()) {
|
|
186
|
+
diagnostics.push(warn("validate.event.epoch.empty", `Event "${event.id}" defines an empty epoch string.`, undefined, `${fieldPrefix}.epoch`));
|
|
187
|
+
}
|
|
188
|
+
if (typeof event.referencePlane === "string" && !event.referencePlane.trim()) {
|
|
189
|
+
diagnostics.push(warn("validate.event.referencePlane.empty", `Event "${event.id}" defines an empty reference plane string.`, undefined, `${fieldPrefix}.referencePlane`));
|
|
190
|
+
}
|
|
176
191
|
if (!event.targetObjectId && event.participantObjectIds.length === 0) {
|
|
177
192
|
diagnostics.push(error("validate.event.references.required", `Event "${event.id}" must define a "target" or at least one participant.`, undefined, `${fieldPrefix}.participants`));
|
|
178
193
|
}
|
|
@@ -221,10 +236,14 @@ function validateEvent(event, objectMap, diagnostics) {
|
|
|
221
236
|
if (!referencedIds.has(pose.objectId)) {
|
|
222
237
|
diagnostics.push(warn("validate.event.pose.unreferenced", `Event pose "${pose.objectId}" on "${event.id}" is not listed in target/participants.`, undefined, poseFieldPrefix));
|
|
223
238
|
}
|
|
224
|
-
validateEventPose(pose, object, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
239
|
+
validateEventPose(pose, object, event, system, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
240
|
+
}
|
|
241
|
+
const missingPoseIds = [...referencedIds].filter((objectId) => !poseIds.has(objectId));
|
|
242
|
+
if (event.positions.length > 0 && missingPoseIds.length > 0) {
|
|
243
|
+
diagnostics.push(warn("validate.event.positions.partial", `Event "${event.id}" leaves ${missingPoseIds.length} referenced object(s) on their base placement.`, undefined, `${fieldPrefix}.positions`));
|
|
225
244
|
}
|
|
226
245
|
}
|
|
227
|
-
function validateEventPose(pose, object, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
246
|
+
function validateEventPose(pose, object, event, system, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
228
247
|
const placement = pose.placement;
|
|
229
248
|
if (!placement) {
|
|
230
249
|
diagnostics.push(error("validate.event.pose.placement.required", `Event "${eventId}" pose "${pose.objectId}" is missing a placement mode.`, undefined, fieldPrefix));
|
|
@@ -237,6 +256,15 @@ function validateEventPose(pose, object, objectMap, diagnostics, fieldPrefix, ev
|
|
|
237
256
|
if (placement.distance && placement.semiMajor) {
|
|
238
257
|
diagnostics.push(error("validate.event.pose.orbit.distanceConflict", `Event "${eventId}" pose "${pose.objectId}" cannot declare both "distance" and "semiMajor".`, undefined, `${fieldPrefix}.distance`));
|
|
239
258
|
}
|
|
259
|
+
if (placement.phase && !resolveEffectiveEpoch(system, object, event, pose)) {
|
|
260
|
+
diagnostics.push(warn("validate.event.pose.phase.epochMissing", `Event "${eventId}" pose "${pose.objectId}" sets "phase" without an effective epoch.`, undefined, `${fieldPrefix}.phase`));
|
|
261
|
+
}
|
|
262
|
+
if (placement.inclination && !resolveEffectiveReferencePlane(system, object, event, pose)) {
|
|
263
|
+
diagnostics.push(warn("validate.event.pose.inclination.referencePlaneMissing", `Event "${eventId}" pose "${pose.objectId}" sets "inclination" without an effective reference plane.`, undefined, `${fieldPrefix}.inclination`));
|
|
264
|
+
}
|
|
265
|
+
if (placement.period && !massInSolar(objectMap.get(placement.target)?.properties.mass)) {
|
|
266
|
+
diagnostics.push(warn("validate.event.pose.period.massMissing", `Event "${eventId}" pose "${pose.objectId}" sets "period" but its central mass cannot be derived.`, undefined, `${fieldPrefix}.period`));
|
|
267
|
+
}
|
|
240
268
|
return;
|
|
241
269
|
}
|
|
242
270
|
if (placement.mode === "surface") {
|
|
@@ -375,6 +403,67 @@ function durationInDays(value) {
|
|
|
375
403
|
return null;
|
|
376
404
|
}
|
|
377
405
|
}
|
|
406
|
+
function validateProjection(projection, diagnostics, field, viewpointId) {
|
|
407
|
+
if (projection !== "topdown" &&
|
|
408
|
+
projection !== "isometric" &&
|
|
409
|
+
projection !== "orthographic" &&
|
|
410
|
+
projection !== "perspective") {
|
|
411
|
+
diagnostics.push(error("validate.viewpoint.projection.invalid", `Unknown projection "${String(projection)}" in viewpoint "${viewpointId}".`, undefined, field));
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function validateCamera(camera, projection, rotationDeg, diagnostics, viewpointId, focusObjectId, selectedObjectId, filter, objectMap) {
|
|
415
|
+
if (!camera) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const prefix = `viewpoint.${viewpointId}.camera`;
|
|
419
|
+
for (const [key, value] of [
|
|
420
|
+
["azimuth", camera.azimuth],
|
|
421
|
+
["elevation", camera.elevation],
|
|
422
|
+
["roll", camera.roll],
|
|
423
|
+
["distance", camera.distance],
|
|
424
|
+
]) {
|
|
425
|
+
if (value !== null && (!Number.isFinite(value) || (key === "distance" && value <= 0))) {
|
|
426
|
+
diagnostics.push(error("validate.viewpoint.camera.invalid", `Invalid camera ${key} "${String(value)}" in viewpoint "${viewpointId}".`, undefined, `${prefix}.${key}`));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (camera.distance !== null && projection !== "perspective") {
|
|
430
|
+
diagnostics.push(warn("validate.viewpoint.camera.distance.partialEffect", `Camera "distance" only has a semantic effect in perspective viewpoints; "${viewpointId}" uses "${projection}".`, undefined, `${prefix}.distance`));
|
|
431
|
+
}
|
|
432
|
+
if (projection === "topdown" &&
|
|
433
|
+
(camera.elevation !== null || camera.roll !== null)) {
|
|
434
|
+
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.`, undefined, prefix));
|
|
435
|
+
}
|
|
436
|
+
if (projection === "isometric" &&
|
|
437
|
+
camera.elevation !== null) {
|
|
438
|
+
diagnostics.push(info("validate.viewpoint.camera.isometricStored", `Camera elevation on isometric viewpoint "${viewpointId}" is preserved semantically for future 3D rendering.`, undefined, `${prefix}.elevation`));
|
|
439
|
+
}
|
|
440
|
+
if (camera.azimuth !== null && camera.azimuth !== 0 && rotationDeg !== 0) {
|
|
441
|
+
diagnostics.push(warn("validate.viewpoint.rotation.cameraOverlap", `Viewpoint "${viewpointId}" uses camera.azimuth; keep "rotation" only for 2D screen rotation to avoid ambiguity.`, undefined, `${prefix}.azimuth`));
|
|
442
|
+
}
|
|
443
|
+
const hasAnchor = (focusObjectId !== null && objectMap.has(focusObjectId)) ||
|
|
444
|
+
(selectedObjectId !== null && objectMap.has(selectedObjectId)) ||
|
|
445
|
+
!!filter;
|
|
446
|
+
if (!hasAnchor) {
|
|
447
|
+
diagnostics.push(info("validate.viewpoint.camera.anchorMissing", `Viewpoint "${viewpointId}" stores camera settings without a focus object, selection, or filter anchor.`, undefined, prefix));
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function resolveEffectiveEpoch(system, object, event, pose) {
|
|
451
|
+
return normalizeOptionalContextString(pose?.epoch) ??
|
|
452
|
+
normalizeOptionalContextString(event?.epoch) ??
|
|
453
|
+
normalizeOptionalContextString(object.epoch) ??
|
|
454
|
+
normalizeOptionalContextString(system?.epoch) ??
|
|
455
|
+
null;
|
|
456
|
+
}
|
|
457
|
+
function resolveEffectiveReferencePlane(system, object, event, pose) {
|
|
458
|
+
return normalizeOptionalContextString(pose?.referencePlane) ??
|
|
459
|
+
normalizeOptionalContextString(event?.referencePlane) ??
|
|
460
|
+
normalizeOptionalContextString(object.referencePlane) ??
|
|
461
|
+
normalizeOptionalContextString(system?.referencePlane) ??
|
|
462
|
+
null;
|
|
463
|
+
}
|
|
464
|
+
function normalizeOptionalContextString(value) {
|
|
465
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
466
|
+
}
|
|
378
467
|
function toleranceForField(object, field) {
|
|
379
468
|
const tolerance = object.tolerances?.find((entry) => entry.field === field)?.value;
|
|
380
469
|
if (typeof tolerance === "number") {
|
|
@@ -88,6 +88,8 @@ const EVENT_POSE_FIELD_KEYS = new Set([
|
|
|
88
88
|
"free",
|
|
89
89
|
"inner",
|
|
90
90
|
"outer",
|
|
91
|
+
"epoch",
|
|
92
|
+
"referencePlane",
|
|
91
93
|
]);
|
|
92
94
|
export function parseWorldOrbitAtlas(source) {
|
|
93
95
|
return parseAtlasSource(source);
|
|
@@ -132,7 +134,7 @@ function parseAtlasSource(source, forcedOutputVersion) {
|
|
|
132
134
|
if (!sawSchemaHeader) {
|
|
133
135
|
sourceSchemaVersion = assertDraftSchemaHeader(tokens, lineNumber);
|
|
134
136
|
sawSchemaHeader = true;
|
|
135
|
-
if (prepared.comments.length > 0 && sourceSchemaVersion
|
|
137
|
+
if (prepared.comments.length > 0 && isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
136
138
|
diagnostics.push({
|
|
137
139
|
code: "parse.schema21.commentCompatibility",
|
|
138
140
|
severity: "warning",
|
|
@@ -207,15 +209,17 @@ function parseAtlasSource(source, forcedOutputVersion) {
|
|
|
207
209
|
function assertDraftSchemaHeader(tokens, line) {
|
|
208
210
|
if (tokens.length !== 2 ||
|
|
209
211
|
tokens[0].value.toLowerCase() !== "schema" ||
|
|
210
|
-
!["2.0-draft", "2.0", "2.1"].includes(tokens[1].value.toLowerCase())) {
|
|
211
|
-
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
212
|
+
!["2.0-draft", "2.0", "2.1", "2.5"].includes(tokens[1].value.toLowerCase())) {
|
|
213
|
+
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);
|
|
212
214
|
}
|
|
213
215
|
const version = tokens[1].value.toLowerCase();
|
|
214
|
-
return version === "2.
|
|
215
|
-
? "2.
|
|
216
|
-
: version === "2.
|
|
217
|
-
? "2.
|
|
218
|
-
: "2.0"
|
|
216
|
+
return version === "2.5"
|
|
217
|
+
? "2.5"
|
|
218
|
+
: version === "2.1"
|
|
219
|
+
? "2.1"
|
|
220
|
+
: version === "2.0-draft"
|
|
221
|
+
? "2.0-draft"
|
|
222
|
+
: "2.0";
|
|
219
223
|
}
|
|
220
224
|
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, flags) {
|
|
221
225
|
const keyword = tokens[0]?.value.toLowerCase();
|
|
@@ -235,6 +239,8 @@ function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, sy
|
|
|
235
239
|
return {
|
|
236
240
|
kind: "defaults",
|
|
237
241
|
system,
|
|
242
|
+
sourceSchemaVersion,
|
|
243
|
+
diagnostics,
|
|
238
244
|
seenFields: new Set(),
|
|
239
245
|
};
|
|
240
246
|
case "atlas":
|
|
@@ -327,6 +333,7 @@ function startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaV
|
|
|
327
333
|
preset: system.defaults.preset,
|
|
328
334
|
zoom: null,
|
|
329
335
|
rotationDeg: 0,
|
|
336
|
+
camera: null,
|
|
330
337
|
layers: {},
|
|
331
338
|
filter: null,
|
|
332
339
|
};
|
|
@@ -341,6 +348,9 @@ function startViewpointSection(tokens, line, system, viewpointIds, sourceSchemaV
|
|
|
341
348
|
inFilter: false,
|
|
342
349
|
filterIndent: null,
|
|
343
350
|
seenFilterFields: new Set(),
|
|
351
|
+
inCamera: false,
|
|
352
|
+
cameraIndent: null,
|
|
353
|
+
seenCameraFields: new Set(),
|
|
344
354
|
};
|
|
345
355
|
}
|
|
346
356
|
function startAnnotationSection(tokens, line, system, annotationIds) {
|
|
@@ -447,6 +457,8 @@ function startEventSection(tokens, line, events, eventPoseNodes, eventIds, sourc
|
|
|
447
457
|
participantObjectIds: [],
|
|
448
458
|
timing: null,
|
|
449
459
|
visibility: null,
|
|
460
|
+
epoch: null,
|
|
461
|
+
referencePlane: null,
|
|
450
462
|
tags: [],
|
|
451
463
|
color: null,
|
|
452
464
|
hidden: false,
|
|
@@ -571,6 +583,12 @@ function applyDefaultsField(section, tokens, line) {
|
|
|
571
583
|
const value = joinFieldValue(tokens, line);
|
|
572
584
|
switch (key) {
|
|
573
585
|
case "view":
|
|
586
|
+
if (isSchema25Projection(value)) {
|
|
587
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "defaults.view", {
|
|
588
|
+
line,
|
|
589
|
+
column: tokens[0].column,
|
|
590
|
+
});
|
|
591
|
+
}
|
|
574
592
|
section.system.defaults.view = parseProjectionValue(value, line, tokens[0].column);
|
|
575
593
|
return;
|
|
576
594
|
case "scale":
|
|
@@ -610,14 +628,36 @@ function applyAtlasField(section, indent, tokens, line) {
|
|
|
610
628
|
throw new WorldOrbitError(`Unknown atlas field "${tokens[0].value}"`, line, tokens[0].column);
|
|
611
629
|
}
|
|
612
630
|
function applyViewpointField(section, indent, tokens, line) {
|
|
631
|
+
if (section.inCamera && indent <= (section.cameraIndent ?? 0)) {
|
|
632
|
+
section.inCamera = false;
|
|
633
|
+
section.cameraIndent = null;
|
|
634
|
+
}
|
|
613
635
|
if (section.inFilter && indent <= (section.filterIndent ?? 0)) {
|
|
614
636
|
section.inFilter = false;
|
|
615
637
|
section.filterIndent = null;
|
|
616
638
|
}
|
|
639
|
+
if (section.inCamera) {
|
|
640
|
+
applyViewpointCameraField(section, tokens, line);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
617
643
|
if (section.inFilter) {
|
|
618
644
|
applyViewpointFilterField(section, tokens, line);
|
|
619
645
|
return;
|
|
620
646
|
}
|
|
647
|
+
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "camera") {
|
|
648
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.camera", {
|
|
649
|
+
line,
|
|
650
|
+
column: tokens[0].column,
|
|
651
|
+
});
|
|
652
|
+
if (section.seenFields.has("camera")) {
|
|
653
|
+
throw new WorldOrbitError('Duplicate viewpoint field "camera"', line, tokens[0].column);
|
|
654
|
+
}
|
|
655
|
+
section.seenFields.add("camera");
|
|
656
|
+
section.inCamera = true;
|
|
657
|
+
section.cameraIndent = indent;
|
|
658
|
+
section.viewpoint.camera = section.viewpoint.camera ?? createEmptyViewCamera();
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
621
661
|
if (tokens.length === 1 && tokens[0].value.toLowerCase() === "filter") {
|
|
622
662
|
if (section.seenFields.has("filter")) {
|
|
623
663
|
throw new WorldOrbitError('Duplicate viewpoint field "filter"', line, tokens[0].column);
|
|
@@ -643,6 +683,12 @@ function applyViewpointField(section, indent, tokens, line) {
|
|
|
643
683
|
section.viewpoint.selectedObjectId = value;
|
|
644
684
|
return;
|
|
645
685
|
case "projection":
|
|
686
|
+
if (isSchema25Projection(value)) {
|
|
687
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "projection", {
|
|
688
|
+
line,
|
|
689
|
+
column: tokens[0].column,
|
|
690
|
+
});
|
|
691
|
+
}
|
|
646
692
|
section.viewpoint.projection = parseProjectionValue(value, line, tokens[0].column);
|
|
647
693
|
return;
|
|
648
694
|
case "preset":
|
|
@@ -654,6 +700,13 @@ function applyViewpointField(section, indent, tokens, line) {
|
|
|
654
700
|
case "rotation":
|
|
655
701
|
section.viewpoint.rotationDeg = parseFiniteNumber(value, line, tokens[0].column, "rotation");
|
|
656
702
|
return;
|
|
703
|
+
case "camera":
|
|
704
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "viewpoint.camera", {
|
|
705
|
+
line,
|
|
706
|
+
column: tokens[0].column,
|
|
707
|
+
});
|
|
708
|
+
section.viewpoint.camera = parseInlineViewCamera(tokens.slice(1), line, section.viewpoint.camera);
|
|
709
|
+
return;
|
|
657
710
|
case "layers":
|
|
658
711
|
section.viewpoint.layers = parseLayerTokens(tokens.slice(1), line, section.sourceSchemaVersion, section.diagnostics);
|
|
659
712
|
return;
|
|
@@ -668,6 +721,28 @@ function applyViewpointField(section, indent, tokens, line) {
|
|
|
668
721
|
throw new WorldOrbitError(`Unknown viewpoint field "${tokens[0].value}"`, line, tokens[0].column);
|
|
669
722
|
}
|
|
670
723
|
}
|
|
724
|
+
function applyViewpointCameraField(section, tokens, line) {
|
|
725
|
+
const key = requireUniqueField(tokens, section.seenCameraFields, line);
|
|
726
|
+
const value = joinFieldValue(tokens, line);
|
|
727
|
+
const camera = section.viewpoint.camera ?? createEmptyViewCamera();
|
|
728
|
+
switch (key) {
|
|
729
|
+
case "azimuth":
|
|
730
|
+
camera.azimuth = parseFiniteNumber(value, line, tokens[0].column, "camera.azimuth");
|
|
731
|
+
break;
|
|
732
|
+
case "elevation":
|
|
733
|
+
camera.elevation = parseFiniteNumber(value, line, tokens[0].column, "camera.elevation");
|
|
734
|
+
break;
|
|
735
|
+
case "roll":
|
|
736
|
+
camera.roll = parseFiniteNumber(value, line, tokens[0].column, "camera.roll");
|
|
737
|
+
break;
|
|
738
|
+
case "distance":
|
|
739
|
+
camera.distance = parsePositiveNumber(value, line, tokens[0].column, "camera.distance");
|
|
740
|
+
break;
|
|
741
|
+
default:
|
|
742
|
+
throw new WorldOrbitError(`Unknown viewpoint camera field "${tokens[0].value}"`, line, tokens[0].column);
|
|
743
|
+
}
|
|
744
|
+
section.viewpoint.camera = camera;
|
|
745
|
+
}
|
|
671
746
|
function applyViewpointFilterField(section, tokens, line) {
|
|
672
747
|
const key = requireUniqueField(tokens, section.seenFilterFields, line);
|
|
673
748
|
const filter = section.viewpoint.filter ?? createEmptyViewpointFilter();
|
|
@@ -778,6 +853,13 @@ function applyEventField(section, indent, tokens, line) {
|
|
|
778
853
|
section.positionsIndent = null;
|
|
779
854
|
}
|
|
780
855
|
if (section.activePose) {
|
|
856
|
+
if (tokens[0]?.value === "epoch" ||
|
|
857
|
+
tokens[0]?.value === "referencePlane") {
|
|
858
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, `pose.${tokens[0].value}`, {
|
|
859
|
+
line,
|
|
860
|
+
column: tokens[0]?.column ?? 1,
|
|
861
|
+
});
|
|
862
|
+
}
|
|
781
863
|
section.activePose.fields.push(parseEventPoseField(tokens, line, section.activePoseSeenFields));
|
|
782
864
|
return;
|
|
783
865
|
}
|
|
@@ -832,6 +914,20 @@ function applyEventField(section, indent, tokens, line) {
|
|
|
832
914
|
case "visibility":
|
|
833
915
|
section.event.visibility = joinFieldValue(tokens, line);
|
|
834
916
|
return;
|
|
917
|
+
case "epoch":
|
|
918
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "event.epoch", {
|
|
919
|
+
line,
|
|
920
|
+
column: tokens[0].column,
|
|
921
|
+
});
|
|
922
|
+
section.event.epoch = joinFieldValue(tokens, line);
|
|
923
|
+
return;
|
|
924
|
+
case "referenceplane":
|
|
925
|
+
warnIfSchema25Feature(section.sourceSchemaVersion, section.diagnostics, "event.referencePlane", {
|
|
926
|
+
line,
|
|
927
|
+
column: tokens[0].column,
|
|
928
|
+
});
|
|
929
|
+
section.event.referencePlane = joinFieldValue(tokens, line);
|
|
930
|
+
return;
|
|
835
931
|
case "tags":
|
|
836
932
|
section.event.tags = parseTokenList(tokens.slice(1), line, "tags");
|
|
837
933
|
return;
|
|
@@ -981,11 +1077,18 @@ function parseTokenList(tokens, line, fieldName) {
|
|
|
981
1077
|
}
|
|
982
1078
|
function parseProjectionValue(value, line, column) {
|
|
983
1079
|
const normalized = value.toLowerCase();
|
|
984
|
-
if (normalized !== "topdown" &&
|
|
1080
|
+
if (normalized !== "topdown" &&
|
|
1081
|
+
normalized !== "isometric" &&
|
|
1082
|
+
normalized !== "orthographic" &&
|
|
1083
|
+
normalized !== "perspective") {
|
|
985
1084
|
throw new WorldOrbitError(`Unknown projection "${value}"`, line, column);
|
|
986
1085
|
}
|
|
987
1086
|
return normalized;
|
|
988
1087
|
}
|
|
1088
|
+
function isSchema25Projection(value) {
|
|
1089
|
+
const normalized = value.toLowerCase();
|
|
1090
|
+
return normalized === "orthographic" || normalized === "perspective";
|
|
1091
|
+
}
|
|
989
1092
|
function parsePresetValue(value, line, column) {
|
|
990
1093
|
const normalized = value.toLowerCase();
|
|
991
1094
|
if (normalized === "diagram" ||
|
|
@@ -1018,6 +1121,48 @@ function createEmptyViewpointFilter() {
|
|
|
1018
1121
|
groupIds: [],
|
|
1019
1122
|
};
|
|
1020
1123
|
}
|
|
1124
|
+
function createEmptyViewCamera() {
|
|
1125
|
+
return {
|
|
1126
|
+
azimuth: null,
|
|
1127
|
+
elevation: null,
|
|
1128
|
+
roll: null,
|
|
1129
|
+
distance: null,
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
function parseInlineViewCamera(tokens, line, current) {
|
|
1133
|
+
if (tokens.length === 0 || tokens.length % 2 !== 0) {
|
|
1134
|
+
throw new WorldOrbitError('Field "camera" expects "<field> <value>" pairs', line, tokens[0]?.column ?? 1);
|
|
1135
|
+
}
|
|
1136
|
+
const camera = current ? { ...current } : createEmptyViewCamera();
|
|
1137
|
+
const seen = new Set();
|
|
1138
|
+
for (let index = 0; index < tokens.length; index += 2) {
|
|
1139
|
+
const fieldToken = tokens[index];
|
|
1140
|
+
const valueToken = tokens[index + 1];
|
|
1141
|
+
const key = fieldToken.value.toLowerCase();
|
|
1142
|
+
if (seen.has(key)) {
|
|
1143
|
+
throw new WorldOrbitError(`Duplicate viewpoint camera field "${fieldToken.value}"`, line, fieldToken.column);
|
|
1144
|
+
}
|
|
1145
|
+
seen.add(key);
|
|
1146
|
+
const value = valueToken.value;
|
|
1147
|
+
switch (key) {
|
|
1148
|
+
case "azimuth":
|
|
1149
|
+
camera.azimuth = parseFiniteNumber(value, line, fieldToken.column, "camera.azimuth");
|
|
1150
|
+
break;
|
|
1151
|
+
case "elevation":
|
|
1152
|
+
camera.elevation = parseFiniteNumber(value, line, fieldToken.column, "camera.elevation");
|
|
1153
|
+
break;
|
|
1154
|
+
case "roll":
|
|
1155
|
+
camera.roll = parseFiniteNumber(value, line, fieldToken.column, "camera.roll");
|
|
1156
|
+
break;
|
|
1157
|
+
case "distance":
|
|
1158
|
+
camera.distance = parsePositiveNumber(value, line, fieldToken.column, "camera.distance");
|
|
1159
|
+
break;
|
|
1160
|
+
default:
|
|
1161
|
+
throw new WorldOrbitError(`Unknown viewpoint camera field "${fieldToken.value}"`, line, fieldToken.column);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
return camera;
|
|
1165
|
+
}
|
|
1021
1166
|
function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion, diagnostics) {
|
|
1022
1167
|
const fields = [];
|
|
1023
1168
|
let index = 0;
|
|
@@ -1158,7 +1303,7 @@ function normalizeDraftObject(node, sourceSchemaVersion, diagnostics) {
|
|
|
1158
1303
|
object.tolerances = tolerances;
|
|
1159
1304
|
if (typedBlocks && Object.keys(typedBlocks).length > 0)
|
|
1160
1305
|
object.typedBlocks = typedBlocks;
|
|
1161
|
-
if (sourceSchemaVersion
|
|
1306
|
+
if (isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
1162
1307
|
if (object.groups ||
|
|
1163
1308
|
object.epoch ||
|
|
1164
1309
|
object.referencePlane ||
|
|
@@ -1184,23 +1329,25 @@ function normalizeDraftEvent(event, rawPoses) {
|
|
|
1184
1329
|
};
|
|
1185
1330
|
}
|
|
1186
1331
|
function normalizeDraftEventPose(rawPose) {
|
|
1187
|
-
const fieldMap = collectDraftFields(rawPose.fields);
|
|
1332
|
+
const fieldMap = collectDraftFields(rawPose.fields, "event-pose");
|
|
1188
1333
|
const placement = extractPlacementFromFieldMap(fieldMap);
|
|
1189
1334
|
return {
|
|
1190
1335
|
objectId: rawPose.objectId,
|
|
1191
1336
|
placement,
|
|
1192
1337
|
inner: parseOptionalUnitField(fieldMap.get("inner")?.[0], "inner"),
|
|
1193
1338
|
outer: parseOptionalUnitField(fieldMap.get("outer")?.[0], "outer"),
|
|
1339
|
+
epoch: parseOptionalJoinedValue(fieldMap.get("epoch")?.[0]),
|
|
1340
|
+
referencePlane: parseOptionalJoinedValue(fieldMap.get("referencePlane")?.[0]),
|
|
1194
1341
|
};
|
|
1195
1342
|
}
|
|
1196
|
-
function collectDraftFields(fields) {
|
|
1343
|
+
function collectDraftFields(fields, _mode = "object") {
|
|
1197
1344
|
const grouped = new Map();
|
|
1198
1345
|
for (const field of fields) {
|
|
1199
1346
|
const spec = getDraftObjectFieldSpec(field.key);
|
|
1200
|
-
if (!spec) {
|
|
1347
|
+
if (!spec && !EVENT_POSE_FIELD_KEYS.has(field.key)) {
|
|
1201
1348
|
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
1202
1349
|
}
|
|
1203
|
-
if (!spec
|
|
1350
|
+
if (!spec?.allowRepeat && grouped.has(field.key)) {
|
|
1204
1351
|
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
1205
1352
|
}
|
|
1206
1353
|
const existing = grouped.get(field.key) ?? [];
|
|
@@ -1378,7 +1525,7 @@ function validateDraftObjectFieldCompatibility(fields, objectType) {
|
|
|
1378
1525
|
}
|
|
1379
1526
|
}
|
|
1380
1527
|
function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
1381
|
-
if (sourceSchemaVersion
|
|
1528
|
+
if (!isSchemaOlderThan(sourceSchemaVersion, "2.1")) {
|
|
1382
1529
|
return;
|
|
1383
1530
|
}
|
|
1384
1531
|
diagnostics.push({
|
|
@@ -1390,6 +1537,34 @@ function warnIfSchema21Feature(sourceSchemaVersion, diagnostics, featureName, lo
|
|
|
1390
1537
|
column: location.column,
|
|
1391
1538
|
});
|
|
1392
1539
|
}
|
|
1540
|
+
function warnIfSchema25Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
1541
|
+
if (!isSchemaOlderThan(sourceSchemaVersion, "2.5")) {
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
diagnostics.push({
|
|
1545
|
+
code: "parse.schema25.featureCompatibility",
|
|
1546
|
+
severity: "warning",
|
|
1547
|
+
source: "parse",
|
|
1548
|
+
message: `Feature "${featureName}" requires schema 2.5; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
1549
|
+
line: location.line,
|
|
1550
|
+
column: location.column,
|
|
1551
|
+
});
|
|
1552
|
+
}
|
|
1553
|
+
function isSchemaOlderThan(sourceSchemaVersion, requiredVersion) {
|
|
1554
|
+
return schemaVersionRank(sourceSchemaVersion) < schemaVersionRank(requiredVersion);
|
|
1555
|
+
}
|
|
1556
|
+
function schemaVersionRank(version) {
|
|
1557
|
+
switch (version) {
|
|
1558
|
+
case "2.0-draft":
|
|
1559
|
+
return 0;
|
|
1560
|
+
case "2.0":
|
|
1561
|
+
return 1;
|
|
1562
|
+
case "2.1":
|
|
1563
|
+
return 2;
|
|
1564
|
+
case "2.5":
|
|
1565
|
+
return 3;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1393
1568
|
function preprocessAtlasSource(source) {
|
|
1394
1569
|
const chars = [...source];
|
|
1395
1570
|
const comments = [];
|
|
@@ -18,8 +18,8 @@ export function upgradeDocumentToV2(document, options = {}) {
|
|
|
18
18
|
}
|
|
19
19
|
return {
|
|
20
20
|
format: "worldorbit",
|
|
21
|
-
version: "2.
|
|
22
|
-
schemaVersion: "2.
|
|
21
|
+
version: "2.5",
|
|
22
|
+
schemaVersion: "2.5",
|
|
23
23
|
sourceVersion: document.version,
|
|
24
24
|
system,
|
|
25
25
|
groups: structuredClone(document.groups ?? []),
|
|
@@ -82,10 +82,15 @@ function createDraftSystem(document, defaults, atlasMetadata, annotations, diagn
|
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
function createDraftDefaults(document, preset, projection) {
|
|
85
|
+
const rawView = typeof document.system?.properties.view === "string"
|
|
86
|
+
? document.system.properties.view.toLowerCase()
|
|
87
|
+
: null;
|
|
85
88
|
return {
|
|
86
|
-
view:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
view: rawView === "topdown" ||
|
|
90
|
+
rawView === "isometric" ||
|
|
91
|
+
rawView === "orthographic" ||
|
|
92
|
+
rawView === "perspective"
|
|
93
|
+
? rawView
|
|
89
94
|
: projection,
|
|
90
95
|
scale: typeof document.system?.properties.scale === "string"
|
|
91
96
|
? document.system.properties.scale
|
|
@@ -204,6 +209,7 @@ function mapSceneViewpointToDraftViewpoint(viewpoint) {
|
|
|
204
209
|
preset: viewpoint.preset,
|
|
205
210
|
zoom: viewpoint.scale,
|
|
206
211
|
rotationDeg: viewpoint.rotationDeg,
|
|
212
|
+
camera: viewpoint.camera ? { ...viewpoint.camera } : null,
|
|
207
213
|
layers: { ...viewpoint.layers },
|
|
208
214
|
filter: viewpoint.filter
|
|
209
215
|
? {
|
|
@@ -258,6 +264,8 @@ function cloneWorldOrbitEventPose(pose) {
|
|
|
258
264
|
placement: clonePlacement(pose.placement),
|
|
259
265
|
inner: pose.inner ? { ...pose.inner } : undefined,
|
|
260
266
|
outer: pose.outer ? { ...pose.outer } : undefined,
|
|
267
|
+
epoch: pose.epoch ?? null,
|
|
268
|
+
referencePlane: pose.referencePlane ?? null,
|
|
261
269
|
};
|
|
262
270
|
}
|
|
263
271
|
function clonePlacement(placement) {
|
|
@@ -272,23 +280,42 @@ function applyEventPoseOverrides(objects, events, activeEventId) {
|
|
|
272
280
|
return;
|
|
273
281
|
}
|
|
274
282
|
const objectMap = new Map(objects.map((object) => [object.id, object]));
|
|
283
|
+
const referencedIds = new Set([
|
|
284
|
+
...(event.targetObjectId ? [event.targetObjectId] : []),
|
|
285
|
+
...event.participantObjectIds,
|
|
286
|
+
...event.positions.map((pose) => pose.objectId),
|
|
287
|
+
]);
|
|
288
|
+
for (const objectId of referencedIds) {
|
|
289
|
+
const object = objectMap.get(objectId);
|
|
290
|
+
if (!object) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (event.epoch) {
|
|
294
|
+
object.epoch = event.epoch;
|
|
295
|
+
}
|
|
296
|
+
if (event.referencePlane) {
|
|
297
|
+
object.referencePlane = event.referencePlane;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
275
300
|
for (const pose of event.positions) {
|
|
276
301
|
const object = objectMap.get(pose.objectId);
|
|
277
302
|
if (!object) {
|
|
278
303
|
continue;
|
|
279
304
|
}
|
|
280
|
-
|
|
305
|
+
if (pose.placement) {
|
|
306
|
+
object.placement = clonePlacement(pose.placement);
|
|
307
|
+
}
|
|
281
308
|
if (pose.inner) {
|
|
282
309
|
object.properties.inner = { ...pose.inner };
|
|
283
310
|
}
|
|
284
|
-
else {
|
|
285
|
-
delete object.properties.inner;
|
|
286
|
-
}
|
|
287
311
|
if (pose.outer) {
|
|
288
312
|
object.properties.outer = { ...pose.outer };
|
|
289
313
|
}
|
|
290
|
-
|
|
291
|
-
|
|
314
|
+
if (pose.epoch) {
|
|
315
|
+
object.epoch = pose.epoch;
|
|
316
|
+
}
|
|
317
|
+
if (pose.referencePlane) {
|
|
318
|
+
object.referencePlane = pose.referencePlane;
|
|
292
319
|
}
|
|
293
320
|
}
|
|
294
321
|
}
|
|
@@ -384,6 +411,18 @@ function materializeDraftSystemInfo(system) {
|
|
|
384
411
|
if (viewpoint.rotationDeg !== 0) {
|
|
385
412
|
info[`${prefix}.rotation`] = String(viewpoint.rotationDeg);
|
|
386
413
|
}
|
|
414
|
+
if (viewpoint.camera?.azimuth !== null) {
|
|
415
|
+
info[`${prefix}.camera.azimuth`] = String(viewpoint.camera?.azimuth);
|
|
416
|
+
}
|
|
417
|
+
if (viewpoint.camera?.elevation !== null) {
|
|
418
|
+
info[`${prefix}.camera.elevation`] = String(viewpoint.camera?.elevation);
|
|
419
|
+
}
|
|
420
|
+
if (viewpoint.camera?.roll !== null) {
|
|
421
|
+
info[`${prefix}.camera.roll`] = String(viewpoint.camera?.roll);
|
|
422
|
+
}
|
|
423
|
+
if (viewpoint.camera?.distance !== null) {
|
|
424
|
+
info[`${prefix}.camera.distance`] = String(viewpoint.camera?.distance);
|
|
425
|
+
}
|
|
387
426
|
const serializedLayers = serializeViewpointLayers(viewpoint.layers);
|
|
388
427
|
if (serializedLayers) {
|
|
389
428
|
info[`${prefix}.layers`] = serializedLayers;
|