@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.
- package/.eslintrc +147 -0
- package/.github/SECURITY.md +13 -0
- package/.github/dco.yml +4 -0
- package/.github/workflows/github_pages.yml +54 -0
- package/.storybook/main.ts +26 -0
- package/.storybook/manager-head.html +1 -0
- package/.storybook/manager.ts +14 -0
- package/.storybook/preview.ts +29 -0
- package/.storybook/style.css +3 -0
- package/CHARTER.md +69 -0
- package/CODE_OF_CONDUCT.md +178 -0
- package/CONTRIBUTING.md +22 -0
- package/GOVERNANCE.md +21 -0
- package/cosmos-2-0-migration-notes.md +98 -0
- package/cosmos_awesome.md +96 -0
- package/dist/config.d.ts +5 -9
- package/dist/graph/utils/error-message.d.ts +1 -1
- package/dist/helper.d.ts +39 -2
- package/dist/index-FUIgayhu.js +19827 -0
- package/dist/index-FUIgayhu.js.map +1 -0
- package/dist/index.d.ts +17 -64
- package/dist/index.js +14 -14654
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1062 -475
- package/dist/index.min.js.map +1 -1
- package/dist/modules/Clusters/index.d.ts +11 -3
- package/dist/modules/ForceCenter/index.d.ts +10 -3
- package/dist/modules/ForceGravity/index.d.ts +5 -1
- package/dist/modules/ForceLink/index.d.ts +8 -5
- package/dist/modules/ForceManyBody/index.d.ts +16 -7
- package/dist/modules/ForceMouse/index.d.ts +5 -1
- package/dist/modules/GraphData/index.d.ts +0 -1
- package/dist/modules/Lines/index.d.ts +11 -5
- package/dist/modules/Points/index.d.ts +31 -13
- package/dist/modules/Store/index.d.ts +93 -0
- package/dist/modules/core-module.d.ts +3 -3
- package/dist/stories/beginners/basic-set-up/data-gen.d.ts +4 -0
- package/dist/stories/beginners/basic-set-up/index.d.ts +6 -0
- package/dist/stories/beginners/link-hovering/data-generator.d.ts +19 -0
- package/dist/stories/beginners/link-hovering/index.d.ts +6 -0
- package/dist/stories/beginners/point-labels/data.d.ts +13 -0
- package/dist/stories/beginners/point-labels/index.d.ts +10 -0
- package/dist/stories/beginners/point-labels/labels.d.ts +8 -0
- package/dist/stories/beginners/quick-start.d.ts +6 -0
- package/dist/stories/beginners/remove-points/config.d.ts +2 -0
- package/dist/stories/beginners/remove-points/data-gen.d.ts +4 -0
- package/dist/stories/beginners/remove-points/index.d.ts +6 -0
- package/dist/stories/beginners.stories.d.ts +10 -0
- package/dist/stories/clusters/polygon-selection/index.d.ts +6 -0
- package/dist/stories/clusters/polygon-selection/polygon.d.ts +20 -0
- package/dist/stories/clusters/radial.d.ts +6 -0
- package/dist/stories/clusters/with-labels.d.ts +6 -0
- package/dist/stories/clusters/worm.d.ts +6 -0
- package/dist/stories/clusters.stories.d.ts +9 -0
- package/dist/stories/create-cluster-labels.d.ts +4 -0
- package/dist/stories/create-cosmos.d.ts +17 -0
- package/dist/stories/create-story.d.ts +16 -0
- package/dist/stories/experiments/full-mesh.d.ts +6 -0
- package/dist/stories/experiments/mesh-with-holes.d.ts +6 -0
- package/dist/stories/experiments.stories.d.ts +7 -0
- package/dist/stories/generate-mesh-data.d.ts +12 -0
- package/dist/stories/geospatial/moscow-metro-stations/index.d.ts +16 -0
- package/dist/stories/geospatial/moscow-metro-stations/moscow-metro-coords.d.ts +1 -0
- package/dist/stories/geospatial/moscow-metro-stations/point-colors.d.ts +1 -0
- package/dist/stories/geospatial.stories.d.ts +6 -0
- package/dist/stories/shapes/all-shapes/index.d.ts +6 -0
- package/dist/stories/shapes/image-example/index.d.ts +6 -0
- package/dist/stories/shapes.stories.d.ts +7 -0
- package/dist/stories/test-luma-migration.d.ts +6 -0
- package/dist/stories/test.stories.d.ts +6 -0
- package/dist/webgl-device-B9ewDj5L.js +3923 -0
- package/dist/webgl-device-B9ewDj5L.js.map +1 -0
- package/logo.svg +3 -0
- package/package.json +5 -7
- package/rollup.config.js +70 -0
- package/src/config.ts +728 -0
- package/src/declaration.d.ts +12 -0
- package/src/graph/utils/error-message.ts +23 -0
- package/src/helper.ts +113 -0
- package/src/index.ts +1769 -0
- package/src/modules/Clusters/calculate-centermass.frag +12 -0
- package/src/modules/Clusters/calculate-centermass.vert +38 -0
- package/src/modules/Clusters/force-cluster.frag +55 -0
- package/src/modules/Clusters/index.ts +578 -0
- package/src/modules/Drag/index.ts +33 -0
- package/src/modules/FPSMonitor/css.ts +53 -0
- package/src/modules/FPSMonitor/index.ts +28 -0
- package/src/modules/ForceCenter/calculate-centermass.frag +9 -0
- package/src/modules/ForceCenter/calculate-centermass.vert +26 -0
- package/src/modules/ForceCenter/force-center.frag +37 -0
- package/src/modules/ForceCenter/index.ts +284 -0
- package/src/modules/ForceGravity/force-gravity.frag +40 -0
- package/src/modules/ForceGravity/index.ts +107 -0
- package/src/modules/ForceLink/force-spring.ts +89 -0
- package/src/modules/ForceLink/index.ts +293 -0
- package/src/modules/ForceManyBody/calculate-level.frag +9 -0
- package/src/modules/ForceManyBody/calculate-level.vert +37 -0
- package/src/modules/ForceManyBody/force-centermass.frag +61 -0
- package/src/modules/ForceManyBody/force-level.frag +138 -0
- package/src/modules/ForceManyBody/index.ts +525 -0
- package/src/modules/ForceManyBody/quadtree-frag-shader.ts +89 -0
- package/src/modules/ForceManyBodyQuadtree/calculate-level.frag +9 -0
- package/src/modules/ForceManyBodyQuadtree/calculate-level.vert +25 -0
- package/src/modules/ForceManyBodyQuadtree/index.ts +157 -0
- package/src/modules/ForceManyBodyQuadtree/quadtree-frag-shader.ts +93 -0
- package/src/modules/ForceMouse/force-mouse.frag +35 -0
- package/src/modules/ForceMouse/index.ts +102 -0
- package/src/modules/GraphData/index.ts +383 -0
- package/src/modules/Lines/draw-curve-line.frag +59 -0
- package/src/modules/Lines/draw-curve-line.vert +248 -0
- package/src/modules/Lines/geometry.ts +18 -0
- package/src/modules/Lines/hovered-line-index.frag +43 -0
- package/src/modules/Lines/hovered-line-index.vert +13 -0
- package/src/modules/Lines/index.ts +661 -0
- package/src/modules/Points/atlas-utils.ts +137 -0
- package/src/modules/Points/drag-point.frag +34 -0
- package/src/modules/Points/draw-highlighted.frag +44 -0
- package/src/modules/Points/draw-highlighted.vert +145 -0
- package/src/modules/Points/draw-points.frag +259 -0
- package/src/modules/Points/draw-points.vert +203 -0
- package/src/modules/Points/fill-sampled-points.frag +12 -0
- package/src/modules/Points/fill-sampled-points.vert +51 -0
- package/src/modules/Points/find-hovered-point.frag +15 -0
- package/src/modules/Points/find-hovered-point.vert +90 -0
- package/src/modules/Points/find-points-on-area-selection.frag +88 -0
- package/src/modules/Points/find-points-on-polygon-selection.frag +89 -0
- package/src/modules/Points/index.ts +2292 -0
- package/src/modules/Points/track-positions.frag +30 -0
- package/src/modules/Points/update-position.frag +39 -0
- package/src/modules/Shared/buffer.ts +39 -0
- package/src/modules/Shared/clear.frag +10 -0
- package/src/modules/Shared/quad.vert +13 -0
- package/src/modules/Store/index.ts +283 -0
- package/src/modules/Zoom/index.ts +148 -0
- package/src/modules/core-module.ts +28 -0
- package/src/stories/1. welcome.mdx +75 -0
- package/src/stories/2. configuration.mdx +111 -0
- package/src/stories/3. api-reference.mdx +591 -0
- package/src/stories/beginners/basic-set-up/data-gen.ts +33 -0
- package/src/stories/beginners/basic-set-up/index.ts +167 -0
- package/src/stories/beginners/basic-set-up/style.css +35 -0
- package/src/stories/beginners/link-hovering/data-generator.ts +198 -0
- package/src/stories/beginners/link-hovering/index.ts +65 -0
- package/src/stories/beginners/link-hovering/style.css +73 -0
- package/src/stories/beginners/point-labels/data.ts +73 -0
- package/src/stories/beginners/point-labels/index.ts +69 -0
- package/src/stories/beginners/point-labels/labels.ts +46 -0
- package/src/stories/beginners/point-labels/style.css +16 -0
- package/src/stories/beginners/quick-start.ts +54 -0
- package/src/stories/beginners/remove-points/config.ts +25 -0
- package/src/stories/beginners/remove-points/data-gen.ts +30 -0
- package/src/stories/beginners/remove-points/index.ts +96 -0
- package/src/stories/beginners/remove-points/style.css +31 -0
- package/src/stories/beginners.stories.ts +130 -0
- package/src/stories/clusters/polygon-selection/index.ts +52 -0
- package/src/stories/clusters/polygon-selection/polygon.ts +143 -0
- package/src/stories/clusters/polygon-selection/style.css +8 -0
- package/src/stories/clusters/radial.ts +24 -0
- package/src/stories/clusters/with-labels.ts +54 -0
- package/src/stories/clusters/worm.ts +40 -0
- package/src/stories/clusters.stories.ts +77 -0
- package/src/stories/create-cluster-labels.ts +50 -0
- package/src/stories/create-cosmos.ts +72 -0
- package/src/stories/create-story.ts +51 -0
- package/src/stories/experiments/full-mesh.ts +13 -0
- package/src/stories/experiments/mesh-with-holes.ts +13 -0
- package/src/stories/experiments.stories.ts +43 -0
- package/src/stories/generate-mesh-data.ts +125 -0
- package/src/stories/geospatial/moscow-metro-stations/index.ts +66 -0
- package/src/stories/geospatial/moscow-metro-stations/moscow-metro-coords.ts +1 -0
- package/src/stories/geospatial/moscow-metro-stations/point-colors.ts +46 -0
- package/src/stories/geospatial/moscow-metro-stations/style.css +30 -0
- package/src/stories/geospatial.stories.ts +30 -0
- package/src/stories/shapes/all-shapes/index.ts +73 -0
- package/src/stories/shapes/image-example/icons/box.png +0 -0
- package/src/stories/shapes/image-example/icons/lego.png +0 -0
- package/src/stories/shapes/image-example/icons/s.png +0 -0
- package/src/stories/shapes/image-example/icons/swift.png +0 -0
- package/src/stories/shapes/image-example/icons/toolbox.png +0 -0
- package/src/stories/shapes/image-example/index.ts +246 -0
- package/src/stories/shapes.stories.ts +37 -0
- package/src/stories/test-luma-migration.ts +195 -0
- package/src/stories/test.stories.ts +25 -0
- package/src/variables.ts +68 -0
- package/tsconfig.json +41 -0
- 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
|
+
}
|