@cosmos.gl/graph 2.5.0 → 2.6.1

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 (181) hide show
  1. package/dist/config.d.ts +8 -2
  2. package/dist/helper.d.ts +2 -2
  3. package/dist/index.d.ts +21 -2
  4. package/dist/index.js +4950 -14357
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.min.js +1 -1678
  7. package/dist/index.min.js.map +1 -1
  8. package/dist/modules/Clusters/index.d.ts +2 -2
  9. package/dist/modules/Drag/index.d.ts +3 -3
  10. package/dist/modules/ForceCenter/index.d.ts +1 -1
  11. package/dist/modules/ForceGravity/index.d.ts +1 -1
  12. package/dist/modules/ForceLink/index.d.ts +1 -1
  13. package/dist/modules/ForceManyBody/index.d.ts +1 -1
  14. package/dist/modules/ForceManyBodyQuadtree/index.d.ts +1 -1
  15. package/dist/modules/ForceMouse/index.d.ts +1 -1
  16. package/dist/modules/GraphData/index.d.ts +2 -1
  17. package/dist/modules/Lines/index.d.ts +2 -2
  18. package/dist/modules/Points/index.d.ts +5 -2
  19. package/dist/modules/Shared/buffer.d.ts +1 -1
  20. package/dist/modules/Store/index.d.ts +1 -1
  21. package/dist/modules/Zoom/index.d.ts +3 -3
  22. package/dist/modules/core-module.d.ts +5 -5
  23. package/package.json +5 -1
  24. package/.eslintrc +0 -147
  25. package/.github/SECURITY.md +0 -13
  26. package/.github/dco.yml +0 -4
  27. package/.github/workflows/github_pages.yml +0 -54
  28. package/.storybook/main.ts +0 -26
  29. package/.storybook/manager-head.html +0 -1
  30. package/.storybook/manager.ts +0 -14
  31. package/.storybook/preview.ts +0 -29
  32. package/.storybook/style.css +0 -3
  33. package/CHARTER.md +0 -69
  34. package/CODE_OF_CONDUCT.md +0 -178
  35. package/CONTRIBUTING.md +0 -22
  36. package/GOVERNANCE.md +0 -21
  37. package/cosmos-2-0-migration-notes.md +0 -98
  38. package/cosmos_awesome.md +0 -96
  39. package/dist/stories/beginners/basic-set-up/data-gen.d.ts +0 -4
  40. package/dist/stories/beginners/basic-set-up/index.d.ts +0 -5
  41. package/dist/stories/beginners/link-hovering/data-generator.d.ts +0 -19
  42. package/dist/stories/beginners/link-hovering/index.d.ts +0 -5
  43. package/dist/stories/beginners/point-labels/data.d.ts +0 -13
  44. package/dist/stories/beginners/point-labels/index.d.ts +0 -9
  45. package/dist/stories/beginners/point-labels/labels.d.ts +0 -8
  46. package/dist/stories/beginners/quick-start.d.ts +0 -5
  47. package/dist/stories/beginners/remove-points/config.d.ts +0 -2
  48. package/dist/stories/beginners/remove-points/data-gen.d.ts +0 -4
  49. package/dist/stories/beginners/remove-points/index.d.ts +0 -5
  50. package/dist/stories/beginners.stories.d.ts +0 -10
  51. package/dist/stories/clusters/polygon-selection/index.d.ts +0 -6
  52. package/dist/stories/clusters/polygon-selection/polygon.d.ts +0 -20
  53. package/dist/stories/clusters/radial.d.ts +0 -5
  54. package/dist/stories/clusters/with-labels.d.ts +0 -6
  55. package/dist/stories/clusters/worm.d.ts +0 -5
  56. package/dist/stories/clusters.stories.d.ts +0 -9
  57. package/dist/stories/create-cluster-labels.d.ts +0 -4
  58. package/dist/stories/create-cosmos.d.ts +0 -16
  59. package/dist/stories/create-story.d.ts +0 -16
  60. package/dist/stories/experiments/full-mesh.d.ts +0 -5
  61. package/dist/stories/experiments/mesh-with-holes.d.ts +0 -5
  62. package/dist/stories/experiments.stories.d.ts +0 -7
  63. package/dist/stories/generate-mesh-data.d.ts +0 -12
  64. package/dist/stories/geospatial/moscow-metro-stations/index.d.ts +0 -15
  65. package/dist/stories/geospatial/moscow-metro-stations/moscow-metro-coords.d.ts +0 -1
  66. package/dist/stories/geospatial/moscow-metro-stations/point-colors.d.ts +0 -1
  67. package/dist/stories/geospatial.stories.d.ts +0 -6
  68. package/dist/stories/shapes/all-shapes/index.d.ts +0 -5
  69. package/dist/stories/shapes/image-example/index.d.ts +0 -5
  70. package/dist/stories/shapes.stories.d.ts +0 -7
  71. package/logo.svg +0 -3
  72. package/rollup.config.js +0 -70
  73. package/src/config.ts +0 -726
  74. package/src/declaration.d.ts +0 -12
  75. package/src/graph/utils/error-message.ts +0 -23
  76. package/src/helper.ts +0 -74
  77. package/src/index.ts +0 -1608
  78. package/src/modules/Clusters/calculate-centermass.frag +0 -9
  79. package/src/modules/Clusters/calculate-centermass.vert +0 -26
  80. package/src/modules/Clusters/force-cluster.frag +0 -39
  81. package/src/modules/Clusters/index.ts +0 -200
  82. package/src/modules/Drag/index.ts +0 -33
  83. package/src/modules/FPSMonitor/css.ts +0 -53
  84. package/src/modules/FPSMonitor/index.ts +0 -28
  85. package/src/modules/ForceCenter/calculate-centermass.frag +0 -9
  86. package/src/modules/ForceCenter/calculate-centermass.vert +0 -18
  87. package/src/modules/ForceCenter/force-center.frag +0 -27
  88. package/src/modules/ForceCenter/index.ts +0 -104
  89. package/src/modules/ForceGravity/force-gravity.frag +0 -27
  90. package/src/modules/ForceGravity/index.ts +0 -33
  91. package/src/modules/ForceLink/force-spring.ts +0 -73
  92. package/src/modules/ForceLink/index.ts +0 -149
  93. package/src/modules/ForceManyBody/calculate-level.frag +0 -9
  94. package/src/modules/ForceManyBody/calculate-level.vert +0 -25
  95. package/src/modules/ForceManyBody/force-centermass.frag +0 -52
  96. package/src/modules/ForceManyBody/force-level.frag +0 -121
  97. package/src/modules/ForceManyBody/index.ts +0 -223
  98. package/src/modules/ForceManyBody/quadtree-frag-shader.ts +0 -90
  99. package/src/modules/ForceManyBodyQuadtree/calculate-level.frag +0 -9
  100. package/src/modules/ForceManyBodyQuadtree/calculate-level.vert +0 -25
  101. package/src/modules/ForceManyBodyQuadtree/index.ts +0 -157
  102. package/src/modules/ForceManyBodyQuadtree/quadtree-frag-shader.ts +0 -93
  103. package/src/modules/ForceMouse/force-mouse.frag +0 -24
  104. package/src/modules/ForceMouse/index.ts +0 -32
  105. package/src/modules/GraphData/index.ts +0 -383
  106. package/src/modules/Lines/draw-curve-line.frag +0 -46
  107. package/src/modules/Lines/draw-curve-line.vert +0 -194
  108. package/src/modules/Lines/geometry.ts +0 -18
  109. package/src/modules/Lines/hovered-line-index.frag +0 -27
  110. package/src/modules/Lines/hovered-line-index.vert +0 -8
  111. package/src/modules/Lines/index.ts +0 -273
  112. package/src/modules/Points/atlas-utils.ts +0 -137
  113. package/src/modules/Points/drag-point.frag +0 -20
  114. package/src/modules/Points/draw-highlighted.frag +0 -16
  115. package/src/modules/Points/draw-highlighted.vert +0 -86
  116. package/src/modules/Points/draw-points.frag +0 -243
  117. package/src/modules/Points/draw-points.vert +0 -127
  118. package/src/modules/Points/fill-sampled-points.frag +0 -9
  119. package/src/modules/Points/fill-sampled-points.vert +0 -29
  120. package/src/modules/Points/find-hovered-point.frag +0 -9
  121. package/src/modules/Points/find-hovered-point.vert +0 -57
  122. package/src/modules/Points/find-points-on-area-selection.frag +0 -48
  123. package/src/modules/Points/find-points-on-polygon-selection.frag +0 -65
  124. package/src/modules/Points/index.ts +0 -934
  125. package/src/modules/Points/track-positions.frag +0 -18
  126. package/src/modules/Points/update-position.frag +0 -25
  127. package/src/modules/Shared/buffer.ts +0 -37
  128. package/src/modules/Shared/clear.frag +0 -7
  129. package/src/modules/Shared/quad.vert +0 -12
  130. package/src/modules/Store/index.ts +0 -173
  131. package/src/modules/Zoom/index.ts +0 -148
  132. package/src/modules/core-module.ts +0 -28
  133. package/src/stories/1. welcome.mdx +0 -75
  134. package/src/stories/2. configuration.mdx +0 -112
  135. package/src/stories/3. api-reference.mdx +0 -591
  136. package/src/stories/beginners/basic-set-up/data-gen.ts +0 -33
  137. package/src/stories/beginners/basic-set-up/index.ts +0 -163
  138. package/src/stories/beginners/basic-set-up/style.css +0 -35
  139. package/src/stories/beginners/link-hovering/data-generator.ts +0 -198
  140. package/src/stories/beginners/link-hovering/index.ts +0 -61
  141. package/src/stories/beginners/link-hovering/style.css +0 -73
  142. package/src/stories/beginners/point-labels/data.ts +0 -73
  143. package/src/stories/beginners/point-labels/index.ts +0 -65
  144. package/src/stories/beginners/point-labels/labels.ts +0 -46
  145. package/src/stories/beginners/point-labels/style.css +0 -16
  146. package/src/stories/beginners/quick-start.ts +0 -50
  147. package/src/stories/beginners/remove-points/config.ts +0 -25
  148. package/src/stories/beginners/remove-points/data-gen.ts +0 -30
  149. package/src/stories/beginners/remove-points/index.ts +0 -92
  150. package/src/stories/beginners/remove-points/style.css +0 -31
  151. package/src/stories/beginners.stories.ts +0 -117
  152. package/src/stories/clusters/polygon-selection/index.ts +0 -51
  153. package/src/stories/clusters/polygon-selection/polygon.ts +0 -143
  154. package/src/stories/clusters/polygon-selection/style.css +0 -8
  155. package/src/stories/clusters/radial.ts +0 -24
  156. package/src/stories/clusters/with-labels.ts +0 -53
  157. package/src/stories/clusters/worm.ts +0 -40
  158. package/src/stories/clusters.stories.ts +0 -77
  159. package/src/stories/create-cluster-labels.ts +0 -50
  160. package/src/stories/create-cosmos.ts +0 -68
  161. package/src/stories/create-story.ts +0 -51
  162. package/src/stories/experiments/full-mesh.ts +0 -13
  163. package/src/stories/experiments/mesh-with-holes.ts +0 -13
  164. package/src/stories/experiments.stories.ts +0 -43
  165. package/src/stories/generate-mesh-data.ts +0 -125
  166. package/src/stories/geospatial/moscow-metro-stations/index.ts +0 -62
  167. package/src/stories/geospatial/moscow-metro-stations/moscow-metro-coords.ts +0 -1
  168. package/src/stories/geospatial/moscow-metro-stations/point-colors.ts +0 -46
  169. package/src/stories/geospatial/moscow-metro-stations/style.css +0 -30
  170. package/src/stories/geospatial.stories.ts +0 -30
  171. package/src/stories/shapes/all-shapes/index.ts +0 -69
  172. package/src/stories/shapes/image-example/icons/box.png +0 -0
  173. package/src/stories/shapes/image-example/icons/lego.png +0 -0
  174. package/src/stories/shapes/image-example/icons/s.png +0 -0
  175. package/src/stories/shapes/image-example/icons/swift.png +0 -0
  176. package/src/stories/shapes/image-example/icons/toolbox.png +0 -0
  177. package/src/stories/shapes/image-example/index.ts +0 -238
  178. package/src/stories/shapes.stories.ts +0 -37
  179. package/src/variables.ts +0 -68
  180. package/tsconfig.json +0 -41
  181. package/vite.config.ts +0 -54
