vizcraft 1.7.0 → 1.7.1
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/CHANGELOG.md +6 -0
- package/README.md +5 -2
- package/dist/builder.d.ts +11 -4
- package/dist/builder.js +29 -1
- package/dist/edges/paths.d.ts +10 -6
- package/dist/edges/paths.js +12 -6
- package/dist/edges/resolveEdgeGeometry.d.ts +12 -23
- package/dist/edges/resolveEdgeGeometry.js +16 -22
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/shapes/geometry.d.ts +12 -0
- package/dist/shapes/geometry.js +35 -7
- package/dist/types.d.ts +12 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# vizcraft
|
|
2
2
|
|
|
3
|
+
## 1.7.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#101](https://github.com/ChipiKaf/vizcraft/pull/101) [`c5084ad`](https://github.com/ChipiKaf/vizcraft/commit/c5084ad12a36b545df987cd425fd431cfb9903d4) Thanks [@ChipiKaf](https://github.com/ChipiKaf)! - Fix `resolveEdgeGeometry`: `startAnchor`/`endAnchor` now return the true boundary/port positions where the edge exits/enters each node, instead of the ~15%/~85% label positions. Added `startLabel` and `endLabel` fields as explicit aliases for the label positions. For self-loops, anchors correspond to the exit/entry points on the node boundary.
|
|
8
|
+
|
|
3
9
|
## 1.7.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ VizCraft is designed to make creating beautiful, animated node-link diagrams and
|
|
|
17
17
|
|
|
18
18
|
- **Fluent Builder API**: Define your visualization scene using a readable, chainable API.
|
|
19
19
|
- **Grid System**: Built-in 2D grid system for easy, structured layout of nodes.
|
|
20
|
+
- **Auto-Layout Algorithms**: Built-in circular and grid layouts, custom sync/async algorithm support (e.g. ELK), and a `getNodeBoundingBox` utility for robust layout integration.
|
|
20
21
|
- **Two Animation Systems**: Lightweight registry/CSS animations (e.g. edge `flow`) and data-only timeline animations (`AnimationSpec`).
|
|
21
22
|
- **Framework Agnostic**: The core logic is pure TypeScript and can be used with any framework or Vanilla JS.
|
|
22
23
|
- **Custom Overlays**: Create complex, custom UI elements that float on top of your visualization.
|
|
@@ -338,8 +339,10 @@ if (!geo) return; // edge not found or unresolvable
|
|
|
338
339
|
|
|
339
340
|
overlayPath.setAttribute('d', geo.d); // SVG path
|
|
340
341
|
positionToolbar(geo.mid); // midpoint
|
|
341
|
-
drawHandle(geo.startAnchor); //
|
|
342
|
-
drawHandle(geo.endAnchor); //
|
|
342
|
+
drawHandle(geo.startAnchor); // true boundary exit point
|
|
343
|
+
drawHandle(geo.endAnchor); // true boundary entry point
|
|
344
|
+
positionSourceLabel(geo.startLabel); // ~15% along path
|
|
345
|
+
positionTargetLabel(geo.endLabel); // ~85% along path
|
|
343
346
|
geo.waypoints.forEach(drawDot); // waypoints
|
|
344
347
|
if (geo.isSelfLoop) {
|
|
345
348
|
/* ... */
|
package/dist/builder.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Vec2, VizScene, VizNode, VizEdge, NodeLabel, EdgeLabel, RichText, RichTextToken, AnimationConfig, OverlayId, OverlayParams, VizGridConfig, ContainerConfig, EdgeRouting, EdgeMarkerType, EdgePathResolver, NodeOptions, EdgeOptions, PanZoomOptions, PanZoomController, VizSceneMutator, VizPlugin, VizEventMap, LayoutAlgorithm, SvgExportOptions } from './types';
|
|
1
|
+
import type { Vec2, VizScene, VizNode, VizEdge, NodeLabel, EdgeLabel, RichText, RichTextToken, AnimationConfig, OverlayId, OverlayParams, VizGridConfig, ContainerConfig, EdgeRouting, EdgeMarkerType, EdgePathResolver, NodeOptions, EdgeOptions, PanZoomOptions, PanZoomController, VizSceneMutator, VizPlugin, VizEventMap, SyncLayoutAlgorithm, LayoutAlgorithm, SvgExportOptions } from './types';
|
|
2
2
|
import { OverlayBuilder } from './overlays/builder';
|
|
3
3
|
import type { AnimationSpec } from './animation/spec';
|
|
4
4
|
import { type AnimationBuilder, type AnimatableProps, type TweenOptions } from './animation/builder';
|
|
@@ -12,12 +12,19 @@ export interface VizBuilder extends VizSceneMutator {
|
|
|
12
12
|
*/
|
|
13
13
|
use<O>(plugin: VizPlugin<O>, options?: O): VizBuilder;
|
|
14
14
|
/**
|
|
15
|
-
* Applies a layout algorithm to the current nodes and edges.
|
|
16
|
-
* @param algorithm The layout function to execute
|
|
15
|
+
* Applies a **synchronous** layout algorithm to the current nodes and edges.
|
|
16
|
+
* @param algorithm The layout function to execute (must return synchronously)
|
|
17
17
|
* @param options Optional configuration for the layout algorithm
|
|
18
18
|
* @returns The builder, for fluent chaining
|
|
19
19
|
*/
|
|
20
|
-
layout<O>(algorithm:
|
|
20
|
+
layout<O>(algorithm: SyncLayoutAlgorithm<O>, options?: O): VizBuilder;
|
|
21
|
+
/**
|
|
22
|
+
* Applies a layout algorithm that may be asynchronous (e.g. ELK via web workers).
|
|
23
|
+
* @param algorithm The layout function to execute (may return a Promise)
|
|
24
|
+
* @param options Optional configuration for the layout algorithm
|
|
25
|
+
* @returns A Promise that resolves to the builder, for fluent chaining
|
|
26
|
+
*/
|
|
27
|
+
layoutAsync<O>(algorithm: LayoutAlgorithm<O>, options?: O): Promise<VizBuilder>;
|
|
21
28
|
/**
|
|
22
29
|
* Listen for lifecycle events (e.g. 'build', 'mount').
|
|
23
30
|
* @param event The event name
|
package/dist/builder.js
CHANGED
|
@@ -17,6 +17,15 @@ import { getEffectiveNodeBounds } from './interaction/hitTest';
|
|
|
17
17
|
import { defaultCoreIconRegistry } from './shapes/icons';
|
|
18
18
|
import { NodeBuilderImpl, applyNodeOptions } from './nodes/builder';
|
|
19
19
|
import { EdgeBuilderImpl, applyEdgeOptions } from './edges/builder';
|
|
20
|
+
/**
|
|
21
|
+
* Runtime check for Promise-like values without `as any` casting.
|
|
22
|
+
*/
|
|
23
|
+
function isPromiseLike(value) {
|
|
24
|
+
return (typeof value === 'object' &&
|
|
25
|
+
value !== null &&
|
|
26
|
+
'then' in value &&
|
|
27
|
+
typeof value.then === 'function');
|
|
28
|
+
}
|
|
20
29
|
/**
|
|
21
30
|
* Sanitise a CSS color value for use as a suffix in an SVG marker `id`.
|
|
22
31
|
* Non-alphanumeric characters are replaced with underscores.
|
|
@@ -494,6 +503,26 @@ class VizBuilderImpl {
|
|
|
494
503
|
edges: scene.edges,
|
|
495
504
|
};
|
|
496
505
|
const result = algorithm(graph, options);
|
|
506
|
+
// Guard: if the algorithm returned a Promise-like, throw a helpful error
|
|
507
|
+
if (isPromiseLike(result)) {
|
|
508
|
+
throw new Error('VizBuilder.layout: received a Promise from the layout algorithm. ' +
|
|
509
|
+
'Use .layoutAsync() for async layout engines.');
|
|
510
|
+
}
|
|
511
|
+
this._applyLayoutResult(result);
|
|
512
|
+
return this;
|
|
513
|
+
}
|
|
514
|
+
async layoutAsync(algorithm, options) {
|
|
515
|
+
const scene = this.build();
|
|
516
|
+
const graph = {
|
|
517
|
+
nodes: scene.nodes,
|
|
518
|
+
edges: scene.edges,
|
|
519
|
+
};
|
|
520
|
+
const result = await algorithm(graph, options);
|
|
521
|
+
this._applyLayoutResult(result);
|
|
522
|
+
return this;
|
|
523
|
+
}
|
|
524
|
+
/** @internal Apply positions and waypoints from a layout result. */
|
|
525
|
+
_applyLayoutResult(result) {
|
|
497
526
|
// Apply computed node positions
|
|
498
527
|
for (const [id, pos] of Object.entries(result.nodes)) {
|
|
499
528
|
this.updateNode(id, { pos });
|
|
@@ -506,7 +535,6 @@ class VizBuilderImpl {
|
|
|
506
535
|
}
|
|
507
536
|
}
|
|
508
537
|
}
|
|
509
|
-
return this;
|
|
510
538
|
}
|
|
511
539
|
setEdgePathResolver(resolver) {
|
|
512
540
|
this._edgePathResolver = resolver;
|
package/dist/edges/paths.d.ts
CHANGED
|
@@ -43,11 +43,15 @@ export declare function computeEdgeEndpoints(start: VizNode | null, end: VizNode
|
|
|
43
43
|
* @param waypoints Optional intermediate points.
|
|
44
44
|
*/
|
|
45
45
|
export declare function computeEdgePath(start: Vec2, end: Vec2, routing?: EdgeRouting, waypoints?: Vec2[]): EdgePathResult;
|
|
46
|
+
/** Extends EdgePathResult with true boundary exit/entry points for self-loops. */
|
|
47
|
+
export interface SelfLoopResult extends EdgePathResult {
|
|
48
|
+
/** Boundary point where the loop departs the node. */
|
|
49
|
+
exitPoint: Vec2;
|
|
50
|
+
/** Boundary point where the loop returns to the node. */
|
|
51
|
+
entryPoint: Vec2;
|
|
52
|
+
}
|
|
46
53
|
/**
|
|
47
|
-
* Compute the SVG path and
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* @param node The target node
|
|
51
|
-
* @param edge The self-referencing VizEdge
|
|
54
|
+
* Compute the SVG path and positions for a self-loop edge.
|
|
55
|
+
* Exits and enters the same node on the specified side.
|
|
52
56
|
*/
|
|
53
|
-
export declare function computeSelfLoop(node: VizNode, edge: VizEdge):
|
|
57
|
+
export declare function computeSelfLoop(node: VizNode, edge: VizEdge): SelfLoopResult;
|
package/dist/edges/paths.js
CHANGED
|
@@ -297,11 +297,8 @@ function estimateNodeDims(shape) {
|
|
|
297
297
|
return { w: 60, h: 60 };
|
|
298
298
|
}
|
|
299
299
|
/**
|
|
300
|
-
* Compute the SVG path and
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
* @param node The target node
|
|
304
|
-
* @param edge The self-referencing VizEdge
|
|
300
|
+
* Compute the SVG path and positions for a self-loop edge.
|
|
301
|
+
* Exits and enters the same node on the specified side.
|
|
305
302
|
*/
|
|
306
303
|
export function computeSelfLoop(node, edge) {
|
|
307
304
|
const c = effectivePos(node);
|
|
@@ -314,6 +311,7 @@ export function computeSelfLoop(node, edge) {
|
|
|
314
311
|
const spread = Math.min(20, (side === 'top' || side === 'bottom' ? w : h) * 0.8);
|
|
315
312
|
let d = '';
|
|
316
313
|
let start, mid, end;
|
|
314
|
+
let exitPt, entryPt;
|
|
317
315
|
switch (side) {
|
|
318
316
|
case 'top': {
|
|
319
317
|
const sx = c.x - spread / 2;
|
|
@@ -322,6 +320,8 @@ export function computeSelfLoop(node, edge) {
|
|
|
322
320
|
const ey = c.y - h / 2;
|
|
323
321
|
const cpY = sy - size * 1.5;
|
|
324
322
|
d = `M ${sx} ${sy} C ${sx - spread / 2} ${cpY}, ${ex + spread / 2} ${cpY}, ${ex} ${ey}`;
|
|
323
|
+
exitPt = { x: sx, y: sy };
|
|
324
|
+
entryPt = { x: ex, y: ey };
|
|
325
325
|
start = { x: sx, y: sy - size * 0.2 };
|
|
326
326
|
mid = { x: c.x, y: sy - size };
|
|
327
327
|
end = { x: ex, y: ey - size * 0.2 };
|
|
@@ -334,6 +334,8 @@ export function computeSelfLoop(node, edge) {
|
|
|
334
334
|
const ey = c.y + h / 2;
|
|
335
335
|
const cpY = sy + size * 1.5;
|
|
336
336
|
d = `M ${sx} ${sy} C ${sx - spread / 2} ${cpY}, ${ex + spread / 2} ${cpY}, ${ex} ${ey}`;
|
|
337
|
+
exitPt = { x: sx, y: sy };
|
|
338
|
+
entryPt = { x: ex, y: ey };
|
|
337
339
|
start = { x: sx, y: sy + size * 0.2 };
|
|
338
340
|
mid = { x: c.x, y: sy + size };
|
|
339
341
|
end = { x: ex, y: ey + size * 0.2 };
|
|
@@ -346,6 +348,8 @@ export function computeSelfLoop(node, edge) {
|
|
|
346
348
|
const ey = c.y + spread / 2;
|
|
347
349
|
const cpX = sx - size * 1.5;
|
|
348
350
|
d = `M ${sx} ${sy} C ${cpX} ${sy - spread / 2}, ${cpX} ${ey + spread / 2}, ${ex} ${ey}`;
|
|
351
|
+
exitPt = { x: sx, y: sy };
|
|
352
|
+
entryPt = { x: ex, y: ey };
|
|
349
353
|
start = { x: sx - size * 0.2, y: sy };
|
|
350
354
|
mid = { x: sx - size, y: c.y };
|
|
351
355
|
end = { x: ex - size * 0.2, y: ey };
|
|
@@ -358,11 +362,13 @@ export function computeSelfLoop(node, edge) {
|
|
|
358
362
|
const ey = c.y + spread / 2;
|
|
359
363
|
const cpX = sx + size * 1.5;
|
|
360
364
|
d = `M ${sx} ${sy} C ${cpX} ${sy - spread / 2}, ${cpX} ${ey + spread / 2}, ${ex} ${ey}`;
|
|
365
|
+
exitPt = { x: sx, y: sy };
|
|
366
|
+
entryPt = { x: ex, y: ey };
|
|
361
367
|
start = { x: sx + size * 0.2, y: sy };
|
|
362
368
|
mid = { x: sx + size, y: c.y };
|
|
363
369
|
end = { x: ex + size * 0.2, y: ey };
|
|
364
370
|
break;
|
|
365
371
|
}
|
|
366
372
|
}
|
|
367
|
-
return { d, start, mid, end };
|
|
373
|
+
return { d, start, mid, end, exitPoint: exitPt, entryPoint: entryPt };
|
|
368
374
|
}
|
|
@@ -1,26 +1,19 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Convenience function that resolves the full rendered geometry for an edge
|
|
3
|
-
* in a single call. Handles node lookup, dangling edges, self-loop detection,
|
|
4
|
-
* port/angle/boundary anchors, waypoints, and routing.
|
|
5
|
-
*
|
|
6
|
-
* @module
|
|
7
|
-
*/
|
|
1
|
+
/** Resolves full rendered geometry for an edge in a single call. @module */
|
|
8
2
|
import type { Vec2, VizScene, VizEdge, VizNode } from '../types';
|
|
9
3
|
import type { EdgePathResult } from './paths';
|
|
10
|
-
/**
|
|
11
|
-
* Fully resolved edge geometry returned by {@link resolveEdgeGeometry}.
|
|
12
|
-
*
|
|
13
|
-
* Extends `EdgePathResult` with extra convenience fields so consumers
|
|
14
|
-
* never need to orchestrate multiple helpers manually.
|
|
15
|
-
*/
|
|
4
|
+
/** Fully resolved edge geometry. Extends `EdgePathResult` with anchor and label positions. */
|
|
16
5
|
export interface ResolvedEdgeGeometry extends EdgePathResult {
|
|
17
|
-
/**
|
|
6
|
+
/** Boundary/port position where the edge exits the source node. */
|
|
18
7
|
startAnchor: Vec2;
|
|
19
|
-
/**
|
|
8
|
+
/** Boundary/port position where the edge enters the target node. */
|
|
20
9
|
endAnchor: Vec2;
|
|
10
|
+
/** Label position ~15% along the path (alias of `start`). */
|
|
11
|
+
startLabel: Vec2;
|
|
12
|
+
/** Label position ~85% along the path (alias of `end`). */
|
|
13
|
+
endLabel: Vec2;
|
|
21
14
|
/** Waypoints used for the path (empty array when none). */
|
|
22
15
|
waypoints: Vec2[];
|
|
23
|
-
/** Whether this edge is a self-loop
|
|
16
|
+
/** Whether this edge is a self-loop. */
|
|
24
17
|
isSelfLoop: boolean;
|
|
25
18
|
}
|
|
26
19
|
/**
|
|
@@ -43,12 +36,8 @@ export interface ResolvedEdgeGeometry extends EdgePathResult {
|
|
|
43
36
|
*/
|
|
44
37
|
export declare function resolveEdgeGeometry(scene: VizScene, edgeId: string): ResolvedEdgeGeometry | null;
|
|
45
38
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* processing many edges).
|
|
50
|
-
*
|
|
51
|
-
* @internal exported for advanced consumers and testing — prefer
|
|
52
|
-
* {@link resolveEdgeGeometry} for typical usage.
|
|
39
|
+
* Resolve geometry from an edge + a pre-built node map.
|
|
40
|
+
* Prefer {@link resolveEdgeGeometry} unless you already hold the map.
|
|
41
|
+
* @internal
|
|
53
42
|
*/
|
|
54
43
|
export declare function resolveEdgeGeometryFromData(edge: VizEdge, nodesById: Map<string, VizNode>): ResolvedEdgeGeometry | null;
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Convenience function that resolves the full rendered geometry for an edge
|
|
3
|
-
* in a single call. Handles node lookup, dangling edges, self-loop detection,
|
|
4
|
-
* port/angle/boundary anchors, waypoints, and routing.
|
|
5
|
-
*
|
|
6
|
-
* @module
|
|
7
|
-
*/
|
|
1
|
+
/** Resolves full rendered geometry for an edge in a single call. @module */
|
|
8
2
|
import { computeEdgeEndpoints, computeEdgePath, computeSelfLoop, } from './paths';
|
|
9
|
-
// ── Implementation ──────────────────────────────────────────────────────────
|
|
10
3
|
/**
|
|
11
4
|
* Resolve all rendered geometry for a single edge in one call.
|
|
12
5
|
*
|
|
@@ -33,40 +26,41 @@ export function resolveEdgeGeometry(scene, edgeId) {
|
|
|
33
26
|
return resolveEdgeGeometryFromData(edge, nodesById);
|
|
34
27
|
}
|
|
35
28
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* processing many edges).
|
|
40
|
-
*
|
|
41
|
-
* @internal exported for advanced consumers and testing — prefer
|
|
42
|
-
* {@link resolveEdgeGeometry} for typical usage.
|
|
29
|
+
* Resolve geometry from an edge + a pre-built node map.
|
|
30
|
+
* Prefer {@link resolveEdgeGeometry} unless you already hold the map.
|
|
31
|
+
* @internal
|
|
43
32
|
*/
|
|
44
33
|
export function resolveEdgeGeometryFromData(edge, nodesById) {
|
|
45
|
-
// ── Node lookup (null-safe for dangling edges) ──────────────────────────
|
|
46
34
|
const startNode = edge.from ? (nodesById.get(edge.from) ?? null) : null;
|
|
47
35
|
const endNode = edge.to ? (nodesById.get(edge.to) ?? null) : null;
|
|
48
|
-
// Bail out if a referenced node id doesn't exist.
|
|
49
36
|
if (edge.from && !startNode)
|
|
50
37
|
return null;
|
|
51
38
|
if (edge.to && !endNode)
|
|
52
39
|
return null;
|
|
53
|
-
// Bail out if both endpoints are entirely unresolvable.
|
|
54
40
|
if (!startNode && !edge.fromAt && !endNode && !edge.toAt)
|
|
55
41
|
return null;
|
|
56
|
-
// ── Self-loop detection ─────────────────────────────────────────────────
|
|
57
42
|
const isSelfLoop = !!(startNode && endNode && startNode === endNode);
|
|
58
43
|
let pathResult;
|
|
44
|
+
let anchorStart;
|
|
45
|
+
let anchorEnd;
|
|
59
46
|
if (isSelfLoop) {
|
|
60
|
-
|
|
47
|
+
const selfLoop = computeSelfLoop(startNode, edge);
|
|
48
|
+
pathResult = selfLoop;
|
|
49
|
+
anchorStart = selfLoop.exitPoint;
|
|
50
|
+
anchorEnd = selfLoop.entryPoint;
|
|
61
51
|
}
|
|
62
52
|
else {
|
|
63
53
|
const endpoints = computeEdgeEndpoints(startNode, endNode, edge);
|
|
64
54
|
pathResult = computeEdgePath(endpoints.start, endpoints.end, edge.routing, edge.waypoints);
|
|
55
|
+
anchorStart = endpoints.start;
|
|
56
|
+
anchorEnd = endpoints.end;
|
|
65
57
|
}
|
|
66
58
|
return {
|
|
67
59
|
...pathResult,
|
|
68
|
-
startAnchor:
|
|
69
|
-
endAnchor:
|
|
60
|
+
startAnchor: anchorStart,
|
|
61
|
+
endAnchor: anchorEnd,
|
|
62
|
+
startLabel: pathResult.start,
|
|
63
|
+
endLabel: pathResult.end,
|
|
70
64
|
waypoints: edge.waypoints ?? [],
|
|
71
65
|
isSelfLoop,
|
|
72
66
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export * from './edges/paths';
|
|
|
9
9
|
export * from './edges/labels';
|
|
10
10
|
export * from './edges/styles';
|
|
11
11
|
export * from './edges/resolveEdgeGeometry';
|
|
12
|
-
export { getDefaultPorts, getNodePorts, findPort, resolvePortPosition, computeNodeAnchorAtAngle, } from './shapes/geometry';
|
|
12
|
+
export { getDefaultPorts, getNodePorts, findPort, resolvePortPosition, computeNodeAnchorAtAngle, getNodeBoundingBox, } from './shapes/geometry';
|
|
13
13
|
export * from './animation/spec';
|
|
14
14
|
export * from './animation/builder';
|
|
15
15
|
export * from './animation/playback';
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ export * from './edges/paths';
|
|
|
9
9
|
export * from './edges/labels';
|
|
10
10
|
export * from './edges/styles';
|
|
11
11
|
export * from './edges/resolveEdgeGeometry';
|
|
12
|
-
export { getDefaultPorts, getNodePorts, findPort, resolvePortPosition, computeNodeAnchorAtAngle, } from './shapes/geometry';
|
|
12
|
+
export { getDefaultPorts, getNodePorts, findPort, resolvePortPosition, computeNodeAnchorAtAngle, getNodeBoundingBox, } from './shapes/geometry';
|
|
13
13
|
export * from './animation/spec';
|
|
14
14
|
export * from './animation/builder';
|
|
15
15
|
export * from './animation/playback';
|
|
@@ -52,3 +52,15 @@ export declare function findPort(node: VizNode, portId: string): NodePort | unde
|
|
|
52
52
|
* Returns `undefined` if the port id is not found.
|
|
53
53
|
*/
|
|
54
54
|
export declare function resolvePortPosition(node: VizNode, portId: string): Vec2 | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Compute the tight axis-aligned bounding-box size of a node shape.
|
|
57
|
+
*
|
|
58
|
+
* Returns `{ width, height }` that enclose the rendered shape, accounting for
|
|
59
|
+
* orientation, direction, pointer height, and other shape-specific parameters.
|
|
60
|
+
* Intended as a convenience helper for layout algorithms and collision
|
|
61
|
+
* detection, to avoid duplicating per-shape switch logic.
|
|
62
|
+
*/
|
|
63
|
+
export declare function getNodeBoundingBox(shape: NodeShape): {
|
|
64
|
+
width: number;
|
|
65
|
+
height: number;
|
|
66
|
+
};
|
package/dist/shapes/geometry.js
CHANGED
|
@@ -1404,7 +1404,7 @@ export function resolvePortPosition(node, portId) {
|
|
|
1404
1404
|
return { x: pos.x + port.offset.x, y: pos.y + port.offset.y };
|
|
1405
1405
|
}
|
|
1406
1406
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
1407
|
-
/**
|
|
1407
|
+
/** Axis-aligned half-width / half-height for each node shape. */
|
|
1408
1408
|
function shapeBoundingBox(shape) {
|
|
1409
1409
|
switch (shape.kind) {
|
|
1410
1410
|
case 'circle':
|
|
@@ -1415,16 +1415,32 @@ function shapeBoundingBox(shape) {
|
|
|
1415
1415
|
return { hw: shape.w / 2, hh: shape.h / 2 };
|
|
1416
1416
|
case 'ellipse':
|
|
1417
1417
|
return { hw: shape.rx, hh: shape.ry };
|
|
1418
|
-
case 'hexagon':
|
|
1419
|
-
|
|
1418
|
+
case 'hexagon': {
|
|
1419
|
+
const orientation = shape.orientation ?? 'pointy';
|
|
1420
|
+
// pointy-top: width = r·√3, height = 2r
|
|
1421
|
+
// flat-top: width = 2r, height = r·√3
|
|
1422
|
+
const SQRT3_2 = Math.sqrt(3) / 2;
|
|
1423
|
+
return orientation === 'pointy'
|
|
1424
|
+
? { hw: shape.r * SQRT3_2, hh: shape.r }
|
|
1425
|
+
: { hw: shape.r, hh: shape.r * SQRT3_2 };
|
|
1426
|
+
}
|
|
1420
1427
|
case 'cylinder':
|
|
1421
1428
|
return { hw: shape.w / 2, hh: shape.h / 2 };
|
|
1422
1429
|
case 'arc':
|
|
1423
1430
|
return { hw: shape.r, hh: shape.r };
|
|
1424
|
-
case 'blockArrow':
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1431
|
+
case 'blockArrow': {
|
|
1432
|
+
const dir = shape.direction ?? 'right';
|
|
1433
|
+
return dir === 'up' || dir === 'down'
|
|
1434
|
+
? { hw: shape.headWidth / 2, hh: shape.length / 2 }
|
|
1435
|
+
: { hw: shape.length / 2, hh: shape.headWidth / 2 };
|
|
1436
|
+
}
|
|
1437
|
+
case 'callout': {
|
|
1438
|
+
const side = shape.pointerSide ?? 'bottom';
|
|
1439
|
+
const pH = shape.pointerHeight ?? Math.round(shape.h * 0.25);
|
|
1440
|
+
const hw = shape.w / 2 + (side === 'left' || side === 'right' ? pH : 0);
|
|
1441
|
+
const hh = shape.h / 2 + (side === 'top' || side === 'bottom' ? pH : 0);
|
|
1442
|
+
return { hw, hh };
|
|
1443
|
+
}
|
|
1428
1444
|
case 'cloud':
|
|
1429
1445
|
return { hw: shape.w / 2, hh: shape.h / 2 };
|
|
1430
1446
|
case 'cross':
|
|
@@ -1458,3 +1474,15 @@ function shapeBoundingBox(shape) {
|
|
|
1458
1474
|
return { hw: 0, hh: 0 };
|
|
1459
1475
|
}
|
|
1460
1476
|
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Compute the tight axis-aligned bounding-box size of a node shape.
|
|
1479
|
+
*
|
|
1480
|
+
* Returns `{ width, height }` that enclose the rendered shape, accounting for
|
|
1481
|
+
* orientation, direction, pointer height, and other shape-specific parameters.
|
|
1482
|
+
* Intended as a convenience helper for layout algorithms and collision
|
|
1483
|
+
* detection, to avoid duplicating per-shape switch logic.
|
|
1484
|
+
*/
|
|
1485
|
+
export function getNodeBoundingBox(shape) {
|
|
1486
|
+
const { hw, hh } = shapeBoundingBox(shape);
|
|
1487
|
+
return { width: hw * 2, height: hh * 2 };
|
|
1488
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -862,6 +862,16 @@ export interface LayoutResult {
|
|
|
862
862
|
}>;
|
|
863
863
|
}
|
|
864
864
|
/**
|
|
865
|
-
* A layout algorithm computes positions for nodes in a graph.
|
|
865
|
+
* A synchronous layout algorithm that computes positions for nodes in a graph.
|
|
866
|
+
*
|
|
867
|
+
* Use this type for algorithms that return results immediately.
|
|
868
|
+
* See also {@link LayoutAlgorithm} for algorithms that may be async.
|
|
869
|
+
*/
|
|
870
|
+
export type SyncLayoutAlgorithm<Options = any> = (graph: LayoutGraph, options?: Options) => LayoutResult;
|
|
871
|
+
/**
|
|
872
|
+
* A layout algorithm that may be synchronous or asynchronous.
|
|
873
|
+
*
|
|
874
|
+
* Use with {@link VizBuilder.layoutAsync} for async engines (e.g. ELK via
|
|
875
|
+
* web workers). For the sync-only variant, see {@link SyncLayoutAlgorithm}.
|
|
866
876
|
*/
|
|
867
|
-
export type LayoutAlgorithm<Options = any> = (graph: LayoutGraph, options?: Options) => LayoutResult
|
|
877
|
+
export type LayoutAlgorithm<Options = any> = (graph: LayoutGraph, options?: Options) => LayoutResult | Promise<LayoutResult>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vizcraft",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "A fluent, type-safe SVG scene builder for composing nodes, edges, animations, and overlays with incremental DOM updates and no framework dependency.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"visualization",
|