worldorbit 4.0.0 → 5.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 +17 -18
- package/dist/browser/core/dist/draft-parse.js +131 -12
- package/dist/browser/core/dist/draft.js +4 -4
- package/dist/browser/core/dist/format.js +42 -3
- package/dist/browser/core/dist/load.js +7 -2
- package/dist/browser/core/dist/scene.js +215 -3
- package/dist/browser/core/dist/solver.d.ts +8 -1
- package/dist/browser/core/dist/solver.js +72 -0
- package/dist/browser/core/dist/spatial-scene.js +56 -0
- package/dist/browser/core/dist/types.d.ts +74 -2
- package/dist/browser/viewer/dist/render.js +71 -15
- package/dist/browser/viewer/dist/theme.js +1 -0
- package/dist/browser/viewer/dist/types.d.ts +7 -0
- package/dist/browser/viewer/dist/viewer.js +27 -0
- package/dist/obsidian-plugin/main.js +81 -67
- package/dist/unpkg/core/dist/draft-parse.js +131 -12
- package/dist/unpkg/core/dist/draft.js +4 -4
- package/dist/unpkg/core/dist/format.js +42 -3
- package/dist/unpkg/core/dist/load.js +7 -2
- package/dist/unpkg/core/dist/scene.js +215 -3
- package/dist/unpkg/core/dist/solver.d.ts +8 -1
- package/dist/unpkg/core/dist/solver.js +72 -0
- package/dist/unpkg/core/dist/spatial-scene.js +56 -0
- package/dist/unpkg/core/dist/types.d.ts +74 -2
- package/dist/unpkg/viewer/dist/render.js +71 -15
- package/dist/unpkg/viewer/dist/theme.js +1 -0
- package/dist/unpkg/viewer/dist/types.d.ts +7 -0
- package/dist/unpkg/viewer/dist/viewer.js +27 -0
- package/dist/unpkg/worldorbit-core.min.js +12 -12
- package/dist/unpkg/worldorbit-editor.min.js +336 -322
- package/dist/unpkg/worldorbit-markdown.min.js +50 -36
- package/dist/unpkg/worldorbit-viewer.min.js +225 -211
- package/dist/unpkg/worldorbit.js +473 -19
- package/dist/unpkg/worldorbit.min.js +229 -215
- package/package.json +1 -1
- package/packages/core/dist/draft-parse.js +131 -12
- package/packages/core/dist/draft.js +4 -4
- package/packages/core/dist/format.js +42 -3
- package/packages/core/dist/load.js +7 -2
- package/packages/core/dist/scene.js +215 -3
- package/packages/core/dist/solver.d.ts +8 -1
- package/packages/core/dist/solver.js +72 -0
- package/packages/core/dist/spatial-scene.js +56 -0
- package/packages/core/dist/types.d.ts +74 -2
- package/packages/viewer/dist/render.js +71 -15
- package/packages/viewer/dist/theme.js +1 -0
- package/packages/viewer/dist/types.d.ts +7 -0
- package/packages/viewer/dist/viewer.js +27 -0
package/package.json
CHANGED
|
@@ -179,7 +179,7 @@ function parseAtlasSource(source, forcedOutputVersion) {
|
|
|
179
179
|
handleSectionLine(section, indent, tokens, lineNumber);
|
|
180
180
|
}
|
|
181
181
|
if (!sawSchemaHeader) {
|
|
182
|
-
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0" or "schema 3.
|
|
182
|
+
throw new WorldOrbitError('Missing required atlas schema header "schema 2.0", "schema 3.0", or "schema 3.1"');
|
|
183
183
|
}
|
|
184
184
|
const objects = objectNodes.map((node) => normalizeDraftObject(node, sourceSchemaVersion, diagnostics));
|
|
185
185
|
const normalizedEvents = events.map((event) => normalizeDraftEvent(event, eventPoseNodes.get(event.id) ?? []));
|
|
@@ -225,21 +225,23 @@ function parseAtlasSource(source, forcedOutputVersion) {
|
|
|
225
225
|
function assertDraftSchemaHeader(tokens, line) {
|
|
226
226
|
if (tokens.length !== 2 ||
|
|
227
227
|
tokens[0].value.toLowerCase() !== "schema" ||
|
|
228
|
-
!["2.0-draft", "2.0", "2.1", "2.5", "2.6", "3.0"].includes(tokens[1].value.toLowerCase())) {
|
|
229
|
-
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", "schema 2.5", "schema 2.6", "schema 3.0", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
228
|
+
!["2.0-draft", "2.0", "2.1", "2.5", "2.6", "3.0", "3.1"].includes(tokens[1].value.toLowerCase())) {
|
|
229
|
+
throw new WorldOrbitError('Expected atlas header "schema 2.0", "schema 2.1", "schema 2.5", "schema 2.6", "schema 3.0", "schema 3.1", or legacy "schema 2.0-draft"', line, tokens[0]?.column ?? 1);
|
|
230
230
|
}
|
|
231
231
|
const version = tokens[1].value.toLowerCase();
|
|
232
232
|
return version === "2.6"
|
|
233
233
|
? "2.6"
|
|
234
234
|
: version === "3.0"
|
|
235
235
|
? "3.0"
|
|
236
|
-
: version === "
|
|
237
|
-
? "
|
|
238
|
-
: version === "2.
|
|
239
|
-
? "2.
|
|
240
|
-
: version === "2.
|
|
241
|
-
? "2.
|
|
242
|
-
: "2.0"
|
|
236
|
+
: version === "3.1"
|
|
237
|
+
? "3.1"
|
|
238
|
+
: version === "2.5"
|
|
239
|
+
? "2.5"
|
|
240
|
+
: version === "2.1"
|
|
241
|
+
? "2.1"
|
|
242
|
+
: version === "2.0-draft"
|
|
243
|
+
? "2.0-draft"
|
|
244
|
+
: "2.0";
|
|
243
245
|
}
|
|
244
246
|
function startTopLevelSection(tokens, line, sourceSchemaVersion, diagnostics, system, objectNodes, groups, relations, events, trajectories, eventPoseNodes, viewpointIds, annotationIds, groupIds, relationIds, eventIds, trajectoryIds, flags) {
|
|
245
247
|
const keyword = tokens[0]?.value.toLowerCase();
|
|
@@ -523,6 +525,12 @@ function startTrajectorySection(tokens, line, trajectories, trajectoryIds, sourc
|
|
|
523
525
|
craftObjectId: null,
|
|
524
526
|
tags: [],
|
|
525
527
|
color: null,
|
|
528
|
+
renderMode: null,
|
|
529
|
+
stroke: null,
|
|
530
|
+
strokeWidth: null,
|
|
531
|
+
marker: null,
|
|
532
|
+
labelMode: null,
|
|
533
|
+
showWaypoints: null,
|
|
526
534
|
hidden: false,
|
|
527
535
|
segments: [],
|
|
528
536
|
};
|
|
@@ -1090,6 +1098,10 @@ function applyTrajectoryField(section, indent, tokens, line) {
|
|
|
1090
1098
|
aroundObjectId: null,
|
|
1091
1099
|
assist: null,
|
|
1092
1100
|
epoch: null,
|
|
1101
|
+
waypointLabel: null,
|
|
1102
|
+
waypointDate: null,
|
|
1103
|
+
renderHidden: null,
|
|
1104
|
+
sampleDensity: null,
|
|
1093
1105
|
notes: [],
|
|
1094
1106
|
maneuvers: [],
|
|
1095
1107
|
};
|
|
@@ -1124,6 +1136,54 @@ function applyTrajectoryField(section, indent, tokens, line) {
|
|
|
1124
1136
|
case "color":
|
|
1125
1137
|
section.trajectory.color = value;
|
|
1126
1138
|
return;
|
|
1139
|
+
case "rendermode":
|
|
1140
|
+
warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "trajectory.renderMode", {
|
|
1141
|
+
line,
|
|
1142
|
+
column: tokens[0].column,
|
|
1143
|
+
});
|
|
1144
|
+
if (value !== "illustrative" && value !== "solver" && value !== "auto") {
|
|
1145
|
+
throw new WorldOrbitError(`Unknown trajectory render mode "${value}"`, line, tokens[0].column);
|
|
1146
|
+
}
|
|
1147
|
+
section.trajectory.renderMode = value;
|
|
1148
|
+
return;
|
|
1149
|
+
case "stroke":
|
|
1150
|
+
warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "trajectory.stroke", {
|
|
1151
|
+
line,
|
|
1152
|
+
column: tokens[0].column,
|
|
1153
|
+
});
|
|
1154
|
+
section.trajectory.stroke = value;
|
|
1155
|
+
return;
|
|
1156
|
+
case "strokewidth":
|
|
1157
|
+
warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "trajectory.strokeWidth", {
|
|
1158
|
+
line,
|
|
1159
|
+
column: tokens[0].column,
|
|
1160
|
+
});
|
|
1161
|
+
section.trajectory.strokeWidth = parsePositiveNumber(value, line, tokens[0].column, "strokeWidth");
|
|
1162
|
+
return;
|
|
1163
|
+
case "marker":
|
|
1164
|
+
warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "trajectory.marker", {
|
|
1165
|
+
line,
|
|
1166
|
+
column: tokens[0].column,
|
|
1167
|
+
});
|
|
1168
|
+
section.trajectory.marker = value;
|
|
1169
|
+
return;
|
|
1170
|
+
case "labelmode":
|
|
1171
|
+
warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "trajectory.labelMode", {
|
|
1172
|
+
line,
|
|
1173
|
+
column: tokens[0].column,
|
|
1174
|
+
});
|
|
1175
|
+
section.trajectory.labelMode = value;
|
|
1176
|
+
return;
|
|
1177
|
+
case "showwaypoints":
|
|
1178
|
+
warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "trajectory.showWaypoints", {
|
|
1179
|
+
line,
|
|
1180
|
+
column: tokens[0].column,
|
|
1181
|
+
});
|
|
1182
|
+
section.trajectory.showWaypoints = parseAtlasBoolean(value, "showWaypoints", {
|
|
1183
|
+
line,
|
|
1184
|
+
column: tokens[0].column,
|
|
1185
|
+
});
|
|
1186
|
+
return;
|
|
1127
1187
|
case "hidden":
|
|
1128
1188
|
section.trajectory.hidden = parseAtlasBoolean(value, "hidden", {
|
|
1129
1189
|
line,
|
|
@@ -1199,6 +1259,37 @@ function applyTrajectorySegmentField(section, tokens, line) {
|
|
|
1199
1259
|
case "energy":
|
|
1200
1260
|
target.energy = parseAtlasUnitValue(value, { line, column: tokens[0].column });
|
|
1201
1261
|
return;
|
|
1262
|
+
case "waypointlabel":
|
|
1263
|
+
warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "segment.waypointLabel", {
|
|
1264
|
+
line,
|
|
1265
|
+
column: tokens[0].column,
|
|
1266
|
+
});
|
|
1267
|
+
target.waypointLabel = value;
|
|
1268
|
+
return;
|
|
1269
|
+
case "waypointdate":
|
|
1270
|
+
warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "segment.waypointDate", {
|
|
1271
|
+
line,
|
|
1272
|
+
column: tokens[0].column,
|
|
1273
|
+
});
|
|
1274
|
+
target.waypointDate = value;
|
|
1275
|
+
return;
|
|
1276
|
+
case "renderhidden":
|
|
1277
|
+
warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "segment.renderHidden", {
|
|
1278
|
+
line,
|
|
1279
|
+
column: tokens[0].column,
|
|
1280
|
+
});
|
|
1281
|
+
target.renderHidden = parseAtlasBoolean(value, "renderHidden", {
|
|
1282
|
+
line,
|
|
1283
|
+
column: tokens[0].column,
|
|
1284
|
+
});
|
|
1285
|
+
return;
|
|
1286
|
+
case "sampledensity":
|
|
1287
|
+
warnIfSchema31Feature(section.sourceSchemaVersion, section.diagnostics, "segment.sampleDensity", {
|
|
1288
|
+
line,
|
|
1289
|
+
column: tokens[0].column,
|
|
1290
|
+
});
|
|
1291
|
+
target.sampleDensity = parsePositiveNumber(value, line, tokens[0].column, "sampleDensity");
|
|
1292
|
+
return;
|
|
1202
1293
|
case "notes":
|
|
1203
1294
|
target.notes = parseTokenList(tokens.slice(1), line, "notes");
|
|
1204
1295
|
return;
|
|
@@ -1345,7 +1436,8 @@ function parseLayerTokens(tokens, line, sourceSchemaVersion, diagnostics) {
|
|
|
1345
1436
|
raw === "events" ||
|
|
1346
1437
|
raw === "objects" ||
|
|
1347
1438
|
raw === "labels" ||
|
|
1348
|
-
raw === "metadata"
|
|
1439
|
+
raw === "metadata" ||
|
|
1440
|
+
raw === "trajectories") {
|
|
1349
1441
|
if (raw === "events" && sourceSchemaVersion && diagnostics) {
|
|
1350
1442
|
warnIfSchema21Feature(sourceSchemaVersion, diagnostics, "layers.events", {
|
|
1351
1443
|
line,
|
|
@@ -1476,6 +1568,12 @@ function parseInlineObjectFields(tokens, line, objectType, sourceSchemaVersion,
|
|
|
1476
1568
|
column: keyToken.column,
|
|
1477
1569
|
});
|
|
1478
1570
|
}
|
|
1571
|
+
else if (spec.version === "3.1") {
|
|
1572
|
+
warnIfSchema31Feature(sourceSchemaVersion, diagnostics, keyToken.value, {
|
|
1573
|
+
line,
|
|
1574
|
+
column: keyToken.column,
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1479
1577
|
index++;
|
|
1480
1578
|
const valueTokens = [];
|
|
1481
1579
|
if (spec.inlineMode === "single") {
|
|
@@ -1534,6 +1632,12 @@ function parseObjectField(tokens, line, objectType, sourceSchemaVersion, diagnos
|
|
|
1534
1632
|
column: tokens[0].column,
|
|
1535
1633
|
});
|
|
1536
1634
|
}
|
|
1635
|
+
else if (spec.version === "3.1") {
|
|
1636
|
+
warnIfSchema31Feature(sourceSchemaVersion, diagnostics, tokens[0].value, {
|
|
1637
|
+
line,
|
|
1638
|
+
column: tokens[0].column,
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1537
1641
|
const field = {
|
|
1538
1642
|
type: "field",
|
|
1539
1643
|
key: tokens[0].value,
|
|
@@ -1876,6 +1980,19 @@ function warnIfSchema30Feature(sourceSchemaVersion, diagnostics, featureName, lo
|
|
|
1876
1980
|
column: location.column,
|
|
1877
1981
|
});
|
|
1878
1982
|
}
|
|
1983
|
+
function warnIfSchema31Feature(sourceSchemaVersion, diagnostics, featureName, location) {
|
|
1984
|
+
if (!isSchemaOlderThan(sourceSchemaVersion, "3.1")) {
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1987
|
+
diagnostics.push({
|
|
1988
|
+
code: "parse.schema31.featureCompatibility",
|
|
1989
|
+
severity: "warning",
|
|
1990
|
+
source: "parse",
|
|
1991
|
+
message: `Feature "${featureName}" requires schema 3.1; parsed in compatibility mode because the document header is "schema ${sourceSchemaVersion}".`,
|
|
1992
|
+
line: location.line,
|
|
1993
|
+
column: location.column,
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1879
1996
|
function isSchemaOlderThan(sourceSchemaVersion, requiredVersion) {
|
|
1880
1997
|
return schemaVersionRank(sourceSchemaVersion) < schemaVersionRank(requiredVersion);
|
|
1881
1998
|
}
|
|
@@ -1893,8 +2010,10 @@ function schemaVersionRank(version) {
|
|
|
1893
2010
|
return 4;
|
|
1894
2011
|
case "3.0":
|
|
1895
2012
|
return 5;
|
|
1896
|
-
|
|
2013
|
+
case "3.1":
|
|
1897
2014
|
return 6;
|
|
2015
|
+
default:
|
|
2016
|
+
return 7;
|
|
1898
2017
|
}
|
|
1899
2018
|
}
|
|
1900
2019
|
function preprocessAtlasSource(source) {
|
|
@@ -18,15 +18,15 @@ export function upgradeDocumentToV2(document, options = {}) {
|
|
|
18
18
|
}
|
|
19
19
|
return {
|
|
20
20
|
format: "worldorbit",
|
|
21
|
-
version: "3.
|
|
22
|
-
schemaVersion: "3.
|
|
21
|
+
version: "3.1",
|
|
22
|
+
schemaVersion: "3.1",
|
|
23
23
|
sourceVersion: document.version,
|
|
24
24
|
theme: document.theme ?? null,
|
|
25
25
|
system,
|
|
26
26
|
groups: structuredClone(document.groups ?? []),
|
|
27
27
|
relations: structuredClone(document.relations ?? []),
|
|
28
28
|
events: structuredClone(document.events ?? []),
|
|
29
|
-
trajectories: [],
|
|
29
|
+
trajectories: structuredClone(document.trajectories ?? []),
|
|
30
30
|
objects: document.objects.map(cloneWorldOrbitObject).map(normalizeLegacyCraftObject),
|
|
31
31
|
diagnostics,
|
|
32
32
|
};
|
|
@@ -490,7 +490,7 @@ function serializeViewpointLayers(layers) {
|
|
|
490
490
|
if (orbitFront !== undefined || orbitBack !== undefined) {
|
|
491
491
|
tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
|
|
492
492
|
}
|
|
493
|
-
for (const key of ["background", "guides", "relations", "events", "objects", "labels", "metadata"]) {
|
|
493
|
+
for (const key of ["background", "guides", "relations", "events", "objects", "trajectories", "labels", "metadata"]) {
|
|
494
494
|
if (layers[key] !== undefined) {
|
|
495
495
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
496
496
|
}
|
|
@@ -41,11 +41,13 @@ export function formatDocument(document, options = {}) {
|
|
|
41
41
|
schema === "2.5" ||
|
|
42
42
|
schema === "2.6" ||
|
|
43
43
|
schema === "3.0" ||
|
|
44
|
+
schema === "3.1" ||
|
|
44
45
|
schema === "2.0-draft" ||
|
|
45
46
|
document.version === "2.0" ||
|
|
46
47
|
document.version === "2.1" ||
|
|
47
48
|
document.version === "2.5" ||
|
|
48
49
|
document.version === "3.0" ||
|
|
50
|
+
document.version === "3.1" ||
|
|
49
51
|
document.version === "2.6" ||
|
|
50
52
|
document.version === "2.0-draft";
|
|
51
53
|
if (useDraft) {
|
|
@@ -61,7 +63,12 @@ export function formatDocument(document, options = {}) {
|
|
|
61
63
|
: upgradeDocumentToDraftV2(document);
|
|
62
64
|
return formatDraftDocument(legacyDraftDocument);
|
|
63
65
|
}
|
|
64
|
-
const atlasDocument = document.version === "2.0" ||
|
|
66
|
+
const atlasDocument = document.version === "2.0" ||
|
|
67
|
+
document.version === "2.1" ||
|
|
68
|
+
document.version === "2.5" ||
|
|
69
|
+
document.version === "2.6" ||
|
|
70
|
+
document.version === "3.0" ||
|
|
71
|
+
document.version === "3.1"
|
|
65
72
|
? document
|
|
66
73
|
: document.version === "2.0-draft"
|
|
67
74
|
? {
|
|
@@ -70,7 +77,13 @@ export function formatDocument(document, options = {}) {
|
|
|
70
77
|
schemaVersion: "2.0",
|
|
71
78
|
}
|
|
72
79
|
: upgradeDocumentToV2(document);
|
|
73
|
-
if ((schema === "2.0" ||
|
|
80
|
+
if ((schema === "2.0" ||
|
|
81
|
+
schema === "2.1" ||
|
|
82
|
+
schema === "2.5" ||
|
|
83
|
+
schema === "2.6" ||
|
|
84
|
+
schema === "3.0" ||
|
|
85
|
+
schema === "3.1") &&
|
|
86
|
+
atlasDocument.version !== schema) {
|
|
74
87
|
return formatAtlasDocument({
|
|
75
88
|
...atlasDocument,
|
|
76
89
|
version: schema,
|
|
@@ -534,6 +547,24 @@ function formatAtlasTrajectory(trajectory) {
|
|
|
534
547
|
if (trajectory.color) {
|
|
535
548
|
lines.push(` color ${quoteIfNeeded(trajectory.color)}`);
|
|
536
549
|
}
|
|
550
|
+
if (trajectory.renderMode) {
|
|
551
|
+
lines.push(` renderMode ${trajectory.renderMode}`);
|
|
552
|
+
}
|
|
553
|
+
if (trajectory.stroke) {
|
|
554
|
+
lines.push(` stroke ${quoteIfNeeded(trajectory.stroke)}`);
|
|
555
|
+
}
|
|
556
|
+
if (trajectory.strokeWidth !== null && trajectory.strokeWidth !== undefined) {
|
|
557
|
+
lines.push(` strokeWidth ${trajectory.strokeWidth}`);
|
|
558
|
+
}
|
|
559
|
+
if (trajectory.marker) {
|
|
560
|
+
lines.push(` marker ${quoteIfNeeded(trajectory.marker)}`);
|
|
561
|
+
}
|
|
562
|
+
if (trajectory.labelMode) {
|
|
563
|
+
lines.push(` labelMode ${quoteIfNeeded(trajectory.labelMode)}`);
|
|
564
|
+
}
|
|
565
|
+
if (trajectory.showWaypoints !== null && trajectory.showWaypoints !== undefined) {
|
|
566
|
+
lines.push(` showWaypoints ${trajectory.showWaypoints ? "true" : "false"}`);
|
|
567
|
+
}
|
|
537
568
|
if (trajectory.hidden) {
|
|
538
569
|
lines.push(" hidden true");
|
|
539
570
|
}
|
|
@@ -570,6 +601,14 @@ function formatTrajectorySegmentFields(segment) {
|
|
|
570
601
|
...formatOptionalUnit("phaseAngle", segment.phaseAngle),
|
|
571
602
|
...formatOptionalUnit("turnAngle", segment.turnAngle),
|
|
572
603
|
...formatOptionalUnit("energy", segment.energy),
|
|
604
|
+
...(segment.waypointLabel ? [`waypointLabel ${quoteIfNeeded(segment.waypointLabel)}`] : []),
|
|
605
|
+
...(segment.waypointDate ? [`waypointDate ${quoteIfNeeded(segment.waypointDate)}`] : []),
|
|
606
|
+
...(segment.renderHidden !== null && segment.renderHidden !== undefined
|
|
607
|
+
? [`renderHidden ${segment.renderHidden ? "true" : "false"}`]
|
|
608
|
+
: []),
|
|
609
|
+
...(segment.sampleDensity !== null && segment.sampleDensity !== undefined
|
|
610
|
+
? [`sampleDensity ${segment.sampleDensity}`]
|
|
611
|
+
: []),
|
|
573
612
|
...(segment.notes.length > 0 ? [`notes ${segment.notes.map(quoteIfNeeded).join(" ")}`] : []),
|
|
574
613
|
];
|
|
575
614
|
}
|
|
@@ -634,7 +673,7 @@ function formatDraftLayers(layers) {
|
|
|
634
673
|
? "orbits"
|
|
635
674
|
: "-orbits");
|
|
636
675
|
}
|
|
637
|
-
for (const key of ["background", "guides", "relations", "events", "objects", "labels", "metadata"]) {
|
|
676
|
+
for (const key of ["background", "guides", "relations", "events", "objects", "trajectories", "labels", "metadata"]) {
|
|
638
677
|
if (layers[key] !== undefined) {
|
|
639
678
|
tokens.push(layers[key] ? key : `-${key}`);
|
|
640
679
|
}
|
|
@@ -5,11 +5,12 @@ import { WorldOrbitError } from "./errors.js";
|
|
|
5
5
|
import { normalizeDocument } from "./normalize.js";
|
|
6
6
|
import { parseWorldOrbit } from "./parse.js";
|
|
7
7
|
import { validateDocument } from "./validate.js";
|
|
8
|
-
const ATLAS_SCHEMA_PATTERN = /^schema\s+(?:2(?:\.0|\.1|\.5|\.6)?|3(?:\.0)?)$/i;
|
|
8
|
+
const ATLAS_SCHEMA_PATTERN = /^schema\s+(?:2(?:\.0|\.1|\.5|\.6)?|3(?:\.0|\.1)?)$/i;
|
|
9
9
|
const ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
|
|
10
10
|
const ATLAS_SCHEMA_25_PATTERN = /^schema\s+2\.5$/i;
|
|
11
11
|
const ATLAS_SCHEMA_26_PATTERN = /^schema\s+2\.6$/i;
|
|
12
12
|
const ATLAS_SCHEMA_30_PATTERN = /^schema\s+3(?:\.0)?$/i;
|
|
13
|
+
const ATLAS_SCHEMA_31_PATTERN = /^schema\s+3\.1$/i;
|
|
13
14
|
const LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
|
|
14
15
|
export function detectWorldOrbitSchemaVersion(source) {
|
|
15
16
|
for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
|
|
@@ -32,6 +33,9 @@ export function detectWorldOrbitSchemaVersion(source) {
|
|
|
32
33
|
if (ATLAS_SCHEMA_30_PATTERN.test(trimmed)) {
|
|
33
34
|
return "3.0";
|
|
34
35
|
}
|
|
36
|
+
if (ATLAS_SCHEMA_31_PATTERN.test(trimmed)) {
|
|
37
|
+
return "3.1";
|
|
38
|
+
}
|
|
35
39
|
if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
|
|
36
40
|
return "2.0";
|
|
37
41
|
}
|
|
@@ -97,7 +101,8 @@ export function loadWorldOrbitSourceWithDiagnostics(source) {
|
|
|
97
101
|
schemaVersion === "2.1" ||
|
|
98
102
|
schemaVersion === "2.5" ||
|
|
99
103
|
schemaVersion === "2.6" ||
|
|
100
|
-
schemaVersion === "3.0"
|
|
104
|
+
schemaVersion === "3.0" ||
|
|
105
|
+
schemaVersion === "3.1") {
|
|
101
106
|
return loadAtlasSourceWithDiagnostics(source, schemaVersion);
|
|
102
107
|
}
|
|
103
108
|
let ast;
|
|
@@ -138,7 +138,8 @@ export function renderDocumentToScene(document, options = {}) {
|
|
|
138
138
|
const labels = createSceneLabels(objects, width, height, scaleModel.labelMultiplier);
|
|
139
139
|
const relations = createSceneRelations(document, objects);
|
|
140
140
|
const events = createSceneEvents(document.events ?? [], objects, activeEventId);
|
|
141
|
-
const
|
|
141
|
+
const trajectories = createSceneTrajectories(document, objects, events, options);
|
|
142
|
+
const layers = createSceneLayers(orbitVisuals, trajectories, relations, events, leaders, objects, labels);
|
|
142
143
|
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, scaleModel.labelMultiplier);
|
|
143
144
|
const semanticGroups = createSceneSemanticGroups(document, objects);
|
|
144
145
|
const viewpoints = createSceneViewpoints(document, schemaProjection, frame.preset, relationships, objectMap);
|
|
@@ -179,6 +180,7 @@ export function renderDocumentToScene(document, options = {}) {
|
|
|
179
180
|
viewpoints,
|
|
180
181
|
events,
|
|
181
182
|
activeEventId,
|
|
183
|
+
trajectories,
|
|
182
184
|
objects,
|
|
183
185
|
orbitVisuals,
|
|
184
186
|
relations,
|
|
@@ -662,7 +664,7 @@ function createLabelPlacement(object, direction, attempt, labelMultiplier) {
|
|
|
662
664
|
}
|
|
663
665
|
}
|
|
664
666
|
}
|
|
665
|
-
function createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels) {
|
|
667
|
+
function createSceneLayers(orbitVisuals, trajectories, relations, events, leaders, objects, labels) {
|
|
666
668
|
const backOrbitIds = orbitVisuals
|
|
667
669
|
.filter((visual) => !visual.hidden && Boolean(visual.backArcPath))
|
|
668
670
|
.map((visual) => visual.renderId);
|
|
@@ -677,6 +679,15 @@ function createSceneLayers(orbitVisuals, relations, events, leaders, objects, la
|
|
|
677
679
|
},
|
|
678
680
|
{ id: "orbits-back", renderIds: backOrbitIds },
|
|
679
681
|
{ id: "orbits-front", renderIds: frontOrbitIds },
|
|
682
|
+
{
|
|
683
|
+
id: "trajectories",
|
|
684
|
+
renderIds: trajectories
|
|
685
|
+
.filter((trajectory) => !trajectory.hidden)
|
|
686
|
+
.flatMap((trajectory) => [
|
|
687
|
+
trajectory.renderId,
|
|
688
|
+
...trajectory.waypoints.filter((waypoint) => !waypoint.hidden).map((waypoint) => waypoint.renderId),
|
|
689
|
+
]),
|
|
690
|
+
},
|
|
680
691
|
{
|
|
681
692
|
id: "relations",
|
|
682
693
|
renderIds: relations.filter((relation) => !relation.hidden).map((relation) => relation.renderId),
|
|
@@ -819,6 +830,206 @@ function createSceneEvents(events, objects, activeEventId) {
|
|
|
819
830
|
})
|
|
820
831
|
.sort((left, right) => left.event.id.localeCompare(right.event.id));
|
|
821
832
|
}
|
|
833
|
+
function createSceneTrajectories(document, objects, events, options) {
|
|
834
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
835
|
+
return document.trajectories
|
|
836
|
+
.map((trajectory) => createSceneTrajectory(trajectory, objectMap, events, options))
|
|
837
|
+
.sort((left, right) => left.trajectoryId.localeCompare(right.trajectoryId));
|
|
838
|
+
}
|
|
839
|
+
function createSceneTrajectory(trajectory, objectMap, events, options) {
|
|
840
|
+
const craftObject = trajectory.craftObjectId ? objectMap.get(trajectory.craftObjectId) ?? null : null;
|
|
841
|
+
const mode = resolveTrajectoryMode(trajectory, options);
|
|
842
|
+
const stroke = trajectory.stroke ?? trajectory.color ?? craftObject?.fillColor ?? null;
|
|
843
|
+
const strokeWidth = trajectory.strokeWidth ?? 2.4;
|
|
844
|
+
const showWaypoints = options.showTrajectoryWaypoints ?? trajectory.showWaypoints ?? true;
|
|
845
|
+
const labelMode = trajectory.labelMode ?? (options.showTrajectoryLabels === false ? "hidden" : "waypoint");
|
|
846
|
+
const pathParts = [];
|
|
847
|
+
const waypoints = [];
|
|
848
|
+
const objectIds = new Set();
|
|
849
|
+
let lastAnchor = craftObject;
|
|
850
|
+
if (craftObject) {
|
|
851
|
+
objectIds.add(craftObject.objectId);
|
|
852
|
+
}
|
|
853
|
+
trajectory.segments.forEach((segment, index) => {
|
|
854
|
+
const geometry = buildTrajectorySegmentGeometry(trajectory, segment, index, mode, objectMap, lastAnchor, showWaypoints);
|
|
855
|
+
if (geometry.path) {
|
|
856
|
+
pathParts.push(geometry.path);
|
|
857
|
+
}
|
|
858
|
+
geometry.objectIds.forEach((objectId) => objectIds.add(objectId));
|
|
859
|
+
waypoints.push(...geometry.waypoints);
|
|
860
|
+
lastAnchor = geometry.lastAnchor ?? lastAnchor;
|
|
861
|
+
});
|
|
862
|
+
for (const event of events.filter((entry) => entry.event.trajectoryId === trajectory.id)) {
|
|
863
|
+
const eventObject = event.targetObjectId
|
|
864
|
+
? objectMap.get(event.targetObjectId) ?? null
|
|
865
|
+
: event.objectIds.map((objectId) => objectMap.get(objectId) ?? null).find(Boolean) ?? null;
|
|
866
|
+
if (!eventObject) {
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
waypoints.push({
|
|
870
|
+
renderId: `${createRenderId(`${trajectory.id}-${event.eventId}`)}-waypoint`,
|
|
871
|
+
trajectoryId: trajectory.id,
|
|
872
|
+
segmentId: null,
|
|
873
|
+
maneuverId: null,
|
|
874
|
+
objectId: eventObject.objectId,
|
|
875
|
+
x: eventObject.x,
|
|
876
|
+
y: eventObject.y,
|
|
877
|
+
label: event.event.label ?? event.event.id,
|
|
878
|
+
dateLabel: event.event.epoch ?? null,
|
|
879
|
+
hidden: trajectory.hidden || event.hidden || !showWaypoints,
|
|
880
|
+
});
|
|
881
|
+
objectIds.add(eventObject.objectId);
|
|
882
|
+
}
|
|
883
|
+
return {
|
|
884
|
+
renderId: `${createRenderId(trajectory.id)}-trajectory`,
|
|
885
|
+
trajectoryId: trajectory.id,
|
|
886
|
+
trajectory,
|
|
887
|
+
craftObjectId: trajectory.craftObjectId,
|
|
888
|
+
mode,
|
|
889
|
+
path: pathParts.join(" "),
|
|
890
|
+
stroke,
|
|
891
|
+
strokeWidth,
|
|
892
|
+
marker: trajectory.marker ?? "arrow",
|
|
893
|
+
labelMode,
|
|
894
|
+
showWaypoints,
|
|
895
|
+
objectIds: [...objectIds],
|
|
896
|
+
waypoints,
|
|
897
|
+
hidden: trajectory.hidden || pathParts.length === 0,
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
function buildTrajectorySegmentGeometry(trajectory, segment, segmentIndex, mode, objectMap, fallbackStart, showWaypoints) {
|
|
901
|
+
const start = resolveTrajectoryObject(segment.fromObjectId, objectMap) ??
|
|
902
|
+
fallbackStart;
|
|
903
|
+
const assist = resolveTrajectoryObject(segment.assist?.objectId ?? segment.aroundObjectId ?? null, objectMap);
|
|
904
|
+
const end = resolveTrajectoryObject(segment.toObjectId, objectMap) ??
|
|
905
|
+
resolveTrajectoryObject(segment.aroundObjectId, objectMap) ??
|
|
906
|
+
assist ??
|
|
907
|
+
start;
|
|
908
|
+
if (!start || !end) {
|
|
909
|
+
return {
|
|
910
|
+
path: "",
|
|
911
|
+
objectIds: [],
|
|
912
|
+
waypoints: [],
|
|
913
|
+
lastAnchor: fallbackStart,
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
const objectIds = [start.objectId, end.objectId];
|
|
917
|
+
if (assist) {
|
|
918
|
+
objectIds.push(assist.objectId);
|
|
919
|
+
}
|
|
920
|
+
const hidden = trajectory.hidden || segment.renderHidden === true || start.hidden || end.hidden;
|
|
921
|
+
const waypoints = [];
|
|
922
|
+
const label = segment.waypointLabel ?? segment.label ?? humanizeIdentifier(segment.id);
|
|
923
|
+
const dateLabel = segment.waypointDate ?? segment.epoch ?? null;
|
|
924
|
+
if (showWaypoints) {
|
|
925
|
+
if (segmentIndex === 0) {
|
|
926
|
+
waypoints.push(createTrajectoryWaypoint(trajectory.id, segment.id, null, start, start.label, null, hidden));
|
|
927
|
+
}
|
|
928
|
+
if (assist && assist.objectId !== start.objectId && assist.objectId !== end.objectId) {
|
|
929
|
+
waypoints.push(createTrajectoryWaypoint(trajectory.id, segment.id, null, assist, label, dateLabel, hidden));
|
|
930
|
+
}
|
|
931
|
+
waypoints.push(createTrajectoryWaypoint(trajectory.id, segment.id, null, end, label, dateLabel, hidden));
|
|
932
|
+
}
|
|
933
|
+
const samples = mode === "solver"
|
|
934
|
+
? sampleQuadraticPoints(start, resolveTrajectoryControlPoint(start, end, assist, segmentIndex), end, Math.max(10, Math.round((segment.sampleDensity ?? 1) * 14)))
|
|
935
|
+
: null;
|
|
936
|
+
const control = resolveTrajectoryControlPoint(start, end, assist, segmentIndex);
|
|
937
|
+
const path = mode === "solver"
|
|
938
|
+
? pointsToPath(samples ?? [start, end])
|
|
939
|
+
: `M ${formatPathNumber(start.x)} ${formatPathNumber(start.y)} Q ${formatPathNumber(control.x)} ${formatPathNumber(control.y)} ${formatPathNumber(end.x)} ${formatPathNumber(end.y)}`;
|
|
940
|
+
segment.maneuvers.forEach((maneuver, maneuverIndex) => {
|
|
941
|
+
if (!showWaypoints) {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
const point = samplePointOnQuadratic(start, control, end, (maneuverIndex + 1) / (segment.maneuvers.length + 1));
|
|
945
|
+
waypoints.push({
|
|
946
|
+
renderId: `${createRenderId(`${trajectory.id}-${segment.id}-${maneuver.id}`)}-waypoint`,
|
|
947
|
+
trajectoryId: trajectory.id,
|
|
948
|
+
segmentId: segment.id,
|
|
949
|
+
maneuverId: maneuver.id,
|
|
950
|
+
objectId: null,
|
|
951
|
+
x: point.x,
|
|
952
|
+
y: point.y,
|
|
953
|
+
label: maneuver.label ?? maneuver.kind,
|
|
954
|
+
dateLabel: maneuver.epoch ?? null,
|
|
955
|
+
hidden,
|
|
956
|
+
});
|
|
957
|
+
});
|
|
958
|
+
return {
|
|
959
|
+
path,
|
|
960
|
+
objectIds: [...new Set(objectIds)],
|
|
961
|
+
waypoints,
|
|
962
|
+
lastAnchor: end,
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
function createTrajectoryWaypoint(trajectoryId, segmentId, maneuverId, object, label, dateLabel, hidden) {
|
|
966
|
+
return {
|
|
967
|
+
renderId: `${createRenderId(`${trajectoryId}-${segmentId ?? object.objectId}-${maneuverId ?? object.objectId}`)}-waypoint`,
|
|
968
|
+
trajectoryId,
|
|
969
|
+
segmentId,
|
|
970
|
+
maneuverId,
|
|
971
|
+
objectId: object.objectId,
|
|
972
|
+
x: object.x,
|
|
973
|
+
y: object.y,
|
|
974
|
+
label,
|
|
975
|
+
dateLabel,
|
|
976
|
+
hidden,
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
function resolveTrajectoryMode(trajectory, options) {
|
|
980
|
+
const requested = options.trajectoryMode ?? trajectory.renderMode ?? "auto";
|
|
981
|
+
if (requested !== "auto") {
|
|
982
|
+
return requested;
|
|
983
|
+
}
|
|
984
|
+
const segmentCount = trajectory.segments.filter((segment) => segment.fromObjectId || segment.toObjectId || segment.assist?.objectId || segment.aroundObjectId).length;
|
|
985
|
+
return segmentCount > 0 ? "solver" : "illustrative";
|
|
986
|
+
}
|
|
987
|
+
function resolveTrajectoryObject(objectId, objectMap) {
|
|
988
|
+
return objectId ? objectMap.get(objectId) ?? null : null;
|
|
989
|
+
}
|
|
990
|
+
function resolveTrajectoryControlPoint(start, end, assist, segmentIndex) {
|
|
991
|
+
if (assist) {
|
|
992
|
+
return {
|
|
993
|
+
x: assist.x,
|
|
994
|
+
y: assist.y,
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
const dx = end.x - start.x;
|
|
998
|
+
const dy = end.y - start.y;
|
|
999
|
+
const length = Math.max(Math.hypot(dx, dy), 1);
|
|
1000
|
+
const offset = Math.min(Math.max(length * 0.18, 26), 120) * (segmentIndex % 2 === 0 ? 1 : -1);
|
|
1001
|
+
const normalX = -dy / length;
|
|
1002
|
+
const normalY = dx / length;
|
|
1003
|
+
return {
|
|
1004
|
+
x: (start.x + end.x) / 2 + normalX * offset,
|
|
1005
|
+
y: (start.y + end.y) / 2 + normalY * offset,
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
function sampleQuadraticPoints(start, control, end, count) {
|
|
1009
|
+
const samples = [];
|
|
1010
|
+
for (let index = 0; index <= count; index += 1) {
|
|
1011
|
+
samples.push(samplePointOnQuadratic(start, control, end, index / count));
|
|
1012
|
+
}
|
|
1013
|
+
return samples;
|
|
1014
|
+
}
|
|
1015
|
+
function samplePointOnQuadratic(start, control, end, t) {
|
|
1016
|
+
const inverse = 1 - t;
|
|
1017
|
+
return {
|
|
1018
|
+
x: inverse * inverse * start.x + 2 * inverse * t * control.x + t * t * end.x,
|
|
1019
|
+
y: inverse * inverse * start.y + 2 * inverse * t * control.y + t * t * end.y,
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
function pointsToPath(points) {
|
|
1023
|
+
if (points.length === 0) {
|
|
1024
|
+
return "";
|
|
1025
|
+
}
|
|
1026
|
+
return points
|
|
1027
|
+
.map((point, index) => `${index === 0 ? "M" : "L"} ${formatPathNumber(point.x)} ${formatPathNumber(point.y)}`)
|
|
1028
|
+
.join(" ");
|
|
1029
|
+
}
|
|
1030
|
+
function formatPathNumber(value) {
|
|
1031
|
+
return Number.isFinite(value) ? value.toFixed(2) : "0";
|
|
1032
|
+
}
|
|
822
1033
|
function createSceneViewpoints(document, projection, preset, relationships, objectMap) {
|
|
823
1034
|
const generatedOverview = createGeneratedOverviewViewpoint(document, projection, preset);
|
|
824
1035
|
const drafts = new Map();
|
|
@@ -1099,7 +1310,8 @@ function parseViewpointLayers(value) {
|
|
|
1099
1310
|
rawLayer === "events" ||
|
|
1100
1311
|
rawLayer === "objects" ||
|
|
1101
1312
|
rawLayer === "labels" ||
|
|
1102
|
-
rawLayer === "metadata"
|
|
1313
|
+
rawLayer === "metadata" ||
|
|
1314
|
+
rawLayer === "trajectories") {
|
|
1103
1315
|
next[rawLayer] = enabled;
|
|
1104
1316
|
}
|
|
1105
1317
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UnitValue, WorldOrbitTrajectory, WorldOrbitTrajectorySegment } from "./types.js";
|
|
1
|
+
import type { SceneRenderOptions, SpatialTrajectory, UnitValue, WorldOrbitDocument, WorldOrbitTrajectory, WorldOrbitTrajectorySegment } from "./types.js";
|
|
2
2
|
export interface SolverSegmentSample {
|
|
3
3
|
segmentId: string;
|
|
4
4
|
kind: WorldOrbitTrajectorySegment["kind"];
|
|
@@ -23,4 +23,11 @@ export interface SolverTrajectorySnapshot {
|
|
|
23
23
|
segments: SolverSegmentSample[];
|
|
24
24
|
maneuvers: SolverManeuverSample[];
|
|
25
25
|
}
|
|
26
|
+
export interface TrajectorySamplingOptions extends Pick<SceneRenderOptions, "width" | "height" | "padding" | "preset" | "projection" | "camera" | "scaleModel" | "bodyScaleMode"> {
|
|
27
|
+
trajectoryMode?: "illustrative" | "solver" | "auto";
|
|
28
|
+
showTrajectoryWaypoints?: boolean;
|
|
29
|
+
showTrajectoryLabels?: boolean;
|
|
30
|
+
}
|
|
26
31
|
export declare function createTrajectorySolverSnapshot(trajectory: WorldOrbitTrajectory): SolverTrajectorySnapshot;
|
|
32
|
+
export declare function sampleTrajectory(trajectory: WorldOrbitTrajectory, document: WorldOrbitDocument, options?: TrajectorySamplingOptions): SpatialTrajectory | null;
|
|
33
|
+
export declare function sampleDocumentTrajectories(document: WorldOrbitDocument, options?: TrajectorySamplingOptions): SpatialTrajectory[];
|