@neo4j-nvl/base 1.2.0-158df7fc → 1.2.0-1fcd4d14
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/dist/base.mjs +1 -1
- package/dist/types/index.d.ts +5 -4
- package/dist/types/layouts/animatedlayout/AnimatedLayout.d.ts +2 -2
- package/dist/types/layouts/d3forcelayout/d3ForceLayout.d.ts +2 -2
- package/dist/types/layouts/forcedirectedlayout/physlayout/PhysLayout.d.ts +51 -2
- package/dist/types/layouts/forcedirectedlayout/physlayout/shaders/multilevel-fragment.d.ts +1 -1
- package/dist/types/layouts/forcedirectedlayout/physlayout/shaders/multilevel-repulsive-fragment.d.ts +1 -1
- package/dist/types/layouts/forcedirectedlayout/physlayout/solarmerger/SolarMerger.d.ts +57 -0
- package/dist/types/layouts/freeLayout/FreeLayout.d.ts +2 -4
- package/dist/types/layouts/gridLayout/GridLayout.d.ts +2 -6
- package/dist/types/modules/NvlController.d.ts +28 -2
- package/dist/types/modules/dataset.d.ts +3 -1
- package/dist/types/modules/state/types.d.ts +6 -15
- package/dist/types/renderers/domrenderer/canvasrenderer/arrowDrawing.d.ts +2 -16
- package/dist/types/renderers/domrenderer/shared/ImageCache.d.ts +1 -0
- package/dist/types/renderers/domrenderer/shared/arrows/arrows.d.ts +4 -6
- package/dist/types/renderers/webglrenderer/Controller.d.ts +26 -6
- package/dist/types/renderers/webglrenderer/IconLayerRegistry.d.ts +14 -0
- package/dist/types/renderers/webglrenderer/IconLayerRegistry.test.d.ts +1 -0
- package/dist/types/renderers/webglrenderer/Renderer.d.ts +31 -2
- package/dist/types/renderers/webglrenderer/node-animation-fragment.d.ts +1 -1
- package/dist/types/renderers/webglrenderer/node-animation-vertex.d.ts +1 -1
- package/dist/types/renderers/webglrenderer/node-fragment-point.d.ts +1 -1
- package/dist/types/renderers/webglrenderer/node-vertex-point.d.ts +1 -1
- package/dist/types/renderers/webglrenderer/rel-vertex-line.d.ts +1 -1
- package/dist/types/renderers/webglrenderer/viewport-box-fragment.d.ts +1 -1
- package/dist/types/renderers/webglrenderer/viewport-box-vertex.d.ts +1 -1
- package/dist/types/types/graph-element.d.ts +9 -1
- package/dist/types/types/nvl-window-functions.d.ts +2 -2
- package/dist/types/utils/constants.d.ts +1 -0
- package/dist/types/utils/webWorkerUtils.d.ts +1 -1
- package/dist/types/utils/zoomFunctions.d.ts +9 -2
- package/package.json +5 -5
- package/dist/types/layouts/forcedirectedlayout/ForceCytoLayout.d.ts +0 -22
- package/dist/types/layouts/forcedirectedlayout/cosebilkentlayout/CoseBilkentLayout.d.ts +0 -33
package/dist/types/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { CircularOptions, ForceDirectedOptions, HierarchicalOptions, Render
|
|
|
3
3
|
import { CanvasRendererType, CircularLayoutType, ForceDirectedLayoutType, FreeLayoutType, GridLayoutType, HierarchicalLayoutType, SvgRendererType, WebGLRendererType, d3ForceLayoutType } from './modules/state/types';
|
|
4
4
|
import { drawCircleBand } from './renderers/domrenderer/canvasrenderer/nodeDrawing';
|
|
5
5
|
import type { StyledCaption } from './renderers/domrenderer/shared/types';
|
|
6
|
-
import type { Node, PartialNode, PartialRelationship, Relationship } from './types/graph-element';
|
|
6
|
+
import type { Node, NodeWithClientPosition, PartialNode, PartialRelationship, Relationship } from './types/graph-element';
|
|
7
7
|
import { CompatibilityError } from './utils/errors';
|
|
8
8
|
import type { Point } from './utils/geometry';
|
|
9
9
|
import type { HitTargetNode, HitTargetRelationship, HitTargets } from './utils/hittest';
|
|
@@ -139,9 +139,10 @@ declare class NVL {
|
|
|
139
139
|
removeRelationshipsWithIds(relationshipIds: string[]): void;
|
|
140
140
|
/**
|
|
141
141
|
* Returns all nodes that is currently stored in the visualisation.
|
|
142
|
-
*
|
|
142
|
+
* Each node includes layout coordinates (`x`, `y`) and a `clientPosition` in screen space.
|
|
143
|
+
* @returns The array of {@link NodeWithClientPosition nodes}.
|
|
143
144
|
*/
|
|
144
|
-
getNodes():
|
|
145
|
+
getNodes(): NodeWithClientPosition[];
|
|
145
146
|
/**
|
|
146
147
|
* Returns the relationships that are currently in the visualization.
|
|
147
148
|
* @returns An array of the {@link Relationship relationships} in the visualization.
|
|
@@ -415,5 +416,5 @@ declare const colorMapperFunctions: {
|
|
|
415
416
|
*/
|
|
416
417
|
type Renderer = Exclude<InternalRenderer, typeof SvgRendererType>;
|
|
417
418
|
export default NVL;
|
|
418
|
-
export type { NvlOptions, Renderer, Node, Relationship, PartialNode, PartialRelationship, Layout, LayoutOptions, ForceDirectedOptions, HierarchicalOptions, CircularOptions, ExternalCallbacks, HitTargets, HitTargetNode, HitTargetRelationship, Point, NvlMouseEvent, ZoomOptions, StyledCaption, WebGLRendererType, CanvasRendererType };
|
|
419
|
+
export type { NvlOptions, Renderer, Node, NodeWithClientPosition, Relationship, PartialNode, PartialRelationship, Layout, LayoutOptions, ForceDirectedOptions, HierarchicalOptions, CircularOptions, ExternalCallbacks, HitTargets, HitTargetNode, HitTargetRelationship, Point, NvlMouseEvent, ZoomOptions, StyledCaption, WebGLRendererType, CanvasRendererType };
|
|
419
420
|
export { NVL, colorMapperFunctions, CompatibilityError, ForceDirectedLayoutType, HierarchicalLayoutType, GridLayoutType, FreeLayoutType, d3ForceLayoutType, CircularLayoutType, drawCircleBand, nvlResultTransformer, getZoomTargetForNodePositions };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NvlState } from '../../modules/state/types';
|
|
2
|
-
import type { Node } from '../../types/graph-element';
|
|
2
|
+
import type { Node, PartialNode } from '../../types/graph-element';
|
|
3
3
|
export default class AnimatedLayout {
|
|
4
4
|
positions: Record<string, Node>;
|
|
5
5
|
oldPositions: Record<string, Node>;
|
|
@@ -20,6 +20,6 @@ export default class AnimatedLayout {
|
|
|
20
20
|
updateOldPositionFromState(): void;
|
|
21
21
|
startAnimation(): void;
|
|
22
22
|
update(): void;
|
|
23
|
-
updateNodes(positionList:
|
|
23
|
+
updateNodes(positionList: PartialNode[]): void;
|
|
24
24
|
getNodePositions(nodeList: Node[]): Node[];
|
|
25
25
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Simulation, SimulationLinkDatum, SimulationNodeDatum } from 'd3-force';
|
|
2
2
|
import type { NvlState } from '../../modules/state/types';
|
|
3
|
-
import type { Node, Relationship } from '../../types/graph-element';
|
|
3
|
+
import type { Node, PartialNode, Relationship } from '../../types/graph-element';
|
|
4
4
|
import type { Point } from '../../utils/geometry';
|
|
5
5
|
import type { D3Node } from './types';
|
|
6
6
|
export declare class D3ForceLayout {
|
|
@@ -20,7 +20,7 @@ export declare class D3ForceLayout {
|
|
|
20
20
|
state: NvlState;
|
|
21
21
|
});
|
|
22
22
|
setOptions(options: any): void;
|
|
23
|
-
updateNodes(positionList:
|
|
23
|
+
updateNodes(positionList: PartialNode[]): void;
|
|
24
24
|
update(refreshPositions?: boolean): void;
|
|
25
25
|
layout(_nodes: Node[], _rels: Relationship[], firstTimeAddingNodes: boolean): void;
|
|
26
26
|
getNodePositions(nodeList: D3Node[]): any[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ForceDirectedOptions, NvlState } from '../../../modules/state/types';
|
|
2
|
-
import type { Node, Relationship } from '../../../types/graph-element';
|
|
2
|
+
import type { Node, PartialNode, Relationship } from '../../../types/graph-element';
|
|
3
3
|
import type { Point } from '../../../utils/geometry';
|
|
4
4
|
export declare class PhysLayout {
|
|
5
5
|
private physVbo;
|
|
@@ -18,6 +18,7 @@ export declare class PhysLayout {
|
|
|
18
18
|
private stateDisposers;
|
|
19
19
|
private state;
|
|
20
20
|
private dpr;
|
|
21
|
+
private maxGlNodeSize;
|
|
21
22
|
private simulationStopVelocitySquared;
|
|
22
23
|
private gravity;
|
|
23
24
|
private force;
|
|
@@ -45,6 +46,9 @@ export declare class PhysLayout {
|
|
|
45
46
|
private updateData;
|
|
46
47
|
private updateShader;
|
|
47
48
|
private physPositions;
|
|
49
|
+
private physDirty;
|
|
50
|
+
private convergencePbo;
|
|
51
|
+
private convergenceFence;
|
|
48
52
|
private springTexture;
|
|
49
53
|
private sizeTexture;
|
|
50
54
|
private offsetTexture;
|
|
@@ -95,6 +99,51 @@ export declare class PhysLayout {
|
|
|
95
99
|
* Stops the layout
|
|
96
100
|
*/
|
|
97
101
|
terminateUpdate(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Synchronously reads the latest node positions/velocities from the GPU into
|
|
104
|
+
* this.physPositions.
|
|
105
|
+
*/
|
|
106
|
+
private readbackPositions;
|
|
107
|
+
/**
|
|
108
|
+
* Drives the convergence-readback state machine for the current iteration:
|
|
109
|
+
* consumes a pending fence if one is ready, otherwise starts a new readback
|
|
110
|
+
* when a convergence check is due.
|
|
111
|
+
* @returns Whether fresh velocity data is available.
|
|
112
|
+
*/
|
|
113
|
+
private tryConsumeConvergenceReadback;
|
|
114
|
+
/**
|
|
115
|
+
* The GPU texture holding the latest node positions (xy) and velocities (zw).
|
|
116
|
+
* @returns The RGBA32F position texture.
|
|
117
|
+
*
|
|
118
|
+
* @note Texel layout matches the WebGL renderer's `u_positions` sampler, so it
|
|
119
|
+
* can be bound directly to webgl renderer.
|
|
120
|
+
*/
|
|
121
|
+
getPositionTexture(): WebGLTexture;
|
|
122
|
+
/**
|
|
123
|
+
* Maps node id to its texel index in the position texture.
|
|
124
|
+
* @returns The node-id-to-texel-index map.
|
|
125
|
+
*
|
|
126
|
+
* @note This is the SolarMerger order (re-derived on add/remove), which differs from
|
|
127
|
+
* the renderer's insertion order. Consumers rendering from {@link getPositionTexture}
|
|
128
|
+
* must build their sampling indices from this.
|
|
129
|
+
*/
|
|
130
|
+
getNodeTexelIndices(): Readonly<Record<Node['id'], number>>;
|
|
131
|
+
private shouldCheckConvergence;
|
|
132
|
+
/** Allocates the pixel-pack buffer used for the async convergence readback. */
|
|
133
|
+
private setupConvergencePbo;
|
|
134
|
+
/**
|
|
135
|
+
* Kicks off an async copy of the position texture into the pixel-pack buffer.
|
|
136
|
+
* Cancels any in-flight readback.
|
|
137
|
+
*/
|
|
138
|
+
private issueConvergenceReadback;
|
|
139
|
+
/**
|
|
140
|
+
* Polls the fence without blocking; if the readback is ready, copies it into
|
|
141
|
+
* physPositions and clears the fence.
|
|
142
|
+
* @returns Whether fresh velocity data was consumed this call.
|
|
143
|
+
*/
|
|
144
|
+
private consumeConvergenceReadback;
|
|
145
|
+
/** Drops any in-flight readback fence (on stop, reset, or destroy). */
|
|
146
|
+
private cancelConvergenceReadback;
|
|
98
147
|
/**
|
|
99
148
|
* Returns whether the current layout needs to be updated in the next iteration step.
|
|
100
149
|
* @returns Whether the layout should update in the following step.
|
|
@@ -122,7 +171,7 @@ export declare class PhysLayout {
|
|
|
122
171
|
* Updates the existing nodes of a graph with a given list of updates
|
|
123
172
|
* @param nodeList - The list of updates.
|
|
124
173
|
*/
|
|
125
|
-
updateNodes(nodeList:
|
|
174
|
+
updateNodes(nodeList: PartialNode[]): void;
|
|
126
175
|
/**
|
|
127
176
|
* Adds and removes nodes from the graph based on given updates
|
|
128
177
|
* @param fullData - The entire graph object.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_physData;\nuniform sampler2D u_connections;\nuniform sampler2D u_connectionOffsets;\nuniform sampler2D u_pinnedNodes;\nuniform sampler2D u_sizeTexture;\nuniform float u_baseLength;\nuniform float u_curIteration;\nuniform float u_iterationMultiplier;\nuniform vec2 u_gravityCenter;\nuniform float u_numNodes;\nuniform float u_gravity;\n\nuniform sampler2D u_clusterData;\nuniform sampler2D u_prevForce;\nuniform float u_collisionMultiplier;\n\nout vec4 fragColor;\n\nfloat DAMP = 0.6;\nfloat COOL = 0.99998;\nfloat TIMESTEP = 1.0 / 30.0;\nfloat VELOCITYDECAY = 0.6;\nfloat accLimitLow = 500000.0;\nfloat accLimitHigh = 750000.0;\nfloat accLimitPosHigh = 10000000.0;\n\nconst float denseNodeThreshold = 1000.0;\nconst float MIN_DISTANCE = 0.00000001;\nconst float MAX_DISTANCE = 1000000000.0;\nconst float MAX_ACCELERATION = 50000.0;\n\nvec4 getTextureData(sampler2D tex, float index, float base) {\n float x = mod(index, base);\n float y = (index - x) / base;\n return texture(tex, vec2(x + 0.5, y + 0.5) / base);\n}\n\nbool isNan(float val) {\n return (val < 0.0 || 0.0 < val || val == 0.0) ? false : true;\n}\n\nconst float BIG_NUMBER = 999999999999999999.0;\nbool isInf(float val) {\n return val > BIG_NUMBER || val < -BIG_NUMBER;\n}\n\nvec2 getDelta(vec2 v1, vec2 v2) {\n vec2 delta = v1 - v2;\n float dist = length(delta);\n if (dist < MIN_DISTANCE || isNan(dist)) {\n return vec2(MIN_DISTANCE, MIN_DISTANCE);\n }\n if (dist > MAX_DISTANCE) {\n vec2 normDelta = delta / dist;\n return normDelta * MAX_DISTANCE;\n }\n return delta;\n}\n\nvec4 getOtherNodePosition(float i) {\n return getTextureData(u_physData, i, 256.0);\n}\n\nfloat getCombinedNodeSize(float i, float nodeSize) {\n float otherNodeSize = getTextureData(u_sizeTexture, i, 256.0).r;\n return nodeSize + otherNodeSize;\n}\n\nvec2 getSpringForce(float curConnection, float numConnections, float springFScale, vec4 myPosition, float i) {\n float curSpring = getTextureData(u_connections, curConnection + i, 4096.0).r;\n\n vec4 otherPosition = getTextureData(u_physData, curSpring, 256.0);\n float otherDataPosition = getTextureData(u_connectionOffsets, curSpring, 256.0).r;\n float otherNumConnections = getTextureData(u_connections, otherDataPosition, 4096.0).r;\n float avgDegree = max((otherNumConnections + numConnections * 3.0) / 4.0, 4.0);\n\n vec2 delta = getDelta(myPosition.xy, otherPosition.xy);\n float dist = length(delta);\n\n float F = (dist * dist * springFScale) / (u_baseLength * avgDegree);\n if (u_collisionMultiplier > 0.0) {\n F *= u_collisionMultiplier;\n }\n return (-delta / dist) * F;\n}\n\nvec2 getCollisionForce(float combinedNodeSize, float dist, float fScale, vec2 delta) {\n float collisionForce = (combinedNodeSize - dist) / (dist);\n if (u_collisionMultiplier > 0.0) {\n collisionForce *= u_collisionMultiplier;\n }\n return (delta * collisionForce * fScale * combinedNodeSize) / (combinedNodeSize + 1.0);\n}\n\nvec2 getRepulsionForce(float dist, float fScale, vec2 delta) {\n float F = (u_baseLength * u_baseLength * fScale) / (dist);\n return (delta / dist) * F * 0.05;\n}\n\nvoid main(void) {\n float textureSide = 256.0; //#TEXTURE_SIDE#;\n float index = (gl_FragCoord.x - 0.5) + (gl_FragCoord.y - 0.5) * textureSide;\n\n if (index >= u_numNodes) {\n discard;\n }\n\n vec4 clusterData = getTextureData(u_clusterData, index, 256.0);\n\n float clusterIndex = clusterData.x;\n float clusterStartIndex = clusterData.y;\n float clusterSize = clusterData.z;\n
|
|
1
|
+
declare const _default: "#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_physData;\nuniform sampler2D u_connections;\nuniform sampler2D u_connectionOffsets;\nuniform sampler2D u_pinnedNodes;\nuniform sampler2D u_sizeTexture;\nuniform float u_baseLength;\nuniform float u_curIteration;\nuniform float u_iterationMultiplier;\nuniform vec2 u_gravityCenter;\nuniform float u_numNodes;\nuniform float u_gravity;\n\nuniform sampler2D u_clusterData;\nuniform sampler2D u_prevForce;\nuniform float u_collisionMultiplier;\nuniform float u_maxNodeSize;\n\nout vec4 fragColor;\n\nfloat DAMP = 0.6;\nfloat COOL = 0.99998;\nfloat TIMESTEP = 1.0 / 30.0;\nfloat VELOCITYDECAY = 0.6;\nfloat accLimitLow = 500000.0;\nfloat accLimitHigh = 750000.0;\nfloat accLimitPosHigh = 10000000.0;\n\nconst float denseNodeThreshold = 1000.0;\nconst float MIN_DISTANCE = 0.00000001;\nconst float MAX_DISTANCE = 1000000000.0;\nconst float MAX_ACCELERATION = 50000.0;\n\nvec4 getTextureData(sampler2D tex, float index, float base) {\n float x = mod(index, base);\n float y = (index - x) / base;\n return texture(tex, vec2(x + 0.5, y + 0.5) / base);\n}\n\nbool isNan(float val) {\n return (val < 0.0 || 0.0 < val || val == 0.0) ? false : true;\n}\n\nconst float BIG_NUMBER = 999999999999999999.0;\nbool isInf(float val) {\n return val > BIG_NUMBER || val < -BIG_NUMBER;\n}\n\nvec2 getDelta(vec2 v1, vec2 v2) {\n vec2 delta = v1 - v2;\n float dist = length(delta);\n if (dist < MIN_DISTANCE || isNan(dist)) {\n return vec2(MIN_DISTANCE, MIN_DISTANCE);\n }\n if (dist > MAX_DISTANCE) {\n vec2 normDelta = delta / dist;\n return normDelta * MAX_DISTANCE;\n }\n return delta;\n}\n\nvec4 getOtherNodePosition(float i) {\n return getTextureData(u_physData, i, 256.0);\n}\n\nfloat getCombinedNodeSize(float i, float nodeSize) {\n float otherNodeSize = getTextureData(u_sizeTexture, i, 256.0).r;\n return nodeSize + otherNodeSize;\n}\n\nvec2 getSpringForce(float curConnection, float numConnections, float springFScale, vec4 myPosition, float i) {\n float curSpring = getTextureData(u_connections, curConnection + i, 4096.0).r;\n\n vec4 otherPosition = getTextureData(u_physData, curSpring, 256.0);\n float otherDataPosition = getTextureData(u_connectionOffsets, curSpring, 256.0).r;\n float otherNumConnections = getTextureData(u_connections, otherDataPosition, 4096.0).r;\n float avgDegree = max((otherNumConnections + numConnections * 3.0) / 4.0, 4.0);\n\n vec2 delta = getDelta(myPosition.xy, otherPosition.xy);\n float dist = length(delta);\n\n float F = (dist * dist * springFScale) / (u_baseLength * avgDegree);\n if (u_collisionMultiplier > 0.0) {\n F *= u_collisionMultiplier;\n }\n return (-delta / dist) * F;\n}\n\nvec2 getCollisionForce(float combinedNodeSize, float dist, float fScale, vec2 delta) {\n float collisionForce = (combinedNodeSize - dist) / (dist);\n if (u_collisionMultiplier > 0.0) {\n collisionForce *= u_collisionMultiplier;\n }\n return (delta * collisionForce * fScale * combinedNodeSize) / (combinedNodeSize + 1.0);\n}\n\nvec2 getRepulsionForce(float dist, float fScale, vec2 delta) {\n float F = (u_baseLength * u_baseLength * fScale) / (dist);\n return (delta / dist) * F * 0.05;\n}\n\nvoid main(void) {\n float textureSide = 256.0; //#TEXTURE_SIDE#;\n float index = (gl_FragCoord.x - 0.5) + (gl_FragCoord.y - 0.5) * textureSide;\n\n if (index >= u_numNodes) {\n discard;\n }\n\n vec4 clusterData = getTextureData(u_clusterData, index, 256.0);\n\n float clusterIndex = clusterData.x;\n float clusterStartIndex = clusterData.y;\n float clusterSize = clusterData.z;\n\n vec4 myPosition = getTextureData(u_physData, index, 256.0);\n vec4 previousForce = getTextureData(u_prevForce, clusterIndex, 256.0);\n\n vec2 acceleration = previousForce.xy;\n\n float isPinned = getTextureData(u_pinnedNodes, index, 256.0).r;\n float nodeSize = getTextureData(u_sizeTexture, index, 256.0).r;\n\n if (isPinned > 0.5) {\n fragColor = vec4(myPosition.xy, 0.0, 0.0);\n return;\n }\n\n float curConnection = getTextureData(u_connectionOffsets, index, 256.0).r;\n float numConnections = getTextureData(u_connections, curConnection, 4096.0).r;\n\n float fScale = 1.0 + sqrt(u_iterationMultiplier);\n float springFScale = fScale;\n\n if (numConnections > denseNodeThreshold) {\n springFScale = sqrt(fScale);\n }\n\n float numOfRels = 0.0;\n\n // Springs\n for (float i = 1.0; i <= 256.0 * 256.0; i++) {\n if (numOfRels >= numConnections) {\n break;\n }\n acceleration += getSpringForce(curConnection, numConnections, springFScale, myPosition, i);\n numOfRels += 1.0;\n }\n\n // Repulsion && collision detection\n if (u_collisionMultiplier > 0.0) {\n float number_of_collisions = 0.0;\n for (float i = 0.0; i < 256.0 * 256.0; i++) {\n if (i >= u_numNodes) {\n break;\n }\n\n if (i == index) {\n continue;\n }\n\n vec4 otherPosition = getOtherNodePosition(i);\n vec2 delta = getDelta(myPosition.xy, otherPosition.xy);\n float dist = length(delta);\n\n if (number_of_collisions < 40.0 && dist < (nodeSize + u_maxNodeSize) * 2.0) {\n float combinedNodeSize = getCombinedNodeSize(i, nodeSize) * 2.0;\n if (dist < combinedNodeSize) {\n number_of_collisions++;\n acceleration += getCollisionForce(combinedNodeSize, dist, fScale, delta);\n }\n }\n\n if (i >= clusterStartIndex && i < clusterStartIndex + clusterSize) {\n acceleration += getRepulsionForce(dist, fScale, delta);\n }\n }\n } else {\n for (float i = 0.0; i < 256.0 * 256.0; i++) {\n if (i >= clusterStartIndex + clusterSize || i >= u_numNodes) {\n break;\n }\n\n if (i < clusterStartIndex || i == index) {\n continue;\n }\n\n vec4 otherPosition = getOtherNodePosition(i);\n vec2 delta = getDelta(myPosition.xy, otherPosition.xy);\n float dist = length(delta);\n float combinedNodeSize = getCombinedNodeSize(i, nodeSize);\n\n if (dist < combinedNodeSize) {\n acceleration += getCollisionForce(combinedNodeSize, dist, fScale, delta);\n }\n\n acceleration += getRepulsionForce(dist, fScale, delta);\n }\n }\n\n // Gravity\n vec2 delta = getDelta(u_gravityCenter, myPosition.xy);\n float dist = length(delta);\n\n vec2 grav = (delta / dist) * u_gravity * fScale * (dist / 1000.0);\n acceleration += grav;\n\n float accMagnitude = length(acceleration);\n acceleration *= min(MAX_ACCELERATION, accMagnitude) / accMagnitude;\n\n float iterationFrictionThreshold = 1000.0;\n\n if (u_curIteration > iterationFrictionThreshold) {\n float friction = 1.0 + pow((u_curIteration - iterationFrictionThreshold), 2.0) / 100.0;\n acceleration *= 1.0 / friction;\n }\n\n if (u_curIteration == 0.0) {\n fragColor = vec4(myPosition.xy, myPosition.zw);\n } else {\n float TEMP = max(0.02, pow(COOL, u_curIteration));\n vec2 prevVelocity = myPosition.zw;\n vec2 currentPos = myPosition.xy;\n\n vec2 newPos = currentPos + TEMP * ( DAMP * (prevVelocity) + acceleration * TIMESTEP * TIMESTEP);\n vec2 newVelocity = newPos - currentPos;\n fragColor = vec4(newPos, newVelocity);\n }\n}";
|
|
2
2
|
export default _default;
|
package/dist/types/layouts/forcedirectedlayout/physlayout/shaders/multilevel-repulsive-fragment.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "#version 300 es\nprecision mediump float;\nuniform sampler2D u_physData;\nuniform sampler2D u_clusterData;\nuniform sampler2D u_finestIndexes;\nuniform sampler2D u_prevForce;\n\nuniform float u_baseLength;\nuniform float u_numNodes;\nuniform float u_iterationMultiplier;\nuniform float u_isTopLevel;\n\nout vec4 fragColor;\n\nfloat PI = 3.1415926535897932384626433832795;\nfloat TIMESTEP = 1.0 / 30.0;\nfloat VELOCITYDECAY = 1.0;\n\nvec4 getTextureData(sampler2D tex, float index, float base) {\n float x = mod(index, base);\n float y = (index - x) / base;\n return texture(tex, vec2(x + 0.5, y + 0.5) / base);\n}\n\nfloat
|
|
1
|
+
declare const _default: "#version 300 es\nprecision mediump float;\nuniform sampler2D u_physData;\nuniform sampler2D u_clusterData;\nuniform sampler2D u_finestIndexes;\nuniform sampler2D u_prevForce;\n\nuniform float u_baseLength;\nuniform float u_numNodes;\nuniform float u_iterationMultiplier;\nuniform float u_isTopLevel;\n\nout vec4 fragColor;\n\nfloat PI = 3.1415926535897932384626433832795;\nfloat TIMESTEP = 1.0 / 30.0;\nfloat VELOCITYDECAY = 1.0;\n\nvec4 getTextureData(sampler2D tex, float index, float base) {\n float x = mod(index, base);\n float y = (index - x) / base;\n return texture(tex, vec2(x + 0.5, y + 0.5) / base);\n}\n\nfloat getAdjustedClusterWeight(float clusterWeight) {\n return (clusterWeight * clusterWeight) / (clusterWeight + 1.0);\n}\n\nvoid main(void) {\n float index = (gl_FragCoord.x - 0.5) + (gl_FragCoord.y - 0.5) * 256.0;\n\n if (index >= u_numNodes) {\n discard;\n }\n\n vec4 clusterData = getTextureData(u_clusterData, index, 256.0);\n\n float clusterIndex = clusterData.x;\n float clusterStartIndex = clusterData.y;\n float clusterSize = clusterData.z;\n float clusterArea = pow(clusterSize + u_baseLength * 2.0, 2.0);\n\n float finestIndex = getTextureData(u_finestIndexes, index, 256.0).r;\n vec4 myPosition = getTextureData(u_physData, finestIndex, 256.0);\n vec4 previousForce = getTextureData(u_prevForce, clusterIndex, 256.0);\n float fScale = 1.0 + sqrt(u_iterationMultiplier);\n\n vec2 acceleration = previousForce.xy;\n\n // Repulsion & Collision Detection\n for (float i = 0.0; i < 256.0 * 256.0; i++) {\n if (i >= clusterStartIndex + clusterSize || i >= u_numNodes) {\n break;\n }\n if (i < clusterStartIndex || i == index) {\n continue;\n }\n\n vec4 otherClusterData = getTextureData(u_clusterData, i, 256.0);\n float otherClusterWeight = getAdjustedClusterWeight(otherClusterData.w);\n\n float otherFinestIndex = getTextureData(u_finestIndexes, i, 256.0).r;\n vec4 otherPosition = getTextureData(u_physData, otherFinestIndex, 256.0);\n\n vec2 delta = myPosition.xy - otherPosition.xy;\n float dist = max(length(delta), 0.0000001);\n float maxDist = 25.0;\n float repulsionForceScale = 0.1;\n\n float F = (clusterArea * fScale) / (dist * dist);\n\n if (u_isTopLevel == 1.0) {\n repulsionForceScale = 0.2;\n }\n\n acceleration += ((delta / sqrt(dist)) * F) * repulsionForceScale * otherClusterWeight;\n\n if (dist < maxDist) {\n float collide = (maxDist - dist) / (dist * dist);\n acceleration += delta * collide * fScale * maxDist / (maxDist + 1.0);\n }\n }\n\n fragColor = vec4(acceleration, vec2(finestIndex, 0));\n}";
|
|
2
2
|
export default _default;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { Node, Relationship } from '../../../../types/graph-element';
|
|
2
2
|
import type { CoarsenedGraph, SolarRelationship } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Coarsens a graph into a hierarchy of "solar systems", following the sun/planet/moon partitioning scheme from
|
|
5
|
+
* Hachul and Jünger, "Drawing Large Graphs with a Potential-Field-Based Multilevel Algorithm".
|
|
6
|
+
*
|
|
7
|
+
* @see {@link https://kups.ub.uni-koeln.de/1409/ Drawing Large Graphs with a Potential-Field-Based Multilevel Algorithm}
|
|
8
|
+
*/
|
|
3
9
|
export default class SolarMerger {
|
|
4
10
|
graph: {
|
|
5
11
|
nodes: Node[];
|
|
@@ -14,8 +20,59 @@ export default class SolarMerger {
|
|
|
14
20
|
nodes: Node[];
|
|
15
21
|
rels: Relationship[];
|
|
16
22
|
}, nodeIdToIndex: Record<string, number>);
|
|
23
|
+
/**
|
|
24
|
+
* Converts the input graph into the integer-indexed adjacency-list representation {@link SolarMerger.coarsen}
|
|
25
|
+
* operates on.
|
|
26
|
+
*
|
|
27
|
+
* @param inputGraph - The graph to convert.
|
|
28
|
+
* @param nodeIdToIndex - Maps each node's original id to its integer index.
|
|
29
|
+
*/
|
|
17
30
|
private constructGraphObjects;
|
|
31
|
+
/**
|
|
32
|
+
* Repeatedly coarsens the graph, one solar-system level at a time, until it has at most `targetSize` nodes or
|
|
33
|
+
* cannot be coarsened further. Each level's result is recorded in {@link SolarMerger.subGraphs}.
|
|
34
|
+
*
|
|
35
|
+
* @param targetSize - The node count to coarsen down to.
|
|
36
|
+
* @returns The coarsest graph reached.
|
|
37
|
+
*/
|
|
18
38
|
coarsenTo(targetSize: number): CoarsenedGraph;
|
|
39
|
+
/**
|
|
40
|
+
* Repeatedly coarsens the graph by exactly `levelToCoarsenBy` solar-system levels, regardless of the resulting
|
|
41
|
+
* node count. Each level's result is recorded in {@link SolarMerger.subGraphs}.
|
|
42
|
+
*
|
|
43
|
+
* @param levelToCoarsenBy - The number of levels to coarsen by.
|
|
44
|
+
* @returns The coarsest graph reached.
|
|
45
|
+
*/
|
|
19
46
|
coarsenBy(levelToCoarsenBy: number): CoarsenedGraph;
|
|
47
|
+
/**
|
|
48
|
+
* @param relationships - The graph's adjacency list, indexed by node id.
|
|
49
|
+
* @returns The degree a node must exceed to be treated as a hub by {@link SolarMerger.isHub}.
|
|
50
|
+
*/
|
|
51
|
+
private getHubDegreeThreshold;
|
|
52
|
+
/**
|
|
53
|
+
* Whether a node's degree is enough of an outlier that it must become its own sun rather than being absorbed as
|
|
54
|
+
* a planet/moon of another sun.
|
|
55
|
+
*
|
|
56
|
+
* @param index - The node id to check.
|
|
57
|
+
* @param relationships - The graph's adjacency list, indexed by node id.
|
|
58
|
+
* @param hubDegreeThreshold - The threshold from {@link SolarMerger.getHubDegreeThreshold}.
|
|
59
|
+
* @see {@link HubDegreeMultiplier}
|
|
60
|
+
*/
|
|
61
|
+
private isHub;
|
|
62
|
+
/**
|
|
63
|
+
* Partitions `graph` into one level of solar systems and collapses each into a single node, producing the next
|
|
64
|
+
* coarser graph.
|
|
65
|
+
*
|
|
66
|
+
* Follows the three-step solar-system construction from Hachul and Jünger, "Drawing Large Graphs with a
|
|
67
|
+
* Potential-Field-Based Multilevel Algorithm" (create suns, label their neighbors as planets, assign
|
|
68
|
+
* remaining nodes as moons of their nearest planet).
|
|
69
|
+
*
|
|
70
|
+
* @param graph - The graph to coarsen by one level.
|
|
71
|
+
* @param firstIteration - Whether `graph` is the original, uncoarsened input graph.
|
|
72
|
+
* @returns The collapsed graph (`output`), the input graph with nodes reordered by solar system (`sortedInput`),
|
|
73
|
+
* and a map from the input graph's node ids to their position in `sortedInput` (`nodeSortMap`).
|
|
74
|
+
* @see {@link https://kups.ub.uni-koeln.de/1409/ Drawing Large Graphs with a Potential-Field-Based Multilevel Algorithm}
|
|
75
|
+
*
|
|
76
|
+
*/
|
|
20
77
|
private coarsen;
|
|
21
78
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NvlState } from '../../modules/state/types';
|
|
2
|
-
import type { Node } from '../../types/graph-element';
|
|
2
|
+
import type { Node, PartialNode } from '../../types/graph-element';
|
|
3
3
|
import type { Point } from '../../utils/geometry';
|
|
4
4
|
export declare class FreeLayout {
|
|
5
5
|
private state;
|
|
@@ -10,9 +10,7 @@ export declare class FreeLayout {
|
|
|
10
10
|
state: NvlState;
|
|
11
11
|
});
|
|
12
12
|
setOptions(): any;
|
|
13
|
-
updateNodes(positionList:
|
|
14
|
-
id: string;
|
|
15
|
-
})[]): void;
|
|
13
|
+
updateNodes(positionList: PartialNode[]): void;
|
|
16
14
|
update(refreshPositions?: boolean): void;
|
|
17
15
|
layout(nodes: Node[], nodeIds: Record<string, Node>, idToPosition: Record<string, Node>): void;
|
|
18
16
|
setNodePositions(p: Record<string, Point>): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NvlState } from '../../modules/state/types';
|
|
2
|
-
import type { Node } from '../../types/graph-element';
|
|
2
|
+
import type { Node, PartialNode } from '../../types/graph-element';
|
|
3
3
|
import type { Point } from '../../utils/geometry';
|
|
4
4
|
export declare class GridLayout {
|
|
5
5
|
state: NvlState;
|
|
@@ -10,11 +10,7 @@ export declare class GridLayout {
|
|
|
10
10
|
state: NvlState;
|
|
11
11
|
});
|
|
12
12
|
setOptions(): void;
|
|
13
|
-
updateNodes(positionList:
|
|
14
|
-
id: string;
|
|
15
|
-
x: number;
|
|
16
|
-
y: number;
|
|
17
|
-
}[]): void;
|
|
13
|
+
updateNodes(positionList: PartialNode[]): void;
|
|
18
14
|
update(refreshPositions?: boolean): void;
|
|
19
15
|
layout(mobxNodes: any): void;
|
|
20
16
|
setNodePositions(idToPosition: Record<string, Node & Point>): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Node, type PartialNode, type PartialRelationship, type Relationship } from '../types/graph-element';
|
|
1
|
+
import { type Node, type NodeWithClientPosition, type PartialNode, type PartialRelationship, type Relationship } from '../types/graph-element';
|
|
2
2
|
import '../types/nvl-window-functions';
|
|
3
3
|
import type { Point } from '../utils/geometry';
|
|
4
4
|
import type { ExternalCallbacks } from './ExternalCallbackHandler';
|
|
@@ -61,15 +61,39 @@ export default class NvlController {
|
|
|
61
61
|
private layoutComputingCallback;
|
|
62
62
|
private currentLayoutType;
|
|
63
63
|
private descriptionElement;
|
|
64
|
+
/** True when a minimap container was provided; disables the GPU-texture fast path (separate GL context). */
|
|
65
|
+
private readonly hasMinimap;
|
|
64
66
|
constructor(state: NvlState, frame: HTMLElement, options: NvlOptions);
|
|
65
67
|
private onWebGLContextLost;
|
|
66
68
|
private fpsToFrameBudgetMs;
|
|
67
69
|
private getLayoutNeedsToUpdate;
|
|
68
70
|
private isWithinBudget;
|
|
71
|
+
/**
|
|
72
|
+
* Add elapsed frame time to the layout-step accumulator, capped at `layoutTimeLimit`.
|
|
73
|
+
* Prevents a slow frame (e.g. blocked GPU readback) from inflating catch-up debt and
|
|
74
|
+
* scheduling ever more work in a feedback loop; the layout falls behind gracefully instead.
|
|
75
|
+
*/
|
|
76
|
+
private accumulateLayoutElapsedTime;
|
|
77
|
+
/**
|
|
78
|
+
* Whether the GPU force-directed layout is active.
|
|
79
|
+
* @returns `true` when PhysLayout is the current layout.
|
|
80
|
+
*/
|
|
81
|
+
private usesGpuSimulation;
|
|
82
|
+
/**
|
|
83
|
+
* Whether the GPU-texture fast path can be taken this frame.
|
|
84
|
+
* Requires the WebGL renderer, GPU layout active, no minimap, and no CPU-position consumers
|
|
85
|
+
* (layout-step callback, HTML overlays).
|
|
86
|
+
* @returns `true` when rendering directly from the GPU position texture is safe.
|
|
87
|
+
*/
|
|
88
|
+
private canRenderFromGpuTexture;
|
|
89
|
+
/** Syncs idToPosition from the GPU layout on demand; no-op for CPU layouts. */
|
|
90
|
+
syncPositionsFromLayout(): void;
|
|
69
91
|
private updateMinimapZoom;
|
|
70
92
|
private startMainLoop;
|
|
93
|
+
private renderNodeHtmlOverlays;
|
|
94
|
+
private renderRelationshipHtmlOverlays;
|
|
71
95
|
updateHtmlOverlay(): void;
|
|
72
|
-
dumpNodes():
|
|
96
|
+
dumpNodes(): NodeWithClientPosition[];
|
|
73
97
|
dumpRelationships(): Relationship[];
|
|
74
98
|
/**
|
|
75
99
|
* Map a relative Canvas space to the DOM position
|
|
@@ -122,6 +146,8 @@ export default class NvlController {
|
|
|
122
146
|
private getDataUrlForCanvas;
|
|
123
147
|
private initiateFileDownload;
|
|
124
148
|
private updateLayoutAndPositions;
|
|
149
|
+
private getAndUpdatePositions;
|
|
150
|
+
private getLayoutPositions;
|
|
125
151
|
saveToFile(options: SaveToFileOptions): void;
|
|
126
152
|
private createSvgString;
|
|
127
153
|
saveToSvg(options?: SaveToFileOptions): Promise<void>;
|
|
@@ -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
|
|
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}.
|
|
@@ -42,14 +42,6 @@ export interface ForceDirectedOptions {
|
|
|
42
42
|
* require the Intel GPU shader workaround. This option will be removed in a future release.
|
|
43
43
|
*/
|
|
44
44
|
intelWorkaround?: boolean;
|
|
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
|
|
51
|
-
*/
|
|
52
|
-
enableCytoscape?: boolean;
|
|
53
45
|
/**
|
|
54
46
|
* How initial node positions are seeded before the physics simulation runs.
|
|
55
47
|
* @defaultValue 'box'
|
|
@@ -237,22 +229,21 @@ export interface NvlOptions {
|
|
|
237
229
|
minimapViewportBoxColor?: string;
|
|
238
230
|
};
|
|
239
231
|
/**
|
|
240
|
-
* Target minimum presentation
|
|
241
|
-
*
|
|
232
|
+
* Target minimum presentation fps while layout is active, at the cost of stabilization speed when set higher.
|
|
233
|
+
* Budgets layout steps only. Actual fps may undershoot when rendering is expensive.
|
|
242
234
|
*
|
|
243
235
|
* @defaultValue 3
|
|
244
236
|
*
|
|
245
|
-
* @note
|
|
237
|
+
* @note Will be capped between 1 and 120.
|
|
246
238
|
*/
|
|
247
239
|
minFps?: number;
|
|
248
240
|
/**
|
|
249
|
-
* Target maximum presentation
|
|
250
|
-
*
|
|
251
|
-
* computation available is used to speed up layout stabilization.
|
|
241
|
+
* Target maximum presentation fps while layout is active; extra time in the tick goes to layout stabilization.
|
|
242
|
+
* Budgets layout steps only. Actual fps may undershoot when rendering is expensive.
|
|
252
243
|
*
|
|
253
244
|
* @defaultValue 30
|
|
254
245
|
*
|
|
255
|
-
* @note
|
|
246
|
+
* @note Will be capped between 1 and 120.
|
|
256
247
|
*/
|
|
257
248
|
maxFps?: number;
|
|
258
249
|
/**
|
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|