@neo4j-nvl/base 1.2.0-e691fdb4 → 1.2.0-ec100981

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +1 -1
  3. package/dist/base.mjs +1 -1
  4. package/dist/types/index.d.ts +5 -4
  5. package/dist/types/layouts/animatedlayout/AnimatedLayout.d.ts +2 -2
  6. package/dist/types/layouts/d3forcelayout/constants.d.ts +0 -1
  7. package/dist/types/layouts/d3forcelayout/d3ForceLayout.d.ts +2 -2
  8. package/dist/types/layouts/forcedirectedlayout/physlayout/PhysLayout.d.ts +83 -13
  9. package/dist/types/layouts/forcedirectedlayout/physlayout/clustering/ClusterByMerger.d.ts +60 -0
  10. package/dist/types/layouts/forcedirectedlayout/physlayout/clustering/SolarMerger.d.ts +55 -0
  11. package/dist/types/layouts/forcedirectedlayout/physlayout/clustering/clustering-utils.d.ts +112 -0
  12. package/dist/types/layouts/forcedirectedlayout/physlayout/clustering/clustering-utils.test.d.ts +1 -0
  13. package/dist/types/layouts/forcedirectedlayout/physlayout/{solarmerger → clustering}/types.d.ts +5 -0
  14. package/dist/types/layouts/forcedirectedlayout/physlayout/shaders/multilevel-fragment.d.ts +1 -1
  15. package/dist/types/layouts/forcedirectedlayout/physlayout/shaders/multilevel-repulsive-fragment.d.ts +1 -1
  16. package/dist/types/layouts/freeLayout/FreeLayout.d.ts +2 -4
  17. package/dist/types/layouts/gridLayout/GridLayout.d.ts +2 -6
  18. package/dist/types/modules/NvlController.d.ts +30 -2
  19. package/dist/types/modules/dataset.d.ts +3 -1
  20. package/dist/types/modules/state/types.d.ts +34 -15
  21. package/dist/types/renderers/domrenderer/canvasrenderer/arrowDrawing.d.ts +2 -16
  22. package/dist/types/renderers/domrenderer/shared/ImageCache.d.ts +1 -0
  23. package/dist/types/renderers/domrenderer/shared/arrows/arrows.d.ts +4 -6
  24. package/dist/types/renderers/webglrenderer/Controller.d.ts +26 -6
  25. package/dist/types/renderers/webglrenderer/IconLayerRegistry.d.ts +14 -0
  26. package/dist/types/renderers/webglrenderer/IconLayerRegistry.test.d.ts +1 -0
  27. package/dist/types/renderers/webglrenderer/Renderer.d.ts +43 -2
  28. package/dist/types/renderers/webglrenderer/node-animation-fragment.d.ts +1 -1
  29. package/dist/types/renderers/webglrenderer/node-animation-vertex.d.ts +1 -1
  30. package/dist/types/renderers/webglrenderer/node-fragment-point.d.ts +1 -1
  31. package/dist/types/renderers/webglrenderer/node-vertex-point.d.ts +1 -1
  32. package/dist/types/renderers/webglrenderer/rel-glsl-utils.d.ts +1 -0
  33. package/dist/types/renderers/webglrenderer/rel-vertex-arrow.d.ts +2 -0
  34. package/dist/types/renderers/webglrenderer/rel-vertex-line.d.ts +1 -1
  35. package/dist/types/renderers/webglrenderer/viewport-box-fragment.d.ts +1 -1
  36. package/dist/types/renderers/webglrenderer/viewport-box-vertex.d.ts +1 -1
  37. package/dist/types/types/graph-element.d.ts +9 -1
  38. package/dist/types/types/nvl-window-functions.d.ts +2 -2
  39. package/dist/types/utils/constants.d.ts +5 -2
  40. package/dist/types/utils/geometry.d.ts +16 -0
  41. package/dist/types/utils/layoutSeeding.d.ts +7 -0
  42. package/dist/types/utils/layoutSeeding.test.d.ts +1 -0
  43. package/dist/types/utils/webWorkerUtils.d.ts +1 -1
  44. package/dist/types/utils/zoomFunctions.d.ts +9 -2
  45. package/package.json +5 -5
  46. package/dist/types/layouts/forcedirectedlayout/ForceCytoLayout.d.ts +0 -22
  47. package/dist/types/layouts/forcedirectedlayout/cosebilkentlayout/CoseBilkentLayout.d.ts +0 -33
  48. package/dist/types/layouts/forcedirectedlayout/physlayout/solarmerger/SolarMerger.d.ts +0 -21
  49. package/dist/types/layouts/forcedirectedlayout/physlayout/solarmerger/solar-placer.d.ts +0 -2
  50. /package/dist/types/layouts/forcedirectedlayout/physlayout/{solarmerger/SolarMerger.bench.d.ts → clustering/ClusterByMerger.test.d.ts} +0 -0
  51. /package/dist/types/layouts/forcedirectedlayout/physlayout/{solarmerger/SolarMerger.test.d.ts → clustering/SolarMerger.bench.d.ts} +0 -0
  52. /package/dist/types/layouts/forcedirectedlayout/physlayout/{solarmerger/solar-placer.test.d.ts → clustering/SolarMerger.test.d.ts} +0 -0
@@ -8,6 +8,7 @@ export interface DataSet<T> {
8
8
  idToPosition: Record<string, Node & Point>;
9
9
  idToHtmlOverlay?: Record<string, HTMLElement>;
10
10
  version: number;
11
+ maxDimension: number;
11
12
  addChannel: (this: DataSet<T>, channelName: string) => void;
12
13
  removeChannel: (this: DataSet<T>, channelName: string) => void;
13
14
  clearChannel: (this: DataSet<T>, channelName: string) => void;
@@ -29,7 +30,8 @@ export interface Channel<T> {
29
30
  updates: Record<string, T>;
30
31
  removes: Record<string, T>;
31
32
  }
32
- export declare const getHtmlProperty: (item: Partial<Node | Relationship>) => HTMLElement | undefined;
33
+ export declare const getHtmlKey: (item: Node | Relationship) => "html" | "captionHtml";
34
+ export declare const getHtmlProperty: (item: Node | Relationship) => HTMLElement | undefined;
33
35
  /**
34
36
  * Creates an empty dataset in which a graph be stored and updated using mobx actions.
35
37
  * @returns A new and empty {@link DataSet}.
@@ -43,13 +43,26 @@ export interface ForceDirectedOptions {
43
43
  */
44
44
  intelWorkaround?: boolean;
45
45
  /**
46
- * @deprecated This option will be removed in a future release. For small datasets, use the d3Force layout instead.
47
- * Whether to enable automatic switching to CoseBilkent layout for small graphs.
48
- * When enabled, small graphs will automatically use the CoseBilkent layout algorithm
49
- * which can provide better initial positioning for smaller datasets.
50
- * @defaultValue true
46
+ * How initial node positions are seeded before the physics simulation runs.
47
+ * @defaultValue 'box'
48
+ */
49
+ seedingMethod?: 'box' | 'circle';
50
+ /**
51
+ * @experimental
52
+ * Milliseconds of synchronous physics simulation to run before the layout is first rendered.
53
+ * Precompute stops early if the simulation stabilizes before the time budget is reached.
54
+ * @defaultValue 0
55
+ * @note Blocks the main thread, so use sparingly. Will be capped at 1000.
51
56
  */
52
- enableCytoscape?: boolean;
57
+ precomputeMs?: number;
58
+ /**
59
+ * Optional function to group nodes into physical clusters based on a custom identifier.
60
+ * Nodes returning the same string or number will be grouped together in the layout.
61
+ * Nodes returning undefined will be treated as independent.
62
+ *
63
+ * @note Ensure this function has a stable reference to avoid evaluation on every render.
64
+ */
65
+ clusterBy?: (nodeId: string) => string | number | undefined;
53
66
  }
