@cosmos.gl/graph 2.6.2 → 2.7.0-beta.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 (186) hide show
  1. package/.eslintrc +147 -0
  2. package/.github/SECURITY.md +13 -0
  3. package/.github/dco.yml +4 -0
  4. package/.github/workflows/github_pages.yml +54 -0
  5. package/.storybook/main.ts +26 -0
  6. package/.storybook/manager-head.html +1 -0
  7. package/.storybook/manager.ts +14 -0
  8. package/.storybook/preview.ts +29 -0
  9. package/.storybook/style.css +3 -0
  10. package/CHARTER.md +69 -0
  11. package/CODE_OF_CONDUCT.md +178 -0
  12. package/CONTRIBUTING.md +22 -0
  13. package/GOVERNANCE.md +21 -0
  14. package/cosmos-2-0-migration-notes.md +98 -0
  15. package/cosmos_awesome.md +96 -0
  16. package/dist/config.d.ts +5 -18
  17. package/dist/graph/utils/error-message.d.ts +1 -1
  18. package/dist/helper.d.ts +39 -2
  19. package/dist/index-FUIgayhu.js +19827 -0
  20. package/dist/index-FUIgayhu.js.map +1 -0
  21. package/dist/index.d.ts +17 -64
  22. package/dist/index.js +14 -14658
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.min.js +1062 -475
  25. package/dist/index.min.js.map +1 -1
  26. package/dist/modules/Clusters/index.d.ts +11 -3
  27. package/dist/modules/ForceCenter/index.d.ts +10 -3
  28. package/dist/modules/ForceGravity/index.d.ts +5 -1
  29. package/dist/modules/ForceLink/index.d.ts +8 -5
  30. package/dist/modules/ForceManyBody/index.d.ts +16 -7
  31. package/dist/modules/ForceMouse/index.d.ts +5 -1
  32. package/dist/modules/GraphData/index.d.ts +0 -1
  33. package/dist/modules/Lines/index.d.ts +11 -5
  34. package/dist/modules/Points/index.d.ts +31 -13
  35. package/dist/modules/Store/index.d.ts +93 -0
  36. package/dist/modules/core-module.d.ts +3 -3
  37. package/dist/stories/beginners/basic-set-up/data-gen.d.ts +4 -0
  38. package/dist/stories/beginners/basic-set-up/index.d.ts +6 -0
  39. package/dist/stories/beginners/link-hovering/data-generator.d.ts +19 -0
  40. package/dist/stories/beginners/link-hovering/index.d.ts +6 -0
  41. package/dist/stories/beginners/point-labels/data.d.ts +13 -0
  42. package/dist/stories/beginners/point-labels/index.d.ts +10 -0
  43. package/dist/stories/beginners/point-labels/labels.d.ts +8 -0
  44. package/dist/stories/beginners/quick-start.d.ts +6 -0
  45. package/dist/stories/beginners/remove-points/config.d.ts +2 -0
  46. package/dist/stories/beginners/remove-points/data-gen.d.ts +4 -0
  47. package/dist/stories/beginners/remove-points/index.d.ts +6 -0
  48. package/dist/stories/beginners.stories.d.ts +10 -0
  49. package/dist/stories/clusters/polygon-selection/index.d.ts +6 -0
  50. package/dist/stories/clusters/polygon-selection/polygon.d.ts +20 -0
  51. package/dist/stories/clusters/radial.d.ts +6 -0
  52. package/dist/stories/clusters/with-labels.d.ts +6 -0
  53. package/dist/stories/clusters/worm.d.ts +6 -0
  54. package/dist/stories/clusters.stories.d.ts +9 -0
  55. package/dist/stories/create-cluster-labels.d.ts +4 -0
  56. package/dist/stories/create-cosmos.d.ts +17 -0
  57. package/dist/stories/create-story.d.ts +16 -0
  58. package/dist/stories/experiments/full-mesh.d.ts +6 -0
  59. package/dist/stories/experiments/mesh-with-holes.d.ts +6 -0
  60. package/dist/stories/experiments.stories.d.ts +7 -0
  61. package/dist/stories/generate-mesh-data.d.ts +12 -0
  62. package/dist/stories/geospatial/moscow-metro-stations/index.d.ts +16 -0
  63. package/dist/stories/geospatial/moscow-metro-stations/moscow-metro-coords.d.ts +1 -0
  64. package/dist/stories/geospatial/moscow-metro-stations/point-colors.d.ts +1 -0
  65. package/dist/stories/geospatial.stories.d.ts +6 -0
  66. package/dist/stories/shapes/all-shapes/index.d.ts +6 -0
  67. package/dist/stories/shapes/image-example/index.d.ts +6 -0
  68. package/dist/stories/shapes.stories.d.ts +7 -0
  69. package/dist/stories/test-luma-migration.d.ts +6 -0
  70. package/dist/stories/test.stories.d.ts +6 -0
  71. package/dist/webgl-device-B9ewDj5L.js +3923 -0
  72. package/dist/webgl-device-B9ewDj5L.js.map +1 -0
  73. package/logo.svg +3 -0
  74. package/package.json +5 -7
  75. package/rollup.config.js +70 -0
  76. package/src/config.ts +728 -0
  77. package/src/declaration.d.ts +12 -0
  78. package/src/graph/utils/error-message.ts +23 -0
  79. package/src/helper.ts +113 -0
  80. package/src/index.ts +1769 -0
  81. package/src/modules/Clusters/calculate-centermass.frag +12 -0
  82. package/src/modules/Clusters/calculate-centermass.vert +38 -0
  83. package/src/modules/Clusters/force-cluster.frag +55 -0
  84. package/src/modules/Clusters/index.ts +578 -0
  85. package/src/modules/Drag/index.ts +33 -0
  86. package/src/modules/FPSMonitor/css.ts +53 -0
  87. package/src/modules/FPSMonitor/index.ts +28 -0
  88. package/src/modules/ForceCenter/calculate-centermass.frag +9 -0
  89. package/src/modules/ForceCenter/calculate-centermass.vert +26 -0
  90. package/src/modules/ForceCenter/force-center.frag +37 -0
  91. package/src/modules/ForceCenter/index.ts +284 -0
  92. package/src/modules/ForceGravity/force-gravity.frag +40 -0
  93. package/src/modules/ForceGravity/index.ts +107 -0
  94. package/src/modules/ForceLink/force-spring.ts +89 -0
  95. package/src/modules/ForceLink/index.ts +293 -0
  96. package/src/modules/ForceManyBody/calculate-level.frag +9 -0
  97. package/src/modules/ForceManyBody/calculate-level.vert +37 -0
  98. package/src/modules/ForceManyBody/force-centermass.frag +61 -0
  99. package/src/modules/ForceManyBody/force-level.frag +138 -0
  100. package/src/modules/ForceManyBody/index.ts +525 -0
  101. package/src/modules/ForceManyBody/quadtree-frag-shader.ts +89 -0
  102. package/src/modules/ForceManyBodyQuadtree/calculate-level.frag +9 -0
  103. package/src/modules/ForceManyBodyQuadtree/calculate-level.vert +25 -0
  104. package/src/modules/ForceManyBodyQuadtree/index.ts +157 -0
  105. package/src/modules/ForceManyBodyQuadtree/quadtree-frag-shader.ts +93 -0
  106. package/src/modules/ForceMouse/force-mouse.frag +35 -0
  107. package/src/modules/ForceMouse/index.ts +102 -0
  108. package/src/modules/GraphData/index.ts +383 -0
  109. package/src/modules/Lines/draw-curve-line.frag +59 -0
  110. package/src/modules/Lines/draw-curve-line.vert +248 -0
  111. package/src/modules/Lines/geometry.ts +18 -0
  112. package/src/modules/Lines/hovered-line-index.frag +43 -0
  113. package/src/modules/Lines/hovered-line-index.vert +13 -0
  114. package/src/modules/Lines/index.ts +661 -0
  115. package/src/modules/Points/atlas-utils.ts +137 -0
  116. package/src/modules/Points/drag-point.frag +34 -0
  117. package/src/modules/Points/draw-highlighted.frag +44 -0
  118. package/src/modules/Points/draw-highlighted.vert +145 -0
  119. package/src/modules/Points/draw-points.frag +259 -0
  120. package/src/modules/Points/draw-points.vert +203 -0
  121. package/src/modules/Points/fill-sampled-points.frag +12 -0
  122. package/src/modules/Points/fill-sampled-points.vert +51 -0
  123. package/src/modules/Points/find-hovered-point.frag +15 -0
  124. package/src/modules/Points/find-hovered-point.vert +90 -0
  125. package/src/modules/Points/find-points-on-area-selection.frag +88 -0
  126. package/src/modules/Points/find-points-on-polygon-selection.frag +89 -0
  127. package/src/modules/Points/index.ts +2292 -0
  128. package/src/modules/Points/track-positions.frag +30 -0
  129. package/src/modules/Points/update-position.frag +39 -0
  130. package/src/modules/Shared/buffer.ts +39 -0
  131. package/src/modules/Shared/clear.frag +10 -0
  132. package/src/modules/Shared/quad.vert +13 -0
  133. package/src/modules/Store/index.ts +283 -0
  134. package/src/modules/Zoom/index.ts +148 -0
  135. package/src/modules/core-module.ts +28 -0
  136. package/src/stories/1. welcome.mdx +75 -0
  137. package/src/stories/2. configuration.mdx +111 -0
  138. package/src/stories/3. api-reference.mdx +591 -0
  139. package/src/stories/beginners/basic-set-up/data-gen.ts +33 -0
  140. package/src/stories/beginners/basic-set-up/index.ts +167 -0
  141. package/src/stories/beginners/basic-set-up/style.css +35 -0
  142. package/src/stories/beginners/link-hovering/data-generator.ts +198 -0
  143. package/src/stories/beginners/link-hovering/index.ts +65 -0
  144. package/src/stories/beginners/link-hovering/style.css +73 -0
  145. package/src/stories/beginners/point-labels/data.ts +73 -0
  146. package/src/stories/beginners/point-labels/index.ts +69 -0
  147. package/src/stories/beginners/point-labels/labels.ts +46 -0
  148. package/src/stories/beginners/point-labels/style.css +16 -0
  149. package/src/stories/beginners/quick-start.ts +54 -0
  150. package/src/stories/beginners/remove-points/config.ts +25 -0
  151. package/src/stories/beginners/remove-points/data-gen.ts +30 -0
  152. package/src/stories/beginners/remove-points/index.ts +96 -0
  153. package/src/stories/beginners/remove-points/style.css +31 -0
  154. package/src/stories/beginners.stories.ts +130 -0
  155. package/src/stories/clusters/polygon-selection/index.ts +52 -0
  156. package/src/stories/clusters/polygon-selection/polygon.ts +143 -0
  157. package/src/stories/clusters/polygon-selection/style.css +8 -0
  158. package/src/stories/clusters/radial.ts +24 -0
  159. package/src/stories/clusters/with-labels.ts +54 -0
  160. package/src/stories/clusters/worm.ts +40 -0
  161. package/src/stories/clusters.stories.ts +77 -0
  162. package/src/stories/create-cluster-labels.ts +50 -0
  163. package/src/stories/create-cosmos.ts +72 -0
  164. package/src/stories/create-story.ts +51 -0
  165. package/src/stories/experiments/full-mesh.ts +13 -0
  166. package/src/stories/experiments/mesh-with-holes.ts +13 -0
  167. package/src/stories/experiments.stories.ts +43 -0
  168. package/src/stories/generate-mesh-data.ts +125 -0
  169. package/src/stories/geospatial/moscow-metro-stations/index.ts +66 -0
  170. package/src/stories/geospatial/moscow-metro-stations/moscow-metro-coords.ts +1 -0
  171. package/src/stories/geospatial/moscow-metro-stations/point-colors.ts +46 -0
  172. package/src/stories/geospatial/moscow-metro-stations/style.css +30 -0
  173. package/src/stories/geospatial.stories.ts +30 -0
  174. package/src/stories/shapes/all-shapes/index.ts +73 -0
  175. package/src/stories/shapes/image-example/icons/box.png +0 -0
  176. package/src/stories/shapes/image-example/icons/lego.png +0 -0
  177. package/src/stories/shapes/image-example/icons/s.png +0 -0
  178. package/src/stories/shapes/image-example/icons/swift.png +0 -0
  179. package/src/stories/shapes/image-example/icons/toolbox.png +0 -0
  180. package/src/stories/shapes/image-example/index.ts +246 -0
  181. package/src/stories/shapes.stories.ts +37 -0
  182. package/src/stories/test-luma-migration.ts +195 -0
  183. package/src/stories/test.stories.ts +25 -0
  184. package/src/variables.ts +68 -0
  185. package/tsconfig.json +41 -0
  186. package/vite.config.ts +52 -0
