@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.
- 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 -18
- 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 -14658
- 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,383 @@
|
|
|
1
|
+
import { getRgbaColor, isNumber } from '@/graph/helper'
|
|
2
|
+
import { GraphConfig } from '@/graph/config'
|
|
3
|
+
|
|
4
|
+
export enum PointShape {
|
|
5
|
+
Circle = 0,
|
|
6
|
+
Square = 1,
|
|
7
|
+
Triangle = 2,
|
|
8
|
+
Diamond = 3,
|
|
9
|
+
Pentagon = 4,
|
|
10
|
+
Hexagon = 5,
|
|
11
|
+
Star = 6,
|
|
12
|
+
Cross = 7,
|
|
13
|
+
None = 8
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class GraphData {
|
|
17
|
+
public inputPointPositions: Float32Array | undefined
|
|
18
|
+
public inputPointColors: Float32Array | undefined
|
|
19
|
+
public inputPointSizes: Float32Array | undefined
|
|
20
|
+
public inputPointShapes: Float32Array | undefined
|
|
21
|
+
public inputImageData: ImageData[] | undefined
|
|
22
|
+
public inputPointImageIndices: Float32Array | undefined
|
|
23
|
+
public inputPointImageSizes: Float32Array | undefined
|
|
24
|
+
public inputLinkColors: Float32Array | undefined
|
|
25
|
+
public inputLinkWidths: Float32Array | undefined
|
|
26
|
+
public inputLinkStrength: Float32Array | undefined
|
|
27
|
+
public inputPointClusters: (number | undefined)[] | undefined
|
|
28
|
+
public inputClusterPositions: (number | undefined)[] | undefined
|
|
29
|
+
public inputClusterStrength: Float32Array | undefined
|
|
30
|
+
|
|
31
|
+
public pointPositions: Float32Array | undefined
|
|
32
|
+
public pointColors: Float32Array | undefined
|
|
33
|
+
public pointSizes: Float32Array | undefined
|
|
34
|
+
public pointShapes: Float32Array | undefined
|
|
35
|
+
public pointImageIndices: Float32Array | undefined
|
|
36
|
+
public pointImageSizes: Float32Array | undefined
|
|
37
|
+
|
|
38
|
+
public inputLinks: Float32Array | undefined
|
|
39
|
+
public links: Float32Array | undefined
|
|
40
|
+
public linkColors: Float32Array | undefined
|
|
41
|
+
public linkWidths: Float32Array | undefined
|
|
42
|
+
public linkArrowsBoolean: boolean[] | undefined
|
|
43
|
+
public linkArrows: number[] | undefined
|
|
44
|
+
public linkStrength: Float32Array | undefined
|
|
45
|
+
|
|
46
|
+
public pointClusters: (number | undefined)[] | undefined
|
|
47
|
+
public clusterPositions: (number | undefined)[] | undefined
|
|
48
|
+
public clusterStrength: Float32Array | undefined
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Each inner array of `sourceIndexToTargetIndices` and `targetIndexToSourceIndices` contains pairs where:
|
|
52
|
+
* - The first value is the target/source index in the point array.
|
|
53
|
+
* - The second value is the link index in the array of links.
|
|
54
|
+
*/
|
|
55
|
+
public sourceIndexToTargetIndices: ([number, number][] | undefined)[] | undefined
|
|
56
|
+
public targetIndexToSourceIndices: ([number, number][] | undefined)[] | undefined
|
|
57
|
+
|
|
58
|
+
public degree: number[] | undefined
|
|
59
|
+
public inDegree: number[] | undefined
|
|
60
|
+
public outDegree: number[] | undefined
|
|
61
|
+
|
|
62
|
+
private _config: GraphConfig
|
|
63
|
+
|
|
64
|
+
public constructor (config: GraphConfig) {
|
|
65
|
+
this._config = config
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public get pointsNumber (): number | undefined {
|
|
69
|
+
return this.pointPositions && this.pointPositions.length / 2
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public get linksNumber (): number | undefined {
|
|
73
|
+
return this.links && this.links.length / 2
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public updatePoints (): void {
|
|
77
|
+
this.pointPositions = this.inputPointPositions
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Updates the point colors based on the input data or default config value.
|
|
82
|
+
*/
|
|
83
|
+
public updatePointColor (): void {
|
|
84
|
+
if (this.pointsNumber === undefined) {
|
|
85
|
+
this.pointColors = undefined
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Sets point colors to default values from config if the input is missing or does not match input points number.
|
|
90
|
+
const defaultRgba = getRgbaColor(this._config.pointColor)
|
|
91
|
+
if (this.inputPointColors === undefined || this.inputPointColors.length / 4 !== this.pointsNumber) {
|
|
92
|
+
this.pointColors = new Float32Array(this.pointsNumber * 4)
|
|
93
|
+
for (let i = 0; i < this.pointColors.length / 4; i++) {
|
|
94
|
+
this.pointColors[i * 4] = defaultRgba[0]
|
|
95
|
+
this.pointColors[i * 4 + 1] = defaultRgba[1]
|
|
96
|
+
this.pointColors[i * 4 + 2] = defaultRgba[2]
|
|
97
|
+
this.pointColors[i * 4 + 3] = defaultRgba[3]
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
this.pointColors = this.inputPointColors
|
|
101
|
+
for (let i = 0; i < this.pointColors.length / 4; i++) {
|
|
102
|
+
if (!isNumber(this.pointColors[i * 4])) this.pointColors[i * 4] = defaultRgba[0]
|
|
103
|
+
if (!isNumber(this.pointColors[i * 4 + 1])) this.pointColors[i * 4 + 1] = defaultRgba[1]
|
|
104
|
+
if (!isNumber(this.pointColors[i * 4 + 2])) this.pointColors[i * 4 + 2] = defaultRgba[2]
|
|
105
|
+
if (!isNumber(this.pointColors[i * 4 + 3])) this.pointColors[i * 4 + 3] = defaultRgba[3]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Updates the point sizes based on the input data or default config value.
|
|
112
|
+
*/
|
|
113
|
+
public updatePointSize (): void {
|
|
114
|
+
if (this.pointsNumber === undefined) {
|
|
115
|
+
this.pointSizes = undefined
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Sets point sizes to default values from config if the input is missing or does not match input points number.
|
|
120
|
+
if (this.inputPointSizes === undefined || this.inputPointSizes.length !== this.pointsNumber) {
|
|
121
|
+
this.pointSizes = new Float32Array(this.pointsNumber).fill(this._config.pointSize)
|
|
122
|
+
} else {
|
|
123
|
+
this.pointSizes = this.inputPointSizes
|
|
124
|
+
for (let i = 0; i < this.pointSizes.length; i++) {
|
|
125
|
+
if (!isNumber(this.pointSizes[i])) {
|
|
126
|
+
this.pointSizes[i] = this._config.pointSize
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Updates the point shapes based on the input data or default shape.
|
|
134
|
+
* Default behavior: Circle (0).
|
|
135
|
+
* Images are rendered above shapes.
|
|
136
|
+
*/
|
|
137
|
+
public updatePointShape (): void {
|
|
138
|
+
if (this.pointsNumber === undefined) {
|
|
139
|
+
this.pointShapes = undefined
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Determine default shape: Circle
|
|
144
|
+
const defaultShape = PointShape.Circle
|
|
145
|
+
|
|
146
|
+
// Sets point shapes to default values if the input is missing or does not match input points number.
|
|
147
|
+
if (this.inputPointShapes === undefined || this.inputPointShapes.length !== this.pointsNumber) {
|
|
148
|
+
this.pointShapes = new Float32Array(this.pointsNumber).fill(defaultShape)
|
|
149
|
+
} else {
|
|
150
|
+
this.pointShapes = new Float32Array(this.inputPointShapes)
|
|
151
|
+
const pointShapes = this.pointShapes
|
|
152
|
+
for (let i = 0; i < pointShapes.length; i++) {
|
|
153
|
+
const shape = pointShapes[i]
|
|
154
|
+
if (shape == null || !isNumber(shape) || shape < 0 || shape > 8) {
|
|
155
|
+
pointShapes[i] = defaultShape
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Updates the point image indices based on the input data or default value (-1 for no image).
|
|
163
|
+
*/
|
|
164
|
+
public updatePointImageIndices (): void {
|
|
165
|
+
if (this.pointsNumber === undefined) {
|
|
166
|
+
this.pointImageIndices = undefined
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Sets point image indices to -1 if input is missing or doesn't match points count
|
|
171
|
+
if (this.inputPointImageIndices === undefined || this.inputPointImageIndices.length !== this.pointsNumber) {
|
|
172
|
+
this.pointImageIndices = new Float32Array(this.pointsNumber).fill(-1)
|
|
173
|
+
} else {
|
|
174
|
+
const pointImageIndices = new Float32Array(this.inputPointImageIndices)
|
|
175
|
+
for (let i = 0; i < pointImageIndices.length; i++) {
|
|
176
|
+
const rawIndex = pointImageIndices[i]
|
|
177
|
+
const imageIndex = (rawIndex === undefined) ? NaN : rawIndex
|
|
178
|
+
if (!Number.isFinite(imageIndex) || imageIndex < 0) {
|
|
179
|
+
pointImageIndices[i] = -1
|
|
180
|
+
} else {
|
|
181
|
+
pointImageIndices[i] = Math.trunc(imageIndex)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
this.pointImageIndices = pointImageIndices
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Updates the point image sizes based on the input data or default to point sizes.
|
|
190
|
+
*/
|
|
191
|
+
public updatePointImageSizes (): void {
|
|
192
|
+
if (this.pointsNumber === undefined) {
|
|
193
|
+
this.pointImageSizes = undefined
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Sets point image sizes to point sizes if the input is missing or does not match input points number.
|
|
198
|
+
if (this.inputPointImageSizes === undefined || this.inputPointImageSizes.length !== this.pointsNumber) {
|
|
199
|
+
this.pointImageSizes = this.pointSizes ? new Float32Array(this.pointSizes) : new Float32Array(this.pointsNumber).fill(this._config.pointSize)
|
|
200
|
+
} else {
|
|
201
|
+
this.pointImageSizes = new Float32Array(this.inputPointImageSizes)
|
|
202
|
+
for (let i = 0; i < this.pointImageSizes.length; i++) {
|
|
203
|
+
if (!isNumber(this.pointImageSizes[i])) {
|
|
204
|
+
this.pointImageSizes[i] = this.pointSizes?.[i] ?? this._config.pointSize
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
public updateLinks (): void {
|
|
211
|
+
this.links = this.inputLinks
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Updates the link colors based on the input data or default config value.
|
|
216
|
+
*/
|
|
217
|
+
public updateLinkColor (): void {
|
|
218
|
+
if (this.linksNumber === undefined) {
|
|
219
|
+
this.linkColors = undefined
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Sets link colors to default values from config if the input is missing or does not match input links number.
|
|
224
|
+
const defaultRgba = getRgbaColor(this._config.linkColor)
|
|
225
|
+
if (this.inputLinkColors === undefined || this.inputLinkColors.length / 4 !== this.linksNumber) {
|
|
226
|
+
this.linkColors = new Float32Array(this.linksNumber * 4)
|
|
227
|
+
|
|
228
|
+
for (let i = 0; i < this.linkColors.length / 4; i++) {
|
|
229
|
+
this.linkColors[i * 4] = defaultRgba[0]
|
|
230
|
+
this.linkColors[i * 4 + 1] = defaultRgba[1]
|
|
231
|
+
this.linkColors[i * 4 + 2] = defaultRgba[2]
|
|
232
|
+
this.linkColors[i * 4 + 3] = defaultRgba[3]
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
this.linkColors = this.inputLinkColors
|
|
236
|
+
for (let i = 0; i < this.linkColors.length / 4; i++) {
|
|
237
|
+
if (!isNumber(this.linkColors[i * 4])) this.linkColors[i * 4] = defaultRgba[0]
|
|
238
|
+
if (!isNumber(this.linkColors[i * 4 + 1])) this.linkColors[i * 4 + 1] = defaultRgba[1]
|
|
239
|
+
if (!isNumber(this.linkColors[i * 4 + 2])) this.linkColors[i * 4 + 2] = defaultRgba[2]
|
|
240
|
+
if (!isNumber(this.linkColors[i * 4 + 3])) this.linkColors[i * 4 + 3] = defaultRgba[3]
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Updates the link width based on the input data or default config value.
|
|
247
|
+
*/
|
|
248
|
+
public updateLinkWidth (): void {
|
|
249
|
+
if (this.linksNumber === undefined) {
|
|
250
|
+
this.linkWidths = undefined
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Sets link widths to default values from config if the input is missing or does not match input links number.
|
|
255
|
+
if (this.inputLinkWidths === undefined || this.inputLinkWidths.length !== this.linksNumber) {
|
|
256
|
+
this.linkWidths = new Float32Array(this.linksNumber).fill(this._config.linkWidth)
|
|
257
|
+
} else {
|
|
258
|
+
this.linkWidths = this.inputLinkWidths
|
|
259
|
+
for (let i = 0; i < this.linkWidths.length; i++) {
|
|
260
|
+
if (!isNumber(this.linkWidths[i])) {
|
|
261
|
+
this.linkWidths[i] = this._config.linkWidth
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Updates the link arrows based on the input data or default config value.
|
|
269
|
+
*/
|
|
270
|
+
public updateArrows (): void {
|
|
271
|
+
if (this.linksNumber === undefined) {
|
|
272
|
+
this.linkArrows = undefined
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Sets link arrows to default values from config if the input is missing or does not match input links number.
|
|
277
|
+
if (this.linkArrowsBoolean === undefined || this.linkArrowsBoolean.length !== this.linksNumber) {
|
|
278
|
+
this.linkArrows = new Array(this.linksNumber).fill(+this._config.linkArrows)
|
|
279
|
+
} else {
|
|
280
|
+
this.linkArrows = this.linkArrowsBoolean.map(d => +d)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
public updateLinkStrength (): void {
|
|
285
|
+
if (this.linksNumber === undefined) {
|
|
286
|
+
this.linkStrength = undefined
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (this.inputLinkStrength === undefined || this.inputLinkStrength.length !== this.linksNumber) {
|
|
290
|
+
this.linkStrength = undefined
|
|
291
|
+
} else {
|
|
292
|
+
this.linkStrength = this.inputLinkStrength
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
public updateClusters (): void {
|
|
297
|
+
if (this.pointsNumber === undefined) {
|
|
298
|
+
this.pointClusters = undefined
|
|
299
|
+
this.clusterPositions = undefined
|
|
300
|
+
return
|
|
301
|
+
}
|
|
302
|
+
if (this.inputPointClusters === undefined || this.inputPointClusters.length !== this.pointsNumber) {
|
|
303
|
+
this.pointClusters = undefined
|
|
304
|
+
} else {
|
|
305
|
+
this.pointClusters = this.inputPointClusters
|
|
306
|
+
}
|
|
307
|
+
if (this.inputClusterPositions === undefined) {
|
|
308
|
+
this.clusterPositions = undefined
|
|
309
|
+
} else {
|
|
310
|
+
this.clusterPositions = this.inputClusterPositions
|
|
311
|
+
}
|
|
312
|
+
if (this.inputClusterStrength === undefined || this.inputClusterStrength.length !== this.pointsNumber) {
|
|
313
|
+
this.clusterStrength = undefined
|
|
314
|
+
} else {
|
|
315
|
+
this.clusterStrength = this.inputClusterStrength
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
public update (): void {
|
|
320
|
+
this.updatePoints()
|
|
321
|
+
this.updatePointColor()
|
|
322
|
+
this.updatePointSize()
|
|
323
|
+
this.updatePointShape()
|
|
324
|
+
this.updatePointImageIndices()
|
|
325
|
+
this.updatePointImageSizes()
|
|
326
|
+
|
|
327
|
+
this.updateLinks()
|
|
328
|
+
this.updateLinkColor()
|
|
329
|
+
this.updateLinkWidth()
|
|
330
|
+
this.updateArrows()
|
|
331
|
+
this.updateLinkStrength()
|
|
332
|
+
|
|
333
|
+
this.updateClusters()
|
|
334
|
+
|
|
335
|
+
this._createAdjacencyLists()
|
|
336
|
+
this._calculateDegrees()
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
public getAdjacentIndices (index: number): number[] | undefined {
|
|
340
|
+
return [...(this.sourceIndexToTargetIndices?.[index]?.map(d => d[0]) || []), ...(this.targetIndexToSourceIndices?.[index]?.map(d => d[0]) || [])]
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private _createAdjacencyLists (): void {
|
|
344
|
+
if (this.linksNumber === undefined || this.links === undefined) {
|
|
345
|
+
this.sourceIndexToTargetIndices = undefined
|
|
346
|
+
this.targetIndexToSourceIndices = undefined
|
|
347
|
+
return
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
this.sourceIndexToTargetIndices = new Array(this.pointsNumber).fill(undefined)
|
|
351
|
+
this.targetIndexToSourceIndices = new Array(this.pointsNumber).fill(undefined)
|
|
352
|
+
for (let i = 0; i < this.linksNumber; i++) {
|
|
353
|
+
const sourceIndex = this.links[i * 2]
|
|
354
|
+
const targetIndex = this.links[i * 2 + 1]
|
|
355
|
+
if (sourceIndex !== undefined && targetIndex !== undefined) {
|
|
356
|
+
if (this.sourceIndexToTargetIndices[sourceIndex] === undefined) this.sourceIndexToTargetIndices[sourceIndex] = []
|
|
357
|
+
this.sourceIndexToTargetIndices[sourceIndex]?.push([targetIndex, i])
|
|
358
|
+
|
|
359
|
+
if (this.targetIndexToSourceIndices[targetIndex] === undefined) this.targetIndexToSourceIndices[targetIndex] = []
|
|
360
|
+
this.targetIndexToSourceIndices[targetIndex]?.push([sourceIndex, i])
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
private _calculateDegrees (): void {
|
|
366
|
+
if (this.pointsNumber === undefined) {
|
|
367
|
+
this.degree = undefined
|
|
368
|
+
this.inDegree = undefined
|
|
369
|
+
this.outDegree = undefined
|
|
370
|
+
return
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
this.degree = new Array(this.pointsNumber).fill(0)
|
|
374
|
+
this.inDegree = new Array(this.pointsNumber).fill(0)
|
|
375
|
+
this.outDegree = new Array(this.pointsNumber).fill(0)
|
|
376
|
+
|
|
377
|
+
for (let i = 0; i < this.pointsNumber; i++) {
|
|
378
|
+
this.inDegree[i] = this.targetIndexToSourceIndices?.[i]?.length ?? 0
|
|
379
|
+
this.outDegree[i] = this.sourceIndexToTargetIndices?.[i]?.length ?? 0
|
|
380
|
+
this.degree[i] = (this.inDegree[i] ?? 0) + (this.outDegree[i] ?? 0)
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#version 300 es
|
|
2
|
+
#ifdef GL_ES
|
|
3
|
+
precision highp float;
|
|
4
|
+
#endif
|
|
5
|
+
|
|
6
|
+
in vec4 rgbaColor;
|
|
7
|
+
in vec2 pos;
|
|
8
|
+
in float arrowLength;
|
|
9
|
+
in float useArrow;
|
|
10
|
+
in float smoothing;
|
|
11
|
+
in float arrowWidthFactor;
|
|
12
|
+
in float linkIndex;
|
|
13
|
+
|
|
14
|
+
#ifdef USE_UNIFORM_BUFFERS
|
|
15
|
+
layout(std140) uniform drawLineFragmentUniforms {
|
|
16
|
+
float renderMode;
|
|
17
|
+
} drawLineFrag;
|
|
18
|
+
|
|
19
|
+
#define renderMode drawLineFrag.renderMode
|
|
20
|
+
#else
|
|
21
|
+
// renderMode: 0.0 = normal rendering, 1.0 = index buffer rendering for picking
|
|
22
|
+
uniform float renderMode;
|
|
23
|
+
#endif
|
|
24
|
+
|
|
25
|
+
out vec4 fragColor;
|
|
26
|
+
|
|
27
|
+
float map(float value, float min1, float max1, float min2, float max2) {
|
|
28
|
+
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
void main() {
|
|
32
|
+
float opacity = 1.0;
|
|
33
|
+
vec3 color = rgbaColor.rgb;
|
|
34
|
+
|
|
35
|
+
if (useArrow > 0.5) {
|
|
36
|
+
float end_arrow = 0.5 + arrowLength / 2.0;
|
|
37
|
+
float start_arrow = end_arrow - arrowLength;
|
|
38
|
+
float arrowWidthDelta = arrowWidthFactor / 2.0;
|
|
39
|
+
float linkOpacity = rgbaColor.a * smoothstep(0.5 - arrowWidthDelta, 0.5 - arrowWidthDelta - smoothing / 2.0, abs(pos.y));
|
|
40
|
+
float arrowOpacity = 1.0;
|
|
41
|
+
if (pos.x > start_arrow && pos.x < start_arrow + arrowLength) {
|
|
42
|
+
float xmapped = map(pos.x, start_arrow, end_arrow, 0.0, 1.0);
|
|
43
|
+
arrowOpacity = rgbaColor.a * smoothstep(xmapped - smoothing, xmapped, map(abs(pos.y), 0.5, 0.0, 0.0, 1.0));
|
|
44
|
+
if (linkOpacity != arrowOpacity) {
|
|
45
|
+
linkOpacity = max(linkOpacity, arrowOpacity);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
opacity = linkOpacity;
|
|
49
|
+
} else opacity = rgbaColor.a * smoothstep(0.5, 0.5 - smoothing, abs(pos.y));
|
|
50
|
+
|
|
51
|
+
if (renderMode > 0.0) {
|
|
52
|
+
if (opacity > 0.0) {
|
|
53
|
+
fragColor = vec4(linkIndex, 0.0, 0.0, 1.0);
|
|
54
|
+
} else {
|
|
55
|
+
fragColor = vec4(-1.0, 0.0, 0.0, 0.0);
|
|
56
|
+
}
|
|
57
|
+
} else fragColor = vec4(color, opacity);
|
|
58
|
+
|
|
59
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#version 300 es
|
|
2
|
+
#ifdef GL_ES
|
|
3
|
+
precision highp float;
|
|
4
|
+
#endif
|
|
5
|
+
|
|
6
|
+
in vec2 position, pointA, pointB;
|
|
7
|
+
in vec4 color;
|
|
8
|
+
in float width;
|
|
9
|
+
in float arrow;
|
|
10
|
+
in float linkIndices;
|
|
11
|
+
|
|
12
|
+
uniform sampler2D positionsTexture;
|
|
13
|
+
uniform sampler2D pointGreyoutStatus;
|
|
14
|
+
|
|
15
|
+
#ifdef USE_UNIFORM_BUFFERS
|
|
16
|
+
layout(std140) uniform drawLineUniforms {
|
|
17
|
+
mat4 transformationMatrix;
|
|
18
|
+
float pointsTextureSize;
|
|
19
|
+
float widthScale;
|
|
20
|
+
float linkArrowsSizeScale;
|
|
21
|
+
float spaceSize;
|
|
22
|
+
vec2 screenSize;
|
|
23
|
+
vec2 linkVisibilityDistanceRange;
|
|
24
|
+
float linkVisibilityMinTransparency;
|
|
25
|
+
float linkOpacity;
|
|
26
|
+
float greyoutOpacity;
|
|
27
|
+
float curvedWeight;
|
|
28
|
+
float curvedLinkControlPointDistance;
|
|
29
|
+
float curvedLinkSegments;
|
|
30
|
+
float scaleLinksOnZoom;
|
|
31
|
+
float maxPointSize;
|
|
32
|
+
float renderMode;
|
|
33
|
+
float hoveredLinkIndex;
|
|
34
|
+
vec4 hoveredLinkColor;
|
|
35
|
+
float hoveredLinkWidthIncrease;
|
|
36
|
+
} drawLine;
|
|
37
|
+
|
|
38
|
+
#define transformationMatrix drawLine.transformationMatrix
|
|
39
|
+
#define pointsTextureSize drawLine.pointsTextureSize
|
|
40
|
+
#define widthScale drawLine.widthScale
|
|
41
|
+
#define linkArrowsSizeScale drawLine.linkArrowsSizeScale
|
|
42
|
+
#define spaceSize drawLine.spaceSize
|
|
43
|
+
#define screenSize drawLine.screenSize
|
|
44
|
+
#define linkVisibilityDistanceRange drawLine.linkVisibilityDistanceRange
|
|
45
|
+
#define linkVisibilityMinTransparency drawLine.linkVisibilityMinTransparency
|
|
46
|
+
#define linkOpacity drawLine.linkOpacity
|
|
47
|
+
#define greyoutOpacity drawLine.greyoutOpacity
|
|
48
|
+
#define curvedWeight drawLine.curvedWeight
|
|
49
|
+
#define curvedLinkControlPointDistance drawLine.curvedLinkControlPointDistance
|
|
50
|
+
#define curvedLinkSegments drawLine.curvedLinkSegments
|
|
51
|
+
#define scaleLinksOnZoom drawLine.scaleLinksOnZoom
|
|
52
|
+
#define maxPointSize drawLine.maxPointSize
|
|
53
|
+
#define renderMode drawLine.renderMode
|
|
54
|
+
#define hoveredLinkIndex drawLine.hoveredLinkIndex
|
|
55
|
+
#define hoveredLinkColor drawLine.hoveredLinkColor
|
|
56
|
+
#define hoveredLinkWidthIncrease drawLine.hoveredLinkWidthIncrease
|
|
57
|
+
#else
|
|
58
|
+
uniform mat3 transformationMatrix;
|
|
59
|
+
uniform float pointsTextureSize;
|
|
60
|
+
uniform float widthScale;
|
|
61
|
+
uniform float linkArrowsSizeScale;
|
|
62
|
+
uniform float spaceSize;
|
|
63
|
+
uniform vec2 screenSize;
|
|
64
|
+
uniform vec2 linkVisibilityDistanceRange;
|
|
65
|
+
uniform float linkVisibilityMinTransparency;
|
|
66
|
+
uniform float linkOpacity;
|
|
67
|
+
uniform float greyoutOpacity;
|
|
68
|
+
uniform float curvedWeight;
|
|
69
|
+
uniform float curvedLinkControlPointDistance;
|
|
70
|
+
uniform float curvedLinkSegments;
|
|
71
|
+
uniform bool scaleLinksOnZoom;
|
|
72
|
+
uniform float maxPointSize;
|
|
73
|
+
// renderMode: 0.0 = normal rendering, 1.0 = index buffer rendering for picking
|
|
74
|
+
uniform float renderMode;
|
|
75
|
+
uniform float hoveredLinkIndex;
|
|
76
|
+
uniform vec4 hoveredLinkColor;
|
|
77
|
+
uniform float hoveredLinkWidthIncrease;
|
|
78
|
+
#endif
|
|
79
|
+
|
|
80
|
+
out vec4 rgbaColor;
|
|
81
|
+
out vec2 pos;
|
|
82
|
+
out float arrowLength;
|
|
83
|
+
out float useArrow;
|
|
84
|
+
out float smoothing;
|
|
85
|
+
out float arrowWidthFactor;
|
|
86
|
+
out float linkIndex;
|
|
87
|
+
|
|
88
|
+
float map(float value, float min1, float max1, float min2, float max2) {
|
|
89
|
+
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
vec2 conicParametricCurve(vec2 A, vec2 B, vec2 ControlPoint, float t, float w) {
|
|
93
|
+
vec2 divident = (1.0 - t) * (1.0 - t) * A + 2.0 * (1.0 - t) * t * w * ControlPoint + t * t * B;
|
|
94
|
+
float divisor = (1.0 - t) * (1.0 - t) + 2.0 * (1.0 - t) * t * w + t * t;
|
|
95
|
+
return divident / divisor;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
float calculateLinkWidth(float width) {
|
|
99
|
+
float linkWidth;
|
|
100
|
+
if (scaleLinksOnZoom > 0.0) {
|
|
101
|
+
// Use original width if links should scale with zoom
|
|
102
|
+
linkWidth = width;
|
|
103
|
+
} else {
|
|
104
|
+
// Adjust width based on zoom level to maintain visual size
|
|
105
|
+
linkWidth = width / transformationMatrix[0][0];
|
|
106
|
+
// Apply a non-linear scaling to avoid extreme widths
|
|
107
|
+
linkWidth *= min(5.0, max(1.0, transformationMatrix[0][0] * 0.01));
|
|
108
|
+
}
|
|
109
|
+
// Limit link width based on whether it has an arrow
|
|
110
|
+
if (useArrow > 0.5) {
|
|
111
|
+
return min(linkWidth, (maxPointSize * 2.0) / transformationMatrix[0][0]);
|
|
112
|
+
} else {
|
|
113
|
+
return min(linkWidth, maxPointSize / transformationMatrix[0][0]);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
float calculateArrowWidth(float arrowWidth) {
|
|
118
|
+
if (scaleLinksOnZoom > 0.0) {
|
|
119
|
+
return arrowWidth;
|
|
120
|
+
} else {
|
|
121
|
+
// Apply the same scaling logic as calculateLinkWidth to maintain proportionality
|
|
122
|
+
arrowWidth = arrowWidth / transformationMatrix[0][0];
|
|
123
|
+
// Apply the same non-linear scaling to avoid extreme widths
|
|
124
|
+
arrowWidth *= min(5.0, max(1.0, transformationMatrix[0][0] * 0.01));
|
|
125
|
+
return arrowWidth;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
void main() {
|
|
130
|
+
pos = position;
|
|
131
|
+
linkIndex = linkIndices;
|
|
132
|
+
|
|
133
|
+
vec2 pointTexturePosA = (pointA + 0.5) / pointsTextureSize;
|
|
134
|
+
vec2 pointTexturePosB = (pointB + 0.5) / pointsTextureSize;
|
|
135
|
+
|
|
136
|
+
vec4 greyoutStatusA = texture(pointGreyoutStatus, pointTexturePosA);
|
|
137
|
+
vec4 greyoutStatusB = texture(pointGreyoutStatus, pointTexturePosB);
|
|
138
|
+
|
|
139
|
+
vec4 pointPositionA = texture(positionsTexture, pointTexturePosA);
|
|
140
|
+
vec4 pointPositionB = texture(positionsTexture, pointTexturePosB);
|
|
141
|
+
vec2 a = pointPositionA.xy;
|
|
142
|
+
vec2 b = pointPositionB.xy;
|
|
143
|
+
|
|
144
|
+
// Calculate direction vector and its perpendicular
|
|
145
|
+
vec2 xBasis = b - a;
|
|
146
|
+
vec2 yBasis = normalize(vec2(-xBasis.y, xBasis.x));
|
|
147
|
+
|
|
148
|
+
// Calculate link distance and control point for curved link
|
|
149
|
+
float linkDist = length(xBasis);
|
|
150
|
+
float h = curvedLinkControlPointDistance;
|
|
151
|
+
vec2 controlPoint = (a + b) / 2.0 + yBasis * linkDist * h;
|
|
152
|
+
|
|
153
|
+
// Convert link distance to screen pixels
|
|
154
|
+
float linkDistPx = linkDist * transformationMatrix[0][0];
|
|
155
|
+
|
|
156
|
+
// Calculate line width using the width scale
|
|
157
|
+
float linkWidth = width * widthScale;
|
|
158
|
+
float k = 2.0;
|
|
159
|
+
// Arrow width is proportionally larger than the line width
|
|
160
|
+
float arrowWidth = linkWidth * k;
|
|
161
|
+
arrowWidth *= linkArrowsSizeScale;
|
|
162
|
+
|
|
163
|
+
// Ensure arrow width difference is non-negative to prevent unwanted changes to link width
|
|
164
|
+
float arrowWidthDifference = max(0.0, arrowWidth - linkWidth);
|
|
165
|
+
|
|
166
|
+
// Calculate arrow width in pixels
|
|
167
|
+
float arrowWidthPx = calculateArrowWidth(arrowWidth);
|
|
168
|
+
|
|
169
|
+
// Calculate arrow length proportional to its width
|
|
170
|
+
// 0.866 is approximately sqrt(3)/2 - related to equilateral triangle geometry
|
|
171
|
+
// Cap the length to avoid overly long arrows on short links
|
|
172
|
+
arrowLength = min(0.3, (0.866 * arrowWidthPx * 2.0) / linkDist);
|
|
173
|
+
|
|
174
|
+
useArrow = arrow;
|
|
175
|
+
if (useArrow > 0.5) {
|
|
176
|
+
linkWidth += arrowWidthDifference;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
arrowWidthFactor = arrowWidthDifference / linkWidth;
|
|
180
|
+
|
|
181
|
+
// Calculate final link width in pixels with smoothing
|
|
182
|
+
float linkWidthPx = calculateLinkWidth(linkWidth);
|
|
183
|
+
|
|
184
|
+
if (renderMode > 0.0) {
|
|
185
|
+
// Add 5 pixels padding for better hover detection
|
|
186
|
+
linkWidthPx += 5.0 / transformationMatrix[0][0];
|
|
187
|
+
} else {
|
|
188
|
+
// Add pixel increase if this is the hovered link
|
|
189
|
+
if (hoveredLinkIndex == linkIndex) {
|
|
190
|
+
linkWidthPx += hoveredLinkWidthIncrease / transformationMatrix[0][0];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
float smoothingPx = 0.5 / transformationMatrix[0][0];
|
|
194
|
+
smoothing = smoothingPx / linkWidthPx;
|
|
195
|
+
linkWidthPx += smoothingPx;
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
// Calculate final color with opacity based on link distance
|
|
200
|
+
vec3 rgbColor = color.rgb;
|
|
201
|
+
// Adjust opacity based on link distance
|
|
202
|
+
float opacity = color.a * linkOpacity * max(linkVisibilityMinTransparency, map(linkDistPx, linkVisibilityDistanceRange.g, linkVisibilityDistanceRange.r, 0.0, 1.0));
|
|
203
|
+
|
|
204
|
+
// Apply greyed out opacity if either endpoint is greyed out
|
|
205
|
+
if (greyoutStatusA.r > 0.0 || greyoutStatusB.r > 0.0) {
|
|
206
|
+
opacity *= greyoutOpacity;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Pass final color to fragment shader
|
|
210
|
+
rgbaColor = vec4(rgbColor, opacity);
|
|
211
|
+
|
|
212
|
+
// Apply hover color if this is the hovered link and hover color is defined
|
|
213
|
+
if (hoveredLinkIndex == linkIndex && hoveredLinkColor.a > -0.5) {
|
|
214
|
+
// Keep existing RGB values but multiply opacity with hover color opacity
|
|
215
|
+
rgbaColor.rgb = hoveredLinkColor.rgb;
|
|
216
|
+
rgbaColor.a *= hoveredLinkColor.a;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Calculate position on the curved path
|
|
220
|
+
float t = position.x;
|
|
221
|
+
float w = curvedWeight;
|
|
222
|
+
|
|
223
|
+
float tPrev = t - 1.0 / curvedLinkSegments;
|
|
224
|
+
float tNext = t + 1.0 / curvedLinkSegments;
|
|
225
|
+
|
|
226
|
+
vec2 pointCurr = conicParametricCurve(a, b, controlPoint, t, w);
|
|
227
|
+
|
|
228
|
+
vec2 pointPrev = conicParametricCurve(a, b, controlPoint, max(0.0, tPrev), w);
|
|
229
|
+
vec2 pointNext = conicParametricCurve(a, b, controlPoint, min(tNext, 1.0), w);
|
|
230
|
+
|
|
231
|
+
vec2 xBasisCurved = pointNext - pointPrev;
|
|
232
|
+
vec2 yBasisCurved = normalize(vec2(-xBasisCurved.y, xBasisCurved.x));
|
|
233
|
+
|
|
234
|
+
pointCurr += yBasisCurved * linkWidthPx * position.y;
|
|
235
|
+
|
|
236
|
+
// Transform to clip space coordinates
|
|
237
|
+
vec2 p = 2.0 * pointCurr / spaceSize - 1.0;
|
|
238
|
+
p *= spaceSize / screenSize;
|
|
239
|
+
|
|
240
|
+
#ifdef USE_UNIFORM_BUFFERS
|
|
241
|
+
mat3 transformMat3 = mat3(transformationMatrix);
|
|
242
|
+
vec3 final = transformMat3 * vec3(p, 1);
|
|
243
|
+
#else
|
|
244
|
+
vec3 final = transformationMatrix * vec3(p, 1);
|
|
245
|
+
#endif
|
|
246
|
+
|
|
247
|
+
gl_Position = vec4(final.rg, 0, 1);
|
|
248
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { scalePow } from 'd3-scale'
|
|
2
|
+
import { range } from 'd3-array'
|
|
3
|
+
|
|
4
|
+
export const getCurveLineGeometry = (segments: number): number[][] => {
|
|
5
|
+
const scale = scalePow()
|
|
6
|
+
.exponent(2)
|
|
7
|
+
.range([0, 1])
|
|
8
|
+
.domain([-1, 1])
|
|
9
|
+
|
|
10
|
+
const hodographValues = range(0, segments).map(d => -0.5 + d / segments)
|
|
11
|
+
hodographValues.push(0.5)
|
|
12
|
+
const result = new Array(hodographValues.length * 2)
|
|
13
|
+
hodographValues.forEach((d, i) => {
|
|
14
|
+
result[i * 2] = [scale(d * 2), 0.5]
|
|
15
|
+
result[i * 2 + 1] = [scale(d * 2), -0.5]
|
|
16
|
+
})
|
|
17
|
+
return result
|
|
18
|
+
}
|