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