worldorbit 3.2.1 → 4.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 +546 -545
- package/dist/browser/core/dist/atlas-edit.js +146 -1
- package/dist/browser/core/dist/atlas-validate.js +105 -10
- package/dist/browser/core/dist/draft-parse.js +341 -16
- package/dist/browser/core/dist/draft.d.ts +2 -1
- package/dist/browser/core/dist/draft.js +25 -3
- package/dist/browser/core/dist/format.js +86 -4
- package/dist/browser/core/dist/index.d.ts +1 -0
- package/dist/browser/core/dist/index.js +1 -0
- package/dist/browser/core/dist/load.js +7 -2
- package/dist/browser/core/dist/normalize.js +1 -0
- package/dist/browser/core/dist/scene.js +11 -2
- package/dist/browser/core/dist/schema.js +11 -1
- package/dist/browser/core/dist/solver.d.ts +26 -0
- package/dist/browser/core/dist/solver.js +27 -0
- package/dist/browser/core/dist/types.d.ts +57 -3
- package/dist/browser/editor/dist/editor.js +844 -719
- package/dist/browser/editor/dist/types.d.ts +2 -1
- package/dist/browser/obsidian-plugin/dist/diagnostics.d.ts +3 -0
- package/dist/browser/obsidian-plugin/dist/diagnostics.js +23 -0
- package/dist/browser/obsidian-plugin/dist/examples.d.ts +3 -0
- package/dist/browser/obsidian-plugin/dist/examples.js +77 -0
- package/dist/browser/obsidian-plugin/dist/index.d.ts +9 -0
- package/dist/browser/obsidian-plugin/dist/index.js +8 -0
- package/dist/browser/obsidian-plugin/dist/main.d.ts +2 -0
- package/dist/browser/obsidian-plugin/dist/main.js +2 -0
- package/dist/browser/obsidian-plugin/dist/navigation.d.ts +6 -0
- package/dist/browser/obsidian-plugin/dist/navigation.js +44 -0
- package/dist/browser/obsidian-plugin/dist/plugin.d.ts +8 -0
- package/dist/browser/obsidian-plugin/dist/plugin.js +508 -0
- package/dist/browser/obsidian-plugin/dist/positions.d.ts +7 -0
- package/dist/browser/obsidian-plugin/dist/positions.js +14 -0
- package/dist/browser/obsidian-plugin/dist/settings.d.ts +2 -0
- package/dist/browser/obsidian-plugin/dist/settings.js +5 -0
- package/dist/browser/obsidian-plugin/dist/theme.d.ts +2 -0
- package/dist/browser/obsidian-plugin/dist/theme.js +31 -0
- package/dist/browser/obsidian-plugin/dist/types.d.ts +42 -0
- package/dist/browser/obsidian-plugin/dist/types.js +1 -0
- package/dist/browser/obsidian-plugin/dist/viewer-host.d.ts +14 -0
- package/dist/browser/obsidian-plugin/dist/viewer-host.js +110 -0
- package/dist/browser/viewer/dist/index.d.ts +1 -0
- package/dist/browser/viewer/dist/index.js +1 -0
- package/dist/browser/viewer/dist/interactive-2d.d.ts +21 -0
- package/dist/browser/viewer/dist/interactive-2d.js +201 -0
- package/dist/browser/viewer/dist/minimap.js +9 -7
- package/dist/browser/viewer/dist/render.d.ts +1 -1
- package/dist/browser/viewer/dist/render.js +25 -20
- package/dist/browser/viewer/dist/runtime-3d.js +2 -0
- package/dist/browser/viewer/dist/viewer-state.d.ts +1 -1
- package/dist/browser/viewer/dist/viewer-state.js +1 -1
- package/dist/browser/viewer/dist/viewer.js +7 -3
- package/dist/obsidian-plugin/LICENSE +21 -0
- package/dist/obsidian-plugin/README.md +141 -0
- package/dist/obsidian-plugin/main.js +108 -0
- package/dist/obsidian-plugin/manifest.json +9 -0
- package/dist/obsidian-plugin/obsidian_scsh_1.png +0 -0
- package/dist/obsidian-plugin/obsidian_scsh_2.png +0 -0
- package/dist/obsidian-plugin/styles.css +164 -0
- package/dist/unpkg/core/dist/atlas-edit.js +146 -1
- package/dist/unpkg/core/dist/atlas-validate.js +105 -10
- package/dist/unpkg/core/dist/draft-parse.js +341 -16
- package/dist/unpkg/core/dist/draft.d.ts +2 -1
- package/dist/unpkg/core/dist/draft.js +25 -3
- package/dist/unpkg/core/dist/format.js +86 -4
- package/dist/unpkg/core/dist/index.d.ts +1 -0
- package/dist/unpkg/core/dist/index.js +1 -0
- package/dist/unpkg/core/dist/load.js +7 -2
- package/dist/unpkg/core/dist/normalize.js +1 -0
- package/dist/unpkg/core/dist/scene.js +11 -2
- package/dist/unpkg/core/dist/schema.js +11 -1
- package/dist/unpkg/core/dist/solver.d.ts +26 -0
- package/dist/unpkg/core/dist/solver.js +27 -0
- package/dist/unpkg/core/dist/types.d.ts +57 -3
- package/dist/unpkg/editor/dist/editor.js +844 -719
- package/dist/unpkg/editor/dist/types.d.ts +2 -1
- package/dist/unpkg/obsidian-plugin/dist/diagnostics.d.ts +3 -0
- package/dist/unpkg/obsidian-plugin/dist/diagnostics.js +23 -0
- package/dist/unpkg/obsidian-plugin/dist/examples.d.ts +3 -0
- package/dist/unpkg/obsidian-plugin/dist/examples.js +77 -0
- package/dist/unpkg/obsidian-plugin/dist/index.d.ts +9 -0
- package/dist/unpkg/obsidian-plugin/dist/index.js +8 -0
- package/dist/unpkg/obsidian-plugin/dist/main.d.ts +2 -0
- package/dist/unpkg/obsidian-plugin/dist/main.js +2 -0
- package/dist/unpkg/obsidian-plugin/dist/navigation.d.ts +6 -0
- package/dist/unpkg/obsidian-plugin/dist/navigation.js +44 -0
- package/dist/unpkg/obsidian-plugin/dist/plugin.d.ts +8 -0
- package/dist/unpkg/obsidian-plugin/dist/plugin.js +508 -0
- package/dist/unpkg/obsidian-plugin/dist/positions.d.ts +7 -0
- package/dist/unpkg/obsidian-plugin/dist/positions.js +14 -0
- package/dist/unpkg/obsidian-plugin/dist/settings.d.ts +2 -0
- package/dist/unpkg/obsidian-plugin/dist/settings.js +5 -0
- package/dist/unpkg/obsidian-plugin/dist/theme.d.ts +2 -0
- package/dist/unpkg/obsidian-plugin/dist/theme.js +31 -0
- package/dist/unpkg/obsidian-plugin/dist/types.d.ts +42 -0
- package/dist/unpkg/obsidian-plugin/dist/types.js +1 -0
- package/dist/unpkg/obsidian-plugin/dist/viewer-host.d.ts +14 -0
- package/dist/unpkg/obsidian-plugin/dist/viewer-host.js +110 -0
- package/dist/unpkg/viewer/dist/index.d.ts +1 -0
- package/dist/unpkg/viewer/dist/index.js +1 -0
- package/dist/unpkg/viewer/dist/interactive-2d.d.ts +21 -0
- package/dist/unpkg/viewer/dist/interactive-2d.js +201 -0
- package/dist/unpkg/viewer/dist/minimap.js +9 -7
- package/dist/unpkg/viewer/dist/render.d.ts +1 -1
- package/dist/unpkg/viewer/dist/render.js +25 -20
- package/dist/unpkg/viewer/dist/runtime-3d.js +2 -0
- package/dist/unpkg/viewer/dist/viewer-state.d.ts +1 -1
- package/dist/unpkg/viewer/dist/viewer-state.js +1 -1
- package/dist/unpkg/viewer/dist/viewer.js +7 -3
- package/dist/unpkg/worldorbit-core.min.js +10 -10
- package/dist/unpkg/worldorbit-editor.min.js +359 -332
- package/dist/unpkg/worldorbit-markdown.min.js +28 -28
- package/dist/unpkg/worldorbit-viewer.min.js +203 -203
- package/dist/unpkg/worldorbit.js +958 -40
- package/dist/unpkg/worldorbit.min.js +214 -214
- package/package.json +22 -1
- package/packages/core/dist/atlas-edit.js +146 -1
- package/packages/core/dist/atlas-validate.js +105 -10
- package/packages/core/dist/draft-parse.js +341 -16
- package/packages/core/dist/draft.d.ts +2 -1
- package/packages/core/dist/draft.js +25 -3
- package/packages/core/dist/format.js +86 -4
- package/packages/core/dist/index.d.ts +1 -0
- package/packages/core/dist/index.js +1 -0
- package/packages/core/dist/load.js +7 -2
- package/packages/core/dist/normalize.js +1 -0
- package/packages/core/dist/scene.js +11 -2
- package/packages/core/dist/schema.js +11 -1
- package/packages/core/dist/solver.d.ts +26 -0
- package/packages/core/dist/solver.js +27 -0
- package/packages/core/dist/types.d.ts +57 -3
- package/packages/editor/dist/editor.js +844 -719
- package/packages/editor/dist/types.d.ts +2 -1
- package/packages/viewer/dist/index.d.ts +1 -0
- package/packages/viewer/dist/index.js +1 -0
- package/packages/viewer/dist/interactive-2d.d.ts +21 -0
- package/packages/viewer/dist/interactive-2d.js +201 -0
- package/packages/viewer/dist/minimap.js +9 -7
- package/packages/viewer/dist/render.d.ts +1 -1
- package/packages/viewer/dist/render.js +25 -20
- package/packages/viewer/dist/runtime-3d.js +2 -0
- package/packages/viewer/dist/viewer-state.d.ts +1 -1
- package/packages/viewer/dist/viewer-state.js +1 -1
- package/packages/viewer/dist/viewer.js +7 -3
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
.worldorbit-obsidian-block {
|
|
2
|
+
border: 1px solid var(--background-modifier-border);
|
|
3
|
+
border-radius: 14px;
|
|
4
|
+
background: var(--background-primary);
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.worldorbit-obsidian-toolbar {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: 0.5rem;
|
|
12
|
+
flex-wrap: wrap;
|
|
13
|
+
padding: 0.75rem 0.9rem;
|
|
14
|
+
border-bottom: 1px solid var(--background-modifier-border);
|
|
15
|
+
background: var(--background-secondary);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.worldorbit-obsidian-status {
|
|
19
|
+
font-size: var(--font-ui-smaller);
|
|
20
|
+
color: var(--text-muted);
|
|
21
|
+
margin-right: auto;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.worldorbit-obsidian-actions {
|
|
25
|
+
display: inline-flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
gap: 0.5rem;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.worldorbit-obsidian-host {
|
|
31
|
+
position: relative;
|
|
32
|
+
min-height: 280px;
|
|
33
|
+
background:
|
|
34
|
+
radial-gradient(circle at top, color-mix(in srgb, var(--interactive-accent) 12%, transparent), transparent 48%),
|
|
35
|
+
linear-gradient(180deg, var(--background-primary), var(--background-secondary));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.worldorbit-obsidian-host.is-locked {
|
|
39
|
+
cursor: default;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.worldorbit-obsidian-host .wo-viewer-container,
|
|
43
|
+
.worldorbit-obsidian-host svg {
|
|
44
|
+
display: block;
|
|
45
|
+
width: 100%;
|
|
46
|
+
height: auto;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.worldorbit-obsidian-empty {
|
|
50
|
+
padding: 1rem;
|
|
51
|
+
color: var(--text-muted);
|
|
52
|
+
font-size: var(--font-ui-smaller);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.worldorbit-obsidian-example {
|
|
56
|
+
margin: 0 1rem 1rem;
|
|
57
|
+
padding: 0.9rem 1rem;
|
|
58
|
+
border: 1px solid var(--background-modifier-border);
|
|
59
|
+
border-radius: 12px;
|
|
60
|
+
background: var(--background-secondary);
|
|
61
|
+
color: var(--text-normal);
|
|
62
|
+
font-size: var(--font-ui-smaller);
|
|
63
|
+
overflow: auto;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.worldorbit-obsidian-diagnostics {
|
|
67
|
+
display: grid;
|
|
68
|
+
gap: 0.6rem;
|
|
69
|
+
padding: 0.9rem;
|
|
70
|
+
border-top: 1px solid var(--background-modifier-border);
|
|
71
|
+
background: var(--background-secondary);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.worldorbit-obsidian-diagnostic {
|
|
75
|
+
padding: 0.75rem 0.85rem;
|
|
76
|
+
border: 1px solid var(--background-modifier-border);
|
|
77
|
+
border-radius: 12px;
|
|
78
|
+
background: var(--background-primary);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.worldorbit-obsidian-diagnostic[data-severity="error"] {
|
|
82
|
+
border-color: color-mix(in srgb, var(--color-red) 45%, var(--background-modifier-border));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.worldorbit-obsidian-diagnostic[data-severity="warning"] {
|
|
86
|
+
border-color: color-mix(in srgb, var(--color-orange) 45%, var(--background-modifier-border));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.worldorbit-obsidian-diagnostic-meta {
|
|
90
|
+
display: flex;
|
|
91
|
+
gap: 0.55rem;
|
|
92
|
+
align-items: center;
|
|
93
|
+
flex-wrap: wrap;
|
|
94
|
+
margin-bottom: 0.4rem;
|
|
95
|
+
font-size: var(--font-ui-smaller);
|
|
96
|
+
color: var(--text-muted);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.worldorbit-obsidian-diagnostic-badge {
|
|
100
|
+
padding: 0.16rem 0.5rem;
|
|
101
|
+
border-radius: 999px;
|
|
102
|
+
background: var(--background-modifier-hover);
|
|
103
|
+
color: var(--text-normal);
|
|
104
|
+
text-transform: uppercase;
|
|
105
|
+
letter-spacing: 0.04em;
|
|
106
|
+
font-size: 0.72rem;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.worldorbit-obsidian-diagnostic-message {
|
|
110
|
+
color: var(--text-normal);
|
|
111
|
+
line-height: 1.45;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.worldorbit-obsidian-diagnostic-actions {
|
|
115
|
+
margin-top: 0.65rem;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.worldorbit-obsidian-button {
|
|
119
|
+
padding: 0.35rem 0.75rem;
|
|
120
|
+
border: 1px solid var(--background-modifier-border);
|
|
121
|
+
border-radius: 999px;
|
|
122
|
+
background: var(--background-primary-alt, var(--background-primary));
|
|
123
|
+
color: var(--text-normal);
|
|
124
|
+
font-size: var(--font-ui-smaller);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.worldorbit-obsidian-button.mod-cta {
|
|
128
|
+
background: var(--interactive-accent);
|
|
129
|
+
border-color: var(--interactive-accent);
|
|
130
|
+
color: var(--text-on-accent, white);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.worldorbit-obsidian-button[disabled] {
|
|
134
|
+
opacity: 0.55;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.worldorbit-obsidian-icon-button {
|
|
138
|
+
display: inline-flex;
|
|
139
|
+
align-items: center;
|
|
140
|
+
justify-content: center;
|
|
141
|
+
width: 2rem;
|
|
142
|
+
min-width: 2rem;
|
|
143
|
+
height: 2rem;
|
|
144
|
+
padding: 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.worldorbit-obsidian-help {
|
|
148
|
+
color: var(--text-normal);
|
|
149
|
+
line-height: 1.6;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.worldorbit-obsidian-modal {
|
|
153
|
+
min-height: 60vh;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.worldorbit-obsidian-modal-host {
|
|
157
|
+
min-height: 60vh;
|
|
158
|
+
border: 1px solid var(--background-modifier-border);
|
|
159
|
+
border-radius: 16px;
|
|
160
|
+
overflow: hidden;
|
|
161
|
+
background:
|
|
162
|
+
radial-gradient(circle at top, color-mix(in srgb, var(--interactive-accent) 12%, transparent), transparent 48%),
|
|
163
|
+
linear-gradient(180deg, var(--background-primary), var(--background-secondary));
|
|
164
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { collectAtlasDiagnostics } from "./atlas-validate.js";
|
|
2
|
-
export function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "
|
|
2
|
+
export function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "3.0") {
|
|
3
3
|
return {
|
|
4
4
|
format: "worldorbit",
|
|
5
5
|
version,
|
|
@@ -30,6 +30,7 @@ export function createEmptyAtlasDocument(systemId = "WorldOrbit", version = "2.6
|
|
|
30
30
|
groups: [],
|
|
31
31
|
relations: [],
|
|
32
32
|
events: [],
|
|
33
|
+
trajectories: [],
|
|
33
34
|
objects: [],
|
|
34
35
|
diagnostics: [],
|
|
35
36
|
};
|
|
@@ -62,6 +63,19 @@ export function listAtlasDocumentPaths(document) {
|
|
|
62
63
|
paths.push({ kind: "event-pose", id: event.id, key: pose.objectId });
|
|
63
64
|
}
|
|
64
65
|
}
|
|
66
|
+
for (const trajectory of [...document.trajectories].sort(compareIdLike)) {
|
|
67
|
+
paths.push({ kind: "trajectory", id: trajectory.id });
|
|
68
|
+
for (const segment of [...trajectory.segments].sort(compareIdLike)) {
|
|
69
|
+
paths.push({ kind: "trajectory-segment", id: trajectory.id, key: segment.id });
|
|
70
|
+
for (const maneuver of [...segment.maneuvers].sort(compareIdLike)) {
|
|
71
|
+
paths.push({
|
|
72
|
+
kind: "trajectory-maneuver",
|
|
73
|
+
id: trajectory.id,
|
|
74
|
+
key: `${segment.id}:${maneuver.id}`,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
65
79
|
for (const object of [...document.objects].sort(compareIdLike)) {
|
|
66
80
|
paths.push({ kind: "object", id: object.id });
|
|
67
81
|
}
|
|
@@ -81,6 +95,12 @@ export function getAtlasDocumentNode(document, path) {
|
|
|
81
95
|
return path.id ? findEvent(document, path.id) : null;
|
|
82
96
|
case "event-pose":
|
|
83
97
|
return path.id && path.key ? findEventPose(document, path.id, path.key) : null;
|
|
98
|
+
case "trajectory":
|
|
99
|
+
return path.id ? findTrajectory(document, path.id) : null;
|
|
100
|
+
case "trajectory-segment":
|
|
101
|
+
return path.id && path.key ? findTrajectorySegment(document, path.id, path.key) : null;
|
|
102
|
+
case "trajectory-maneuver":
|
|
103
|
+
return path.id && path.key ? findTrajectoryManeuver(document, path.id, path.key) : null;
|
|
84
104
|
case "object":
|
|
85
105
|
return path.id ? findObject(document, path.id) : null;
|
|
86
106
|
case "viewpoint":
|
|
@@ -133,6 +153,24 @@ export function upsertAtlasDocumentNode(document, path, value) {
|
|
|
133
153
|
}
|
|
134
154
|
upsertEventPose(next.events, path.id, value);
|
|
135
155
|
return next;
|
|
156
|
+
case "trajectory":
|
|
157
|
+
if (!path.id) {
|
|
158
|
+
throw new Error('Trajectory updates require an "id" value.');
|
|
159
|
+
}
|
|
160
|
+
upsertById(next.trajectories, value);
|
|
161
|
+
return next;
|
|
162
|
+
case "trajectory-segment":
|
|
163
|
+
if (!path.id || !path.key) {
|
|
164
|
+
throw new Error('Trajectory segment updates require a trajectory "id" and segment "key" value.');
|
|
165
|
+
}
|
|
166
|
+
upsertTrajectorySegment(next.trajectories, path.id, value);
|
|
167
|
+
return next;
|
|
168
|
+
case "trajectory-maneuver":
|
|
169
|
+
if (!path.id || !path.key) {
|
|
170
|
+
throw new Error('Trajectory maneuver updates require a trajectory "id" and maneuver "key" value.');
|
|
171
|
+
}
|
|
172
|
+
upsertTrajectoryManeuver(next.trajectories, path.id, path.key, value);
|
|
173
|
+
return next;
|
|
136
174
|
case "object":
|
|
137
175
|
if (!path.id) {
|
|
138
176
|
throw new Error('Object updates require an "id" value.');
|
|
@@ -194,6 +232,30 @@ export function removeAtlasDocumentNode(document, path) {
|
|
|
194
232
|
}
|
|
195
233
|
}
|
|
196
234
|
return next;
|
|
235
|
+
case "trajectory":
|
|
236
|
+
if (path.id) {
|
|
237
|
+
next.trajectories = next.trajectories.filter((trajectory) => trajectory.id !== path.id);
|
|
238
|
+
}
|
|
239
|
+
return next;
|
|
240
|
+
case "trajectory-segment":
|
|
241
|
+
if (path.id && path.key) {
|
|
242
|
+
const trajectory = findTrajectory(next, path.id);
|
|
243
|
+
if (trajectory) {
|
|
244
|
+
trajectory.segments = trajectory.segments.filter((segment) => segment.id !== path.key);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return next;
|
|
248
|
+
case "trajectory-maneuver":
|
|
249
|
+
if (path.id && path.key) {
|
|
250
|
+
const maneuver = splitTrajectoryManeuverKey(path.key);
|
|
251
|
+
if (maneuver) {
|
|
252
|
+
const segment = findTrajectorySegment(next, path.id, maneuver.segmentId);
|
|
253
|
+
if (segment) {
|
|
254
|
+
segment.maneuvers = segment.maneuvers.filter((entry) => entry.id !== maneuver.maneuverId);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return next;
|
|
197
259
|
case "viewpoint":
|
|
198
260
|
if (path.id) {
|
|
199
261
|
system.viewpoints = system.viewpoints.filter((viewpoint) => viewpoint.id !== path.id);
|
|
@@ -278,6 +340,31 @@ export function resolveAtlasDiagnosticPath(document, diagnostic) {
|
|
|
278
340
|
};
|
|
279
341
|
}
|
|
280
342
|
}
|
|
343
|
+
if (diagnostic.field?.startsWith("trajectory.")) {
|
|
344
|
+
const parts = diagnostic.field.split(".");
|
|
345
|
+
if (parts[1] && findTrajectory(document, parts[1])) {
|
|
346
|
+
if (parts[2] === "segment" && parts[3] && findTrajectorySegment(document, parts[1], parts[3])) {
|
|
347
|
+
if (parts[4] === "maneuver" &&
|
|
348
|
+
parts[5] &&
|
|
349
|
+
findTrajectoryManeuver(document, parts[1], `${parts[3]}:${parts[5]}`)) {
|
|
350
|
+
return {
|
|
351
|
+
kind: "trajectory-maneuver",
|
|
352
|
+
id: parts[1],
|
|
353
|
+
key: `${parts[3]}:${parts[5]}`,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
return {
|
|
357
|
+
kind: "trajectory-segment",
|
|
358
|
+
id: parts[1],
|
|
359
|
+
key: parts[3],
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
kind: "trajectory",
|
|
364
|
+
id: parts[1],
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
}
|
|
281
368
|
if (diagnostic.field && diagnostic.field in ensureSystem(document).atlasMetadata) {
|
|
282
369
|
return {
|
|
283
370
|
kind: "metadata",
|
|
@@ -315,6 +402,20 @@ function findEvent(document, eventId) {
|
|
|
315
402
|
function findEventPose(document, eventId, objectId) {
|
|
316
403
|
return findEvent(document, eventId)?.positions.find((pose) => pose.objectId === objectId) ?? null;
|
|
317
404
|
}
|
|
405
|
+
function findTrajectory(document, trajectoryId) {
|
|
406
|
+
return document.trajectories.find((trajectory) => trajectory.id === trajectoryId) ?? null;
|
|
407
|
+
}
|
|
408
|
+
function findTrajectorySegment(document, trajectoryId, segmentId) {
|
|
409
|
+
return findTrajectory(document, trajectoryId)?.segments.find((segment) => segment.id === segmentId) ?? null;
|
|
410
|
+
}
|
|
411
|
+
function findTrajectoryManeuver(document, trajectoryId, combinedKey) {
|
|
412
|
+
const parsed = splitTrajectoryManeuverKey(combinedKey);
|
|
413
|
+
if (!parsed) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
return findTrajectorySegment(document, trajectoryId, parsed.segmentId)
|
|
417
|
+
?.maneuvers.find((maneuver) => maneuver.id === parsed.maneuverId) ?? null;
|
|
418
|
+
}
|
|
318
419
|
function findViewpoint(system, viewpointId) {
|
|
319
420
|
return system?.viewpoints.find((viewpoint) => viewpoint.id === viewpointId) ?? null;
|
|
320
421
|
}
|
|
@@ -343,6 +444,50 @@ function upsertEventPose(events, eventId, value) {
|
|
|
343
444
|
}
|
|
344
445
|
event.positions[index] = value;
|
|
345
446
|
}
|
|
447
|
+
function upsertTrajectorySegment(trajectories, trajectoryId, value) {
|
|
448
|
+
const trajectory = trajectories.find((entry) => entry.id === trajectoryId);
|
|
449
|
+
if (!trajectory) {
|
|
450
|
+
throw new Error(`Unknown trajectory "${trajectoryId}" for segment update.`);
|
|
451
|
+
}
|
|
452
|
+
const index = trajectory.segments.findIndex((entry) => entry.id === value.id);
|
|
453
|
+
if (index === -1) {
|
|
454
|
+
trajectory.segments.push(value);
|
|
455
|
+
trajectory.segments.sort(compareIdLike);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
trajectory.segments[index] = value;
|
|
459
|
+
}
|
|
460
|
+
function upsertTrajectoryManeuver(trajectories, trajectoryId, combinedKey, value) {
|
|
461
|
+
const parsed = splitTrajectoryManeuverKey(combinedKey);
|
|
462
|
+
if (!parsed) {
|
|
463
|
+
throw new Error(`Invalid trajectory maneuver key "${combinedKey}".`);
|
|
464
|
+
}
|
|
465
|
+
const trajectory = trajectories.find((entry) => entry.id === trajectoryId);
|
|
466
|
+
if (!trajectory) {
|
|
467
|
+
throw new Error(`Unknown trajectory "${trajectoryId}" for maneuver update.`);
|
|
468
|
+
}
|
|
469
|
+
const segment = trajectory.segments.find((entry) => entry.id === parsed.segmentId);
|
|
470
|
+
if (!segment) {
|
|
471
|
+
throw new Error(`Unknown trajectory segment "${parsed.segmentId}" on "${trajectoryId}".`);
|
|
472
|
+
}
|
|
473
|
+
const index = segment.maneuvers.findIndex((entry) => entry.id === value.id);
|
|
474
|
+
if (index === -1) {
|
|
475
|
+
segment.maneuvers.push(value);
|
|
476
|
+
segment.maneuvers.sort(compareIdLike);
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
segment.maneuvers[index] = value;
|
|
480
|
+
}
|
|
481
|
+
function splitTrajectoryManeuverKey(key) {
|
|
482
|
+
const separator = key.indexOf(":");
|
|
483
|
+
if (separator <= 0 || separator >= key.length - 1) {
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
return {
|
|
487
|
+
segmentId: key.slice(0, separator),
|
|
488
|
+
maneuverId: key.slice(separator + 1),
|
|
489
|
+
};
|
|
490
|
+
}
|
|
346
491
|
function compareIdLike(left, right) {
|
|
347
492
|
return left.id.localeCompare(right.id);
|
|
348
493
|
}
|
|
@@ -12,6 +12,7 @@ export function collectAtlasDiagnostics(document, sourceSchemaVersion) {
|
|
|
12
12
|
const objectMap = new Map(document.objects.map((object) => [object.id, object]));
|
|
13
13
|
const groupIds = new Set(document.groups.map((group) => group.id));
|
|
14
14
|
const eventIds = new Set(document.events.map((event) => event.id));
|
|
15
|
+
const trajectoryMap = new Map(document.trajectories.map((trajectory) => [trajectory.id, trajectory]));
|
|
15
16
|
if (!document.system) {
|
|
16
17
|
diagnostics.push(error("validate.system.required", "Atlas documents must declare exactly one system."));
|
|
17
18
|
}
|
|
@@ -22,6 +23,7 @@ export function collectAtlasDiagnostics(document, sourceSchemaVersion) {
|
|
|
22
23
|
["annotation", document.system?.annotations.map((annotation) => annotation.id) ?? []],
|
|
23
24
|
["relation", document.relations.map((relation) => relation.id)],
|
|
24
25
|
["event", document.events.map((event) => event.id)],
|
|
26
|
+
["trajectory", document.trajectories.map((trajectory) => trajectory.id)],
|
|
25
27
|
["object", document.objects.map((object) => object.id)],
|
|
26
28
|
]) {
|
|
27
29
|
for (const id of ids) {
|
|
@@ -41,10 +43,13 @@ export function collectAtlasDiagnostics(document, sourceSchemaVersion) {
|
|
|
41
43
|
validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, diagnostics, objectMap);
|
|
42
44
|
}
|
|
43
45
|
for (const object of document.objects) {
|
|
44
|
-
validateObject(object, document.system, objectMap, groupIds, diagnostics);
|
|
46
|
+
validateObject(object, document.system, objectMap, groupIds, trajectoryMap, diagnostics);
|
|
45
47
|
}
|
|
46
48
|
for (const event of document.events) {
|
|
47
|
-
validateEvent(event, document.system, objectMap, diagnostics);
|
|
49
|
+
validateEvent(event, document.system, objectMap, trajectoryMap, diagnostics);
|
|
50
|
+
}
|
|
51
|
+
for (const trajectory of document.trajectories) {
|
|
52
|
+
validateTrajectory(trajectory, objectMap, diagnostics);
|
|
48
53
|
}
|
|
49
54
|
return diagnostics;
|
|
50
55
|
}
|
|
@@ -84,7 +89,7 @@ function validateViewpoint(viewpoint, groupIds, eventIds, sourceSchemaVersion, d
|
|
|
84
89
|
validateProjection(viewpoint.projection, diagnostics, `viewpoint.${viewpoint.id}.projection`, viewpoint.id);
|
|
85
90
|
validateCamera(viewpoint.camera, viewpoint.projection, viewpoint.rotationDeg, diagnostics, viewpoint.id, viewpoint.focusObjectId, viewpoint.selectedObjectId, filter, objectMap);
|
|
86
91
|
}
|
|
87
|
-
function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
92
|
+
function validateObject(object, system, objectMap, groupIds, trajectoryMap, diagnostics) {
|
|
88
93
|
const placement = object.placement;
|
|
89
94
|
const orbitPlacement = placement?.mode === "orbit" ? placement : null;
|
|
90
95
|
const parentObject = placement?.mode === "orbit" ? objectMap.get(placement.target) ?? null : null;
|
|
@@ -101,6 +106,14 @@ function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
|
101
106
|
if (typeof object.referencePlane === "string" && !object.referencePlane.trim()) {
|
|
102
107
|
diagnostics.push(warn("validate.referencePlane.empty", `Object "${object.id}" defines an empty reference plane string.`, object.id, "referencePlane"));
|
|
103
108
|
}
|
|
109
|
+
if (object.trajectoryId) {
|
|
110
|
+
if (!trajectoryMap.has(object.trajectoryId)) {
|
|
111
|
+
diagnostics.push(error("validate.trajectory.object.unknown", `Unknown trajectory "${object.trajectoryId}" on "${object.id}".`, object.id, "trajectory"));
|
|
112
|
+
}
|
|
113
|
+
else if (!isTrajectoryCapableObject(object)) {
|
|
114
|
+
diagnostics.push(error("validate.trajectory.object.invalidType", `Only craft or legacy ship-like structures may reference trajectories; found "${object.type}" on "${object.id}".`, object.id, "trajectory"));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
104
117
|
if (orbitPlacement) {
|
|
105
118
|
if (!objectMap.has(orbitPlacement.target)) {
|
|
106
119
|
diagnostics.push(error("validate.orbit.target.unknown", `Unknown placement target "${orbitPlacement.target}" on "${object.id}".`, object.id, "orbit"));
|
|
@@ -128,8 +141,8 @@ function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
|
128
141
|
}
|
|
129
142
|
}
|
|
130
143
|
if (placement?.mode === "at") {
|
|
131
|
-
if (object.type !== "structure" && object.type !== "phenomenon") {
|
|
132
|
-
diagnostics.push(error("validate.at.objectType", `Only structures and phenomena may use "at" placement; found "${object.type}" on "${object.id}".`, object.id, "at"));
|
|
144
|
+
if (object.type !== "craft" && object.type !== "structure" && object.type !== "phenomenon") {
|
|
145
|
+
diagnostics.push(error("validate.at.objectType", `Only craft, structures, and phenomena may use "at" placement; found "${object.type}" on "${object.id}".`, object.id, "at"));
|
|
133
146
|
}
|
|
134
147
|
if (!validateAtTarget(object, objectMap, diagnostics)) {
|
|
135
148
|
diagnostics.push(error("validate.at.target.unknown", `Unknown at-reference target "${placement.target}" on "${object.id}".`, object.id, "at"));
|
|
@@ -176,7 +189,7 @@ function validateObject(object, system, objectMap, groupIds, diagnostics) {
|
|
|
176
189
|
}
|
|
177
190
|
}
|
|
178
191
|
}
|
|
179
|
-
function validateEvent(event, system, objectMap, diagnostics) {
|
|
192
|
+
function validateEvent(event, system, objectMap, trajectoryMap, diagnostics) {
|
|
180
193
|
const fieldPrefix = `event.${event.id}`;
|
|
181
194
|
const referencedIds = new Set();
|
|
182
195
|
if (!event.kind.trim()) {
|
|
@@ -188,6 +201,9 @@ function validateEvent(event, system, objectMap, diagnostics) {
|
|
|
188
201
|
if (typeof event.referencePlane === "string" && !event.referencePlane.trim()) {
|
|
189
202
|
diagnostics.push(warn("validate.event.referencePlane.empty", `Event "${event.id}" defines an empty reference plane string.`, undefined, `${fieldPrefix}.referencePlane`));
|
|
190
203
|
}
|
|
204
|
+
if (event.trajectoryId && !trajectoryMap.has(event.trajectoryId)) {
|
|
205
|
+
diagnostics.push(error("validate.event.trajectory.unknown", `Unknown trajectory "${event.trajectoryId}" on event "${event.id}".`, undefined, `${fieldPrefix}.trajectory`));
|
|
206
|
+
}
|
|
191
207
|
if (!event.targetObjectId && event.participantObjectIds.length === 0) {
|
|
192
208
|
diagnostics.push(error("validate.event.references.required", `Event "${event.id}" must define a "target" or at least one participant.`, undefined, `${fieldPrefix}.participants`));
|
|
193
209
|
}
|
|
@@ -236,19 +252,25 @@ function validateEvent(event, system, objectMap, diagnostics) {
|
|
|
236
252
|
if (!referencedIds.has(pose.objectId)) {
|
|
237
253
|
diagnostics.push(warn("validate.event.pose.unreferenced", `Event pose "${pose.objectId}" on "${event.id}" is not listed in target/participants.`, undefined, poseFieldPrefix));
|
|
238
254
|
}
|
|
239
|
-
validateEventPose(pose, object, event, system, objectMap, diagnostics, poseFieldPrefix, event.id);
|
|
255
|
+
validateEventPose(pose, object, event, system, objectMap, trajectoryMap, diagnostics, poseFieldPrefix, event.id);
|
|
240
256
|
}
|
|
241
257
|
const missingPoseIds = [...referencedIds].filter((objectId) => !poseIds.has(objectId));
|
|
242
258
|
if (event.positions.length > 0 && missingPoseIds.length > 0) {
|
|
243
259
|
diagnostics.push(warn("validate.event.positions.partial", `Event "${event.id}" leaves ${missingPoseIds.length} referenced object(s) on their base placement.`, undefined, `${fieldPrefix}.positions`));
|
|
244
260
|
}
|
|
245
261
|
}
|
|
246
|
-
function validateEventPose(pose, object, event, system, objectMap, diagnostics, fieldPrefix, eventId) {
|
|
262
|
+
function validateEventPose(pose, object, event, system, objectMap, trajectoryMap, diagnostics, fieldPrefix, eventId) {
|
|
247
263
|
const placement = pose.placement;
|
|
248
264
|
if (!placement) {
|
|
249
265
|
diagnostics.push(error("validate.event.pose.placement.required", `Event "${eventId}" pose "${pose.objectId}" is missing a placement mode.`, undefined, fieldPrefix));
|
|
250
266
|
return;
|
|
251
267
|
}
|
|
268
|
+
if (pose.trajectorySegmentId && !findTrajectorySegment(trajectoryMap, pose.trajectorySegmentId)) {
|
|
269
|
+
diagnostics.push(error("validate.event.pose.segment.unknown", `Unknown trajectory segment "${pose.trajectorySegmentId}" on "${eventId}:${pose.objectId}".`, undefined, `${fieldPrefix}.segment`));
|
|
270
|
+
}
|
|
271
|
+
if (pose.trajectoryManeuverId && !findTrajectoryManeuver(trajectoryMap, pose.trajectoryManeuverId)) {
|
|
272
|
+
diagnostics.push(error("validate.event.pose.maneuver.unknown", `Unknown trajectory maneuver "${pose.trajectoryManeuverId}" on "${eventId}:${pose.objectId}".`, undefined, `${fieldPrefix}.maneuver`));
|
|
273
|
+
}
|
|
252
274
|
if (placement.mode === "orbit") {
|
|
253
275
|
if (!objectMap.has(placement.target)) {
|
|
254
276
|
diagnostics.push(error("validate.event.pose.orbit.target.unknown", `Unknown event orbit target "${placement.target}" on "${eventId}:${pose.objectId}".`, undefined, `${fieldPrefix}.orbit`));
|
|
@@ -278,8 +300,8 @@ function validateEventPose(pose, object, event, system, objectMap, diagnostics,
|
|
|
278
300
|
return;
|
|
279
301
|
}
|
|
280
302
|
if (placement.mode === "at") {
|
|
281
|
-
if (object.type !== "structure" && object.type !== "phenomenon") {
|
|
282
|
-
diagnostics.push(error("validate.event.pose.at.objectType", `Only structures and phenomena may use "at" placement in events; found "${object.type}" on "${eventId}:${pose.objectId}".`, undefined, `${fieldPrefix}.at`));
|
|
303
|
+
if (object.type !== "craft" && object.type !== "structure" && object.type !== "phenomenon") {
|
|
304
|
+
diagnostics.push(error("validate.event.pose.at.objectType", `Only craft, structures, and phenomena may use "at" placement in events; found "${object.type}" on "${eventId}:${pose.objectId}".`, undefined, `${fieldPrefix}.at`));
|
|
283
305
|
}
|
|
284
306
|
const reference = placement.reference;
|
|
285
307
|
if (reference.kind === "named" && !objectMap.has(reference.name)) {
|
|
@@ -298,6 +320,79 @@ function validateEventPose(pose, object, event, system, objectMap, diagnostics,
|
|
|
298
320
|
}
|
|
299
321
|
}
|
|
300
322
|
}
|
|
323
|
+
function validateTrajectory(trajectory, objectMap, diagnostics) {
|
|
324
|
+
if (trajectory.craftObjectId) {
|
|
325
|
+
const craft = objectMap.get(trajectory.craftObjectId);
|
|
326
|
+
if (!craft) {
|
|
327
|
+
diagnostics.push(error("validate.trajectory.craft.unknown", `Unknown craft "${trajectory.craftObjectId}" on trajectory "${trajectory.id}".`, undefined, `trajectory.${trajectory.id}.craft`));
|
|
328
|
+
}
|
|
329
|
+
else if (!isTrajectoryCapableObject(craft)) {
|
|
330
|
+
diagnostics.push(error("validate.trajectory.craft.invalidType", `Trajectory "${trajectory.id}" targets "${trajectory.craftObjectId}", which is not craft-like.`, undefined, `trajectory.${trajectory.id}.craft`));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
for (const segment of trajectory.segments) {
|
|
334
|
+
validateTrajectorySegment(trajectory.id, segment, objectMap, diagnostics);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function validateTrajectorySegment(trajectoryId, segment, objectMap, diagnostics) {
|
|
338
|
+
const fieldPrefix = `trajectory.${trajectoryId}.segment.${segment.id}`;
|
|
339
|
+
for (const [field, objectId] of [
|
|
340
|
+
["from", segment.fromObjectId],
|
|
341
|
+
["to", segment.toObjectId],
|
|
342
|
+
["around", segment.aroundObjectId],
|
|
343
|
+
]) {
|
|
344
|
+
if (objectId && !objectMap.has(objectId)) {
|
|
345
|
+
diagnostics.push(error(`validate.trajectory.segment.${field}.unknown`, `Unknown ${field} object "${objectId}" on trajectory "${trajectoryId}" segment "${segment.id}".`, undefined, `${fieldPrefix}.${field}`));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (segment.assist?.objectId && !objectMap.has(segment.assist.objectId)) {
|
|
349
|
+
diagnostics.push(error("validate.trajectory.segment.assist.unknown", `Unknown assist object "${segment.assist.objectId}" on trajectory "${trajectoryId}" segment "${segment.id}".`, undefined, `${fieldPrefix}.assist`));
|
|
350
|
+
}
|
|
351
|
+
if (segment.kind === "flyby" && !segment.assist?.objectId) {
|
|
352
|
+
diagnostics.push(error("validate.trajectory.segment.assist.required", `Trajectory "${trajectoryId}" segment "${segment.id}" is a flyby and requires an "assist" object.`, undefined, `${fieldPrefix}.assist`));
|
|
353
|
+
}
|
|
354
|
+
if ((segment.kind === "capture" || segment.kind === "departure") && !segment.toObjectId && !segment.aroundObjectId) {
|
|
355
|
+
diagnostics.push(error("validate.trajectory.segment.target.required", `Trajectory "${trajectoryId}" segment "${segment.id}" requires a target reference.`, undefined, `${fieldPrefix}.to`));
|
|
356
|
+
}
|
|
357
|
+
for (const maneuver of segment.maneuvers) {
|
|
358
|
+
validateTrajectoryManeuver(trajectoryId, segment.id, maneuver, diagnostics);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function validateTrajectoryManeuver(trajectoryId, segmentId, maneuver, diagnostics) {
|
|
362
|
+
if (!maneuver.kind.trim()) {
|
|
363
|
+
diagnostics.push(error("validate.trajectory.maneuver.kind.required", `Trajectory "${trajectoryId}" segment "${segmentId}" maneuver "${maneuver.id}" is missing a kind.`, undefined, `trajectory.${trajectoryId}.segment.${segmentId}.maneuver.${maneuver.id}.kind`));
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function isTrajectoryCapableObject(object) {
|
|
367
|
+
if (object.type === "craft") {
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
if (object.type !== "structure") {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
const kind = typeof object.properties.kind === "string" ? object.properties.kind.toLowerCase() : "";
|
|
374
|
+
return kind === "ship" || kind === "probe" || kind === "station";
|
|
375
|
+
}
|
|
376
|
+
function findTrajectorySegment(trajectories, segmentId) {
|
|
377
|
+
for (const trajectory of trajectories.values()) {
|
|
378
|
+
const match = trajectory.segments.find((segment) => segment.id === segmentId);
|
|
379
|
+
if (match) {
|
|
380
|
+
return match;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
function findTrajectoryManeuver(trajectories, maneuverId) {
|
|
386
|
+
for (const trajectory of trajectories.values()) {
|
|
387
|
+
for (const segment of trajectory.segments) {
|
|
388
|
+
const match = segment.maneuvers.find((maneuver) => maneuver.id === maneuverId);
|
|
389
|
+
if (match) {
|
|
390
|
+
return match;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
301
396
|
function validateAtTarget(object, objectMap, diagnostics) {
|
|
302
397
|
const reference = object.placement?.mode === "at" ? object.placement.reference : null;
|
|
303
398
|
if (!reference) {
|