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
|
@@ -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[];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderDocumentToScene } from "./scene.js";
|
|
1
2
|
export function createTrajectorySolverSnapshot(trajectory) {
|
|
2
3
|
return {
|
|
3
4
|
trajectoryId: trajectory.id,
|
|
@@ -15,6 +16,58 @@ export function createTrajectorySolverSnapshot(trajectory) {
|
|
|
15
16
|
maneuvers: trajectory.segments.flatMap((segment) => segment.maneuvers.map((maneuver) => mapManeuver(segment.id, maneuver))),
|
|
16
17
|
};
|
|
17
18
|
}
|
|
19
|
+
export function sampleTrajectory(trajectory, document, options = {}) {
|
|
20
|
+
const scene = renderDocumentToScene(document, {
|
|
21
|
+
...options,
|
|
22
|
+
trajectoryMode: options.trajectoryMode ?? trajectory.renderMode ?? "auto",
|
|
23
|
+
showTrajectoryWaypoints: options.showTrajectoryWaypoints ?? true,
|
|
24
|
+
showTrajectoryLabels: options.showTrajectoryLabels ?? true,
|
|
25
|
+
});
|
|
26
|
+
const rendered = scene.trajectories.find((entry) => entry.trajectoryId === trajectory.id);
|
|
27
|
+
return rendered ? mapRenderTrajectoryToSpatial(rendered) : null;
|
|
28
|
+
}
|
|
29
|
+
export function sampleDocumentTrajectories(document, options = {}) {
|
|
30
|
+
const scene = renderDocumentToScene(document, {
|
|
31
|
+
...options,
|
|
32
|
+
trajectoryMode: options.trajectoryMode ?? "auto",
|
|
33
|
+
showTrajectoryWaypoints: options.showTrajectoryWaypoints ?? true,
|
|
34
|
+
showTrajectoryLabels: options.showTrajectoryLabels ?? true,
|
|
35
|
+
});
|
|
36
|
+
return scene.trajectories.map(mapRenderTrajectoryToSpatial);
|
|
37
|
+
}
|
|
38
|
+
function mapRenderTrajectoryToSpatial(trajectory) {
|
|
39
|
+
return {
|
|
40
|
+
trajectoryId: trajectory.trajectoryId,
|
|
41
|
+
trajectory: trajectory.trajectory,
|
|
42
|
+
craftObjectId: trajectory.craftObjectId,
|
|
43
|
+
mode: trajectory.mode,
|
|
44
|
+
stroke: trajectory.stroke,
|
|
45
|
+
strokeWidth: trajectory.strokeWidth,
|
|
46
|
+
marker: trajectory.marker,
|
|
47
|
+
labelMode: trajectory.labelMode,
|
|
48
|
+
showWaypoints: trajectory.showWaypoints,
|
|
49
|
+
samples: samplePathPoints(trajectory.path).map((point) => ({
|
|
50
|
+
x: point.x,
|
|
51
|
+
y: 0,
|
|
52
|
+
z: point.y,
|
|
53
|
+
})),
|
|
54
|
+
waypoints: trajectory.waypoints.map((waypoint) => ({
|
|
55
|
+
trajectoryId: waypoint.trajectoryId,
|
|
56
|
+
segmentId: waypoint.segmentId,
|
|
57
|
+
maneuverId: waypoint.maneuverId,
|
|
58
|
+
objectId: waypoint.objectId,
|
|
59
|
+
position: {
|
|
60
|
+
x: waypoint.x,
|
|
61
|
+
y: 0,
|
|
62
|
+
z: waypoint.y,
|
|
63
|
+
},
|
|
64
|
+
label: waypoint.label,
|
|
65
|
+
dateLabel: waypoint.dateLabel,
|
|
66
|
+
hidden: waypoint.hidden,
|
|
67
|
+
})),
|
|
68
|
+
hidden: trajectory.hidden,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
18
71
|
function mapManeuver(segmentId, maneuver) {
|
|
19
72
|
return {
|
|
20
73
|
segmentId,
|
|
@@ -25,3 +78,22 @@ function mapManeuver(segmentId, maneuver) {
|
|
|
25
78
|
duration: maneuver.duration ?? null,
|
|
26
79
|
};
|
|
27
80
|
}
|
|
81
|
+
function samplePathPoints(path) {
|
|
82
|
+
const matches = [...path.matchAll(/[MLQ]\s*(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)(?:\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?))?(?:\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?))?/g)];
|
|
83
|
+
if (matches.length === 0) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
const points = [];
|
|
87
|
+
for (const match of matches) {
|
|
88
|
+
const command = match[0][0];
|
|
89
|
+
if (command === "M" || command === "L") {
|
|
90
|
+
points.push({ x: Number(match[1]), y: Number(match[2]) });
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (command === "Q") {
|
|
94
|
+
points.push({ x: Number(match[1]), y: Number(match[2]) });
|
|
95
|
+
points.push({ x: Number(match[5]), y: Number(match[6]) });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return points;
|
|
99
|
+
}
|
|
@@ -36,6 +36,7 @@ export function renderDocumentToSpatialScene(document, options = {}) {
|
|
|
36
36
|
const spatialObjects = scene.objects.map((entry) => createSpatialObject(entry, scene, sceneCenter, objectMap, orbitMap, scaleModel, positionCache, minimumMotionMetric));
|
|
37
37
|
const spatialObjectMap = new Map(spatialObjects.map((object) => [object.objectId, object]));
|
|
38
38
|
const spatialOrbits = scene.orbitVisuals.map((orbit) => createSpatialOrbit(orbit, spatialObjectMap, minimumMotionMetric, scene.activeEventId !== null));
|
|
39
|
+
const spatialTrajectories = scene.trajectories.map((trajectory) => createSpatialTrajectory(trajectory, spatialObjectMap));
|
|
39
40
|
const focusTargets = spatialObjects.map((object) => ({
|
|
40
41
|
objectId: object.objectId,
|
|
41
42
|
center: { ...object.position },
|
|
@@ -65,6 +66,7 @@ export function renderDocumentToSpatialScene(document, options = {}) {
|
|
|
65
66
|
timeFrozen: scene.activeEventId !== null,
|
|
66
67
|
objects: spatialObjects,
|
|
67
68
|
orbits: spatialOrbits,
|
|
69
|
+
trajectories: spatialTrajectories,
|
|
68
70
|
focusTargets,
|
|
69
71
|
};
|
|
70
72
|
}
|
|
@@ -117,6 +119,41 @@ function createSpatialOrbit(orbit, objectMap, minimumMotionMetric, frozen) {
|
|
|
117
119
|
createMotionModel(orbit.object, orbit, minimumMotionMetric, frozen),
|
|
118
120
|
};
|
|
119
121
|
}
|
|
122
|
+
function createSpatialTrajectory(trajectory, objectMap) {
|
|
123
|
+
const samples = samplePathPoints(trajectory.path).map((point) => ({
|
|
124
|
+
x: point.x,
|
|
125
|
+
y: 0,
|
|
126
|
+
z: point.y,
|
|
127
|
+
}));
|
|
128
|
+
return {
|
|
129
|
+
trajectoryId: trajectory.trajectoryId,
|
|
130
|
+
trajectory: trajectory.trajectory,
|
|
131
|
+
craftObjectId: trajectory.craftObjectId,
|
|
132
|
+
mode: trajectory.mode,
|
|
133
|
+
stroke: trajectory.stroke,
|
|
134
|
+
strokeWidth: trajectory.strokeWidth,
|
|
135
|
+
marker: trajectory.marker,
|
|
136
|
+
labelMode: trajectory.labelMode,
|
|
137
|
+
showWaypoints: trajectory.showWaypoints,
|
|
138
|
+
samples,
|
|
139
|
+
waypoints: trajectory.waypoints.map((waypoint) => {
|
|
140
|
+
const object = waypoint.objectId ? objectMap.get(waypoint.objectId) ?? null : null;
|
|
141
|
+
return {
|
|
142
|
+
trajectoryId: waypoint.trajectoryId,
|
|
143
|
+
segmentId: waypoint.segmentId,
|
|
144
|
+
maneuverId: waypoint.maneuverId,
|
|
145
|
+
objectId: waypoint.objectId,
|
|
146
|
+
position: object
|
|
147
|
+
? { ...object.position }
|
|
148
|
+
: { x: waypoint.x, y: 0, z: waypoint.y },
|
|
149
|
+
label: waypoint.label,
|
|
150
|
+
dateLabel: waypoint.dateLabel,
|
|
151
|
+
hidden: waypoint.hidden,
|
|
152
|
+
};
|
|
153
|
+
}),
|
|
154
|
+
hidden: trajectory.hidden,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
120
157
|
function resolveSpatialObjectPosition(entry, scene, sceneCenter, objectMap, orbitMap, cache) {
|
|
121
158
|
const cached = cache.get(entry.objectId);
|
|
122
159
|
if (cached) {
|
|
@@ -419,3 +456,22 @@ function clampNumber(value, min, max) {
|
|
|
419
456
|
function degreesToRadians(value) {
|
|
420
457
|
return (value * Math.PI) / 180;
|
|
421
458
|
}
|
|
459
|
+
function samplePathPoints(path) {
|
|
460
|
+
const matches = [...path.matchAll(/[MLQ]\s*(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)(?:\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?))?(?:\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?))?/g)];
|
|
461
|
+
if (matches.length === 0) {
|
|
462
|
+
return [];
|
|
463
|
+
}
|
|
464
|
+
const points = [];
|
|
465
|
+
for (const match of matches) {
|
|
466
|
+
const command = match[0][0];
|
|
467
|
+
if (command === "M" || command === "L") {
|
|
468
|
+
points.push({ x: Number(match[1]), y: Number(match[2]) });
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
if (command === "Q") {
|
|
472
|
+
points.push({ x: Number(match[1]), y: Number(match[2]) });
|
|
473
|
+
points.push({ x: Number(match[5]), y: Number(match[6]) });
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return points;
|
|
477
|
+
}
|
|
@@ -2,7 +2,7 @@ export type WorldOrbitObjectType = "system" | "star" | "planet" | "moon" | "belt
|
|
|
2
2
|
export type PlacementMode = "orbit" | "at" | "surface" | "free";
|
|
3
3
|
export type Unit = "au" | "km" | "m" | "ly" | "pc" | "kpc" | "re" | "rj" | "sol" | "me" | "mj" | "s" | "min" | "h" | "d" | "y" | "ky" | "my" | "gy" | "K" | "deg";
|
|
4
4
|
export type WorldOrbitDocumentVersion = "1.0";
|
|
5
|
-
export type WorldOrbitAtlasDocumentVersion = "2.0" | "2.1" | "2.5" | "2.6" | "3.0";
|
|
5
|
+
export type WorldOrbitAtlasDocumentVersion = "2.0" | "2.1" | "2.5" | "2.6" | "3.0" | "3.1";
|
|
6
6
|
export type WorldOrbitDraftDocumentVersion = "2.0-draft";
|
|
7
7
|
export type WorldOrbitAnyDocumentVersion = WorldOrbitDocumentVersion | WorldOrbitAtlasDocumentVersion | WorldOrbitDraftDocumentVersion;
|
|
8
8
|
export type ViewProjection = "topdown" | "isometric" | "orthographic" | "perspective";
|
|
@@ -201,6 +201,12 @@ export interface WorldOrbitTrajectory {
|
|
|
201
201
|
tags: string[];
|
|
202
202
|
color: string | null;
|
|
203
203
|
hidden: boolean;
|
|
204
|
+
renderMode?: TrajectoryRenderMode | null;
|
|
205
|
+
stroke?: string | null;
|
|
206
|
+
strokeWidth?: number | null;
|
|
207
|
+
marker?: string | null;
|
|
208
|
+
labelMode?: string | null;
|
|
209
|
+
showWaypoints?: boolean | null;
|
|
204
210
|
segments: WorldOrbitTrajectorySegment[];
|
|
205
211
|
}
|
|
206
212
|
export interface WorldOrbitTrajectorySegment {
|
|
@@ -221,9 +227,14 @@ export interface WorldOrbitTrajectorySegment {
|
|
|
221
227
|
phaseAngle?: UnitValue;
|
|
222
228
|
turnAngle?: UnitValue;
|
|
223
229
|
energy?: UnitValue;
|
|
230
|
+
waypointLabel?: string | null;
|
|
231
|
+
waypointDate?: string | null;
|
|
232
|
+
renderHidden?: boolean | null;
|
|
233
|
+
sampleDensity?: number | null;
|
|
224
234
|
notes: string[];
|
|
225
235
|
maneuvers: WorldOrbitManeuver[];
|
|
226
236
|
}
|
|
237
|
+
export type TrajectoryRenderMode = "illustrative" | "solver" | "auto";
|
|
227
238
|
export type WorldOrbitTrajectorySegmentKind = "departure" | "transfer" | "flyby" | "capture" | "stationkeeping" | "escape";
|
|
228
239
|
export interface WorldOrbitManeuver {
|
|
229
240
|
id: string;
|
|
@@ -330,6 +341,9 @@ export interface SceneRenderOptions {
|
|
|
330
341
|
scaleModel?: Partial<RenderScaleModel>;
|
|
331
342
|
bodyScaleMode?: BodyScaleMode;
|
|
332
343
|
activeEventId?: string | null;
|
|
344
|
+
trajectoryMode?: TrajectoryRenderMode;
|
|
345
|
+
showTrajectoryWaypoints?: boolean;
|
|
346
|
+
showTrajectoryLabels?: boolean;
|
|
333
347
|
}
|
|
334
348
|
export interface SpatialScaleModel {
|
|
335
349
|
orbitDistanceMultiplier: number;
|
|
@@ -435,7 +449,35 @@ export interface RenderSceneEvent {
|
|
|
435
449
|
y: number;
|
|
436
450
|
hidden: boolean;
|
|
437
451
|
}
|
|
438
|
-
export
|
|
452
|
+
export interface RenderSceneTrajectoryWaypoint {
|
|
453
|
+
renderId: string;
|
|
454
|
+
trajectoryId: string;
|
|
455
|
+
segmentId: string | null;
|
|
456
|
+
maneuverId: string | null;
|
|
457
|
+
objectId: string | null;
|
|
458
|
+
x: number;
|
|
459
|
+
y: number;
|
|
460
|
+
label: string | null;
|
|
461
|
+
dateLabel: string | null;
|
|
462
|
+
hidden: boolean;
|
|
463
|
+
}
|
|
464
|
+
export interface RenderSceneTrajectory {
|
|
465
|
+
renderId: string;
|
|
466
|
+
trajectoryId: string;
|
|
467
|
+
trajectory: WorldOrbitTrajectory;
|
|
468
|
+
craftObjectId: string | null;
|
|
469
|
+
mode: TrajectoryRenderMode;
|
|
470
|
+
path: string;
|
|
471
|
+
stroke: string | null;
|
|
472
|
+
strokeWidth: number;
|
|
473
|
+
marker: string | null;
|
|
474
|
+
labelMode: string | null;
|
|
475
|
+
showWaypoints: boolean;
|
|
476
|
+
objectIds: string[];
|
|
477
|
+
waypoints: RenderSceneTrajectoryWaypoint[];
|
|
478
|
+
hidden: boolean;
|
|
479
|
+
}
|
|
480
|
+
export type SceneLayerId = "background" | "guides" | "orbits-back" | "orbits-front" | "trajectories" | "relations" | "events" | "objects" | "labels" | "metadata";
|
|
439
481
|
export interface RenderSceneViewpointFilter {
|
|
440
482
|
query: string | null;
|
|
441
483
|
objectTypes: Array<Exclude<WorldOrbitObjectType, "system">>;
|
|
@@ -554,6 +596,34 @@ export interface SpatialOrbit {
|
|
|
554
596
|
hidden: boolean;
|
|
555
597
|
motion: OrbitalMotionModel | null;
|
|
556
598
|
}
|
|
599
|
+
export interface SpatialTrajectorySample {
|
|
600
|
+
x: number;
|
|
601
|
+
y: number;
|
|
602
|
+
z: number;
|
|
603
|
+
}
|
|
604
|
+
export interface SpatialTrajectory {
|
|
605
|
+
trajectoryId: string;
|
|
606
|
+
trajectory: WorldOrbitTrajectory;
|
|
607
|
+
craftObjectId: string | null;
|
|
608
|
+
mode: TrajectoryRenderMode;
|
|
609
|
+
stroke: string | null;
|
|
610
|
+
strokeWidth: number;
|
|
611
|
+
marker: string | null;
|
|
612
|
+
labelMode: string | null;
|
|
613
|
+
showWaypoints: boolean;
|
|
614
|
+
samples: SpatialTrajectorySample[];
|
|
615
|
+
waypoints: Array<{
|
|
616
|
+
trajectoryId: string;
|
|
617
|
+
segmentId: string | null;
|
|
618
|
+
maneuverId: string | null;
|
|
619
|
+
objectId: string | null;
|
|
620
|
+
position: CoordinatePoint3D;
|
|
621
|
+
label: string | null;
|
|
622
|
+
dateLabel: string | null;
|
|
623
|
+
hidden: boolean;
|
|
624
|
+
}>;
|
|
625
|
+
hidden: boolean;
|
|
626
|
+
}
|
|
557
627
|
export interface SpatialFocusTarget {
|
|
558
628
|
objectId: string;
|
|
559
629
|
center: CoordinatePoint3D;
|
|
@@ -581,6 +651,7 @@ export interface RenderScene {
|
|
|
581
651
|
viewpoints: RenderSceneViewpoint[];
|
|
582
652
|
events: RenderSceneEvent[];
|
|
583
653
|
activeEventId: string | null;
|
|
654
|
+
trajectories: RenderSceneTrajectory[];
|
|
584
655
|
objects: RenderSceneObject[];
|
|
585
656
|
orbitVisuals: RenderOrbitVisual[];
|
|
586
657
|
relations: RenderSceneRelation[];
|
|
@@ -608,6 +679,7 @@ export interface SpatialScene {
|
|
|
608
679
|
timeFrozen: boolean;
|
|
609
680
|
objects: SpatialSceneObject[];
|
|
610
681
|
orbits: SpatialOrbit[];
|
|
682
|
+
trajectories: SpatialTrajectory[];
|
|
611
683
|
focusTargets: SpatialFocusTarget[];
|
|
612
684
|
}
|
|
613
685
|
export type SceneLayoutPreset = "compact" | "balanced" | "presentation";
|