54
67
  /**
55
68
  * The options for the hierarchical layout
@@ -224,17 +237,23 @@ export interface NvlOptions {
224
237
  minimapViewportBoxColor?: string;
225
238
  };
226
239
  /**
227
- * @internal
228
- * @hidden
229
- * Defines a time limit in ms for how long layout iterations may run
230
- * before yielding to the renderer.
231
- * Higher values let layout stabilisation complete faster
232
- * at the cost of less frequent rendering during stabilisation.
233
- * Lower values keep rendering smoother at the cost of slower layout movement.
240
+ * Target minimum presentation fps while layout is active, at the cost of stabilization speed when set higher.
241
+ * Budgets layout steps only. Actual fps may undershoot when rendering is expensive.
242
+ *
243
+ * @defaultValue 3
244
+ *
245
+ * @note Will be capped between 1 and 120.
246
+ */
247
+ minFps?: number;
248
+ /**
249
+ * Target maximum presentation fps while layout is active; extra time in the tick goes to layout stabilization.
250
+ * Budgets layout steps only. Actual fps may undershoot when rendering is expensive.
251
+ *
252
+ * @defaultValue 30
234
253
  *
235
- * @defaultValue 300
254
+ * @note Will be capped between 1 and 120.
236
255
  */
237
- layoutTimeLimit?: number;
256
+ maxFps?: number;
238
257
  /**
239
258
  * Disables tracking of library usage
240
259
  * @defaultValue false
@@ -18,20 +18,6 @@ export declare const calculateLabelDimensions: (fullCaption: string, measureWidt
18
18
  label: string;
19
19
  width: number;
20
20
  };
21
- /**
22
- * Calculate label transform including rotation and flip adjustments.
23
- *
24
- * @param angle - The base rotation angle in radians
25
- * @param fontSize - The font size in pixels (including pixel ratio scaling)
26
- * @param flip - Whether to flip the label (applies additional rotation)
27
- * @param point - The base point (used to calculate offset in flip case)
28
- * @returns object with final position and angle
29
- */
30
- export declare const calculateLabelTransform: (angle: number, fontSize: number, flip: boolean, point: Point) => {
31
- finalX: number;
32
- finalY: number;
33
- finalAngle: number;
34
- };
35
21
  /**
36
22
  * Calculate vertical alignment offset for label based on caption alignment.
37
23
  *
@@ -55,7 +41,7 @@ export declare const calculateLabelHeightAlign: (captionAlign: string, fontSize:
55
41
  * @param flip - Whether the label is flipped
56
42
  * @returns object with position, rotation, width, and height for hit detection
57
43
  */
