如何更改 d3-force 中节点和链接的角度?
How to change angle of nodes and links in d3-force?
我是新手d3-force
,我发现线和节点的角度很难改变,you can run my code here。
无论如何,我的代码很简单:
const width = 800;
const height = 400;
const centerX = width / 2;
const centerY = height / 2;
const graph = ({
nodes: Array.from({length:8}, () => ({})),
links: [
{source: 1, target: 0},
{source: 2, target: 0},
{source: 3, target: 0},
{source: 4, target: 0},
{source: 5, target: 0},
{source: 6, target: 0},
{source: 7, target: 0},
]
});
const svg = d3.select("svg").attr("viewBox", [0, 0, width, height]),
link = svg
.selectAll(".link")
.data(graph.links)
.join("line"),
node = svg
.selectAll(".node")
.data(graph.nodes)
.join("g");
node.append("circle")
.attr("r", 12)
.attr("cursor", "move")
.attr("fill", "#ccc")
.attr("stroke", "#000")
.attr("stroke-width", "1.5px");
node.append("text").attr("dy", 25).text(function(d) {return d.index})
const simulation = d3
.forceSimulation()
.nodes(graph.nodes)
.force("link", d3.forceLink(graph.links).distance(100))
.force("charge", d3.forceManyBody().strength(-400))
.force("center", d3.forceCenter(width / 2, height / 2))
.stop();
for (let i = 0, n = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay())); i < n; ++i) {
simulation.tick();
}
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y)
.attr("stroke", "#000")
.attr("stroke-width", "1.5px")
node
.attr("transform", function (d) {return "translate(" + d.x + ", " + d.y + ")";});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg></svg>
它像这样生成 svg:
但我想要这个:
我挖了d3-force
here的源码,发现每次都加上Math.PI * (3 - Math.sqrt(5))
这是我想要的:
- 角度是多少? --> 所有节点平均分布,所以如果我们有
n
个节点,它将是Math.PI / (n-1)
- 订单怎么样? --> 保持顺序顺时针
- 第一个节点在哪里? --> 以
Math.PI / (n-1) / 2
开头
- children 的 children 呢? --> 遵循相同的规则,但是 parent link.
的一半长度
我不会使用 d3-force,而是使用一些基本的三角函数自己计算位置:
const width = 600;
const height = 400;
const centerX = width / 2;
const centerY = height / 2;
const graph = ({
nodes: d3.range(8).map(i => ({ id: i })),
links: [{
source: 0,
target: 1
},
{
source: 0,
target: 2
},
{
source: 0,
target: 3
},
{
source: 0,
target: 4
},
{
source: 1,
target: 5
},
{
source: 1,
target: 6
},
{
source: 1,
target: 7
},
]
});
graph.root = graph.nodes[0];
graph.nodes.forEach(n => {
n.children = [];
});
// Replace ID's with references to the nodes themselves
graph.links.forEach(l => {
l.source = graph.nodes.find(n => n.id === l.source);
l.target = graph.nodes.find(n => n.id === l.target);
// Register the target as a child of the source
l.source.children.push(l.target);
l.target.parent = l.source;
});
// Place the nodes
graph.nodes.forEach(n => {
if(n.parent === undefined) {
// root
n.x = centerX;
n.y = centerY;
n.level = 0;
return;
}
const parent = n.parent;
n.level = parent.level + 1;
const nSiblings = parent.children.length;
const ithSibling = parent.children.indexOf(n);
// Position the node
const angle = 2 * Math.PI / nSiblings; // in radians
const startAngle = - angle / 2;
const radius = 200 - 60 * n.level;
console.log(angle, startAngle);
n.x = parent.x + radius * Math.cos((ithSibling + 1) * angle + startAngle);
// Use a plus to keep the order clockwise, - for counterclockwise
n.y = parent.y + radius * Math.sin((ithSibling + 1) * angle + startAngle);
});
const svg = d3.select("svg").attr("viewBox", [0, 0, width, height]),
link = svg
.selectAll(".link")
.data(graph.links)
.join("line"),
node = svg
.selectAll(".node")
.data(graph.nodes)
.join("g");
node.append("circle")
.attr("r", 12)
.attr("cursor", "move")
.attr("fill", "#ccc")
.attr("stroke", "#000")
.attr("stroke-width", "1.5px");
node.append("text").attr("dy", 25).text(function(d) {
return d.id
});
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y)
.attr("stroke", "#000")
.attr("stroke-width", "1.5px")
node
.attr("transform", function(d) {
return "translate(" + d.x + ", " + d.y + ")";
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg></svg>
我是新手d3-force
,我发现线和节点的角度很难改变,you can run my code here。
无论如何,我的代码很简单:
const width = 800;
const height = 400;
const centerX = width / 2;
const centerY = height / 2;
const graph = ({
nodes: Array.from({length:8}, () => ({})),
links: [
{source: 1, target: 0},
{source: 2, target: 0},
{source: 3, target: 0},
{source: 4, target: 0},
{source: 5, target: 0},
{source: 6, target: 0},
{source: 7, target: 0},
]
});
const svg = d3.select("svg").attr("viewBox", [0, 0, width, height]),
link = svg
.selectAll(".link")
.data(graph.links)
.join("line"),
node = svg
.selectAll(".node")
.data(graph.nodes)
.join("g");
node.append("circle")
.attr("r", 12)
.attr("cursor", "move")
.attr("fill", "#ccc")
.attr("stroke", "#000")
.attr("stroke-width", "1.5px");
node.append("text").attr("dy", 25).text(function(d) {return d.index})
const simulation = d3
.forceSimulation()
.nodes(graph.nodes)
.force("link", d3.forceLink(graph.links).distance(100))
.force("charge", d3.forceManyBody().strength(-400))
.force("center", d3.forceCenter(width / 2, height / 2))
.stop();
for (let i = 0, n = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay())); i < n; ++i) {
simulation.tick();
}
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y)
.attr("stroke", "#000")
.attr("stroke-width", "1.5px")
node
.attr("transform", function (d) {return "translate(" + d.x + ", " + d.y + ")";});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg></svg>
但我想要这个:
我挖了d3-force
here的源码,发现每次都加上Math.PI * (3 - Math.sqrt(5))
这是我想要的:
- 角度是多少? --> 所有节点平均分布,所以如果我们有
n
个节点,它将是Math.PI / (n-1)
- 订单怎么样? --> 保持顺序顺时针
- 第一个节点在哪里? --> 以
Math.PI / (n-1) / 2
开头
- children 的 children 呢? --> 遵循相同的规则,但是 parent link. 的一半长度
我不会使用 d3-force,而是使用一些基本的三角函数自己计算位置:
const width = 600;
const height = 400;
const centerX = width / 2;
const centerY = height / 2;
const graph = ({
nodes: d3.range(8).map(i => ({ id: i })),
links: [{
source: 0,
target: 1
},
{
source: 0,
target: 2
},
{
source: 0,
target: 3
},
{
source: 0,
target: 4
},
{
source: 1,
target: 5
},
{
source: 1,
target: 6
},
{
source: 1,
target: 7
},
]
});
graph.root = graph.nodes[0];
graph.nodes.forEach(n => {
n.children = [];
});
// Replace ID's with references to the nodes themselves
graph.links.forEach(l => {
l.source = graph.nodes.find(n => n.id === l.source);
l.target = graph.nodes.find(n => n.id === l.target);
// Register the target as a child of the source
l.source.children.push(l.target);
l.target.parent = l.source;
});
// Place the nodes
graph.nodes.forEach(n => {
if(n.parent === undefined) {
// root
n.x = centerX;
n.y = centerY;
n.level = 0;
return;
}
const parent = n.parent;
n.level = parent.level + 1;
const nSiblings = parent.children.length;
const ithSibling = parent.children.indexOf(n);
// Position the node
const angle = 2 * Math.PI / nSiblings; // in radians
const startAngle = - angle / 2;
const radius = 200 - 60 * n.level;
console.log(angle, startAngle);
n.x = parent.x + radius * Math.cos((ithSibling + 1) * angle + startAngle);
// Use a plus to keep the order clockwise, - for counterclockwise
n.y = parent.y + radius * Math.sin((ithSibling + 1) * angle + startAngle);
});
const svg = d3.select("svg").attr("viewBox", [0, 0, width, height]),
link = svg
.selectAll(".link")
.data(graph.links)
.join("line"),
node = svg
.selectAll(".node")
.data(graph.nodes)
.join("g");
node.append("circle")
.attr("r", 12)
.attr("cursor", "move")
.attr("fill", "#ccc")
.attr("stroke", "#000")
.attr("stroke-width", "1.5px");
node.append("text").attr("dy", 25).text(function(d) {
return d.id
});
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y)
.attr("stroke", "#000")
.attr("stroke-width", "1.5px")
node
.attr("transform", function(d) {
return "translate(" + d.x + ", " + d.y + ")";
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg></svg>