@@ -0,0 +1,33 @@
1
+ import { drag } from 'd3-drag'
2
+ import { Store } from '@/graph/modules/Store'
3
+ import { GraphConfigInterface } from '@/graph/config'
4
+
5
+ export class Drag {
6
+ public readonly store: Store
7
+ public readonly config: GraphConfigInterface
8
+ public isActive = false
9
+ public behavior = drag<HTMLCanvasElement, undefined>()
10
+ .subject((event) => {
11
+ return this.store.hoveredPoint && !this.store.isSpaceKeyPressed ? { x: event.x, y: event.y } : undefined
12
+ })
13
+ .on('start', (e) => {
14
+ if (this.store.hoveredPoint) {
15
+ this.store.draggingPointIndex = this.store.hoveredPoint.index
16
+ this.isActive = true
17
+ this.config?.onDragStart?.(e)
18
+ }
19
+ })
20
+ .on('drag', (e) => {
21
+ this.config?.onDrag?.(e)
22
+ })
23
+ .on('end', (e) => {
24
+ this.isActive = false
25
+ this.store.draggingPointIndex = undefined
26
+ this.config?.onDragEnd?.(e)
27
+ })
28
+
29
+ public constructor (store: Store, config: GraphConfigInterface) {
30
+ this.store = store
31
+ this.config = config
32
+ }
33
+ }
@@ -0,0 +1,53 @@
1
+ export const benchCSS = `
2
+ #gl-bench {
3
+ position:absolute;
4
+ right:0;
5
+ top:0;
6
+ z-index:1000;
7
+ -webkit-user-select: none;
8
+ -moz-user-select: none;
9
+ user-select: none;
10
+ }
11
+ #gl-bench div {
12
+ position: relative;
13
+ display: block;
14
+ margin: 4px;
15
+ padding: 0 7px 0 10px;
16
+ background: #5f69de;
17
+ border-radius: 15px;
18
+ cursor: pointer;
19
+ opacity: 0.9;
20
+ }
21
+ #gl-bench svg {
22
+ height: 60px;
23
+ margin: 0 -1px;
24
+ }
25
+ #gl-bench text {
26
+ font-size: 12px;
27
+ font-family: Helvetica,Arial,sans-serif;
28
+ font-weight: 700;
29
+ dominant-baseline: middle;
30
+ text-anchor: middle;
31
+ }
32
+ #gl-bench .gl-mem {
33
+ font-size: 9px;
34
+ }
35
+ #gl-bench line {
36
+ stroke-width: 5;
37
+ stroke: #112211;
38
+ stroke-linecap: round;
39
+ }
40
+ #gl-bench polyline {
41
+ fill: none;
42
+ stroke: #112211;
43
+ stroke-linecap: round;
44
+ stroke-linejoin: round;
45
+ stroke-width: 3.5;
46
+ }
47
+ #gl-bench rect {
48
+ fill: #8288e4;
49
+ }
50
+ #gl-bench .opacity {
51
+ stroke: #8288e4;
52
+ }
53
+ `
@@ -0,0 +1,28 @@
1
+
2
+ import { select } from 'd3-selection'
3
+ import GLBench from 'gl-bench/dist/gl-bench'
4
+ import { benchCSS } from './css'
5
+
6
+ export class FPSMonitor {
7
+ private bench: GLBench | undefined
8
+
9
+ public constructor (canvas: HTMLCanvasElement) {
10
+ this.destroy()
11
+ const gl = (canvas.getContext('webgl') || canvas.getContext('experimental-webgl')) as WebGL2RenderingContext
12
+ this.bench = new GLBench(gl, { css: benchCSS })
13
+ }
14
+
15
+ public begin (): void {
16
+ this.bench?.begin('frame')
17
+ }
18
+
19
+ public end (now: number): void {
20
+ this.bench?.end('frame')
21
+ this.bench?.nextFrame(now)
22
+ }
23
+
24
+ public destroy (): void {
25
+ this.bench = undefined
26
+ select('#gl-bench').remove()
27
+ }
28
+ }
@@ -0,0 +1,9 @@
1
+ #version 300 es
2
+ precision highp float;
3
+
4
+ in vec4 rgba;
5
+ out vec4 fragColor;
6
+
7
+ void main() {
8
+ fragColor = rgba;
9
+ }
@@ -0,0 +1,26 @@
1
+ #version 300 es
2
+ precision highp float;
3
+
4
+ uniform sampler2D positionsTexture;
5
+
6
+ #ifdef USE_UNIFORM_BUFFERS
7
+ layout(std140) uniform calculateCentermassUniforms {
8
+ float pointsTextureSize;
9
+ } calculateCentermass;
10
+
11
+ #define pointsTextureSize calculateCentermass.pointsTextureSize
12
+ #else
13
+ uniform float pointsTextureSize;
14
+ #endif
15
+
16
+ in vec2 pointIndices;
17
+
18
+ out vec4 rgba;
19
+
20
+ void main() {
21
+ vec4 pointPosition = texture(positionsTexture, pointIndices / pointsTextureSize);
22
+ rgba = vec4(pointPosition.xy, 1.0, 0.0);
23
+
24
+ gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
25
+ gl_PointSize = 1.0;
26
+ }
@@ -0,0 +1,37 @@
1
+ #version 300 es
2
+ precision highp float;
3
+
4
+ uniform sampler2D positionsTexture;
5
+ uniform sampler2D centermassTexture;
6
+
7
+ #ifdef USE_UNIFORM_BUFFERS
8
+ layout(std140) uniform forceCenterUniforms {
9
+ float centerForce;
10
+ float alpha;
11
+ } forceCenter;
12
+
13
+ #define centerForce forceCenter.centerForce
14
+ #define alpha forceCenter.alpha
15
+ #else
16
+ uniform float centerForce;
17
+ uniform float alpha;
18
+ #endif
19
+
20
+ in vec2 textureCoords;
21
+ out vec4 fragColor;
22
+
23
+ void main() {
24
+ vec4 pointPosition = texture(positionsTexture, textureCoords);
25
+ vec4 velocity = vec4(0.0);
26
+ vec4 centermassValues = texture(centermassTexture, vec2(0.0));
27
+ vec2 centermassPosition = centermassValues.xy / centermassValues.b;
28
+ vec2 distVector = centermassPosition - pointPosition.xy;
29
+ float dist = sqrt(dot(distVector, distVector));
30
+ if (dist > 0.0) {
31
+ float angle = atan(distVector.y, distVector.x);
32
+ float addV = alpha * centerForce * dist * 0.01;
33
+ velocity.rg += addV * vec2(cos(angle), sin(angle));
34
+ }
35
+
36
+ fragColor = velocity;
37
+ }
@@ -0,0 +1,284 @@
1
+ import { Buffer, Framebuffer, RenderPass, Texture, UniformStore } from '@luma.gl/core'
2
+ import { Model } from '@luma.gl/engine'
3
+ import { CoreModule } from '@/graph/modules/core-module'
4
+ import calculateCentermassFrag from '@/graph/modules/ForceCenter/calculate-centermass.frag?raw'
5
+ import calculateCentermassVert from '@/graph/modules/ForceCenter/calculate-centermass.vert?raw'
6
+ import forceFrag from '@/graph/modules/ForceCenter/force-center.frag?raw'
7
+ import { createIndexesForBuffer } from '@/graph/modules/Shared/buffer'
8
+ import clearFrag from '@/graph/modules/Shared/clear.frag?raw'
9
+ import updateVert from '@/graph/modules/Shared/quad.vert?raw'
10
+
11
+ export class ForceCenter extends CoreModule {
12
+ private centermassTexture: Texture | undefined
13
+ private centermassFbo: Framebuffer | undefined
14
+ private pointIndices: Buffer | undefined
15
+
16
+ private clearCentermassCommand: Model | undefined
17
+ private calculateCentermassCommand: Model | undefined
18
+ private runCommand: Model | undefined
19
+
20
+ private clearVertexCoordBuffer: Buffer | undefined
21
+ private forceVertexCoordBuffer: Buffer | undefined
22
+
23
+ private calculateUniformStore: UniformStore<{
24
+ calculateCentermassUniforms: {
25
+ pointsTextureSize: number;
26
+ };
27
+ }> | undefined
28
+
29
+ private forceUniformStore: UniformStore<{
30
+ forceCenterUniforms: {
31
+ centerForce: number;
32
+ alpha: number;
33
+ };
34
+ }> | undefined
35
+
36
+ private previousPointsTextureSize: number | undefined
37
+
38
+ public create (): void {
39
+ const { device, store } = this
40
+ const { pointsTextureSize } = store
41
+ if (!pointsTextureSize) return
42
+
43
+ if (!this.centermassTexture || this.centermassTexture.destroyed) {
44
+ this.centermassTexture = device.createTexture({
45
+ width: 1,
46
+ height: 1,
47
+ format: 'rgba32float',
48
+ usage: Texture.SAMPLE | Texture.RENDER | Texture.COPY_DST,
49
+ })
50
+ }
51
+ this.centermassTexture.copyImageData({
52
+ data: new Float32Array(4).fill(0),
53
+ bytesPerRow: 1,
54
+ mipLevel: 0,
55
+ x: 0,
56
+ y: 0,
57
+ })
58
+
59
+ if (!this.centermassFbo || this.centermassFbo.destroyed) {
60
+ this.centermassFbo = device.createFramebuffer({
61
+ width: 1,
62
+ height: 1,
63
+ colorAttachments: [this.centermassTexture],
64
+ })
65
+ }
66
+
67
+ const indexData = createIndexesForBuffer(pointsTextureSize)
68
+ if (!this.pointIndices || this.pointIndices.byteLength !== indexData.byteLength) {
69
+ this.pointIndices?.destroy()
70
+ this.pointIndices = device.createBuffer({
71
+ data: indexData,
72
+ usage: Buffer.VERTEX | Buffer.COPY_DST,
73
+ })
74
+ } else {
75
+ this.pointIndices.write(indexData)
76
+ }
77
+
78
+ this.previousPointsTextureSize = pointsTextureSize
79
+ }
80
+
81
+ public initPrograms (): void {
82
+ const { device, store, points } = this
83
+ if (!points || !store.pointsTextureSize) return
84
+ if (!this.centermassFbo || this.centermassFbo.destroyed || !this.centermassTexture || this.centermassTexture.destroyed) return
85
+ if (!this.pointIndices) return
86
+
87
+ // Fullscreen quad buffer (shared by clear and force passes)
88
+ if (!this.clearVertexCoordBuffer || this.clearVertexCoordBuffer.destroyed) {
89
+ this.clearVertexCoordBuffer = device.createBuffer({
90
+ data: new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
91
+ })
92
+ }
93
+ if (!this.forceVertexCoordBuffer || this.forceVertexCoordBuffer.destroyed) {
94
+ this.forceVertexCoordBuffer = device.createBuffer({
95
+ data: new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
96
+ })
97
+ }
98
+
99
+ if (!this.calculateUniformStore) {
100
+ this.calculateUniformStore = new UniformStore({
101
+ calculateCentermassUniforms: {
102
+ uniformTypes: {
103
+ pointsTextureSize: 'f32',
104
+ },
105
+ },
106
+ })
107
+ }
108
+
109
+ if (!this.forceUniformStore) {
110
+ this.forceUniformStore = new UniformStore({
111
+ forceCenterUniforms: {
112
+ uniformTypes: {
113
+ centerForce: 'f32',
114
+ alpha: 'f32',
115
+ },
116
+ },
117
+ })
118
+ }
119
+
120
+ if (!this.clearCentermassCommand) {
121
+ this.clearCentermassCommand = new Model(device, {
122
+ fs: clearFrag,
123
+ vs: updateVert,
124
+ topology: 'triangle-strip',
125
+ vertexCount: 4,
126
+ attributes: {
127
+ vertexCoord: this.clearVertexCoordBuffer,
128
+ },
129
+ bufferLayout: [
130
+ { name: 'vertexCoord', format: 'float32x2' },
131
+ ],
132
+ parameters: {
133
+ depthWriteEnabled: false,
134
+ depthCompare: 'always',
135
+ },
136
+ })
137
+ }
138
+
139
+ if (!this.calculateCentermassCommand) {
140
+ this.calculateCentermassCommand = new Model(device, {
141
+ fs: calculateCentermassFrag,
142
+ vs: calculateCentermassVert,
143
+ topology: 'point-list',
144
+ vertexCount: this.data.pointsNumber ?? 0,
145
+ attributes: {
146
+ pointIndices: this.pointIndices,
147
+ },
148
+ bufferLayout: [
149
+ { name: 'pointIndices', format: 'float32x2' },
150
+ ],
151
+ defines: {
152
+ USE_UNIFORM_BUFFERS: true,
153
+ },
154
+ bindings: {
155
+ calculateCentermassUniforms: this.calculateUniformStore.getManagedUniformBuffer(device, 'calculateCentermassUniforms'),
156
+ positionsTexture: points.previousPositionTexture!,
157
+ },
158
+ parameters: {
159
+ blend: true,
160
+ blendColorOperation: 'add',
161
+ blendColorSrcFactor: 'one',
162
+ blendColorDstFactor: 'one',
163
+ blendAlphaOperation: 'add',
164
+ blendAlphaSrcFactor: 'one',
165
+ blendAlphaDstFactor: 'one',
166
+ depthWriteEnabled: false,
167
+ depthCompare: 'always',
168
+ },
169
+ })
170
+ } else {
171
+ this.calculateCentermassCommand.setVertexCount(this.data.pointsNumber ?? 0)
172
+ }
173
+
174
+ if (!this.runCommand) {
175
+ this.runCommand = new Model(device, {
176
+ fs: forceFrag,
177
+ vs: updateVert,
178
+ topology: 'triangle-strip',
179
+ vertexCount: 4,
180
+ attributes: {
181
+ vertexCoord: this.forceVertexCoordBuffer,
182
+ },
183
+ bufferLayout: [
184
+ { name: 'vertexCoord', format: 'float32x2' },
185
+ ],
186
+ defines: {
187
+ USE_UNIFORM_BUFFERS: true,
188
+ },
189
+ bindings: {
190
+ forceCenterUniforms: this.forceUniformStore.getManagedUniformBuffer(device, 'forceCenterUniforms'),
191
+ positionsTexture: points.previousPositionTexture!,
192
+ centermassTexture: this.centermassTexture,
193
+ },
194
+ parameters: {
195
+ depthWriteEnabled: false,
196
+ depthCompare: 'always',
197
+ },
198
+ })
199
+ }
200
+ }
201
+
202
+ public run (renderPass?: RenderPass): void {
203
+ const { device, store, points } = this
204
+ if (!points || !this.centermassFbo || !this.centermassTexture) return
205
+ if (!this.calculateCentermassCommand || !this.calculateUniformStore || !this.runCommand || !this.forceUniformStore) return
206
+ if (!points.previousPositionTexture || points.previousPositionTexture.destroyed) return
207
+ if (!renderPass && (!points.velocityFbo || points.velocityFbo.destroyed)) return
208
+
209
+ // Skip if sizes changed and create() wasn't called yet
210
+ if (store.pointsTextureSize !== this.previousPointsTextureSize) return
211
+
212
+ // Clear centermass then accumulate
213
+ const centermassPass = device.beginRenderPass({
214
+ framebuffer: this.centermassFbo,
215
+ clearColor: [0, 0, 0, 0],
216
+ })
217
+
218
+ this.calculateUniformStore.setUniforms({
219
+ calculateCentermassUniforms: {
220
+ pointsTextureSize: store.pointsTextureSize ?? 0,
221
+ },
222
+ })
223
+ this.calculateCentermassCommand.setBindings({
224
+ calculateCentermassUniforms: this.calculateUniformStore.getManagedUniformBuffer(device, 'calculateCentermassUniforms'),
225
+ positionsTexture: points.previousPositionTexture!,
226
+ })
227
+
228
+ // No need to draw clear model separately; pass clearColor already zeroed
229
+ this.calculateCentermassCommand.draw(centermassPass)
230
+ centermassPass.end()
231
+
232
+ // Apply center force into velocity
233
+ this.forceUniformStore.setUniforms({
234
+ forceCenterUniforms: {
235
+ centerForce: this.config.simulationCenter ?? 0,
236
+ alpha: store.alpha,
237
+ },
238
+ })
239
+ this.runCommand.setBindings({
240
+ forceCenterUniforms: this.forceUniformStore.getManagedUniformBuffer(device, 'forceCenterUniforms'),
241
+ positionsTexture: points.previousPositionTexture!,
242
+ centermassTexture: this.centermassTexture,
243
+ })
244
+
245
+ const pass = renderPass ?? device.beginRenderPass({
246
+ framebuffer: points.velocityFbo,
247
+ })
248
+
249
+ this.runCommand.draw(pass)
250
+
251
+ if (!renderPass) pass.end()
252
+ }
253
+
254
+ public destroy (): void {
255
+ this.calculateUniformStore?.destroy()
256
+ this.calculateUniformStore = undefined
257
+ this.forceUniformStore?.destroy()
258
+ this.forceUniformStore = undefined
259
+
260
+ this.clearCentermassCommand?.destroy()
261
+ this.clearCentermassCommand = undefined
262
+ this.calculateCentermassCommand?.destroy()
263
+ this.calculateCentermassCommand = undefined
264
+ this.runCommand?.destroy()
265
+ this.runCommand = undefined
266
+
267
+ if (this.clearVertexCoordBuffer && !this.clearVertexCoordBuffer.destroyed) this.clearVertexCoordBuffer.destroy()
268
+ this.clearVertexCoordBuffer = undefined
269
+
270
+ if (this.forceVertexCoordBuffer && !this.forceVertexCoordBuffer.destroyed) this.forceVertexCoordBuffer.destroy()
271
+ this.forceVertexCoordBuffer = undefined
272
+
273
+ this.pointIndices?.destroy()
274
+ this.pointIndices = undefined
275
+
276
+ if (this.centermassTexture && !this.centermassTexture.destroyed) this.centermassTexture.destroy()
277
+ this.centermassTexture = undefined
278
+
279
+ if (this.centermassFbo && !this.centermassFbo.destroyed) this.centermassFbo.destroy()
280
+ this.centermassFbo = undefined
281
+
282
+ this.previousPointsTextureSize = undefined
283
+ }
284
+ }
@@ -0,0 +1,40 @@
1
+ #version 300 es
2
+ precision highp float;
3
+
4
+ uniform sampler2D positionsTexture;
5
+
6
+ #ifdef USE_UNIFORM_BUFFERS
7
+ layout(std140) uniform forceGravityUniforms {
8
+ float gravity;
9
+ float spaceSize;
10
+ float alpha;
11
+ } forceGravity;
12
+
13
+ #define gravity forceGravity.gravity
14
+ #define spaceSize forceGravity.spaceSize
15
+ #define alpha forceGravity.alpha
16
+ #else
17
+ uniform float gravity;
18
+ uniform float spaceSize;
19
+ uniform float alpha;
20
+ #endif
21
+
22
+ in vec2 textureCoords;
23
+ out vec4 fragColor;
24
+
25
+ void main() {
26
+ vec4 pointPosition = texture(positionsTexture, textureCoords);
27
+
28
+ vec4 velocity = vec4(0.0);
29
+
30
+ vec2 centerPosition = vec2(spaceSize * 0.5);
31
+ vec2 distVector = centerPosition - pointPosition.rg;
32
+ float dist = sqrt(dot(distVector, distVector));
33
+ if (dist > 0.0) {
34
+ float angle = atan(distVector.y, distVector.x);
35
+ float additionalVelocity = alpha * gravity * dist * 0.1;
36
+ velocity.rg += additionalVelocity * vec2(cos(angle), sin(angle));
37
+ }
38
+
39
+ fragColor = velocity;
40
+ }
@@ -0,0 +1,107 @@
1
+ import { Buffer, RenderPass, UniformStore } from '@luma.gl/core'
2
+ import { Model } from '@luma.gl/engine'
3
+ import { CoreModule } from '@/graph/modules/core-module'
4
+ import forceFrag from '@/graph/modules/ForceGravity/force-gravity.frag?raw'
5
+ import updateVert from '@/graph/modules/Shared/quad.vert?raw'
6
+
7
+ export class ForceGravity extends CoreModule {
8
+ private runCommand: Model | undefined
9
+ private vertexCoordBuffer: Buffer | undefined
10
+ private uniformStore: UniformStore<{
11
+ forceGravityUniforms: {
12
+ gravity: number;
13
+ spaceSize: number;
14
+ alpha: number;
15
+ };
16
+ }> | undefined
17
+
18
+ public initPrograms (): void {
19
+ const { device, points, store } = this
20
+ if (!points || !store.pointsTextureSize) return
21
+
22
+ if (!this.vertexCoordBuffer || this.vertexCoordBuffer.destroyed) {
23
+ this.vertexCoordBuffer = device.createBuffer({
24
+ data: new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
25
+ })
26
+ }
27
+
28
+ if (!this.uniformStore) {
29
+ this.uniformStore = new UniformStore({
30
+ forceGravityUniforms: {
31
+ uniformTypes: {
32
+ gravity: 'f32',
33
+ spaceSize: 'f32',
34
+ alpha: 'f32',
35
+ },
36
+ },
37
+ })
38
+ }
39
+
40
+ if (!this.runCommand) {
41
+ this.runCommand = new Model(device, {
42
+ fs: forceFrag,
43
+ vs: updateVert,
44
+ topology: 'triangle-strip',
45
+ vertexCount: 4,
46
+ attributes: {
47
+ vertexCoord: this.vertexCoordBuffer,
48
+ },
49
+ bufferLayout: [
50
+ { name: 'vertexCoord', format: 'float32x2' },
51
+ ],
52
+ defines: {
53
+ USE_UNIFORM_BUFFERS: true,
54
+ },
55
+ bindings: {
56
+ forceGravityUniforms: this.uniformStore.getManagedUniformBuffer(device, 'forceGravityUniforms'),
57
+ positionsTexture: points.previousPositionTexture!,
58
+ },
59
+ parameters: {
60
+ depthWriteEnabled: false,
61
+ depthCompare: 'always',
62
+ },
63
+ })
64
+ }
65
+ }
66
+
67
+ public run (renderPass?: RenderPass): void {
68
+ const { device, points, store } = this
69
+ if (!points || !this.runCommand || !this.uniformStore) return
70
+ if (!points.previousPositionTexture || points.previousPositionTexture.destroyed) return
71
+ if (!renderPass && (!points.velocityFbo || points.velocityFbo.destroyed)) return
72
+
73
+ this.uniformStore.setUniforms({
74
+ forceGravityUniforms: {
75
+ gravity: this.config.simulationGravity ?? 0,
76
+ spaceSize: store.adjustedSpaceSize ?? 0,
77
+ alpha: store.alpha,
78
+ },
79
+ })
80
+
81
+ this.runCommand.setBindings({
82
+ forceGravityUniforms: this.uniformStore.getManagedUniformBuffer(device, 'forceGravityUniforms'),
83
+ positionsTexture: points.previousPositionTexture!,
84
+ })
85
+
86
+ const pass = renderPass ?? device.beginRenderPass({
87
+ framebuffer: points.velocityFbo,
88
+ })
89
+
90
+ this.runCommand.draw(pass)
91
+
92
+ if (!renderPass) pass.end()
93
+ }
94
+
95
+ public destroy (): void {
96
+ this.uniformStore?.destroy()
97
+ this.uniformStore = undefined
98
+
99
+ if (this.runCommand && !this.runCommand.destroyed) this.runCommand.destroy()
100
+ this.runCommand = undefined
101
+
102
+ if (this.vertexCoordBuffer && !this.vertexCoordBuffer.destroyed) {
103
+ this.vertexCoordBuffer.destroy()
104
+ }
105
+ this.vertexCoordBuffer = undefined
106
+ }
107
+ }