@cosmos.gl/graph 2.6.2-rc.0 → 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 -9
  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 -14654
  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,30 @@
1
+ #version 300 es
2
+ #ifdef GL_ES
3
+ precision highp float;
4
+ #endif
5
+
6
+ uniform sampler2D positionsTexture;
7
+ uniform sampler2D trackedIndices;
8
+
9
+ #ifdef USE_UNIFORM_BUFFERS
10
+ layout(std140) uniform trackPointsUniforms {
11
+ float pointsTextureSize;
12
+ } trackPoints;
13
+
14
+ #define pointsTextureSize trackPoints.pointsTextureSize
15
+ #else
16
+ uniform float pointsTextureSize;
17
+ #endif
18
+
19
+ in vec2 textureCoords;
20
+
21
+ out vec4 fragColor;
22
+
23
+ void main() {
24
+ vec4 trackedPointIndices = texture(trackedIndices, textureCoords);
25
+ if (trackedPointIndices.r < 0.0) discard;
26
+ vec4 pointPosition = texture(positionsTexture, (trackedPointIndices.rg + 0.5) / pointsTextureSize);
27
+
28
+ fragColor = vec4(pointPosition.rg, 1.0, 1.0);
29
+ }
30
+
@@ -0,0 +1,39 @@
1
+ #version 300 es
2
+ #ifdef GL_ES
3
+ precision highp float;
4
+ #endif
5
+
6
+ uniform sampler2D positionsTexture;
7
+ uniform sampler2D velocity;
8
+
9
+ #ifdef USE_UNIFORM_BUFFERS
10
+ layout(std140) uniform updatePositionUniforms {
11
+ float friction;
12
+ float spaceSize;
13
+ } updatePosition;
14
+
15
+ #define friction updatePosition.friction
16
+ #define spaceSize updatePosition.spaceSize
17
+ #else
18
+ uniform float friction;
19
+ uniform float spaceSize;
20
+ #endif
21
+
22
+ in vec2 textureCoords;
23
+
24
+ out vec4 fragColor;
25
+
26
+ void main() {
27
+ vec4 pointPosition = texture(positionsTexture, textureCoords);
28
+ vec4 pointVelocity = texture(velocity, textureCoords);
29
+
30
+ // Friction
31
+ pointVelocity.rg *= friction;
32
+
33
+ pointPosition.rg += pointVelocity.rg;
34
+
35
+ pointPosition.r = clamp(pointPosition.r, 0.0, spaceSize);
36
+ pointPosition.g = clamp(pointPosition.g, 0.0, spaceSize);
37
+
38
+ fragColor = pointPosition;
39
+ }
@@ -0,0 +1,39 @@
1
+ import regl from 'regl'
2
+
3
+ export function createQuadBuffer (reglInstance: regl.Regl): { buffer: regl.Buffer; size: number } {
4
+ const QUAD_POSITIONS = [-1, -1, 1, -1, -1, 1, 1, 1]
5
+
6
+ const quadBuffer = reglInstance.buffer(new Float32Array(QUAD_POSITIONS))
7
+ return {
8
+ buffer: quadBuffer,
9
+ size: 2,
10
+ }
11
+ }
12
+
13
+ export function createIndexesForBuffer (textureSize: number): Float32Array {
14
+ const indexes = new Float32Array(textureSize * textureSize * 2)
15
+ for (let y = 0; y < textureSize; y++) {
16
+ for (let x = 0; x < textureSize; x++) {
17
+ const i = y * textureSize * 2 + x * 2
18
+ indexes[i + 0] = x
19
+ indexes[i + 1] = y
20
+ }
21
+ }
22
+ return indexes
23
+ }
24
+
25
+ export function destroyFramebuffer (fbo?: regl.Framebuffer2D): void {
26
+ if (!fbo) return
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ if ((fbo as any)?._framebuffer?.framebuffer) {
29
+ fbo.destroy()
30
+ }
31
+ }
32
+
33
+ export function destroyBuffer (fbo?: regl.Buffer): void {
34
+ if (!fbo) return
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ if ((fbo as any)?._buffer?.buffer) {
37
+ fbo.destroy()
38
+ }
39
+ }
@@ -0,0 +1,10 @@
1
+ #version 300 es
2
+ #ifdef GL_ES
3
+ precision highp float;
4
+ #endif
5
+
6
+ out vec4 fragColor;
7
+
8
+ void main() {
9
+ fragColor = vec4(0.0);
10
+ }
@@ -0,0 +1,13 @@
1
+ #version 300 es
2
+ #ifdef GL_ES
3
+ precision highp float;
4
+ #endif
5
+
6
+ in vec2 vertexCoord; // Vertex coordinates in normalized device coordinates
7
+ out vec2 textureCoords; // Texture coordinates to pass to the fragment shader
8
+
9
+ void main() {
10
+ // Convert vertex coordinates from [-1, 1] range to [0, 1] range for texture sampling
11
+ textureCoords = (vertexCoord + 1.0) / 2.0;
12
+ gl_Position = vec4(vertexCoord, 0, 1);
13
+ }
@@ -0,0 +1,283 @@
1
+ import { scaleLinear } from 'd3-scale'
2
+ import { mat3 } from 'gl-matrix'
3
+ import { Random } from 'random'
4
+ import { getRgbaColor, rgbToBrightness } from '@/graph/helper'
5
+ import { hoveredPointRingOpacity, focusedPointRingOpacity, defaultConfigValues } from '@/graph/variables'
6
+ import type { GraphConfigInterface } from '@/graph/config'
7
+
8
+ export const ALPHA_MIN = 0.001
9
+ export const MAX_POINT_SIZE = 64
10
+
11
+ /**
12
+ * Maximum number of executions to delay before performing hover detection.
13
+ * This threshold prevents excessive hover detection calls for performance optimization.
14
+ * The `findHoveredItem` method will skip actual detection until this count is reached.
15
+ */
16
+ export const MAX_HOVER_DETECTION_DELAY = 4
17
+
18
+ export type Hovered = { index: number; position: [ number, number ] }
19
+ type Focused = { index: number }
20
+
21
+ /**
22
+ * Type alias for a 4x4 matrix stored as a 16-element array in column-major order.
23
+ * Used for std140 uniform buffer layout compatibility.
24
+ */
25
+ type Mat4Array = [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number]
26
+
27
+ export class Store {
28
+ public pointsTextureSize = 0
29
+ public linksTextureSize = 0
30
+ public alpha = 1
31
+ public transform = mat3.create()
32
+ public screenSize: [number, number] = [0, 0]
33
+ public mousePosition = [0, 0]
34
+ public screenMousePosition = [0, 0]
35
+ public selectedArea = [[0, 0], [0, 0]]
36
+ public isSimulationRunning = false
37
+ public simulationProgress = 0
38
+ public selectedIndices: Float32Array | null = null
39
+ public maxPointSize = MAX_POINT_SIZE
40
+ public hoveredPoint: Hovered | undefined = undefined
41
+ public focusedPoint: Focused | undefined = undefined
42
+ public draggingPointIndex: number | undefined = undefined
43
+ public hoveredLinkIndex: number | undefined = undefined
44
+ public adjustedSpaceSize = defaultConfigValues.spaceSize
45
+ public isSpaceKeyPressed = false
46
+ public div: HTMLDivElement | undefined
47
+ public webglMaxTextureSize = 16384 // Default fallback value
48
+
49
+ public hoveredPointRingColor = [1, 1, 1, hoveredPointRingOpacity]
50
+ public focusedPointRingColor = [1, 1, 1, focusedPointRingOpacity]
51
+ public hoveredLinkColor = [-1, -1, -1, -1]
52
+ // -1 means that the color is not set
53
+ public greyoutPointColor = [-1, -1, -1, -1]
54
+ // If backgroundColor is dark, isDarkenGreyout is true
55
+ public isDarkenGreyout = false
56
+ // Whether link hovering is enabled based on configured event handlers
57
+ public isLinkHoveringEnabled = false
58
+ private alphaTarget = 0
59
+ private scalePointX = scaleLinear()
60
+ private scalePointY = scaleLinear()
61
+ private random = new Random()
62
+ private _backgroundColor: [number, number, number, number] = [0, 0, 0, 0]
63
+
64
+ public get backgroundColor (): [number, number, number, number] {
65
+ return this._backgroundColor
66
+ }
67
+
68
+ /**
69
+ * Gets the transformation matrix as a 4x4 matrix for std140 uniform buffer layout.
70
+ *
71
+ * This method converts the internal 3x3 transformation matrix (mat3) to a 4x4 matrix format
72
+ * required by WebGPU uniform buffers using the std140 layout standard.
73
+ *
74
+ * ## Matrix Storage Format
75
+ *
76
+ * Matrices are stored in **column-major order** (GLSL convention). The internal `transform`
77
+ * array is a 9-element array representing a 3x3 matrix:
78
+ *
79
+ * ```
80
+ * [m00, m10, m20, m01, m11, m21, m02, m12, m22]
81
+ * ```
82
+ *
83
+ * Which represents the matrix:
84
+ * ```
85
+ * [m00 m01 m02]
86
+ * [m10 m11 m12]
87
+ * [m20 m21 m22]
88
+ * ```
89
+ *
90
+ * ## Why This Conversion Is Needed
91
+ *
92
+ * The internal `transform` property stores a 3x3 matrix (9 elements) which is sufficient for
93
+ * 2D transformations (translation, rotation, scaling in x/y plane). However, when passing
94
+ * transformation matrices to GPU shaders via uniform buffers, we must comply with the std140
95
+ * layout standard.
96
+ *
97
+ * ### std140 Layout Requirements
98
+ *
99
+ * The std140 layout standard (used in both OpenGL and WebGPU) defines strict alignment rules:
100
+ *
101
+ * 1. **Matrix Alignment**: In std140, matrices are stored as arrays of column vectors
102
+ * - `mat3` requires each column to be aligned to 16 bytes (vec4 alignment)
103
+ * - This means `mat3` occupies 3 columns × 16 bytes = 48 bytes total
104
+ * - `mat4` occupies 4 columns × 16 bytes = 64 bytes total
105
+ *
106
+ * 2. **Alignment Issues with mat3**:
107
+ * - The 48-byte size of `mat3` creates awkward alignment and padding requirements
108
+ * - Different GPU drivers may handle `mat3` padding inconsistently
109
+ * - This can lead to data misalignment and incorrect transformations
110
+ *
111
+ * 3. **Why mat4 is Preferred**:
112
+ * - `mat4` has clean 64-byte alignment (power of 2)
113
+ * - Consistent behavior across all GPU drivers and platforms
114
+ * - The shader can easily extract the 3x3 portion: `mat3 transformMat3 = mat3(transformationMatrix)`
115
+ * - The 4th column is set to `[0, 0, 0, 1]` (homogeneous coordinate) which doesn't affect 2D transforms
116
+ *
117
+ * ### Conversion Process
118
+ *
119
+ * The 3x3 matrix is converted to 4x4 by:
120
+ * - Placing the 3x3 values in the top-left corner (preserving column-major order)
121
+ * - Setting the 4th column to `[0, 0, 0, 1]` (homogeneous coordinate)
122
+ * - The 4th row is implicitly `[0, 0, 0, 1]` due to column-major storage
123
+ *
124
+ * This creates a valid 4x4 transformation matrix that:
125
+ * - Maintains the same 2D transformation behavior
126
+ * - Satisfies std140 alignment requirements
127
+ * - Works consistently across all GPU platforms
128
+ *
129
+ * ### Usage in Shaders
130
+ *
131
+ * Shaders using uniform buffers receive this as `mat4` and extract the 3x3 portion:
132
+ * ```glsl
133
+ * layout(std140) uniform uniforms {
134
+ * mat4 transformationMatrix;
135
+ * } uniforms;
136
+ *
137
+ * mat3 transformMat3 = mat3(uniforms.transformationMatrix);
138
+ * vec3 final = transformMat3 * vec3(position, 1);
139
+ * ```
140
+ *
141
+ * @returns A 16-element array representing a 4x4 matrix in column-major order,
142
+ * suitable for std140 uniform buffer layout. The matrix preserves the
143
+ * 2D transformation from the original 3x3 matrix.
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * const matrix = store.transformationMatrix4x4;
148
+ * uniformStore.setUniforms({
149
+ * uniforms: {
150
+ * transformationMatrix: matrix // Expects mat4x4<f32> in shader
151
+ * }
152
+ * });
153
+ * ```
154
+ */
155
+ public get transformationMatrix4x4 (): Mat4Array {
156
+ const t = this.transform
157
+
158
+ // Validate transform array length
159
+ if (t.length !== 9) {
160
+ throw new Error(`Transform must be a 9-element array (3x3 matrix), got ${t.length} elements`)
161
+ }
162
+
163
+ // Convert 3x3 to 4x4 matrix in column-major order
164
+ return [
165
+ t[0], t[1], t[2], 0, // Column 0
166
+ t[3], t[4], t[5], 0, // Column 1
167
+ t[6], t[7], t[8], 0, // Column 2
168
+ 0, 0, 0, 1, // Column 3 (homogeneous)
169
+ ]
170
+ }
171
+
172
+ public set backgroundColor (color: [number, number, number, number]) {
173
+ this._backgroundColor = color
174
+ const brightness = rgbToBrightness(color[0], color[1], color[2])
175
+ document.documentElement.style.setProperty('--cosmosgl-attribution-color', brightness > 0.65 ? 'black' : 'white')
176
+ document.documentElement.style.setProperty('--cosmosgl-error-message-color', brightness > 0.65 ? 'black' : 'white')
177
+ if (this.div) this.div.style.backgroundColor = `rgba(${color[0] * 255}, ${color[1] * 255}, ${color[2] * 255}, ${color[3]})`
178
+
179
+ this.isDarkenGreyout = brightness < 0.65
180
+ }
181
+
182
+ public addRandomSeed (seed: number | string): void {
183
+ this.random = this.random.clone(seed)
184
+ }
185
+
186
+ public getRandomFloat (min: number, max: number): number {
187
+ return this.random.float(min, max)
188
+ }
189
+
190
+ /**
191
+ * If the config parameter `spaceSize` exceeds the limits of WebGL,
192
+ * it reduces the space size without changing the config parameter.
193
+ */
194
+ public adjustSpaceSize (configSpaceSize: number, webglMaxTextureSize: number): void {
195
+ if (configSpaceSize >= webglMaxTextureSize) {
196
+ this.adjustedSpaceSize = webglMaxTextureSize / 2
197
+ console.warn(`The \`spaceSize\` has been reduced to ${this.adjustedSpaceSize} due to WebGL limits`)
198
+ } else this.adjustedSpaceSize = configSpaceSize
199
+ }
200
+
201
+ /**
202
+ * Sets the WebGL texture size limit for use in atlas creation and other texture operations.
203
+ */
204
+ public setWebGLMaxTextureSize (webglMaxTextureSize: number): void {
205
+ this.webglMaxTextureSize = webglMaxTextureSize
206
+ }
207
+
208
+ public updateScreenSize (width: number, height: number): void {
209
+ const { adjustedSpaceSize } = this
210
+ this.screenSize = [width, height]
211
+ this.scalePointX
212
+ .domain([0, adjustedSpaceSize])
213
+ .range([(width - adjustedSpaceSize) / 2, (width + adjustedSpaceSize) / 2])
214
+ this.scalePointY
215
+ .domain([adjustedSpaceSize, 0])
216
+ .range([(height - adjustedSpaceSize) / 2, (height + adjustedSpaceSize) / 2])
217
+ }
218
+
219
+ public scaleX (x: number): number {
220
+ return this.scalePointX(x)
221
+ }
222
+
223
+ public scaleY (y: number): number {
224
+ return this.scalePointY(y)
225
+ }
226
+
227
+ public setHoveredPointRingColor (color: string | [number, number, number, number]): void {
228
+ const convertedRgba = getRgbaColor(color)
229
+ this.hoveredPointRingColor[0] = convertedRgba[0]
230
+ this.hoveredPointRingColor[1] = convertedRgba[1]
231
+ this.hoveredPointRingColor[2] = convertedRgba[2]
232
+ }
233
+
234
+ public setFocusedPointRingColor (color: string | [number, number, number, number]): void {
235
+ const convertedRgba = getRgbaColor(color)
236
+ this.focusedPointRingColor[0] = convertedRgba[0]
237
+ this.focusedPointRingColor[1] = convertedRgba[1]
238
+ this.focusedPointRingColor[2] = convertedRgba[2]
239
+ }
240
+
241
+ public setGreyoutPointColor (color: string | [number, number, number, number] | undefined): void {
242
+ if (color === undefined) {
243
+ this.greyoutPointColor = [-1, -1, -1, -1]
244
+ return
245
+ }
246
+ const convertedRgba = getRgbaColor(color)
247
+ this.greyoutPointColor[0] = convertedRgba[0]
248
+ this.greyoutPointColor[1] = convertedRgba[1]
249
+ this.greyoutPointColor[2] = convertedRgba[2]
250
+ this.greyoutPointColor[3] = convertedRgba[3]
251
+ }
252
+
253
+ public updateLinkHoveringEnabled (config: Pick<GraphConfigInterface, 'onLinkClick' | 'onLinkMouseOver' | 'onLinkMouseOut'>): void {
254
+ this.isLinkHoveringEnabled = !!(config.onLinkClick || config.onLinkMouseOver || config.onLinkMouseOut)
255
+ if (!this.isLinkHoveringEnabled) {
256
+ this.hoveredLinkIndex = undefined
257
+ }
258
+ }
259
+
260
+ public setHoveredLinkColor (color?: string | [number, number, number, number]): void {
261
+ if (color === undefined) {
262
+ this.hoveredLinkColor = [-1, -1, -1, -1]
263
+ return
264
+ }
265
+ const convertedRgba = getRgbaColor(color)
266
+ this.hoveredLinkColor[0] = convertedRgba[0]
267
+ this.hoveredLinkColor[1] = convertedRgba[1]
268
+ this.hoveredLinkColor[2] = convertedRgba[2]
269
+ this.hoveredLinkColor[3] = convertedRgba[3]
270
+ }
271
+
272
+ public setFocusedPoint (index?: number): void {
273
+ if (index !== undefined) {
274
+ this.focusedPoint = { index }
275
+ } else this.focusedPoint = undefined
276
+ }
277
+
278
+ public addAlpha (decay: number): number {
279
+ return (this.alphaTarget - this.alpha) * this.alphaDecay(decay)
280
+ }
281
+
282
+ private alphaDecay = (decay: number): number => 1 - Math.pow(ALPHA_MIN, 1 / decay)
283
+ }
@@ -0,0 +1,148 @@
1
+ import { zoom, ZoomTransform, zoomIdentity, D3ZoomEvent } from 'd3-zoom'
2
+ import { extent } from 'd3-array'
3
+ import { mat3 } from 'gl-matrix'
4
+ import { Store } from '@/graph/modules/Store'
5
+ import { GraphConfigInterface } from '@/graph/config'
6
+ import { clamp } from '@/graph/helper'
7
+
8
+ export class Zoom {
9
+ public readonly store: Store
10
+ public readonly config: GraphConfigInterface
11
+ public eventTransform = zoomIdentity
12
+ public behavior = zoom<HTMLCanvasElement, undefined>()
13
+ .scaleExtent([0.001, Infinity])
14
+ .on('start', (e: D3ZoomEvent<HTMLCanvasElement, undefined>) => {
15
+ this.isRunning = true
16
+ const userDriven = !!e.sourceEvent
17
+ this.config?.onZoomStart?.(e, userDriven)
18
+ })
19
+ .on('zoom', (e: D3ZoomEvent<HTMLCanvasElement, undefined>) => {
20
+ this.eventTransform = e.transform
21
+ const { eventTransform: { x, y, k }, store: { transform, screenSize } } = this
22
+ const w = screenSize[0]
23
+ const h = screenSize[1]
24
+ if (!w || !h) return
25
+ mat3.projection(transform, w, h)
26
+ mat3.translate(transform, transform, [x, y])
27
+ mat3.scale(transform, transform, [k, k])
28
+ mat3.translate(transform, transform, [w / 2, h / 2])
29
+ mat3.scale(transform, transform, [w / 2, h / 2])
30
+ mat3.scale(transform, transform, [1, -1])
31
+
32
+ const userDriven = !!e.sourceEvent
33
+ this.config?.onZoom?.(e, userDriven)
34
+ })
35
+ .on('end', (e: D3ZoomEvent<HTMLCanvasElement, undefined>) => {
36
+ this.isRunning = false
37
+
38
+ const userDriven = !!e.sourceEvent
39
+ this.config?.onZoomEnd?.(e, userDriven)
40
+ })
41
+
42
+ public isRunning = false
43
+
44
+ public constructor (store: Store, config: GraphConfigInterface) {
45
+ this.store = store
46
+ this.config = config
47
+ }
48
+
49
+ /**
50
+ * Get the zoom transform that will fit the given point positions into the viewport
51
+ *
52
+ * @param positions An array of point positions in the form `[x, y]`
53
+ * @param scale An optional scale factor to apply to the transform
54
+ * @param padding Padding around the viewport in percentage
55
+ */
56
+ public getTransform (positions: [number, number][], scale?: number, padding = 0.1): ZoomTransform {
57
+ if (positions.length === 0) return this.eventTransform
58
+ const { store: { screenSize } } = this
59
+ const width = screenSize[0]
60
+ const height = screenSize[1]
61
+ const xExtent = extent(positions.map(d => d[0])) as [number, number]
62
+ const yExtent = extent(positions.map(d => d[1])) as [number, number]
63
+ xExtent[0] = this.store.scaleX(xExtent[0])
64
+ xExtent[1] = this.store.scaleX(xExtent[1])
65
+ yExtent[0] = this.store.scaleY(yExtent[0])
66
+ yExtent[1] = this.store.scaleY(yExtent[1])
67
+ // Adjust extent with one screen pixel if one point coordinate is set
68
+ if (xExtent[0] === xExtent[1]) {
69
+ xExtent[0] -= 0.5
70
+ xExtent[1] += 0.5
71
+ }
72
+ if (yExtent[0] === yExtent[1]) {
73
+ yExtent[0] += 0.5
74
+ yExtent[1] -= 0.5
75
+ }
76
+
77
+ const xScale = (width * (1 - padding * 2)) / (xExtent[1] - xExtent[0])
78
+ const yScale = (height * (1 - padding * 2)) / (yExtent[0] - yExtent[1])
79
+ const clampedScale = clamp(scale ?? Math.min(xScale, yScale), ...this.behavior.scaleExtent())
80
+ const xCenter = (xExtent[1] + xExtent[0]) / 2
81
+ const yCenter = (yExtent[1] + yExtent[0]) / 2
82
+ const translateX = width / 2 - xCenter * clampedScale
83
+ const translateY = height / 2 - yCenter * clampedScale
84
+
85
+ const transform = zoomIdentity
86
+ .translate(translateX, translateY)
87
+ .scale(clampedScale)
88
+
89
+ return transform
90
+ }
91
+
92
+ public getDistanceToPoint (position: [number, number]): number {
93
+ const { x, y, k } = this.eventTransform
94
+ const point = this.getTransform([position], k)
95
+ const dx = x - point.x
96
+ const dy = y - point.y
97
+ return Math.sqrt(dx * dx + dy * dy)
98
+ }
99
+
100
+ public getMiddlePointTransform (position: [number, number]): ZoomTransform {
101
+ const { store: { screenSize }, eventTransform: { x, y, k } } = this
102
+ const width = screenSize[0]
103
+ const height = screenSize[1]
104
+ const currX = (width / 2 - x) / k
105
+ const currY = (height / 2 - y) / k
106
+ const pointX = this.store.scaleX(position[0])
107
+ const pointY = this.store.scaleY(position[1])
108
+ const centerX = (currX + pointX) / 2
109
+ const centerY = (currY + pointY) / 2
110
+
111
+ const scale = 1
112
+ const translateX = width / 2 - centerX * scale
113
+ const translateY = height / 2 - centerY * scale
114
+
115
+ return zoomIdentity
116
+ .translate(translateX, translateY)
117
+ .scale(scale)
118
+ }
119
+
120
+ public convertScreenToSpacePosition (screenPosition: [number, number]): [number, number] {
121
+ const { eventTransform: { x, y, k }, store: { screenSize } } = this
122
+ const w = screenSize[0]
123
+ const h = screenSize[1]
124
+ const invertedX = (screenPosition[0] - x) / k
125
+ const invertedY = (screenPosition[1] - y) / k
126
+ const spacePosition = [invertedX, (h - invertedY)] as [number, number]
127
+ spacePosition[0] -= (w - this.store.adjustedSpaceSize) / 2
128
+ spacePosition[1] -= (h - this.store.adjustedSpaceSize) / 2
129
+ return spacePosition
130
+ }
131
+
132
+ public convertSpaceToScreenPosition (spacePosition: [number, number]): [number, number] {
133
+ const screenPointX = this.eventTransform.applyX(this.store.scaleX(spacePosition[0]))
134
+ const screenPointY = this.eventTransform.applyY(this.store.scaleY(spacePosition[1]))
135
+ return [screenPointX, screenPointY]
136
+ }
137
+
138
+ public convertSpaceToScreenRadius (spaceRadius: number): number {
139
+ const { config: { scalePointsOnZoom }, store: { maxPointSize }, eventTransform: { k } } = this
140
+ let size = spaceRadius * 2
141
+ if (scalePointsOnZoom) {
142
+ size *= k
143
+ } else {
144
+ size *= Math.min(5.0, Math.max(1.0, k * 0.01))
145
+ }
146
+ return Math.min(size, maxPointSize) / 2
147
+ }
148
+ }
@@ -0,0 +1,28 @@
1
+ import { Device } from '@luma.gl/core'
2
+ import { GraphConfigInterface } from '@/graph/config'
3
+ import { GraphData } from '@/graph/modules/GraphData'
4
+ import { Points } from '@/graph/modules/Points'
5
+ import { Store } from '@/graph/modules/Store'
6
+
7
+ export class CoreModule {
8
+ public readonly device: Device
9
+ public readonly config: GraphConfigInterface
10
+ public readonly store: Store
11
+ public readonly data: GraphData
12
+ public readonly points: Points | undefined
13
+ public _debugRandomNumber = Math.floor(Math.random() * 1000)
14
+
15
+ public constructor (
16
+ device: Device,
17
+ config: GraphConfigInterface,
18
+ store: Store,
19
+ data: GraphData,
20
+ points?: Points
21
+ ) {
22
+ this.device = device
23
+ this.config = config
24
+ this.store = store
25
+ this.data = data
26
+ if (points) this.points = points
27
+ }
28
+ }
@@ -0,0 +1,75 @@
1
+ import { Meta } from "@storybook/blocks";
2
+
3
+ <Meta title="Welcome to cosmos.gl" />
4
+
5
+ <p style={{ fontSize: '2rem', lineHeight: '1.25em' }}>Welcome to cosmos.gl — a high-performance WebGL library for visualizing network graphs and machine learning embeddings.</p>
6
+
7
+ <video style={{ width: '100%' }} src="https://user-images.githubusercontent.com/755708/173392407-9b05cbb6-d39e-4c2c-ab41-50900cfda823.mp4" loop autoPlay muted playsInline>
8
+ </video>
9
+
10
+ <p style={{ fontSize: '1.0rem' }}>Here you can find documentaion and examples of how to use cosmos.gl</p>
11
+
12
+ ---
13
+
14
+ ### Quick Start
15
+
16
+ Install the package:
17
+
18
+ ```bash
19
+ npm install @cosmos.gl/graph
20
+ ```
21
+
22
+
23
+ Get the data, [configure](../?path=/docs/configuration--docs) the graph and run the simulation:
24
+ ```javascript
25
+ import { Graph } from '@cosmos.gl/graph'
26
+
27
+ const div = document.querySelector('div')
28
+ const config = {
29
+ spaceSize: 4096,
30
+ simulationFriction: 0.1, // keeps the graph inert
31
+ simulationGravity: 0, // disables gravity
32
+ simulationRepulsion: 0.5, // increases repulsion between points
33
+ curvedLinks: true, // curved links
34
+ fitViewDelay: 1000, // wait 1 second before fitting the view
35
+ fitViewPadding: 0.3, // centers the graph width padding of ~30% of screen
36
+ rescalePositions: true, // rescale positions
37
+ enableDrag: true, // enable dragging points
38
+ onPointClick: pointIndex => { console.log('Clicked point index: ', pointIndex) },
39
+ onBackgroundClick: () => { console.log('Clicked background') },
40
+ /* ... */
41
+ }
42
+
43
+ const graph = new Graph(div, config)
44
+
45
+ // Points: [x1, y1, x2, y2, x3, y3]
46
+ const pointPositions = new Float32Array([
47
+ 0.0, 0.0, // Point 1 at (0,0)
48
+ 1.0, 0.0, // Point 2 at (1,0)
49
+ 0.5, 1.0, // Point 3 at (0.5,1)
50
+ ]);
51
+
52
+ graph.setPointPositions(pointPositions)
53
+
54
+ // Links: [sourceIndex1, targetIndex1, sourceIndex2, targetIndex2]
55
+ const links = new Float32Array([
56
+ 0, 1, // Link from point 0 to point 1
57
+ 1, 2, // Link from point 1 to point 2
58
+ 2, 0, // Link from point 2 to point 0
59
+ ]);
60
+
61
+ graph.setLinks(links)
62
+
63
+ graph.render()
64
+ ```
65
+
66
+ Try the [Quick Start](../?path=/story/examples-beginners--quick-start) example.
67
+
68
+ ### Examples
69
+ - [Basic Set-Up](../?path=/story/examples-beginners--basic-set-up)
70
+ - [Adding Point Labels](../?path=/story/examples-beginners--point-labels) (via [`@interacta/css-labels`](https://www.npmjs.com/package/@interacta/css-labels))
71
+
72
+
73
+ ---
74
+ Copyright [OpenJS Foundation](https://openjsf.org) and cosmos.gl contributors. All rights reserved. The [OpenJS Foundation](https://openjsf.org) has registered trademarks and uses trademarks. For a list of trademarks of the [OpenJS Foundation](https://openjsf.org), please see our [Trademark Policy](https://trademark-policy.openjsf.org/) and [Trademark List](https://trademark-list.openjsf.org/). Trademarks and logos not indicated on the [list of OpenJS Foundation trademarks](https://trademark-list.openjsf.org) are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.
75
+ [The OpenJS Foundation](https://openjsf.org/) | [Terms of Use](https://terms-of-use.openjsf.org/) | [Privacy Policy](https://privacy-policy.openjsf.org/) | [Bylaws](https://bylaws.openjsf.org/) | [Code of Conduct](https://code-of-conduct.openjsf.org) | [Trademark Policy](https://trademark-policy.openjsf.org/) | [Trademark List](https://trademark-list.openjsf.org/) | [Cookie Policy](https://www.linuxfoundation.org/cookies/)