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