@neo4j-nvl/base 1.2.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/dist/base.mjs +1 -1
- package/dist/types/index.d.ts +21 -16
- package/dist/types/layouts/animatedlayout/AnimatedLayout.d.ts +2 -2
- package/dist/types/layouts/d3forcelayout/constants.d.ts +0 -1
- package/dist/types/layouts/d3forcelayout/d3ForceLayout.d.ts +3 -3
- package/dist/types/layouts/forcedirectedlayout/PhysLayout.d.ts +275 -0
- package/dist/types/layouts/forcedirectedlayout/clustering/ClusterByMerger.d.ts +62 -0
- package/dist/types/layouts/forcedirectedlayout/clustering/SolarMerger.d.ts +56 -0
- package/dist/types/layouts/forcedirectedlayout/clustering/clustering-utils.d.ts +143 -0
- package/dist/types/layouts/forcedirectedlayout/clustering/clustering-utils.test.d.ts +1 -0
- package/dist/types/layouts/forcedirectedlayout/{physlayout/solarmerger → clustering}/types.d.ts +11 -1
- package/dist/types/layouts/forcedirectedlayout/shaders/multilevel-fragment.d.ts +2 -0
- package/dist/types/layouts/forcedirectedlayout/shaders/multilevel-repulsive-fragment.d.ts +2 -0
- package/dist/types/layouts/forcedirectedlayout/shaders/multilevel-update-fragment.d.ts +2 -0
- package/dist/types/layouts/forcedirectedlayout/shaders/sim-vertex.d.ts +2 -0
- package/dist/types/layouts/freeLayout/FreeLayout.d.ts +2 -4
- package/dist/types/layouts/gridLayout/GridLayout.d.ts +2 -6
- package/dist/types/layouts/hierarchicallayout/HierarchicalLayout.d.ts +5 -0
- package/dist/types/modules/ExternalCallbackHandler.d.ts +3 -3
- package/dist/types/modules/NvlController.d.ts +85 -5
- package/dist/types/modules/Shader.d.ts +1 -1
- package/dist/types/modules/dataset.d.ts +4 -2
- package/dist/types/modules/state/types.d.ts +129 -27
- package/dist/types/modules/state/utils.d.ts +2 -2
- package/dist/types/renderers/ClusterOverlay.d.ts +73 -0
- package/dist/types/renderers/ClusterOverlay.test.d.ts +1 -0
- package/dist/types/renderers/domrenderer/shared/ImageCache.d.ts +1 -0
- package/dist/types/renderers/domrenderer/shared/types.d.ts +1 -2
- package/dist/types/renderers/webglrenderer/Controller.d.ts +27 -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 +43 -3
- 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-fragment-line.d.ts +1 -1
- package/dist/types/renderers/webglrenderer/rel-glsl-utils.d.ts +1 -0
- package/dist/types/renderers/webglrenderer/rel-vertex-arrow.d.ts +2 -0
- 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/canvasManagement.d.ts +1 -1
- package/dist/types/utils/clusterGeometry.d.ts +14 -0
- package/dist/types/utils/clusterGeometry.test.d.ts +1 -0
- package/dist/types/utils/clusterHitRegions.d.ts +42 -0
- package/dist/types/utils/clusterHitRegions.test.d.ts +1 -0
- package/dist/types/utils/constants.d.ts +6 -0
- package/dist/types/utils/geometry.d.ts +44 -1
- package/dist/types/utils/hittest.d.ts +30 -4
- package/dist/types/utils/jsDriverResultTransformer.d.ts +11 -9
- package/dist/types/utils/layoutSeeding.d.ts +7 -0
- package/dist/types/utils/layoutSeeding.test.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 +3 -3
- package/dist/types/layouts/forcedirectedlayout/ForceCytoLayout.d.ts +0 -22
- package/dist/types/layouts/forcedirectedlayout/cosebilkentlayout/CoseBilkentLayout.d.ts +0 -33
- package/dist/types/layouts/forcedirectedlayout/physlayout/PhysLayout.d.ts +0 -174
- package/dist/types/layouts/forcedirectedlayout/physlayout/shaders/multilevel-fragment.d.ts +0 -2
- package/dist/types/layouts/forcedirectedlayout/physlayout/shaders/multilevel-repulsive-fragment.d.ts +0 -2
- package/dist/types/layouts/forcedirectedlayout/physlayout/shaders/multilevel-update-fragment.d.ts +0 -2
- package/dist/types/layouts/forcedirectedlayout/physlayout/shaders/sim-vertex.d.ts +0 -2
- package/dist/types/layouts/forcedirectedlayout/physlayout/shaders/workaround-fragment.d.ts +0 -2
- package/dist/types/layouts/forcedirectedlayout/physlayout/solarmerger/SolarMerger.d.ts +0 -21
- package/dist/types/layouts/forcedirectedlayout/physlayout/solarmerger/solar-placer.d.ts +0 -2
- /package/dist/types/layouts/forcedirectedlayout/{physlayout/PhysLayout.test.d.ts → PhysLayout.test.d.ts} +0 -0
- /package/dist/types/layouts/forcedirectedlayout/{physlayout/solarmerger/SolarMerger.bench.d.ts → clustering/ClusterByMerger.test.d.ts} +0 -0
- /package/dist/types/layouts/forcedirectedlayout/{physlayout/solarmerger/SolarMerger.test.d.ts → clustering/SolarMerger.bench.d.ts} +0 -0
- /package/dist/types/layouts/forcedirectedlayout/{physlayout/solarmerger/solar-placer.test.d.ts → clustering/SolarMerger.test.d.ts} +0 -0
- /package/dist/types/layouts/forcedirectedlayout/{physlayout/glwrapper.d.ts → glwrapper.d.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types/layouts/forcedirectedlayout/{physlayout/solarmerger → clustering}/types.d.ts
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Node } from '
|
|
1
|
+
import type { Node } from '../../../types/graph-element';
|
|
2
2
|
export interface SolarNode extends Omit<Node, 'id' | 'size'> {
|
|
3
3
|
id: number;
|
|
4
4
|
weight?: number;
|
|
@@ -13,6 +13,7 @@ export interface SolarNode extends Omit<Node, 'id' | 'size'> {
|
|
|
13
13
|
y: number;
|
|
14
14
|
};
|
|
15
15
|
size?: number | (() => number);
|
|
16
|
+
synthetic?: boolean;
|
|
16
17
|
}
|
|
17
18
|
export interface SolarRelationship {
|
|
18
19
|
id: string;
|
|
@@ -23,6 +24,11 @@ export interface CoarsenedGraph {
|
|
|
23
24
|
nodes: SolarNode[];
|
|
24
25
|
relationships: number[][];
|
|
25
26
|
idToRel: Record<string, SolarRelationship>;
|
|
27
|
+
/**
|
|
28
|
+
* Contains the "real" {@link relationships} plus any synthetic edges purely for layout purposes.
|
|
29
|
+
* Undefined when there are no synthetic edges (e.g. SolarMerger).
|
|
30
|
+
*/
|
|
31
|
+
springRelationships?: number[][];
|
|
26
32
|
}
|
|
27
33
|
export interface Sun extends SolarNode {
|
|
28
34
|
planets: Planet[];
|
|
@@ -30,6 +36,8 @@ export interface Sun extends SolarNode {
|
|
|
30
36
|
children: () => Planet[];
|
|
31
37
|
previousIndex: number;
|
|
32
38
|
}
|
|
39
|
+
/** Sun fields known before {@link attachPlanetsToSun} fills planets/size/children/weight. */
|
|
40
|
+
export type PartialSun = Omit<Sun, 'planets' | 'size' | 'children' | 'previousIndex' | 'weight'>;
|
|
33
41
|
export interface Planet extends SolarNode {
|
|
34
42
|
parent: Sun;
|
|
35
43
|
sunId: number;
|
|
@@ -37,6 +45,8 @@ export interface Planet extends SolarNode {
|
|
|
37
45
|
size: () => number;
|
|
38
46
|
children: () => Moon[];
|
|
39
47
|
}
|
|
48
|
+
/** Planet fields known before {@link attachPlanetsToSun} sets parent. */
|
|
49
|
+
export type PartialPlanet = Omit<Planet, 'parent'>;
|
|
40
50
|
export interface Moon extends SolarNode {
|
|
41
51
|
parent: Planet;
|
|
42
52
|
sunId: number;
|
|
@@ -0,0 +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;\nuniform float u_maxNodeSize;\nuniform float u_crossClusterRelationshipMultiplier;\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, float myClusterStartIndex) {\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) * u_collisionMultiplier;\n\n // u_crossClusterRelationshipMultiplier only damps edges that cross a cluster boundary\n if (u_crossClusterRelationshipMultiplier < 1.0) {\n float otherClusterStartIndex = getTextureData(u_clusterData, curSpring, 256.0).y;\n F *= otherClusterStartIndex == myClusterStartIndex ? 1.0 : u_crossClusterRelationshipMultiplier;\n }\n\n return (-delta / dist) * F;\n}\n\nvec2 getCollisionForce(float combinedNodeSize, float dist, float fScale, vec2 delta) {\n float collisionForce = (combinedNodeSize - dist) / (dist) * u_collisionMultiplier;\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) * u_collisionMultiplier;\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 // Prime the second ping-pong texture with the initial state without calculating forces\n // that would otherwise be discarded at the end of this first iteration.\n if (u_curIteration == 0.0) {\n fragColor = vec4(myPosition.xy, myPosition.zw);\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, clusterStartIndex);\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 }\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 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}";
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +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 getAdjustedClusterWeight(float clusterWeight) {\n return (clusterWeight * clusterWeight) / (clusterWeight + 1.0);\n}\n\n// Repulsion area from peer mass (weight). .z size is only for mid-level loop bounds.\nfloat getPeerFootprintArea(float peerWeight) {\n return pow(sqrt(peerWeight) * 4.0 + u_baseLength * 2.0, 2.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 bool isTopLevel = u_isTopLevel == 1.0;\n\n float clusterIndex = clusterData.x;\n float clusterStartIndex = clusterData.y;\n float clusterSize = clusterData.z;\n float singleNodeRepulsionScale = isTopLevel && clusterSize == 1.0 ? 0.5 : 1.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 float loopEnd = isTopLevel ? u_numNodes : (clusterStartIndex + clusterSize);\n\n // Repulsion & Collision Detection\n for (float i = 0.0; i < 256.0 * 256.0; i++) {\n if (i >= loopEnd || i >= u_numNodes) {\n break;\n }\n if ((!isTopLevel && 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 area = getPeerFootprintArea(otherClusterData.w);\n float F = (area * fScale) / (dist * dist);\n\n if (isTopLevel) {\n repulsionForceScale = 0.2;\n }\n\n acceleration += ((delta / sqrt(dist)) * F) * repulsionForceScale * otherClusterWeight * singleNodeRepulsionScale;\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
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: "#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_physData;\nuniform sampler2D u_updateData;\nuniform float u_numNodesNew;\n\nout vec4 fragColor;\n\nconst float INDEX_FLAG = 999999.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\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_numNodesNew) {\n discard;\n }\n\n float firstValue = getTextureData(u_updateData, index * 2.0, 256.0).r;\n float secondValue = getTextureData(u_updateData, index * 2.0 + 1.0, 256.0).r;\n\n if (secondValue == INDEX_FLAG) {\n vec4 myPosition = getTextureData(u_physData, firstValue, 256.0);\n fragColor = myPosition;\n } else {\n fragColor = vec4(firstValue, secondValue, 0.0, 0.0);\n }\n}\n";
|
|
2
|
+
export default _default;
|
|
@@ -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;
|
|
@@ -7,6 +7,8 @@ type HierarchicalLayoutConfig = {
|
|
|
7
7
|
export declare class HierarchicalLayout extends AnimatedLayout {
|
|
8
8
|
private direction;
|
|
9
9
|
private packing;
|
|
10
|
+
private clusterBy?;
|
|
11
|
+
private lastClusterByAssignments;
|
|
10
12
|
private stateDisposers;
|
|
11
13
|
private oldComputing;
|
|
12
14
|
private computing;
|
|
@@ -14,9 +16,12 @@ export declare class HierarchicalLayout extends AnimatedLayout {
|
|
|
14
16
|
private worker;
|
|
15
17
|
private directionChanged;
|
|
16
18
|
private packingChanged;
|
|
19
|
+
private clusterByChanged;
|
|
17
20
|
private workersDisabled;
|
|
18
21
|
constructor(config: HierarchicalLayoutConfig);
|
|
19
22
|
setOptions(options: LayoutOptions): void;
|
|
23
|
+
private applyClusterByOption;
|
|
24
|
+
private buildClusterAssignments;
|
|
20
25
|
update(refreshPositions?: boolean): void;
|
|
21
26
|
getShouldUpdate(): boolean;
|
|
22
27
|
getComputing(): boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { NodeWithClientPosition } from '../types/graph-element';
|
|
2
2
|
/**
|
|
3
3
|
* An object specifying the required callback functions for certain actions
|
|
4
4
|
*/
|
|
@@ -6,7 +6,7 @@ export interface ExternalCallbacks {
|
|
|
6
6
|
/** Triggered when a layout is done moving. */
|
|
7
7
|
onLayoutDone?: () => void;
|
|
8
8
|
/** Triggered on each step of a layout. */
|
|
9
|
-
onLayoutStep?: (p:
|
|
9
|
+
onLayoutStep?: (p: NodeWithClientPosition[]) => void;
|
|
10
10
|
/** Triggered when NVL throws an error after initialization. */
|
|
11
11
|
onError?: (error: Error) => void;
|
|
12
12
|
/** Triggered when NVL is initialized. */
|
|
@@ -32,7 +32,7 @@ export default class ExternalCallbackHandler {
|
|
|
32
32
|
onInitialization(): void;
|
|
33
33
|
onZoomTransitionDone(): void;
|
|
34
34
|
onLayoutDone(): void;
|
|
35
|
-
onLayoutStep(p:
|
|
35
|
+
onLayoutStep(p: NodeWithClientPosition[]): void;
|
|
36
36
|
onLayoutComputing(isComputing: boolean): void;
|
|
37
37
|
onError(error: Error): void;
|
|
38
38
|
onWebGLContextLost(webGLContextEvent: WebGLContextEvent): 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';
|
|
@@ -31,10 +31,13 @@ export default class NvlController {
|
|
|
31
31
|
private readonly forceLayout;
|
|
32
32
|
private readonly canvasRenderer;
|
|
33
33
|
private readonly svgRenderer;
|
|
34
|
+
private clusterOverlay;
|
|
34
35
|
private readonly glCanvas;
|
|
35
36
|
private canvasRect;
|
|
36
37
|
private readonly glMinimapCanvas;
|
|
37
38
|
private readonly c2dCanvas;
|
|
39
|
+
private clusterOverlayCanvas;
|
|
40
|
+
private readonly canvasParent;
|
|
38
41
|
private readonly svg;
|
|
39
42
|
private isInRenderSwitchAnimation;
|
|
40
43
|
private previousRenderer;
|
|
@@ -46,25 +49,96 @@ export default class NvlController {
|
|
|
46
49
|
private readonly stateDisposers;
|
|
47
50
|
private readonly zoomTransitionHandler;
|
|
48
51
|
private currentLayout;
|
|
52
|
+
private lastTimestamp;
|
|
53
|
+
private accumulator;
|
|
49
54
|
private readonly layoutTimeLimit;
|
|
55
|
+
private readonly smoothFrameBudgetMs;
|
|
50
56
|
private pixelRatio;
|
|
51
57
|
private readonly removeResizeListener;
|
|
52
58
|
private readonly removeMinimapResizeListener;
|
|
53
59
|
private pendingZoom;
|
|
54
60
|
private pendingPan;
|
|
55
61
|
private pendingViewportOverride;
|
|
56
|
-
private layoutRunner;
|
|
57
62
|
private animationRequestId;
|
|
58
63
|
private layoutDoneCallback;
|
|
59
64
|
private layoutComputingCallback;
|
|
60
65
|
private currentLayoutType;
|
|
61
66
|
private descriptionElement;
|
|
67
|
+
/** True when a minimap container was provided at construction. */
|
|
68
|
+
private readonly hasMinimap;
|
|
69
|
+
private lastMinimapPositions;
|
|
70
|
+
private lastMinimapCacheVersion;
|
|
62
71
|
constructor(state: NvlState, frame: HTMLElement, options: NvlOptions);
|
|
63
72
|
private onWebGLContextLost;
|
|
73
|
+
private fpsToFrameBudgetMs;
|
|
74
|
+
private getLayoutNeedsToUpdate;
|
|
75
|
+
private isWithinBudget;
|
|
76
|
+
/**
|
|
77
|
+
* Add elapsed frame time to the layout-step accumulator, capped at `layoutTimeLimit`.
|
|
78
|
+
* Prevents a slow frame (e.g. blocked GPU readback) from inflating catch-up debt and
|
|
79
|
+
* scheduling ever more work in a feedback loop; the layout falls behind gracefully instead.
|
|
80
|
+
*/
|
|
81
|
+
private accumulateLayoutElapsedTime;
|
|
82
|
+
/**
|
|
83
|
+
* Whether the GPU force-directed layout is active.
|
|
84
|
+
* @returns `true` when PhysLayout is the current layout.
|
|
85
|
+
*/
|
|
86
|
+
private usesGpuSimulation;
|
|
87
|
+
/**
|
|
88
|
+
* Whether the GPU-texture fast path can be taken this frame.
|
|
89
|
+
* Requires the WebGL renderer, GPU layout active, and no CPU-position consumers
|
|
90
|
+
* (layout-step callback, HTML overlays). A provided minimap no longer blocks this path;
|
|
91
|
+
* the minimap and cluster overlay are updated from the lagged async position cache instead.
|
|
92
|
+
* @returns `true` when rendering directly from the GPU position texture is safe.
|
|
93
|
+
*/
|
|
94
|
+
private canRenderFromGpuTexture;
|
|
95
|
+
/**
|
|
96
|
+
* Uploads lagged PhysLayout cache positions to the minimap when the cache version changed.
|
|
97
|
+
* @returns `true` when positions were refreshed this call.
|
|
98
|
+
*/
|
|
99
|
+
private refreshMinimapFromCacheIfStale;
|
|
100
|
+
/**
|
|
101
|
+
* Fits, renders, and caches minimap positions (and PhysLayout cache version when applicable).
|
|
102
|
+
*/
|
|
103
|
+
private syncMinimap;
|
|
104
|
+
/** Whether either cluster decoration is enabled in the current layout options. */
|
|
105
|
+
private hasClusterDecorations;
|
|
106
|
+
/**
|
|
107
|
+
* Creates the decoration canvas and its overlay.
|
|
108
|
+
* @returns The overlay to draw with.
|
|
109
|
+
*/
|
|
110
|
+
private createClusterOverlay;
|
|
111
|
+
/**
|
|
112
|
+
* Resolves the overlay to draw into, allocating it on first use and tearing it down again
|
|
113
|
+
* while cluster decorations are off.
|
|
114
|
+
* @returns The overlay to draw with, or null when decorations are disabled.
|
|
115
|
+
*/
|
|
116
|
+
private ensureClusterOverlay;
|
|
117
|
+
/** Tears the overlay and its canvas back down once decorations are switched off. */
|
|
118
|
+
private destroyClusterOverlay;
|
|
119
|
+
/**
|
|
120
|
+
* Draws (or clears) cluster decorations on the Canvas2D overlay from explicit positions.
|
|
121
|
+
* @param positions - CPU node positions for this frame, or null to clear.
|
|
122
|
+
*/
|
|
123
|
+
private renderClusterOverlay;
|
|
124
|
+
/**
|
|
125
|
+
* Draws cluster decorations from PhysLayout's lagged async PBO cache without a sync dump.
|
|
126
|
+
* @param forceLayout - The PhysLayout instance to render the cluster overlay from.
|
|
127
|
+
*/
|
|
128
|
+
private renderClusterOverlayFromCache;
|
|
129
|
+
/** Syncs idToPosition from the GPU layout on demand; no-op for CPU layouts. */
|
|
130
|
+
syncPositionsFromLayout(): void;
|
|
131
|
+
/**
|
|
132
|
+
* Fitted {@link ClusteringOptions.clusterBy} hull regions for the current layout, or [].
|
|
133
|
+
* @returns Cluster hit regions for picking.
|
|
134
|
+
*/
|
|
135
|
+
getClusterByHitRegions(): import("../utils/clusterHitRegions").ClusterHitRegion[];
|
|
64
136
|
private updateMinimapZoom;
|
|
65
137
|
private startMainLoop;
|
|
138
|
+
private renderNodeHtmlOverlays;
|
|
139
|
+
private renderRelationshipHtmlOverlays;
|
|
66
140
|
updateHtmlOverlay(): void;
|
|
67
|
-
dumpNodes():
|
|
141
|
+
dumpNodes(): NodeWithClientPosition[];
|
|
68
142
|
dumpRelationships(): Relationship[];
|
|
69
143
|
/**
|
|
70
144
|
* Map a relative Canvas space to the DOM position
|
|
@@ -93,7 +167,7 @@ export default class NvlController {
|
|
|
93
167
|
};
|
|
94
168
|
registerDoneCallback(cb: NvlController['layoutDoneCallback']): void;
|
|
95
169
|
registerComputingCallback(cb: NvlController['layoutComputingCallback']): void;
|
|
96
|
-
getPosition(nodeId: string): Node;
|
|
170
|
+
getPosition(nodeId: string): Node & Point;
|
|
97
171
|
getScale(): number;
|
|
98
172
|
getPan(): {
|
|
99
173
|
x: number;
|
|
@@ -113,10 +187,16 @@ export default class NvlController {
|
|
|
113
187
|
getCanvasNodesAt(point: Point, hitNodeMarginWidth?: number): import("..").HitTargetNode[];
|
|
114
188
|
private getLayout;
|
|
115
189
|
setLayout(layoutType: Layout): void;
|
|
116
|
-
setLayoutOptions(options
|
|
190
|
+
setLayoutOptions(options?: LayoutOptions): void;
|
|
191
|
+
/**
|
|
192
|
+
* Syncs positions if needed and redraws (or clears) the cluster overlay from current state.
|
|
193
|
+
*/
|
|
194
|
+
private refreshClusterOverlay;
|
|
117
195
|
private getDataUrlForCanvas;
|
|
118
196
|
private initiateFileDownload;
|
|
119
197
|
private updateLayoutAndPositions;
|
|
198
|
+
private getAndUpdatePositions;
|
|
199
|
+
private getLayoutPositions;
|
|
120
200
|
saveToFile(options: SaveToFileOptions): void;
|
|
121
201
|
private createSvgString;
|
|
122
202
|
saveToSvg(options?: SaveToFileOptions): Promise<void>;
|
|
@@ -4,7 +4,7 @@ export default class Shader {
|
|
|
4
4
|
private curTexture;
|
|
5
5
|
private attributeInfo;
|
|
6
6
|
private uniformInfo;
|
|
7
|
-
constructor(gl:
|
|
7
|
+
constructor(gl: WebGL2RenderingContext, vsSource: string, fsSource: string, defines?: {});
|
|
8
8
|
setUniform(name: string, value: any): void;
|
|
9
9
|
setAttributePointer(name: string, size: number, start: number, stride: number): void;
|
|
10
10
|
setAttributePointerFloat(name: string, size: number, start: number, stride: number): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Node, Relationship } from '../types/graph-element';
|
|
2
|
-
import type
|
|
2
|
+
import { type Point } from '../utils/geometry';
|
|
3
3
|
/** A graph stored with mobx. */
|
|
4
4
|
export interface DataSet<T> {
|
|
5
5
|
items: T[];
|
|
@@ -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}.
|
|
@@ -34,29 +34,98 @@ export type DisabledItemStyles = {
|
|
|
34
34
|
fontColor: string;
|
|
35
35
|
};
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
37
|
+
* Identifier returned by {@link ClusterByFn} to group nodes into physical clusters.
|
|
38
|
+
* Nodes sharing the same string or number are clustered together.
|
|
38
39
|
*/
|
|
39
|
-
export
|
|
40
|
+
export type ClusterId = string | number;
|
|
41
|
+
/**
|
|
42
|
+
* A physical cluster produced by {@link ClusteringOptions.clusterBy}.
|
|
43
|
+
* Not a graph element — used for hit-testing and interaction callbacks.
|
|
44
|
+
*/
|
|
45
|
+
export type Cluster = {
|
|
46
|
+
/** Semantic cluster id returned by {@link ClusterByFn}. */
|
|
47
|
+
id: ClusterId;
|
|
48
|
+
/** Real member node ids belonging to this cluster (at least two). */
|
|
49
|
+
nodeIds: string[];
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Function that maps a node id to a {@link ClusterId} for force-directed clustering.
|
|
53
|
+
* Returns `undefined` for nodes that should remain independent.
|
|
54
|
+
*/
|
|
55
|
+
export type ClusterByFn = (nodeId: string) => ClusterId | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Options that control physical clustering and cluster decorations for the force-directed layout.
|
|
58
|
+
*/
|
|
59
|
+
export interface ClusteringOptions {
|
|
60
|
+
/**
|
|
61
|
+
* Optional function to group nodes into physical clusters based on a custom identifier.
|
|
62
|
+
* Nodes returning the same string or number will be grouped together in the layout.
|
|
63
|
+
* Nodes returning undefined will be treated as independent.
|
|
64
|
+
*
|
|
65
|
+
* @note Ensure this function has a stable reference to avoid evaluation on every render.
|
|
66
|
+
*/
|
|
67
|
+
clusterBy?: ClusterByFn;
|
|
40
68
|
/**
|
|
41
|
-
*
|
|
42
|
-
* When enabled, this flag modifies how physics simulation shaders are compiled to avoid
|
|
43
|
-
* rendering problems on Intel graphics hardware.
|
|
69
|
+
* Draws a convex-hull outline around each cluster.
|
|
44
70
|
*
|
|
45
|
-
* @
|
|
71
|
+
* @note Outline color is the same as the nodes in the cluster.
|
|
72
|
+
* If the nodes in the cluster have different colors, the outline uses
|
|
73
|
+
* {@link NvlOptions.styling.overlayLineColor}.
|
|
74
|
+
* @defaultValue false
|
|
75
|
+
*/
|
|
76
|
+
drawOutline?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Fills a soft glowing halo around each cluster.
|
|
46
79
|
*
|
|
47
|
-
* @
|
|
48
|
-
*
|
|
80
|
+
* @note Halo color is the same as the nodes in the cluster.
|
|
81
|
+
* If the nodes in the cluster have different colors, the halo won't be shown.
|
|
82
|
+
* @defaultValue false
|
|
49
83
|
*/
|
|
50
|
-
|
|
84
|
+
drawHalo?: boolean;
|
|
51
85
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* @
|
|
86
|
+
* Spring force multiplier for relationships that cross a {@link ClusteringOptions.clusterBy}
|
|
87
|
+
* cluster boundary.
|
|
88
|
+
* Capped between `0` and `1`.
|
|
89
|
+
* `0` disables cross-cluster spring pull. `1` is full strength. Intra-cluster edges are always
|
|
90
|
+
* full strength. Only applies when {@link ClusteringOptions.clusterBy} is set.
|
|
91
|
+
* @defaultValue 0
|
|
92
|
+
* @experimental
|
|
93
|
+
* @note Can cause unstable layouts when set to close to `1` in highly connected graphs.
|
|
94
|
+
*/
|
|
95
|
+
crossClusterRelationshipMultiplier?: number;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The options for the force directed layout
|
|
99
|
+
*/
|
|
100
|
+
export interface ForceDirectedOptions {
|
|
101
|
+
/**
|
|
102
|
+
* How initial node positions are seeded before the physics simulation runs.
|
|
103
|
+
* @defaultValue 'box'
|
|
104
|
+
*/
|
|
105
|
+
seedingMethod?: 'box' | 'circle';
|
|
106
|
+
/**
|
|
107
|
+
* Physical clustering and cluster decoration options.
|
|
57
108
|
*/
|
|
58
|
-
|
|
109
|
+
clustering?: ClusteringOptions;
|
|
59
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Returns the clustering options from force-directed layout options, if present.
|
|
113
|
+
* @param layoutOptions - The layout options to read from.
|
|
114
|
+
* @returns The clustering options, or undefined when absent / not force-directed.
|
|
115
|
+
*/
|
|
116
|
+
export declare const getClusteringOptions: (layoutOptions?: LayoutOptions) => ClusteringOptions | undefined;
|
|
117
|
+
/**
|
|
118
|
+
* Whether {@link ClusteringOptions.drawOutline} is enabled on the given layout options.
|
|
119
|
+
* @param layoutOptions - The layout options to check.
|
|
120
|
+
* @returns Whether {@link ClusteringOptions.drawOutline} is enabled on the given layout options.
|
|
121
|
+
*/
|
|
122
|
+
export declare const isDrawClusterOutlineEnabled: (layoutOptions?: LayoutOptions) => boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Whether {@link ClusteringOptions.drawHalo} is enabled on the given layout options.
|
|
125
|
+
* @param layoutOptions - The layout options to check.
|
|
126
|
+
* @returns Whether {@link ClusteringOptions.drawHalo} is enabled on the given layout options.
|
|
127
|
+
*/
|
|
128
|
+
export declare const isDrawClusterHaloEnabled: (layoutOptions?: LayoutOptions) => boolean;
|
|
60
129
|
/**
|
|
61
130
|
* The options for the hierarchical layout
|
|
62
131
|
*/
|
|
@@ -65,6 +134,16 @@ export interface HierarchicalOptions {
|
|
|
65
134
|
direction?: 'up' | 'down' | 'left' | 'right';
|
|
66
135
|
/** The packing method to be used */
|
|
67
136
|
packing?: 'bin' | 'stack';
|
|
137
|
+
/**
|
|
138
|
+
* Optional function to group nodes into semantic clusters based on a custom identifier.
|
|
139
|
+
* Nodes returning the same string or number will be grouped together in the layout via
|
|
140
|
+
* dagre compound subgraphs. Nodes returning undefined will be treated as independent.
|
|
141
|
+
*
|
|
142
|
+
* @note Ensure this function has a stable reference to avoid evaluation on every render.
|
|
143
|
+
*
|
|
144
|
+
* @experimental
|
|
145
|
+
*/
|
|
146
|
+
clusterBy?: ClusterByFn;
|
|
68
147
|
}
|
|
69
148
|
export declare const isHierarchicalLayoutOptions: (options: LayoutOptions) => options is HierarchicalOptions;
|
|
70
149
|
/**
|
|
@@ -102,11 +181,12 @@ export declare const d3ForceLayoutType = "d3Force";
|
|
|
102
181
|
*/
|
|
103
182
|
export declare const CircularLayoutType = "circular";
|
|
104
183
|
/**
|
|
105
|
-
* A
|
|
184
|
+
* A WebGL2 renderer that uses the GPU for rendering.
|
|
106
185
|
*
|
|
107
186
|
* @remarks
|
|
108
|
-
*
|
|
109
|
-
*
|
|
187
|
+
* Upgraded to WebGL2 in NVL 2.0.
|
|
188
|
+
* This renderer has better performance than the canvas renderer.
|
|
189
|
+
* Supports rendering node icons and relationship arrowheads, but does not support captions.
|
|
110
190
|
*/
|
|
111
191
|
export declare const WebGLRendererType = "webgl";
|
|
112
192
|
/**
|
|
@@ -114,7 +194,7 @@ export declare const WebGLRendererType = "webgl";
|
|
|
114
194
|
*
|
|
115
195
|
* @remarks
|
|
116
196
|
* This renderer has lower performance than the WebGL renderer,
|
|
117
|
-
* but supports captions
|
|
197
|
+
* but supports captions.
|
|
118
198
|
*/
|
|
119
199
|
export declare const CanvasRendererType = "canvas";
|
|
120
200
|
/**
|
|
@@ -183,8 +263,9 @@ export interface NvlOptions {
|
|
|
183
263
|
/**
|
|
184
264
|
* What renderer to use
|
|
185
265
|
* @defaultValue 'canvas'
|
|
186
|
-
* WebGL renderer uses GPU and has better performance.
|
|
187
|
-
*
|
|
266
|
+
* WebGL renderer uses GPU (WebGL2) and has better performance.
|
|
267
|
+
* Supports node icons and relationship arrowheads.
|
|
268
|
+
* Captions are only displayed when using the canvas or svg renderer.
|
|
188
269
|
*/
|
|
189
270
|
renderer?: Renderer;
|
|
190
271
|
/**
|
|
@@ -226,15 +307,36 @@ export interface NvlOptions {
|
|
|
226
307
|
disabledItemColor?: string;
|
|
227
308
|
/** The color to use for the labels of the disabled nodes and relationships */
|
|
228
309
|
disabledItemFontColor?: string;
|
|
229
|
-
/**
|
|
310
|
+
/**
|
|
311
|
+
* Stroke color for overlay UI lines (e.g. minimap viewport box).
|
|
312
|
+
* Use a dark color on light backgrounds and a light color on dark backgrounds.
|
|
313
|
+
* @defaultValue `#1A1B1D`
|
|
314
|
+
*/
|
|
315
|
+
overlayLineColor?: string;
|
|
316
|
+
/**
|
|
317
|
+
* The color to use for the viewport box in the minimap.
|
|
318
|
+
* @deprecated Use {@link overlayLineColor}.
|
|
319
|
+
*/
|
|
230
320
|
minimapViewportBoxColor?: string;
|
|
231
321
|
};
|
|
232
322
|
/**
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
323
|
+
* Target minimum presentation fps while layout is active, at the cost of stabilization speed when set higher.
|
|
324
|
+
* Budgets layout steps only. Actual fps may undershoot when rendering is expensive.
|
|
325
|
+
*
|
|
326
|
+
* @defaultValue 3
|
|
327
|
+
*
|
|
328
|
+
* @note Will be capped between 1 and 120.
|
|
329
|
+
*/
|
|
330
|
+
minFps?: number;
|
|
331
|
+
/**
|
|
332
|
+
* Target maximum presentation fps while layout is active; extra time in the tick goes to layout stabilization.
|
|
333
|
+
* Budgets layout steps only. Actual fps may undershoot when rendering is expensive.
|
|
334
|
+
*
|
|
335
|
+
* @defaultValue 30
|
|
336
|
+
*
|
|
337
|
+
* @note Will be capped between 1 and 120.
|
|
236
338
|
*/
|
|
237
|
-
|
|
339
|
+
maxFps?: number;
|
|
238
340
|
/**
|
|
239
341
|
* Disables tracking of library usage
|
|
240
342
|
* @defaultValue false
|
|
@@ -320,6 +422,6 @@ export interface NvlState {
|
|
|
320
422
|
disabledItemStyles: DisabledItemStyles;
|
|
321
423
|
defaultNodeColor: string;
|
|
322
424
|
defaultRelationshipColor: string;
|
|
323
|
-
|
|
425
|
+
overlayLineColor: string;
|
|
324
426
|
}
|
|
325
427
|
export {};
|
|
@@ -6,7 +6,7 @@ import type { NvlOptions } from './types';
|
|
|
6
6
|
* If both caption and captions are provided, captions takes precedence.
|
|
7
7
|
*/
|
|
8
8
|
export declare const processCaptions: (item: Partial<Node | Relationship>) => StyledCaption[];
|
|
9
|
-
export declare const processColorStyles: (styling
|
|
9
|
+
export declare const processColorStyles: (styling?: NvlOptions["styling"]) => {
|
|
10
10
|
nodeBorderStyles: {
|
|
11
11
|
default: {
|
|
12
12
|
rings?: {
|
|
@@ -42,5 +42,5 @@ export declare const processColorStyles: (styling: NvlOptions["styling"]) => {
|
|
|
42
42
|
};
|
|
43
43
|
defaultNodeColor: string;
|
|
44
44
|
defaultRelationshipColor: string;
|
|
45
|
-
|
|
45
|
+
overlayLineColor: string;
|
|
46
46
|
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type CoarsenedGraph } from '../layouts/forcedirectedlayout/clustering/types';
|
|
2
|
+
import { type NvlState } from '../modules/state/types';
|
|
3
|
+
import type { Node } from '../types/graph-element';
|
|
4
|
+
import { type Point } from '../utils/geometry';
|
|
5
|
+
type ClusterPositions = Readonly<Record<string, Point>> | (() => (Node & Point)[]);
|
|
6
|
+
/**
|
|
7
|
+
* Canvas2D overlay for cluster decorations.
|
|
8
|
+
*/
|
|
9
|
+
export default class ClusterOverlay {
|
|
10
|
+
private readonly canvas;
|
|
11
|
+
private readonly context;
|
|
12
|
+
private readonly state;
|
|
13
|
+
private readonly stateDisposers;
|
|
14
|
+
private readonly clusterColors;
|
|
15
|
+
private clusterColorsDirty;
|
|
16
|
+
private cachedFirstSubGraphLevel;
|
|
17
|
+
private cachedIdToPosition;
|
|
18
|
+
private cachedPositionListVersion;
|
|
19
|
+
private readonly clusterGeometry;
|
|
20
|
+
private geometryPositionsVersion;
|
|
21
|
+
constructor(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, state: NvlState);
|
|
22
|
+
/** Clears the overlay. */
|
|
23
|
+
clear(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Draws cluster decorations for a given multi-member first-level sun system and positions.
|
|
26
|
+
* @param subGraphs - Layout coarsening hierarchy (`subGraphs[1]` = first-level suns).
|
|
27
|
+
* @param positions - Node id to position map, or a getter for a lagged position list.
|
|
28
|
+
* @param cacheVersion - PhysLayout positions-cache version.
|
|
29
|
+
*/
|
|
30
|
+
render(subGraphs: CoarsenedGraph[], positions: ClusterPositions, cacheVersion: number): void;
|
|
31
|
+
destroy(): void;
|
|
32
|
+
private resetClusterColorCache;
|
|
33
|
+
private resetGeometryCache;
|
|
34
|
+
private syncGeometryCacheVersion;
|
|
35
|
+
private resetPositionCache;
|
|
36
|
+
/**
|
|
37
|
+
* Returns an id to position map. Records are used as-is. Getters are converted once per `cacheVersion`.
|
|
38
|
+
* @param positions - Node id to position map, or a getter for a lagged position list.
|
|
39
|
+
* @param cacheVersion - PhysLayout positions-cache version; ignored for maps.
|
|
40
|
+
* @returns An id to position map.
|
|
41
|
+
*/
|
|
42
|
+
private resolveIdToPosition;
|
|
43
|
+
/**
|
|
44
|
+
* Rebuilds {@link clusterColors} for every multi-member first-level sun.
|
|
45
|
+
* @param firstSubGraphLevel - `subGraphs[1]` from the current layout coarsening hierarchy.
|
|
46
|
+
*/
|
|
47
|
+
private rebuildClusterColors;
|
|
48
|
+
/**
|
|
49
|
+
* Returns fitted world-space hull for a cluster, reusing the last fit while the
|
|
50
|
+
* positions cache version is unchanged (e.g. pan/zoom-only frames while layout is stable).
|
|
51
|
+
* @param sunId - Original id of the sun the hull belongs to.
|
|
52
|
+
* @param collectMembers - Builds the member disks to fit; only called on a cache miss.
|
|
53
|
+
* @returns The fitted hull, or null when the cluster cannot be fitted.
|
|
54
|
+
*/
|
|
55
|
+
private getCachedHull;
|
|
56
|
+
private fillHalo;
|
|
57
|
+
private strokeOutline;
|
|
58
|
+
/**
|
|
59
|
+
* Traces a closed rounded path along the given points.
|
|
60
|
+
* @param points - The points to trace the path along.
|
|
61
|
+
*/
|
|
62
|
+
private traceSmoothClosedPath;
|
|
63
|
+
private applyZoomAndPan;
|
|
64
|
+
/**
|
|
65
|
+
* Returns the shared member color when color is consistent; otherwise null.
|
|
66
|
+
* @param nodeIds - The IDs of the nodes in the cluster.
|
|
67
|
+
* @param idToItem - The map of node IDs to items.
|
|
68
|
+
* @param defaultNodeColor - The default node color.
|
|
69
|
+
* @returns The shared member color when color is consistent; otherwise null.
|
|
70
|
+
*/
|
|
71
|
+
private resolveUnanimousClusterColor;
|
|
72
|
+
}
|
|
73
|
+
export {};
|