@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.
Files changed (36) hide show
  1. package/dist/config.d.ts +69 -0
  2. package/dist/index.d.ts +16 -6
  3. package/dist/index.js +4328 -4129
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.min.js +113 -45
  6. package/dist/index.min.js.map +1 -1
  7. package/dist/modules/Lines/index.d.ts +8 -0
  8. package/dist/modules/Store/index.d.ts +14 -2
  9. package/dist/modules/core-module.d.ts +1 -0
  10. package/dist/stories/beginners/link-hovering/data-generator.d.ts +19 -0
  11. package/dist/stories/beginners/link-hovering/index.d.ts +5 -0
  12. package/dist/stories/beginners.stories.d.ts +1 -0
  13. package/dist/variables.d.ts +5 -2
  14. package/package.json +1 -1
  15. package/src/config.ts +86 -2
  16. package/src/index.ts +151 -31
  17. package/src/modules/Lines/draw-curve-line.frag +12 -1
  18. package/src/modules/Lines/draw-curve-line.vert +29 -2
  19. package/src/modules/Lines/hovered-line-index.frag +27 -0
  20. package/src/modules/Lines/hovered-line-index.vert +8 -0
  21. package/src/modules/Lines/index.ts +112 -2
  22. package/src/modules/Store/index.ts +33 -2
  23. package/src/modules/core-module.ts +1 -0
  24. package/src/stories/1. welcome.mdx +2 -1
  25. package/src/stories/2. configuration.mdx +10 -1
  26. package/src/stories/3. api-reference.mdx +13 -4
  27. package/src/stories/beginners/basic-set-up/index.ts +20 -10
  28. package/src/stories/beginners/link-hovering/data-generator.ts +198 -0
  29. package/src/stories/beginners/link-hovering/index.ts +61 -0
  30. package/src/stories/beginners/link-hovering/style.css +73 -0
  31. package/src/stories/beginners/quick-start.ts +2 -1
  32. package/src/stories/beginners/remove-points/index.ts +28 -30
  33. package/src/stories/beginners.stories.ts +17 -0
  34. package/src/stories/clusters/polygon-selection/index.ts +2 -4
  35. package/src/stories/shapes/image-example/index.ts +7 -8
  36. package/src/variables.ts +5 -2
@@ -4,13 +4,14 @@ attribute vec2 position, pointA, pointB;
4
4
  attribute vec4 color;
5
5
  attribute float width;
6
6
  attribute float arrow;
7
+ attribute float linkIndices;
7
8
 
8
9
  uniform sampler2D positionsTexture;
9
10
  uniform sampler2D pointGreyoutStatus;
10
11
  uniform mat3 transformationMatrix;
11
12
  uniform float pointsTextureSize;
12
13
  uniform float widthScale;
13
- uniform float arrowSizeScale;
14
+ uniform float linkArrowsSizeScale;
14
15
  uniform float spaceSize;
15
16
  uniform vec2 screenSize;
16
17
  uniform vec2 linkVisibilityDistanceRange;
@@ -22,6 +23,11 @@ uniform float curvedLinkControlPointDistance;
22
23
  uniform float curvedLinkSegments;
23
24
  uniform bool scaleLinksOnZoom;
24
25
  uniform float maxPointSize;
26
+ // renderMode: 0.0 = normal rendering, 1.0 = index buffer rendering for picking
27
+ uniform float renderMode;
28
+ uniform float hoveredLinkIndex;
29
+ uniform vec4 hoveredLinkColor;
30
+ uniform float hoveredLinkWidthIncrease;
25
31
 
26
32
  varying vec4 rgbaColor;
27
33
  varying vec2 pos;
@@ -29,6 +35,7 @@ varying float arrowLength;
29
35
  varying float useArrow;
30
36
  varying float smoothing;
31
37
  varying float arrowWidthFactor;
38
+ varying float linkIndex;
32
39
 
