@cosmos.gl/graph 2.6.2-rc.0 → 2.7.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/.eslintrc +147 -0
  2. package/.github/SECURITY.md +13 -0
  3. package/.github/dco.yml +4 -0
  4. package/.github/workflows/github_pages.yml +54 -0
  5. package/.storybook/main.ts +26 -0
  6. package/.storybook/manager-head.html +1 -0
  7. package/.storybook/manager.ts +14 -0
  8. package/.storybook/preview.ts +29 -0
  9. package/.storybook/style.css +3 -0
  10. package/CHARTER.md +69 -0
  11. package/CODE_OF_CONDUCT.md +178 -0
  12. package/CONTRIBUTING.md +22 -0
  13. package/GOVERNANCE.md +21 -0
  14. package/cosmos-2-0-migration-notes.md +98 -0
  15. package/cosmos_awesome.md +96 -0
  16. package/dist/config.d.ts +5 -9
  17. package/dist/graph/utils/error-message.d.ts +1 -1
  18. package/dist/helper.d.ts +39 -2
  19. package/dist/index-FUIgayhu.js +19827 -0
  20. package/dist/index-FUIgayhu.js.map +1 -0
  21. package/dist/index.d.ts +17 -64
  22. package/dist/index.js +14 -14654
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.min.js +1062 -475
  25. package/dist/index.min.js.map +1 -1
  26. package/dist/modules/Clusters/index.d.ts +11 -3
  27. package/dist/modules/ForceCenter/index.d.ts +10 -3
  28. package/dist/modules/ForceGravity/index.d.ts +5 -1
  29. package/dist/modules/ForceLink/index.d.ts +8 -5
  30. package/dist/modules/ForceManyBody/index.d.ts +16 -7
  31. package/dist/modules/ForceMouse/index.d.ts +5 -1
  32. package/dist/modules/GraphData/index.d.ts +0 -1
  33. package/dist/modules/Lines/index.d.ts +11 -5
  34. package/dist/modules/Points/index.d.ts +31 -13
  35. package/dist/modules/Store/index.d.ts +93 -0
  36. package/dist/modules/core-module.d.ts +3 -3
  37. package/dist/stories/beginners/basic-set-up/data-gen.d.ts +4 -0
  38. package/dist/stories/beginners/basic-set-up/index.d.ts +6 -0
  39. package/dist/stories/beginners/link-hovering/data-generator.d.ts +19 -0
  40. package/dist/stories/beginners/link-hovering/index.d.ts +6 -0
  41. package/dist/stories/beginners/point-labels/data.d.ts +13 -0
  42. package/dist/stories/beginners/point-labels/index.d.ts +10 -0
  43. package/dist/stories/beginners/point-labels/labels.d.ts +8 -0
  44. package/dist/stories/beginners/quick-start.d.ts +6 -0
  45. package/dist/stories/beginners/remove-points/config.d.ts +2 -0
  46. package/dist/stories/beginners/remove-points/data-gen.d.ts +4 -0
  47. package/dist/stories/beginners/remove-points/index.d.ts +6 -0
  48. package/dist/stories/beginners.stories.d.ts +10 -0
  49. package/dist/stories/clusters/polygon-selection/index.d.ts +6 -0
  50. package/dist/stories/clusters/polygon-selection/polygon.d.ts +20 -0
  51. package/dist/stories/clusters/radial.d.ts +6 -0
  52. package/dist/stories/clusters/with-labels.d.ts +6 -0
  53. package/dist/stories/clusters/worm.d.ts +6 -0
  54. package/dist/stories/clusters.stories.d.ts +9 -0
  55. package/dist/stories/create-cluster-labels.d.ts +4 -0
  56. package/dist/stories/create-cosmos.d.ts +17 -0
  57. package/dist/stories/create-story.d.ts +16 -0
  58. package/dist/stories/experiments/full-mesh.d.ts +6 -0
  59. package/dist/stories/experiments/mesh-with-holes.d.ts +6 -0
  60. package/dist/stories/experiments.stories.d.ts +7 -0
  61. package/dist/stories/generate-mesh-data.d.ts +12 -0
  62. package/dist/stories/geospatial/moscow-metro-stations/index.d.ts +16 -0
  63. package/dist/stories/geospatial/moscow-metro-stations/moscow-metro-coords.d.ts +1 -0
  64. package/dist/stories/geospatial/moscow-metro-stations/point-colors.d.ts +1 -0
  65. package/dist/stories/geospatial.stories.d.ts +6 -0
  66. package/dist/stories/shapes/all-shapes/index.d.ts +6 -0
  67. package/dist/stories/shapes/image-example/index.d.ts +6 -0
  68. package/dist/stories/shapes.stories.d.ts +7 -0
  69. package/dist/stories/test-luma-migration.d.ts +6 -0
  70. package/dist/stories/test.stories.d.ts +6 -0
  71. package/dist/webgl-device-B9ewDj5L.js +3923 -0
  72. package/dist/webgl-device-B9ewDj5L.js.map +1 -0
  73. package/logo.svg +3 -0
  74. package/package.json +5 -7
  75. package/rollup.config.js +70 -0
  76. package/src/config.ts +728 -0
  77. package/src/declaration.d.ts +12 -0
  78. package/src/graph/utils/error-message.ts +23 -0
  79. package/src/helper.ts +113 -0
  80. package/src/index.ts +1769 -0
  81. package/src/modules/Clusters/calculate-centermass.frag +12 -0
  82. package/src/modules/Clusters/calculate-centermass.vert +38 -0
  83. package/src/modules/Clusters/force-cluster.frag +55 -0
  84. package/src/modules/Clusters/index.ts +578 -0
  85. package/src/modules/Drag/index.ts +33 -0
  86. package/src/modules/FPSMonitor/css.ts +53 -0
  87. package/src/modules/FPSMonitor/index.ts +28 -0
  88. package/src/modules/ForceCenter/calculate-centermass.frag +9 -0
  89. package/src/modules/ForceCenter/calculate-centermass.vert +26 -0
  90. package/src/modules/ForceCenter/force-center.frag +37 -0
  91. package/src/modules/ForceCenter/index.ts +284 -0
  92. package/src/modules/ForceGravity/force-gravity.frag +40 -0
  93. package/src/modules/ForceGravity/index.ts +107 -0
  94. package/src/modules/ForceLink/force-spring.ts +89 -0
  95. package/src/modules/ForceLink/index.ts +293 -0
  96. package/src/modules/ForceManyBody/calculate-level.frag +9 -0
  97. package/src/modules/ForceManyBody/calculate-level.vert +37 -0
  98. package/src/modules/ForceManyBody/force-centermass.frag +61 -0
  99. package/src/modules/ForceManyBody/force-level.frag +138 -0
  100. package/src/modules/ForceManyBody/index.ts +525 -0
  101. package/src/modules/ForceManyBody/quadtree-frag-shader.ts +89 -0
  102. package/src/modules/ForceManyBodyQuadtree/calculate-level.frag +9 -0
  103. package/src/modules/ForceManyBodyQuadtree/calculate-level.vert +25 -0
  104. package/src/modules/ForceManyBodyQuadtree/index.ts +157 -0
  105. package/src/modules/ForceManyBodyQuadtree/quadtree-frag-shader.ts +93 -0
  106. package/src/modules/ForceMouse/force-mouse.frag +35 -0
  107. package/src/modules/ForceMouse/index.ts +102 -0
  108. package/src/modules/GraphData/index.ts +383 -0
  109. package/src/modules/Lines/draw-curve-line.frag +59 -0
  110. package/src/modules/Lines/draw-curve-line.vert +248 -0
  111. package/src/modules/Lines/geometry.ts +18 -0
  112. package/src/modules/Lines/hovered-line-index.frag +43 -0
  113. package/src/modules/Lines/hovered-line-index.vert +13 -0
  114. package/src/modules/Lines/index.ts +661 -0
  115. package/src/modules/Points/atlas-utils.ts +137 -0
  116. package/src/modules/Points/drag-point.frag +34 -0
  117. package/src/modules/Points/draw-highlighted.frag +44 -0
  118. package/src/modules/Points/draw-highlighted.vert +145 -0
  119. package/src/modules/Points/draw-points.frag +259 -0
  120. package/src/modules/Points/draw-points.vert +203 -0
  121. package/src/modules/Points/fill-sampled-points.frag +12 -0
  122. package/src/modules/Points/fill-sampled-points.vert +51 -0
  123. package/src/modules/Points/find-hovered-point.frag +15 -0
  124. package/src/modules/Points/find-hovered-point.vert +90 -0
  125. package/src/modules/Points/find-points-on-area-selection.frag +88 -0
  126. package/src/modules/Points/find-points-on-polygon-selection.frag +89 -0
  127. package/src/modules/Points/index.ts +2292 -0
  128. package/src/modules/Points/track-positions.frag +30 -0
  129. package/src/modules/Points/update-position.frag +39 -0
  130. package/src/modules/Shared/buffer.ts +39 -0
  131. package/src/modules/Shared/clear.frag +10 -0
  132. package/src/modules/Shared/quad.vert +13 -0
  133. package/src/modules/Store/index.ts +283 -0
  134. package/src/modules/Zoom/index.ts +148 -0
  135. package/src/modules/core-module.ts +28 -0
  136. package/src/stories/1. welcome.mdx +75 -0
  137. package/src/stories/2. configuration.mdx +111 -0
  138. package/src/stories/3. api-reference.mdx +591 -0
  139. package/src/stories/beginners/basic-set-up/data-gen.ts +33 -0
  140. package/src/stories/beginners/basic-set-up/index.ts +167 -0
  141. package/src/stories/beginners/basic-set-up/style.css +35 -0
  142. package/src/stories/beginners/link-hovering/data-generator.ts +198 -0
  143. package/src/stories/beginners/link-hovering/index.ts +65 -0
  144. package/src/stories/beginners/link-hovering/style.css +73 -0
  145. package/src/stories/beginners/point-labels/data.ts +73 -0
  146. package/src/stories/beginners/point-labels/index.ts +69 -0
  147. package/src/stories/beginners/point-labels/labels.ts +46 -0
  148. package/src/stories/beginners/point-labels/style.css +16 -0
  149. package/src/stories/beginners/quick-start.ts +54 -0
  150. package/src/stories/beginners/remove-points/config.ts +25 -0
  151. package/src/stories/beginners/remove-points/data-gen.ts +30 -0
  152. package/src/stories/beginners/remove-points/index.ts +96 -0
  153. package/src/stories/beginners/remove-points/style.css +31 -0
  154. package/src/stories/beginners.stories.ts +130 -0
  155. package/src/stories/clusters/polygon-selection/index.ts +52 -0
  156. package/src/stories/clusters/polygon-selection/polygon.ts +143 -0
  157. package/src/stories/clusters/polygon-selection/style.css +8 -0
  158. package/src/stories/clusters/radial.ts +24 -0
  159. package/src/stories/clusters/with-labels.ts +54 -0
  160. package/src/stories/clusters/worm.ts +40 -0
  161. package/src/stories/clusters.stories.ts +77 -0
  162. package/src/stories/create-cluster-labels.ts +50 -0
  163. package/src/stories/create-cosmos.ts +72 -0
  164. package/src/stories/create-story.ts +51 -0
  165. package/src/stories/experiments/full-mesh.ts +13 -0
  166. package/src/stories/experiments/mesh-with-holes.ts +13 -0
  167. package/src/stories/experiments.stories.ts +43 -0
  168. package/src/stories/generate-mesh-data.ts +125 -0
  169. package/src/stories/geospatial/moscow-metro-stations/index.ts +66 -0
  170. package/src/stories/geospatial/moscow-metro-stations/moscow-metro-coords.ts +1 -0
  171. package/src/stories/geospatial/moscow-metro-stations/point-colors.ts +46 -0
  172. package/src/stories/geospatial/moscow-metro-stations/style.css +30 -0
  173. package/src/stories/geospatial.stories.ts +30 -0
  174. package/src/stories/shapes/all-shapes/index.ts +73 -0
  175. package/src/stories/shapes/image-example/icons/box.png +0 -0
  176. package/src/stories/shapes/image-example/icons/lego.png +0 -0
  177. package/src/stories/shapes/image-example/icons/s.png +0 -0
  178. package/src/stories/shapes/image-example/icons/swift.png +0 -0
  179. package/src/stories/shapes/image-example/icons/toolbox.png +0 -0
  180. package/src/stories/shapes/image-example/index.ts +246 -0
  181. package/src/stories/shapes.stories.ts +37 -0
  182. package/src/stories/test-luma-migration.ts +195 -0
  183. package/src/stories/test.stories.ts +25 -0
  184. package/src/variables.ts +68 -0
  185. package/tsconfig.json +41 -0
  186. package/vite.config.ts +52 -0
