@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,54 @@
|
|
|
1
|
+
import { Graph } from '@cosmos.gl/graph'
|
|
2
|
+
import { createClusterLabels } from '../create-cluster-labels'
|
|
3
|
+
import { createCosmos } from '../create-cosmos'
|
|
4
|
+
import { generateMeshData } from '../generate-mesh-data'
|
|
5
|
+
|
|
6
|
+
export const withLabels = async (): Promise<{div: HTMLDivElement; graph: Graph; destroy: () => void }> => {
|
|
7
|
+
let nClusters = 2
|
|
8
|
+
const { pointPositions, pointColors, pointClusters } = generateMeshData(100, 100, nClusters, 1.0)
|
|
9
|
+
|
|
10
|
+
const { div, graph, destroy: baseDestroy } = await createCosmos({
|
|
11
|
+
pointPositions,
|
|
12
|
+
pointColors,
|
|
13
|
+
pointClusters,
|
|
14
|
+
simulationGravity: 2,
|
|
15
|
+
simulationCluster: 0.25,
|
|
16
|
+
simulationRepulsion: 10,
|
|
17
|
+
pointSize: 10,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const updateClusterLabels = createClusterLabels({ div })
|
|
21
|
+
graph.setZoomLevel(0.3)
|
|
22
|
+
|
|
23
|
+
graph.setConfig({
|
|
24
|
+
onZoom: updateClusterLabels.bind(this, graph),
|
|
25
|
+
onSimulationTick: updateClusterLabels.bind(this, graph),
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
let increasing = true
|
|
29
|
+
const interval = setInterval(() => {
|
|
30
|
+
if (increasing) {
|
|
31
|
+
nClusters += 5
|
|
32
|
+
if (nClusters >= 15) {
|
|
33
|
+
increasing = false
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
nClusters -= 5
|
|
37
|
+
if (nClusters <= 2) {
|
|
38
|
+
increasing = true
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const nextData = generateMeshData(100, 100, nClusters, 1.0)
|
|
43
|
+
graph.setPointClusters(nextData.pointClusters)
|
|
44
|
+
graph.setPointColors(nextData.pointColors)
|
|
45
|
+
graph.render(1)
|
|
46
|
+
}, 1500)
|
|
47
|
+
|
|
48
|
+
const destroy = (): void => {
|
|
49
|
+
clearInterval(interval)
|
|
50
|
+
baseDestroy?.()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { div, graph, destroy }
|
|
54
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
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 worm = async (): Promise<{graph: Graph; div: HTMLDivElement; destroy?: () => void}> => {
|
|
7
|
+
const { pointPositions, pointColors, links, linkColors, pointClusters } = generateMeshData(100, 100, 1000, 1.0)
|
|
8
|
+
|
|
9
|
+
const { div, graph, destroy } = await createCosmos({
|
|
10
|
+
simulationGravity: 0.5,
|
|
11
|
+
simulationRepulsion: 1,
|
|
12
|
+
simulationLinkSpring: 1,
|
|
13
|
+
pointPositions,
|
|
14
|
+
pointColors,
|
|
15
|
+
pointClusters,
|
|
16
|
+
links,
|
|
17
|
+
linkColors,
|
|
18
|
+
onSimulationTick: () => {
|
|
19
|
+
const currentPointColors = graph.getPointColors()
|
|
20
|
+
const newPointColors = new Float32Array(currentPointColors.length)
|
|
21
|
+
for (let i = 0; i < currentPointColors.length / 4; i++) {
|
|
22
|
+
if (i === 0) {
|
|
23
|
+
newPointColors[i * 4] = currentPointColors[currentPointColors.length - 4] as number
|
|
24
|
+
newPointColors[i * 4 + 1] = currentPointColors[currentPointColors.length - 3] as number
|
|
25
|
+
newPointColors[i * 4 + 2] = currentPointColors[currentPointColors.length - 2] as number
|
|
26
|
+
newPointColors[i * 4 + 3] = currentPointColors[currentPointColors.length - 1] as number
|
|
27
|
+
} else {
|
|
28
|
+
newPointColors[i * 4] = currentPointColors[(i - 1) * 4] as number
|
|
29
|
+
newPointColors[i * 4 + 1] = currentPointColors[(i - 1) * 4 + 1] as number
|
|
30
|
+
newPointColors[i * 4 + 2] = currentPointColors[(i - 1) * 4 + 2] as number
|
|
31
|
+
newPointColors[i * 4 + 3] = currentPointColors[(i - 1) * 4 + 3] as number
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
graph.setPointColors(newPointColors)
|
|
35
|
+
graph.render()
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return { div, graph, destroy }
|
|
40
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { Meta } from '@storybook/html'
|
|
2
|
+
import { CosmosStoryProps } from '@/graph/stories/create-cosmos'
|
|
3
|
+
import { createStory, Story } from '@/graph/stories/create-story'
|
|
4
|
+
import { withLabels } from './clusters/with-labels'
|
|
5
|
+
import { worm } from './clusters/worm'
|
|
6
|
+
import { radial } from './clusters/radial'
|
|
7
|
+
import { polygonSelection } from './clusters/polygon-selection'
|
|
8
|
+
|
|
9
|
+
import createCosmosRaw from './create-cosmos?raw'
|
|
10
|
+
import generateMeshDataRaw from './generate-mesh-data?raw'
|
|
11
|
+
import withLabelsStoryRaw from './clusters/with-labels?raw'
|
|
12
|
+
import createClusterLabelsRaw from './create-cluster-labels?raw'
|
|
13
|
+
import wormStory from './clusters/worm?raw'
|
|
14
|
+
import radialStory from './clusters/radial?raw'
|
|
15
|
+
import polygonSelectionStory from './clusters/polygon-selection?raw'
|
|
16
|
+
import polygonSelectionStyleRaw from './clusters/polygon-selection/style.css?raw'
|
|
17
|
+
import polygonSelectionPolygonRaw from './clusters/polygon-selection/polygon.ts?raw'
|
|
18
|
+
|
|
19
|
+
const meta: Meta<CosmosStoryProps> = {
|
|
20
|
+
title: 'Examples/Clusters',
|
|
21
|
+
parameters: {
|
|
22
|
+
controls: {
|
|
23
|
+
disable: true,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const sourceCodeAddonParams = [
|
|
29
|
+
{ name: 'create-cosmos', code: createCosmosRaw },
|
|
30
|
+
{ name: 'generate-mesh-data', code: generateMeshDataRaw },
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
export const Worm: Story = {
|
|
34
|
+
...createStory(worm),
|
|
35
|
+
parameters: {
|
|
36
|
+
sourceCode: [
|
|
37
|
+
{ name: 'Story', code: wormStory },
|
|
38
|
+
...sourceCodeAddonParams,
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const Radial: Story = {
|
|
44
|
+
...createStory(radial),
|
|
45
|
+
parameters: {
|
|
46
|
+
sourceCode: [
|
|
47
|
+
{ name: 'Story', code: radialStory },
|
|
48
|
+
...sourceCodeAddonParams,
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const WithLabels: Story = {
|
|
54
|
+
...createStory(withLabels),
|
|
55
|
+
parameters: {
|
|
56
|
+
sourceCode: [
|
|
57
|
+
{ name: 'Story', code: withLabelsStoryRaw },
|
|
58
|
+
{ name: 'create-cluster-labels', code: createClusterLabelsRaw },
|
|
59
|
+
...sourceCodeAddonParams,
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const PolygonSelection: Story = {
|
|
65
|
+
...createStory(polygonSelection),
|
|
66
|
+
parameters: {
|
|
67
|
+
sourceCode: [
|
|
68
|
+
{ name: 'Story', code: polygonSelectionStory },
|
|
69
|
+
{ name: 'polygon.ts', code: polygonSelectionPolygonRaw },
|
|
70
|
+
...sourceCodeAddonParams,
|
|
71
|
+
{ name: 'style.css', code: polygonSelectionStyleRaw },
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// eslint-disable-next-line import/no-default-export
|
|
77
|
+
export default meta
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Graph } from '@cosmos.gl/graph'
|
|
2
|
+
|
|
3
|
+
export const createClusterLabels = (props: { div: HTMLDivElement }): (graph: Graph) => void => {
|
|
4
|
+
const clusterLabelDivs: HTMLDivElement[] = []
|
|
5
|
+
return function updateClusterLabels (graph: Graph): void {
|
|
6
|
+
const clusterPositions = graph.getClusterPositions()
|
|
7
|
+
const nClusters = clusterPositions.length / 2
|
|
8
|
+
if (nClusters === 0) return
|
|
9
|
+
if (clusterLabelDivs.length !== nClusters) {
|
|
10
|
+
clusterLabelDivs.forEach((div) => div.remove())
|
|
11
|
+
for (let i = 0; i < nClusters; i++) {
|
|
12
|
+
const clusterLabelDiv = document.createElement('div')
|
|
13
|
+
const contentLabel = document.createElement('p')
|
|
14
|
+
clusterLabelDiv.appendChild(contentLabel)
|
|
15
|
+
clusterLabelDiv.style.position = 'absolute'
|
|
16
|
+
clusterLabelDiv.style.pointerEvents = 'none'
|
|
17
|
+
|
|
18
|
+
contentLabel.style.fontFamily = [
|
|
19
|
+
'"Nunito Sans"',
|
|
20
|
+
'-apple-system',
|
|
21
|
+
'".SFNSText-Regular"',
|
|
22
|
+
'"San Francisco"',
|
|
23
|
+
'BlinkMacSystemFont',
|
|
24
|
+
'"Segoe UI"',
|
|
25
|
+
'"Helvetica Neue"',
|
|
26
|
+
'Helvetica',
|
|
27
|
+
'Arial',
|
|
28
|
+
'sans-serif',
|
|
29
|
+
].join(', ')
|
|
30
|
+
contentLabel.style.fontWeight = 'bold'
|
|
31
|
+
contentLabel.style.color = 'white'
|
|
32
|
+
contentLabel.style.transform = 'translate(-50%, -100%)'
|
|
33
|
+
contentLabel.innerText = `Cluster ${i + 1}`
|
|
34
|
+
|
|
35
|
+
props.div.appendChild(clusterLabelDiv)
|
|
36
|
+
clusterLabelDivs[i] = clusterLabelDiv
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < nClusters; i++) {
|
|
41
|
+
const clusterPosition = clusterPositions.slice(i * 2, i * 2 + 2)
|
|
42
|
+
const x = clusterPosition[0]
|
|
43
|
+
const y = clusterPosition[1]
|
|
44
|
+
const clusterLabelDiv = clusterLabelDivs[i] as HTMLDivElement
|
|
45
|
+
const screenXY = graph.spaceToScreenPosition([x ?? 0, y ?? 0])
|
|
46
|
+
clusterLabelDiv.style.top = `${screenXY[1]}px`
|
|
47
|
+
clusterLabelDiv.style.left = `${screenXY[0]}px`
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Graph, GraphConfigInterface } from '@cosmos.gl/graph'
|
|
2
|
+
|
|
3
|
+
export type CosmosStoryProps = GraphConfigInterface & {
|
|
4
|
+
pointPositions: Float32Array;
|
|
5
|
+
pointColors?: Float32Array;
|
|
6
|
+
pointSizes?: Float32Array;
|
|
7
|
+
|
|
8
|
+
links?: Float32Array;
|
|
9
|
+
linkColors?: Float32Array;
|
|
10
|
+
linkWidths?: Float32Array;
|
|
11
|
+
// linkStrength?: Float32Array;
|
|
12
|
+
|
|
13
|
+
pointClusters?: number[];
|
|
14
|
+
clusterPositions?: number[];
|
|
15
|
+
clusterStrength?: Float32Array;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const createCosmos = (props: CosmosStoryProps): { div: HTMLDivElement; graph: Graph; destroy?: () => void} => {
|
|
19
|
+
const div = document.createElement('div')
|
|
20
|
+
div.style.height = '100vh'
|
|
21
|
+
div.style.width = '100%'
|
|
22
|
+
|
|
23
|
+
const config: GraphConfigInterface = {
|
|
24
|
+
backgroundColor: '#2d313a',
|
|
25
|
+
pointSize: 3,
|
|
26
|
+
pointColor: '#4B5BBF',
|
|
27
|
+
pointGreyoutOpacity: 0.1,
|
|
28
|
+
scalePointsOnZoom: true,
|
|
29
|
+
linkWidth: 0.8,
|
|
30
|
+
linkColor: '#5F74C2',
|
|
31
|
+
linkArrows: false,
|
|
32
|
+
linkGreyoutOpacity: 0,
|
|
33
|
+
curvedLinks: true,
|
|
34
|
+
renderLinks: true,
|
|
35
|
+
renderHoveredPointRing: true,
|
|
36
|
+
fitViewOnInit: false,
|
|
37
|
+
hoveredPointRingColor: '#4B5BBF',
|
|
38
|
+
enableDrag: true,
|
|
39
|
+
simulationLinkDistance: 1,
|
|
40
|
+
simulationLinkSpring: 2,
|
|
41
|
+
simulationRepulsion: 0.5,
|
|
42
|
+
simulationGravity: 0.02,
|
|
43
|
+
simulationFriction: 0.7,
|
|
44
|
+
simulationDecay: 10000000,
|
|
45
|
+
attribution: 'visualized with <a href="https://cosmograph.app/" style="color: var(--cosmosgl-attribution-color);" target="_blank">Cosmograph</a>',
|
|
46
|
+
...props,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const graph = new Graph(div, config)
|
|
50
|
+
|
|
51
|
+
graph.setPointPositions(props.pointPositions)
|
|
52
|
+
if (props.pointColors) graph.setPointColors(props.pointColors)
|
|
53
|
+
if (props.pointSizes) graph.setPointSizes(props.pointSizes)
|
|
54
|
+
|
|
55
|
+
if (props.links) graph.setLinks(props.links)
|
|
56
|
+
if (props.linkColors) graph.setLinkColors(props.linkColors)
|
|
57
|
+
if (props.linkWidths) graph.setLinkWidths(props.linkWidths)
|
|
58
|
+
// if (props.linkStrength) graph.setLinkStrength(props.linkStrength)
|
|
59
|
+
|
|
60
|
+
if (props.pointClusters) graph.setPointClusters(props.pointClusters)
|
|
61
|
+
if (props.clusterPositions) graph.setClusterPositions(props.clusterPositions)
|
|
62
|
+
if (props.clusterStrength) graph.setPointClusterStrength(props.clusterStrength)
|
|
63
|
+
|
|
64
|
+
graph.zoom(0.9)
|
|
65
|
+
graph.render()
|
|
66
|
+
|
|
67
|
+
const destroy = (): void => {
|
|
68
|
+
graph.destroy()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { div, graph, destroy }
|
|
72
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Graph } from '@cosmos.gl/graph'
|
|
2
|
+
import type { StoryObj } from '@storybook/html'
|
|
3
|
+
import { CosmosStoryProps } from '@/graph/stories/create-cosmos'
|
|
4
|
+
|
|
5
|
+
export type Story = StoryObj<CosmosStoryProps & { graph: Graph; destroy?: () => void }>;
|
|
6
|
+
|
|
7
|
+
export const createStory: (storyFunction: () => {
|
|
8
|
+
graph: Graph;
|
|
9
|
+
div: HTMLDivElement;
|
|
10
|
+
destroy?: () => void;
|
|
11
|
+
} | Promise<{
|
|
12
|
+
graph: Graph;
|
|
13
|
+
div: HTMLDivElement;
|
|
14
|
+
destroy?: () => void;
|
|
15
|
+
}>) => Story = (storyFunction) => ({
|
|
16
|
+
async beforeEach (d): Promise<() => void> {
|
|
17
|
+
return (): void => {
|
|
18
|
+
d.args.destroy?.()
|
|
19
|
+
d.args.graph?.destroy()
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
render: (args): HTMLDivElement => {
|
|
23
|
+
const result = storyFunction()
|
|
24
|
+
|
|
25
|
+
if (result instanceof Promise) {
|
|
26
|
+
// For async story functions, create a simple div and update it when ready
|
|
27
|
+
const div = document.createElement('div')
|
|
28
|
+
div.style.height = '100vh'
|
|
29
|
+
div.style.width = '100%'
|
|
30
|
+
div.innerHTML = '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #666;">Loading story...</div>'
|
|
31
|
+
|
|
32
|
+
result.then((story) => {
|
|
33
|
+
args.graph = story.graph
|
|
34
|
+
args.destroy = story.destroy
|
|
35
|
+
// Replace the content with the actual story div
|
|
36
|
+
div.innerHTML = ''
|
|
37
|
+
div.appendChild(story.div)
|
|
38
|
+
}).catch((error) => {
|
|
39
|
+
console.error('Failed to load story:', error)
|
|
40
|
+
div.innerHTML = '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #ff0000;">Failed to load story</div>'
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
return div
|
|
44
|
+
} else {
|
|
45
|
+
// Synchronous story function
|
|
46
|
+
args.graph = result.graph
|
|
47
|
+
args.destroy = result.destroy
|
|
48
|
+
return result.div
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Graph } from '@cosmos.gl/graph'
|
|
2
|
+
import { createCosmos } from '../create-cosmos'
|
|
3
|
+
import { generateMeshData } from '../generate-mesh-data'
|
|
4
|
+
|
|
5
|
+
export const fullMesh = async (): Promise<{ graph: Graph; div: HTMLDivElement; destroy?: () => void}> => {
|
|
6
|
+
const { pointPositions, links, pointColors } = generateMeshData(40, 30, 15, 1.0)
|
|
7
|
+
|
|
8
|
+
return createCosmos({
|
|
9
|
+
pointPositions,
|
|
10
|
+
links,
|
|
11
|
+
pointColors,
|
|
12
|
+
})
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Graph } from '@cosmos.gl/graph'
|
|
2
|
+
import { createCosmos } from '../create-cosmos'
|
|
3
|
+
import { generateMeshData } from '../generate-mesh-data'
|
|
4
|
+
|
|
5
|
+
export const meshWithHoles = async (): Promise<{ graph: Graph; div: HTMLDivElement; destroy?: () => void}> => {
|
|
6
|
+
const { pointPositions, links, pointColors } = generateMeshData(40, 80, 15, 0.8)
|
|
7
|
+
|
|
8
|
+
return createCosmos({
|
|
9
|
+
pointPositions,
|
|
10
|
+
links,
|
|
11
|
+
pointColors,
|
|
12
|
+
})
|
|
13
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
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 { meshWithHoles } from './experiments/mesh-with-holes'
|
|
6
|
+
import { fullMesh } from './experiments/full-mesh'
|
|
7
|
+
|
|
8
|
+
import createCosmosRaw from './create-cosmos?raw'
|
|
9
|
+
import generateMeshDataRaw from './generate-mesh-data?raw'
|
|
10
|
+
import meshWithHolesRaw from './experiments/mesh-with-holes?raw'
|
|
11
|
+
import fullMeshRaw from './experiments/full-mesh?raw'
|
|
12
|
+
|
|
13
|
+
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
|
|
14
|
+
const meta: Meta<CosmosStoryProps> = {
|
|
15
|
+
title: 'Examples/Experiments',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const sourceCodeAddonParams = [
|
|
19
|
+
{ name: 'create-cosmos', code: createCosmosRaw },
|
|
20
|
+
{ name: 'generate-mesh-data', code: generateMeshDataRaw },
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
export const FullMesh: Story = {
|
|
24
|
+
...createStory(fullMesh),
|
|
25
|
+
parameters: {
|
|
26
|
+
sourceCode: [
|
|
27
|
+
{ name: 'Story', code: fullMeshRaw },
|
|
28
|
+
...sourceCodeAddonParams,
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
export const MeshWithHoles: Story = {
|
|
33
|
+
...createStory(meshWithHoles),
|
|
34
|
+
parameters: {
|
|
35
|
+
sourceCode: [
|
|
36
|
+
{ name: 'Story', code: meshWithHolesRaw },
|
|
37
|
+
...sourceCodeAddonParams,
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// eslint-disable-next-line import/no-default-export
|
|
43
|
+
export default meta
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { scaleLinear, scaleSequential } from 'd3-scale'
|
|
2
|
+
import { interpolateWarm } from 'd3-scale-chromatic'
|
|
3
|
+
import { getRgbaColor } from '@cosmos.gl/graph'
|
|
4
|
+
|
|
5
|
+
function getRandom (min: number, max: number): number {
|
|
6
|
+
return Math.random() * (max - min) + min
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function getPositionOnCircle (radius: number, angle: number, center: number): [number, number] {
|
|
10
|
+
const x = center + radius * Math.cos(angle)
|
|
11
|
+
const y = center + radius * Math.sin(angle)
|
|
12
|
+
return [x, y]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type MeshData = {
|
|
16
|
+
pointPositions: Float32Array;
|
|
17
|
+
pointColors: Float32Array;
|
|
18
|
+
pointSizes: Float32Array;
|
|
19
|
+
|
|
20
|
+
links: Float32Array;
|
|
21
|
+
linkColors: Float32Array;
|
|
22
|
+
linkWidths: Float32Array;
|
|
23
|
+
// linkStrength: Float32Array;
|
|
24
|
+
|
|
25
|
+
pointClusters: number[];
|
|
26
|
+
clusterPositions: number[];
|
|
27
|
+
clusterStrength: Float32Array;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function generateMeshData (
|
|
31
|
+
n: number,
|
|
32
|
+
m: number,
|
|
33
|
+
nClusters: number,
|
|
34
|
+
wholeness: number,
|
|
35
|
+
radialness = [10, 1000]
|
|
36
|
+
): MeshData {
|
|
37
|
+
const pointColorScale = scaleSequential(interpolateWarm)
|
|
38
|
+
pointColorScale.domain([0, nClusters])
|
|
39
|
+
const radius = scaleLinear(radialness)
|
|
40
|
+
radius.domain([0, nClusters])
|
|
41
|
+
|
|
42
|
+
const pointPositions = new Float32Array(n * m * 2)
|
|
43
|
+
const links: number[] = []
|
|
44
|
+
const pointClusters = new Array(n * m)
|
|
45
|
+
const clusterPositions = new Array(nClusters * 2)
|
|
46
|
+
const clusterStrength = new Float32Array(n * m)
|
|
47
|
+
const pointColors = new Float32Array(n * m * 4)
|
|
48
|
+
const pointSizes = new Float32Array(n * m)
|
|
49
|
+
|
|
50
|
+
// The maximum texture size can vary between devices and WebGL versions
|
|
51
|
+
// The space size should be less than the maximum texture size to ensure better visual quality of the generated mesh.
|
|
52
|
+
const gl = document.createElement('canvas').getContext('webgl')
|
|
53
|
+
const spaceSize = (gl?.getParameter(gl.MAX_TEXTURE_SIZE) ?? 16384) / 2
|
|
54
|
+
gl?.getExtension('WEBGL_lose_context')?.loseContext()
|
|
55
|
+
|
|
56
|
+
for (let clusterIndex = 0; clusterIndex < nClusters; clusterIndex += 1) {
|
|
57
|
+
const [x, y] = getPositionOnCircle(radius(clusterIndex), 15 * Math.PI * (clusterIndex / nClusters), spaceSize / 2)
|
|
58
|
+
clusterPositions[clusterIndex * 2] = x
|
|
59
|
+
clusterPositions[clusterIndex * 2 + 1] = y
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (let pointIndex = 0; pointIndex < n * m; pointIndex += 1) {
|
|
63
|
+
const x = spaceSize * getRandom(0.495, 0.505)
|
|
64
|
+
const y = spaceSize * getRandom(0.495, 0.505)
|
|
65
|
+
pointPositions[pointIndex * 2] = x
|
|
66
|
+
pointPositions[pointIndex * 2 + 1] = y
|
|
67
|
+
|
|
68
|
+
pointClusters[pointIndex] = pointIndex % nClusters
|
|
69
|
+
clusterStrength[pointIndex] = (nClusters - (pointIndex % nClusters)) / nClusters
|
|
70
|
+
const pointColor = pointColorScale(pointIndex % nClusters)
|
|
71
|
+
const rgba = getRgbaColor(pointColor)
|
|
72
|
+
pointColors[pointIndex * 4] = rgba[0]
|
|
73
|
+
pointColors[pointIndex * 4 + 1] = rgba[1]
|
|
74
|
+
pointColors[pointIndex * 4 + 2] = rgba[2]
|
|
75
|
+
pointColors[pointIndex * 4 + 3] = rgba[3]
|
|
76
|
+
|
|
77
|
+
pointSizes[pointIndex] = getRandom(1, 5)
|
|
78
|
+
|
|
79
|
+
const nextPointIndex = pointIndex + 1
|
|
80
|
+
const bottomPointIndex = pointIndex + n
|
|
81
|
+
const pointLine = Math.floor(pointIndex / n)
|
|
82
|
+
const nextPointLine = Math.floor(nextPointIndex / n)
|
|
83
|
+
const bottomPointLine = Math.floor(bottomPointIndex / n)
|
|
84
|
+
|
|
85
|
+
if (pointLine === nextPointLine && Math.random() < wholeness) {
|
|
86
|
+
links.push(pointIndex)
|
|
87
|
+
links.push(nextPointIndex)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (bottomPointLine < m && Math.random() < wholeness) {
|
|
91
|
+
links.push(pointIndex)
|
|
92
|
+
links.push(bottomPointIndex)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const linkColors = new Float32Array(links.length / 2 * 4)
|
|
97
|
+
const linkWidths = new Float32Array(links.length / 2)
|
|
98
|
+
// const linkStrength = new Float32Array(links.length / 2)
|
|
99
|
+
for (let i = 0; i < links.length / 2; i++) {
|
|
100
|
+
const sourcePointIndex = links[i * 2] as number
|
|
101
|
+
const rgba = getRgbaColor(pointColorScale(sourcePointIndex % nClusters))
|
|
102
|
+
linkColors[i * 4 + 0] = rgba[0]
|
|
103
|
+
linkColors[i * 4 + 1] = rgba[1]
|
|
104
|
+
linkColors[i * 4 + 2] = rgba[2]
|
|
105
|
+
linkColors[i * 4 + 3] = 0.9
|
|
106
|
+
|
|
107
|
+
linkWidths[i] = getRandom(0.4, 0.8)
|
|
108
|
+
// linkStrength[i] = (n * m - sourcePointIndex) / (n * m)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
pointPositions,
|
|
113
|
+
pointColors,
|
|
114
|
+
pointSizes,
|
|
115
|
+
|
|
116
|
+
links: new Float32Array(links),
|
|
117
|
+
linkColors,
|
|
118
|
+
linkWidths,
|
|
119
|
+
// linkStrength,
|
|
120
|
+
|
|
121
|
+
pointClusters,
|
|
122
|
+
clusterStrength,
|
|
123
|
+
clusterPositions,
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Graph } from '@cosmos.gl/graph'
|
|
2
|
+
import { moscowMetroCoords } from './moscow-metro-coords'
|
|
3
|
+
import { getPointColors } from './point-colors'
|
|
4
|
+
import './style.css'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This example demonstrates the importance of rescaling positions by Cosmos.
|
|
8
|
+
* The Moscow Metro station coordinates are are normalized (0-1 range in both dimensions).
|
|
9
|
+
* By default, cosmos.gl rescales these positions to fit the canvas.
|
|
10
|
+
* When disabling rescaling (`rescalePositions: false`):
|
|
11
|
+
* - Points render using raw coordinates
|
|
12
|
+
* - The entire graph occupies a tiny 1x1 area in WebGL's clip space (-1 to 1)
|
|
13
|
+
* - This causes visual artifacts due to WebGL's floating-point precision limitations
|
|
14
|
+
* - Points cluster in the center and may exhibit rendering glitches
|
|
15
|
+
*/
|
|
16
|
+
export const moscowMetroStations = (): {graph: Graph; div: HTMLDivElement; destroy?: () => void } => {
|
|
17
|
+
const div = document.createElement('div')
|
|
18
|
+
div.className = 'app'
|
|
19
|
+
|
|
20
|
+
const graphDiv = document.createElement('div')
|
|
21
|
+
graphDiv.className = 'graph'
|
|
22
|
+
div.appendChild(graphDiv)
|
|
23
|
+
|
|
24
|
+
const actionsDiv = document.createElement('div')
|
|
25
|
+
actionsDiv.className = 'actions'
|
|
26
|
+
div.appendChild(actionsDiv)
|
|
27
|
+
|
|
28
|
+
let rescalePositions = true
|
|
29
|
+
|
|
30
|
+
const graph = new Graph(graphDiv, {
|
|
31
|
+
backgroundColor: '#2d313a',
|
|
32
|
+
scalePointsOnZoom: false,
|
|
33
|
+
rescalePositions,
|
|
34
|
+
pointColor: '#FEE08B',
|
|
35
|
+
enableSimulation: false,
|
|
36
|
+
enableDrag: false,
|
|
37
|
+
fitViewOnInit: true,
|
|
38
|
+
attribution: 'visualized with <a href="https://cosmograph.app/" style="color: var(--cosmosgl-attribution-color);" target="_blank">Cosmograph</a>',
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const pointColors = getPointColors(moscowMetroCoords)
|
|
42
|
+
|
|
43
|
+
graph.setPointPositions(new Float32Array(moscowMetroCoords))
|
|
44
|
+
graph.setPointColors(pointColors)
|
|
45
|
+
graph.render()
|
|
46
|
+
|
|
47
|
+
const disableEnableRescaleButton = document.createElement('div')
|
|
48
|
+
disableEnableRescaleButton.className = 'action'
|
|
49
|
+
disableEnableRescaleButton.textContent = 'Disable Rescale'
|
|
50
|
+
actionsDiv.appendChild(disableEnableRescaleButton)
|
|
51
|
+
|
|
52
|
+
disableEnableRescaleButton.addEventListener('click', () => {
|
|
53
|
+
rescalePositions = !rescalePositions
|
|
54
|
+
disableEnableRescaleButton.textContent = rescalePositions ? 'Disable Rescale' : 'Enable Rescale'
|
|
55
|
+
graph.setConfig({ rescalePositions })
|
|
56
|
+
graph.setPointPositions(new Float32Array(moscowMetroCoords))
|
|
57
|
+
graph.render()
|
|
58
|
+
graph.fitView()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const destroy = (): void => {
|
|
62
|
+
graph.destroy()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { div, graph, destroy }
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const moscowMetroCoords = [0.6048172222222222, 0.8100822222222221, 0.6048466666666666, 0.8100211111111111, 0.6047641666666667, 0.8099794444444445, 0.6046672222222222, 0.8099377777777779, 0.6046313888888889, 0.8098894444444444, 0.6046005555555556, 0.8098627777777778, 0.6045797222222222, 0.8098277777777778, 0.6045522222222223, 0.8098094444444445, 0.6045200000000001, 0.8097761111111111, 0.6044905555555555, 0.809765, 0.6044722222222223, 0.8097288888888888, 0.6044547222222222, 0.8096961111111111, 0.6044286111111111, 0.8096422222222223, 0.604385, 0.8095927777777777, 0.6043441666666667, 0.8095738888888888, 0.6043311111111112, 0.8095016666666666, 0.6042591666666667, 0.8094033333333334, 0.6041833333333333, 0.8093172222222222, 0.6041202777777778, 0.8092427777777778, 0.6040902777777778, 0.8091438888888888, 0.6040052777777778, 0.8090722222222223, 0.6039555555555556, 0.809015, 0.603910125, 0.8088964833333334, 0.6039827833333333, 0.8088124555555556, 0.6040537, 0.8087146833333334, 0.6040783888888889, 0.8086688666666666, 0.6041465694444444, 0.8086257777777778, 0.6041172222222222, 0.8104394444444444, 0.6041011111111112, 0.8103655555555556, 0.6041002777777777, 0.8103049999999999, 0.6041297222222222, 0.8102211111111111, 0.604161111111111, 0.8101055555555556, 0.6042091666666667, 0.8100283333333334, 0.6042580555555556, 0.8100016666666666, 0.6043277777777778, 0.8099427777777778, 0.6043986111111112, 0.8098705555555555, 0.6044327777777778, 0.809833888888889, 0.6044624999999999, 0.8098038888888889, 0.6044972222222222, 0.8097655555555556, 0.6045263888888889, 0.809675, 0.6045491666666667, 0.809613888888889, 0.6046044444444445, 0.8094855555555556, 0.6046225, 0.8094166666666667, 0.6046216666666667, 0.809325, 0.6045797222222222, 0.809195, 0.604601111111111, 0.8090877777777779, 0.6046372222222222, 0.8090077777777778, 0.6047080555555555, 0.8089622222222222, 0.6047738888888889, 0.8089488888888888, 0.6048455555555555, 0.8089649999999999, 0.6049055555555556, 0.80907, 0.6037622222222222, 0.8103127777777778, 0.6037838888888889, 0.8102538888888888, 0.6038394444444445, 0.8101966666666667, 0.6038477777777778, 0.81014, 0.6038975, 0.8100211111111111, 0.6039113888888888, 0.8097594444444444, 0.6039355555555556, 0.8096711111111111, 0.6040163888888889, 0.8096150000000001, 0.604085, 0.8096088888888889, 0.6042172222222223, 0.8096455555555555, 0.6043458333333334, 0.80969, 0.6043952777777778, 0.8097077777777778, 0.6044613888888889, 0.8097344444444445, 0.6045044444444444, 0.8097588888888888, 0.6046047222222223, 0.8097644444444444, 0.6046683333333334, 0.80985, 0.6047325, 0.8098983333333334, 0.60478, 0.8099072222222222, 0.6048627777777777, 0.8099366666666666, 0.6049483333333333, 0.8099322222222223, 0.6049980555555556, 0.8099711111111112, 0.6049961111111111, 0.8100522222222222, 0.6040163888888889, 0.8096150000000001, 0.6040752777777778, 0.8096444444444444, 0.6041202777777778, 0.8096644444444444, 0.6041602777777778, 0.8096877777777778, 0.6042083333333333, 0.8097000000000001, 0.6042622222222223, 0.8096661111111112, 0.6043008333333333, 0.8096599999999999, 0.6042608333333334, 0.8097127777777777, 0.6042799999999999, 0.8097233333333334, 0.604348611111111, 0.8096866666666668, 0.6043958333333334, 0.8097155555555556, 0.6044463888888889, 0.8097322222222222, 0.6044680555555555, 0.8097361111111111, 0.6043455555555556, 0.8096922222222221, 0.6043816666666667, 0.809785, 0.6044011111111112, 0.8098688888888889, 0.6044522222222223, 0.8098883333333333, 0.6045327777777778, 0.8098877777777778, 0.6045969444444445, 0.80986, 0.6046097222222222, 0.8097611111111112, 0.6045880555555556, 0.8096766666666667, 0.6045497222222223, 0.8096211111111111, 0.6045119444444444, 0.8096061111111112, 0.6044697222222223, 0.8096094444444445, 0.6044208333333333, 0.8096427777777778, 0.6046147222222221, 0.8104844444444445, 0.6046233333333333, 0.8103855555555555, 0.6045908333333334, 0.8103066666666666, 0.6045508333333334, 0.8102494444444445, 0.6045586111111111, 0.8101172222222223, 0.6045527777777778, 0.8100488888888888, 0.604545, 0.8099644444444445, 0.6045327777777778, 0.8098955555555556, 0.6045330555555556, 0.8098516666666667, 0.6045483333333334, 0.8098116666666666, 0.6045369444444444, 0.8097516666666666, 0.6045205555555555, 0.8096733333333332, 0.6044755555555555, 0.8096144444444445, 0.6044674999999999, 0.8095544444444444, 0.6044058333333333, 0.8094872222222221, 0.6043702777777779, 0.8093761111111112, 0.6043408333333333, 0.8093222222222222, 0.6043177777777777, 0.8092788888888889, 0.6042791666666667, 0.8092061111111112, 0.6042380555555555, 0.8091266666666667, 0.6042188888888889, 0.8090738888888889, 0.6041894444444444, 0.808995, 0.6042591666666667, 0.8089238888888889, 0.6043169444444445, 0.8088944444444445, 0.60399, 0.8103372222222223, 0.6039988888888889, 0.8102805555555557, 0.6039911111111111, 0.8101483333333335, 0.6039866666666667, 0.810101111111111, 0.6040669444444444, 0.8100477777777778, 0.6041486111111111, 0.8099638888888888, 0.60422, 0.809875, 0.6042966666666666, 0.8098544444444444, 0.6043369444444445, 0.8098055555555554, 0.6043875, 0.8097844444444444, 0.6044663888888889, 0.8098055555555554, 0.6045163888888889, 0.8097811111111112, 0.6045369444444444, 0.8097516666666666, 0.6045894444444444, 0.8096677777777779, 0.6046275, 0.8096216666666667, 0.6046858333333334, 0.809585, 0.6048100000000001, 0.8094933333333333, 0.6049044444444445, 0.8094755555555556, 0.6049813888888889, 0.8095388888888888, 0.6050502777777779, 0.8095311111111111, 0.6051458333333333, 0.8094516666666667, 0.6051563888888889, 0.8093638888888889, 0.6051616666666667, 0.8093016666666668, 0.6037075, 0.8090772222222221, 0.6037644444444444, 0.8091088888888889, 0.6038058333333334, 0.8091533333333334, 0.6038636111111111, 0.8091627777777777, 0.6039366666666667, 0.8092194444444445, 0.6040252777777777, 0.8092827777777779, 0.6041202777777778, 0.8093866666666666, 0.6041622222222223, 0.8094305555555555, 0.60421, 0.8094827777777778, 0.6041583333333334, 0.8095811111111112, 0.6042172222222223, 0.8096455555555555, 0.6042552777777778, 0.8097083333333334, 0.6045205555555555, 0.8096733333333332, 0.6045952777777778, 0.8096727777777779, 0.6046733333333333, 0.8097066666666666, 0.6047752777777777, 0.8097355555555555, 0.6048611111111111, 0.8097644444444444, 0.6049627777777777, 0.8097283333333334, 0.6050461111111111, 0.8097316666666666, 0.6051772222222223, 0.809695, 0.6044083333333333, 0.8105444444444444, 0.6044538888888888, 0.8104655555555556, 0.6044575, 0.8103516666666667, 0.6044163888888889, 0.8102627777777778, 0.6043736111111111, 0.8101950000000001, 0.6043791666666667, 0.8100977777777778, 0.6043936111111111, 0.8100383333333332, 0.6044125, 0.8099594444444445, 0.6044475, 0.8098944444444445, 0.6044963888888889, 0.8098377777777779, 0.6044713888888888, 0.8098022222222222, 0.6044644444444445, 0.8097283333333334, 0.6044944444444444, 0.809655, 0.6045127777777778, 0.8096000000000001, 0.6045080555555556, 0.8094927777777777, 0.6045066666666666, 0.80935, 0.6044733333333333, 0.8092911111111112, 0.6044597222222222, 0.8092372222222223, 0.60444, 0.80918, 0.6044630555555556, 0.8091138888888889, 0.6044694444444445, 0.8090133333333334, 0.6044566666666666, 0.8089577777777778, 0.6044455555555556, 0.8088605555555556, 0.604435, 0.8087933333333333, 0.6043802777777778, 0.8087183333333333, 0.604300925, 0.8103611111111112, 0.6043364194444444, 0.8103101833333333, 0.6043729166666667, 0.8102555555555556, 0.6043736111111111, 0.8101950000000001, 0.6044113888888889, 0.8101266666666667, 0.6044522222222223, 0.8100738888888889, 0.6044894444444444, 0.8099744444444444, 0.6044863888888888, 0.8098977777777777, 0.6045, 0.8098266666666667, 0.6045533333333334, 0.8098155555555555, 0.604603611111111, 0.8097583333333335, 0.6046719444444444, 0.8097016666666667, 0.6046313888888889, 0.8096283333333334, 0.6046555555555555, 0.8095477777777779, 0.6046808333333333, 0.8094855555555556, 0.6047972222222222, 0.8094077777777777, 0.6048694444444445, 0.8093938888888889, 0.6048938888888888, 0.8093100000000001, 0.6048625, 0.8092205555555555, 0.6048419444444444, 0.8091666666666667, 0.6048433333333334, 0.8090744444444444, 0.6048436111111111, 0.8090061111111112, 0.6048480555555555, 0.8089577777777778, 0.6046638888888889, 0.8099461111111111, 0.6047305916666667, 0.8098905111111111, 0.6047409638888889, 0.8098040999999999, 0.6047754138888889, 0.8097426111111111, 0.6048057666666666, 0.8096208222222223, 0.6048011111111111, 0.8094833333333333, 0.6047972222222222, 0.8094111111111112, 0.604736111111111, 0.8093583333333334, 0.6046794444444444, 0.8093083333333333, 0.6045797222222222, 0.809195, 0.6044983333333332, 0.809185, 0.6044397222222222, 0.8091833333333333, 0.6043669444444445, 0.8092016666666666, 0.6042775, 0.809215, 0.6042272222222221, 0.8092783333333333, 0.6041805555555555, 0.809321111111111, 0.6041252777777777, 0.80938, 0.6040672222222222, 0.809428888888889, 0.6040325000000001, 0.8095294444444445, 0.6040158333333333, 0.8096133333333333, 0.6040572222222222, 0.8097177777777778, 0.6040855555555555, 0.8097694444444444, 0.6041233333333333, 0.8098544444444444, 0.6042552777777778, 0.8097083333333334, 0.6042388888888889, 0.8097594444444444, 0.6042241666666667, 0.8098711111111112, 0.6042591666666667, 0.8099255555555556, 0.6043252777777778, 0.8099544444444444, 0.6044111111111111, 0.8099644444444445, 0.6043227777777778, 0.8088911111111111, 0.6043802777777778, 0.8087894444444444, 0.6043797222222222, 0.8087166666666668, 0.6043183333333333, 0.8086005555555555, 0.6042861111111111, 0.8085861111111111, 0.6042522222222222, 0.8085655555555555, 0.6042105555555556, 0.8085444444444445, 0.6043858333333334, 0.8101055555555556, 0.6044197222222222, 0.8101211111111111, 0.6044691666666667, 0.8101211111111111, 0.6045191666666667, 0.8101211111111111, 0.6045508333333334, 0.8101333333333334, 0.6045691666666667, 0.8101622222222223, 0.6043594444444444, 0.8102622222222221, 0.6044250000000001, 0.8102644444444445, 0.6045591666666666, 0.8102555555555556, 0.6046258333333333, 0.8102222222222223, 0.604723888888889, 0.8101666666666666, 0.6048247222222222, 0.8100955555555557, 0.60485, 0.8100216666666666, 0.604841111111111, 0.8099366666666666, 0.6048475, 0.8098411111111111, 0.6048508333333333, 0.8097722222222223, 0.6048177777777778, 0.8096727777777779, 0.6048005555555556, 0.8096233333333334, 0.6047669444444443, 0.8095772222222223, 0.6047161111111111, 0.8095461111111111, 0.6047161111111111, 0.8095461111111111, 0.6046197222222222, 0.8094794444444444, 0.6045786111111111, 0.8094350000000001, 0.6044969444444445, 0.8093888888888888, 0.6044583333333333, 0.8093888888888888, 0.604405, 0.8094827777777778, 0.6043419444444444, 0.8095572222222223, 0.6042591666666667, 0.8096711111111111, 0.6042561111111111, 0.8097066666666666, 0.6042377777777778, 0.8097638888888888, 0.6041866666666666, 0.8098733333333333, 0.6041791666666667, 0.8099322222222223, 0.6041633333333334, 0.809995, 0.6041302777777777, 0.8100755555555555, 0.6041558333333333, 0.8101433333333334, 0.6042230555555556, 0.8102205555555555, 0.6043091666666667, 0.8102622222222221, 0.6048057666666666, 0.8096208222222223, 0.6048941666666666, 0.8095833333333333, 0.6049491666666666, 0.8095477777777779, 0.6050528055555555, 0.8094720555555556, 0.6051419444444445, 0.8094627777777779, 0.60522, 0.8095, 0.6052788888888888, 0.8094916666666667, 0.6053511111111111, 0.8094644444444444]
|