@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,137 @@
1
+ /**
2
+ * Creates a texture atlas from an array of ImageData objects.
3
+ *
4
+ * A texture atlas is a single large texture that contains multiple smaller images.
5
+ * This allows efficient rendering by reducing the number of texture bindings needed.
6
+ *
7
+ * The atlas uses a grid layout where each image gets a square region sized to
8
+ * accommodate the largest image dimension. Images are placed left-to-right, top-to-bottom.
9
+ *
10
+ * @param imageDataArray - Array of ImageData objects to pack into the atlas
11
+ * @param webglMaxTextureSize - WebGL maximum texture size limit (default: 16384)
12
+ * @returns Atlas data object containing:
13
+ * - atlasData: RGBA pixel data as Uint8Array
14
+ * - atlasSize: Total atlas texture size in pixels
15
+ * - atlasCoords: UV coordinates for each image as Float32Array
16
+ * - atlasCoordsSize: Grid size (number of rows/columns)
17
+ * Returns null if creation fails or no valid images provided
18
+ */
19
+ export function createAtlasDataFromImageData (
20
+ imageDataArray: ImageData[],
21
+ webglMaxTextureSize = 16384
22
+ ): {
23
+ atlasData: Uint8Array;
24
+ atlasSize: number;
25
+ atlasCoords: Float32Array;
26
+ atlasCoordsSize: number;
27
+ } | null {
28
+ // Step 1: Validate input - ensure we have images to process
29
+ if (!imageDataArray?.length) {
30
+ return null
31
+ }
32
+
33
+ // Step 2: Find the maximum dimension across all images
34
+ // The max dimension determines the size of each grid cell in the atlas
35
+ let maxDimension = 0
36
+ for (const imageData of imageDataArray) {
37
+ const dimension = Math.max(imageData.width, imageData.height)
38
+ if (dimension > maxDimension) {
39
+ maxDimension = dimension
40
+ }
41
+ }
42
+
43
+ // Step 3: Validate that we found valid image dimensions
44
+ if (maxDimension === 0) {
45
+ console.warn('Invalid image dimensions: all images have zero width or height')
46
+ return null
47
+ }
48
+
49
+ const originalMaxDimension = maxDimension
50
+
51
+ // Step 4: Calculate optimal atlas grid size
52
+ const atlasCoordsSize = Math.ceil(Math.sqrt(imageDataArray.length))
53
+ let atlasSize = atlasCoordsSize * maxDimension
54
+
55
+ // Step 5: Apply WebGL size limit scaling if necessary
56
+ let scalingFactor = 1.0
57
+
58
+ if (atlasSize > webglMaxTextureSize) {
59
+ // Calculate required scale to fit within WebGL limits
60
+ scalingFactor = webglMaxTextureSize / atlasSize
61
+
62
+ // Apply scaling to both the individual image dimensions and atlas size
63
+ maxDimension = Math.max(1, Math.floor(maxDimension * scalingFactor))
64
+ atlasSize = Math.max(1, Math.floor(atlasSize * scalingFactor))
65
+
66
+ console.warn(
67
+ '🖼️ Atlas scaling required: Original size ' +
68
+ `${(originalMaxDimension * atlasCoordsSize).toLocaleString()}px exceeds WebGL limit ` +
69
+ `${webglMaxTextureSize.toLocaleString()}px. Scaling down to ${atlasSize.toLocaleString()}px ` +
70
+ `(${Math.round(scalingFactor * 100)}% of original quality)`
71
+ )
72
+ }
73
+
74
+ // Step 6: Create buffers for atlas data
75
+ const atlasData = new Uint8Array(atlasSize * atlasSize * 4).fill(0)
76
+ const atlasCoords = new Float32Array(atlasCoordsSize * atlasCoordsSize * 4).fill(-1)
77
+
78
+ // Step 7: Pack each image into the atlas grid
79
+ for (const [index, imageData] of imageDataArray.entries()) {
80
+ const originalWidth = imageData.width
81
+ const originalHeight = imageData.height
82
+ if (originalWidth === 0 || originalHeight === 0) {
83
+ // leave coords at -1 for this index and continue
84
+ continue
85
+ }
86
+
87
+ // Calculate individual scale for this image based on maxDimension
88
+ // This ensures each image fits optimally within its grid cell
89
+ const individualScale = Math.min(1.0, maxDimension / Math.max(originalWidth, originalHeight))
90
+
91
+ const scaledWidth = Math.floor(originalWidth * individualScale)
92
+ const scaledHeight = Math.floor(originalHeight * individualScale)
93
+
94
+ // Calculate grid position (row, column) for this image
95
+ const row = Math.floor(index / atlasCoordsSize)
96
+ const col = index % atlasCoordsSize
97
+
98
+ // Calculate pixel position in the atlas texture
99
+ const atlasX = col * maxDimension
100
+ const atlasY = row * maxDimension
101
+
102
+ // Calculate and store UV coordinates for this image
103
+ atlasCoords[index * 4] = atlasX / atlasSize // minU
104
+ atlasCoords[index * 4 + 1] = atlasY / atlasSize // minV
105
+ atlasCoords[index * 4 + 2] = (atlasX + scaledWidth) / atlasSize // maxU
106
+ atlasCoords[index * 4 + 3] = (atlasY + scaledHeight) / atlasSize // maxV
107
+
108
+ // Copy image pixel data into the atlas texture
109
+ for (let y = 0; y < scaledHeight; y++) {
110
+ for (let x = 0; x < scaledWidth; x++) {
111
+ // Calculate source pixel coordinates (with scaling)
112
+ const srcX = Math.floor(x * (originalWidth / scaledWidth))
113
+ const srcY = Math.floor(y * (originalHeight / scaledHeight))
114
+
115
+ // Calculate source pixel index in the original image
116
+ const srcIndex = (srcY * originalWidth + srcX) * 4
117
+
118
+ // Calculate target pixel index in the atlas texture
119
+ const atlasIndex = ((atlasY + y) * atlasSize + (atlasX + x)) * 4
120
+
121
+ // Copy RGBA values from source to atlas
122
+ atlasData[atlasIndex] = imageData.data[srcIndex] ?? 0 // Red channel
123
+ atlasData[atlasIndex + 1] = imageData.data[srcIndex + 1] ?? 0 // Green channel
124
+ atlasData[atlasIndex + 2] = imageData.data[srcIndex + 2] ?? 0 // Blue channel
125
+ atlasData[atlasIndex + 3] = imageData.data[srcIndex + 3] ?? 255 // Alpha channel
126
+ }
127
+ }
128
+ }
129
+
130
+ // Return the complete atlas data
131
+ return {
132
+ atlasData,
133
+ atlasSize,
134
+ atlasCoords,
135
+ atlasCoordsSize,
136
+ }
137
+ }
@@ -0,0 +1,34 @@
1
+ #version 300 es
2
+ #ifdef GL_ES
3
+ precision highp float;
4
+ #endif
5
+
6
+ uniform sampler2D positionsTexture;
7
+
8
+ #ifdef USE_UNIFORM_BUFFERS
9
+ layout(std140) uniform dragPointUniforms {
10
+ vec2 mousePos;
11
+ float index;
12
+ } dragPoint;
13
+
14
+ #define mousePos dragPoint.mousePos
15
+ #define index dragPoint.index
16
+ #else
17
+ uniform vec2 mousePos;
18
+ uniform float index;
19
+ #endif
20
+
21
+ in vec2 textureCoords;
22
+
23
+ out vec4 fragColor;
24
+
25
+ void main() {
26
+ vec4 pointPosition = texture(positionsTexture, textureCoords);
27
+
28
+ // Check if a point is being dragged
29
+ if (index >= 0.0 && index == pointPosition.b) {
30
+ pointPosition.rg = mousePos.rg;
31
+ }
32
+
33
+ fragColor = pointPosition;
34
+ }
@@ -0,0 +1,44 @@
1
+ #version 300 es
2
+ #ifdef GL_ES
3
+ precision highp float;
4
+ #endif
5
+
6
+ #ifdef USE_UNIFORM_BUFFERS
7
+ layout(std140) uniform drawHighlightedUniforms {
8
+ float size;
9
+ mat4 transformationMatrix;
10
+ float pointsTextureSize;
11
+ float sizeScale;
12
+ float spaceSize;
13
+ vec2 screenSize;
14
+ float scalePointsOnZoom;
15
+ float pointIndex;
16
+ float maxPointSize;
17
+ vec4 color;
18
+ float universalPointOpacity;
19
+ float greyoutOpacity;
20
+ float isDarkenGreyout;
21
+ vec4 backgroundColor;
22
+ vec4 greyoutColor;
23
+ float width;
24
+ } drawHighlighted;
25
+
26
+ #define width drawHighlighted.width
27
+ #else
28
+ uniform float width;
29
+ #endif
30
+
31
+ in vec2 vertexPosition;
32
+ in float pointOpacity;
33
+ in vec3 rgbColor;
34
+
35
+ out vec4 fragColor;
36
+
37
+ const float smoothing = 1.05;
38
+
39
+ void main () {
40
+ float r = dot(vertexPosition, vertexPosition);
41
+ float opacity = smoothstep(r, r * smoothing, 1.0);
42
+ float stroke = smoothstep(width, width * smoothing, r);
43
+ fragColor = vec4(rgbColor, opacity * stroke * pointOpacity);
44
+ }
@@ -0,0 +1,145 @@
1
+ #version 300 es
2
+ #ifdef GL_ES
3
+ precision highp float;
4
+ #endif
5
+
6
+ in vec2 vertexCoord;
7
+
8
+ uniform sampler2D positionsTexture;
9
+ uniform sampler2D pointGreyoutStatusTexture;
10
+
11
+ #ifdef USE_UNIFORM_BUFFERS
12
+ layout(std140) uniform drawHighlightedUniforms {
13
+ float size;
14
+ mat4 transformationMatrix;
15
+ float pointsTextureSize;
16
+ float sizeScale;
17
+ float spaceSize;
18
+ vec2 screenSize;
19
+ float scalePointsOnZoom;
20
+ float pointIndex;
21
+ float maxPointSize;
22
+ vec4 color;
23
+ float universalPointOpacity;
24
+ float greyoutOpacity;
25
+ float isDarkenGreyout;
26
+ vec4 backgroundColor;
27
+ vec4 greyoutColor;
28
+ float width;
29
+ } drawHighlighted;
30
+
31
+ #define size drawHighlighted.size
32
+ #define transformationMatrix drawHighlighted.transformationMatrix
33
+ #define pointsTextureSize drawHighlighted.pointsTextureSize
34
+ #define sizeScale drawHighlighted.sizeScale
35
+ #define spaceSize drawHighlighted.spaceSize
36
+ #define screenSize drawHighlighted.screenSize
37
+ #define scalePointsOnZoom drawHighlighted.scalePointsOnZoom
38
+ #define pointIndex drawHighlighted.pointIndex
39
+ #define maxPointSize drawHighlighted.maxPointSize
40
+ #define color drawHighlighted.color
41
+ #define universalPointOpacity drawHighlighted.universalPointOpacity
42
+ #define greyoutOpacity drawHighlighted.greyoutOpacity
43
+ #define isDarkenGreyout drawHighlighted.isDarkenGreyout
44
+ #define backgroundColor drawHighlighted.backgroundColor
45
+ #define greyoutColor drawHighlighted.greyoutColor
46
+ #else
47
+ uniform float size;
48
+ uniform mat3 transformationMatrix;
49
+ uniform float pointsTextureSize;
50
+ uniform float sizeScale;
51
+ uniform float spaceSize;
52
+ uniform vec2 screenSize;
53
+ uniform float scalePointsOnZoom;
54
+ uniform float pointIndex;
55
+ uniform float maxPointSize;
56
+ uniform vec4 color;
57
+ uniform float universalPointOpacity;
58
+ uniform float greyoutOpacity;
59
+ uniform float isDarkenGreyout;
60
+ uniform vec4 backgroundColor;
61
+ uniform vec4 greyoutColor;
62
+ uniform float width;
63
+ #endif
64
+ out vec2 vertexPosition;
65
+ out float pointOpacity;
66
+ out vec3 rgbColor;
67
+
68
+ float calculatePointSize(float pointSize) {
69
+ float pSize;
70
+
71
+ if (scalePointsOnZoom > 0.0) {
72
+ pSize = pointSize * transformationMatrix[0][0];
73
+ } else {
74
+ pSize = pointSize * min(5.0, max(1.0, transformationMatrix[0][0] * 0.01));
75
+ }
76
+
77
+ return min(pSize, maxPointSize);
78
+ }
79
+
80
+ const float relativeRingRadius = 1.3;
81
+
82
+ void main () {
83
+ vertexPosition = vertexCoord;
84
+
85
+ vec2 textureCoordinates = vec2(mod(pointIndex, pointsTextureSize), floor(pointIndex / pointsTextureSize)) + 0.5;
86
+ vec4 pointPosition = texture(positionsTexture, textureCoordinates / pointsTextureSize);
87
+
88
+ rgbColor = color.rgb;
89
+ pointOpacity = color.a * universalPointOpacity;
90
+ vec4 greyoutStatus = texture(pointGreyoutStatusTexture, textureCoordinates / pointsTextureSize);
91
+ if (greyoutStatus.r > 0.0) {
92
+ if (greyoutColor[0] != -1.0) {
93
+ rgbColor = greyoutColor.rgb;
94
+ pointOpacity = greyoutColor.a;
95
+ } else {
96
+ // If greyoutColor is not set, make color lighter or darker based on isDarkenGreyout
97
+ float blendFactor = 0.65; // Controls how much to modify (0.0 = original, 1.0 = target color)
98
+
99
+ #ifdef USE_UNIFORM_BUFFERS
100
+ if (isDarkenGreyout > 0.0) {
101
+ // Darken the color
102
+ rgbColor = mix(rgbColor, vec3(0.2), blendFactor);
103
+ } else {
104
+ // Lighten the color
105
+ rgbColor = mix(rgbColor, max(backgroundColor.rgb, vec3(0.8)), blendFactor);
106
+ }
107
+ #else
108
+ if (isDarkenGreyout > 0.0) {
109
+ // Darken the color
110
+ rgbColor = mix(rgbColor, vec3(0.2), blendFactor);
111
+ } else {
112
+ // Lighten the color
113
+ rgbColor = mix(rgbColor, max(backgroundColor.rgb, vec3(0.8)), blendFactor);
114
+ }
115
+ #endif
116
+ }
117
+
118
+ if (greyoutOpacity != -1.0) {
119
+ pointOpacity *= greyoutOpacity;
120
+ }
121
+ }
122
+
123
+ // Calculate point radius
124
+ float pointSize = (calculatePointSize(size * sizeScale) * relativeRingRadius) / transformationMatrix[0][0];
125
+ float radius = pointSize * 0.5;
126
+
127
+ // Calculate point position in screen space
128
+ vec2 a = pointPosition.xy;
129
+ vec2 b = pointPosition.xy + vec2(0.0, radius);
130
+ vec2 xBasis = b - a;
131
+ vec2 yBasis = normalize(vec2(-xBasis.y, xBasis.x));
132
+ vec2 pointPositionInScreenSpace = a + xBasis * vertexCoord.x + yBasis * radius * vertexCoord.y;
133
+
134
+ // Transform point position to normalized device coordinates
135
+ vec2 p = 2.0 * pointPositionInScreenSpace / spaceSize - 1.0;
136
+ p *= spaceSize / screenSize;
137
+ #ifdef USE_UNIFORM_BUFFERS
138
+ mat3 transformMat3 = mat3(transformationMatrix);
139
+ vec3 final = transformMat3 * vec3(p, 1);
140
+ #else
141
+ vec3 final = transformationMatrix * vec3(p, 1);
142
+ #endif
143
+
144
+ gl_Position = vec4(final.rg, 0, 1);
145
+ }
@@ -0,0 +1,259 @@
1
+ #version 300 es
2
+ #ifdef GL_ES
3
+ precision highp float;
4
+ #endif
5
+
6
+ uniform sampler2D imageAtlasTexture;
7
+
8
+ #ifdef USE_UNIFORM_BUFFERS
9
+ layout(std140) uniform drawFragmentUniforms {
10
+ float greyoutOpacity;
11
+ float pointOpacity;
12
+ float isDarkenGreyout;
13
+ vec4 backgroundColor;
14
+ } drawFragment;
15
+
16
+ #define greyoutOpacity drawFragment.greyoutOpacity
17
+ #define pointOpacity drawFragment.pointOpacity
18
+ #define isDarkenGreyout drawFragment.isDarkenGreyout
19
+ #define backgroundColor drawFragment.backgroundColor
20
+ #else
21
+ uniform float greyoutOpacity;
22
+ uniform float pointOpacity;
23
+ uniform float isDarkenGreyout;
24
+ uniform vec4 backgroundColor;
25
+ #endif
26
+
27
+
28
+ in float pointShape;
29
+ in float isGreyedOut;
30
+ in vec4 shapeColor;
31
+ in vec4 imageAtlasUV;
32
+ in float shapeSize;
33
+ in float imageSizeVarying;
34
+ in float overallSize;
35
+
36
+ out vec4 fragColor;
37
+
38
+ // Smoothing controls the smoothness of the point's edge
39
+ const float smoothing = 0.9;
40
+
41
+ // Shape constants
42
+ const float CIRCLE = 0.0;
43
+ const float SQUARE = 1.0;
44
+ const float TRIANGLE = 2.0;
45
+ const float DIAMOND = 3.0;
46
+ const float PENTAGON = 4.0;
47
+ const float HEXAGON = 5.0;
48
+ const float STAR = 6.0;
49
+ const float CROSS = 7.0;
50
+ const float NONE = 8.0;
51
+
52
+ // Distance functions for different shapes
53
+ float circleDistance(vec2 p) {
54
+ return dot(p, p);
55
+ }
56
+
57
+ // Function to apply greyout logic to image colors
58
+ vec4 applyGreyoutToImage(vec4 imageColor, float isGreyedOutValue) {
59
+ vec3 finalColor = imageColor.rgb;
60
+ float finalAlpha = imageColor.a;
61
+
62
+ if (isGreyedOutValue > 0.0) {
63
+ float blendFactor = 0.65; // Controls how much to modify (0.0 = original, 1.0 = target color)
64
+
65
+ if (isDarkenGreyout > 0.0) {
66
+ finalColor = mix(finalColor, vec3(0.2), blendFactor);
67
+ } else {
68
+ finalColor = mix(finalColor, max(backgroundColor.rgb, vec3(0.8)), blendFactor);
69
+ }
70
+ }
71
+
72
+ return vec4(finalColor, finalAlpha);
73
+ }
74
+
75
+ float squareDistance(vec2 p) {
76
+ vec2 d = abs(p) - vec2(0.8);
77
+ return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
78
+ }
79
+
80
+ float triangleDistance(vec2 p) {
81
+ const float k = sqrt(3.0); // ≈1.732; slope of 60° lines for an equilateral triangle
82
+ p.x = abs(p.x) - 0.9; // fold the X axis and shift: brings left and right halves together
83
+ p.y = p.y + 0.55; // move the whole shape up slightly so it is centred vertically
84
+
85
+ // reflect points that fall outside the main triangle back inside, to reuse the same maths
86
+ if (p.x + k * p.y > 0.0)
87
+ p = vec2(p.x - k * p.y, -k * p.x - p.y) / 2.0;
88
+
89
+ p.x -= clamp(p.x, -1.0, 0.0); // clip any remainder on the left side
90
+
91
+ // Return signed distance: negative = inside; positive = outside
92
+ return -length(p) * sign(p.y);
93
+ }
94
+
95
+ float diamondDistance(vec2 p) {
96
+ // aspect > 1 → taller diamond
97
+ const float aspect = 1.2;
98
+ return abs(p.x) + abs(p.y) / aspect - 0.8;
99
+ }
100
+
101
+ float pentagonDistance(vec2 p) {
102
+ // Regular pentagon signed-distance (Inigo Quilez)
103
+ const vec3 k = vec3(0.809016994, 0.587785252, 0.726542528);
104
+ p.x = abs(p.x);
105
+
106
+ // Reflect across the two tilted edges ─ only if point is outside
107
+ p -= 2.0 * min(dot(vec2(-k.x, k.y), p), 0.0) * vec2(-k.x, k.y);
108
+ p -= 2.0 * min(dot(vec2( k.x, k.y), p), 0.0) * vec2( k.x, k.y);
109
+
110
+ // Clip against the top horizontal edge (keeps top point sharp)
111
+ p -= vec2(clamp(p.x, -k.z * k.x, k.z * k.x), k.z);
112
+
113
+ // Return signed distance (negative → inside, positive → outside)
114
+ return length(p) * sign(p.y);
115
+ }
116
+
117
+ float hexagonDistance(vec2 p) {
118
+ const vec3 k = vec3(-0.866025404, 0.5, 0.577350269);
119
+ p = abs(p);
120
+ p -= 2.0 * min(dot(k.xy, p), 0.0) * k.xy;
121
+ p -= vec2(clamp(p.x, -k.z * 0.8, k.z * 0.8), 0.8);
122
+ return length(p) * sign(p.y);
123
+ }
124
+
125
+ float starDistance(vec2 p) {
126
+ // 5-point star signed-distance function (adapted from Inigo Quilez)
127
+ // r – outer radius, rf – inner/outer radius ratio
128
+ const float r = 0.9;
129
+ const float rf = 0.45;
130
+
131
+ // Pre-computed rotation vectors for the star arms (36° increments)
132
+ const vec2 k1 = vec2(0.809016994, -0.587785252);
133
+ const vec2 k2 = vec2(-k1.x, k1.y);
134
+
135
+ // Fold the plane into a single arm sector
136
+ p.x = abs(p.x);
137
+ p -= 2.0 * max(dot(k1, p), 0.0) * k1;
138
+ p -= 2.0 * max(dot(k2, p), 0.0) * k2;
139
+ p.x = abs(p.x);
140
+
141
+ // Translate so the top tip of the star lies on the X-axis
142
+ p.y -= r;
143
+
144
+ // Vector describing the edge between an outer tip and its adjacent inner point
145
+ vec2 ba = rf * vec2(-k1.y, k1.x) - vec2(0.0, 1.0);
146
+ // Project the point onto that edge and clamp the projection to the segment
147
+ float h = clamp(dot(p, ba) / dot(ba, ba), 0.0, r);
148
+
149
+ // Return signed distance (negative => inside, positive => outside)
150
+ return length(p - ba * h) * sign(p.y * ba.x - p.x * ba.y);
151
+ }
152
+
153
+ float crossDistance(vec2 p) {
154
+ // Signed distance function for a cross (union of two rectangles)
155
+ // Adapted from Inigo Quilez (https://iquilezles.org/)
156
+ // Each arm has half-sizes 0.3 (thickness) and 0.8 (length)
157
+ p = abs(p);
158
+ if (p.y > p.x) p = p.yx; // exploit symmetry
159
+
160
+ vec2 q = p - vec2(0.8, 0.3); // subtract half-sizes (length, thickness)
161
+
162
+ // Standard rectangle SDF, then take union of the two arms
163
+ return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0);
164
+ }
165
+
166
+ float getShapeDistance(vec2 p, float shape) {
167
+ if (shape == SQUARE) return squareDistance(p);
168
+ else if (shape == TRIANGLE) return triangleDistance(p);
169
+ else if (shape == DIAMOND) return diamondDistance(p);
170
+ else if (shape == PENTAGON) return pentagonDistance(p);
171
+ else if (shape == HEXAGON) return hexagonDistance(p);
172
+ else if (shape == STAR) return starDistance(p);
173
+ else if (shape == CROSS) return crossDistance(p);
174
+ else return circleDistance(p); // Default to circle
175
+ }
176
+
177
+ void main() {
178
+ // Discard the fragment if the point is fully transparent and has no image
179
+ if (shapeColor.a == 0.0 && imageAtlasUV.x == -1.0) {
180
+ discard;
181
+ }
182
+
183
+ // Discard the fragment if the point has no shape and no image
184
+ if (pointShape == NONE && imageAtlasUV.x == -1.0) {
185
+ discard;
186
+ }
187
+
188
+ // Calculate coordinates within the point
189
+ vec2 pointCoord = 2.0 * gl_PointCoord - 1.0;
190
+
191
+ vec4 finalShapeColor = vec4(0.0);
192
+ vec4 finalImageColor = vec4(0.0);
193
+
194
+ // Handle shape rendering with centering logic
195
+ if (pointShape != NONE) {
196
+ // Calculate shape coordinates with centering
197
+ vec2 shapeCoord = pointCoord;
198
+ if (overallSize > shapeSize && shapeSize > 0.0) {
199
+ // Shape is smaller than overall size, center it
200
+ float scale = shapeSize / overallSize;
201
+ shapeCoord = pointCoord / scale;
202
+ }
203
+
204
+ float opacity;
205
+ if (pointShape == CIRCLE) {
206
+ // For circles, use the original distance calculation
207
+ float pointCenterDistance = dot(shapeCoord, shapeCoord);
208
+ opacity = 1.0 - smoothstep(smoothing, 1.0, pointCenterDistance);
209
+ } else {
210
+ // For other shapes, use the shape distance function
211
+ float shapeDistance = getShapeDistance(shapeCoord, pointShape);
212
+ opacity = 1.0 - smoothstep(-0.01, 0.01, shapeDistance);
213
+ }
214
+ opacity *= shapeColor.a;
215
+
216
+ finalShapeColor = vec4(shapeColor.rgb, opacity);
217
+ }
218
+
219
+ // Handle image rendering with centering logic
220
+ if (imageAtlasUV.x != -1.0) {
221
+ // Calculate image coordinates with centering
222
+ vec2 imageCoord = pointCoord;
223
+ if (overallSize > imageSizeVarying && imageSizeVarying > 0.0) {
224
+ // Image is smaller than overall size, center it
225
+ float scale = imageSizeVarying / overallSize;
226
+ imageCoord = pointCoord / scale;
227
+
228
+ // Check if we're outside the valid image area
229
+ if (abs(imageCoord.x) > 1.0 || abs(imageCoord.y) > 1.0) {
230
+ // We're outside the image bounds, don't render the image
231
+ finalImageColor = vec4(0.0);
232
+ } else {
233
+ // Sample from texture atlas
234
+ vec2 atlasUV = mix(imageAtlasUV.xy, imageAtlasUV.zw, (imageCoord + 1.0) * 0.5);
235
+ vec4 imageColor = texture(imageAtlasTexture, atlasUV);
236
+ finalImageColor = applyGreyoutToImage(imageColor, isGreyedOut);
237
+ }
238
+ } else {
239
+ // Image is same size or larger than overall size, no scaling needed
240
+ // Sample from texture atlas
241
+ vec2 atlasUV = mix(imageAtlasUV.xy, imageAtlasUV.zw, (imageCoord + 1.0) * 0.5);
242
+ vec4 imageColor = texture(imageAtlasTexture, atlasUV);
243
+ finalImageColor = applyGreyoutToImage(imageColor, isGreyedOut);
244
+ }
245
+ }
246
+
247
+ float finalPointAlpha = max(finalShapeColor.a, finalImageColor.a);
248
+ if (isGreyedOut > 0.0 && greyoutOpacity != -1.0) {
249
+ finalPointAlpha *= greyoutOpacity;
250
+ } else {
251
+ finalPointAlpha *= pointOpacity;
252
+ }
253
+
254
+ // Blend image color above point color
255
+ fragColor = vec4(
256
+ mix(finalShapeColor.rgb, finalImageColor.rgb, finalImageColor.a),
257
+ finalPointAlpha
258
+ );
259
+ }