58
- export declare const calculateLabelGeometry: (point: Point, angle: number, maxWidth: number, fontSize: number, heightAlign: number, padding: number, pixelRatio: number, flip: boolean) => {
44
+ export declare const calculateLabelGeometry: (point: Point, angle: number, maxWidth: number, fontSize: number, heightAlign: number, padding: number, pixelRatio: number) => {
59
45
  position: {
60
46
  x: number;
61
47
  y: number;
@@ -164,5 +150,5 @@ export declare const drawLoop: (ctx: CanvasRenderingContext2D, fromPoint: Point,
164
150
  * @param fontColor - The font color to use
165
151
  * @param flip - Whether to flip the label orientation (default false)
166
152
  */
167
- export declare const drawLabel: (ctx: CanvasRenderingContext2D, point: Point, angle: number, maxWidth: number, rel: Relationship, bundle: ArrowBundle, disabledItemStyles: DisabledItemStyles, fontColor: string, flip?: boolean) => void;
153
+ export declare const drawLabel: (ctx: CanvasRenderingContext2D, point: Point, angle: number, maxWidth: number, rel: Relationship, bundle: ArrowBundle, disabledItemStyles: DisabledItemStyles, fontColor: string) => void;
168
154
  export {};
@@ -4,6 +4,7 @@ declare class ImageCache {
4
4
  private readonly onImageLoadedCb?;
5
5
  constructor(onImageLoaded?: () => void);
6
6
  getImage(src: string, inverted?: boolean): HTMLCanvasElement;
7
+ isDrawn(src: string, inverted?: boolean): boolean;
7
8
  private getOrCreateEntry;
8
9
  private invertCanvas;
9
10
  private loadImage;
@@ -44,12 +44,8 @@ export declare const getSelfArrowControlPoints: (rel: Relationship, node: Node,
44
44
  };
45
45
  nodeGap: number;
46
46
  };
47
- /**
48
- * Calculates position, angle and flip for a self-referencing relationship label.
49
- * Used by both Canvas and SVG renderers to ensure consistent positioning.
50
- * This function handles all the math internally including headAngle and headSelectedAdjustment calculations.
51
- */
52
- export declare const getSelfArrowLabelPosition: (apexPoint: Point, angle: number, endPoint: Point, control2Point: Point, isSelected: boolean, selectionRings: BorderStyle["rings"], relWidth?: number) => {
47
+ /** Calculates anchor position and readable rotation for a self-referencing relationship label. */
48
+ export declare const getSelfArrowLabelPosition: (apexPoint: Point, angle: number, endPoint: Point, control2Point: Point, isSelected: boolean, selectionRings: BorderStyle["rings"], relWidth?: number, captionSize?: number) => {
53
49
  x: number;
54
50
  y: number;
55
51
  angle: number;
@@ -173,3 +169,5 @@ export declare const getOverlayIconAlignment: (position: [number, number], width
173
169
  widthAlign: number;
174
170
  heightAlign: number;
175
171
  };
172
+ /** Vertical offset for a self-loop caption; `top`/`bottom` mean outside/inside the loop. */
173
+ export declare const getSelfLoopHeightAlign: (captionAlign: string, fontSize: number, lineWidth: number, padding: number, flip: 1 | -1) => number;
@@ -10,8 +10,10 @@ export interface ControllerInterface {
10
10
  stateDisposers: NvlState['autorun'][];
11
11
  state: NvlState;
12
12
  needsToRun: () => boolean;
13
- renderMainScene: (positionArray: Node[]) => void;
14
- renderMinimap: (positionArray: Node[]) => void;
13
+ renderMainScene: (positionArray: Node[], positionsChanged?: boolean) => void;
14
+ renderMainSceneFromTexture: (positionTexture: WebGLTexture) => void;
15
+ setPositionTexelIndices: (map: Readonly<Record<string, number>> | null) => void;
16
+ renderMinimap: (positionArray: Node[], positionsChanged?: boolean) => void;
15
17
  checkForUpdates: (nodes: DataSet<Node>, rels: DataSet<Relationship>) => void;
16
18
  onResize: () => void;
17
19
  updateMainViewport: (zoom: number, panX: number, panY: number) => void;
@@ -34,8 +36,24 @@ export default class Controller implements ControllerInterface {
34
36
  state?: NvlState;
35
37
  });
36
38
  needsToRun(): boolean;
37
- renderMainScene(positionArray: Node[]): void;
38
- renderMinimap(positionArray: Node[]): void;
39
+ renderMainScene(positionArray: Node[], positionsChanged?: boolean): void;
40
+ /**
41
+ * Renders the main scene from a GPU position texture instead of a
42
+ * CPU position array, avoiding the readback + upload round trip.
43
+ * @param positionTexture - Live GPU node-position texture to render from.
44
+ */
45
+ renderMainSceneFromTexture(positionTexture: WebGLTexture): void;
46
+ /**
47
+ * Sets the node-id -> texel-index map for the main scene renderer so it
48
+ * samples a shared position texture in the layout's order. Pass null to use
49
+ * insertion order.
50
+ * @param map - The node-id-to-texel-index map, or null.
51
+ *
52
+ * @note The minimap renders from its own CPU-uploaded texture and
53
+ * is left in insertion order.
54
+ */
55
+ setPositionTexelIndices(map: Readonly<Record<string, number>> | null): void;
56
+ renderMinimap(positionArray: Node[], positionsChanged?: boolean): void;
39
57
  checkForUpdates(nodes: DataSet<Node>, rels: DataSet<Relationship>): void;
40
58
  onResize(): void;
41
59
  updateMainViewport(zoom: number, panX: number, panY: number): void;
@@ -52,8 +70,10 @@ export declare class DummyController implements ControllerInterface {
52
70
  minimapMouseDown: boolean;
53
71
  stateDisposers: NvlState['autorun'][];
54
72
  state: NvlState;
55
- renderMainScene(positionArray: Node[]): void;
56
- renderMinimap(positionArray: Node[]): void;
73
+ renderMainScene(positionArray: Node[], positionsChanged?: boolean): void;
74
+ renderMainSceneFromTexture(positionTexture: WebGLTexture): void;
75
+ setPositionTexelIndices(map: Readonly<Record<string, number>> | null): void;
76
+ renderMinimap(positionArray: Node[], positionsChanged?: boolean): void;
57
77
  checkForUpdates(nodes: DataSet<Node>, rels: DataSet<Relationship>): void;
58
78
  onResize(): void;
59
79
  updateMainViewport(zoom: number, panX: number, panY: number): void;
@@ -0,0 +1,14 @@
1
+ export declare const MaxIconLayers = 256;
2
+ /**
3
+ * Maps icon URLs to stable layer indices for a WebGL `sampler2DArray`.
4
+ * Delegates image loading and rasterization to {@link ImageCache}.
5
+ */
6
+ export declare class IconLayerRegistry {
7
+ private readonly imageCache;
8
+ private readonly layerMap;
9
+ constructor(onImageLoaded?: () => void);
10
+ getLayer(url: string, inverted: boolean): number | null;
11
+ hasLoadingLayers(): boolean;
12
+ layerCount(): number;
13
+ getAvailableCanvases(): Map<number, HTMLCanvasElement>;
14
+ }
@@ -2,11 +2,17 @@ import { mat4 } from 'gl-matrix';
2
2
  import type { DataSet } from '../../modules/dataset';
3
3
  import type { NvlState } from '../../modules/state/types';
4
4
  import type { Node, Relationship } from '../../types/graph-element';
5
+ export type RendererOptions = {
6
+ enableIcons?: boolean;
7
+ };
5
8
  export default class Renderer {
6
9
  private readonly nodeShader;
7
10
  private readonly nodeAnimShader;
8
11
  private readonly relShader;
12
+ private readonly arrowShader;
9
13
  private relDataBuffer;
14
+ private arrowDataBuffer;
15
+ private arrowData;
10
16
  private readonly viewportBoxShader;
11
17
  private readonly defaultRelColor;
12
18
  private readonly defaultNodeColor;
@@ -14,38 +20,73 @@ export default class Renderer {
14
20
  private readonly disableNodeColor;
15
21
  private readonly gl;
16
22
  activeNodes: Record<string, boolean>;
23
+ numActiveNodes: number;
17
24
  readonly canvas: HTMLCanvasElement;
18
25
  projection: mat4;
19
26
  private idToIndex;
27
+ /** Node-id → texel-index map for GPU-shared rendering; null falls back to insertion order. */
28
+ private texelIndexForId;
29
+ /** texelIndexForId value when vertex buffers were last built; used to detect changes in the texel-index mapping. */
30
+ private texelMapAtBuild;
31
+ private lastNodes;
32
+ private lastBundledRels;
33
+ private idToSize;
20
34
  private posBuffer;
35
+ private positionsDirty;
21
36
  private numRels;
22
37
  private numNodes;
23
38
  private nodeDataByte;
24
39
  private relIdToIndex;
25
40
  private relData;
26
41
  private viewportBoxBuffer;
42
+ private readonly viewportBoxVertices;
27
43
  private relBuffer;
44
+ private arrowBuffer;
28
45
  private nodeBuffer;
29
46
  private posTexture;
47
+ private iconTexture;
30
48
  private nodeVao;
31
49
  private relVao;
50
+ private arrowVao;
32
51
  private nodeAnimVao;
33
- constructor(gl: WebGL2RenderingContext, nodes: DataSet<Node>, rels: DataSet<Relationship>, state: NvlState);
52
+ private readonly iconLayers;
53
+ private uploadedIconLayers;
54
+ private nodeColors;
55
+ private nodeDisabled;
56
+ constructor(gl: WebGL2RenderingContext, nodes: DataSet<Node>, rels: DataSet<Relationship>, state: NvlState, options?: RendererOptions);
34
57
  private setShaderUniforms;
35
58
  setData(data: {
36
59
  nodes: Node[];
37
60
  rels: Relationship[];
38
61
  }): void;
39
- render(positionArray: Node[]): void;
62
+ /** Sets the texel-index map for GPU-shared rendering, or null for insertion order. */
63
+ setPositionTexelIndices(map: Readonly<Record<string, number>> | null): void;
64
+ /** Texel index for a node id; falls back to insertion index, or 0 if the id is unknown. */
65
+ private getTexelIndex;
66
+ /**
67
+ * Rebuilds vertex buffers when the texel-index mapping has changed since the last build.
68
+ */
69
+ private rebuildBuffersIfTexelMappingChanged;
70
+ render(positionArray: Node[], positionsChanged?: boolean): void;
71
+ /** Renders using a GPU position texture directly as `u_positions` */
72
+ renderFromGpuTexture(positionTexture: WebGLTexture): void;
73
+ private renderRelationships;
40
74
  renderViewbox(): void;
41
75
  updateNodes(updates: Node[]): void;
42
76
  updateRelationships(relItems: Relationship[]): void;
43
77
  private createPositionTexture;
78
+ private createIconTexture;
79
+ private uploadPendingIconLayers;
80
+ hasPendingIcons(): boolean;
44
81
  updateViewportBox(zoom: number, panX: number, panY: number, viewportWidth: number, viewportHeight: number): void;
45
82
  updateViewport(zoom: number, panX: number, panY: number): void;
46
83
  private setupViewportRendering;
84
+ private resolveNodeColor;
47
85
  private setupNodeRendering;
48
86
  private setupRelationshipRendering;
87
+ private setupArrowRendering;
88
+ private getRelBufferAttributes;
89
+ private updateEndpointSizesForNode;
49
90
  private renderAnimations;
50
91
  destroy(): void;
51
92
  }
@@ -1,2 +1,2 @@
1
- declare const _default: "#version 300 es\nprecision lowp float;\n\nuniform mediump float u_zoom;\nuniform lowp float u_animPos;\nuniform mediump float u_glAdjust;\n\nin lowp vec4 color;\nin lowp float varNodeSize;\n\nout vec4 fragColor;\n\nmediump float circleSize = 0.50;\n\nvoid main(void) {\n\n lowp float dist = distance(gl_PointCoord, vec2(0.5));\n\n if (dist > 0.51) {\n discard;\n }\n else {\n lowp float alpha = 1.0 - smoothstep(circleSize - (1.0 / (varNodeSize * 2.0 * u_zoom * u_glAdjust)), circleSize, dist);\n lowp float pulseAlpha = 0.9 - 0.9 * u_animPos;\n fragColor = vec4(color.xyz, 1.0) * alpha * pulseAlpha;\n }\n}\n";
1
+ declare const _default: "#version 300 es\nprecision mediump float;\n\nuniform mediump float u_zoom;\nuniform lowp float u_animPos;\nuniform mediump float u_glAdjust;\n\nin lowp vec4 color;\nin mediump float varNodeSize;\n\nout vec4 fragColor;\n\nconst float pulseCircleEdge = 0.50;\n\nvoid main(void) {\n float dist = distance(gl_PointCoord, vec2(0.5));\n\n if (dist > 0.51) {\n discard;\n } else {\n float alpha = 1.0 - smoothstep(pulseCircleEdge - (1.0 / (varNodeSize * 2.0 * u_zoom * u_glAdjust)), pulseCircleEdge, dist);\n float pulseAlpha = 0.9 - 0.9 * u_animPos;\n fragColor = vec4(color.xyz, 1.0) * alpha * pulseAlpha;\n }\n}\n";
2
2
  export default _default;
@@ -1,2 +1,2 @@
1
- declare const _default: "#version 300 es\nuniform mat4 u_projection;\nuniform sampler2D u_positions;\nuniform lowp float u_animPos;\nuniform mediump float u_zoom;\nuniform mediump float u_glAdjust;\n\nin float a_size;\nin lowp vec2 a_index;\nin lowp vec4 a_color;\nin lowp float a_active;\n\nout lowp float varNodeSize;\nout lowp vec4 color;\n\nvoid main(void) {\n vec4 data = texture(u_positions, a_index);\n\n gl_Position = u_projection * vec4(data.xy, 0.0, 1.0);\n\n float nodeSize = a_size * u_zoom * u_glAdjust;\n nodeSize = nodeSize * (0.4 / 0.5); // Remove extra space added for shadow and selection ring\n\n gl_PointSize = a_active > 0.5\n ? nodeSize + 0.88 * nodeSize * u_animPos\n : 0.0;\n\n color = a_color;\n varNodeSize = a_size;\n}";
1
+ declare const _default: "#version 300 es\nuniform mat4 u_projection;\nuniform sampler2D u_positions;\nuniform lowp float u_animPos;\nuniform mediump float u_zoom;\nuniform mediump float u_glAdjust;\n\nin float a_size;\nin highp vec2 a_index;\nin lowp vec4 a_color;\nin lowp float a_active;\n\nout mediump float varNodeSize;\nout lowp vec4 color;\n\nvoid main(void) {\n vec4 data = texture(u_positions, a_index);\n\n gl_Position = u_projection * vec4(data.xy, 0.0, 1.0);\n\n float nodeSize = a_size * u_zoom * u_glAdjust;\n nodeSize = nodeSize * 0.8;\n\n gl_PointSize = a_active > 0.5\n ? nodeSize + 0.88 * nodeSize * u_animPos\n : 0.0;\n\n color = a_color;\n varNodeSize = a_size;\n}";
2
2
  export default _default;
@@ -1,2 +1,2 @@
1
- declare const _default: "#version 300 es\nprecision lowp float;\n\nuniform mediump float u_zoom;\nuniform mediump float u_glAdjust;\n\nin lowp vec4 color;\nin lowp float drawDefaultBorder;\nin lowp vec3 nodeBorderColor;\nin lowp vec3 selectedBorderColor;\nin lowp vec3 selectedInnerBorderColor;\nin lowp vec3 shadowColor;\nin lowp float selected;\nin lowp float hovered;\nin lowp float varNodeSize;\n\nout vec4 fragColor;\n\nmediump float circleSize = 0.40;\nmediump float defaultBoderSize = 0.42;\nmediump float innerSelectedRingSize = 0.43;\nmediump float outerSelectedRingSize = 0.47;\nmediump float shadowSize = 0.51;\n\nlowp float getShadowAlpha(lowp float size, lowp float dist) {\n return (1.0 - smoothstep(size, shadowSize, dist)) * 0.25;\n}\n\nlowp float getCircleAlpha(lowp float ringSize, lowp float nodeSize, lowp float dist) {\n return color.w - smoothstep(ringSize - nodeSize, ringSize, dist);\n}\n\nvoid main() {\n lowp float dist = distance(gl_PointCoord, vec2(0.5));\n\n if (dist > 0.51) {\n discard;\n }\n else {\n lowp float nodeSize = 1.0 / varNodeSize;\n lowp float nodeAlpha = getCircleAlpha(circleSize, nodeSize, dist);\n lowp float borderAlpha = getCircleAlpha(defaultBoderSize, nodeSize, dist);\n lowp float ringAlpha = getCircleAlpha(innerSelectedRingSize, nodeSize, dist);\n lowp vec3 finalColor = color.xyz;\n lowp float finalAlpha = nodeAlpha;\n\n if (selected > 0.0) {\n lowp float shadowAlpha = getShadowAlpha(outerSelectedRingSize, dist);\n lowp float ringAlpha2 = getCircleAlpha(outerSelectedRingSize, nodeSize, dist);\n\n if (drawDefaultBorder > 0.0) {\n finalColor = mix(nodeBorderColor, finalColor, nodeAlpha);\n finalColor = mix(selectedInnerBorderColor, finalColor, borderAlpha);\n finalColor = mix(selectedBorderColor, finalColor, ringAlpha);\n finalColor = mix(shadowColor, finalColor, ringAlpha2);\n finalAlpha = max(shadowAlpha, max(nodeAlpha, max(borderAlpha, ringAlpha2)));\n } else {\n finalColor = mix(selectedInnerBorderColor, finalColor, nodeAlpha);\n finalColor = mix(selectedBorderColor, finalColor, ringAlpha);\n finalColor = mix(shadowColor, finalColor, ringAlpha2);\n finalAlpha = max(shadowAlpha, max(nodeAlpha, max(ringAlpha, ringAlpha2)));\n }\n } else if (hovered > 0.0) {\n if (drawDefaultBorder > 0.0) {\n lowp float shadowAlpha = getShadowAlpha(defaultBoderSize, dist);\n \n finalColor = mix(nodeBorderColor, finalColor, nodeAlpha);\n finalColor = mix(shadowColor, finalColor, borderAlpha);\n finalAlpha = max(shadowAlpha, max(nodeAlpha, ringAlpha));\n } else {\n lowp float shadowAlpha = getShadowAlpha(circleSize, dist);\n\n finalColor = mix(shadowColor, finalColor, nodeAlpha);\n finalAlpha = max(shadowAlpha, nodeAlpha);\n }\n } else {\n if (drawDefaultBorder > 0.0) {\n finalColor = mix(nodeBorderColor, finalColor, nodeAlpha);\n finalAlpha = borderAlpha;\n }\n }\n fragColor = vec4(finalColor, 1.0) * finalAlpha;\n }\n}\n";
1
+ declare const _default: "#version 300 es\nprecision mediump float;\n\nuniform lowp vec3 u_nodeBorderColor;\nuniform lowp vec3 u_selectedBorderColor;\nuniform lowp vec3 u_selectedInnerBorderColor;\nuniform lowp vec3 u_shadowColor;\nuniform lowp float u_drawDefaultBorder;\nuniform lowp sampler2DArray u_icons;\n\nin lowp vec4 color;\nin lowp float selected;\nin lowp float hovered;\nin mediump float varNodeSize;\nin mediump float iconLayer;\nin lowp float hasIcon;\n\nout vec4 fragColor;\n\nconst float circleSize = 0.40;\nconst float defaultBorderSize = 0.42;\nconst float innerSelectedRingSize = 0.43;\nconst float outerSelectedRingSize = 0.47;\nconst float shadowSize = 0.51;\n\nfloat getShadowAlpha(float size, float dist) {\n return (1.0 - smoothstep(size, shadowSize, dist)) * 0.25;\n}\n\nfloat getCircleAlpha(float ringSize, float nodeSize, float dist) {\n return color.w - smoothstep(ringSize - nodeSize, ringSize, dist);\n}\n\nvoid main() {\n float dist = distance(gl_PointCoord, vec2(0.5));\n\n if (dist > 0.51) {\n discard;\n } else {\n float nodeSize = 1.0 / varNodeSize;\n float nodeAlpha = getCircleAlpha(circleSize, nodeSize, dist);\n float borderAlpha = getCircleAlpha(defaultBorderSize, nodeSize, dist);\n float ringAlpha = getCircleAlpha(innerSelectedRingSize, nodeSize, dist);\n vec3 finalColor = color.xyz;\n float finalAlpha = nodeAlpha;\n\n if (selected > 0.0) {\n float shadowAlpha = getShadowAlpha(outerSelectedRingSize, dist);\n float ringAlpha2 = getCircleAlpha(outerSelectedRingSize, nodeSize, dist);\n\n if (u_drawDefaultBorder > 0.0) {\n finalColor = mix(u_nodeBorderColor, finalColor, nodeAlpha);\n finalColor = mix(u_selectedInnerBorderColor, finalColor, borderAlpha);\n finalColor = mix(u_selectedBorderColor, finalColor, ringAlpha);\n finalColor = mix(u_shadowColor, finalColor, ringAlpha2);\n finalAlpha = max(shadowAlpha, max(nodeAlpha, max(borderAlpha, ringAlpha2)));\n } else {\n finalColor = mix(u_selectedInnerBorderColor, finalColor, nodeAlpha);\n finalColor = mix(u_selectedBorderColor, finalColor, ringAlpha);\n finalColor = mix(u_shadowColor, finalColor, ringAlpha2);\n finalAlpha = max(shadowAlpha, max(nodeAlpha, max(ringAlpha, ringAlpha2)));\n }\n } else if (hovered > 0.0) {\n if (u_drawDefaultBorder > 0.0) {\n float shadowAlpha = getShadowAlpha(defaultBorderSize, dist);\n finalColor = mix(u_nodeBorderColor, finalColor, nodeAlpha);\n finalColor = mix(u_shadowColor, finalColor, borderAlpha);\n finalAlpha = max(shadowAlpha, max(nodeAlpha, ringAlpha));\n } else {\n float shadowAlpha = getShadowAlpha(circleSize, dist);\n finalColor = mix(u_shadowColor, finalColor, nodeAlpha);\n finalAlpha = max(shadowAlpha, nodeAlpha);\n }\n } else {\n if (u_drawDefaultBorder > 0.0) {\n finalColor = mix(u_nodeBorderColor, finalColor, nodeAlpha);\n finalAlpha = borderAlpha;\n }\n }\n\n if (hasIcon > 0.5) {\n vec2 uv = (gl_PointCoord - vec2(0.5)) / circleSize + 0.5;\n\n float inX = step(0.0, uv.x) - step(1.0, uv.x);\n float inY = step(0.0, uv.y) - step(1.0, uv.y);\n float isInside = inX * inY;\n\n vec4 ic = texture(u_icons, vec3(uv, iconLayer));\n finalColor = mix(finalColor, ic.rgb, ic.a * isInside);\n }\n\n fragColor = vec4(finalColor, 1.0) * finalAlpha;\n }\n}\n";
2
2
  export default _default;
@@ -1,2 +1,2 @@
1
- declare const _default: "#version 300 es\nuniform mat4 u_projection;\nuniform sampler2D u_positions;\nuniform mediump float u_zoom;\nuniform mediump float u_glAdjust;\nuniform lowp vec3 u_nodeBorderColor;\nuniform lowp vec3 u_selectedBorderColor;\nuniform lowp vec3 u_selectedInnerBorderColor;\nuniform lowp vec3 u_shadowColor;\nuniform lowp float u_drawDefaultBorder;\n\nin float a_size;\nin lowp vec2 a_index;\nin lowp vec4 a_color;\nin lowp float a_selected;\nin lowp float a_hovered;\n\nout lowp vec4 color;\nout lowp float selected;\nout lowp float hovered;\nout lowp float varNodeSize;\nout lowp vec3 nodeBorderColor;\nout lowp vec3 selectedBorderColor;\nout lowp vec3 selectedInnerBorderColor;\nout lowp vec3 shadowColor;\nout lowp float drawDefaultBorder;\n\nlowp float MinNodeSize = 5.0;\n\nvoid main(void) {\n vec4 data = texture(u_positions, a_index);\n gl_Position = u_projection * vec4(data.xy, 0.0, 1.0);\n\n lowp float nodeSize = a_size * u_zoom * u_glAdjust;\n if (nodeSize < MinNodeSize) {\n gl_PointSize = MinNodeSize;\n varNodeSize = MinNodeSize;\n } else {\n gl_PointSize = nodeSize;\n varNodeSize = nodeSize;\n }\n\n color = a_color;\n selected = a_selected;\n hovered = a_hovered;\n nodeBorderColor = u_nodeBorderColor;\n selectedBorderColor = u_selectedBorderColor;\n selectedInnerBorderColor = u_selectedInnerBorderColor;\n shadowColor = u_shadowColor;\n drawDefaultBorder = u_drawDefaultBorder;\n}";
1
+ declare const _default: "#version 300 es\nuniform mat4 u_projection;\nuniform sampler2D u_positions;\nuniform mediump float u_zoom;\nuniform mediump float u_glAdjust;\n\nin float a_size;\nin highp vec2 a_index;\nin lowp vec4 a_color;\nin lowp float a_selected;\nin lowp float a_hovered;\nin lowp float a_iconLayer;\nin lowp float a_hasIcon;\n\nout lowp vec4 color;\nout lowp float selected;\nout lowp float hovered;\nout mediump float varNodeSize;\nout mediump float iconLayer;\nout lowp float hasIcon;\n\nconst float MinNodeSize = 5.0;\n\nvoid main(void) {\n vec4 data = texture(u_positions, a_index);\n gl_Position = u_projection * vec4(data.xy, 0.0, 1.0);\n\n float nodeSize = max(a_size * u_zoom * u_glAdjust, MinNodeSize);\n gl_PointSize = nodeSize;\n varNodeSize = nodeSize;\n\n color = a_color;\n selected = a_selected;\n hovered = a_hovered;\n iconLayer = a_iconLayer * 255.0;\n hasIcon = a_hasIcon;\n}";
2
2
  export default _default;
@@ -0,0 +1 @@
1
+ export declare const arrowShaderUtilsGLSL = "\nconst float CircleSize = 0.40;\nconst float HeadHalfWidthScale = 4.0;\nconst float HeadLengthScale = 2.5;\nconst float MaxHeadHalfWidthRatio = 0.6;\nconst float MaxHeadLengthRatio = 1.0;\n\n// Self-referencing relationships aren't supported in WebGL yet.\nbool relationshipIsSelfLoop(vec2 fromTexel, vec2 toTexel) {\n return all(equal(fromTexel, toTexel));\n}\n\nvec2 perpendicular(vec2 dir) {\n return vec2(-dir.y, dir.x);\n}\n\nfloat nodeRadiusFromSize(float size) {\n return size * u_glAdjust * CircleSize;\n}\n\nfloat relationshipHalfWidth(float width) {\n return width * u_glAdjust * 0.25;\n}\n\nfloat naturalArrowHeadHalfWidth(float lineHalfWidth) {\n return lineHalfWidth * HeadHalfWidthScale + u_glAdjust;\n}\n\nfloat naturalArrowHeadLength(float headHalfWidth) {\n return headHalfWidth * HeadLengthScale;\n}\n\nfloat clampArrowHeadHalfWidth(float lineHalfWidth, float nodeRadius) {\n float natural = naturalArrowHeadHalfWidth(lineHalfWidth);\n float minHalfWidth = lineHalfWidth;\n float maxHalfWidth = max(minHalfWidth, nodeRadius * MaxHeadHalfWidthRatio);\n return clamp(natural, minHalfWidth, maxHalfWidth);\n}\n\nfloat clampArrowHeadLength(float headHalfWidth, float nodeRadius) {\n float natural = naturalArrowHeadLength(headHalfWidth);\n float maxLength = nodeRadius * MaxHeadLengthRatio;\n return min(natural, maxLength);\n}\n\nfloat arrowHeadClearance(float lineHalfWidth, float nodeRadius) {\n float headHalfWidth = clampArrowHeadHalfWidth(lineHalfWidth, nodeRadius);\n return clampArrowHeadLength(headHalfWidth, nodeRadius);\n}\n";
@@ -0,0 +1,2 @@
1
+ declare const _default: "#version 300 es\nuniform mat4 u_projection;\nuniform sampler2D u_positions;\nuniform mediump float u_glAdjust;\n\nin vec2 a_from;\nin vec2 a_to;\nin lowp vec4 a_color;\nin float a_width;\nin float a_toSize;\n\nout lowp vec4 color;\n\n\nconst float CircleSize = 0.40;\nconst float HeadHalfWidthScale = 4.0;\nconst float HeadLengthScale = 2.5;\nconst float MaxHeadHalfWidthRatio = 0.6;\nconst float MaxHeadLengthRatio = 1.0;\n\n// Self-referencing relationships aren't supported in WebGL yet.\nbool relationshipIsSelfLoop(vec2 fromTexel, vec2 toTexel) {\n return all(equal(fromTexel, toTexel));\n}\n\nvec2 perpendicular(vec2 dir) {\n return vec2(-dir.y, dir.x);\n}\n\nfloat nodeRadiusFromSize(float size) {\n return size * u_glAdjust * CircleSize;\n}\n\nfloat relationshipHalfWidth(float width) {\n return width * u_glAdjust * 0.25;\n}\n\nfloat naturalArrowHeadHalfWidth(float lineHalfWidth) {\n return lineHalfWidth * HeadHalfWidthScale + u_glAdjust;\n}\n\nfloat naturalArrowHeadLength(float headHalfWidth) {\n return headHalfWidth * HeadLengthScale;\n}\n\nfloat clampArrowHeadHalfWidth(float lineHalfWidth, float nodeRadius) {\n float natural = naturalArrowHeadHalfWidth(lineHalfWidth);\n float minHalfWidth = lineHalfWidth;\n float maxHalfWidth = max(minHalfWidth, nodeRadius * MaxHeadHalfWidthRatio);\n return clamp(natural, minHalfWidth, maxHalfWidth);\n}\n\nfloat clampArrowHeadLength(float headHalfWidth, float nodeRadius) {\n float natural = naturalArrowHeadLength(headHalfWidth);\n float maxLength = nodeRadius * MaxHeadLengthRatio;\n return min(natural, maxLength);\n}\n\nfloat arrowHeadClearance(float lineHalfWidth, float nodeRadius) {\n float headHalfWidth = clampArrowHeadHalfWidth(lineHalfWidth, nodeRadius);\n return clampArrowHeadLength(headHalfWidth, nodeRadius);\n}\n\n\nvoid main(void) {\n vec4 from = texture(u_positions, a_from);\n vec4 to = texture(u_positions, a_to);\n\n if (relationshipIsSelfLoop(a_from, a_to)) {\n color = a_color;\n gl_Position = u_projection * vec4(from.xy, 0.0, 1.0);\n return;\n }\n\n vec2 delta = to.xy - from.xy;\n vec2 dir = normalize(delta);\n vec2 perp = perpendicular(dir);\n\n float toRadius = nodeRadiusFromSize(a_toSize);\n float lineHalfWidth = relationshipHalfWidth(a_width);\n float headHalfWidth = clampArrowHeadHalfWidth(lineHalfWidth, toRadius);\n float headLength = clampArrowHeadLength(headHalfWidth, toRadius);\n\n vec2 tip = to.xy - dir * toRadius;\n vec2 baseCenter = tip - dir * headLength;\n\n int corner = gl_VertexID % 3;\n float isTip = float(corner == 0);\n float perpSign = float(corner == 1) - float(corner == 2);\n vec2 basePos = mix(baseCenter, tip, isTip);\n vec2 vertexPosition = basePos + perp * (headHalfWidth * perpSign);\n\n color = a_color;\n gl_Position = u_projection * vec4(vertexPosition, 0.0, 1.0);\n}\n";
2
+ export default _default;
@@ -1,2 +1,2 @@
1
- declare const _default: "#version 300 es\nuniform mat4 u_projection;\nuniform sampler2D u_positions;\nuniform mediump float u_glAdjust;\n\nin vec2 a_from;\nin vec2 a_to;\nin lowp vec4 a_color;\nin lowp float a_triside;\nin float a_width;\n\nout lowp vec4 color;\n\nvoid main(void) {\n vec4 from = texture(u_positions, a_from);\n vec4 to = texture(u_positions, a_to);\n vec2 direction = normalize(to.xy - from.xy);\n direction = vec2(-direction.y, direction.x);\n\n float width = a_width * u_glAdjust * 0.5;\n\n if (a_triside == 0.0) {\n from.y += (width / 2.0) * direction.y;\n from.x += (width / 2.0) * direction.x;\n } else {\n from.y -= (width / 2.0) * direction.y;\n from.x -= (width / 2.0) * direction.x;\n }\n\n color = a_color;\n gl_Position = u_projection * vec4(from.xy, 0.0, 1.0);\n}\n";
1
+ declare const _default: "#version 300 es\nuniform mat4 u_projection;\nuniform sampler2D u_positions;\nuniform mediump float u_glAdjust;\n\nin vec2 a_from;\nin vec2 a_to;\nin lowp vec4 a_color;\nin lowp float a_triside;\nin lowp float a_isTarget;\nin float a_endpointSize;\nin float a_otherEndpointSize;\nin float a_width;\n\nout lowp vec4 color;\n\n\nconst float CircleSize = 0.40;\nconst float HeadHalfWidthScale = 4.0;\nconst float HeadLengthScale = 2.5;\nconst float MaxHeadHalfWidthRatio = 0.6;\nconst float MaxHeadLengthRatio = 1.0;\n\n// Self-referencing relationships aren't supported in WebGL yet.\nbool relationshipIsSelfLoop(vec2 fromTexel, vec2 toTexel) {\n return all(equal(fromTexel, toTexel));\n}\n\nvec2 perpendicular(vec2 dir) {\n return vec2(-dir.y, dir.x);\n}\n\nfloat nodeRadiusFromSize(float size) {\n return size * u_glAdjust * CircleSize;\n}\n\nfloat relationshipHalfWidth(float width) {\n return width * u_glAdjust * 0.25;\n}\n\nfloat naturalArrowHeadHalfWidth(float lineHalfWidth) {\n return lineHalfWidth * HeadHalfWidthScale + u_glAdjust;\n}\n\nfloat naturalArrowHeadLength(float headHalfWidth) {\n return headHalfWidth * HeadLengthScale;\n}\n\nfloat clampArrowHeadHalfWidth(float lineHalfWidth, float nodeRadius) {\n float natural = naturalArrowHeadHalfWidth(lineHalfWidth);\n float minHalfWidth = lineHalfWidth;\n float maxHalfWidth = max(minHalfWidth, nodeRadius * MaxHeadHalfWidthRatio);\n return clamp(natural, minHalfWidth, maxHalfWidth);\n}\n\nfloat clampArrowHeadLength(float headHalfWidth, float nodeRadius) {\n float natural = naturalArrowHeadLength(headHalfWidth);\n float maxLength = nodeRadius * MaxHeadLengthRatio;\n return min(natural, maxLength);\n}\n\nfloat arrowHeadClearance(float lineHalfWidth, float nodeRadius) {\n float headHalfWidth = clampArrowHeadHalfWidth(lineHalfWidth, nodeRadius);\n return clampArrowHeadLength(headHalfWidth, nodeRadius);\n}\n\n\nvoid main(void) {\n vec4 from = texture(u_positions, a_from);\n vec4 to = texture(u_positions, a_to);\n\n if (relationshipIsSelfLoop(a_from, a_to)) {\n color = a_color;\n gl_Position = u_projection * vec4(from.xy, 0.0, 1.0);\n return;\n }\n\n vec2 delta = from.xy - to.xy;\n float halfWidth = relationshipHalfWidth(a_width);\n float ownRadius = nodeRadiusFromSize(a_endpointSize);\n float otherRadius = nodeRadiusFromSize(a_otherEndpointSize);\n float toRadius = mix(otherRadius, ownRadius, a_isTarget);\n float arrowClearance = arrowHeadClearance(halfWidth, toRadius);\n float endpointClearance = ownRadius + a_isTarget * arrowClearance;\n\n vec2 along = normalize(delta);\n from.xy -= along * endpointClearance;\n\n float sideSign = 1.0 - 2.0 * a_triside;\n from.xy += sideSign * halfWidth * perpendicular(along);\n\n color = a_color;\n gl_Position = u_projection * vec4(from.xy, 0.0, 1.0);\n}\n";
2
2
  export default _default;
@@ -1,2 +1,2 @@
1
- declare const _default: "#version 300 es\nprecision lowp float;\n\nin lowp vec4 minimapViewportBoxColor;\n\nout vec4 fragColor;\n\nvoid main(void) {\n fragColor = minimapViewportBoxColor;\n}\n";
1
+ declare const _default: "#version 300 es\nprecision lowp float;\n\nuniform lowp vec4 u_minimapViewportBoxColor;\n\nout vec4 fragColor;\n\nvoid main(void) {\n fragColor = u_minimapViewportBoxColor;\n}\n";
2
2
  export default _default;
@@ -1,2 +1,2 @@
1
- declare const _default: "#version 300 es\nin vec2 coordinates;\nuniform mat4 u_projection;\nuniform lowp vec4 u_minimapViewportBoxColor;\n\nout lowp vec4 minimapViewportBoxColor;\n\nvoid main(void) {\n gl_Position = u_projection * vec4(coordinates, 0.0, 1.0);\n minimapViewportBoxColor = u_minimapViewportBoxColor;\n}\n";
1
+ declare const _default: "#version 300 es\nin vec2 coordinates;\nuniform mat4 u_projection;\n\nvoid main(void) {\n gl_Position = u_projection * vec4(coordinates, 0.0, 1.0);\n}\n";
2
2
  export default _default;
@@ -1,4 +1,5 @@
1
1
  import type { StyledCaption } from '../renderers/domrenderer/shared/types';
2
+ import type { Point } from '../utils/geometry';
2
3
  /**
3
4
  * The base type for graph elements.
4
5
  * @private
@@ -97,6 +98,13 @@ interface Node extends GraphElement {
97
98
  */
98
99
  html?: HTMLElement;
99
100
  }
101
+ /**
102
+ * A node returned by {@link NVL.getNodes} with layout coordinates and screen position.
103
+ * `x` and `y` are canvas/layout space; `clientPosition` is relative to the canvas element.
104
+ */
105
+ type NodeWithClientPosition = Node & Point & {
106
+ clientPosition: Point;
107
+ };
100
108
  /**
101
109
  * A node with only a subset of its properties.
102
110
  */
@@ -132,4 +140,4 @@ type PartialRelationship = Partial<Relationship> & {
132
140
  /** The id of the relationship. */
133
141
  id: string;
134
142
  };
135
- export type { Node, Relationship, PartialNode, PartialRelationship };
143
+ export type { Node, NodeWithClientPosition, Relationship, PartialNode, PartialRelationship };
@@ -1,6 +1,6 @@
1
- import type { Node, Relationship } from './graph-element';
1
+ import type { Node, NodeWithClientPosition, Relationship } from './graph-element';
2
2
  export interface NvlWindow {
3
- __Nvl_dumpNodes: (instanceId?: string) => Node[];
3
+ __Nvl_dumpNodes: (instanceId?: string) => NodeWithClientPosition[];
4
4
  __Nvl_dumpRelationships: (instanceId?: string) => Relationship[];
5
5
  __Nvl_getNodesOnScreen: (instanceId?: string) => {
6
6
  nodes: Node[];
@@ -6,8 +6,11 @@ export declare const Gravity = 25;
6
6
  export declare const SimulationStopVelocity = 0.25;
7
7
  export declare const MappingEnd = 999999;
8
8
  export declare const LayoutStepIntervalMs = 13;
9
- export declare const SmoothFrameBudgetMs = 8;
10
- export declare const DefaultLayoutTimeLimit = 300;
9
+ export declare const MaxLayoutStepsPerFrame = 20;
10
+ export declare const DefaultMaxFps = 30;
11
+ export declare const DefaultMinFps = 3;
12
+ export declare const MinAllowedFps = 1;
13
+ export declare const MaxAllowedFps = 120;
11
14
  export declare const ActiveAnimDuration = 1000;
12
15
  export declare const DefaultNodeSize = 25;
13
16
  export declare const DefaultNodeColor = "#FFDF81";
@@ -126,3 +126,19 @@ export declare const getIntersection: (p0: Point, p1: Point, p2: Point, p3: Poin
126
126
  * @returns The distance between the point and the curve.
127
127
  */
128
128
  export declare const getDistanceToQuadratic: (fromCoordinates: Point, toCoordinates: Point, control: Point, point: Point) => number;
129
+ /**
130
+ * Converts a polar coordinate (radius + angle around a centre) to a cartesian point.
131
+ * @param centre - The centre of the circle.
132
+ * @param radius - The radius of the circle.
133
+ * @param angle - The angle around the circle, in radians.
134
+ * @returns The point at that angle and radius from the centre.
135
+ */
136
+ export declare const pointOnCircle: (centre: Point, radius: number, angle: number) => Point;
137
+ /**
138
+ * Places child points evenly spaced around a circle centred on a point.
139
+ * @param centre - The centre of the circle.
140
+ * @param radius - The radius of the circle.
141
+ * @param noOfChildren - The number of points to place around the circle.
142
+ * @returns The evenly spaced points.
143
+ */
144
+ export declare const calculateCirclePositions: (centre: Point, radius: number, noOfChildren: number) => Point[];
@@ -0,0 +1,7 @@
1
+ import type { Point } from './geometry';
2
+ export type SeedingMethod = 'box' | 'circle';
3
+ export declare const getCircleSeedRadius: (nodeCount: number, seedSpacing: number) => number;
4
+ export declare const getBoxStartSize: (nodeCount: number) => number;
5
+ export declare const getCirclePositionsForNodeCount: (nodeCount: number, center: Point, radius: number) => Point[];
6
+ export declare const getBoxPositionsForNodeCount: (nodeCount: number, center: Point) => Point[];
7
+ export declare const getSeedPositions: (nodeCount: number, seedingMethod: SeedingMethod, center?: Point) => Point[];
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,3 @@
1
- type Layouts = 'CoseBilkentLayout' | 'HierarchicalLayout';
1
+ type Layouts = 'HierarchicalLayout';
2
2
  export declare const initializeWorker: (workerName: Layouts, workersDisabled?: boolean) => SharedWorker;
3
3
  export {};
@@ -1,5 +1,11 @@
1
1
  import type { Node } from '../index';
2
2
  import type { Point } from './geometry';
3
+ /**
4
+ * Gets padding for fitting nodes, which is twice the largest node radius.
5
+ * @param maxNodeRadius - The largest node radius.
6
+ * @returns The padding for fitting nodes.
7
+ */
8
+ declare const getFitNodesPadding: (maxNodeRadius?: number) => number;
3
9
  declare const getExtremeLimits: (nodePositions: Point[]) => Point[];
4
10
  declare const getMaxDistance: (extremeLimits?: Point[]) => number;
5
11
  /**
@@ -40,7 +46,8 @@ declare const createZoomTarget: (zoomX: number, zoomY: number, minZoom?: number,
40
46
  * @param nodePositions - The node positions.
41
47
  * @param containerWidth - The width of the container NVL is displayed in.
42
48
  * @param containerHeight - The height of the container NVL is displayed in.
43
- * @param maxNodeRadius - The maximum node radius, which is added as margin to the scene (defaults to `50`).
49
+ * @param maxNodeRadius - The maximum node radius, which is added as margin to the scene
50
+ * (defaults to 2 * {@link DefaultNodeSize}).
44
51
  * @returns The zoom and pan targets.
45
52
  */
46
53
  declare const getZoomTargetForNodePositions: (nodePositions: Point[], containerWidth: number, containerHeight: number, maxNodeRadius?: number) => {
@@ -55,4 +62,4 @@ declare const getFlexibleZoomLimit: (nodePositions: Point[], rect: HTMLCanvasEle
55
62
  declare const getNewZoomWithinLimits: (newZoom: number, oldZoom: number, minZoom: number, maxZoom: number) => number;
56
63
  declare const isZoomOutAtLimit: (newZoom: number, oldZoom: number, zoomOutLimit: number) => boolean;
57
64
  declare const getDynamicZoom: (nodePositions: Point[] & Node[], minZoom: number, maxZoom: number, rect: HTMLCanvasElement, newZoom: number, currentZoom: number) => number;
58
- export { getFlexibleZoomLimit, getZoomTargetForNodePositions, isZoomOutAtLimit, getNewZoomWithinLimits, getExtremeLimits, getMaxDistance, getPositionBoundaries, getZoomLevel, createZoomTarget, getDynamicZoom };
65
+ export { getFitNodesPadding, getFlexibleZoomLimit, getZoomTargetForNodePositions, isZoomOutAtLimit, getNewZoomWithinLimits, getExtremeLimits, getMaxDistance, getPositionBoundaries, getZoomLevel, createZoomTarget, getDynamicZoom };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neo4j-nvl/base",
3
- "version": "1.2.0-e691fdb4",
3
+ "version": "1.2.0-ec100981",
4
4
  "license": "SEE LICENSE IN 'LICENSE.txt'",
5
5
  "homepage": "https://neo4j.com/docs/nvl/current/",
6
6
  "description": "Base library for the Neo4j Visualization Library",
@@ -30,7 +30,7 @@
30
30
  "postpack": "rm LICENSE.txt && rm CHANGELOG.md"
31
31
  },
32
32
  "dependencies": {
33
- "@neo4j-nvl/layout-workers": "1.2.0-e691fdb4",
33
+ "@neo4j-nvl/layout-workers": "1.2.0-ec100981",
34
34
  "@segment/analytics-next": "1.81.1",
35
35
  "color-string": "1.9.1",
36
36
  "d3-force": "3.0.0",
@@ -38,14 +38,14 @@
38
38
  "glsl-inject-defines": "1.0.3",
39
39
  "lodash": "4.18.1",
40
40
  "loglevel": "1.9.2",
41
- "mobx": "3.6.2",
41
+ "mobx": "6.15.0",
42
42
  "node-pid-controller": "1.0.1",
43
43
  "resizelistener": "1.1.0",
44
44
  "tinycolor2": "1.6.0",
45
- "uuid": "14.0.0"
45
+ "uuid": "14.0.1"
46
46
  },
47
47
  "devDependencies": {
48
- "@babel/core": "7.28.5",
48
+ "@babel/core": "7.29.7",
49
49
  "@types/color-string": "1.5.5",
50
50
  "@types/d3-force": "3.0.10",
51
51
  "@types/lodash": "4.17.21",
@@ -1,22 +0,0 @@
1
- export declare const ForceDirectedLayoutType = "forceDirected";
2
- export declare const CoseBilkentType = "coseBilkent";
3
- export declare class ForceCytoLayout {
4
- private readonly physLayout;
5
- private readonly coseBilkentLayout;
6
- private readonly state;
7
- private currentLayoutType;
8
- private enableCytoscape;
9
- private currentLayout;
10
- constructor(config: any);
11
- setOptions(options: any): void;
12
- private getLayout;
13
- private setLayout;
14
- copyLayoutPositions(nodeList: any, prevLayout: any, nextLayout: any): void;
15
- update(refreshPositions?: boolean): void;
16
- getShouldUpdate(): boolean;
17
- getComputing(): boolean;
18
- updateNodes(positionList: any): void;
19
- getNodePositions(data: any): import("../..").Node[];
20
- terminateUpdate(): void;
21
- destroy(): void;
22
- }
@@ -1,33 +0,0 @@
1
- import type { NvlState } from '../../../modules/state/types';
2
- import type { Node, Relationship } from '../../../types/graph-element';
3
- import AnimatedLayout from '../../animatedlayout/AnimatedLayout';
4
- type LayoutData = {
5
- elements: {
6
- group: string;
7
- data: {
8
- id: string;
9
- };
10
- }[];
11
- spacingFactor: number;
12
- };
13
- export declare class CoseBilkentLayout extends AnimatedLayout {
14
- private stateDisposers;
15
- cytoCompleteCallback: () => void;
16
- computing: boolean;
17
- pendingLayoutData: LayoutData;
18
- worker: SharedWorker;
19
- private workersDisabled;
20
- constructor(config: {
21
- state: NvlState;
22
- cytoCompleteCallback?: () => void;
23
- animationCompleteCallback?: () => void;
24
- });
25
- setOptions(): void;
26
- update(refreshPositions?: boolean, updateMobXNodes?: Node[], updateMobXRelationships?: Relationship[]): void;
27
- getShouldUpdate(): boolean;
28
- getComputing(): boolean;
29
- layout(nodes: Node[], relationships: Relationship[]): void;
30
- terminateUpdate(): void;
31
- destroy(): void;
32
- }
33
- export {};