@@ -1,934 +0,0 @@
1
- import regl from 'regl'
2
- // import { scaleLinear } from 'd3-scale'
3
- // import { extent } from 'd3-array'
4
- import { CoreModule } from '@/graph/modules/core-module'
5
- import { defaultConfigValues } from '@/graph/variables'
6
- import drawPointsFrag from '@/graph/modules/Points/draw-points.frag'
7
- import drawPointsVert from '@/graph/modules/Points/draw-points.vert'
8
- import findPointsOnAreaSelectionFrag from '@/graph/modules/Points/find-points-on-area-selection.frag'
9
- import findPointsOnPolygonSelectionFrag from '@/graph/modules/Points/find-points-on-polygon-selection.frag'
10
- import drawHighlightedFrag from '@/graph/modules/Points/draw-highlighted.frag'
11
- import drawHighlightedVert from '@/graph/modules/Points/draw-highlighted.vert'
12
- import findHoveredPointFrag from '@/graph/modules/Points/find-hovered-point.frag'
13
- import findHoveredPointVert from '@/graph/modules/Points/find-hovered-point.vert'
14
- import fillGridWithSampledPointsFrag from '@/graph/modules/Points/fill-sampled-points.frag'
15
- import fillGridWithSampledPointsVert from '@/graph/modules/Points/fill-sampled-points.vert'
16
- import updatePositionFrag from '@/graph/modules/Points/update-position.frag'
17
- import { createIndexesForBuffer, createQuadBuffer } from '@/graph/modules/Shared/buffer'
18
- import trackPositionsFrag from '@/graph/modules/Points/track-positions.frag'
19
- import dragPointFrag from '@/graph/modules/Points/drag-point.frag'
20
- import updateVert from '@/graph/modules/Shared/quad.vert'
21
- import clearFrag from '@/graph/modules/Shared/clear.frag'
22
- import { readPixels } from '@/graph/helper'
23
- import { createAtlasDataFromImageData } from '@/graph/modules/Points/atlas-utils'
24
-
25
- export class Points extends CoreModule {
26
- public currentPositionFbo: regl.Framebuffer2D | undefined
27
- public previousPositionFbo: regl.Framebuffer2D | undefined
28
- public velocityFbo: regl.Framebuffer2D | undefined
29
- public selectedFbo: regl.Framebuffer2D | undefined
30
- public hoveredFbo: regl.Framebuffer2D | undefined
31
- public greyoutStatusFbo: regl.Framebuffer2D | undefined
32
- public scaleX: ((x: number) => number) | undefined
33
- public scaleY: ((y: number) => number) | undefined
34
- public shouldSkipRescale: boolean | undefined
35
- public imageAtlasTexture: regl.Texture2D | undefined
36
- public imageCount = 0
37
- private colorBuffer: regl.Buffer | undefined
38
- private sizeFbo: regl.Framebuffer2D | undefined
39
- private sizeBuffer: regl.Buffer | undefined
40
- private shapeBuffer: regl.Buffer | undefined
41
- private imageIndicesBuffer: regl.Buffer | undefined
42
- private imageSizesBuffer: regl.Buffer | undefined
43
- private imageAtlasCoordsTexture: regl.Texture2D | undefined
44
- private imageAtlasCoordsTextureSize: number | undefined
45
- private trackedIndicesFbo: regl.Framebuffer2D | undefined
46
- private trackedPositionsFbo: regl.Framebuffer2D | undefined
47
- private sampledPointsFbo: regl.Framebuffer2D | undefined
48
- private trackedPositions: Map<number, [number, number]> | undefined
49
- private isPositionsUpToDate = false
50
- private drawCommand: regl.DrawCommand | undefined
51
- private drawHighlightedCommand: regl.DrawCommand | undefined
52
- private updatePositionCommand: regl.DrawCommand | undefined
53
- private dragPointCommand: regl.DrawCommand | undefined
54
- private findPointsOnAreaSelectionCommand: regl.DrawCommand | undefined
55
- private findPointsOnPolygonSelectionCommand: regl.DrawCommand | undefined
56
- private findHoveredPointCommand: regl.DrawCommand | undefined
57
- private clearHoveredFboCommand: regl.DrawCommand | undefined
58
- private clearSampledPointsFboCommand: regl.DrawCommand | undefined
59
- private fillSampledPointsFboCommand: regl.DrawCommand | undefined
60
- private trackPointsCommand: regl.DrawCommand | undefined
61
- private trackedIndices: number[] | undefined
62
- private selectedTexture: regl.Texture2D | undefined
63
- private greyoutStatusTexture: regl.Texture2D | undefined
64
- private sizeTexture: regl.Texture2D | undefined
65
- private trackedIndicesTexture: regl.Texture2D | undefined
66
- private polygonPathTexture: regl.Texture2D | undefined
67
- private polygonPathFbo: regl.Framebuffer2D | undefined
68
- private polygonPathLength = 0
69
- private drawPointIndices: regl.Buffer | undefined
70
- private hoveredPointIndices: regl.Buffer | undefined
71
- private sampledPointIndices: regl.Buffer | undefined
72
-
73
- public updatePositions (): void {
74
- const { reglInstance, store, data, config: { rescalePositions, enableSimulation } } = this
75
-
76
- const { pointsTextureSize } = store
77
- if (!pointsTextureSize || !data.pointPositions || data.pointsNumber === undefined) return
78
-
79
- const initialState = new Float32Array(pointsTextureSize * pointsTextureSize * 4)
80
-
81
- let shouldRescale = rescalePositions
82
- // If rescalePositions isn't specified in config and simulation is disabled, default to true
83
- if (rescalePositions === undefined && !enableSimulation) shouldRescale = true
84
- // Skip rescaling if `shouldSkipRescale` flag is set (allowing one-time skip of rescaling)
85
- // Temporary flag is used to skip rescaling when change point positions or adding new points by function `setPointPositions`
86
- // This flag overrides any other rescaling settings
87
- if (this.shouldSkipRescale) shouldRescale = false
88
-
89
- if (shouldRescale) {
90
- this.rescaleInitialNodePositions()
91
- } else if (!this.shouldSkipRescale) {
92
- // Only reset scale functions if not temporarily skipping rescale
93
- this.scaleX = undefined
94
- this.scaleY = undefined
95
- }
96
-
97
- // Reset temporary flag
98
- this.shouldSkipRescale = undefined
99
-
100
- for (let i = 0; i < data.pointsNumber; ++i) {
101
- initialState[i * 4 + 0] = data.pointPositions[i * 2 + 0] as number
102
- initialState[i * 4 + 1] = data.pointPositions[i * 2 + 1] as number
103
- initialState[i * 4 + 2] = i
104
- }
105
-
106
- // Create position buffer
107
- if (!this.currentPositionFbo) this.currentPositionFbo = reglInstance.framebuffer()
108
- this.currentPositionFbo({
109
- color: reglInstance.texture({
110
- data: initialState,
111
- shape: [pointsTextureSize, pointsTextureSize, 4],
112
- type: 'float',
113
- }),
114
- depth: false,
115
- stencil: false,
116
- })
117
-
118
- if (!this.previousPositionFbo) this.previousPositionFbo = reglInstance.framebuffer()
119
- this.previousPositionFbo({
120
- color: reglInstance.texture({
121
- data: initialState,
122
- shape: [pointsTextureSize, pointsTextureSize, 4],
123
- type: 'float',
124
- }),
125
- depth: false,
126
- stencil: false,
127
- })
128
-
129
- if (this.config.enableSimulation) {
130
- // Create velocity buffer
131
- if (!this.velocityFbo) this.velocityFbo = reglInstance.framebuffer()
132
- this.velocityFbo({
133
- color: reglInstance.texture({
134
- data: new Float32Array(pointsTextureSize * pointsTextureSize * 4).fill(0),
135
- shape: [pointsTextureSize, pointsTextureSize, 4],
136
- type: 'float',
137
- }),
138
- depth: false,
139
- stencil: false,
140
- })
141
- }
142
-
143
- // Create selected points buffer
144
- if (!this.selectedTexture) this.selectedTexture = reglInstance.texture()
145
- this.selectedTexture({
146
- data: initialState,
147
- shape: [pointsTextureSize, pointsTextureSize, 4],
148
- type: 'float',
149
- })
150
- if (!this.selectedFbo) this.selectedFbo = reglInstance.framebuffer()
151
- this.selectedFbo({
152
- color: this.selectedTexture,
153
- depth: false,
154
- stencil: false,
155
- })
156
-
157
- if (!this.hoveredFbo) this.hoveredFbo = reglInstance.framebuffer()
158
- this.hoveredFbo({
159
- shape: [2, 2],
160
- colorType: 'float',
161
- depth: false,
162
- stencil: false,
163
- })
164
-
165
- if (!this.drawPointIndices) this.drawPointIndices = reglInstance.buffer(0)
166
- this.drawPointIndices(createIndexesForBuffer(store.pointsTextureSize))
167
-
168
- if (!this.hoveredPointIndices) this.hoveredPointIndices = reglInstance.buffer(0)
169
- this.hoveredPointIndices(createIndexesForBuffer(store.pointsTextureSize))
170
-
171
- if (!this.sampledPointIndices) this.sampledPointIndices = reglInstance.buffer(0)
172
- this.sampledPointIndices(createIndexesForBuffer(store.pointsTextureSize))
173
-
174
- this.updateGreyoutStatus()
175
- this.updateSampledPointsGrid()
176
-
177
- this.trackPointsByIndices()
178
- }
179
-
180
- public initPrograms (): void {
181
- const { reglInstance, config, store, data } = this
182
- if (config.enableSimulation) {
183
- if (!this.updatePositionCommand) {
184
- this.updatePositionCommand = reglInstance({
185
- frag: updatePositionFrag,
186
- vert: updateVert,
187
- framebuffer: () => this.currentPositionFbo as regl.Framebuffer2D,
188
- primitive: 'triangle strip',
189
- count: 4,
190
- attributes: { vertexCoord: createQuadBuffer(reglInstance) },
191
- uniforms: {
192
- positionsTexture: () => this.previousPositionFbo,
193
- velocity: () => this.velocityFbo,
194
- friction: () => config.simulationFriction,
195
- spaceSize: () => store.adjustedSpaceSize,
196
- },
197
- })
198
- }
199
- }
200
- if (!this.dragPointCommand) {
201
- this.dragPointCommand = reglInstance({
202
- frag: dragPointFrag,
203
- vert: updateVert,
204
- framebuffer: () => this.currentPositionFbo as regl.Framebuffer2D,
205
- primitive: 'triangle strip',
206
- count: 4,
207
- attributes: { vertexCoord: createQuadBuffer(reglInstance) },
208
- uniforms: {
209
- positionsTexture: () => this.previousPositionFbo,
210
- mousePos: () => store.mousePosition,
211
- index: () => store.hoveredPoint?.index ?? -1,
212
- },
213
- })
214
- }
215
-
216
- if (!this.drawCommand) {
217
- this.drawCommand = reglInstance({
218
- frag: drawPointsFrag,
219
- vert: drawPointsVert,
220
- primitive: 'points',
221
- count: () => data.pointsNumber ?? 0,
222
- attributes: {
223
- pointIndices: {
224
- buffer: this.drawPointIndices,
225
- size: 2,
226
- },
227
- size: {
228
- buffer: () => this.sizeBuffer,
229
- size: 1,
230
- },
231
- color: {
232
- buffer: () => this.colorBuffer,
233
- size: 4,
234
- },
235
- shape: {
236
- buffer: () => this.shapeBuffer,
237
- size: 1,
238
- },
239
- imageIndex: {
240
- buffer: () => this.imageIndicesBuffer,
241
- size: 1,
242
- },
243
- imageSize: {
244
- buffer: () => this.imageSizesBuffer,
245
- size: 1,
246
- },
247
- },
248
- uniforms: {
249
- positionsTexture: () => this.currentPositionFbo,
250
- pointGreyoutStatus: () => this.greyoutStatusFbo,
251
- ratio: () => config.pixelRatio,
252
- sizeScale: () => config.pointSizeScale,
253
- pointsTextureSize: () => store.pointsTextureSize,
254
- transformationMatrix: () => store.transform,
255
- spaceSize: () => store.adjustedSpaceSize,
256
- screenSize: () => store.screenSize,
257
- pointOpacity: () => config.pointOpacity,
258
- greyoutOpacity: () => config.pointGreyoutOpacity ?? -1,
259
- greyoutColor: () => store.greyoutPointColor,
260
- backgroundColor: () => store.backgroundColor,
261
- isDarkenGreyout: () => store.isDarkenGreyout,
262
- scalePointsOnZoom: () => config.scalePointsOnZoom,
263
- maxPointSize: () => store.maxPointSize,
264
- skipSelected: reglInstance.prop<{ skipSelected: boolean }, 'skipSelected'>('skipSelected'),
265
- skipUnselected: reglInstance.prop<{ skipUnselected: boolean }, 'skipUnselected'>('skipUnselected'),
266
- imageAtlasTexture: () => this.imageAtlasTexture,
267
- imageAtlasCoords: () => this.imageAtlasCoordsTexture,
268
- hasImages: () => this.imageCount > 0,
269
- imageCount: () => this.imageCount,
270
- imageAtlasCoordsTextureSize: () => this.imageAtlasCoordsTextureSize,
271
- },
272
- blend: {
273
- enable: true,
274
- func: {
275
- dstRGB: 'one minus src alpha',
276
- srcRGB: 'src alpha',
277
- dstAlpha: 'one minus src alpha',
278
- srcAlpha: 'one',
279
- },
280
- equation: {
281
- rgb: 'add',
282
- alpha: 'add',
283
- },
284
- },
285
- depth: {
286
- enable: false,
287
- mask: false,
288
- },
289
- })
290
- }
291
-
292
- if (!this.findPointsOnAreaSelectionCommand) {
293
- this.findPointsOnAreaSelectionCommand = reglInstance({
294
- frag: findPointsOnAreaSelectionFrag,
295
- vert: updateVert,
296
- framebuffer: () => this.selectedFbo as regl.Framebuffer2D,
297
- primitive: 'triangle strip',
298
- count: 4,
299
- attributes: {
300
- vertexCoord: createQuadBuffer(reglInstance),
301
- },
302
- uniforms: {
303
- positionsTexture: () => this.currentPositionFbo,
304
- pointSize: () => this.sizeFbo,
305
- spaceSize: () => store.adjustedSpaceSize,
306
- screenSize: () => store.screenSize,
307
- sizeScale: () => config.pointSizeScale,
308
- transformationMatrix: () => store.transform,
309
- ratio: () => config.pixelRatio,
310
- selection0: () => store.selectedArea[0],
311
- selection1: () => store.selectedArea[1],
312
- scalePointsOnZoom: () => config.scalePointsOnZoom,
313
- maxPointSize: () => store.maxPointSize,
314
- },
315
- })
316
- }
317
-
318
- if (!this.findPointsOnPolygonSelectionCommand) {
319
- this.findPointsOnPolygonSelectionCommand = reglInstance({
320
- frag: findPointsOnPolygonSelectionFrag,
321
- vert: updateVert,
322
- framebuffer: () => this.selectedFbo as regl.Framebuffer2D,
323
- primitive: 'triangle strip',
324
- count: 4,
325
- attributes: {
326
- vertexCoord: createQuadBuffer(reglInstance),
327
- },
328
- uniforms: {
329
- positionsTexture: () => this.currentPositionFbo,
330
- spaceSize: () => store.adjustedSpaceSize,
331
- screenSize: () => store.screenSize,
332
- transformationMatrix: () => store.transform,
333
- polygonPathTexture: () => this.polygonPathTexture,
334
- polygonPathLength: () => this.polygonPathLength,
335
- },
336
- })
337
- }
338
-
339
- if (!this.clearHoveredFboCommand) {
340
- this.clearHoveredFboCommand = reglInstance({
341
- frag: clearFrag,
342
- vert: updateVert,
343
- framebuffer: this.hoveredFbo as regl.Framebuffer2D,
344
- primitive: 'triangle strip',
345
- count: 4,
346
- attributes: { vertexCoord: createQuadBuffer(reglInstance) },
347
- })
348
- }
349
-
350
- if (!this.findHoveredPointCommand) {
351
- this.findHoveredPointCommand = reglInstance({
352
- frag: findHoveredPointFrag,
353
- vert: findHoveredPointVert,
354
- primitive: 'points',
355
- count: () => data.pointsNumber ?? 0,
356
- framebuffer: () => this.hoveredFbo as regl.Framebuffer2D,
357
- attributes: {
358
- pointIndices: {
359
- buffer: this.hoveredPointIndices,
360
- size: 2,
361
- },
362
- size: {
363
- buffer: () => this.sizeBuffer,
364
- size: 1,
365
- },
366
- },
367
- uniforms: {
368
- positionsTexture: () => this.currentPositionFbo,
369
- ratio: () => config.pixelRatio,
370
- sizeScale: () => config.pointSizeScale,
371
- pointsTextureSize: () => store.pointsTextureSize,
372
- transformationMatrix: () => store.transform,
373
- spaceSize: () => store.adjustedSpaceSize,
374
- screenSize: () => store.screenSize,
375
- scalePointsOnZoom: () => config.scalePointsOnZoom,
376
- mousePosition: () => store.screenMousePosition,
377
- maxPointSize: () => store.maxPointSize,
378
- },
379
- depth: {
380
- enable: false,
381
- mask: false,
382
- },
383
- })
384
- }
385
-
386
- if (!this.clearSampledPointsFboCommand) {
387
- this.clearSampledPointsFboCommand = reglInstance({
388
- frag: clearFrag,
389
- vert: updateVert,
390
- framebuffer: () => this.sampledPointsFbo as regl.Framebuffer2D,
391
- primitive: 'triangle strip',
392
- count: 4,
393
- attributes: { vertexCoord: createQuadBuffer(reglInstance) },
394
- })
395
- }
396
-
397
- if (!this.fillSampledPointsFboCommand) {
398
- this.fillSampledPointsFboCommand = reglInstance({
399
- frag: fillGridWithSampledPointsFrag,
400
- vert: fillGridWithSampledPointsVert,
401
- primitive: 'points',
402
- count: () => data.pointsNumber ?? 0,
403
- framebuffer: () => this.sampledPointsFbo as regl.Framebuffer2D,
404
- attributes: {
405
- pointIndices: {
406
- buffer: this.sampledPointIndices,
407
- size: 2,
408
- },
409
- },
410
- uniforms: {
411
- positionsTexture: () => this.currentPositionFbo,
412
- pointsTextureSize: () => store.pointsTextureSize,
413
- transformationMatrix: () => store.transform,
414
- spaceSize: () => store.adjustedSpaceSize,
415
- screenSize: () => store.screenSize,
416
- },
417
- depth: {
418
- enable: false,
419
- mask: false,
420
- },
421
- })
422
- }
423
-
424
- if (!this.drawHighlightedCommand) {
425
- this.drawHighlightedCommand = reglInstance({
426
- frag: drawHighlightedFrag,
427
- vert: drawHighlightedVert,
428
- attributes: { vertexCoord: createQuadBuffer(reglInstance) },
429
- primitive: 'triangle strip',
430
- count: 4,
431
- uniforms: {
432
- color: reglInstance.prop<{ color: number[] }, 'color'>('color'),
433
- width: reglInstance.prop<{ width: number }, 'width'>('width'),
434
- pointIndex: reglInstance.prop<{ pointIndex: number }, 'pointIndex'>('pointIndex'),
435
- size: reglInstance.prop<{ size: number }, 'size'>('size'),
436
- positionsTexture: () => this.currentPositionFbo,
437
- sizeScale: () => config.pointSizeScale,
438
- pointsTextureSize: () => store.pointsTextureSize,
439
- transformationMatrix: () => store.transform,
440
- spaceSize: () => store.adjustedSpaceSize,
441
- screenSize: () => store.screenSize,
442
- scalePointsOnZoom: () => config.scalePointsOnZoom,
443
- maxPointSize: () => store.maxPointSize,
444
- pointGreyoutStatusTexture: () => this.greyoutStatusFbo,
445
- universalPointOpacity: () => config.pointOpacity,
446
- greyoutOpacity: () => config.pointGreyoutOpacity ?? -1,
447
- isDarkenGreyout: () => store.isDarkenGreyout,
448
- backgroundColor: () => store.backgroundColor,
449
- greyoutColor: () => store.greyoutPointColor,
450
- },
451
- blend: {
452
- enable: true,
453
- func: {
454
- dstRGB: 'one minus src alpha',
455
- srcRGB: 'src alpha',
456
- dstAlpha: 'one minus src alpha',
457
- srcAlpha: 'one',
458
- },
459
- equation: {
460
- rgb: 'add',
461
- alpha: 'add',
462
- },
463
- },
464
- depth: {
465
- enable: false,
466
- mask: false,
467
- },
468
- })
469
- }
470
-
471
- if (!this.trackPointsCommand) {
472
- this.trackPointsCommand = reglInstance({
473
- frag: trackPositionsFrag,
474
- vert: updateVert,
475
- framebuffer: () => this.trackedPositionsFbo as regl.Framebuffer2D,
476
- primitive: 'triangle strip',
477
- count: 4,
478
- attributes: { vertexCoord: createQuadBuffer(reglInstance) },
479
- uniforms: {
480
- positionsTexture: () => this.currentPositionFbo,
481
- trackedIndices: () => this.trackedIndicesFbo,
482
- pointsTextureSize: () => store.pointsTextureSize,
483
- },
484
- })
485
- }
486
- }
487
-
488
- public updateColor (): void {
489
- const { reglInstance, store: { pointsTextureSize }, data } = this
490
- if (!pointsTextureSize) return
491
- if (!this.colorBuffer) this.colorBuffer = reglInstance.buffer(0)
492
- this.colorBuffer(data.pointColors as Float32Array)
493
- }
494
-
495
- public updateGreyoutStatus (): void {
496
- const { reglInstance, store: { selectedIndices, pointsTextureSize } } = this
497
- if (!pointsTextureSize) return
498
-
499
- // Greyout status: 0 - false, highlighted or normal point; 1 - true, greyout point
500
- const initialState = new Float32Array(pointsTextureSize * pointsTextureSize * 4)
501
- .fill(selectedIndices ? 1 : 0)
502
-
503
- if (selectedIndices) {
504
- for (const selectedIndex of selectedIndices) {
505
- initialState[selectedIndex * 4] = 0
506
- }
507
- }
508
- if (!this.greyoutStatusTexture) this.greyoutStatusTexture = reglInstance.texture()
509
- this.greyoutStatusTexture({
510
- data: initialState,
511
- width: pointsTextureSize,
512
- height: pointsTextureSize,
513
- type: 'float',
514
- })
515
- if (!this.greyoutStatusFbo) this.greyoutStatusFbo = reglInstance.framebuffer()
516
- this.greyoutStatusFbo({
517
- color: this.greyoutStatusTexture,
518
- depth: false,
519
- stencil: false,
520
- })
521
- }
522
-
523
- public updateSize (): void {
524
- const { reglInstance, store: { pointsTextureSize }, data } = this
525
- if (!pointsTextureSize || data.pointsNumber === undefined || data.pointSizes === undefined) return
526
- if (!this.sizeBuffer) this.sizeBuffer = reglInstance.buffer(0)
527
- this.sizeBuffer(data.pointSizes)
528
-
529
- const initialState = new Float32Array(pointsTextureSize * pointsTextureSize * 4)
530
- for (let i = 0; i < data.pointsNumber; i++) {
531
- initialState[i * 4] = data.pointSizes[i] as number
532
- }
533
-
534
- if (!this.sizeTexture) this.sizeTexture = reglInstance.texture()
535
- this.sizeTexture({
536
- data: initialState,
537
- width: pointsTextureSize,
538
- height: pointsTextureSize,
539
- type: 'float',
540
- })
541
-
542
- if (!this.sizeFbo) this.sizeFbo = reglInstance.framebuffer()
543
- this.sizeFbo({
544
- color: this.sizeTexture,
545
- depth: false,
546
- stencil: false,
547
- })
548
- }
549
-
550
- public updateShape (): void {
551
- const { reglInstance, data } = this
552
- if (data.pointsNumber === undefined || data.pointShapes === undefined) return
553
- if (!this.shapeBuffer) this.shapeBuffer = reglInstance.buffer(0)
554
- this.shapeBuffer(data.pointShapes)
555
- }
556
-
557
- public updateImageIndices (): void {
558
- const { reglInstance, data } = this
559
- if (data.pointsNumber === undefined || data.pointImageIndices === undefined) return
560
- if (!this.imageIndicesBuffer) this.imageIndicesBuffer = reglInstance.buffer(0)
561
- this.imageIndicesBuffer(data.pointImageIndices)
562
- }
563
-
564
- public updateImageSizes (): void {
565
- const { reglInstance, data } = this
566
- if (data.pointsNumber === undefined || data.pointImageSizes === undefined) return
567
- if (!this.imageSizesBuffer) this.imageSizesBuffer = reglInstance.buffer(0)
568
- this.imageSizesBuffer(data.pointImageSizes)
569
- }
570
-
571
- public createAtlas (): void {
572
- const { reglInstance, data, store } = this
573
- if (!this.imageAtlasTexture) this.imageAtlasTexture = reglInstance.texture()
574
- if (!this.imageAtlasCoordsTexture) this.imageAtlasCoordsTexture = reglInstance.texture()
575
-
576
- if (!data.inputImageData?.length) {
577
- this.imageCount = 0
578
- this.imageAtlasCoordsTextureSize = 0
579
- return
580
- }
581
-
582
- const atlasResult = createAtlasDataFromImageData(data.inputImageData, store.webglMaxTextureSize)
583
- if (!atlasResult) {
584
- console.warn('Failed to create atlas from image data')
585
- return
586
- }
587
-
588
- this.imageCount = data.inputImageData.length
589
- const { atlasData, atlasSize, atlasCoords, atlasCoordsSize } = atlasResult
590
- this.imageAtlasCoordsTextureSize = atlasCoordsSize
591
-
592
- this.imageAtlasTexture({
593
- data: atlasData,
594
- shape: [atlasSize, atlasSize, 4],
595
- type: 'uint8',
596
- })
597
-
598
- this.imageAtlasCoordsTexture({
599
- data: atlasCoords,
600
- shape: [atlasCoordsSize, atlasCoordsSize, 4],
601
- type: 'float',
602
- })
603
- }
604
-
605
- public updateSampledPointsGrid (): void {
606
- const { store: { screenSize }, config: { pointSamplingDistance }, reglInstance } = this
607
- let dist = pointSamplingDistance ?? Math.min(...screenSize) / 2
608
- if (dist === 0) dist = defaultConfigValues.pointSamplingDistance
609
- const w = Math.ceil(screenSize[0] / dist)
610
- const h = Math.ceil(screenSize[1] / dist)
611
- if (!this.sampledPointsFbo) this.sampledPointsFbo = reglInstance.framebuffer()
612
- this.sampledPointsFbo({
613
- shape: [w, h],
614
- depth: false,
615
- stencil: false,
616
- colorType: 'float',
617
- })
618
- }
619
-
620
- public trackPoints (): void {
621
- if (!this.trackedIndices?.length) return
622
- this.trackPointsCommand?.()
623
- }
624
-
625
- public draw (): void {
626
- const { config: { renderHoveredPointRing, pointSize }, store, data } = this
627
- if (!this.colorBuffer) this.updateColor()
628
- if (!this.sizeBuffer) this.updateSize()
629
- if (!this.shapeBuffer) this.updateShape()
630
- if (!this.imageIndicesBuffer) this.updateImageIndices()
631
- if (!this.imageSizesBuffer) this.updateImageSizes()
632
- if (!this.imageAtlasCoordsTexture || !this.imageAtlasTexture) this.createAtlas()
633
-
634
- // Render in layers: unselected points first (behind), then selected points (in front)
635
- if (store.selectedIndices && store.selectedIndices.length > 0) {
636
- // First draw unselected points (they will appear behind)
637
- this.drawCommand?.({ skipSelected: true, skipUnselected: false })
638
- // Then draw selected points (they will appear in front)
639
- this.drawCommand?.({ skipSelected: false, skipUnselected: true })
640
- } else {
641
- // If no selection, draw all points
642
- this.drawCommand?.({ skipSelected: false, skipUnselected: false })
643
- }
644
- if ((renderHoveredPointRing) && store.hoveredPoint) {
645
- this.drawHighlightedCommand?.({
646
- width: 0.85,
647
- color: store.hoveredPointRingColor,
648
- pointIndex: store.hoveredPoint.index,
649
- size: data.pointSizes?.[store.hoveredPoint.index] ?? pointSize,
650
- })
651
- }
652
- if (store.focusedPoint) {
653
- this.drawHighlightedCommand?.({
654
- width: 0.75,
655
- color: store.focusedPointRingColor,
656
- pointIndex: store.focusedPoint.index,
657
- size: data.pointSizes?.[store.focusedPoint.index] ?? pointSize,
658
- })
659
- }
660
- }
661
-
662
- public updatePosition (): void {
663
- this.updatePositionCommand?.()
664
- this.swapFbo()
665
- // Invalidate tracked positions cache since positions have changed
666
- this.isPositionsUpToDate = false
667
- }
668
-
669
- public drag (): void {
670
- this.dragPointCommand?.()
671
- this.swapFbo()
672
- // Invalidate tracked positions cache since positions have changed
673
- this.isPositionsUpToDate = false
674
- }
675
-
676
- public findPointsOnAreaSelection (): void {
677
- this.findPointsOnAreaSelectionCommand?.()
678
- }
679
-
680
- public findPointsOnPolygonSelection (): void {
681
- this.findPointsOnPolygonSelectionCommand?.()
682
- }
683
-
684
- public updatePolygonPath (polygonPath: [number, number][]): void {
685
- const { reglInstance } = this
686
- this.polygonPathLength = polygonPath.length
687
-
688
- if (polygonPath.length === 0) {
689
- this.polygonPathTexture = undefined
690
- this.polygonPathFbo = undefined
691
- return
692
- }
693
-
694
- // Calculate texture size (square texture)
695
- const textureSize = Math.ceil(Math.sqrt(polygonPath.length))
696
- const textureData = new Float32Array(textureSize * textureSize * 4)
697
-
698
- // Fill texture with polygon path points
699
- for (const [i, point] of polygonPath.entries()) {
700
- const [x, y] = point
701
- textureData[i * 4] = x
702
- textureData[i * 4 + 1] = y
703
- textureData[i * 4 + 2] = 0 // unused
704
- textureData[i * 4 + 3] = 0 // unused
705
- }
706
-
707
- if (!this.polygonPathTexture) this.polygonPathTexture = reglInstance.texture()
708
- this.polygonPathTexture({
709
- data: textureData,
710
- width: textureSize,
711
- height: textureSize,
712
- type: 'float',
713
- })
714
-
715
- if (!this.polygonPathFbo) this.polygonPathFbo = reglInstance.framebuffer()
716
- this.polygonPathFbo({
717
- color: this.polygonPathTexture,
718
- depth: false,
719
- stencil: false,
720
- })
721
- }
722
-
723
- public findHoveredPoint (): void {
724
- this.clearHoveredFboCommand?.()
725
- this.findHoveredPointCommand?.()
726
- }
727
-
728
- public trackPointsByIndices (indices?: number[] | undefined): void {
729
- const { store: { pointsTextureSize }, reglInstance } = this
730
- this.trackedIndices = indices
731
-
732
- // Clear cache when changing tracked indices
733
- this.trackedPositions = undefined
734
- this.isPositionsUpToDate = false
735
-
736
- if (!indices?.length || !pointsTextureSize) return
737
- const textureSize = Math.ceil(Math.sqrt(indices.length))
738
-
739
- const initialState = new Float32Array(textureSize * textureSize * 4).fill(-1)
740
- for (const [i, sortedIndex] of indices.entries()) {
741
- if (sortedIndex !== undefined) {
742
- initialState[i * 4] = sortedIndex % pointsTextureSize
743
- initialState[i * 4 + 1] = Math.floor(sortedIndex / pointsTextureSize)
744
- initialState[i * 4 + 2] = 0
745
- initialState[i * 4 + 3] = 0
746
- }
747
- }
748
- if (!this.trackedIndicesTexture) this.trackedIndicesTexture = reglInstance.texture()
749
- this.trackedIndicesTexture({
750
- data: initialState,
751
- width: textureSize,
752
- height: textureSize,
753
- type: 'float',
754
- })
755
- if (!this.trackedIndicesFbo) this.trackedIndicesFbo = reglInstance.framebuffer()
756
- this.trackedIndicesFbo({
757
- color: this.trackedIndicesTexture,
758
- depth: false,
759
- stencil: false,
760
- })
761
-
762
- if (!this.trackedPositionsFbo) this.trackedPositionsFbo = reglInstance.framebuffer()
763
- this.trackedPositionsFbo({
764
- shape: [textureSize, textureSize],
765
- depth: false,
766
- stencil: false,
767
- colorType: 'float',
768
- })
769
-
770
- this.trackPoints()
771
- }
772
-
773
- /**
774
- * Get current X and Y coordinates of the tracked points.
775
- *
776
- * When the simulation is disabled or stopped, this method returns a cached
777
- * result to avoid expensive GPU-to-CPU memory transfers (`readPixels`).
778
- *
779
- * @returns A ReadonlyMap where keys are point indices and values are [x, y] coordinates.
780
- */
781
- public getTrackedPositionsMap (): ReadonlyMap<number, [number, number]> {
782
- if (!this.trackedIndices) return new Map()
783
-
784
- const { config: { enableSimulation }, store: { isSimulationRunning } } = this
785
-
786
- // Use cached positions when simulation is inactive and cache is valid
787
- if ((!enableSimulation || !isSimulationRunning) &&
788
- this.isPositionsUpToDate &&
789
- this.trackedPositions) {
790
- return this.trackedPositions
791
- }
792
-
793
- const pixels = readPixels(this.reglInstance, this.trackedPositionsFbo as regl.Framebuffer2D)
794
-
795
- const tracked = new Map<number, [number, number]>()
796
- for (let i = 0; i < pixels.length / 4; i += 1) {
797
- const x = pixels[i * 4]
798
- const y = pixels[i * 4 + 1]
799
- const index = this.trackedIndices[i]
800
- if (x !== undefined && y !== undefined && index !== undefined) {
801
- tracked.set(index, [x, y])
802
- }
803
- }
804
-
805
- // If simulation is inactive, cache the result for next time
806
- if (!enableSimulation || !isSimulationRunning) {
807
- this.trackedPositions = tracked
808
- this.isPositionsUpToDate = true
809
- }
810
-
811
- return tracked
812
- }
813
-
814
- public getSampledPointPositionsMap (): Map<number, [number, number]> {
815
- const positions = new Map<number, [number, number]>()
816
- if (!this.sampledPointsFbo) return positions
817
- this.clearSampledPointsFboCommand?.()
818
- this.fillSampledPointsFboCommand?.()
819
- const pixels = readPixels(this.reglInstance, this.sampledPointsFbo as regl.Framebuffer2D)
820
- for (let i = 0; i < pixels.length / 4; i++) {
821
- const index = pixels[i * 4]
822
- const isNotEmpty = !!pixels[i * 4 + 1]
823
- const x = pixels[i * 4 + 2]
824
- const y = pixels[i * 4 + 3]
825
-
826
- if (isNotEmpty && index !== undefined && x !== undefined && y !== undefined) {
827
- positions.set(index, [x, y])
828
- }
829
- }
830
- return positions
831
- }
832
-
833
- public getSampledPoints (): { indices: number[]; positions: number[] } {
834
- const indices: number[] = []
835
- const positions: number[] = []
836
- if (!this.sampledPointsFbo) return { indices, positions }
837
-
838
- this.clearSampledPointsFboCommand?.()
839
- this.fillSampledPointsFboCommand?.()
840
- const pixels = readPixels(this.reglInstance, this.sampledPointsFbo as regl.Framebuffer2D)
841
-
842
- for (let i = 0; i < pixels.length / 4; i++) {
843
- const index = pixels[i * 4]
844
- const isNotEmpty = !!pixels[i * 4 + 1]
845
- const x = pixels[i * 4 + 2]
846
- const y = pixels[i * 4 + 3]
847
-
848
- if (isNotEmpty && index !== undefined && x !== undefined && y !== undefined) {
849
- indices.push(index)
850
- positions.push(x, y)
851
- }
852
- }
853
-
854
- return { indices, positions }
855
- }
856
-
857
- public getTrackedPositionsArray (): number[] {
858
- const positions: number[] = []
859
- if (!this.trackedIndices) return positions
860
- positions.length = this.trackedIndices.length * 2
861
- const pixels = readPixels(this.reglInstance, this.trackedPositionsFbo as regl.Framebuffer2D)
862
- for (let i = 0; i < pixels.length / 4; i += 1) {
863
- const x = pixels[i * 4]
864
- const y = pixels[i * 4 + 1]
865
- const index = this.trackedIndices[i]
866
- if (x !== undefined && y !== undefined && index !== undefined) {
867
- positions[i * 2] = x
868
- positions[i * 2 + 1] = y
869
- }
870
- }
871
- return positions
872
- }
873
-
874
- private swapFbo (): void {
875
- const temp = this.previousPositionFbo
876
- this.previousPositionFbo = this.currentPositionFbo
877
- this.currentPositionFbo = temp
878
- }
879
-
880
- private rescaleInitialNodePositions (): void {
881
- const { config: { spaceSize } } = this
882
- if (!this.data.pointPositions || !spaceSize) return
883
-
884
- const points = this.data.pointPositions
885
- const pointsNumber = points.length / 2
886
- let minX = Infinity
887
- let maxX = -Infinity
888
- let minY = Infinity
889
- let maxY = -Infinity
890
- for (let i = 0; i < points.length; i += 2) {
891
- const x = points[i] as number
892
- const y = points[i + 1] as number
893
- minX = Math.min(minX, x)
894
- maxX = Math.max(maxX, x)
895
- minY = Math.min(minY, y)
896
- maxY = Math.max(maxY, y)
897
- }
898
- const w = maxX - minX
899
- const h = maxY - minY
900
- const range = Math.max(w, h)
901
-
902
- // Do not rescale if the range is greater than the space size (no need to)
903
- if (range > spaceSize) {
904
- this.scaleX = undefined
905
- this.scaleY = undefined
906
- return
907
- }
908
-
909
- // Density threshold - points per pixel ratio (0.001 = 0.1%)
910
- const densityThreshold = spaceSize * spaceSize * 0.001
911
- // Calculate effective space size based on point density
912
- const effectiveSpaceSize = pointsNumber > densityThreshold
913
- // For dense datasets: scale up based on point count, minimum 120% of space
914
- ? spaceSize * Math.max(1.2, Math.sqrt(pointsNumber) / spaceSize)
915
- // For sparse datasets: use 10% of space to cluster points closer
916
- : spaceSize * 0.1
917
-
918
- // Calculate uniform scale factor to fit data within effective space
919
- const scaleFactor = effectiveSpaceSize / range
920
- // Center the data horizontally by adding padding on x-axis
921
- const offsetX = ((range - w) / 2) * scaleFactor
922
- // Center the data vertically by adding padding on y-axis
923
- const offsetY = ((range - h) / 2) * scaleFactor
924
-
925
- this.scaleX = (x: number): number => (x - minX) * scaleFactor + offsetX
926
- this.scaleY = (y: number): number => (y - minY) * scaleFactor + offsetY
927
-
928
- // Apply scaling to point positions
929
- for (let i = 0; i < pointsNumber; i++) {
930
- this.data.pointPositions[i * 2] = this.scaleX(points[i * 2] as number)
931
- this.data.pointPositions[i * 2 + 1] = this.scaleY(points[i * 2 + 1] as number)
932
- }
933
- }
934
- }