@cosmos.gl/graph 2.4.0 → 2.5.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/dist/config.d.ts +69 -0
- package/dist/index.d.ts +16 -6
- package/dist/index.js +4328 -4129
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +113 -45
- package/dist/index.min.js.map +1 -1
- package/dist/modules/Lines/index.d.ts +8 -0
- package/dist/modules/Store/index.d.ts +14 -2
- package/dist/modules/core-module.d.ts +1 -0
- package/dist/stories/beginners/link-hovering/data-generator.d.ts +19 -0
- package/dist/stories/beginners/link-hovering/index.d.ts +5 -0
- package/dist/stories/beginners.stories.d.ts +1 -0
- package/dist/variables.d.ts +5 -2
- package/package.json +1 -1
- package/src/config.ts +86 -2
- package/src/index.ts +151 -31
- package/src/modules/Lines/draw-curve-line.frag +12 -1
- package/src/modules/Lines/draw-curve-line.vert +29 -2
- package/src/modules/Lines/hovered-line-index.frag +27 -0
- package/src/modules/Lines/hovered-line-index.vert +8 -0
- package/src/modules/Lines/index.ts +112 -2
- package/src/modules/Store/index.ts +33 -2
- package/src/modules/core-module.ts +1 -0
- package/src/stories/1. welcome.mdx +2 -1
- package/src/stories/2. configuration.mdx +10 -1
- package/src/stories/3. api-reference.mdx +13 -4
- package/src/stories/beginners/basic-set-up/index.ts +20 -10
- package/src/stories/beginners/link-hovering/data-generator.ts +198 -0
- package/src/stories/beginners/link-hovering/index.ts +61 -0
- package/src/stories/beginners/link-hovering/style.css +73 -0
- package/src/stories/beginners/quick-start.ts +2 -1
- package/src/stories/beginners/remove-points/index.ts +28 -30
- package/src/stories/beginners.stories.ts +17 -0
- package/src/stories/clusters/polygon-selection/index.ts +2 -4
- package/src/stories/shapes/image-example/index.ts +7 -8
- package/src/variables.ts +5 -2
|
@@ -1,17 +1,25 @@
|
|
|
1
|
+
import { default as regl } from 'regl';
|
|
1
2
|
import { CoreModule } from '../core-module';
|
|
2
3
|
export declare class Lines extends CoreModule {
|
|
4
|
+
linkIndexFbo: regl.Framebuffer2D | undefined;
|
|
5
|
+
hoveredLineIndexFbo: regl.Framebuffer2D | undefined;
|
|
3
6
|
private drawCurveCommand;
|
|
7
|
+
private hoveredLineIndexCommand;
|
|
4
8
|
private pointsBuffer;
|
|
5
9
|
private colorBuffer;
|
|
6
10
|
private widthBuffer;
|
|
7
11
|
private arrowBuffer;
|
|
8
12
|
private curveLineGeometry;
|
|
9
13
|
private curveLineBuffer;
|
|
14
|
+
private linkIndexBuffer;
|
|
15
|
+
private quadBuffer;
|
|
10
16
|
initPrograms(): void;
|
|
11
17
|
draw(): void;
|
|
18
|
+
updateLinkIndexFbo(): void;
|
|
12
19
|
updatePointsBuffer(): void;
|
|
13
20
|
updateColor(): void;
|
|
14
21
|
updateWidth(): void;
|
|
15
22
|
updateArrow(): void;
|
|
16
23
|
updateCurveLineGeometry(): void;
|
|
24
|
+
findHoveredLine(): void;
|
|
17
25
|
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { mat3 } from 'gl-matrix';
|
|
2
|
+
import { GraphConfigInterface } from '../../config';
|
|
2
3
|
export declare const ALPHA_MIN = 0.001;
|
|
3
4
|
export declare const MAX_POINT_SIZE = 64;
|
|
5
|
+
/**
|
|
6
|
+
* Maximum number of executions to delay before performing hover detection.
|
|
7
|
+
* This threshold prevents excessive hover detection calls for performance optimization.
|
|
8
|
+
* The `findHoveredItem` method will skip actual detection until this count is reached.
|
|
9
|
+
*/
|
|
10
|
+
export declare const MAX_HOVER_DETECTION_DELAY = 4;
|
|
4
11
|
export type Hovered = {
|
|
5
12
|
index: number;
|
|
6
13
|
position: [number, number];
|
|
@@ -24,14 +31,17 @@ export declare class Store {
|
|
|
24
31
|
hoveredPoint: Hovered | undefined;
|
|
25
32
|
focusedPoint: Focused | undefined;
|
|
26
33
|
draggingPointIndex: number | undefined;
|
|
34
|
+
hoveredLinkIndex: number | undefined;
|
|
27
35
|
adjustedSpaceSize: number;
|
|
28
36
|
isSpaceKeyPressed: boolean;
|
|
29
37
|
div: HTMLDivElement | undefined;
|
|
30
38
|
webglMaxTextureSize: number;
|
|
31
39
|
hoveredPointRingColor: number[];
|
|
32
40
|
focusedPointRingColor: number[];
|
|
41
|
+
hoveredLinkColor: number[];
|
|
33
42
|
greyoutPointColor: number[];
|
|
34
43
|
isDarkenGreyout: boolean;
|
|
44
|
+
isLinkHoveringEnabled: boolean;
|
|
35
45
|
private alphaTarget;
|
|
36
46
|
private scalePointX;
|
|
37
47
|
private scalePointY;
|
|
@@ -53,9 +63,11 @@ export declare class Store {
|
|
|
53
63
|
updateScreenSize(width: number, height: number): void;
|
|
54
64
|
scaleX(x: number): number;
|
|
55
65
|
scaleY(y: number): number;
|
|
56
|
-
setHoveredPointRingColor(color: string): void;
|
|
57
|
-
setFocusedPointRingColor(color: string): void;
|
|
66
|
+
setHoveredPointRingColor(color: string | [number, number, number, number]): void;
|
|
67
|
+
setFocusedPointRingColor(color: string | [number, number, number, number]): void;
|
|
58
68
|
setGreyoutPointColor(color: string | [number, number, number, number] | undefined): void;
|
|
69
|
+
updateLinkHoveringEnabled(config: Pick<GraphConfigInterface, 'onLinkClick' | 'onLinkMouseOver' | 'onLinkMouseOut'>): void;
|
|
70
|
+
setHoveredLinkColor(color?: string | [number, number, number, number]): void;
|
|
59
71
|
setFocusedPoint(index?: number): void;
|
|
60
72
|
addAlpha(decay: number): number;
|
|
61
73
|
private alphaDecay;
|
|
@@ -9,5 +9,6 @@ export declare class CoreModule {
|
|
|
9
9
|
readonly store: Store;
|
|
10
10
|
readonly data: GraphData;
|
|
11
11
|
readonly points: Points | undefined;
|
|
12
|
+
_debugRandomNumber: number;
|
|
12
13
|
constructor(reglInstance: regl.Regl, config: GraphConfigInterface, store: Store, data: GraphData, points?: Points);
|
|
13
14
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface Point {
|
|
2
|
+
id: number;
|
|
3
|
+
}
|
|
4
|
+
interface Link {
|
|
5
|
+
source: number;
|
|
6
|
+
target: number;
|
|
7
|
+
}
|
|
8
|
+
interface NetworkData {
|
|
9
|
+
pointPositions: Float32Array;
|
|
10
|
+
pointColors: Float32Array;
|
|
11
|
+
pointSizes: Float32Array;
|
|
12
|
+
links: Float32Array;
|
|
13
|
+
linkColors: Float32Array;
|
|
14
|
+
linkWidths: Float32Array;
|
|
15
|
+
points: Point[];
|
|
16
|
+
connections: Link[];
|
|
17
|
+
}
|
|
18
|
+
export declare function generateData(pointCount?: number): NetworkData;
|
|
19
|
+
export {};
|
package/dist/variables.d.ts
CHANGED
|
@@ -13,18 +13,21 @@ export declare const defaultConfigValues: {
|
|
|
13
13
|
spaceSize: number;
|
|
14
14
|
pointSizeScale: number;
|
|
15
15
|
linkWidthScale: number;
|
|
16
|
-
|
|
16
|
+
linkArrowsSizeScale: number;
|
|
17
17
|
renderLinks: boolean;
|
|
18
18
|
curvedLinks: boolean;
|
|
19
19
|
curvedLinkSegments: number;
|
|
20
20
|
curvedLinkWeight: number;
|
|
21
21
|
curvedLinkControlPointDistance: number;
|
|
22
|
-
|
|
22
|
+
linkArrows: boolean;
|
|
23
23
|
linkVisibilityDistanceRange: number[];
|
|
24
24
|
linkVisibilityMinTransparency: number;
|
|
25
25
|
hoveredPointCursor: string;
|
|
26
|
+
hoveredLinkCursor: string;
|
|
26
27
|
renderHoveredPointRing: boolean;
|
|
27
28
|
hoveredPointRingColor: string;
|
|
29
|
+
hoveredLinkColor: undefined;
|
|
30
|
+
hoveredLinkWidthIncrease: number;
|
|
28
31
|
focusedPointRingColor: string;
|
|
29
32
|
focusedPointIndex: undefined;
|
|
30
33
|
useClassicQuadtree: boolean;
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -98,6 +98,12 @@ export interface GraphConfigInterface {
|
|
|
98
98
|
*/
|
|
99
99
|
hoveredPointCursor?: string;
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Cursor style to use when hovering over a link
|
|
103
|
+
* Default value: `auto`
|
|
104
|
+
*/
|
|
105
|
+
hoveredLinkCursor?: string;
|
|
106
|
+
|
|
101
107
|
/**
|
|
102
108
|
* Turns ring rendering around a point on hover on / off
|
|
103
109
|
* Default value: `false`
|
|
@@ -158,6 +164,19 @@ export interface GraphConfigInterface {
|
|
|
158
164
|
* Default value: `1`
|
|
159
165
|
*/
|
|
160
166
|
linkWidth?: number;
|
|
167
|
+
/**
|
|
168
|
+
* The color to use for links when they are hovered.
|
|
169
|
+
* This can be either a hex color string (e.g., '#ff3333') or an array of RGBA values
|
|
170
|
+
* in the format `[red, green, blue, alpha]` where each value is a number between 0 and 255.
|
|
171
|
+
* Default value: `undefined`
|
|
172
|
+
*/
|
|
173
|
+
hoveredLinkColor?: string | [number, number, number, number];
|
|
174
|
+
/**
|
|
175
|
+
* Number of pixels to add to the link width when hovered.
|
|
176
|
+
* The hovered width is calculated as: originalWidth + hoveredLinkWidthIncrease
|
|
177
|
+
* Default value: `5`
|
|
178
|
+
*/
|
|
179
|
+
hoveredLinkWidthIncrease?: number;
|
|
161
180
|
/**
|
|
162
181
|
* Scale factor for the link width.
|
|
163
182
|
* Default value: `1`
|
|
@@ -324,9 +343,15 @@ export interface GraphConfigInterface {
|
|
|
324
343
|
onSimulationPause?: () => void;
|
|
325
344
|
/**
|
|
326
345
|
* Callback function that will be called when the simulation is restarted.
|
|
346
|
+
* @deprecated Use `onSimulationUnpause` instead. This callback will be removed in a future version.
|
|
327
347
|
* Default value: `undefined`
|
|
328
348
|
*/
|
|
329
349
|
onSimulationRestart?: () => void;
|
|
350
|
+
/**
|
|
351
|
+
* Callback function that will be called when the simulation is unpaused.
|
|
352
|
+
* Default value: `undefined`
|
|
353
|
+
*/
|
|
354
|
+
onSimulationUnpause?: () => void;
|
|
330
355
|
|
|
331
356
|
/**
|
|
332
357
|
* Callback function that will be called on every canvas click.
|
|
@@ -339,6 +364,40 @@ export interface GraphConfigInterface {
|
|
|
339
364
|
index: number | undefined, pointPosition: [number, number] | undefined, event: MouseEvent
|
|
340
365
|
) => void;
|
|
341
366
|
|
|
367
|
+
/**
|
|
368
|
+
* Callback function that will be called when a point is clicked.
|
|
369
|
+
* The point index will be passed as the first argument,
|
|
370
|
+
* position as the second argument and the corresponding mouse event as the third argument:
|
|
371
|
+
* `(index: number, pointPosition: [number, number], event: MouseEvent) => void`.
|
|
372
|
+
* Default value: `undefined`
|
|
373
|
+
*/
|
|
374
|
+
onPointClick?: (
|
|
375
|
+
index: number,
|
|
376
|
+
pointPosition: [number, number],
|
|
377
|
+
event: MouseEvent
|
|
378
|
+
) => void;
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Callback function that will be called when a link is clicked.
|
|
382
|
+
* The link index will be passed as the first argument and the corresponding mouse event as the second argument:
|
|
383
|
+
* `(linkIndex: number, event: MouseEvent) => void`.
|
|
384
|
+
* Default value: `undefined`
|
|
385
|
+
*/
|
|
386
|
+
onLinkClick?: (
|
|
387
|
+
linkIndex: number,
|
|
388
|
+
event: MouseEvent
|
|
389
|
+
) => void;
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Callback function that will be called when the background (empty space) is clicked.
|
|
393
|
+
* The mouse event will be passed as the first argument:
|
|
394
|
+
* `(event: MouseEvent) => void`.
|
|
395
|
+
* Default value: `undefined`
|
|
396
|
+
*/
|
|
397
|
+
onBackgroundClick?: (
|
|
398
|
+
event: MouseEvent
|
|
399
|
+
) => void;
|
|
400
|
+
|
|
342
401
|
/**
|
|
343
402
|
* Callback function that will be called when mouse movement happens.
|
|
344
403
|
* If the mouse moves over a point, its index will be passed as the first argument,
|
|
@@ -374,6 +433,22 @@ export interface GraphConfigInterface {
|
|
|
374
433
|
*/
|
|
375
434
|
onPointMouseOut?: (event: MouseEvent | D3ZoomEvent<HTMLCanvasElement, undefined> | D3DragEvent<HTMLCanvasElement, undefined, Hovered> | undefined) => void;
|
|
376
435
|
|
|
436
|
+
/**
|
|
437
|
+
* Callback function that will be called when the mouse moves over a link.
|
|
438
|
+
* The link index will be passed as the first argument:
|
|
439
|
+
* `(linkIndex: number) => void`.
|
|
440
|
+
* Default value: `undefined`
|
|
441
|
+
*/
|
|
442
|
+
onLinkMouseOver?: (linkIndex: number) => void;
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Callback function that will be called when the mouse moves out of a link.
|
|
446
|
+
* The event will be passed as the first argument:
|
|
447
|
+
* `(event: MouseEvent | D3ZoomEvent<HTMLCanvasElement, undefined> | D3DragEvent<HTMLCanvasElement, undefined, Hovered> | undefined) => void`.
|
|
448
|
+
* Default value: `undefined`
|
|
449
|
+
*/
|
|
450
|
+
onLinkMouseOut?: (event: MouseEvent | D3ZoomEvent<HTMLCanvasElement, undefined> | D3DragEvent<HTMLCanvasElement, undefined, Hovered> | undefined) => void;
|
|
451
|
+
|
|
377
452
|
/**
|
|
378
453
|
* Callback function that will be called when zooming or panning starts.
|
|
379
454
|
* First argument is a D3 Zoom Event and second indicates whether
|
|
@@ -546,6 +621,7 @@ export class GraphConfig implements GraphConfigInterface {
|
|
|
546
621
|
public pointOpacity = defaultPointOpacity
|
|
547
622
|
public pointSizeScale = defaultConfigValues.pointSizeScale
|
|
548
623
|
public hoveredPointCursor = defaultConfigValues.hoveredPointCursor
|
|
624
|
+
public hoveredLinkCursor = defaultConfigValues.hoveredLinkCursor
|
|
549
625
|
public renderHoveredPointRing = defaultConfigValues.renderHoveredPointRing
|
|
550
626
|
public hoveredPointRingColor = defaultConfigValues.hoveredPointRingColor
|
|
551
627
|
public focusedPointRingColor = defaultConfigValues.focusedPointRingColor
|
|
@@ -555,13 +631,15 @@ export class GraphConfig implements GraphConfigInterface {
|
|
|
555
631
|
public linkGreyoutOpacity = defaultGreyoutLinkOpacity
|
|
556
632
|
public linkWidth = defaultLinkWidth
|
|
557
633
|
public linkWidthScale = defaultConfigValues.linkWidthScale
|
|
634
|
+
public hoveredLinkColor = defaultConfigValues.hoveredLinkColor
|
|
635
|
+
public hoveredLinkWidthIncrease = defaultConfigValues.hoveredLinkWidthIncrease
|
|
558
636
|
public renderLinks = defaultConfigValues.renderLinks
|
|
559
637
|
public curvedLinks = defaultConfigValues.curvedLinks
|
|
560
638
|
public curvedLinkSegments = defaultConfigValues.curvedLinkSegments
|
|
561
639
|
public curvedLinkWeight = defaultConfigValues.curvedLinkWeight
|
|
562
640
|
public curvedLinkControlPointDistance = defaultConfigValues.curvedLinkControlPointDistance
|
|
563
|
-
public linkArrows = defaultConfigValues.
|
|
564
|
-
public linkArrowsSizeScale = defaultConfigValues.
|
|
641
|
+
public linkArrows = defaultConfigValues.linkArrows
|
|
642
|
+
public linkArrowsSizeScale = defaultConfigValues.linkArrowsSizeScale
|
|
565
643
|
public scaleLinksOnZoom = defaultConfigValues.scaleLinksOnZoom
|
|
566
644
|
public linkVisibilityDistanceRange = defaultConfigValues.linkVisibilityDistanceRange
|
|
567
645
|
public linkVisibilityMinTransparency = defaultConfigValues.linkVisibilityMinTransparency
|
|
@@ -586,11 +664,17 @@ export class GraphConfig implements GraphConfigInterface {
|
|
|
586
664
|
public onSimulationEnd: GraphConfigInterface['onSimulationEnd'] = undefined
|
|
587
665
|
public onSimulationPause: GraphConfigInterface['onSimulationPause'] = undefined
|
|
588
666
|
public onSimulationRestart: GraphConfigInterface['onSimulationRestart'] = undefined
|
|
667
|
+
public onSimulationUnpause: GraphConfigInterface['onSimulationUnpause'] = undefined
|
|
589
668
|
|
|
590
669
|
public onClick: GraphConfigInterface['onClick'] = undefined
|
|
670
|
+
public onPointClick: GraphConfigInterface['onPointClick'] = undefined
|
|
671
|
+
public onLinkClick: GraphConfigInterface['onLinkClick'] = undefined
|
|
672
|
+
public onBackgroundClick: GraphConfigInterface['onBackgroundClick'] = undefined
|
|
591
673
|
public onMouseMove: GraphConfigInterface['onMouseMove'] = undefined
|
|
592
674
|
public onPointMouseOver: GraphConfigInterface['onPointMouseOver'] = undefined
|
|
593
675
|
public onPointMouseOut: GraphConfigInterface['onPointMouseOut'] = undefined
|
|
676
|
+
public onLinkMouseOver: GraphConfigInterface['onLinkMouseOver'] = undefined
|
|
677
|
+
public onLinkMouseOut: GraphConfigInterface['onLinkMouseOut'] = undefined
|
|
594
678
|
public onZoomStart: GraphConfigInterface['onZoomStart'] = undefined
|
|
595
679
|
public onZoom: GraphConfigInterface['onZoom'] = undefined
|
|
596
680
|
public onZoomEnd: GraphConfigInterface['onZoomEnd'] = undefined
|
package/src/index.ts
CHANGED
|
@@ -17,10 +17,10 @@ import { FPSMonitor } from '@/graph/modules/FPSMonitor'
|
|
|
17
17
|
import { GraphData } from '@/graph/modules/GraphData'
|
|
18
18
|
import { Lines } from '@/graph/modules/Lines'
|
|
19
19
|
import { Points } from '@/graph/modules/Points'
|
|
20
|
-
import { Store, ALPHA_MIN, MAX_POINT_SIZE, type Hovered } from '@/graph/modules/Store'
|
|
20
|
+
import { Store, ALPHA_MIN, MAX_POINT_SIZE, MAX_HOVER_DETECTION_DELAY, type Hovered } from '@/graph/modules/Store'
|
|
21
21
|
import { Zoom } from '@/graph/modules/Zoom'
|
|
22
22
|
import { Drag } from '@/graph/modules/Drag'
|
|
23
|
-
import { defaultConfigValues, defaultScaleToZoom } from '@/graph/variables'
|
|
23
|
+
import { defaultConfigValues, defaultScaleToZoom, defaultGreyoutPointColor, defaultBackgroundColor } from '@/graph/variables'
|
|
24
24
|
import { createWebGLErrorMessage } from './graph/utils/error-message'
|
|
25
25
|
|
|
26
26
|
export class Graph {
|
|
@@ -50,12 +50,12 @@ export class Graph {
|
|
|
50
50
|
|
|
51
51
|
private currentEvent: D3ZoomEvent<HTMLCanvasElement, undefined> | D3DragEvent<HTMLCanvasElement, undefined, Hovered> | MouseEvent | undefined
|
|
52
52
|
/**
|
|
53
|
-
* The value of `
|
|
54
|
-
* When the counter reaches
|
|
53
|
+
* The value of `_findHoveredItemExecutionCount` is incremented by 1 on each animation frame.
|
|
54
|
+
* When the counter reaches MAX_HOVER_DETECTION_DELAY (default 4), it is reset to 0 and the `findHoveredPoint` or `findHoveredLine` method is executed.
|
|
55
55
|
*/
|
|
56
|
-
private
|
|
56
|
+
private _findHoveredItemExecutionCount = 0
|
|
57
57
|
/**
|
|
58
|
-
* If the mouse is not on the Canvas, the `findHoveredPoint` method will not be executed.
|
|
58
|
+
* If the mouse is not on the Canvas, the `findHoveredPoint` or `findHoveredLine` method will not be executed.
|
|
59
59
|
*/
|
|
60
60
|
private _isMouseOnCanvas = false
|
|
61
61
|
/**
|
|
@@ -123,7 +123,30 @@ export class Graph {
|
|
|
123
123
|
this.canvasD3Selection
|
|
124
124
|
.on('mouseenter.cosmos', () => { this._isMouseOnCanvas = true })
|
|
125
125
|
.on('mousemove.cosmos', () => { this._isMouseOnCanvas = true })
|
|
126
|
-
.on('mouseleave.cosmos', () => {
|
|
126
|
+
.on('mouseleave.cosmos', (event) => {
|
|
127
|
+
this._isMouseOnCanvas = false
|
|
128
|
+
this.currentEvent = event
|
|
129
|
+
|
|
130
|
+
// Clear point hover state and trigger callback if needed
|
|
131
|
+
if (this.store.hoveredPoint !== undefined && this.config.onPointMouseOut) {
|
|
132
|
+
this.config.onPointMouseOut(event)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Clear link hover state and trigger callback if needed
|
|
136
|
+
if (this.store.hoveredLinkIndex !== undefined && this.config.onLinkMouseOut) {
|
|
137
|
+
this.config.onLinkMouseOut(event)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Reset right-click flag
|
|
141
|
+
this.isRightClickMouse = false
|
|
142
|
+
|
|
143
|
+
// Clear hover states
|
|
144
|
+
this.store.hoveredPoint = undefined
|
|
145
|
+
this.store.hoveredLinkIndex = undefined
|
|
146
|
+
|
|
147
|
+
// Update cursor style after clearing hover states
|
|
148
|
+
this.updateCanvasCursor()
|
|
149
|
+
})
|
|
127
150
|
select(document)
|
|
128
151
|
.on('keydown.cosmos', (event) => { if (event.code === 'Space') this.store.isSpaceKeyPressed = true })
|
|
129
152
|
.on('keyup.cosmos', (event) => { if (event.code === 'Space') this.store.isSpaceKeyPressed = false })
|
|
@@ -176,18 +199,15 @@ export class Graph {
|
|
|
176
199
|
this.clusters = new Clusters(this.reglInstance, this.config, this.store, this.graph, this.points)
|
|
177
200
|
|
|
178
201
|
this.store.backgroundColor = getRgbaColor(this.config.backgroundColor)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
if (this.config.focusedPointRingColor) {
|
|
183
|
-
this.store.setFocusedPointRingColor(this.config.focusedPointRingColor)
|
|
184
|
-
}
|
|
202
|
+
this.store.setHoveredPointRingColor(this.config.hoveredPointRingColor ?? defaultConfigValues.hoveredPointRingColor)
|
|
203
|
+
this.store.setFocusedPointRingColor(this.config.focusedPointRingColor ?? defaultConfigValues.focusedPointRingColor)
|
|
185
204
|
if (this.config.focusedPointIndex !== undefined) {
|
|
186
205
|
this.store.setFocusedPoint(this.config.focusedPointIndex)
|
|
187
206
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
207
|
+
this.store.setGreyoutPointColor(this.config.pointGreyoutColor ?? defaultGreyoutPointColor)
|
|
208
|
+
this.store.setHoveredLinkColor(this.config.hoveredLinkColor ?? defaultConfigValues.hoveredLinkColor)
|
|
209
|
+
|
|
210
|
+
this.store.updateLinkHoveringEnabled(this.config)
|
|
191
211
|
|
|
192
212
|
if (this.config.showFPSMonitor) this.fpsMonitor = new FPSMonitor(this.canvas)
|
|
193
213
|
|
|
@@ -251,15 +271,20 @@ export class Graph {
|
|
|
251
271
|
prevConfig.curvedLinks !== this.config.curvedLinks) {
|
|
252
272
|
this.lines.updateCurveLineGeometry()
|
|
253
273
|
}
|
|
254
|
-
if (prevConfig.backgroundColor !== this.config.backgroundColor)
|
|
274
|
+
if (prevConfig.backgroundColor !== this.config.backgroundColor) {
|
|
275
|
+
this.store.backgroundColor = getRgbaColor(this.config.backgroundColor ?? defaultBackgroundColor)
|
|
276
|
+
}
|
|
255
277
|
if (prevConfig.hoveredPointRingColor !== this.config.hoveredPointRingColor) {
|
|
256
|
-
this.store.setHoveredPointRingColor(this.config.hoveredPointRingColor)
|
|
278
|
+
this.store.setHoveredPointRingColor(this.config.hoveredPointRingColor ?? defaultConfigValues.hoveredPointRingColor)
|
|
257
279
|
}
|
|
258
280
|
if (prevConfig.focusedPointRingColor !== this.config.focusedPointRingColor) {
|
|
259
|
-
this.store.setFocusedPointRingColor(this.config.focusedPointRingColor)
|
|
281
|
+
this.store.setFocusedPointRingColor(this.config.focusedPointRingColor ?? defaultConfigValues.focusedPointRingColor)
|
|
260
282
|
}
|
|
261
283
|
if (prevConfig.pointGreyoutColor !== this.config.pointGreyoutColor) {
|
|
262
|
-
this.store.setGreyoutPointColor(this.config.pointGreyoutColor)
|
|
284
|
+
this.store.setGreyoutPointColor(this.config.pointGreyoutColor ?? defaultGreyoutPointColor)
|
|
285
|
+
}
|
|
286
|
+
if (prevConfig.hoveredLinkColor !== this.config.hoveredLinkColor) {
|
|
287
|
+
this.store.setHoveredLinkColor(this.config.hoveredLinkColor ?? defaultConfigValues.hoveredLinkColor)
|
|
263
288
|
}
|
|
264
289
|
if (prevConfig.focusedPointIndex !== this.config.focusedPointIndex) {
|
|
265
290
|
this.store.setFocusedPoint(this.config.focusedPointIndex)
|
|
@@ -285,6 +310,12 @@ export class Graph {
|
|
|
285
310
|
if (prevConfig.enableZoom !== this.config.enableZoom || prevConfig.enableDrag !== this.config.enableDrag) {
|
|
286
311
|
this.updateZoomDragBehaviors()
|
|
287
312
|
}
|
|
313
|
+
|
|
314
|
+
if (prevConfig.onLinkClick !== this.config.onLinkClick ||
|
|
315
|
+
prevConfig.onLinkMouseOver !== this.config.onLinkMouseOver ||
|
|
316
|
+
prevConfig.onLinkMouseOut !== this.config.onLinkMouseOut) {
|
|
317
|
+
this.store.updateLinkHoveringEnabled(this.config)
|
|
318
|
+
}
|
|
288
319
|
}
|
|
289
320
|
|
|
290
321
|
/**
|
|
@@ -1065,7 +1096,8 @@ export class Graph {
|
|
|
1065
1096
|
}
|
|
1066
1097
|
|
|
1067
1098
|
/**
|
|
1068
|
-
* Pause the simulation.
|
|
1099
|
+
* Pause the simulation. When paused, the simulation stops running
|
|
1100
|
+
* and can be resumed using the unpause method.
|
|
1069
1101
|
*/
|
|
1070
1102
|
public pause (): void {
|
|
1071
1103
|
if (this._isDestroyed) return
|
|
@@ -1074,7 +1106,19 @@ export class Graph {
|
|
|
1074
1106
|
}
|
|
1075
1107
|
|
|
1076
1108
|
/**
|
|
1077
|
-
*
|
|
1109
|
+
* Unpause the simulation. This method resumes a paused
|
|
1110
|
+
* simulation and continues its execution.
|
|
1111
|
+
*/
|
|
1112
|
+
public unpause (): void {
|
|
1113
|
+
if (this._isDestroyed) return
|
|
1114
|
+
this.store.isSimulationRunning = true
|
|
1115
|
+
this.config.onSimulationUnpause?.()
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Restart/Resume the simulation. This method unpauses a paused
|
|
1120
|
+
* simulation and resumes its execution.
|
|
1121
|
+
* @deprecated Use `unpause()` instead. This method will be removed in a future version.
|
|
1078
1122
|
*/
|
|
1079
1123
|
public restart (): void {
|
|
1080
1124
|
if (this._isDestroyed) return
|
|
@@ -1245,6 +1289,7 @@ export class Graph {
|
|
|
1245
1289
|
}
|
|
1246
1290
|
|
|
1247
1291
|
private frame (): void {
|
|
1292
|
+
if (this._isDestroyed) return
|
|
1248
1293
|
const { config: { simulationGravity, simulationCenter, renderLinks, enableSimulation }, store: { alpha, isSimulationRunning } } = this
|
|
1249
1294
|
if (alpha < ALPHA_MIN && isSimulationRunning) this.end()
|
|
1250
1295
|
if (!this.store.pointsTextureSize) return
|
|
@@ -1252,7 +1297,9 @@ export class Graph {
|
|
|
1252
1297
|
this.requestAnimationFrameId = window.requestAnimationFrame((now) => {
|
|
1253
1298
|
this.fpsMonitor?.begin()
|
|
1254
1299
|
this.resizeCanvas()
|
|
1255
|
-
if (!this.dragInstance.isActive)
|
|
1300
|
+
if (!this.dragInstance.isActive) {
|
|
1301
|
+
this.findHoveredItem()
|
|
1302
|
+
}
|
|
1256
1303
|
|
|
1257
1304
|
if (enableSimulation) {
|
|
1258
1305
|
if (this.isRightClickMouse && this.config.enableRightClickRepulsion) {
|
|
@@ -1318,7 +1365,9 @@ export class Graph {
|
|
|
1318
1365
|
this.fpsMonitor?.end(now)
|
|
1319
1366
|
|
|
1320
1367
|
this.currentEvent = undefined
|
|
1321
|
-
this.
|
|
1368
|
+
if (!this._isDestroyed) {
|
|
1369
|
+
this.frame()
|
|
1370
|
+
}
|
|
1322
1371
|
})
|
|
1323
1372
|
}
|
|
1324
1373
|
|
|
@@ -1338,6 +1387,23 @@ export class Graph {
|
|
|
1338
1387
|
this.store.hoveredPoint?.position,
|
|
1339
1388
|
event
|
|
1340
1389
|
)
|
|
1390
|
+
|
|
1391
|
+
if (this.store.hoveredPoint) {
|
|
1392
|
+
this.config.onPointClick?.(
|
|
1393
|
+
this.store.hoveredPoint.index,
|
|
1394
|
+
this.store.hoveredPoint.position,
|
|
1395
|
+
event
|
|
1396
|
+
)
|
|
1397
|
+
} else if (this.store.hoveredLinkIndex !== undefined) {
|
|
1398
|
+
this.config.onLinkClick?.(
|
|
1399
|
+
this.store.hoveredLinkIndex,
|
|
1400
|
+
event
|
|
1401
|
+
)
|
|
1402
|
+
} else {
|
|
1403
|
+
this.config.onBackgroundClick?.(
|
|
1404
|
+
event
|
|
1405
|
+
)
|
|
1406
|
+
}
|
|
1341
1407
|
}
|
|
1342
1408
|
|
|
1343
1409
|
private updateMousePosition (event: MouseEvent | D3DragEvent<HTMLCanvasElement, undefined, Hovered>): void {
|
|
@@ -1365,6 +1431,7 @@ export class Graph {
|
|
|
1365
1431
|
}
|
|
1366
1432
|
|
|
1367
1433
|
private resizeCanvas (forceResize = false): void {
|
|
1434
|
+
if (this._isDestroyed) return
|
|
1368
1435
|
const prevWidth = this.canvas.width
|
|
1369
1436
|
const prevHeight = this.canvas.height
|
|
1370
1437
|
const w = this.canvas.clientWidth
|
|
@@ -1382,6 +1449,10 @@ export class Graph {
|
|
|
1382
1449
|
this.canvasD3Selection
|
|
1383
1450
|
?.call(this.zoomInstance.behavior.transform, this.zoomInstance.getTransform([centerPosition], k))
|
|
1384
1451
|
this.points?.updateSampledPointsGrid()
|
|
1452
|
+
// Only update link index FBO if link hovering is enabled
|
|
1453
|
+
if (this.store.isLinkHoveringEnabled) {
|
|
1454
|
+
this.lines?.updateLinkIndexFbo()
|
|
1455
|
+
}
|
|
1385
1456
|
}
|
|
1386
1457
|
}
|
|
1387
1458
|
|
|
@@ -1413,13 +1484,31 @@ export class Graph {
|
|
|
1413
1484
|
}
|
|
1414
1485
|
}
|
|
1415
1486
|
|
|
1416
|
-
private
|
|
1417
|
-
if (
|
|
1418
|
-
if (this.
|
|
1419
|
-
this.
|
|
1487
|
+
private findHoveredItem (): void {
|
|
1488
|
+
if (this._isDestroyed || !this._isMouseOnCanvas || !this.reglInstance) return
|
|
1489
|
+
if (this._findHoveredItemExecutionCount < MAX_HOVER_DETECTION_DELAY) {
|
|
1490
|
+
this._findHoveredItemExecutionCount += 1
|
|
1420
1491
|
return
|
|
1421
1492
|
}
|
|
1422
|
-
this.
|
|
1493
|
+
this._findHoveredItemExecutionCount = 0
|
|
1494
|
+
this.findHoveredPoint()
|
|
1495
|
+
|
|
1496
|
+
if (this.graph.linksNumber && this.store.isLinkHoveringEnabled) {
|
|
1497
|
+
this.findHoveredLine()
|
|
1498
|
+
} else if (this.store.hoveredLinkIndex !== undefined) {
|
|
1499
|
+
// Clear stale hoveredLinkIndex when there are no links
|
|
1500
|
+
const wasHovered = this.store.hoveredLinkIndex !== undefined
|
|
1501
|
+
this.store.hoveredLinkIndex = undefined
|
|
1502
|
+
if (wasHovered && this.config.onLinkMouseOut) {
|
|
1503
|
+
this.config.onLinkMouseOut(this.currentEvent)
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
this.updateCanvasCursor()
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
private findHoveredPoint (): void {
|
|
1511
|
+
if (this._isDestroyed || !this.reglInstance || !this.points) return
|
|
1423
1512
|
this.points.findHoveredPoint()
|
|
1424
1513
|
let isMouseover = false
|
|
1425
1514
|
let isMouseout = false
|
|
@@ -1447,15 +1536,46 @@ export class Graph {
|
|
|
1447
1536
|
)
|
|
1448
1537
|
}
|
|
1449
1538
|
if (isMouseout) this.config.onPointMouseOut?.(this.currentEvent)
|
|
1450
|
-
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
private findHoveredLine (): void {
|
|
1542
|
+
if (this._isDestroyed || !this.reglInstance || !this.lines) return
|
|
1543
|
+
if (this.store.hoveredPoint) {
|
|
1544
|
+
if (this.store.hoveredLinkIndex !== undefined) {
|
|
1545
|
+
this.store.hoveredLinkIndex = undefined
|
|
1546
|
+
this.config.onLinkMouseOut?.(this.currentEvent)
|
|
1547
|
+
}
|
|
1548
|
+
return
|
|
1549
|
+
}
|
|
1550
|
+
this.lines.findHoveredLine()
|
|
1551
|
+
let isMouseover = false
|
|
1552
|
+
let isMouseout = false
|
|
1553
|
+
|
|
1554
|
+
const pixels = readPixels(this.reglInstance, this.lines.hoveredLineIndexFbo as regl.Framebuffer2D)
|
|
1555
|
+
const hoveredLineIndex = pixels[0] as number
|
|
1556
|
+
|
|
1557
|
+
if (hoveredLineIndex >= 0) {
|
|
1558
|
+
if (this.store.hoveredLinkIndex !== hoveredLineIndex) isMouseover = true
|
|
1559
|
+
this.store.hoveredLinkIndex = hoveredLineIndex
|
|
1560
|
+
} else {
|
|
1561
|
+
if (this.store.hoveredLinkIndex !== undefined) isMouseout = true
|
|
1562
|
+
this.store.hoveredLinkIndex = undefined
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
if (isMouseover && this.store.hoveredLinkIndex !== undefined) {
|
|
1566
|
+
this.config.onLinkMouseOver?.(this.store.hoveredLinkIndex)
|
|
1567
|
+
}
|
|
1568
|
+
if (isMouseout) this.config.onLinkMouseOut?.(this.currentEvent)
|
|
1451
1569
|
}
|
|
1452
1570
|
|
|
1453
1571
|
private updateCanvasCursor (): void {
|
|
1454
|
-
const { hoveredPointCursor } = this.config
|
|
1572
|
+
const { hoveredPointCursor, hoveredLinkCursor } = this.config
|
|
1455
1573
|
if (this.dragInstance.isActive) select(this.canvas).style('cursor', 'grabbing')
|
|
1456
1574
|
else if (this.store.hoveredPoint) {
|
|
1457
1575
|
if (!this.config.enableDrag || this.store.isSpaceKeyPressed) select(this.canvas).style('cursor', hoveredPointCursor)
|
|
1458
1576
|
else select(this.canvas).style('cursor', 'grab')
|
|
1577
|
+
} else if (this.store.isLinkHoveringEnabled && this.store.hoveredLinkIndex !== undefined) {
|
|
1578
|
+
select(this.canvas).style('cursor', hoveredLinkCursor)
|
|
1459
1579
|
} else select(this.canvas).style('cursor', null)
|
|
1460
1580
|
}
|
|
1461
1581
|
|
|
@@ -6,6 +6,10 @@ varying float arrowLength;
|
|
|
6
6
|
varying float useArrow;
|
|
7
7
|
varying float smoothing;
|
|
8
8
|
varying float arrowWidthFactor;
|
|
9
|
+
varying float linkIndex;
|
|
10
|
+
|
|
11
|
+
// renderMode: 0.0 = normal rendering, 1.0 = index buffer rendering for picking
|
|
12
|
+
uniform float renderMode;
|
|
9
13
|
|
|
10
14
|
float map(float value, float min1, float max1, float min2, float max2) {
|
|
11
15
|
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
|
|
@@ -31,5 +35,12 @@ void main() {
|
|
|
31
35
|
opacity = linkOpacity;
|
|
32
36
|
} else opacity = rgbaColor.a * smoothstep(0.5, 0.5 - smoothing, abs(pos.y));
|
|
33
37
|
|
|
34
|
-
|
|
38
|
+
if (renderMode > 0.0) {
|
|
39
|
+
if (opacity > 0.0) {
|
|
40
|
+
gl_FragColor = vec4(linkIndex, 0.0, 0.0, 1.0);
|
|
41
|
+
} else {
|
|
42
|
+
gl_FragColor = vec4(-1.0, 0.0, 0.0, 0.0);
|
|
43
|
+
}
|
|
44
|
+
} else gl_FragColor = vec4(color, opacity);
|
|
45
|
+
|
|
35
46
|
}
|