worldorbit 2.5.15 → 2.5.17
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 +1 -1
- package/dist/browser/core/dist/index.js +2444 -342
- package/dist/browser/editor/dist/index.js +11702 -0
- package/dist/browser/markdown/dist/index.js +2207 -392
- package/dist/browser/viewer/dist/index.js +2302 -382
- package/dist/unpkg/core/dist/index.js +2447 -345
- package/dist/unpkg/editor/dist/index.js +11727 -0
- package/dist/unpkg/markdown/dist/index.js +2210 -395
- package/dist/unpkg/viewer/dist/index.js +2305 -385
- package/dist/unpkg/worldorbit-core.min.js +12 -12
- package/dist/unpkg/worldorbit-editor.min.js +894 -0
- package/dist/unpkg/worldorbit-markdown.min.js +66 -58
- package/dist/unpkg/worldorbit-viewer.min.js +76 -68
- package/dist/unpkg/worldorbit.js +797 -78
- package/dist/unpkg/worldorbit.min.js +80 -72
- package/package.json +3 -2
- package/packages/core/dist/atlas-edit.js +74 -0
- package/packages/core/dist/atlas-validate.js +122 -8
- package/packages/core/dist/draft-parse.js +212 -8
- package/packages/core/dist/draft.d.ts +5 -2
- package/packages/core/dist/draft.js +59 -3
- package/packages/core/dist/format.js +63 -1
- package/packages/core/dist/normalize.js +1 -0
- package/packages/core/dist/scene.js +248 -46
- package/packages/core/dist/types.d.ts +41 -2
- package/packages/editor/dist/editor.d.ts +2 -0
- package/packages/editor/dist/editor.js +3578 -0
- package/packages/editor/dist/index.d.ts +2 -0
- package/packages/editor/dist/index.js +1 -0
- package/packages/editor/dist/types.d.ts +55 -0
- package/packages/editor/dist/types.js +1 -0
- package/packages/markdown/dist/html.d.ts +3 -0
- package/packages/markdown/dist/html.js +57 -0
- package/packages/markdown/dist/index.d.ts +4 -0
- package/packages/markdown/dist/index.js +3 -0
- package/packages/markdown/dist/rehype.d.ts +10 -0
- package/packages/markdown/dist/rehype.js +49 -0
- package/packages/markdown/dist/remark.d.ts +9 -0
- package/packages/markdown/dist/remark.js +28 -0
- package/packages/markdown/dist/types.d.ts +11 -0
- package/packages/markdown/dist/types.js +1 -0
- package/packages/viewer/dist/atlas-state.js +6 -0
- package/packages/viewer/dist/atlas-viewer.js +1 -0
- package/packages/viewer/dist/render.js +31 -2
- package/packages/viewer/dist/theme.js +1 -0
- package/packages/viewer/dist/tooltip.js +9 -0
- package/packages/viewer/dist/types.d.ts +8 -1
- package/packages/viewer/dist/viewer.js +12 -1
|
@@ -18,8 +18,10 @@ export function renderDocumentToScene(document, options = {}) {
|
|
|
18
18
|
const scaleModel = resolveScaleModel(layoutPreset, options.scaleModel);
|
|
19
19
|
const spacingFactor = layoutPresetSpacing(layoutPreset);
|
|
20
20
|
const systemId = document.system?.id ?? null;
|
|
21
|
-
const
|
|
22
|
-
const
|
|
21
|
+
const activeEventId = options.activeEventId ?? null;
|
|
22
|
+
const effectiveObjects = createEffectiveObjects(document.objects, document.events ?? [], activeEventId);
|
|
23
|
+
const objectMap = new Map(effectiveObjects.map((object) => [object.id, object]));
|
|
24
|
+
const relationships = buildSceneRelationships(effectiveObjects, objectMap);
|
|
23
25
|
const positions = new Map();
|
|
24
26
|
const orbitDrafts = [];
|
|
25
27
|
const leaderDrafts = [];
|
|
@@ -28,7 +30,7 @@ export function renderDocumentToScene(document, options = {}) {
|
|
|
28
30
|
const atObjects = [];
|
|
29
31
|
const surfaceChildren = new Map();
|
|
30
32
|
const orbitChildren = new Map();
|
|
31
|
-
for (const object of
|
|
33
|
+
for (const object of effectiveObjects) {
|
|
32
34
|
const placement = object.placement;
|
|
33
35
|
if (!placement) {
|
|
34
36
|
rootObjects.push(object);
|
|
@@ -129,13 +131,14 @@ export function renderDocumentToScene(document, options = {}) {
|
|
|
129
131
|
const objects = [...positions.values()].map((position) => createSceneObject(position, scaleModel, relationships));
|
|
130
132
|
const orbitVisuals = orbitDrafts.map((draft) => createOrbitVisual(draft, relationships.groupIds.get(draft.object.id) ?? null));
|
|
131
133
|
const leaders = leaderDrafts.map((draft) => createLeaderLine(draft));
|
|
132
|
-
const labels = createSceneLabels(objects, height, scaleModel.labelMultiplier);
|
|
134
|
+
const labels = createSceneLabels(objects, width, height, scaleModel.labelMultiplier);
|
|
133
135
|
const relations = createSceneRelations(document, objects);
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
+
const events = createSceneEvents(document.events ?? [], objects, activeEventId);
|
|
137
|
+
const layers = createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels);
|
|
138
|
+
const groups = createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, scaleModel.labelMultiplier);
|
|
136
139
|
const semanticGroups = createSceneSemanticGroups(document, objects);
|
|
137
140
|
const viewpoints = createSceneViewpoints(document, projection, frame.preset, relationships, objectMap);
|
|
138
|
-
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels);
|
|
141
|
+
const contentBounds = calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, scaleModel.labelMultiplier);
|
|
139
142
|
return {
|
|
140
143
|
width,
|
|
141
144
|
height,
|
|
@@ -162,6 +165,8 @@ export function renderDocumentToScene(document, options = {}) {
|
|
|
162
165
|
groups,
|
|
163
166
|
semanticGroups,
|
|
164
167
|
viewpoints,
|
|
168
|
+
events,
|
|
169
|
+
activeEventId,
|
|
165
170
|
objects,
|
|
166
171
|
orbitVisuals,
|
|
167
172
|
relations,
|
|
@@ -180,6 +185,37 @@ export function rotatePoint(point, center, rotationDeg) {
|
|
|
180
185
|
y: center.y + dx * sin + dy * cos,
|
|
181
186
|
};
|
|
182
187
|
}
|
|
188
|
+
function createEffectiveObjects(objects, events, activeEventId) {
|
|
189
|
+
const cloned = objects.map((object) => structuredClone(object));
|
|
190
|
+
if (!activeEventId) {
|
|
191
|
+
return cloned;
|
|
192
|
+
}
|
|
193
|
+
const activeEvent = events.find((event) => event.id === activeEventId);
|
|
194
|
+
if (!activeEvent) {
|
|
195
|
+
return cloned;
|
|
196
|
+
}
|
|
197
|
+
const objectMap = new Map(cloned.map((object) => [object.id, object]));
|
|
198
|
+
for (const pose of activeEvent.positions) {
|
|
199
|
+
const object = objectMap.get(pose.objectId);
|
|
200
|
+
if (!object) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
object.placement = pose.placement ? structuredClone(pose.placement) : null;
|
|
204
|
+
if (pose.inner) {
|
|
205
|
+
object.properties.inner = { ...pose.inner };
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
delete object.properties.inner;
|
|
209
|
+
}
|
|
210
|
+
if (pose.outer) {
|
|
211
|
+
object.properties.outer = { ...pose.outer };
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
delete object.properties.outer;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return cloned;
|
|
218
|
+
}
|
|
183
219
|
function resolveLayoutPreset(document) {
|
|
184
220
|
const rawScale = String(document.system?.properties.scale ?? "balanced").toLowerCase();
|
|
185
221
|
switch (rawScale) {
|
|
@@ -339,26 +375,17 @@ function createLeaderLine(draft) {
|
|
|
339
375
|
hidden: draft.object.properties.hidden === true,
|
|
340
376
|
};
|
|
341
377
|
}
|
|
342
|
-
function createSceneLabels(objects, sceneHeight, labelMultiplier) {
|
|
378
|
+
function createSceneLabels(objects, sceneWidth, sceneHeight, labelMultiplier) {
|
|
343
379
|
const labels = [];
|
|
344
380
|
const occupied = [];
|
|
381
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
345
382
|
const visibleObjects = [...objects]
|
|
346
383
|
.filter((object) => !object.hidden && object.object.renderHints?.renderLabel !== false)
|
|
347
|
-
.sort(
|
|
384
|
+
.sort(compareLabelPlacementOrder);
|
|
348
385
|
for (const object of visibleObjects) {
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
let secondaryY = labelY + direction * (16 * labelMultiplier);
|
|
353
|
-
let bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
|
|
354
|
-
let attempts = 0;
|
|
355
|
-
while (occupied.some((entry) => rectsOverlap(entry, bounds)) && attempts < 10) {
|
|
356
|
-
labelY += direction * 14 * labelMultiplier;
|
|
357
|
-
secondaryY += direction * 14 * labelMultiplier;
|
|
358
|
-
bounds = createLabelRect(object.x, labelY, secondaryY, labelHalfWidth, direction);
|
|
359
|
-
attempts += 1;
|
|
360
|
-
}
|
|
361
|
-
occupied.push(bounds);
|
|
386
|
+
const placement = selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) ??
|
|
387
|
+
createLabelPlacement(object, defaultVerticalDirection(object, objectMap.get(object.parentId ?? "") ?? null, sceneHeight), 0, labelMultiplier);
|
|
388
|
+
occupied.push(createLabelRect(object, placement, labelMultiplier));
|
|
362
389
|
labels.push({
|
|
363
390
|
renderId: `${object.renderId}-label`,
|
|
364
391
|
objectId: object.objectId,
|
|
@@ -367,17 +394,134 @@ function createSceneLabels(objects, sceneHeight, labelMultiplier) {
|
|
|
367
394
|
semanticGroupIds: [...object.semanticGroupIds],
|
|
368
395
|
label: object.label,
|
|
369
396
|
secondaryLabel: object.secondaryLabel,
|
|
370
|
-
x:
|
|
371
|
-
y: labelY,
|
|
372
|
-
secondaryY,
|
|
373
|
-
textAnchor:
|
|
374
|
-
direction: direction
|
|
397
|
+
x: placement.x,
|
|
398
|
+
y: placement.labelY,
|
|
399
|
+
secondaryY: placement.secondaryY,
|
|
400
|
+
textAnchor: placement.textAnchor,
|
|
401
|
+
direction: placement.direction,
|
|
375
402
|
hidden: object.hidden,
|
|
376
403
|
});
|
|
377
404
|
}
|
|
378
405
|
return labels;
|
|
379
406
|
}
|
|
380
|
-
function
|
|
407
|
+
function compareLabelPlacementOrder(left, right) {
|
|
408
|
+
const priorityDiff = labelPlacementPriority(left) - labelPlacementPriority(right);
|
|
409
|
+
if (priorityDiff !== 0) {
|
|
410
|
+
return priorityDiff;
|
|
411
|
+
}
|
|
412
|
+
const renderPriorityDiff = (right.object.renderHints?.renderPriority ?? 0) - (left.object.renderHints?.renderPriority ?? 0);
|
|
413
|
+
if (renderPriorityDiff !== 0) {
|
|
414
|
+
return renderPriorityDiff;
|
|
415
|
+
}
|
|
416
|
+
return left.sortKey - right.sortKey;
|
|
417
|
+
}
|
|
418
|
+
function labelPlacementPriority(object) {
|
|
419
|
+
switch (object.object.type) {
|
|
420
|
+
case "star":
|
|
421
|
+
return 0;
|
|
422
|
+
case "planet":
|
|
423
|
+
return 1;
|
|
424
|
+
case "moon":
|
|
425
|
+
return 2;
|
|
426
|
+
case "belt":
|
|
427
|
+
case "ring":
|
|
428
|
+
return 3;
|
|
429
|
+
case "asteroid":
|
|
430
|
+
case "comet":
|
|
431
|
+
return 4;
|
|
432
|
+
case "structure":
|
|
433
|
+
case "phenomenon":
|
|
434
|
+
return 5;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
function selectLabelPlacement(object, objectMap, occupied, sceneWidth, sceneHeight, labelMultiplier) {
|
|
438
|
+
for (const direction of preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight)) {
|
|
439
|
+
const maxAttempts = direction === "left" || direction === "right" ? 4 : 6;
|
|
440
|
+
for (let attempt = 0; attempt <= maxAttempts; attempt += 1) {
|
|
441
|
+
const placement = createLabelPlacement(object, direction, attempt, labelMultiplier);
|
|
442
|
+
const rect = createLabelRect(object, placement, labelMultiplier);
|
|
443
|
+
if (!occupied.some((entry) => rectsOverlap(entry, rect))) {
|
|
444
|
+
return placement;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
function preferredLabelDirections(object, objectMap, sceneWidth, sceneHeight) {
|
|
451
|
+
const parent = object.parentId ? objectMap.get(object.parentId) ?? null : null;
|
|
452
|
+
const vertical = defaultVerticalDirection(object, parent, sceneHeight);
|
|
453
|
+
const oppositeVertical = vertical === "below" ? "above" : "below";
|
|
454
|
+
const horizontal = defaultHorizontalDirection(object, parent, sceneWidth);
|
|
455
|
+
const oppositeHorizontal = horizontal === "right" ? "left" : "right";
|
|
456
|
+
const preferHorizontal = object.object.type === "structure" ||
|
|
457
|
+
object.object.type === "phenomenon" ||
|
|
458
|
+
object.object.placement?.mode === "at" ||
|
|
459
|
+
object.object.placement?.mode === "surface" ||
|
|
460
|
+
object.object.placement?.mode === "free";
|
|
461
|
+
return preferHorizontal
|
|
462
|
+
? [horizontal, vertical, oppositeHorizontal, oppositeVertical]
|
|
463
|
+
: [vertical, horizontal, oppositeVertical, oppositeHorizontal];
|
|
464
|
+
}
|
|
465
|
+
function defaultVerticalDirection(object, parent, sceneHeight) {
|
|
466
|
+
if (parent && Math.abs(object.y - parent.y) > 6) {
|
|
467
|
+
return object.y >= parent.y ? "below" : "above";
|
|
468
|
+
}
|
|
469
|
+
return object.y > sceneHeight * 0.62 ? "above" : "below";
|
|
470
|
+
}
|
|
471
|
+
function defaultHorizontalDirection(object, parent, sceneWidth) {
|
|
472
|
+
if (parent && Math.abs(object.x - parent.x) > 6) {
|
|
473
|
+
return object.x >= parent.x ? "right" : "left";
|
|
474
|
+
}
|
|
475
|
+
return object.x >= sceneWidth / 2 ? "right" : "left";
|
|
476
|
+
}
|
|
477
|
+
function createLabelPlacement(object, direction, attempt, labelMultiplier) {
|
|
478
|
+
const step = 14 * labelMultiplier;
|
|
479
|
+
switch (direction) {
|
|
480
|
+
case "above": {
|
|
481
|
+
const labelY = object.y - (object.radius + 18 * labelMultiplier + attempt * step);
|
|
482
|
+
return {
|
|
483
|
+
x: object.x,
|
|
484
|
+
labelY,
|
|
485
|
+
secondaryY: labelY - 16 * labelMultiplier,
|
|
486
|
+
textAnchor: "middle",
|
|
487
|
+
direction,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
case "below": {
|
|
491
|
+
const labelY = object.y + object.radius + 18 * labelMultiplier + attempt * step;
|
|
492
|
+
return {
|
|
493
|
+
x: object.x,
|
|
494
|
+
labelY,
|
|
495
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
496
|
+
textAnchor: "middle",
|
|
497
|
+
direction,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
case "left": {
|
|
501
|
+
const x = object.x - (object.visualRadius + 16 * labelMultiplier + attempt * step);
|
|
502
|
+
const labelY = object.y - 4 * labelMultiplier;
|
|
503
|
+
return {
|
|
504
|
+
x,
|
|
505
|
+
labelY,
|
|
506
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
507
|
+
textAnchor: "end",
|
|
508
|
+
direction,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
case "right": {
|
|
512
|
+
const x = object.x + object.visualRadius + 16 * labelMultiplier + attempt * step;
|
|
513
|
+
const labelY = object.y - 4 * labelMultiplier;
|
|
514
|
+
return {
|
|
515
|
+
x,
|
|
516
|
+
labelY,
|
|
517
|
+
secondaryY: labelY + 16 * labelMultiplier,
|
|
518
|
+
textAnchor: "start",
|
|
519
|
+
direction,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
function createSceneLayers(orbitVisuals, relations, events, leaders, objects, labels) {
|
|
381
525
|
const backOrbitIds = orbitVisuals
|
|
382
526
|
.filter((visual) => !visual.hidden && Boolean(visual.backArcPath))
|
|
383
527
|
.map((visual) => visual.renderId);
|
|
@@ -396,6 +540,10 @@ function createSceneLayers(orbitVisuals, relations, leaders, objects, labels) {
|
|
|
396
540
|
id: "relations",
|
|
397
541
|
renderIds: relations.filter((relation) => !relation.hidden).map((relation) => relation.renderId),
|
|
398
542
|
},
|
|
543
|
+
{
|
|
544
|
+
id: "events",
|
|
545
|
+
renderIds: events.filter((event) => !event.hidden).map((event) => event.renderId),
|
|
546
|
+
},
|
|
399
547
|
{
|
|
400
548
|
id: "objects",
|
|
401
549
|
renderIds: objects.filter((object) => !object.hidden).map((object) => object.renderId),
|
|
@@ -407,7 +555,7 @@ function createSceneLayers(orbitVisuals, relations, leaders, objects, labels) {
|
|
|
407
555
|
{ id: "metadata", renderIds: ["wo-title", "wo-subtitle", "wo-meta"] },
|
|
408
556
|
];
|
|
409
557
|
}
|
|
410
|
-
function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships) {
|
|
558
|
+
function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships, labelMultiplier) {
|
|
411
559
|
const groups = new Map();
|
|
412
560
|
const ensureGroup = (groupId) => {
|
|
413
561
|
if (!groupId) {
|
|
@@ -456,7 +604,7 @@ function createSceneGroups(objects, orbitVisuals, leaders, labels, relationships
|
|
|
456
604
|
}
|
|
457
605
|
}
|
|
458
606
|
for (const group of groups.values()) {
|
|
459
|
-
group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels);
|
|
607
|
+
group.contentBounds = calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier);
|
|
460
608
|
}
|
|
461
609
|
return [...groups.values()].sort((left, right) => left.label.localeCompare(right.label));
|
|
462
610
|
}
|
|
@@ -496,6 +644,40 @@ function createSceneRelations(document, objects) {
|
|
|
496
644
|
})
|
|
497
645
|
.sort((left, right) => left.relation.id.localeCompare(right.relation.id));
|
|
498
646
|
}
|
|
647
|
+
function createSceneEvents(events, objects, activeEventId) {
|
|
648
|
+
const objectMap = new Map(objects.map((object) => [object.objectId, object]));
|
|
649
|
+
return events
|
|
650
|
+
.map((event) => {
|
|
651
|
+
const objectIds = [...new Set([
|
|
652
|
+
...(event.targetObjectId ? [event.targetObjectId] : []),
|
|
653
|
+
...event.participantObjectIds,
|
|
654
|
+
])];
|
|
655
|
+
const positions = objectIds
|
|
656
|
+
.map((objectId) => objectMap.get(objectId))
|
|
657
|
+
.filter(Boolean);
|
|
658
|
+
const centroidX = positions.length > 0
|
|
659
|
+
? positions.reduce((sum, object) => sum + object.x, 0) / positions.length
|
|
660
|
+
: 0;
|
|
661
|
+
const centroidY = positions.length > 0
|
|
662
|
+
? positions.reduce((sum, object) => sum + object.y, 0) / positions.length
|
|
663
|
+
: 0;
|
|
664
|
+
return {
|
|
665
|
+
renderId: `${createRenderId(event.id)}-event`,
|
|
666
|
+
eventId: event.id,
|
|
667
|
+
event,
|
|
668
|
+
objectIds,
|
|
669
|
+
participantIds: [...event.participantObjectIds],
|
|
670
|
+
targetObjectId: event.targetObjectId,
|
|
671
|
+
x: centroidX,
|
|
672
|
+
y: centroidY,
|
|
673
|
+
hidden: event.hidden ||
|
|
674
|
+
positions.length === 0 ||
|
|
675
|
+
positions.every((object) => object.hidden) ||
|
|
676
|
+
(activeEventId !== null && event.id !== activeEventId),
|
|
677
|
+
};
|
|
678
|
+
})
|
|
679
|
+
.sort((left, right) => left.event.id.localeCompare(right.event.id));
|
|
680
|
+
}
|
|
499
681
|
function createSceneViewpoints(document, projection, preset, relationships, objectMap) {
|
|
500
682
|
const generatedOverview = createGeneratedOverviewViewpoint(document, projection, preset);
|
|
501
683
|
const drafts = new Map();
|
|
@@ -552,6 +734,7 @@ function createGeneratedOverviewViewpoint(document, projection, preset) {
|
|
|
552
734
|
summary: "Fit the whole system with the current atlas defaults.",
|
|
553
735
|
objectId: null,
|
|
554
736
|
selectedObjectId: null,
|
|
737
|
+
eventIds: [],
|
|
555
738
|
projection,
|
|
556
739
|
preset,
|
|
557
740
|
rotationDeg: 0,
|
|
@@ -588,6 +771,9 @@ function applyViewpointField(draft, field, value, document, projection, preset,
|
|
|
588
771
|
draft.select = normalizedValue;
|
|
589
772
|
}
|
|
590
773
|
return;
|
|
774
|
+
case "events":
|
|
775
|
+
draft.eventIds = splitListValue(normalizedValue);
|
|
776
|
+
return;
|
|
591
777
|
case "projection":
|
|
592
778
|
case "view":
|
|
593
779
|
draft.projection = parseViewProjection(normalizedValue) ?? projection;
|
|
@@ -646,6 +832,7 @@ function finalizeViewpointDraft(draft, projection, preset, objectMap) {
|
|
|
646
832
|
summary: draft.summary?.trim() || createViewpointSummary(label, objectId, filter),
|
|
647
833
|
objectId,
|
|
648
834
|
selectedObjectId,
|
|
835
|
+
eventIds: [...new Set(draft.eventIds ?? [])],
|
|
649
836
|
projection: draft.projection ?? projection,
|
|
650
837
|
preset: draft.preset ?? preset,
|
|
651
838
|
rotationDeg: draft.rotationDeg ?? 0,
|
|
@@ -720,6 +907,7 @@ function parseViewpointLayers(value) {
|
|
|
720
907
|
rawLayer === "orbits-back" ||
|
|
721
908
|
rawLayer === "orbits-front" ||
|
|
722
909
|
rawLayer === "relations" ||
|
|
910
|
+
rawLayer === "events" ||
|
|
723
911
|
rawLayer === "objects" ||
|
|
724
912
|
rawLayer === "labels" ||
|
|
725
913
|
rawLayer === "metadata") {
|
|
@@ -790,7 +978,7 @@ function createViewpointSummary(label, objectId, filter) {
|
|
|
790
978
|
}
|
|
791
979
|
return parts.join(" - ");
|
|
792
980
|
}
|
|
793
|
-
function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels) {
|
|
981
|
+
function calculateContentBounds(width, height, objects, orbitVisuals, leaders, labels, labelMultiplier) {
|
|
794
982
|
let minX = Number.POSITIVE_INFINITY;
|
|
795
983
|
let minY = Number.POSITIVE_INFINITY;
|
|
796
984
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
@@ -820,7 +1008,7 @@ function calculateContentBounds(width, height, objects, orbitVisuals, leaders, l
|
|
|
820
1008
|
for (const label of labels) {
|
|
821
1009
|
if (label.hidden)
|
|
822
1010
|
continue;
|
|
823
|
-
includeLabelBounds(label, include);
|
|
1011
|
+
includeLabelBounds(label, include, labelMultiplier);
|
|
824
1012
|
}
|
|
825
1013
|
if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
|
|
826
1014
|
return createBounds(0, 0, width, height);
|
|
@@ -862,13 +1050,10 @@ function includeObjectBounds(object, include) {
|
|
|
862
1050
|
include(object.x - object.visualRadius - 24, object.y - object.visualRadius - 16);
|
|
863
1051
|
include(object.x + object.visualRadius + 24, object.y + object.visualRadius + 36);
|
|
864
1052
|
}
|
|
865
|
-
function includeLabelBounds(label, include) {
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
include(
|
|
869
|
-
include(label.x + labelHalfWidth, label.y + 8);
|
|
870
|
-
include(label.x - labelHalfWidth, label.secondaryY - 14);
|
|
871
|
-
include(label.x + labelHalfWidth, label.secondaryY + 8);
|
|
1053
|
+
function includeLabelBounds(label, include, labelMultiplier) {
|
|
1054
|
+
const bounds = createLabelRectFromText(label.x, label.y, label.secondaryY, label.textAnchor, label.direction, label.label, label.secondaryLabel, labelMultiplier);
|
|
1055
|
+
include(bounds.left, bounds.top);
|
|
1056
|
+
include(bounds.right, bounds.bottom);
|
|
872
1057
|
}
|
|
873
1058
|
function placeObject(object, x, y, depth, positions, orbitDrafts, leaderDrafts, context) {
|
|
874
1059
|
if (positions.has(object.id)) {
|
|
@@ -1286,7 +1471,7 @@ function resolveParentId(object, objectMap) {
|
|
|
1286
1471
|
return null;
|
|
1287
1472
|
}
|
|
1288
1473
|
}
|
|
1289
|
-
function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels) {
|
|
1474
|
+
function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels, labelMultiplier) {
|
|
1290
1475
|
let minX = Number.POSITIVE_INFINITY;
|
|
1291
1476
|
let minY = Number.POSITIVE_INFINITY;
|
|
1292
1477
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
@@ -1315,7 +1500,7 @@ function calculateGroupBounds(group, objects, orbitVisuals, leaders, labels) {
|
|
|
1315
1500
|
}
|
|
1316
1501
|
for (const label of labels) {
|
|
1317
1502
|
if (!label.hidden && group.labelIds.includes(label.objectId)) {
|
|
1318
|
-
includeLabelBounds(label, include);
|
|
1503
|
+
includeLabelBounds(label, include, labelMultiplier);
|
|
1319
1504
|
}
|
|
1320
1505
|
}
|
|
1321
1506
|
if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
|
|
@@ -1340,12 +1525,29 @@ function resolveGroupRootObjectId(object, objectMap) {
|
|
|
1340
1525
|
}
|
|
1341
1526
|
return current.id;
|
|
1342
1527
|
}
|
|
1343
|
-
function createLabelRect(
|
|
1528
|
+
function createLabelRect(object, placement, labelMultiplier) {
|
|
1529
|
+
return createLabelRectFromText(placement.x, placement.labelY, placement.secondaryY, placement.textAnchor, placement.direction, object.label, object.secondaryLabel, labelMultiplier);
|
|
1530
|
+
}
|
|
1531
|
+
function createLabelRectFromText(x, labelY, secondaryY, textAnchor, direction, label, secondaryLabel, labelMultiplier) {
|
|
1532
|
+
const labelHalfWidth = estimateLabelHalfWidthFromText(label, secondaryLabel, labelMultiplier);
|
|
1533
|
+
const labelWidth = labelHalfWidth * 2;
|
|
1534
|
+
const topPadding = direction === "above" ? 18 : 12;
|
|
1535
|
+
const bottomPadding = direction === "above" ? 8 : 12;
|
|
1536
|
+
let left = x - labelHalfWidth;
|
|
1537
|
+
let right = x + labelHalfWidth;
|
|
1538
|
+
if (textAnchor === "start") {
|
|
1539
|
+
left = x;
|
|
1540
|
+
right = x + labelWidth;
|
|
1541
|
+
}
|
|
1542
|
+
else if (textAnchor === "end") {
|
|
1543
|
+
left = x - labelWidth;
|
|
1544
|
+
right = x;
|
|
1545
|
+
}
|
|
1344
1546
|
return {
|
|
1345
|
-
left
|
|
1346
|
-
right
|
|
1347
|
-
top: Math.min(labelY, secondaryY) -
|
|
1348
|
-
bottom: Math.max(labelY, secondaryY) +
|
|
1547
|
+
left,
|
|
1548
|
+
right,
|
|
1549
|
+
top: Math.min(labelY, secondaryY) - topPadding,
|
|
1550
|
+
bottom: Math.max(labelY, secondaryY) + bottomPadding,
|
|
1349
1551
|
};
|
|
1350
1552
|
}
|
|
1351
1553
|
function rectsOverlap(left, right) {
|
|
@@ -60,6 +60,7 @@ export interface WorldOrbitDocument {
|
|
|
60
60
|
system: WorldOrbitSystem | null;
|
|
61
61
|
groups: WorldOrbitGroup[];
|
|
62
62
|
relations: WorldOrbitRelation[];
|
|
63
|
+
events: WorldOrbitEvent[];
|
|
63
64
|
objects: WorldOrbitObject[];
|
|
64
65
|
}
|
|
65
66
|
export interface WorldOrbitAtlasDocument {
|
|
@@ -70,6 +71,7 @@ export interface WorldOrbitAtlasDocument {
|
|
|
70
71
|
system: WorldOrbitAtlasSystem | null;
|
|
71
72
|
groups: WorldOrbitGroup[];
|
|
72
73
|
relations: WorldOrbitRelation[];
|
|
74
|
+
events: WorldOrbitEvent[];
|
|
73
75
|
objects: WorldOrbitObject[];
|
|
74
76
|
diagnostics: WorldOrbitDiagnostic[];
|
|
75
77
|
}
|
|
@@ -81,6 +83,7 @@ export interface WorldOrbitDraftDocument {
|
|
|
81
83
|
system: WorldOrbitAtlasSystem | null;
|
|
82
84
|
groups: WorldOrbitGroup[];
|
|
83
85
|
relations: WorldOrbitRelation[];
|
|
86
|
+
events: WorldOrbitEvent[];
|
|
84
87
|
objects: WorldOrbitObject[];
|
|
85
88
|
diagnostics: WorldOrbitDiagnostic[];
|
|
86
89
|
}
|
|
@@ -132,6 +135,26 @@ export interface WorldOrbitRelation {
|
|
|
132
135
|
color: string | null;
|
|
133
136
|
hidden: boolean;
|
|
134
137
|
}
|
|
138
|
+
export interface WorldOrbitEvent {
|
|
139
|
+
id: string;
|
|
140
|
+
kind: string;
|
|
141
|
+
label: string;
|
|
142
|
+
summary: string | null;
|
|
143
|
+
targetObjectId: string | null;
|
|
144
|
+
participantObjectIds: string[];
|
|
145
|
+
timing: string | null;
|
|
146
|
+
visibility: string | null;
|
|
147
|
+
tags: string[];
|
|
148
|
+
color: string | null;
|
|
149
|
+
hidden: boolean;
|
|
150
|
+
positions: WorldOrbitEventPose[];
|
|
151
|
+
}
|
|
152
|
+
export interface WorldOrbitEventPose {
|
|
153
|
+
objectId: string;
|
|
154
|
+
placement: Placement | null;
|
|
155
|
+
inner?: UnitValue;
|
|
156
|
+
outer?: UnitValue;
|
|
157
|
+
}
|
|
135
158
|
export interface WorldOrbitResonance {
|
|
136
159
|
targetObjectId: string;
|
|
137
160
|
ratio: string;
|
|
@@ -212,6 +235,7 @@ export interface SceneRenderOptions {
|
|
|
212
235
|
preset?: RenderPresetName;
|
|
213
236
|
projection?: "document" | ViewProjection;
|
|
214
237
|
scaleModel?: Partial<RenderScaleModel>;
|
|
238
|
+
activeEventId?: string | null;
|
|
215
239
|
}
|
|
216
240
|
export interface RenderBounds {
|
|
217
241
|
minX: number;
|
|
@@ -294,7 +318,18 @@ export interface RenderSceneLabel {
|
|
|
294
318
|
direction: "above" | "below" | "left" | "right";
|
|
295
319
|
hidden: boolean;
|
|
296
320
|
}
|
|
297
|
-
export
|
|
321
|
+
export interface RenderSceneEvent {
|
|
322
|
+
renderId: string;
|
|
323
|
+
eventId: string;
|
|
324
|
+
event: WorldOrbitEvent;
|
|
325
|
+
objectIds: string[];
|
|
326
|
+
participantIds: string[];
|
|
327
|
+
targetObjectId: string | null;
|
|
328
|
+
x: number;
|
|
329
|
+
y: number;
|
|
330
|
+
hidden: boolean;
|
|
331
|
+
}
|
|
332
|
+
export type SceneLayerId = "background" | "guides" | "orbits-back" | "orbits-front" | "relations" | "events" | "objects" | "labels" | "metadata";
|
|
298
333
|
export interface RenderSceneViewpointFilter {
|
|
299
334
|
query: string | null;
|
|
300
335
|
objectTypes: Array<Exclude<WorldOrbitObjectType, "system">>;
|
|
@@ -307,6 +342,7 @@ export interface RenderSceneViewpoint {
|
|
|
307
342
|
summary: string;
|
|
308
343
|
objectId: string | null;
|
|
309
344
|
selectedObjectId: string | null;
|
|
345
|
+
eventIds: string[];
|
|
310
346
|
projection: ViewProjection;
|
|
311
347
|
preset: RenderPresetName | null;
|
|
312
348
|
rotationDeg: number;
|
|
@@ -368,6 +404,8 @@ export interface RenderScene {
|
|
|
368
404
|
groups: RenderSceneGroup[];
|
|
369
405
|
semanticGroups: RenderSceneSemanticGroup[];
|
|
370
406
|
viewpoints: RenderSceneViewpoint[];
|
|
407
|
+
events: RenderSceneEvent[];
|
|
408
|
+
activeEventId: string | null;
|
|
371
409
|
objects: RenderSceneObject[];
|
|
372
410
|
orbitVisuals: RenderOrbitVisual[];
|
|
373
411
|
relations: RenderSceneRelation[];
|
|
@@ -421,6 +459,7 @@ export interface WorldOrbitAtlasViewpoint {
|
|
|
421
459
|
summary: string;
|
|
422
460
|
focusObjectId: string | null;
|
|
423
461
|
selectedObjectId: string | null;
|
|
462
|
+
events: string[];
|
|
424
463
|
projection: ViewProjection;
|
|
425
464
|
preset: RenderPresetName | null;
|
|
426
465
|
zoom: number | null;
|
|
@@ -448,7 +487,7 @@ export interface WorldOrbitAtlasSystem {
|
|
|
448
487
|
viewpoints: WorldOrbitAtlasViewpoint[];
|
|
449
488
|
annotations: WorldOrbitAtlasAnnotation[];
|
|
450
489
|
}
|
|
451
|
-
export type AtlasDocumentPathKind = "system" | "defaults" | "metadata" | "group" | "object" | "viewpoint" | "annotation" | "relation";
|
|
490
|
+
export type AtlasDocumentPathKind = "system" | "defaults" | "metadata" | "group" | "event" | "event-pose" | "object" | "viewpoint" | "annotation" | "relation";
|
|
452
491
|
export interface AtlasDocumentPath {
|
|
453
492
|
kind: AtlasDocumentPathKind;
|
|
454
493
|
id?: string;
|