33
40
  float map(float value, float min1, float max1, float min2, float max2) {
34
41
  return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
@@ -73,6 +80,7 @@ float calculateArrowWidth(float arrowWidth) {
73
80
 
74
81
  void main() {
75
82
  pos = position;
83
+ linkIndex = linkIndices;
76
84
 
77
85
  vec2 pointTexturePosA = (pointA + 0.5) / pointsTextureSize;
78
86
  vec2 pointTexturePosB = (pointB + 0.5) / pointsTextureSize;
@@ -102,7 +110,7 @@ void main() {
102
110
  float k = 2.0;
103
111
  // Arrow width is proportionally larger than the line width
104
112
  float arrowWidth = linkWidth * k;
105
- arrowWidth *= arrowSizeScale;
113
+ arrowWidth *= linkArrowsSizeScale;
106
114
 
107
115
  // Ensure arrow width difference is non-negative to prevent unwanted changes to link width
108
116
  float arrowWidthDifference = max(0.0, arrowWidth - linkWidth);
@@ -124,10 +132,22 @@ void main() {
124
132
 
125
133
  // Calculate final link width in pixels with smoothing
126
134
  float linkWidthPx = calculateLinkWidth(linkWidth);
135
+
136
+ if (renderMode > 0.0) {
137
+ // Add 5 pixels padding for better hover detection
138
+ linkWidthPx += 5.0 / transformationMatrix[0][0];
139
+ } else {
140
+ // Add pixel increase if this is the hovered link
141
+ if (hoveredLinkIndex == linkIndex) {
142
+ linkWidthPx += hoveredLinkWidthIncrease / transformationMatrix[0][0];
143
+ }
144
+ }
127
145
  float smoothingPx = 0.5 / transformationMatrix[0][0];
128
146
  smoothing = smoothingPx / linkWidthPx;
129
147
  linkWidthPx += smoothingPx;
130
148
 
149
+
150
+
131
151
  // Calculate final color with opacity based on link distance
132
152
  vec3 rgbColor = color.rgb;
133
153
  // Adjust opacity based on link distance
@@ -141,6 +161,13 @@ void main() {
141
161
  // Pass final color to fragment shader
142
162
  rgbaColor = vec4(rgbColor, opacity);
143
163
 
164
+ // Apply hover color if this is the hovered link and hover color is defined
165
+ if (hoveredLinkIndex == linkIndex && hoveredLinkColor.a > -0.5) {
166
+ // Keep existing RGB values but multiply opacity with hover color opacity
167
+ rgbaColor.rgb = hoveredLinkColor.rgb;
168
+ rgbaColor.a *= hoveredLinkColor.a;
169
+ }
170
+
144
171
  // Calculate position on the curved path
145
172
  float t = position.x;
146
173
  float w = curvedWeight;
@@ -0,0 +1,27 @@
1
+ precision highp float;
2
+
3
+ uniform sampler2D linkIndexTexture;
4
+ uniform vec2 mousePosition;
5
+ uniform vec2 screenSize;
6
+
7
+ varying vec2 vTexCoord;
8
+
9
+ void main() {
10
+ // Convert mouse position to texture coordinates
11
+ vec2 texCoord = mousePosition / screenSize;
12
+
13
+ // Read the link index from the linkIndexFbo texture at mouse position
14
+ vec4 linkIndexData = texture2D(linkIndexTexture, texCoord);
15
+
16
+ // Extract the link index (stored in the red channel)
17
+ float linkIndex = linkIndexData.r;
18
+
19
+ // Check if there's a valid link at this position (alpha > 0)
20
+ if (linkIndexData.a > 0.0 && linkIndex >= 0.0) {
21
+ // Output the link index
22
+ gl_FragColor = vec4(linkIndex, 0.0, 0.0, 1.0);
23
+ } else {
24
+ // No link at this position, output -1 to indicate no hover
25
+ gl_FragColor = vec4(-1.0, 0.0, 0.0, 0.0);
26
+ }
27
+ }
@@ -0,0 +1,8 @@
1
+ attribute vec2 position;
2
+
3
+ varying vec2 vTexCoord;
4
+
5
+ void main() {
6
+ vTexCoord = position * 0.5 + 0.5;
7
+ gl_Position = vec4(position, 0.0, 1.0);
8
+ }
@@ -2,21 +2,44 @@ import regl from 'regl'
2
2
  import { CoreModule } from '@/graph/modules/core-module'
3
3
  import drawLineFrag from '@/graph/modules/Lines/draw-curve-line.frag'
4
4
  import drawLineVert from '@/graph/modules/Lines/draw-curve-line.vert'
5
+ import hoveredLineIndexFrag from '@/graph/modules/Lines/hovered-line-index.frag'
6
+ import hoveredLineIndexVert from '@/graph/modules/Lines/hovered-line-index.vert'
5
7
  import { defaultConfigValues } from '@/graph/variables'
6
8
  import { getCurveLineGeometry } from '@/graph/modules/Lines/geometry'
7
9
 
8
10
  export class Lines extends CoreModule {
11
+ public linkIndexFbo: regl.Framebuffer2D | undefined
12
+ public hoveredLineIndexFbo: regl.Framebuffer2D | undefined
9
13
  private drawCurveCommand: regl.DrawCommand | undefined
14
+ private hoveredLineIndexCommand: regl.DrawCommand | undefined
10
15
  private pointsBuffer: regl.Buffer | undefined
11
16
  private colorBuffer: regl.Buffer | undefined
12
17
  private widthBuffer: regl.Buffer | undefined
13
18
  private arrowBuffer: regl.Buffer | undefined
14
19
  private curveLineGeometry: number[][] | undefined
15
20
  private curveLineBuffer: regl.Buffer | undefined
21
+ private linkIndexBuffer: regl.Buffer | undefined
22
+ private quadBuffer: regl.Buffer | undefined
16
23
 
17
24
  public initPrograms (): void {
18
25
  const { reglInstance, config, store } = this
19
26
 
27
+ this.updateLinkIndexFbo()
28
+
29
+ // Initialize the hovered line index FBO
30
+ if (!this.hoveredLineIndexFbo) {
31
+ this.hoveredLineIndexFbo = reglInstance.framebuffer({
32
+ color: reglInstance.texture({
33
+ width: 1,
34
+ height: 1,
35
+ format: 'rgba',
36
+ type: 'float',
37
+ }),
38
+ depth: false,
39
+ stencil: false,
40
+ })
41
+ }
42
+
20
43
  if (!this.drawCurveCommand) {
21
44
  this.drawCurveCommand = reglInstance({
22
45
  vert: drawLineVert,
@@ -57,6 +80,12 @@ export class Lines extends CoreModule {
57
80
  offset: Float32Array.BYTES_PER_ELEMENT * 0,
58
81
  stride: Float32Array.BYTES_PER_ELEMENT * 1,
59
82
  },
83
+ linkIndices: {
84
+ buffer: () => this.linkIndexBuffer,
85
+ divisor: 1,
86
+ offset: Float32Array.BYTES_PER_ELEMENT * 0,
87
+ stride: Float32Array.BYTES_PER_ELEMENT * 1,
88
+ },
60
89
  },
61
90
  uniforms: {
62
91
  positionsTexture: () => this.points?.currentPositionFbo,
@@ -64,7 +93,7 @@ export class Lines extends CoreModule {
64
93
  transformationMatrix: () => store.transform,
65
94
  pointsTextureSize: () => store.pointsTextureSize,
66
95
  widthScale: () => config.linkWidthScale,
67
- arrowSizeScale: () => config.linkArrowsSizeScale,
96
+ linkArrowsSizeScale: () => config.linkArrowsSizeScale,
68
97
  spaceSize: () => store.adjustedSpaceSize,
69
98
  screenSize: () => store.screenSize,
70
99
  linkVisibilityDistanceRange: () => config.linkVisibilityDistanceRange,
@@ -76,11 +105,24 @@ export class Lines extends CoreModule {
76
105
  curvedWeight: () => config.curvedLinkWeight,
77
106
  curvedLinkControlPointDistance: () => config.curvedLinkControlPointDistance,
78
107
  curvedLinkSegments: () => config.curvedLinks ? config.curvedLinkSegments ?? defaultConfigValues.curvedLinkSegments : 1,
108
+ hoveredLinkIndex: () => store.hoveredLinkIndex ?? -1,
109
+ hoveredLinkColor: () => store.hoveredLinkColor,
110
+ hoveredLinkWidthIncrease: () => config.hoveredLinkWidthIncrease,
111
+ renderMode: reglInstance.prop<{ renderMode: number }, 'renderMode'>('renderMode'),
79
112
  },
80
113
  cull: {
81
114
  enable: true,
82
115
  face: 'back',
83
116
  },
117
+ /**
118
+ * Blending behavior for link index rendering (renderMode: 1.0 - hover detection):
119
+ *
120
+ * When rendering link indices to the framebuffer, we use full opacity (1.0).
121
+ * This means:
122
+ * - The source color completely overwrites the destination
123
+ * - No blending occurs - it's like drawing with a permanent marker
124
+ * - This preserves the exact index values we need for picking/selection
125
+ */
84
126
  blend: {
85
127
  enable: true,
86
128
  func: {
@@ -98,11 +140,37 @@ export class Lines extends CoreModule {
98
140
  enable: false,
99
141
  mask: false,
100
142
  },
143
+ framebuffer: reglInstance.prop<{ framebuffer: regl.Framebuffer2D }, 'framebuffer'>('framebuffer'),
101
144
  count: () => this.curveLineGeometry?.length ?? 0,
102
145
  instances: () => this.data.linksNumber ?? 0,
103
146
  primitive: 'triangle strip',
104
147
  })
105
148
  }
149
+
150
+ if (!this.hoveredLineIndexCommand) {
151
+ this.hoveredLineIndexCommand = reglInstance({
152
+ vert: hoveredLineIndexVert,
153
+ frag: hoveredLineIndexFrag,
154
+ attributes: {
155
+ position: {
156
+ buffer: () => this.quadBuffer,
157
+ },
158
+ },
159
+ uniforms: {
160
+ linkIndexTexture: () => this.linkIndexFbo,
161
+ mousePosition: () => store.screenMousePosition,
162
+ screenSize: () => store.screenSize,
163
+ },
164
+ framebuffer: this.hoveredLineIndexFbo,
165
+ count: 4,
166
+ primitive: 'triangle strip',
167
+ })
168
+ }
169
+
170
+ // Initialize quad buffer for full-screen rendering
171
+ if (!this.quadBuffer) {
172
+ this.quadBuffer = reglInstance.buffer([-1, -1, 1, -1, -1, 1, 1, 1])
173
+ }
106
174
  }
107
175
 
108
176
  public draw (): void {
@@ -111,7 +179,28 @@ export class Lines extends CoreModule {
111
179
  if (!this.widthBuffer) this.updateWidth()
112
180
  if (!this.arrowBuffer) this.updateArrow()
113
181
  if (!this.curveLineGeometry) this.updateCurveLineGeometry()
114
- this.drawCurveCommand?.()
182
+
183
+ // Render normal links (renderMode: 0.0 = normal rendering)
184
+ this.drawCurveCommand?.({ framebuffer: null, renderMode: 0.0 })
185
+ }
186
+
187
+ public updateLinkIndexFbo (): void {
188
+ const { reglInstance, store } = this
189
+
190
+ // Only create and update the link index FBO if link hovering is enabled
191
+ if (!this.store.isLinkHoveringEnabled) return
192
+
193
+ if (!this.linkIndexFbo) this.linkIndexFbo = reglInstance.framebuffer()
194
+ this.linkIndexFbo({
195
+ color: reglInstance.texture({
196
+ width: store.screenSize[0],
197
+ height: store.screenSize[1],
198
+ format: 'rgba',
199
+ type: 'float',
200
+ }),
201
+ depth: false,
202
+ stencil: false,
203
+ })
115
204
  }
116
205
 
117
206
  public updatePointsBuffer (): void {
@@ -134,6 +223,13 @@ export class Lines extends CoreModule {
134
223
 
135
224
  if (!this.pointsBuffer) this.pointsBuffer = reglInstance.buffer(0)
136
225
  this.pointsBuffer(instancePoints)
226
+
227
+ const linkIndices = new Float32Array(data.linksNumber)
228
+ for (let i = 0; i < data.linksNumber; i++) {
229
+ linkIndices[i] = i
230
+ }
231
+ if (!this.linkIndexBuffer) this.linkIndexBuffer = reglInstance.buffer(0)
232
+ this.linkIndexBuffer(linkIndices)
137
233
  }
138
234
 
139
235
  public updateColor (): void {
@@ -160,4 +256,18 @@ export class Lines extends CoreModule {
160
256
  if (!this.curveLineBuffer) this.curveLineBuffer = reglInstance.buffer(0)
161
257
  this.curveLineBuffer(this.curveLineGeometry)
162
258
  }
259
+
260
+ public findHoveredLine (): void {
261
+ if (!this.data.linksNumber || !this.store.isLinkHoveringEnabled) return
262
+ if (!this.linkIndexFbo) this.updateLinkIndexFbo()
263
+ this.reglInstance.clear({
264
+ framebuffer: this.linkIndexFbo as regl.Framebuffer2D,
265
+ color: [0, 0, 0, 0],
266
+ })
267
+ // Render to index buffer for picking/hover detection (renderMode: 1.0 = index rendering)
268
+ this.drawCurveCommand?.({ framebuffer: this.linkIndexFbo, renderMode: 1.0 })
269
+
270
+ // Execute the command to read the link index at mouse position
271
+ this.hoveredLineIndexCommand?.()
272
+ }
163
273
  }
@@ -3,10 +3,18 @@ import { mat3 } from 'gl-matrix'
3
3
  import { Random } from 'random'
4
4
  import { getRgbaColor, rgbToBrightness } from '@/graph/helper'
5
5
  import { hoveredPointRingOpacity, focusedPointRingOpacity, defaultConfigValues } from '@/graph/variables'
6
+ import type { GraphConfigInterface } from '@/graph/config'
6
7
 
7
8
  export const ALPHA_MIN = 0.001
8
9
  export const MAX_POINT_SIZE = 64
9
10
 
11
+ /**
12
+ * Maximum number of executions to delay before performing hover detection.
13
+ * This threshold prevents excessive hover detection calls for performance optimization.
14
+ * The `findHoveredItem` method will skip actual detection until this count is reached.
15
+ */
16
+ export const MAX_HOVER_DETECTION_DELAY = 4
17
+
10
18
  export type Hovered = { index: number; position: [ number, number ] }
11
19
  type Focused = { index: number }
12
20
 
@@ -26,6 +34,7 @@ export class Store {
26
34
  public hoveredPoint: Hovered | undefined = undefined
27
35
  public focusedPoint: Focused | undefined = undefined
28
36
  public draggingPointIndex: number | undefined = undefined
37
+ public hoveredLinkIndex: number | undefined = undefined
29
38
  public adjustedSpaceSize = defaultConfigValues.spaceSize
30
39
  public isSpaceKeyPressed = false
31
40
  public div: HTMLDivElement | undefined
@@ -33,10 +42,13 @@ export class Store {
33
42
 
34
43
  public hoveredPointRingColor = [1, 1, 1, hoveredPointRingOpacity]
35
44
  public focusedPointRingColor = [1, 1, 1, focusedPointRingOpacity]
45
+ public hoveredLinkColor = [-1, -1, -1, -1]
36
46
  // -1 means that the color is not set
37
47
  public greyoutPointColor = [-1, -1, -1, -1]
38
48
  // If backgroundColor is dark, isDarkenGreyout is true
39
49
  public isDarkenGreyout = false
50
+ // Whether link hovering is enabled based on configured event handlers
51
+ public isLinkHoveringEnabled = false
40
52
  private alphaTarget = 0
41
53
  private scalePointX = scaleLinear()
42
54
  private scalePointY = scaleLinear()
@@ -102,14 +114,14 @@ export class Store {
102
114
  return this.scalePointY(y)
103
115
  }
104
116
 
105
- public setHoveredPointRingColor (color: string): void {
117
+ public setHoveredPointRingColor (color: string | [number, number, number, number]): void {
106
118
  const convertedRgba = getRgbaColor(color)
107
119
  this.hoveredPointRingColor[0] = convertedRgba[0]
108
120
  this.hoveredPointRingColor[1] = convertedRgba[1]
109
121
  this.hoveredPointRingColor[2] = convertedRgba[2]
110
122
  }
111
123
 
112
- public setFocusedPointRingColor (color: string): void {
124
+ public setFocusedPointRingColor (color: string | [number, number, number, number]): void {
113
125
  const convertedRgba = getRgbaColor(color)
114
126
  this.focusedPointRingColor[0] = convertedRgba[0]
115
127
  this.focusedPointRingColor[1] = convertedRgba[1]
@@ -128,6 +140,25 @@ export class Store {
128
140
  this.greyoutPointColor[3] = convertedRgba[3]
129
141
  }
130
142
 
143
+ public updateLinkHoveringEnabled (config: Pick<GraphConfigInterface, 'onLinkClick' | 'onLinkMouseOver' | 'onLinkMouseOut'>): void {
144
+ this.isLinkHoveringEnabled = !!(config.onLinkClick || config.onLinkMouseOver || config.onLinkMouseOut)
145
+ if (!this.isLinkHoveringEnabled) {
146
+ this.hoveredLinkIndex = undefined
147
+ }
148
+ }
149
+
150
+ public setHoveredLinkColor (color?: string | [number, number, number, number]): void {
151
+ if (color === undefined) {
152
+ this.hoveredLinkColor = [-1, -1, -1, -1]
153
+ return
154
+ }
155
+ const convertedRgba = getRgbaColor(color)
156
+ this.hoveredLinkColor[0] = convertedRgba[0]
157
+ this.hoveredLinkColor[1] = convertedRgba[1]
158
+ this.hoveredLinkColor[2] = convertedRgba[2]
159
+ this.hoveredLinkColor[3] = convertedRgba[3]
160
+ }
161
+
131
162
  public setFocusedPoint (index?: number): void {
132
163
  if (index !== undefined) {
133
164
  this.focusedPoint = { index }
@@ -10,6 +10,7 @@ export class CoreModule {
10
10
  public readonly store: Store
11
11
  public readonly data: GraphData
12
12
  public readonly points: Points | undefined
13
+ public _debugRandomNumber = Math.floor(Math.random() * 1000)
13
14
 
14
15
  public constructor (
15
16
  reglInstance: regl.Regl,
@@ -35,7 +35,8 @@ const config = {
35
35
  fitViewPadding: 0.3, // centers the graph width padding of ~30% of screen
36
36
  rescalePositions: true, // rescale positions
37
37
  enableDrag: true, // enable dragging points
38
- onClick: pointIndex => { console.log('Clicked point index: ', pointIndex) },
38
+ onPointClick: pointIndex => { console.log('Clicked point index: ', pointIndex) },
39
+ onBackgroundClick: () => { console.log('Clicked background') },
39
40
  /* ... */
40
41
  }
41
42
 
@@ -16,6 +16,7 @@ import { Meta } from "@storybook/blocks";
16
16
  | pointOpacity | Universal opacity value applied to all points. This value multiplies with individual point alpha values (if set via setPointColors). Useful for dynamically controlling opacity of all points without updating individual RGBA arrays. | `1.0` |
17
17
  | pointSizeScale | Scale factor for the point size | `1` |
18
18
  | hoveredPointCursor | Cursor style to use when hovering over a point | `auto` |
19
+ | hoveredLinkCursor | Cursor style to use when hovering over a link | `auto` |
19
20
  | renderHoveredPointRing | Turns ring rendering around a point on hover on / off | `false` |
20
21
  | hoveredPointRingColor | Hovered point ring color hex value or an array of RGBA values | `white` |
21
22
  | focusedPointRingColor | Focused point ring color hex value or an array of RGBA values | `white` |
@@ -26,6 +27,8 @@ import { Meta } from "@storybook/blocks";
26
27
  | linkGreyoutOpacity | Greyed out link opacity value when the selection is active | `0.1` |
27
28
  | linkWidth | The default width value to use for links when no link widths are provided or if the width value in the array is `undefined` or `null` | `1` |
28
29
  | linkWidthScale | Scale factor for the link width | `1` |
30
+ | hoveredLinkColor | The color to use for links when they are hovered. This can be either a hex color string (e.g., '#ff3333') or an array of RGBA values in the format `[red, green, blue, alpha]` where each value is a number between 0 and 255 | `undefined` |
31
+ | hoveredLinkWidthIncrease | Number of pixels to add to the link width when hovered | `5` |
29
32
  | scaleLinksOnZoom | Increase/decrease link width when zooming | `false` |
30
33
  | curvedLinks | If set to true, links are rendered as curved lines. Otherwise as straight lines | `false` |
31
34
  | curvedLinkSegments | Number of segments in a curved line | `19` |
@@ -86,11 +89,17 @@ cosmos.gl layout algorithm was inspired by the [d3-force](https://github.com/d3/
86
89
  | onSimulationTick | Called on every simulation tick, with the current alpha value and hover information |
87
90
  | onSimulationEnd | Called when simulation stops |
88
91
  | onSimulationPause | Called when simulation pauses |
89
- | onSimulationRestart | Called when simulation restarts |
92
+ | onSimulationUnpause | Called when simulation unpauses |
93
+ | onSimulationRestart | **[DEPRECATED]** Called when simulation restarts. Use `onSimulationUnpause` instead |
90
94
  | onClick | Called on canvas click with point index and position |
95
+ | onPointClick | Called when a point is clicked |
96
+ | onLinkClick | Called when a link is clicked |
97
+ | onBackgroundClick | Called when the background (empty space) is clicked |
91
98
  | onMouseMove | Called on mouse movement with hover info |
92
99
  | onPointMouseOver | Called when pointer enters a point |
93
100
  | onPointMouseOut | Called when pointer leaves a point |
101
+ | onLinkMouseOver | Called when pointer enters a link |
102
+ | onLinkMouseOut | Called when pointer leaves a link |
94
103
  | onZoomStart | Called when zoom/pan starts |
95
104
  | onZoom | Called during zoom/pan |
96
105
  | onZoomEnd | Called when zoom/pan ends |
@@ -8,11 +8,14 @@ This method sets the [cosmos.gl configuration](../?path=/docs/configuration--doc
8
8
 
9
9
  * **`config`** (Object): The configuration object adhering to cosmos.gl configuration properties.
10
10
 
11
- ### <a name="set_point_positions" href="#set_point_positions">#</a> graph.<b>setPointPositions</b>(<i>pointPositions</i>)
11
+ ### <a name="set_point_positions" href="#set_point_positions">#</a> graph.<b>setPointPositions</b>(<i>pointPositions</i>, [<i>dontRescale</i>])
12
12
 
13
13
  This method sets the positions of points in a cosmos.gl graph using the provided coordinates array.
14
14
 
15
15
  * **`pointPositions`** (Float32Array): A Float32Array representing the x and y coordinates of points in the format `[x1, y1, x2, y2, ..., xN, yN]`. Each pair represents the coordinates of a single point.
16
+ * **`dontRescale`** (Boolean, optional): For this call only, controls whether to rescale the points.
17
+ - `true`: Don't rescale the points.
18
+ - `false` or `undefined` (default): Use the behavior defined by `config.rescalePositions`.
16
19
 
17
20
  **Example:**
18
21
  ```javascript
@@ -246,7 +249,7 @@ graph.render();
246
249
 
247
250
  In this example, the `linkArrows` array contains three boolean values. The first value `true` sets an arrow on the first link, the second value `false` leaves the second link without an arrow, and the third value `true` sets an arrow on the third link.
248
251
 
249
- ### <a name="set_link_strengths" href="#set_link_strengths">#</a> graph.<b>setLinkStrength</b>(<i>linkStrength</i>)
252
+ ### <a name="set_link_strength" href="#set_link_strength">#</a> graph.<b>setLinkStrength</b>(<i>linkStrength</i>)
250
253
 
251
254
  This method sets the strength of the graph links.
252
255
 
@@ -298,7 +301,7 @@ This method sets the force strength coefficients for clustering points in the gr
298
301
 
299
302
  The `render` method renders the graph and, optionally, controls the initial energy of the simulation.
300
303
 
301
- * **`runSimulation`** (number, optional): The higher the value, the more initial energy the simulation will get. Zero value stops the simulation.
304
+ * **`simulationAlpha`** (number, optional): The higher the value, the more initial energy the simulation will get. Zero value stops the simulation.
302
305
 
303
306
  ### <a name="zoom_to_point_by_index" href="#zoom_to_point_by_index">#</a> graph.<b>zoomToPointByIndex</b>(<i>index</i>, [<i>duration</i>], [<i>scale</i>], [<i>canZoomOut</i>])
304
307
 
@@ -508,7 +511,13 @@ Starts the simulation with an optional <i>alpha</i> parameter, which controls th
508
511
 
509
512
  Pauses the current simulation in the graph.
510
513
 
511
- ### <a name="restart" href="#restart">#</a> graph.<b>restart</b>()
514
+ ### <a name="unpause" href="#unpause">#</a> graph.<b>unpause</b>()
515
+
516
+ Unpauses (resumes) the current simulation in the graph.
517
+
518
+ ### <a name="restart" href="#restart">#</a> graph.<b>restart</b>() <b style={{ color: 'orange' }}>[DEPRECATED]</b>
519
+
520
+ **⚠️ Deprecated:** Use `unpause()` instead. This method will be removed in a future version.
512
521
 
513
522
  Restarts the current simulation in the graph.
514
523
 
@@ -38,15 +38,15 @@ export const basicSetUp = (): { graph: Graph; div: HTMLDivElement} => {
38
38
  simulationRepulsion: 0.2,
39
39
  simulationGravity: 0.1,
40
40
  simulationDecay: 100000,
41
- onClick: (index: number | undefined): void => {
42
- if (index !== undefined) {
43
- graph.selectPointByIndex(index)
44
- graph.zoomToPointByIndex(index)
45
- } else {
46
- graph.unselectPoints()
47
- }
41
+ onPointClick: (index: number): void => {
42
+ graph.selectPointByIndex(index)
43
+ graph.zoomToPointByIndex(index)
48
44
  console.log('Clicked point index: ', index)
49
45
  },
46
+ onBackgroundClick: (): void => {
47
+ graph.unselectPoints()
48
+ console.log('Clicked background')
49
+ },
50
50
  attribution: 'visualized with <a href="https://cosmograph.app/" style="color: var(--cosmosgl-attribution-color);" target="_blank">Cosmograph</a>',
51
51
  })
52
52
 
@@ -71,18 +71,28 @@ export const basicSetUp = (): { graph: Graph; div: HTMLDivElement} => {
71
71
  graph.pause()
72
72
  }
73
73
 
74
- function start (): void {
74
+ function unpause (): void {
75
75
  isPaused = false
76
76
  pauseButton.textContent = 'Pause'
77
- graph.start()
77
+ // if the graph is at 100% progress, start the graph
78
+ if (graph.progress === 1) {
79
+ graph.start()
80
+ } else {
81
+ graph.unpause()
82
+ }
78
83
  }
79
84
 
80
85
  function togglePause (): void {
81
- if (isPaused) start()
86
+ if (isPaused) unpause()
82
87
  else pause()
83
88
  }
84
89
 
85
90
  pauseButton.addEventListener('click', togglePause)
91
+ graph.setConfig({
92
+ onSimulationEnd: (): void => {
93
+ pause()
94
+ },
95
+ })
86
96
 
87
97
  // Zoom and Select
88
98
  function getRandomPointIndex (): number {