Skip to content

Commit 609287d

Browse files
committedDec 31, 2021
feat(network): add the ability to control the center force strength
1 parent 7fdff22 commit 609287d

File tree

13 files changed

+101
-48
lines changed

13 files changed

+101
-48
lines changed
 

‎Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ website: ##@2 website start website in dev mode
223223

224224
website-build: ##@2 website build website
225225
@echo "${YELLOW}Building website${RESET}"
226+
@-rm -rf website/.cache
226227
@cd website && yarn build
227228

228229
website-serve: ##@2 website build & serve website

‎packages/generators/src/network.ts

+16-16
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
11
import random from 'lodash/random'
22

33
type Link = {
4-
distance: number
54
source: string
65
target: string
6+
distance: number
77
}
88

99
type ExtraNode = {
10-
color: string
11-
depth: number
1210
id: string
13-
radius: number
11+
height: number
12+
color: string
13+
size: number
1414
}
1515

1616
export const generateNetworkData = ({
17-
rootNodeRadius = 12,
17+
rootNodeSize = 24,
1818
minMidNodes = 6,
1919
maxMidNodes = 16,
20-
midNodeRadius = 8,
20+
midNodeSize = 16,
2121
minLeaves = 4,
2222
maxLeaves = 16,
23-
leafRadius = 4,
23+
leafSize = 8,
2424
} = {}) => {
2525
const rootNode = {
2626
id: '0',
27-
radius: rootNodeRadius,
28-
depth: 0,
27+
height: 2,
28+
size: rootNodeSize,
2929
color: 'rgb(244, 117, 96)',
3030
}
3131
let nodes = Array.from({ length: random(minMidNodes, maxMidNodes) }, (_, k) => ({
3232
id: `${k + 1}`,
33-
radius: midNodeRadius,
34-
depth: 1,
33+
height: 1,
34+
size: midNodeSize,
3535
color: 'rgb(97, 205, 187)',
3636
}))
3737

@@ -41,28 +41,28 @@ export const generateNetworkData = ({
4141
links.push({
4242
source: '0',
4343
target: source.id,
44-
distance: 50,
44+
distance: 80,
4545
})
4646
nodes.forEach(target => {
4747
if (Math.random() < 0.04) {
4848
links.push({
4949
source: source.id,
5050
target: target.id,
51-
distance: 70,
51+
distance: 80,
5252
})
5353
}
5454
})
5555
Array.from({ length: random(minLeaves, maxLeaves) }, (_, k) => {
5656
extraNodes.push({
5757
id: `${source.id}.${k}`,
58-
radius: leafRadius,
59-
depth: 2,
58+
height: 0,
59+
size: leafSize,
6060
color: 'rgb(232, 193, 160)',
6161
})
6262
links.push({
6363
source: source.id,
6464
target: `${source.id}.${k}`,
65-
distance: 30,
65+
distance: 50,
6666
})
6767

6868
return null

‎packages/network/src/Network.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const InnerNetwork = <Node extends InputNode, Link extends InputLink>({
2121
data: { nodes: rawNodes, links: rawLinks },
2222

2323
linkDistance = svgDefaultProps.linkDistance,
24+
centeringStrength = svgDefaultProps.centeringStrength,
2425
repulsivity = svgDefaultProps.repulsivity,
2526
distanceMin = svgDefaultProps.distanceMin,
2627
distanceMax = svgDefaultProps.distanceMax,
@@ -70,6 +71,7 @@ const InnerNetwork = <Node extends InputNode, Link extends InputLink>({
7071
nodes: rawNodes,
7172
links: rawLinks,
7273
linkDistance,
74+
centeringStrength,
7375
repulsivity,
7476
distanceMin,
7577
distanceMax,

‎packages/network/src/NetworkCanvas.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const InnerNetworkCanvas = <Node extends InputNode, Link extends InputLink>({
1919
data: { nodes: rawNodes, links: rawLinks },
2020

2121
linkDistance = canvasDefaultProps.linkDistance,
22+
centeringStrength = canvasDefaultProps.centeringStrength,
2223
repulsivity = canvasDefaultProps.repulsivity,
2324
distanceMin = canvasDefaultProps.distanceMin,
2425
distanceMax = canvasDefaultProps.distanceMax,
@@ -54,6 +55,7 @@ const InnerNetworkCanvas = <Node extends InputNode, Link extends InputLink>({
5455
nodes: rawNodes,
5556
links: rawLinks,
5657
linkDistance,
58+
centeringStrength,
5759
repulsivity,
5860
distanceMin,
5961
distanceMax,

‎packages/network/src/defaults.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ export const commonDefaultProps: Omit<
2222
layers: ['links', 'nodes', 'annotations'],
2323

2424
linkDistance: 30,
25-
repulsivity: 3,
25+
centeringStrength: 1,
26+
repulsivity: 10,
2627
distanceMin: 1,
2728
distanceMax: Infinity,
28-
iterations: 160,
29+
iterations: 120,
2930

3031
nodeSize: 12,
3132
activeNodeSize: 18,

‎packages/network/src/hooks.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ const useDerivedProp = <Target, Output extends string | number>(
2525

2626
const useComputeForces = <Node extends InputNode, Link extends InputLink>({
2727
linkDistance,
28+
centeringStrength,
2829
repulsivity,
2930
distanceMin,
3031
distanceMax,
3132
center,
3233
}: {
3334
linkDistance: NetworkCommonProps<Node, Link>['linkDistance']
35+
centeringStrength: NetworkCommonProps<Node, Link>['centeringStrength']
3436
repulsivity: NetworkCommonProps<Node, Link>['repulsivity']
3537
distanceMin: NetworkCommonProps<Node, Link>['distanceMin']
3638
distanceMax: NetworkCommonProps<Node, Link>['distanceMax']
@@ -42,9 +44,9 @@ const useComputeForces = <Node extends InputNode, Link extends InputLink>({
4244
const centerY = center[1]
4345

4446
return useMemo(() => {
45-
const linkForce = forceLink<TransientNode<Node>, TransientLink<Node, Link>>().distance(
46-
link => getLinkDistance(link.data)
47-
)
47+
const linkForce = forceLink<TransientNode<Node>, TransientLink<Node, Link>>()
48+
.distance(link => getLinkDistance(link.data))
49+
.strength(centeringStrength)
4850

4951
const chargeForce = forceManyBody()
5052
.strength(-repulsivity)
@@ -54,7 +56,15 @@ const useComputeForces = <Node extends InputNode, Link extends InputLink>({
5456
const centerForce = forceCenter(centerX, centerY)
5557

5658
return { link: linkForce, charge: chargeForce, center: centerForce }
57-
}, [getLinkDistance, repulsivity, distanceMin, distanceMax, centerX, centerY])
59+
}, [
60+
getLinkDistance,
61+
centeringStrength,
62+
repulsivity,
63+
distanceMin,
64+
distanceMax,
65+
centerX,
66+
centerY,
67+
])
5868
}
5969

6070
const useNodeStyle = <Node extends InputNode, Link extends InputLink>({
@@ -141,6 +151,7 @@ export const useNetwork = <Node extends InputNode = InputNode, Link extends Inpu
141151
nodes,
142152
links,
143153
linkDistance = commonDefaultProps.linkDistance,
154+
centeringStrength = commonDefaultProps.centeringStrength,
144155
repulsivity = commonDefaultProps.repulsivity,
145156
distanceMin = commonDefaultProps.distanceMin,
146157
distanceMax = commonDefaultProps.distanceMax,
@@ -159,6 +170,7 @@ export const useNetwork = <Node extends InputNode = InputNode, Link extends Inpu
159170
nodes: Node[]
160171
links: Link[]
161172
linkDistance?: NetworkCommonProps<Node, Link>['linkDistance']
173+
centeringStrength?: NetworkCommonProps<Node, Link>['centeringStrength']
162174
repulsivity?: NetworkCommonProps<Node, Link>['repulsivity']
163175
distanceMin?: NetworkCommonProps<Node, Link>['distanceMin']
164176
distanceMax?: NetworkCommonProps<Node, Link>['distanceMax']
@@ -180,6 +192,7 @@ export const useNetwork = <Node extends InputNode = InputNode, Link extends Inpu
180192

181193
const forces = useComputeForces<Node, Link>({
182194
linkDistance,
195+
centeringStrength,
183196
repulsivity,
184197
distanceMin,
185198
distanceMax,

‎packages/network/src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ export type NetworkCommonProps<Node extends InputNode, Link extends InputLink> =
139139
margin: Box
140140

141141
linkDistance: DerivedProp<Link, number>
142+
centeringStrength: number
142143
repulsivity: number
143144
distanceMin: number
144145
distanceMax: number

‎website/src/components/icons/NetworkIcon.tsx

+19-9
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,30 @@ import networkDarkColoredImg from '../../assets/icons/network-dark-colored.png'
77
import { ICON_SIZE, Icon, colors, IconImg } from './styled'
88
import { IconType } from './types'
99

10+
type Node = {
11+
id: string
12+
size: number
13+
color: string
14+
}
15+
1016
const getData = (currentColors: any) => {
1117
let nodes = 'ABCDE'.split('').map(id => ({
1218
id,
13-
radius: 5,
19+
size: 10,
1420
color: currentColors[2],
1521
}))
1622
const links = nodes.map(node => ({
1723
source: 'root',
1824
target: node.id,
19-
distance: 2,
25+
distance: 30,
2026
}))
2127

22-
const leaves: any[] = []
28+
const leaves: Node[] = []
2329
nodes.forEach(node => {
2430
Array.from({ length: 7 }, (v, k) => {
2531
leaves.push({
2632
id: `${node.id}.${k}`,
27-
radius: 3,
33+
size: 6,
2834
color: currentColors[4],
2935
})
3036
links.push({
@@ -36,26 +42,30 @@ const getData = (currentColors: any) => {
3642
})
3743

3844
nodes = nodes.concat(leaves)
39-
nodes.unshift({ id: 'root', radius: 11, color: currentColors[4] })
45+
nodes.unshift({ id: 'root', size: 20, color: currentColors[4] })
4046

4147
return { nodes, links }
4248
}
4349

50+
type Link = ReturnType<typeof getData>['links'][number]
51+
4452
const chartProps = {
4553
width: ICON_SIZE,
4654
height: ICON_SIZE,
47-
linkDistance: (link: any) => link.distance,
48-
repulsivity: 5,
55+
linkDistance: (link: Link) => link.distance,
56+
centeringStrength: 1.2,
57+
repulsivity: 4,
4958
linkThickness: 2,
50-
nodeColor: (node: any) => node.color,
59+
nodeSize: (node: Node) => node.size,
60+
nodeColor: (node: Node) => node.color,
5161
linkColor: { from: 'source.color' },
5262
animate: false,
5363
isInteractive: false,
5464
}
5565

5666
const NetworkIconItem = ({ type }: { type: IconType }) => (
5767
<Icon id={`network-${type}`} type={type}>
58-
<Network {...chartProps} data={getData(colors[type].colors)} />
68+
<Network<Node, Link> {...chartProps} data={getData(colors[type].colors)} />
5969
</Icon>
6070
)
6171

‎website/src/data/components/network/mapper.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
import { settingsMapper } from '../../../lib/settings'
22

3-
export const dynamicNodeSizeValue = 'dynamic: (node: InputNode) => node.radius * 2'
4-
export const dynamicActiveNodeSizeValue = 'dynamic: (node: InputNode) => node.radius * 3'
3+
export const dynamicNodeSizeValue = 'dynamic: (node: InputNode) => node.size'
4+
export const dynamicActiveNodeSizeValue = 'dynamic: (node: InputNode) => node.size * 1.5'
55
export const dynamicLinkThicknessValue =
66
'dynamic: (link: ComputedLink) => (2 - link.source.data.depth) * 2'
77

88
export default settingsMapper({
99
nodeSize: (value: number | typeof dynamicNodeSizeValue) => {
1010
if (value === dynamicNodeSizeValue) {
11-
return (node: any) => node.radius * 2
11+
return (node: any) => node.size
1212
}
1313

1414
return value
1515
},
1616
activeNodeSize: (value: number | typeof dynamicActiveNodeSizeValue) => {
1717
if (value === dynamicActiveNodeSizeValue) {
18-
return (node: any) => node.radius * 3
18+
return (node: any) => node.size * 1.5
1919
}
2020

2121
return value
2222
},
2323
linkThickness: (value: number | typeof dynamicLinkThicknessValue) => {
2424
if (value === dynamicLinkThicknessValue) {
25-
return (link: any) => (2 - link.source.data.depth) * 2
25+
return (link: any) => 2 + link.target.data.height * 2
2626
}
2727

2828
return value

‎website/src/data/components/network/meta.yml

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ Network:
1717
- label: Custom link component
1818
link: network--custom-link
1919
description: |
20-
A network component connecting nodes with links.
20+
A network component connecting nodes with links using various forces,
21+
the resulting layout will depends on `linkDistance`, `centeringStrength`
22+
and `repulsivity`, so you should play with those parameters in order
23+
to achieve the desired result.
24+
25+
You can also add some extra constrains via `distanceMin` and `distanceMax`.
2126
2227
The responsive alternative of this component is `ResponsiveNetwork`.
2328

‎website/src/data/components/network/props.ts

+21-4
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,30 @@ const props: ChartProperty[] = [
5252
description: `
5353
If you set a **number**, this value will be used for all links.
5454
55-
If you use a **string**, this will be used to pick the distance
56-
from the corresponding link property, thus, this property
57-
should exist on each link.
58-
5955
If you use a **function**, it will receive a link and must return
6056
the desired distance.
57+
58+
Please note that in most cases you won't get links having the
59+
exact distance you specified as it also depends on the other forces.
60+
`,
61+
},
62+
{
63+
key: 'centeringStrength',
64+
group: 'Simulation',
65+
type: 'number',
66+
help: 'Control how much the centering force affects nodes positioning.',
67+
description: `
68+
This value will also affect the strength
69+
of \`distanceMin\` and \`distanceMax\`.
6170
`,
71+
flavors: allFlavors,
72+
defaultValue: defaults.centeringStrength,
73+
control: {
74+
type: 'range',
75+
min: 0,
76+
max: 2,
77+
step: 0.1,
78+
},
6279
},
6380
{
6481
key: 'repulsivity',

‎website/src/pages/network/canvas.tsx

+6-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ResponsiveNetworkCanvas, canvasDefaultProps as defaults } from '@nivo/n
44
import { generateNetworkData } from '@nivo/generators'
55
import { ComponentTemplate } from '../../components/components/ComponentTemplate'
66
import meta from '../../data/components/network/meta.yml'
7-
import mapper from '../../data/components/network/mapper'
7+
import mapper, { dynamicLinkThicknessValue } from '../../data/components/network/mapper'
88
import { groups } from '../../data/components/network/props'
99

1010
const initialProperties = Object.freeze({
@@ -18,9 +18,10 @@ const initialProperties = Object.freeze({
1818
left: 0,
1919
},
2020

21-
linkDistance: 'distance',
22-
repulsivity: 4,
23-
iterations: 60,
21+
linkDistance: (link: any) => link.distance,
22+
centeringStrength: 0.3,
23+
repulsivity: 6,
24+
iterations: defaults.iterations,
2425

2526
nodeSize: defaults.nodeSize,
2627
activeNodeSize: defaults.activeNodeSize,
@@ -29,8 +30,8 @@ const initialProperties = Object.freeze({
2930
nodeBorderWidth: 1,
3031
nodeBorderColor: { theme: 'background' },
3132

33+
linkThickness: dynamicLinkThicknessValue,
3234
linkColor: defaults.linkColor,
33-
linkThickness: defaults.linkThickness,
3435

3536
annotations: defaults.annotations,
3637

‎website/src/pages/network/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ const initialProperties = Object.freeze({
2323
},
2424

2525
linkDistance: (link: Link) => link.distance,
26-
distanceMax: 50,
27-
repulsivity: defaults.repulsivity,
26+
centeringStrength: 0.3,
27+
repulsivity: 6,
2828
iterations: defaults.iterations,
2929

3030
nodeSize: dynamicNodeSizeValue,

0 commit comments

Comments
 (0)
Please sign in to comment.