@@ -0,0 +1,46 @@
1
+ import { LabelRenderer, LabelOptions } from '@interacta/css-labels'
2
+ import { Graph } from '@cosmos.gl/graph'
3
+
4
+ export class CosmosLabels {
5
+ private labelRenderer: LabelRenderer
6
+ private labels: LabelOptions[] = []
7
+ private pointIndexToLabel: Map<number, string>
8
+
9
+ public constructor (div: HTMLDivElement, pointIndexToLabel: Map<number, string>) {
10
+ this.labelRenderer = new LabelRenderer(div, { pointerEvents: 'none' })
11
+ this.pointIndexToLabel = pointIndexToLabel
12
+ }
13
+
14
+ public update (graph: Graph): void {
15
+ // Get coordinates of the tracked nodes
16
+ const trackedNodesPositions = graph.getTrackedPointPositionsMap()
17
+ let index = 0
18
+ trackedNodesPositions.forEach((positions, pointIndex) => {
19
+ // Convert coordinates to the screen space
20
+ const screenPosition = graph.spaceToScreenPosition([
21
+ positions?.[0] ?? 0,
22
+ positions?.[1] ?? 0,
23
+ ])
24
+
25
+ // Get the node radius and convert it to the screen space value in pixels
26
+ const radius = graph.spaceToScreenRadius(
27
+ graph.getPointRadiusByIndex(pointIndex) as number
28
+ )
29
+
30
+ // Set label properties
31
+ this.labels[index] = {
32
+ id: `${pointIndex}`,
33
+ text: this.pointIndexToLabel.get(pointIndex) ?? '',
34
+ x: screenPosition[0],
35
+ y: screenPosition[1] - (radius + 2),
36
+ opacity: 1,
37
+ }
38
+
39
+ index += 1
40
+ })
41
+
42
+ // Pass labels configuration to the renderer and draw them
43
+ this.labelRenderer.setLabels(this.labels)
44
+ this.labelRenderer.draw(true)
45
+ }
46
+ }
@@ -0,0 +1,16 @@
1
+ body {
2
+ font-family: "Nunito Sans", -apple-system, ".SFNSText-Regular", "San Francisco", BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
3
+ margin: 0px;
4
+ }
5
+
6
+ .app {
7
+ position: absolute;
8
+ width: 100%;
9
+ height: 100%;
10
+ color: white;
11
+ }
12
+
13
+ .graph {
14
+ width: 100%;
15
+ height: 100vh;
16
+ }
@@ -0,0 +1,54 @@
1
+ import { Graph, GraphConfigInterface } from '@cosmos.gl/graph'
2
+
3
+ export const quickStart = (): { graph: Graph; div: HTMLDivElement; destroy?: () => void } => {
4
+ const div = document.createElement('div')
5
+ div.style.height = '100vh'
6
+ div.style.width = '100%'
7
+
8
+ const config: GraphConfigInterface = {
9
+ spaceSize: 4096,
10
+ backgroundColor: '#2d313a',
11
+ pointColor: '#F069B4',
12
+ scalePointsOnZoom: true,
13
+ simulationFriction: 0.1, // keeps the graph inert
14
+ simulationGravity: 0, // disables gravity
15
+ simulationRepulsion: 0.5, // increases repulsion between points
16
+ curvedLinks: true, // curved links
17
+ fitViewDelay: 1000, // wait 1 second before fitting the view
18
+ fitViewPadding: 0.3, // centers the graph width padding of ~30% of screen
19
+ rescalePositions: true, // rescale positions
20
+ enableDrag: true, // enable dragging points
21
+ onPointClick: pointIndex => { console.log('Clicked point index: ', pointIndex) },
22
+ onBackgroundClick: () => { console.log('Clicked background') },
23
+ attribution: 'visualized with <a href="https://cosmograph.app/" style="color: var(--cosmosgl-attribution-color);" target="_blank">Cosmograph</a>',
24
+ /* ... */
25
+ }
26
+
27
+ const graph = new Graph(div, config)
28
+
29
+ // Points: [x1, y1, x2, y2, x3, y3]
30
+ const pointPositions = new Float32Array([
31
+ 0.0, 0.0, // Point 1 at (0,0)
32
+ 1.0, 0.0, // Point 2 at (1,0)
33
+ 0.5, 1.0, // Point 3 at (0.5,1)
34
+ ])
35
+
36
+ graph.setPointPositions(pointPositions)
37
+
38
+ // Links: [sourceIndex1, targetIndex1, sourceIndex2, targetIndex2]
39
+ const links = new Float32Array([
40
+ 0, 1, // Link from point 0 to point 1
41
+ 1, 2, // Link from point 1 to point 2
42
+ 2, 0, // Link from point 2 to point 0
43
+ ])
44
+
45
+ graph.setLinks(links)
46
+
47
+ graph.render()
48
+
49
+ const destroy = (): void => {
50
+ graph.destroy()
51
+ }
52
+
53
+ return { div, graph, destroy }
54
+ }
@@ -0,0 +1,25 @@
1
+ import { GraphConfigInterface } from '@cosmos.gl/graph'
2
+
3
+ export const config: GraphConfigInterface = {
4
+ spaceSize: 4096,
5
+ backgroundColor: '#2d313a',
6
+ pointSize: 4,
7
+ pointColor: '#4B5BBF',
8
+ scalePointsOnZoom: true,
9
+ pointGreyoutOpacity: 0.1,
10
+ linkWidth: 0.6,
11
+ linkColor: '#5F74C2',
12
+ linkArrows: false,
13
+ linkGreyoutOpacity: 0,
14
+ hoveredPointCursor: 'pointer',
15
+ renderHoveredPointRing: true,
16
+ fitViewDuration: 1000,
17
+ fitViewPadding: 0.3,
18
+ enableSimulationDuringZoom: true,
19
+ simulationLinkDistance: 1,
20
+ simulationLinkSpring: 2,
21
+ simulationRepulsion: 0.2,
22
+ simulationGravity: 0.1,
23
+ simulationDecay: 100000,
24
+ attribution: 'visualized with <a href="https://cosmograph.app/" style="color: var(--cosmosgl-attribution-color);" target="_blank">Cosmograph</a>',
25
+ }
@@ -0,0 +1,30 @@
1
+ function getRandom (min: number, max: number): number {
2
+ return Math.random() * (max - min) + min
3
+ }
4
+
5
+ export function generateData (n = 100, m = 100): { pointPositions: number[]; links: number[] } {
6
+ const pointPositions = new Array(n * m * 2)
7
+ const links: number[] = []
8
+ for (let pointIndex = 0; pointIndex < n * m; pointIndex += 1) {
9
+ const x = 4096 * getRandom(0.495, 0.505)
10
+ const y = 4096 * getRandom(0.495, 0.505)
11
+ pointPositions[pointIndex * 2] = x
12
+ pointPositions[pointIndex * 2 + 1] = y
13
+ const nextPointIndex = pointIndex + 1
14
+ const bottomPointIndex = pointIndex + n
15
+ const pointLine = Math.floor(pointIndex / n)
16
+ const nextPointLine = Math.floor(nextPointIndex / n)
17
+ const bottomPointLine = Math.floor(bottomPointIndex / n)
18
+ if (pointLine === nextPointLine) {
19
+ links.push(pointIndex)
20
+ links.push(nextPointIndex)
21
+ }
22
+
23
+ if (bottomPointLine < m) {
24
+ links.push(pointIndex)
25
+ links.push(bottomPointIndex)
26
+ }
27
+ }
28
+
29
+ return { pointPositions, links }
30
+ }
@@ -0,0 +1,96 @@
1
+ import { Graph } from '@cosmos.gl/graph'
2
+ import { generateData } from './data-gen'
3
+ import { config } from './config'
4
+ import './style.css'
5
+
6
+ export const removePoints = (): { graph: Graph; div: HTMLDivElement; destroy?: () => void } => {
7
+ const { pointPositions, links } = generateData()
8
+ const div = document.createElement('div')
9
+ div.className = 'app'
10
+
11
+ const graphDiv = document.createElement('div')
12
+ graphDiv.className = 'graph'
13
+ div.appendChild(graphDiv)
14
+
15
+ const actionsDiv = document.createElement('div')
16
+ actionsDiv.className = 'actions'
17
+ div.appendChild(actionsDiv)
18
+
19
+ let isPaused = false
20
+
21
+ let graphLinks = links
22
+
23
+ const graph = new Graph(graphDiv, {
24
+ ...config,
25
+ onPointClick: (i): void => {
26
+ // Filter out the clicked point from positions array
27
+ const currentPositions = graph.getPointPositions()
28
+ const newPointPositions = currentPositions
29
+ .filter((pos, posIndex) => {
30
+ return (
31
+ (posIndex % 2 === 0 && posIndex !== i * 2) ||
32
+ (posIndex % 2 === 1 && posIndex !== i * 2 + 1)
33
+ )
34
+ })
35
+
36
+ // Convert links array to source-target pairs for easier filtering
37
+ const pairedNumbers = []
38
+ for (let j = 0; j < graphLinks.length; j += 2) {
39
+ const pair = [graphLinks[j], graphLinks[j + 1]]
40
+ pairedNumbers.push(pair)
41
+ }
42
+
43
+ // Remove links connected to deleted point and adjust indices of remaining links
44
+ graphLinks = (pairedNumbers
45
+ .filter(
46
+ ([sourceIndex, targetIndex]) => sourceIndex !== i && targetIndex !== i
47
+ )
48
+ .flat() as number[])
49
+ .map((p) => {
50
+ if (p > i) return p - 1
51
+ else return p
52
+ })
53
+
54
+ graph.setPointPositions(new Float32Array(newPointPositions))
55
+ graph.setLinks(new Float32Array(graphLinks))
56
+ graph.render(isPaused ? 0 : undefined)
57
+ console.log('Clicked node: ', i)
58
+ },
59
+ })
60
+ graph.setPointPositions(new Float32Array(pointPositions))
61
+ graph.setLinks(new Float32Array(graphLinks))
62
+ graph.render()
63
+ graph.zoom(0.9)
64
+
65
+ /* ~ Demo Actions ~ */
66
+ // Start / Pause
67
+ const pauseButton = document.createElement('div')
68
+ pauseButton.className = 'action'
69
+ pauseButton.textContent = 'Pause'
70
+ actionsDiv.appendChild(pauseButton)
71
+
72
+ function pause (): void {
73
+ isPaused = true
74
+ pauseButton.textContent = 'Start'
75
+ graph.pause()
76
+ }
77
+
78
+ function start (): void {
79
+ isPaused = false
80
+ pauseButton.textContent = 'Pause'
81
+ graph.start()
82
+ }
83
+
84
+ function togglePause (): void {
85
+ if (isPaused) start()
86
+ else pause()
87
+ }
88
+
89
+ pauseButton.addEventListener('click', togglePause)
90
+
91
+ const destroy = (): void => {
92
+ graph.destroy()
93
+ }
94
+
95
+ return { div, graph, destroy }
96
+ }
@@ -0,0 +1,31 @@
1
+ body {
2
+ font-family: "Nunito Sans", -apple-system, ".SFNSText-Regular", "San Francisco", BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
3
+ margin: 0px;
4
+ }
5
+
6
+ .app {
7
+ position: absolute;
8
+ width: 100%;
9
+ height: 100%;
10
+ }
11
+
12
+ .graph {
13
+ width: 100%;
14
+ height: 100vh;
15
+ }
16
+
17
+
18
+ .actions {
19
+ position: absolute;
20
+ top: 10px;
21
+ left: 10px;
22
+ color: #ccc;
23
+ }
24
+
25
+ .action {
26
+ margin-left: 2px;
27
+ font-size: 10pt;
28
+ text-decoration: underline;
29
+ cursor: pointer;
30
+ user-select: none;
31
+ }
@@ -0,0 +1,130 @@
1
+ import type { Meta } from '@storybook/html'
2
+
3
+ import { createStory, Story } from '@/graph/stories/create-story'
4
+ import { CosmosStoryProps } from './create-cosmos'
5
+ import { quickStart } from './beginners/quick-start'
6
+ import { basicSetUp } from './beginners/basic-set-up'
7
+ import { pointLabels } from './beginners/point-labels'
8
+ import { removePoints } from './beginners/remove-points'
9
+ import { linkHovering } from './beginners/link-hovering'
10
+
11
+ import quickStartStoryRaw from './beginners/quick-start?raw'
12
+ import basicSetUpStoryRaw from './beginners/basic-set-up/index?raw'
13
+ import basicSetUpStoryCssRaw from './beginners/basic-set-up/style.css?raw'
14
+ import basicSetUpStoryDataGenRaw from './beginners/basic-set-up/data-gen?raw'
15
+ import pointLabelsStoryRaw from './beginners/point-labels/index?raw'
16
+ import pointLabelsStoryDataRaw from './beginners/point-labels/data.ts?raw'
17
+ import pointLabelsStoryLabelsRaw from './beginners/point-labels/labels.ts?raw'
18
+ import pointLabelsStoryCssRaw from './beginners/point-labels/style.css?raw'
19
+ import removePointsStoryRaw from './beginners/remove-points/index?raw'
20
+ import removePointsStoryCssRaw from './beginners/remove-points/style.css?raw'
21
+ import removePointsStoryConfigRaw from './beginners/remove-points/config.ts?raw'
22
+ import removePointsStoryDataGenRaw from './beginners/remove-points/data-gen.ts?raw'
23
+ import linkHoveringStoryRaw from './beginners/link-hovering/index?raw'
24
+ import linkHoveringStoryDataGenRaw from './beginners/link-hovering/data-generator.ts?raw'
25
+ import linkHoveringStoryCssRaw from './beginners/link-hovering/style.css?raw'
26
+
27
+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
28
+ const meta: Meta<CosmosStoryProps> = {
29
+ title: 'Examples/Beginners',
30
+ }
31
+
32
+ export const QuickStart: Story = {
33
+ ...createStory(quickStart),
34
+ parameters: {
35
+ sourceCode: [
36
+ { name: 'Story', code: quickStartStoryRaw },
37
+ ],
38
+ },
39
+ }
40
+
41
+ export const BasicSetUp: Story = {
42
+ ...createStory(basicSetUp),
43
+ name: '100x100 grid',
44
+ parameters: {
45
+ sourceCode: [
46
+ { name: 'Story', code: basicSetUpStoryRaw },
47
+ { name: 'style.css', code: basicSetUpStoryCssRaw },
48
+ { name: 'data-gen', code: basicSetUpStoryDataGenRaw },
49
+ ],
50
+ },
51
+ }
52
+
53
+ export const PointLabels: Story = {
54
+ loaders: [
55
+ async (): Promise<{ data: { performances: [] } }> => {
56
+ try {
57
+ const response = await fetch('https://gist.githubusercontent.com/Stukova/e6c4c7777e0166431a983999213f10c8/raw/performances.json')
58
+ if (!response.ok) {
59
+ throw new Error(`HTTP error! Status: ${response.status}`)
60
+ }
61
+ return {
62
+ data: await response.json(),
63
+ }
64
+ } catch (error) {
65
+ console.error('Failed to fetch data:', error)
66
+ return {
67
+ data: { performances: [] },
68
+ }
69
+ }
70
+ },
71
+ ],
72
+ async beforeEach (d): Promise<() => void> {
73
+ return (): void => {
74
+ d.args.destroy?.()
75
+ d.args.graph?.destroy()
76
+ }
77
+ },
78
+ render: (args, { loaded: { data } }): HTMLDivElement => {
79
+ const div = document.createElement('div')
80
+ div.style.height = '100vh'
81
+ div.style.width = '100%'
82
+
83
+ try {
84
+ const story = pointLabels(data.performances)
85
+ args.graph = story.graph
86
+ args.destroy = story.destroy
87
+ div.appendChild(story.div)
88
+ } catch (error) {
89
+ console.error('Failed to load PointLabels story:', error)
90
+ div.innerHTML = '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #ff0000;">Failed to load story</div>'
91
+ }
92
+
93
+ return div
94
+ },
95
+ parameters: {
96
+ sourceCode: [
97
+ { name: 'Story', code: pointLabelsStoryRaw },
98
+ { name: 'data.ts', code: pointLabelsStoryDataRaw },
99
+ { name: 'labels.ts', code: pointLabelsStoryLabelsRaw },
100
+ { name: 'style.css', code: pointLabelsStoryCssRaw },
101
+ ],
102
+ },
103
+ }
104
+
105
+ export const RemovePoints: Story = {
106
+ ...createStory(removePoints),
107
+ parameters: {
108
+ sourceCode: [
109
+ { name: 'Story', code: removePointsStoryRaw },
110
+ { name: 'config.ts', code: removePointsStoryConfigRaw },
111
+ { name: 'data-gen.ts', code: removePointsStoryDataGenRaw },
112
+ { name: 'style.css', code: removePointsStoryCssRaw },
113
+ ],
114
+ },
115
+ }
116
+
117
+ export const LinkHovering: Story = {
118
+ ...createStory(linkHovering),
119
+ name: 'Link Hovering',
120
+ parameters: {
121
+ sourceCode: [
122
+ { name: 'Story', code: linkHoveringStoryRaw },
123
+ { name: 'data-generator.ts', code: linkHoveringStoryDataGenRaw },
124
+ { name: 'style.css', code: linkHoveringStoryCssRaw },
125
+ ],
126
+ },
127
+ }
128
+
129
+ // eslint-disable-next-line import/no-default-export
130
+ export default meta
@@ -0,0 +1,52 @@
1
+ import { Graph } from '@cosmos.gl/graph'
2
+ import { createCosmos } from '../../create-cosmos'
3
+ import { generateMeshData } from '../../generate-mesh-data'
4
+ import { PolygonSelection } from './polygon'
5
+
6
+ export const polygonSelection = async (): Promise<{div: HTMLDivElement; graph: Graph; destroy: () => void }> => {
7
+ const nClusters = 25
8
+ const { pointPositions, pointColors, pointClusters } = generateMeshData(150, 150, nClusters, 1.0)
9
+
10
+ const { div, graph, destroy: baseDestroy } = await createCosmos({
11
+ pointPositions,
12
+ pointColors,
13
+ pointClusters,
14
+ simulationGravity: 1.5,
15
+ simulationCluster: 0.3,
16
+ simulationRepulsion: 8,
17
+ pointSize: 8,
18
+ backgroundColor: '#1a1a2e',
19
+ pointGreyoutOpacity: 0.2,
20
+ onBackgroundClick: (): void => {
21
+ graph.unselectPoints()
22
+ },
23
+ })
24
+
25
+ graph.setZoomLevel(0.4)
26
+
27
+ const polygonSelection = new PolygonSelection(div, (polygonPoints) => {
28
+ graph.selectPointsInPolygon(polygonPoints)
29
+ })
30
+
31
+ const actionsDiv = document.createElement('div')
32
+ actionsDiv.className = 'actions'
33
+ div.appendChild(actionsDiv)
34
+
35
+ const polygonButton = document.createElement('div')
36
+ polygonButton.className = 'action'
37
+ polygonButton.textContent = 'Enable Polygon Selection'
38
+ polygonButton.addEventListener('click', () => {
39
+ polygonSelection.enablePolygonMode()
40
+ })
41
+ actionsDiv.appendChild(polygonButton)
42
+
43
+ const destroy = (): void => {
44
+ polygonSelection.destroy()
45
+ if (actionsDiv.parentNode) {
46
+ actionsDiv.parentNode.removeChild(actionsDiv)
47
+ }
48
+ baseDestroy?.()
49
+ }
50
+
51
+ return { div, graph, destroy }
52
+ }
@@ -0,0 +1,143 @@
1
+ import './style.css'
2
+
3
+ export class PolygonSelection {
4
+ private canvas: HTMLCanvasElement
5
+ private ctx: CanvasRenderingContext2D
6
+ private isDrawing = false
7
+ private points: Array<{ x: number; y: number }> = []
8
+ private graphDiv: HTMLElement
9
+ private onPolygonComplete?: (points: [number, number][]) => void
10
+ private boundStartDrawing: (e: MouseEvent) => void
11
+ private boundDraw: (e: MouseEvent) => void
12
+ private boundStopDrawing: () => void
13
+ private resizeObserver: ResizeObserver
14
+
15
+ public constructor (graphDiv: HTMLElement, onPolygonComplete?: (points: [number, number][]) => void) {
16
+ this.graphDiv = graphDiv
17
+ this.onPolygonComplete = onPolygonComplete
18
+
19
+ // Bind event handlers
20
+ this.boundStartDrawing = this.startDrawing.bind(this)
21
+ this.boundDraw = this.draw.bind(this)
22
+ this.boundStopDrawing = this.stopDrawing.bind(this)
23
+
24
+ // Create canvas
25
+ this.canvas = document.createElement('canvas')
26
+ this.canvas.className = 'polygon-canvas'
27
+
28
+ const ctx = this.canvas.getContext('2d')
29
+ if (!ctx) throw new Error('Could not get canvas context')
30
+ this.ctx = ctx
31
+
32
+ this.graphDiv.appendChild(this.canvas)
33
+
34
+ this.resizeObserver = new ResizeObserver(() => {
35
+ this.resizeCanvas()
36
+ })
37
+ this.resizeObserver.observe(this.graphDiv)
38
+ }
39
+
40
+ public enablePolygonMode (): void {
41
+ this.canvas.style.pointerEvents = 'auto'
42
+ this.canvas.style.cursor = 'crosshair'
43
+
44
+ // Add event listeners
45
+ this.canvas.addEventListener('mousedown', this.boundStartDrawing)
46
+ this.canvas.addEventListener('mousemove', this.boundDraw)
47
+ this.canvas.addEventListener('mouseup', this.boundStopDrawing)
48
+ }
49
+
50
+ public disablePolygonMode (): void {
51
+ this.canvas.style.pointerEvents = 'none'
52
+ this.canvas.style.cursor = 'default'
53
+
54
+ // Remove event listeners
55
+ this.canvas.removeEventListener('mousedown', this.boundStartDrawing)
56
+ this.canvas.removeEventListener('mousemove', this.boundDraw)
57
+ this.canvas.removeEventListener('mouseup', this.boundStopDrawing)
58
+
59
+ // Clear canvas
60
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
61
+ }
62
+
63
+ public destroy (): void {
64
+ this.disablePolygonMode()
65
+ this.resizeObserver.disconnect()
66
+ if (this.canvas.parentNode) {
67
+ this.canvas.parentNode.removeChild(this.canvas)
68
+ }
69
+ }
70
+
71
+ private resizeCanvas (): void {
72
+ const rect = this.graphDiv.getBoundingClientRect()
73
+
74
+ // Apply pixel ratio for crisp rendering
75
+ const pixelRatio = window.devicePixelRatio || 1
76
+ this.canvas.width = rect.width * pixelRatio
77
+ this.canvas.height = rect.height * pixelRatio
78
+
79
+ // Reset transform and scale the context to match the pixel ratio
80
+ this.ctx.resetTransform()
81
+ this.ctx.scale(pixelRatio, pixelRatio)
82
+ }
83
+
84
+ private startDrawing (e: MouseEvent): void {
85
+ this.isDrawing = true
86
+ this.points = []
87
+ const rect = this.canvas.getBoundingClientRect()
88
+ this.points.push({ x: e.clientX - rect.left, y: e.clientY - rect.top })
89
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
90
+ }
91
+
92
+ private draw (e: MouseEvent): void {
93
+ if (!this.isDrawing) return
94
+
95
+ const rect = this.canvas.getBoundingClientRect()
96
+ this.points.push({ x: e.clientX - rect.left, y: e.clientY - rect.top })
97
+
98
+ // Clear the entire canvas accounting for pixel ratio
99
+ const pixelRatio = window.devicePixelRatio || 1
100
+ this.ctx.clearRect(0, 0, this.canvas.width / pixelRatio, this.canvas.height / pixelRatio)
101
+
102
+ this.ctx.beginPath()
103
+ if (this.points.length > 0 && this.points[0]) {
104
+ this.ctx.moveTo(this.points[0].x, this.points[0].y)
105
+ }
106
+
107
+ for (let i = 1; i < this.points.length; i++) {
108
+ const point = this.points[i]
109
+ if (point) {
110
+ this.ctx.lineTo(point.x, point.y)
111
+ }
112
+ }
113
+
114
+ this.ctx.strokeStyle = '#ffffff'
115
+ this.ctx.lineWidth = 2
116
+ this.ctx.stroke()
117
+ }
118
+
119
+ private stopDrawing (): void {
120
+ if (!this.isDrawing) return
121
+ this.isDrawing = false
122
+
123
+ if (this.points.length > 2) {
124
+ this.ctx.closePath()
125
+ this.ctx.stroke()
126
+
127
+ const polygonPoints: [number, number][] = this.points.map(p => [p.x, p.y])
128
+ const firstPolygonPoint = polygonPoints[0]
129
+ const lastPolygonPoint = polygonPoints[polygonPoints.length - 1]
130
+ if (firstPolygonPoint && lastPolygonPoint && (firstPolygonPoint[0] !== lastPolygonPoint[0] || firstPolygonPoint[1] !== lastPolygonPoint[1])) {
131
+ polygonPoints.push(firstPolygonPoint)
132
+ }
133
+
134
+ if (this.onPolygonComplete) {
135
+ this.onPolygonComplete(polygonPoints)
136
+ }
137
+ }
138
+
139
+ const pixelRatio = window.devicePixelRatio || 1
140
+ this.ctx.clearRect(0, 0, this.canvas.width / pixelRatio, this.canvas.height / pixelRatio)
141
+ this.disablePolygonMode()
142
+ }
143
+ }
@@ -0,0 +1,8 @@
1
+ .polygon-canvas {
2
+ position: absolute;
3
+ top: 0;
4
+ left: 0;
5
+ pointer-events: none;
6
+ width: 100%;
7
+ height: 100%;
8
+ }
@@ -0,0 +1,24 @@
1
+
2
+ import { Graph } from '@cosmos.gl/graph'
3
+ import { createCosmos } from '../create-cosmos'
4
+ import { generateMeshData } from '../generate-mesh-data'
5
+
6
+ export const radial = async (): Promise<{ graph: Graph; div: HTMLDivElement; destroy?: () => void }> => {
7
+ const {
8
+ pointPositions, pointColors, pointSizes,
9
+ links, linkColors, linkWidths,
10
+ pointClusters, clusterPositions, clusterStrength,
11
+ } = generateMeshData(100, 100, 100, 1.0)
12
+
13
+ return createCosmos({
14
+ pointPositions,
15
+ pointColors,
16
+ pointSizes,
17
+ pointClusters,
18
+ links,
19
+ linkColors,
20
+ linkWidths,
21
+ clusterPositions,
22
+ clusterStrength,
23
+ })
24
+ }