worldorbit 2.5.13 → 2.5.15
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 +37 -11
- package/dist/unpkg/worldorbit-core.min.js +12 -5
- package/dist/unpkg/worldorbit-markdown.min.js +32 -23
- package/dist/unpkg/worldorbit-viewer.min.js +55 -41
- package/dist/unpkg/worldorbit.js +1713 -231
- package/dist/unpkg/worldorbit.min.js +58 -44
- package/package.json +2 -2
- package/packages/core/README.md +5 -1
- package/packages/core/dist/atlas-edit.d.ts +2 -2
- package/packages/core/dist/atlas-edit.js +70 -7
- package/packages/core/dist/atlas-utils.d.ts +22 -0
- package/packages/core/dist/atlas-utils.js +189 -0
- package/packages/core/dist/atlas-validate.d.ts +2 -0
- package/packages/core/dist/atlas-validate.js +285 -0
- package/packages/core/dist/draft-parse.js +786 -153
- package/packages/core/dist/draft.d.ts +3 -0
- package/packages/core/dist/draft.js +47 -3
- package/packages/core/dist/format.js +165 -9
- package/packages/core/dist/load.js +58 -13
- package/packages/core/dist/normalize.js +7 -0
- package/packages/core/dist/scene.js +66 -13
- package/packages/core/dist/types.d.ts +97 -3
- package/packages/markdown/README.md +1 -1
- package/packages/viewer/README.md +2 -1
- package/packages/viewer/dist/atlas-state.js +7 -1
- package/packages/viewer/dist/atlas-viewer.js +35 -1
- package/packages/viewer/dist/render.js +16 -7
- package/packages/viewer/dist/theme.js +4 -0
- package/packages/viewer/dist/tooltip.js +35 -0
- package/packages/viewer/dist/types.d.ts +7 -0
- package/packages/viewer/dist/viewer.js +4 -0
- package/packages/editor/dist/editor.d.ts +0 -2
- package/packages/editor/dist/editor.js +0 -2998
- package/packages/editor/dist/index.d.ts +0 -2
- package/packages/editor/dist/index.js +0 -1
- package/packages/editor/dist/types.d.ts +0 -53
- package/packages/editor/dist/types.js +0 -1
- package/packages/markdown/dist/html.d.ts +0 -3
- package/packages/markdown/dist/html.js +0 -57
- package/packages/markdown/dist/index.d.ts +0 -4
- package/packages/markdown/dist/index.js +0 -3
- package/packages/markdown/dist/rehype.d.ts +0 -10
- package/packages/markdown/dist/rehype.js +0 -49
- package/packages/markdown/dist/remark.d.ts +0 -9
- package/packages/markdown/dist/remark.js +0 -28
- package/packages/markdown/dist/types.d.ts +0 -11
- package/packages/markdown/dist/types.js +0 -1
|
@@ -130,8 +130,10 @@ export function renderDocumentToScene(document, options = {}) {
|
|
|
130
130
|
const orbitVisuals = orbitDrafts.map((draft) => createOrbitVisual(draft, relationships.groupIds.get(draft.object.id) ?? null));
|
|
131
131
|
const leaders = leaderDrafts.map((draft) => createLeaderLine(draft));
|
|
132
132
|
const labels = createSceneLabels(objects, height, scaleModel.labelMultiplier);
|
|
133
|
-
const
|
|
133
|
+
const relations = createSceneRelations(document, objects);
|
|
134
|
+
const layers = createSceneLayers(orbitVisuals, relations, leaders, objects, labels);
|
|
134
135
|
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships);
|
|
136
|
+
const semanticGroups = createSceneSemanticGroups(document, objects);
|
|
135
137
|
const viewpoints = createSceneViewpoints(document, projection, frame.preset, relationships, objectMap);
|
|
136
138
|
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels);
|
|
137
139
|
return {
|
|
@@ -141,7 +143,7 @@ export function renderDocumentToScene(document, options = {}) {
|
|
|
141
143
|
renderPreset: frame.preset,
|
|
142
144
|
projection,
|
|
143
145
|
scaleModel,
|
|
144
|
-
title: String(document.system?.properties.title ?? document.system?.id ?? "WorldOrbit") ||
|
|
146
|
+
title: String(document.system?.title ?? document.system?.properties.title ?? document.system?.id ?? "WorldOrbit") ||
|
|
145
147
|
"WorldOrbit",
|
|
146
148
|
subtitle: `${capitalizeLabel(projection)} view - ${capitalizeLabel(layoutPreset)} layout`,
|
|
147
149
|
systemId,
|
|
@@ -158,9 +160,11 @@ export function renderDocumentToScene(document, options = {}) {
|
|
|
158
160
|
contentBounds,
|
|
159
161
|
layers,
|
|
160
162
|
groups,
|
|
163
|
+
semanticGroups,
|
|
161
164
|
viewpoints,
|
|
162
165
|
objects,
|
|
163
166
|
orbitVisuals,
|
|
167
|
+
relations,
|
|
164
168
|
leaders,
|
|
165
169
|
labels,
|
|
166
170
|
};
|
|
@@ -272,6 +276,7 @@ function layoutPresetSpacing(layoutPreset) {
|
|
|
272
276
|
}
|
|
273
277
|
function createSceneObject(position, scaleModel, relationships) {
|
|
274
278
|
const { object, x, y, radius, sortKey, anchorX, anchorY } = position;
|
|
279
|
+
const renderPriority = object.renderHints?.renderPriority ?? 0;
|
|
275
280
|
return {
|
|
276
281
|
renderId: createRenderId(object.id),
|
|
277
282
|
objectId: object.id,
|
|
@@ -280,11 +285,12 @@ function createSceneObject(position, scaleModel, relationships) {
|
|
|
280
285
|
ancestorIds: relationships.ancestorIds.get(object.id) ?? [],
|
|
281
286
|
childIds: relationships.childIds.get(object.id) ?? [],
|
|
282
287
|
groupId: relationships.groupIds.get(object.id) ?? null,
|
|
288
|
+
semanticGroupIds: [...(object.groups ?? [])],
|
|
283
289
|
x,
|
|
284
290
|
y,
|
|
285
291
|
radius,
|
|
286
292
|
visualRadius: visualExtentForObject(object, radius, scaleModel),
|
|
287
|
-
sortKey,
|
|
293
|
+
sortKey: sortKey + renderPriority * 0.001,
|
|
288
294
|
anchorX,
|
|
289
295
|
anchorY,
|
|
290
296
|
label: object.id,
|
|
@@ -303,6 +309,7 @@ function createOrbitVisual(draft, groupId) {
|
|
|
303
309
|
object: draft.object,
|
|
304
310
|
parentId: draft.parentId,
|
|
305
311
|
groupId,
|
|
312
|
+
semanticGroupIds: [...(draft.object.groups ?? [])],
|
|
306
313
|
kind: draft.kind,
|
|
307
314
|
cx: draft.cx,
|
|
308
315
|
cy: draft.cy,
|
|
@@ -314,7 +321,7 @@ function createOrbitVisual(draft, groupId) {
|
|
|
314
321
|
bandThickness: draft.bandThickness,
|
|
315
322
|
frontArcPath: draft.frontArcPath,
|
|
316
323
|
backArcPath: draft.backArcPath,
|
|
317
|
-
hidden: draft.object.properties.hidden === true,
|
|
324
|
+
hidden: draft.object.properties.hidden === true || draft.object.renderHints?.renderOrbit === false,
|
|
318
325
|
};
|
|
319
326
|
}
|
|
320
327
|
function createLeaderLine(draft) {
|
|
@@ -323,6 +330,7 @@ function createLeaderLine(draft) {
|
|
|
323
330
|
objectId: draft.object.id,
|
|
324
331
|
object: draft.object,
|
|
325
332
|
groupId: draft.groupId,
|
|
333
|
+
semanticGroupIds: [...(draft.object.groups ?? [])],
|
|
326
334
|
x1: draft.x1,
|
|
327
335
|
y1: draft.y1,
|
|
328
336
|
x2: draft.x2,
|
|
@@ -335,7 +343,7 @@ function createSceneLabels(objects, sceneHeight, labelMultiplier) {
|
|
|
335
343
|
const labels = [];
|
|
336
344
|
const occupied = [];
|
|
337
345
|
const visibleObjects = [...objects]
|
|
338
|
-
.filter((object) => !object.hidden)
|
|
346
|
+
.filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false)
|
|
339
347
|
.sort((left, right) => left.sortKey - right.sortKey);
|
|
340
348
|
for (const object of visibleObjects) {
|
|
341
349
|
const direction = object.y > sceneHeight * 0.62 ? -1 : 1;
|
|
@@ -356,6 +364,7 @@ function createSceneLabels(objects, sceneHeight, labelMultiplier) {
|
|
|
356
364
|
objectId: object.objectId,
|
|
357
365
|
object: object.object,
|
|
358
366
|
groupId: object.groupId,
|
|
367
|
+
semanticGroupIds: [...object.semanticGroupIds],
|
|
359
368
|
label: object.label,
|
|
360
369
|
secondaryLabel: object.secondaryLabel,
|
|
361
370
|
x: object.x,
|
|
@@ -368,7 +377,7 @@ function createSceneLabels(objects, sceneHeight, labelMultiplier) {
|
|
|
368
377
|
}
|
|
369
378
|
return labels;
|
|
370
379
|
}
|
|
371
|
-
function createSceneLayers(orbitVisuals, leaders, objects, labels) {
|
|
380
|
+
function createSceneLayers(orbitVisuals, relations, leaders, objects, labels) {
|
|
372
381
|
const backOrbitIds = orbitVisuals
|
|
373
382
|
.filter((visual) => !visual.hidden && Boolean(visual.backArcPath))
|
|
374
383
|
.map((visual) => visual.renderId);
|
|
@@ -383,6 +392,10 @@ function createSceneLayers(orbitVisuals, leaders, objects, labels) {
|
|
|
383
392
|
},
|
|
384
393
|
{ id: "orbits-back", renderIds: backOrbitIds },
|
|
385
394
|
{ id: "orbits-front", renderIds: frontOrbitIds },
|
|
395
|
+
{
|
|
396
|
+
id: "relations",
|
|
397
|
+
renderIds: relations.filter((relation) => !relation.hidden).map((relation) => relation.renderId),
|
|
398
|
+
},
|
|
386
399
|
{
|
|
387
400
|
id: "objects",
|
|
388
401
|
renderIds: objects.filter((object) => !object.hidden).map((object) => object.renderId),
|
|
@@ -447,6 +460,42 @@ function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships
|
|
|
447
460
|
}
|
|
448
461
|
return [...groups.values()].sort((left, right) => left.label.localeCompare(right.label));
|
|
449
462
|
}
|
|
463
|
+
function createSceneSemanticGroups(document, objects) {
|
|
464
|
+
return [...document.groups]
|
|
465
|
+
.map((group) => ({
|
|
466
|
+
id: group.id,
|
|
467
|
+
label: group.label,
|
|
468
|
+
summary: group.summary,
|
|
469
|
+
color: group.color,
|
|
470
|
+
tags: [...group.tags],
|
|
471
|
+
hidden: group.hidden,
|
|
472
|
+
objectIds: objects
|
|
473
|
+
.filter((object) => !object.hidden && object.semanticGroupIds.includes(group.id))
|
|
474
|
+
.map((object) => object.objectId),
|
|
475
|
+
}))
|
|
476
|
+
.sort((left, right) => left.label.localeCompare(right.label));
|
|
477
|
+
}
|
|
478
|
+
function createSceneRelations(document, objects) {
|
|
479
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
480
|
+
return document.relations
|
|
481
|
+
.map((relation) => {
|
|
482
|
+
const from = objectMap.get(relation.from);
|
|
483
|
+
const to = objectMap.get(relation.to);
|
|
484
|
+
return {
|
|
485
|
+
renderId: `${createRenderId(relation.id)}-relation`,
|
|
486
|
+
relationId: relation.id,
|
|
487
|
+
relation,
|
|
488
|
+
fromObjectId: relation.from,
|
|
489
|
+
toObjectId: relation.to,
|
|
490
|
+
x1: from?.x ?? 0,
|
|
491
|
+
y1: from?.y ?? 0,
|
|
492
|
+
x2: to?.x ?? 0,
|
|
493
|
+
y2: to?.y ?? 0,
|
|
494
|
+
hidden: relation.hidden || !from || !to || from.hidden || to.hidden,
|
|
495
|
+
};
|
|
496
|
+
})
|
|
497
|
+
.sort((left, right) => left.relation.id.localeCompare(right.relation.id));
|
|
498
|
+
}
|
|
450
499
|
function createSceneViewpoints(document, projection, preset, relationships, objectMap) {
|
|
451
500
|
const generatedOverview = createGeneratedOverviewViewpoint(document, projection, preset);
|
|
452
501
|
const drafts = new Map();
|
|
@@ -464,7 +513,7 @@ function createSceneViewpoints(document, projection, preset, relationships, obje
|
|
|
464
513
|
}
|
|
465
514
|
const field = fieldParts.join(".").toLowerCase();
|
|
466
515
|
const draft = drafts.get(id) ?? { id };
|
|
467
|
-
applyViewpointField(draft, field, value, projection, preset, relationships, objectMap);
|
|
516
|
+
applyViewpointField(draft, field, value, document, projection, preset, relationships, objectMap);
|
|
468
517
|
drafts.set(id, draft);
|
|
469
518
|
}
|
|
470
519
|
const viewpoints = [...drafts.values()]
|
|
@@ -495,9 +544,8 @@ function createSceneViewpoints(document, projection, preset, relationships, obje
|
|
|
495
544
|
});
|
|
496
545
|
}
|
|
497
546
|
function createGeneratedOverviewViewpoint(document, projection, preset) {
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
: "Overview";
|
|
547
|
+
const title = document.system?.title ?? document.system?.properties.title;
|
|
548
|
+
const label = title ? `${String(title)} Overview` : "Overview";
|
|
501
549
|
return {
|
|
502
550
|
id: "overview",
|
|
503
551
|
label,
|
|
@@ -513,7 +561,7 @@ function createGeneratedOverviewViewpoint(document, projection, preset) {
|
|
|
513
561
|
generated: true,
|
|
514
562
|
};
|
|
515
563
|
}
|
|
516
|
-
function applyViewpointField(draft, field, value, projection, preset, relationships, objectMap) {
|
|
564
|
+
function applyViewpointField(draft, field, value, document, projection, preset, relationships, objectMap) {
|
|
517
565
|
const normalizedValue = value.trim();
|
|
518
566
|
switch (field) {
|
|
519
567
|
case "label":
|
|
@@ -580,7 +628,7 @@ function applyViewpointField(draft, field, value, projection, preset, relationsh
|
|
|
580
628
|
case "groups":
|
|
581
629
|
draft.filter = {
|
|
582
630
|
...(draft.filter ?? createEmptyViewpointFilter()),
|
|
583
|
-
groupIds: parseViewpointGroups(normalizedValue, relationships, objectMap),
|
|
631
|
+
groupIds: parseViewpointGroups(normalizedValue, document, relationships, objectMap),
|
|
584
632
|
};
|
|
585
633
|
return;
|
|
586
634
|
}
|
|
@@ -671,6 +719,7 @@ function parseViewpointLayers(value) {
|
|
|
671
719
|
rawLayer === "guides" ||
|
|
672
720
|
rawLayer === "orbits-back" ||
|
|
673
721
|
rawLayer === "orbits-front" ||
|
|
722
|
+
rawLayer === "relations" ||
|
|
674
723
|
rawLayer === "objects" ||
|
|
675
724
|
rawLayer === "labels" ||
|
|
676
725
|
rawLayer === "metadata") {
|
|
@@ -690,8 +739,12 @@ function parseViewpointObjectTypes(value) {
|
|
|
690
739
|
entry === "structure" ||
|
|
691
740
|
entry === "phenomenon");
|
|
692
741
|
}
|
|
693
|
-
function parseViewpointGroups(value, relationships, objectMap) {
|
|
742
|
+
function parseViewpointGroups(value, document, relationships, objectMap) {
|
|
694
743
|
return splitListValue(value).map((entry) => {
|
|
744
|
+
if (document.schemaVersion === "2.1" ||
|
|
745
|
+
document.groups.some((group) => group.id === entry)) {
|
|
746
|
+
return entry;
|
|
747
|
+
}
|
|
695
748
|
if (entry.startsWith("wo-") && entry.endsWith("-group")) {
|
|
696
749
|
return entry;
|
|
697
750
|
}
|
|
@@ -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";
|
|
5
|
+
export type WorldOrbitAtlasDocumentVersion = "2.0" | "2.1";
|
|
6
6
|
export type WorldOrbitDraftDocumentVersion = "2.0-draft";
|
|
7
7
|
export type WorldOrbitAnyDocumentVersion = WorldOrbitDocumentVersion | WorldOrbitAtlasDocumentVersion | WorldOrbitDraftDocumentVersion;
|
|
8
8
|
export type ViewProjection = "topdown" | "isometric";
|
|
@@ -56,38 +56,102 @@ export interface AstInfoEntryNode {
|
|
|
56
56
|
export interface WorldOrbitDocument {
|
|
57
57
|
format: "worldorbit";
|
|
58
58
|
version: WorldOrbitDocumentVersion;
|
|
59
|
+
schemaVersion: WorldOrbitAnyDocumentVersion;
|
|
59
60
|
system: WorldOrbitSystem | null;
|
|
61
|
+
groups: WorldOrbitGroup[];
|
|
62
|
+
relations: WorldOrbitRelation[];
|
|
60
63
|
objects: WorldOrbitObject[];
|
|
61
64
|
}
|
|
62
65
|
export interface WorldOrbitAtlasDocument {
|
|
63
66
|
format: "worldorbit";
|
|
64
67
|
version: WorldOrbitAtlasDocumentVersion;
|
|
68
|
+
schemaVersion: WorldOrbitAtlasDocumentVersion;
|
|
65
69
|
sourceVersion: WorldOrbitDocumentVersion;
|
|
66
70
|
system: WorldOrbitAtlasSystem | null;
|
|
71
|
+
groups: WorldOrbitGroup[];
|
|
72
|
+
relations: WorldOrbitRelation[];
|
|
67
73
|
objects: WorldOrbitObject[];
|
|
68
74
|
diagnostics: WorldOrbitDiagnostic[];
|
|
69
75
|
}
|
|
70
76
|
export interface WorldOrbitDraftDocument {
|
|
71
77
|
format: "worldorbit";
|
|
72
78
|
version: WorldOrbitDraftDocumentVersion;
|
|
79
|
+
schemaVersion: WorldOrbitDraftDocumentVersion;
|
|
73
80
|
sourceVersion: WorldOrbitDocumentVersion;
|
|
74
81
|
system: WorldOrbitAtlasSystem | null;
|
|
82
|
+
groups: WorldOrbitGroup[];
|
|
83
|
+
relations: WorldOrbitRelation[];
|
|
75
84
|
objects: WorldOrbitObject[];
|
|
76
85
|
diagnostics: WorldOrbitDiagnostic[];
|
|
77
86
|
}
|
|
78
87
|
export interface WorldOrbitSystem {
|
|
79
88
|
type: "system";
|
|
80
89
|
id: string;
|
|
90
|
+
title?: string | null;
|
|
91
|
+
description?: string | null;
|
|
92
|
+
epoch?: string | null;
|
|
93
|
+
referencePlane?: string | null;
|
|
81
94
|
properties: Record<string, NormalizedValue>;
|
|
82
95
|
info: Record<string, string>;
|
|
83
96
|
}
|
|
84
97
|
export interface WorldOrbitObject {
|
|
85
98
|
type: Exclude<WorldOrbitObjectType, "system">;
|
|
86
99
|
id: string;
|
|
100
|
+
groups?: string[];
|
|
101
|
+
epoch?: string | null;
|
|
102
|
+
referencePlane?: string | null;
|
|
103
|
+
tidalLock?: boolean;
|
|
104
|
+
resonance?: WorldOrbitResonance | null;
|
|
105
|
+
renderHints?: WorldOrbitRenderHints | null;
|
|
106
|
+
deriveRules?: WorldOrbitDeriveRule[];
|
|
107
|
+
validationRules?: WorldOrbitValidationRule[];
|
|
108
|
+
lockedFields?: string[];
|
|
109
|
+
tolerances?: WorldOrbitToleranceRule[];
|
|
110
|
+
typedBlocks?: Partial<Record<WorldOrbitTypedBlockName, Record<string, string>>>;
|
|
87
111
|
properties: Record<string, NormalizedValue>;
|
|
88
112
|
placement: Placement | null;
|
|
89
113
|
info: Record<string, string>;
|
|
90
114
|
}
|
|
115
|
+
export type WorldOrbitTypedBlockName = "climate" | "habitability" | "settlement";
|
|
116
|
+
export interface WorldOrbitGroup {
|
|
117
|
+
id: string;
|
|
118
|
+
label: string;
|
|
119
|
+
summary: string;
|
|
120
|
+
color: string | null;
|
|
121
|
+
tags: string[];
|
|
122
|
+
hidden: boolean;
|
|
123
|
+
}
|
|
124
|
+
export interface WorldOrbitRelation {
|
|
125
|
+
id: string;
|
|
126
|
+
from: string;
|
|
127
|
+
to: string;
|
|
128
|
+
kind: string;
|
|
129
|
+
label: string | null;
|
|
130
|
+
summary: string | null;
|
|
131
|
+
tags: string[];
|
|
132
|
+
color: string | null;
|
|
133
|
+
hidden: boolean;
|
|
134
|
+
}
|
|
135
|
+
export interface WorldOrbitResonance {
|
|
136
|
+
targetObjectId: string;
|
|
137
|
+
ratio: string;
|
|
138
|
+
}
|
|
139
|
+
export interface WorldOrbitRenderHints {
|
|
140
|
+
renderLabel?: boolean;
|
|
141
|
+
renderOrbit?: boolean;
|
|
142
|
+
renderPriority?: number;
|
|
143
|
+
}
|
|
144
|
+
export interface WorldOrbitDeriveRule {
|
|
145
|
+
field: string;
|
|
146
|
+
strategy: string;
|
|
147
|
+
}
|
|
148
|
+
export interface WorldOrbitValidationRule {
|
|
149
|
+
rule: string;
|
|
150
|
+
}
|
|
151
|
+
export interface WorldOrbitToleranceRule {
|
|
152
|
+
field: string;
|
|
153
|
+
value: NormalizedValue;
|
|
154
|
+
}
|
|
91
155
|
export type NormalizedValue = string | number | boolean | string[] | UnitValue;
|
|
92
156
|
export type Placement = OrbitPlacement | AtPlacement | SurfacePlacement | FreePlacement;
|
|
93
157
|
export interface OrbitPlacement {
|
|
@@ -167,6 +231,7 @@ export interface RenderSceneObject {
|
|
|
167
231
|
ancestorIds: string[];
|
|
168
232
|
childIds: string[];
|
|
169
233
|
groupId: string | null;
|
|
234
|
+
semanticGroupIds: string[];
|
|
170
235
|
x: number;
|
|
171
236
|
y: number;
|
|
172
237
|
radius: number;
|
|
@@ -186,6 +251,7 @@ export interface RenderOrbitVisual {
|
|
|
186
251
|
object: WorldOrbitObject;
|
|
187
252
|
parentId: string;
|
|
188
253
|
groupId: string | null;
|
|
254
|
+
semanticGroupIds: string[];
|
|
189
255
|
kind: "circle" | "ellipse";
|
|
190
256
|
cx: number;
|
|
191
257
|
cy: number;
|
|
@@ -205,6 +271,7 @@ export interface RenderLeaderLine {
|
|
|
205
271
|
objectId: string;
|
|
206
272
|
object: WorldOrbitObject;
|
|
207
273
|
groupId: string | null;
|
|
274
|
+
semanticGroupIds: string[];
|
|
208
275
|
x1: number;
|
|
209
276
|
y1: number;
|
|
210
277
|
x2: number;
|
|
@@ -217,6 +284,7 @@ export interface RenderSceneLabel {
|
|
|
217
284
|
objectId: string;
|
|
218
285
|
object: WorldOrbitObject;
|
|
219
286
|
groupId: string | null;
|
|
287
|
+
semanticGroupIds: string[];
|
|
220
288
|
label: string;
|
|
221
289
|
secondaryLabel: string;
|
|
222
290
|
x: number;
|
|
@@ -226,7 +294,7 @@ export interface RenderSceneLabel {
|
|
|
226
294
|
direction: "above" | "below" | "left" | "right";
|
|
227
295
|
hidden: boolean;
|
|
228
296
|
}
|
|
229
|
-
export type SceneLayerId = "background" | "guides" | "orbits-back" | "orbits-front" | "objects" | "labels" | "metadata";
|
|
297
|
+
export type SceneLayerId = "background" | "guides" | "orbits-back" | "orbits-front" | "relations" | "objects" | "labels" | "metadata";
|
|
230
298
|
export interface RenderSceneViewpointFilter {
|
|
231
299
|
query: string | null;
|
|
232
300
|
objectTypes: Array<Exclude<WorldOrbitObjectType, "system">>;
|
|
@@ -261,6 +329,27 @@ export interface RenderSceneGroup {
|
|
|
261
329
|
leaderIds: string[];
|
|
262
330
|
contentBounds: RenderBounds;
|
|
263
331
|
}
|
|
332
|
+
export interface RenderSceneSemanticGroup {
|
|
333
|
+
id: string;
|
|
334
|
+
label: string;
|
|
335
|
+
summary: string;
|
|
336
|
+
color: string | null;
|
|
337
|
+
tags: string[];
|
|
338
|
+
hidden: boolean;
|
|
339
|
+
objectIds: string[];
|
|
340
|
+
}
|
|
341
|
+
export interface RenderSceneRelation {
|
|
342
|
+
renderId: string;
|
|
343
|
+
relationId: string;
|
|
344
|
+
relation: WorldOrbitRelation;
|
|
345
|
+
fromObjectId: string;
|
|
346
|
+
toObjectId: string;
|
|
347
|
+
x1: number;
|
|
348
|
+
y1: number;
|
|
349
|
+
x2: number;
|
|
350
|
+
y2: number;
|
|
351
|
+
hidden: boolean;
|
|
352
|
+
}
|
|
264
353
|
export interface RenderScene {
|
|
265
354
|
width: number;
|
|
266
355
|
height: number;
|
|
@@ -277,9 +366,11 @@ export interface RenderScene {
|
|
|
277
366
|
contentBounds: RenderBounds;
|
|
278
367
|
layers: RenderSceneLayer[];
|
|
279
368
|
groups: RenderSceneGroup[];
|
|
369
|
+
semanticGroups: RenderSceneSemanticGroup[];
|
|
280
370
|
viewpoints: RenderSceneViewpoint[];
|
|
281
371
|
objects: RenderSceneObject[];
|
|
282
372
|
orbitVisuals: RenderOrbitVisual[];
|
|
373
|
+
relations: RenderSceneRelation[];
|
|
283
374
|
leaders: RenderLeaderLine[];
|
|
284
375
|
labels: RenderSceneLabel[];
|
|
285
376
|
}
|
|
@@ -349,12 +440,15 @@ export interface WorldOrbitAtlasSystem {
|
|
|
349
440
|
type: "system";
|
|
350
441
|
id: string;
|
|
351
442
|
title: string | null;
|
|
443
|
+
description: string | null;
|
|
444
|
+
epoch: string | null;
|
|
445
|
+
referencePlane: string | null;
|
|
352
446
|
defaults: WorldOrbitAtlasDefaults;
|
|
353
447
|
atlasMetadata: Record<string, string>;
|
|
354
448
|
viewpoints: WorldOrbitAtlasViewpoint[];
|
|
355
449
|
annotations: WorldOrbitAtlasAnnotation[];
|
|
356
450
|
}
|
|
357
|
-
export type AtlasDocumentPathKind = "system" | "defaults" | "metadata" | "object" | "viewpoint" | "annotation";
|
|
451
|
+
export type AtlasDocumentPathKind = "system" | "defaults" | "metadata" | "group" | "object" | "viewpoint" | "annotation" | "relation";
|
|
358
452
|
export interface AtlasDocumentPath {
|
|
359
453
|
kind: AtlasDocumentPathKind;
|
|
360
454
|
id?: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @worldorbit/markdown
|
|
2
2
|
|
|
3
|
-
WorldOrbit markdown contains build-time integration for fenced `worldorbit` blocks in Unified, Remark, and Rehype pipelines.
|
|
3
|
+
WorldOrbit markdown contains build-time integration for fenced `worldorbit` blocks in Unified, Remark, and Rehype pipelines and accepts the same Schema 2.1 atlas source as the core loader.
|
|
4
4
|
|
|
5
5
|
Main exports:
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @worldorbit/viewer
|
|
2
2
|
|
|
3
|
-
WorldOrbit viewer contains scene-to-SVG rendering, theme presets, browser interactivity, and embed hydration helpers.
|
|
3
|
+
WorldOrbit viewer contains scene-to-SVG rendering, theme presets, browser interactivity, atlas controls, and embed hydration helpers for WorldOrbit documents up to Schema 2.1.
|
|
4
4
|
|
|
5
5
|
Main exports:
|
|
6
6
|
|
|
@@ -8,5 +8,6 @@ Main exports:
|
|
|
8
8
|
- `renderDocumentToSvg(document, options?)`
|
|
9
9
|
- `renderSourceToSvg(source, options?)`
|
|
10
10
|
- `createInteractiveViewer(container, options)`
|
|
11
|
+
- `createAtlasViewer(container, options)`
|
|
11
12
|
- `createWorldOrbitEmbedMarkup(payload, options?)`
|
|
12
13
|
- `mountWorldOrbitEmbeds(root?, options?)`
|
|
@@ -133,6 +133,7 @@ export function sceneViewpointToLayerOptions(viewpoint) {
|
|
|
133
133
|
return {
|
|
134
134
|
background: viewpoint.layers.background,
|
|
135
135
|
guides: viewpoint.layers.guides,
|
|
136
|
+
relations: viewpoint.layers.relations,
|
|
136
137
|
orbits: viewpoint.layers["orbits-front"] === undefined && viewpoint.layers["orbits-back"] === undefined
|
|
137
138
|
? undefined
|
|
138
139
|
: viewpoint.layers["orbits-front"] !== false || viewpoint.layers["orbits-back"] !== false,
|
|
@@ -174,7 +175,12 @@ function matchesObjectFilter(object, filter) {
|
|
|
174
175
|
return false;
|
|
175
176
|
}
|
|
176
177
|
if (filter.groupIds?.length && (!object.groupId || !filter.groupIds.includes(object.groupId))) {
|
|
177
|
-
|
|
178
|
+
const hasSemanticMatch = object.semanticGroupIds.length > 0 &&
|
|
179
|
+
filter.groupIds.some((groupId) => object.semanticGroupIds.includes(groupId));
|
|
180
|
+
const hasLegacyMatch = Boolean(object.groupId && filter.groupIds.includes(object.groupId));
|
|
181
|
+
if (!hasSemanticMatch && !hasLegacyMatch) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
178
184
|
}
|
|
179
185
|
if (filter.tags?.length) {
|
|
180
186
|
const objectTags = Array.isArray(object.object.properties.tags)
|
|
@@ -9,6 +9,7 @@ export function createAtlasViewer(container, options) {
|
|
|
9
9
|
const controls = {
|
|
10
10
|
search: options.controls?.search ?? true,
|
|
11
11
|
typeFilter: options.controls?.typeFilter ?? true,
|
|
12
|
+
groupFilter: options.controls?.groupFilter ?? true,
|
|
12
13
|
viewpointSelect: options.controls?.viewpointSelect ?? true,
|
|
13
14
|
inspector: options.controls?.inspector ?? true,
|
|
14
15
|
bookmarks: options.controls?.bookmarks ?? true,
|
|
@@ -18,6 +19,7 @@ export function createAtlasViewer(container, options) {
|
|
|
18
19
|
const toolbar = container.querySelector("[data-atlas-toolbar]");
|
|
19
20
|
const searchInput = container.querySelector("[data-atlas-search]");
|
|
20
21
|
const typeFilterSelect = container.querySelector("[data-atlas-type-filter]");
|
|
22
|
+
const groupFilterSelect = container.querySelector("[data-atlas-group-filter]");
|
|
21
23
|
const viewpointSelect = container.querySelector("[data-atlas-viewpoint]");
|
|
22
24
|
const bookmarkButton = container.querySelector("[data-atlas-bookmark]");
|
|
23
25
|
const bookmarkList = container.querySelector("[data-atlas-bookmarks]");
|
|
@@ -31,6 +33,7 @@ export function createAtlasViewer(container, options) {
|
|
|
31
33
|
let searchQuery = options.initialQuery?.trim() ?? baseFilter?.query ?? "";
|
|
32
34
|
let objectTypeFilter = options.initialObjectType ??
|
|
33
35
|
(baseFilter?.objectTypes?.length === 1 ? baseFilter.objectTypes[0] : null);
|
|
36
|
+
let groupFilter = baseFilter?.groupIds?.[0] ?? null;
|
|
34
37
|
let bookmarks = [];
|
|
35
38
|
let viewer;
|
|
36
39
|
viewer = createInteractiveViewer(stage, {
|
|
@@ -78,6 +81,7 @@ export function createAtlasViewer(container, options) {
|
|
|
78
81
|
});
|
|
79
82
|
applyCurrentFilter();
|
|
80
83
|
populateViewpoints();
|
|
84
|
+
populateGroups();
|
|
81
85
|
syncControlsFromFilter(viewer.getFilter());
|
|
82
86
|
renderBookmarks();
|
|
83
87
|
updateSearchResults();
|
|
@@ -90,6 +94,10 @@ export function createAtlasViewer(container, options) {
|
|
|
90
94
|
objectTypeFilter = (typeFilterSelect.value || null);
|
|
91
95
|
applyCurrentFilter();
|
|
92
96
|
});
|
|
97
|
+
groupFilterSelect?.addEventListener("change", () => {
|
|
98
|
+
groupFilter = groupFilterSelect.value || null;
|
|
99
|
+
applyCurrentFilter();
|
|
100
|
+
});
|
|
93
101
|
viewpointSelect?.addEventListener("change", () => {
|
|
94
102
|
const activeViewer = requireViewer();
|
|
95
103
|
if (!viewpointSelect.value) {
|
|
@@ -233,6 +241,7 @@ export function createAtlasViewer(container, options) {
|
|
|
233
241
|
return api;
|
|
234
242
|
function refreshAfterInputChange() {
|
|
235
243
|
populateViewpoints();
|
|
244
|
+
populateGroups();
|
|
236
245
|
applyCurrentFilter();
|
|
237
246
|
renderBookmarks();
|
|
238
247
|
updateSearchResults();
|
|
@@ -249,7 +258,7 @@ export function createAtlasViewer(container, options) {
|
|
|
249
258
|
query: searchQuery || undefined,
|
|
250
259
|
objectTypes: objectTypeFilter ? [objectTypeFilter] : undefined,
|
|
251
260
|
tags: baseFilter?.tags,
|
|
252
|
-
groupIds: baseFilter?.groupIds,
|
|
261
|
+
groupIds: groupFilter ? [groupFilter] : baseFilter?.groupIds,
|
|
253
262
|
includeAncestors: baseFilter?.includeAncestors ?? true,
|
|
254
263
|
});
|
|
255
264
|
}
|
|
@@ -257,12 +266,16 @@ export function createAtlasViewer(container, options) {
|
|
|
257
266
|
searchQuery = filter?.query?.trim() ?? "";
|
|
258
267
|
objectTypeFilter =
|
|
259
268
|
filter?.objectTypes?.length === 1 ? filter.objectTypes[0] : null;
|
|
269
|
+
groupFilter = filter?.groupIds?.length === 1 ? filter.groupIds[0] : null;
|
|
260
270
|
if (searchInput && document.activeElement !== searchInput) {
|
|
261
271
|
searchInput.value = searchQuery;
|
|
262
272
|
}
|
|
263
273
|
if (typeFilterSelect) {
|
|
264
274
|
typeFilterSelect.value = objectTypeFilter ?? "";
|
|
265
275
|
}
|
|
276
|
+
if (groupFilterSelect) {
|
|
277
|
+
groupFilterSelect.value = groupFilter ?? "";
|
|
278
|
+
}
|
|
266
279
|
}
|
|
267
280
|
function populateViewpoints() {
|
|
268
281
|
if (!viewpointSelect) {
|
|
@@ -278,6 +291,17 @@ export function createAtlasViewer(container, options) {
|
|
|
278
291
|
].join("");
|
|
279
292
|
viewpointSelect.value = active;
|
|
280
293
|
}
|
|
294
|
+
function populateGroups() {
|
|
295
|
+
if (!groupFilterSelect) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const activeViewer = requireViewer();
|
|
299
|
+
groupFilterSelect.innerHTML = [
|
|
300
|
+
`<option value="">All groups</option>`,
|
|
301
|
+
...activeViewer.getScene().semanticGroups.map((group) => `<option value="${escapeHtml(group.id)}">${escapeHtml(group.label)}</option>`),
|
|
302
|
+
].join("");
|
|
303
|
+
groupFilterSelect.value = groupFilter ?? "";
|
|
304
|
+
}
|
|
281
305
|
function syncViewpointControl() {
|
|
282
306
|
if (!viewpointSelect) {
|
|
283
307
|
return;
|
|
@@ -313,6 +337,8 @@ export function createAtlasViewer(container, options) {
|
|
|
313
337
|
projection: activeViewer.getScene().projection,
|
|
314
338
|
renderPreset: activeViewer.getScene().renderPreset,
|
|
315
339
|
groupCount: activeViewer.getScene().groups.length,
|
|
340
|
+
semanticGroupCount: activeViewer.getScene().semanticGroups.length,
|
|
341
|
+
relationCount: activeViewer.getScene().relations.length,
|
|
316
342
|
viewpointCount: activeViewer.getScene().viewpoints.length,
|
|
317
343
|
},
|
|
318
344
|
};
|
|
@@ -351,6 +377,14 @@ function buildAtlasViewerMarkup(controls) {
|
|
|
351
377
|
</select>
|
|
352
378
|
</label>`
|
|
353
379
|
: "",
|
|
380
|
+
controls.groupFilter
|
|
381
|
+
? `<label class="wo-atlas-field">
|
|
382
|
+
<span>Group</span>
|
|
383
|
+
<select data-atlas-group-filter>
|
|
384
|
+
<option value="">All groups</option>
|
|
385
|
+
</select>
|
|
386
|
+
</label>`
|
|
387
|
+
: "",
|
|
354
388
|
controls.viewpointSelect
|
|
355
389
|
? `<label class="wo-atlas-field">
|
|
356
390
|
<span>Viewpoint</span>
|
|
@@ -32,6 +32,13 @@ export function renderSceneToSvg(scene, options = {}) {
|
|
|
32
32
|
.map((leader) => `<line class="wo-leader wo-leader-${leader.mode}" x1="${leader.x1}" y1="${leader.y1}" x2="${leader.x2}" y2="${leader.y2}" data-render-id="${escapeXml(leader.renderId)}" data-group-id="${escapeAttribute(leader.groupId ?? "")}" />`)
|
|
33
33
|
.join("")
|
|
34
34
|
: "";
|
|
35
|
+
const relationMarkup = layers.relations
|
|
36
|
+
? scene.relations
|
|
37
|
+
.filter((relation) => !relation.hidden)
|
|
38
|
+
.filter((relation) => visibleObjectIds.has(relation.fromObjectId) && visibleObjectIds.has(relation.toObjectId))
|
|
39
|
+
.map((relation) => `<line class="wo-relation" x1="${relation.x1}" y1="${relation.y1}" x2="${relation.x2}" y2="${relation.y2}" data-render-id="${escapeXml(relation.renderId)}" data-relation-id="${escapeAttribute(relation.relationId)}" />`)
|
|
40
|
+
.join("")
|
|
41
|
+
: "";
|
|
35
42
|
const objectMarkup = layers.objects
|
|
36
43
|
? visibleObjects
|
|
37
44
|
.map((object) => renderSceneObject(object, options.selectedObjectId ?? null, theme))
|
|
@@ -75,10 +82,11 @@ export function renderSceneToSvg(scene, options = {}) {
|
|
|
75
82
|
.wo-bg-glow { fill: url(#wo-bg-glow); }
|
|
76
83
|
.wo-grid { fill: none; stroke: ${theme.guide}; stroke-width: 1; }
|
|
77
84
|
.wo-orbit { fill: none; stroke: ${theme.orbit}; stroke-width: 1.5; }
|
|
78
|
-
.wo-orbit-back { opacity: 0.38; stroke-dasharray: 8 6; }
|
|
79
|
-
.wo-orbit-front { opacity: 0.9; }
|
|
80
|
-
.wo-orbit-band { stroke: ${theme.orbitBand}; stroke-linecap: round; }
|
|
81
|
-
.wo-
|
|
85
|
+
.wo-orbit-back { opacity: 0.38; stroke-dasharray: 8 6; }
|
|
86
|
+
.wo-orbit-front { opacity: 0.9; }
|
|
87
|
+
.wo-orbit-band { stroke: ${theme.orbitBand}; stroke-linecap: round; }
|
|
88
|
+
.wo-relation { stroke: ${theme.relation}; stroke-width: 2; stroke-dasharray: 10 6; }
|
|
89
|
+
.wo-leader { stroke: ${theme.leader}; stroke-width: 1.5; stroke-dasharray: 6 5; }
|
|
82
90
|
.wo-label { fill: ${theme.ink}; font-family: ${theme.fontFamily}; font-weight: 600; letter-spacing: 0.02em; }
|
|
83
91
|
.wo-label-secondary { fill: ${theme.muted}; font-family: ${theme.fontFamily}; font-weight: 500; }
|
|
84
92
|
.wo-title { fill: ${theme.ink}; font: 700 24px ${theme.displayFont}; letter-spacing: 0.06em; text-transform: uppercase; }
|
|
@@ -109,9 +117,10 @@ export function renderSceneToSvg(scene, options = {}) {
|
|
|
109
117
|
<g data-worldorbit-world="true">
|
|
110
118
|
<g data-worldorbit-camera-root="${WORLD_LAYER_ID}" id="${WORLD_LAYER_ID}">
|
|
111
119
|
<g data-worldorbit-world-content="true">
|
|
112
|
-
${layers.orbits ? `<g data-layer-id="orbits-back">${orbitMarkup.back}</g>` : ""}
|
|
113
|
-
${layers.guides ? `<g data-layer-id="guides">${leaderMarkup}</g>` : ""}
|
|
114
|
-
${layers.
|
|
120
|
+
${layers.orbits ? `<g data-layer-id="orbits-back">${orbitMarkup.back}</g>` : ""}
|
|
121
|
+
${layers.guides ? `<g data-layer-id="guides">${leaderMarkup}</g>` : ""}
|
|
122
|
+
${layers.relations ? `<g data-layer-id="relations">${relationMarkup}</g>` : ""}
|
|
123
|
+
${layers.objects ? `<g data-layer-id="objects">${objectMarkup}</g>` : ""}
|
|
115
124
|
${layers.orbits ? `<g data-layer-id="orbits-front">${orbitMarkup.front}</g>` : ""}
|
|
116
125
|
${layers.labels ? `<g data-layer-id="labels">${labelMarkup}</g>` : ""}
|
|
117
126
|
</g>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const DEFAULT_LAYERS = {
|
|
2
2
|
background: true,
|
|
3
3
|
guides: true,
|
|
4
|
+
relations: true,
|
|
4
5
|
orbits: true,
|
|
5
6
|
objects: true,
|
|
6
7
|
labels: true,
|
|
@@ -15,6 +16,7 @@ const THEME_PRESETS = {
|
|
|
15
16
|
backgroundGlow: "rgba(240, 180, 100, 0.18)",
|
|
16
17
|
panel: "rgba(7, 17, 27, 0.9)",
|
|
17
18
|
panelLine: "rgba(168, 207, 242, 0.18)",
|
|
19
|
+
relation: "rgba(240, 180, 100, 0.42)",
|
|
18
20
|
orbit: "rgba(163, 209, 255, 0.24)",
|
|
19
21
|
orbitBand: "rgba(255, 190, 120, 0.28)",
|
|
20
22
|
guide: "rgba(255, 255, 255, 0.04)",
|
|
@@ -37,6 +39,7 @@ const THEME_PRESETS = {
|
|
|
37
39
|
backgroundGlow: "rgba(120, 255, 215, 0.16)",
|
|
38
40
|
panel: "rgba(7, 20, 30, 0.9)",
|
|
39
41
|
panelLine: "rgba(120, 255, 215, 0.16)",
|
|
42
|
+
relation: "rgba(156, 231, 255, 0.42)",
|
|
40
43
|
orbit: "rgba(120, 255, 215, 0.2)",
|
|
41
44
|
orbitBand: "rgba(137, 185, 255, 0.24)",
|
|
42
45
|
guide: "rgba(255, 255, 255, 0.035)",
|
|
@@ -59,6 +62,7 @@ const THEME_PRESETS = {
|
|
|
59
62
|
backgroundGlow: "rgba(255, 127, 95, 0.18)",
|
|
60
63
|
panel: "rgba(24, 9, 13, 0.9)",
|
|
61
64
|
panelLine: "rgba(255, 166, 149, 0.16)",
|
|
65
|
+
relation: "rgba(255, 178, 125, 0.42)",
|
|
62
66
|
orbit: "rgba(255, 188, 164, 0.22)",
|
|
63
67
|
orbitBand: "rgba(255, 214, 139, 0.24)",
|
|
64
68
|
guide: "rgba(255, 255, 255, 0.03)",
|