@cosmos.gl/graph 2.3.1 → 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/.eslintrc +61 -0
- package/CHARTER.md +69 -0
- package/GOVERNANCE.md +21 -0
- package/dist/config.d.ts +69 -0
- package/dist/index.d.ts +62 -21
- package/dist/index.js +5672 -5188
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +272 -86
- package/dist/index.min.js.map +1 -1
- package/dist/modules/GraphData/index.d.ts +18 -2
- package/dist/modules/Lines/index.d.ts +8 -0
- package/dist/modules/Points/atlas-utils.d.ts +24 -0
- package/dist/modules/Points/index.d.ts +21 -2
- package/dist/modules/Store/index.d.ts +20 -3
- 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/stories/create-story.d.ts +5 -1
- package/dist/stories/shapes/image-example/index.d.ts +5 -0
- package/dist/stories/shapes.stories.d.ts +1 -0
- package/dist/variables.d.ts +5 -2
- package/package.json +4 -4
- package/src/config.ts +87 -2
- package/src/declaration.d.ts +5 -0
- package/src/index.ts +270 -98
- package/src/modules/GraphData/index.ts +68 -6
- 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/Points/atlas-utils.ts +137 -0
- package/src/modules/Points/draw-highlighted.vert +3 -3
- package/src/modules/Points/draw-points.frag +106 -14
- package/src/modules/Points/draw-points.vert +51 -25
- package/src/modules/Points/find-points-on-area-selection.frag +6 -5
- package/src/modules/Points/index.ts +121 -13
- package/src/modules/Store/index.ts +44 -5
- 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 +61 -5
- 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/create-story.ts +32 -5
- package/src/stories/shapes/image-example/icons/box.png +0 -0
- package/src/stories/shapes/image-example/icons/lego.png +0 -0
- package/src/stories/shapes/image-example/icons/s.png +0 -0
- package/src/stories/shapes/image-example/icons/swift.png +0 -0
- package/src/stories/shapes/image-example/icons/toolbox.png +0 -0
- package/src/stories/shapes/image-example/index.ts +238 -0
- package/src/stories/shapes.stories.ts +12 -0
- package/src/variables.ts +5 -2
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
|
/**
|
|
@@ -64,18 +64,20 @@ export class Graph {
|
|
|
64
64
|
private _isFirstRenderAfterInit = true
|
|
65
65
|
private _fitViewOnInitTimeoutID: number | undefined
|
|
66
66
|
|
|
67
|
-
private
|
|
68
|
-
private
|
|
69
|
-
private
|
|
70
|
-
private
|
|
71
|
-
private
|
|
72
|
-
private
|
|
73
|
-
private
|
|
74
|
-
private
|
|
75
|
-
private
|
|
76
|
-
private
|
|
77
|
-
private
|
|
78
|
-
private
|
|
67
|
+
private isPointPositionsUpdateNeeded = false
|
|
68
|
+
private isPointColorUpdateNeeded = false
|
|
69
|
+
private isPointSizeUpdateNeeded = false
|
|
70
|
+
private isPointShapeUpdateNeeded = false
|
|
71
|
+
private isPointImageIndicesUpdateNeeded = false
|
|
72
|
+
private isLinksUpdateNeeded = false
|
|
73
|
+
private isLinkColorUpdateNeeded = false
|
|
74
|
+
private isLinkWidthUpdateNeeded = false
|
|
75
|
+
private isLinkArrowUpdateNeeded = false
|
|
76
|
+
private isPointClusterUpdateNeeded = false
|
|
77
|
+
private isForceManyBodyUpdateNeeded = false
|
|
78
|
+
private isForceLinkUpdateNeeded = false
|
|
79
|
+
private isForceCenterUpdateNeeded = false
|
|
80
|
+
private isPointImageSizesUpdateNeeded = false
|
|
79
81
|
|
|
80
82
|
private _isDestroyed = false
|
|
81
83
|
|
|
@@ -114,13 +116,37 @@ export class Graph {
|
|
|
114
116
|
this.reglInstance = reglInstance
|
|
115
117
|
|
|
116
118
|
this.store.adjustSpaceSize(this.config.spaceSize, this.reglInstance.limits.maxTextureSize)
|
|
119
|
+
this.store.setWebGLMaxTextureSize(this.reglInstance.limits.maxTextureSize)
|
|
117
120
|
this.store.updateScreenSize(w, h)
|
|
118
121
|
|
|
119
122
|
this.canvasD3Selection = select<HTMLCanvasElement, undefined>(this.canvas)
|
|
120
123
|
this.canvasD3Selection
|
|
121
124
|
.on('mouseenter.cosmos', () => { this._isMouseOnCanvas = true })
|
|
122
125
|
.on('mousemove.cosmos', () => { this._isMouseOnCanvas = true })
|
|
123
|
-
.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
|
+
})
|
|
124
150
|
select(document)
|
|
125
151
|
.on('keydown.cosmos', (event) => { if (event.code === 'Space') this.store.isSpaceKeyPressed = true })
|
|
126
152
|
.on('keyup.cosmos', (event) => { if (event.code === 'Space') this.store.isSpaceKeyPressed = false })
|
|
@@ -173,18 +199,15 @@ export class Graph {
|
|
|
173
199
|
this.clusters = new Clusters(this.reglInstance, this.config, this.store, this.graph, this.points)
|
|
174
200
|
|
|
175
201
|
this.store.backgroundColor = getRgbaColor(this.config.backgroundColor)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
if (this.config.focusedPointRingColor) {
|
|
180
|
-
this.store.setFocusedPointRingColor(this.config.focusedPointRingColor)
|
|
181
|
-
}
|
|
202
|
+
this.store.setHoveredPointRingColor(this.config.hoveredPointRingColor ?? defaultConfigValues.hoveredPointRingColor)
|
|
203
|
+
this.store.setFocusedPointRingColor(this.config.focusedPointRingColor ?? defaultConfigValues.focusedPointRingColor)
|
|
182
204
|
if (this.config.focusedPointIndex !== undefined) {
|
|
183
205
|
this.store.setFocusedPoint(this.config.focusedPointIndex)
|
|
184
206
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
207
|
+
this.store.setGreyoutPointColor(this.config.pointGreyoutColor ?? defaultGreyoutPointColor)
|
|
208
|
+
this.store.setHoveredLinkColor(this.config.hoveredLinkColor ?? defaultConfigValues.hoveredLinkColor)
|
|
209
|
+
|
|
210
|
+
this.store.updateLinkHoveringEnabled(this.config)
|
|
188
211
|
|
|
189
212
|
if (this.config.showFPSMonitor) this.fpsMonitor = new FPSMonitor(this.canvas)
|
|
190
213
|
|
|
@@ -248,15 +271,20 @@ export class Graph {
|
|
|
248
271
|
prevConfig.curvedLinks !== this.config.curvedLinks) {
|
|
249
272
|
this.lines.updateCurveLineGeometry()
|
|
250
273
|
}
|
|
251
|
-
if (prevConfig.backgroundColor !== this.config.backgroundColor)
|
|
274
|
+
if (prevConfig.backgroundColor !== this.config.backgroundColor) {
|
|
275
|
+
this.store.backgroundColor = getRgbaColor(this.config.backgroundColor ?? defaultBackgroundColor)
|
|
276
|
+
}
|
|
252
277
|
if (prevConfig.hoveredPointRingColor !== this.config.hoveredPointRingColor) {
|
|
253
|
-
this.store.setHoveredPointRingColor(this.config.hoveredPointRingColor)
|
|
278
|
+
this.store.setHoveredPointRingColor(this.config.hoveredPointRingColor ?? defaultConfigValues.hoveredPointRingColor)
|
|
254
279
|
}
|
|
255
280
|
if (prevConfig.focusedPointRingColor !== this.config.focusedPointRingColor) {
|
|
256
|
-
this.store.setFocusedPointRingColor(this.config.focusedPointRingColor)
|
|
281
|
+
this.store.setFocusedPointRingColor(this.config.focusedPointRingColor ?? defaultConfigValues.focusedPointRingColor)
|
|
257
282
|
}
|
|
258
283
|
if (prevConfig.pointGreyoutColor !== this.config.pointGreyoutColor) {
|
|
259
|
-
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)
|
|
260
288
|
}
|
|
261
289
|
if (prevConfig.focusedPointIndex !== this.config.focusedPointIndex) {
|
|
262
290
|
this.store.setFocusedPoint(this.config.focusedPointIndex)
|
|
@@ -282,6 +310,12 @@ export class Graph {
|
|
|
282
310
|
if (prevConfig.enableZoom !== this.config.enableZoom || prevConfig.enableDrag !== this.config.enableDrag) {
|
|
283
311
|
this.updateZoomDragBehaviors()
|
|
284
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
|
+
}
|
|
285
319
|
}
|
|
286
320
|
|
|
287
321
|
/**
|
|
@@ -297,18 +331,19 @@ export class Graph {
|
|
|
297
331
|
public setPointPositions (pointPositions: Float32Array, dontRescale?: boolean | undefined): void {
|
|
298
332
|
if (this._isDestroyed || !this.points) return
|
|
299
333
|
this.graph.inputPointPositions = pointPositions
|
|
300
|
-
this.points.
|
|
301
|
-
this.
|
|
334
|
+
this.points.shouldSkipRescale = dontRescale
|
|
335
|
+
this.isPointPositionsUpdateNeeded = true
|
|
302
336
|
// Links related texture depends on point positions, so we need to update it
|
|
303
|
-
this.
|
|
337
|
+
this.isLinksUpdateNeeded = true
|
|
304
338
|
// Point related textures depend on point positions length, so we need to update them
|
|
305
|
-
this.
|
|
306
|
-
this.
|
|
307
|
-
this.
|
|
308
|
-
this.
|
|
309
|
-
this.
|
|
310
|
-
this.
|
|
311
|
-
this.
|
|
339
|
+
this.isPointColorUpdateNeeded = true
|
|
340
|
+
this.isPointSizeUpdateNeeded = true
|
|
341
|
+
this.isPointShapeUpdateNeeded = true
|
|
342
|
+
this.isPointImageIndicesUpdateNeeded = true
|
|
343
|
+
this.isPointClusterUpdateNeeded = true
|
|
344
|
+
this.isForceManyBodyUpdateNeeded = true
|
|
345
|
+
this.isForceLinkUpdateNeeded = true
|
|
346
|
+
this.isForceCenterUpdateNeeded = true
|
|
312
347
|
}
|
|
313
348
|
|
|
314
349
|
/**
|
|
@@ -321,7 +356,7 @@ export class Graph {
|
|
|
321
356
|
public setPointColors (pointColors: Float32Array): void {
|
|
322
357
|
if (this._isDestroyed) return
|
|
323
358
|
this.graph.inputPointColors = pointColors
|
|
324
|
-
this.
|
|
359
|
+
this.isPointColorUpdateNeeded = true
|
|
325
360
|
}
|
|
326
361
|
|
|
327
362
|
/**
|
|
@@ -345,7 +380,7 @@ export class Graph {
|
|
|
345
380
|
public setPointSizes (pointSizes: Float32Array): void {
|
|
346
381
|
if (this._isDestroyed) return
|
|
347
382
|
this.graph.inputPointSizes = pointSizes
|
|
348
|
-
this.
|
|
383
|
+
this.isPointSizeUpdateNeeded = true
|
|
349
384
|
}
|
|
350
385
|
|
|
351
386
|
/**
|
|
@@ -353,13 +388,55 @@ export class Graph {
|
|
|
353
388
|
*
|
|
354
389
|
* @param {Float32Array} pointShapes - A Float32Array representing the shapes of points in the format [shape1, shape2, ..., shapen],
|
|
355
390
|
* where `n` is the index of the point and each shape value corresponds to a PointShape enum:
|
|
356
|
-
* 0 = Circle, 1 = Square, 2 = Triangle, 3 = Diamond, 4 = Pentagon, 5 = Hexagon, 6 = Star, 7 = Cross.
|
|
391
|
+
* 0 = Circle, 1 = Square, 2 = Triangle, 3 = Diamond, 4 = Pentagon, 5 = Hexagon, 6 = Star, 7 = Cross, 8 = None.
|
|
357
392
|
* Example: `new Float32Array([0, 1, 2])` sets the first point to Circle, the second point to Square, and the third point to Triangle.
|
|
393
|
+
* Images are rendered above shapes.
|
|
358
394
|
*/
|
|
359
395
|
public setPointShapes (pointShapes: Float32Array): void {
|
|
360
396
|
if (this._isDestroyed) return
|
|
361
397
|
this.graph.inputPointShapes = pointShapes
|
|
362
|
-
this.
|
|
398
|
+
this.isPointShapeUpdateNeeded = true
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Sets the images for the graph points using ImageData objects.
|
|
403
|
+
* Images are rendered above shapes.
|
|
404
|
+
* To use images, provide image indices via setPointImageIndices().
|
|
405
|
+
*
|
|
406
|
+
* @param {ImageData[]} imageDataArray - Array of ImageData objects to use as point images.
|
|
407
|
+
* Example: `setImageData([imageData1, imageData2, imageData3])`
|
|
408
|
+
*/
|
|
409
|
+
public setImageData (imageDataArray: ImageData[]): void {
|
|
410
|
+
if (this._isDestroyed || !this.points) return
|
|
411
|
+
this.graph.inputImageData = imageDataArray
|
|
412
|
+
this.points.createAtlas()
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Sets which image each point should use from the images array.
|
|
417
|
+
* Images are rendered above shapes.
|
|
418
|
+
*
|
|
419
|
+
* @param {Float32Array} imageIndices - A Float32Array representing which image each point uses in the format [index1, index2, ..., indexn],
|
|
420
|
+
* where `n` is the index of the point and each value is an index into the images array provided to `setImageData`.
|
|
421
|
+
* Example: `new Float32Array([0, 1, 0])` sets the first point to use image 0, second point to use image 1, third point to use image 0.
|
|
422
|
+
*/
|
|
423
|
+
public setPointImageIndices (imageIndices: Float32Array): void {
|
|
424
|
+
if (this._isDestroyed) return
|
|
425
|
+
this.graph.inputPointImageIndices = imageIndices
|
|
426
|
+
this.isPointImageIndicesUpdateNeeded = true
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Sets the sizes for the point images.
|
|
431
|
+
*
|
|
432
|
+
* @param {Float32Array} imageSizes - A Float32Array representing the sizes of point images in the format [size1, size2, ..., sizen],
|
|
433
|
+
* where `n` is the index of the point.
|
|
434
|
+
* Example: `new Float32Array([10, 20, 30])` sets the first image to size 10, the second image to size 20, and the third image to size 30.
|
|
435
|
+
*/
|
|
436
|
+
public setPointImageSizes (imageSizes: Float32Array): void {
|
|
437
|
+
if (this._isDestroyed) return
|
|
438
|
+
this.graph.inputPointImageSizes = imageSizes
|
|
439
|
+
this.isPointImageSizesUpdateNeeded = true
|
|
363
440
|
}
|
|
364
441
|
|
|
365
442
|
/**
|
|
@@ -384,12 +461,12 @@ export class Graph {
|
|
|
384
461
|
public setLinks (links: Float32Array): void {
|
|
385
462
|
if (this._isDestroyed) return
|
|
386
463
|
this.graph.inputLinks = links
|
|
387
|
-
this.
|
|
464
|
+
this.isLinksUpdateNeeded = true
|
|
388
465
|
// Links related texture depends on links length, so we need to update it
|
|
389
|
-
this.
|
|
390
|
-
this.
|
|
391
|
-
this.
|
|
392
|
-
this.
|
|
466
|
+
this.isLinkColorUpdateNeeded = true
|
|
467
|
+
this.isLinkWidthUpdateNeeded = true
|
|
468
|
+
this.isLinkArrowUpdateNeeded = true
|
|
469
|
+
this.isForceLinkUpdateNeeded = true
|
|
393
470
|
}
|
|
394
471
|
|
|
395
472
|
/**
|
|
@@ -402,7 +479,7 @@ export class Graph {
|
|
|
402
479
|
public setLinkColors (linkColors: Float32Array): void {
|
|
403
480
|
if (this._isDestroyed) return
|
|
404
481
|
this.graph.inputLinkColors = linkColors
|
|
405
|
-
this.
|
|
482
|
+
this.isLinkColorUpdateNeeded = true
|
|
406
483
|
}
|
|
407
484
|
|
|
408
485
|
/**
|
|
@@ -426,7 +503,7 @@ export class Graph {
|
|
|
426
503
|
public setLinkWidths (linkWidths: Float32Array): void {
|
|
427
504
|
if (this._isDestroyed) return
|
|
428
505
|
this.graph.inputLinkWidths = linkWidths
|
|
429
|
-
this.
|
|
506
|
+
this.isLinkWidthUpdateNeeded = true
|
|
430
507
|
}
|
|
431
508
|
|
|
432
509
|
/**
|
|
@@ -450,7 +527,7 @@ export class Graph {
|
|
|
450
527
|
public setLinkArrows (linkArrows: boolean[]): void {
|
|
451
528
|
if (this._isDestroyed) return
|
|
452
529
|
this.graph.linkArrowsBoolean = linkArrows
|
|
453
|
-
this.
|
|
530
|
+
this.isLinkArrowUpdateNeeded = true
|
|
454
531
|
}
|
|
455
532
|
|
|
456
533
|
/**
|
|
@@ -463,7 +540,7 @@ export class Graph {
|
|
|
463
540
|
public setLinkStrength (linkStrength: Float32Array): void {
|
|
464
541
|
if (this._isDestroyed) return
|
|
465
542
|
this.graph.inputLinkStrength = linkStrength
|
|
466
|
-
this.
|
|
543
|
+
this.isForceLinkUpdateNeeded = true
|
|
467
544
|
}
|
|
468
545
|
|
|
469
546
|
/**
|
|
@@ -480,7 +557,7 @@ export class Graph {
|
|
|
480
557
|
public setPointClusters (pointClusters: (number | undefined)[]): void {
|
|
481
558
|
if (this._isDestroyed) return
|
|
482
559
|
this.graph.inputPointClusters = pointClusters
|
|
483
|
-
this.
|
|
560
|
+
this.isPointClusterUpdateNeeded = true
|
|
484
561
|
}
|
|
485
562
|
|
|
486
563
|
/**
|
|
@@ -496,7 +573,7 @@ export class Graph {
|
|
|
496
573
|
public setClusterPositions (clusterPositions: (number | undefined)[]): void {
|
|
497
574
|
if (this._isDestroyed) return
|
|
498
575
|
this.graph.inputClusterPositions = clusterPositions
|
|
499
|
-
this.
|
|
576
|
+
this.isPointClusterUpdateNeeded = true
|
|
500
577
|
}
|
|
501
578
|
|
|
502
579
|
/**
|
|
@@ -512,7 +589,7 @@ export class Graph {
|
|
|
512
589
|
public setPointClusterStrength (clusterStrength: Float32Array): void {
|
|
513
590
|
if (this._isDestroyed) return
|
|
514
591
|
this.graph.inputClusterStrength = clusterStrength
|
|
515
|
-
this.
|
|
592
|
+
this.isPointClusterUpdateNeeded = true
|
|
516
593
|
}
|
|
517
594
|
|
|
518
595
|
/**
|
|
@@ -936,9 +1013,11 @@ export class Graph {
|
|
|
936
1013
|
|
|
937
1014
|
/**
|
|
938
1015
|
* Get current X and Y coordinates of the tracked points.
|
|
939
|
-
*
|
|
1016
|
+
* Do not mutate the returned map - it may affect future calls.
|
|
1017
|
+
* @returns A ReadonlyMap where keys are point indices and values are their corresponding X and Y coordinates in the [number, number] format.
|
|
1018
|
+
* @see trackPointPositionsByIndices To set which points should be tracked
|
|
940
1019
|
*/
|
|
941
|
-
public getTrackedPointPositionsMap ():
|
|
1020
|
+
public getTrackedPointPositionsMap (): ReadonlyMap<number, [number, number]> {
|
|
942
1021
|
if (this._isDestroyed || !this.points) return new Map()
|
|
943
1022
|
return this.points.getTrackedPositionsMap()
|
|
944
1023
|
}
|
|
@@ -1017,7 +1096,8 @@ export class Graph {
|
|
|
1017
1096
|
}
|
|
1018
1097
|
|
|
1019
1098
|
/**
|
|
1020
|
-
* Pause the simulation.
|
|
1099
|
+
* Pause the simulation. When paused, the simulation stops running
|
|
1100
|
+
* and can be resumed using the unpause method.
|
|
1021
1101
|
*/
|
|
1022
1102
|
public pause (): void {
|
|
1023
1103
|
if (this._isDestroyed) return
|
|
@@ -1026,7 +1106,19 @@ export class Graph {
|
|
|
1026
1106
|
}
|
|
1027
1107
|
|
|
1028
1108
|
/**
|
|
1029
|
-
*
|
|
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.
|
|
1030
1122
|
*/
|
|
1031
1123
|
public restart (): void {
|
|
1032
1124
|
if (this._isDestroyed) return
|
|
@@ -1114,36 +1206,40 @@ export class Graph {
|
|
|
1114
1206
|
*/
|
|
1115
1207
|
public create (): void {
|
|
1116
1208
|
if (this._isDestroyed || !this.points || !this.lines) return
|
|
1117
|
-
if (this.
|
|
1118
|
-
if (this.
|
|
1119
|
-
if (this.
|
|
1120
|
-
if (this.
|
|
1121
|
-
|
|
1122
|
-
if (this.
|
|
1123
|
-
|
|
1124
|
-
if (this.
|
|
1125
|
-
if (this.
|
|
1126
|
-
|
|
1127
|
-
if (this.
|
|
1128
|
-
|
|
1209
|
+
if (this.isPointPositionsUpdateNeeded) this.points.updatePositions()
|
|
1210
|
+
if (this.isPointColorUpdateNeeded) this.points.updateColor()
|
|
1211
|
+
if (this.isPointSizeUpdateNeeded) this.points.updateSize()
|
|
1212
|
+
if (this.isPointShapeUpdateNeeded) this.points.updateShape()
|
|
1213
|
+
if (this.isPointImageIndicesUpdateNeeded) this.points.updateImageIndices()
|
|
1214
|
+
if (this.isPointImageSizesUpdateNeeded) this.points.updateImageSizes()
|
|
1215
|
+
|
|
1216
|
+
if (this.isLinksUpdateNeeded) this.lines.updatePointsBuffer()
|
|
1217
|
+
if (this.isLinkColorUpdateNeeded) this.lines.updateColor()
|
|
1218
|
+
if (this.isLinkWidthUpdateNeeded) this.lines.updateWidth()
|
|
1219
|
+
if (this.isLinkArrowUpdateNeeded) this.lines.updateArrow()
|
|
1220
|
+
|
|
1221
|
+
if (this.isForceManyBodyUpdateNeeded) this.forceManyBody?.create()
|
|
1222
|
+
if (this.isForceLinkUpdateNeeded) {
|
|
1129
1223
|
this.forceLinkIncoming?.create(LinkDirection.INCOMING)
|
|
1130
1224
|
this.forceLinkOutgoing?.create(LinkDirection.OUTGOING)
|
|
1131
1225
|
}
|
|
1132
|
-
if (this.
|
|
1133
|
-
if (this.
|
|
1134
|
-
|
|
1135
|
-
this.
|
|
1136
|
-
this.
|
|
1137
|
-
this.
|
|
1138
|
-
this.
|
|
1139
|
-
this.
|
|
1140
|
-
this.
|
|
1141
|
-
this.
|
|
1142
|
-
this.
|
|
1143
|
-
this.
|
|
1144
|
-
this.
|
|
1145
|
-
this.
|
|
1146
|
-
this.
|
|
1226
|
+
if (this.isForceCenterUpdateNeeded) this.forceCenter?.create()
|
|
1227
|
+
if (this.isPointClusterUpdateNeeded) this.clusters?.create()
|
|
1228
|
+
|
|
1229
|
+
this.isPointPositionsUpdateNeeded = false
|
|
1230
|
+
this.isPointColorUpdateNeeded = false
|
|
1231
|
+
this.isPointSizeUpdateNeeded = false
|
|
1232
|
+
this.isPointShapeUpdateNeeded = false
|
|
1233
|
+
this.isPointImageIndicesUpdateNeeded = false
|
|
1234
|
+
this.isPointImageSizesUpdateNeeded = false
|
|
1235
|
+
this.isLinksUpdateNeeded = false
|
|
1236
|
+
this.isLinkColorUpdateNeeded = false
|
|
1237
|
+
this.isLinkWidthUpdateNeeded = false
|
|
1238
|
+
this.isLinkArrowUpdateNeeded = false
|
|
1239
|
+
this.isPointClusterUpdateNeeded = false
|
|
1240
|
+
this.isForceManyBodyUpdateNeeded = false
|
|
1241
|
+
this.isForceLinkUpdateNeeded = false
|
|
1242
|
+
this.isForceCenterUpdateNeeded = false
|
|
1147
1243
|
}
|
|
1148
1244
|
|
|
1149
1245
|
/**
|
|
@@ -1193,6 +1289,7 @@ export class Graph {
|
|
|
1193
1289
|
}
|
|
1194
1290
|
|
|
1195
1291
|
private frame (): void {
|
|
1292
|
+
if (this._isDestroyed) return
|
|
1196
1293
|
const { config: { simulationGravity, simulationCenter, renderLinks, enableSimulation }, store: { alpha, isSimulationRunning } } = this
|
|
1197
1294
|
if (alpha < ALPHA_MIN && isSimulationRunning) this.end()
|
|
1198
1295
|
if (!this.store.pointsTextureSize) return
|
|
@@ -1200,7 +1297,9 @@ export class Graph {
|
|
|
1200
1297
|
this.requestAnimationFrameId = window.requestAnimationFrame((now) => {
|
|
1201
1298
|
this.fpsMonitor?.begin()
|
|
1202
1299
|
this.resizeCanvas()
|
|
1203
|
-
if (!this.dragInstance.isActive)
|
|
1300
|
+
if (!this.dragInstance.isActive) {
|
|
1301
|
+
this.findHoveredItem()
|
|
1302
|
+
}
|
|
1204
1303
|
|
|
1205
1304
|
if (enableSimulation) {
|
|
1206
1305
|
if (this.isRightClickMouse && this.config.enableRightClickRepulsion) {
|
|
@@ -1266,7 +1365,9 @@ export class Graph {
|
|
|
1266
1365
|
this.fpsMonitor?.end(now)
|
|
1267
1366
|
|
|
1268
1367
|
this.currentEvent = undefined
|
|
1269
|
-
this.
|
|
1368
|
+
if (!this._isDestroyed) {
|
|
1369
|
+
this.frame()
|
|
1370
|
+
}
|
|
1270
1371
|
})
|
|
1271
1372
|
}
|
|
1272
1373
|
|
|
@@ -1286,6 +1387,23 @@ export class Graph {
|
|
|
1286
1387
|
this.store.hoveredPoint?.position,
|
|
1287
1388
|
event
|
|
1288
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
|
+
}
|
|
1289
1407
|
}
|
|
1290
1408
|
|
|
1291
1409
|
private updateMousePosition (event: MouseEvent | D3DragEvent<HTMLCanvasElement, undefined, Hovered>): void {
|
|
@@ -1313,6 +1431,7 @@ export class Graph {
|
|
|
1313
1431
|
}
|
|
1314
1432
|
|
|
1315
1433
|
private resizeCanvas (forceResize = false): void {
|
|
1434
|
+
if (this._isDestroyed) return
|
|
1316
1435
|
const prevWidth = this.canvas.width
|
|
1317
1436
|
const prevHeight = this.canvas.height
|
|
1318
1437
|
const w = this.canvas.clientWidth
|
|
@@ -1330,6 +1449,10 @@ export class Graph {
|
|
|
1330
1449
|
this.canvasD3Selection
|
|
1331
1450
|
?.call(this.zoomInstance.behavior.transform, this.zoomInstance.getTransform([centerPosition], k))
|
|
1332
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
|
+
}
|
|
1333
1456
|
}
|
|
1334
1457
|
}
|
|
1335
1458
|
|
|
@@ -1361,13 +1484,31 @@ export class Graph {
|
|
|
1361
1484
|
}
|
|
1362
1485
|
}
|
|
1363
1486
|
|
|
1364
|
-
private
|
|
1365
|
-
if (
|
|
1366
|
-
if (this.
|
|
1367
|
-
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
|
|
1368
1491
|
return
|
|
1369
1492
|
}
|
|
1370
|
-
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
|
|
1371
1512
|
this.points.findHoveredPoint()
|
|
1372
1513
|
let isMouseover = false
|
|
1373
1514
|
let isMouseout = false
|
|
@@ -1395,15 +1536,46 @@ export class Graph {
|
|
|
1395
1536
|
)
|
|
1396
1537
|
}
|
|
1397
1538
|
if (isMouseout) this.config.onPointMouseOut?.(this.currentEvent)
|
|
1398
|
-
|
|
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)
|
|
1399
1569
|
}
|
|
1400
1570
|
|
|
1401
1571
|
private updateCanvasCursor (): void {
|
|
1402
|
-
const { hoveredPointCursor } = this.config
|
|
1572
|
+
const { hoveredPointCursor, hoveredLinkCursor } = this.config
|
|
1403
1573
|
if (this.dragInstance.isActive) select(this.canvas).style('cursor', 'grabbing')
|
|
1404
1574
|
else if (this.store.hoveredPoint) {
|
|
1405
1575
|
if (!this.config.enableDrag || this.store.isSpaceKeyPressed) select(this.canvas).style('cursor', hoveredPointCursor)
|
|
1406
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)
|
|
1407
1579
|
} else select(this.canvas).style('cursor', null)
|
|
1408
1580
|
}
|
|
1409
1581
|
|