D3 力模拟 - 使节点更接近相同角度的点
D3 force simulation - Bring nodes closer to a point in same angle
这个问题是基于当一个节点N1的大小改变并且我在节点上运行模拟时,N1周围的节点将以相同的角度移动但是我这里的问题是当 N1 的大小变回原始大小时,让节点更靠近 N1。我怎样才能做到这一点?
我在下面使用过,但节点没有更近
simulation.nodes(nodes);
simulation.restart();
for (var i = 0; i < 300; ++i) simulation.tick();
此外,如果我尝试使用力模拟,那么其他节点的位置就会完全改变,请在此处观看视频http://recordit.co/797i1E8ocT
d3.forceSimulation(nodes)
.force('x', d3.forceX(plot.x))
.force('y', d3.forceY(plot.y))
提前致谢。
节点没有靠近,因为您的模拟 "speed",称为 alpha
值,已耗尽(衰减)。只需更换
simulation.restart();
你不需要
simulation.alpha(1);
节点将正常运行。
但是,正如您所注意到的,在几次扩展和折叠之后,节点可能会从其初始位置显着移动。这个问题可以通过多种方式解决。您可以使用某种确定性算法来计算节点位置(例如树布局),但在展开和折叠节点时很难实现平滑过渡。另一种方法是使用额外的力 "pin" 节点到它们的初始位置,这将每个节点吸引到它的第一个计算位置。
要实现它,您可以在模拟初始化及其第一个 运行:
之后保存初始位置
for (let d of data.children) {
d.initialX = d.x;
d.initialY = d.y;
}
然后用将每个节点吸引到其初始位置的力替换x
和y
力:
simulation
.force("x", d3.forceX(d => d.initialX).strength(0.2))
.force("y", d3.forceY(d => d.initialY).strength(0.2));
强度决定了碰撞力和钉扎力之间的平衡。强度越大,节点将越积极地尝试占据其初始位置。
也可能需要使用点吸引而不是 x
和 y
的总和 — 看看 d3-force-attract 包。
以下代码段说明了所描述的方法。
var w = 650,
h = 650;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var color = d3.scaleOrdinal(d3.schemeCategory10)
var data = {
name: "root",
children: [{
label: 'RED1',
size: 20,
color: 'red'
}, {
label: 'RAD2',
size: 20,
color: '#c99700'
}, {
label: 'BIL3',
size: 20,
color: 'blue'
}, {
label: 'EEN4',
size: 10,
color: '#007377'
}, {
label: 'INO5',
size: 40,
color: '#b4975a'
}, {
label: 'RAD6',
size: 40,
color: '#c99700'
}, {
label: 'BIL7',
size: 30,
color: '#008ce6'
}, {
label: 'INO8',
size: 30,
color: '#b4975a'
}, {
label: 'INO9',
size: 40,
color: '#b4975a'
}, {
label: 'RAD10',
size: 40,
color: '#c99700'
}, {
label: 'BIL11',
size: 30,
color: '#008ce6'
}, {
label: 'INO12',
size: 30,
color: '#b4975a'
}]
};
var render = function() {
var simulation = d3.forceSimulation(data.children)
.force("x", d3.forceX(w / 2))
.force("y", d3.forceY(h / 2))
.force("collide", d3.forceCollide(function(d) {
return d.size + 20
}))
.stop();
for (var i = 0; i < 100; ++i) simulation.tick();
for (let d of data.children) {
d.initialX = d.x;
d.initialY = d.y;
}
simulation
.force("x", d3.forceX(d => d.initialX).strength(0.2))
.force("y", d3.forceY(d => d.initialY).strength(0.2));
let nodeLevel1 = svg.selectAll('circle')
.data(data.children, (d) => {
// Attaching key for uniqueness
return d.label;
});
nodeLevel1.exit().remove();
let nodeLevel1Enter = nodeLevel1
.enter()
.append("circle")
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.attr("r", function(d) {
return d.size
})
.style("fill", function(d) {
return d.color;
})
nodeLevel1Enter = nodeLevel1Enter
.merge(nodeLevel1)
nodeLevel1Enter
.transition()
.duration(1600)
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.attr("r", function(d) {
return d.size
})
.style("fill", function(d) {
return d.color;
})
d3.select('#updatesize').on('click', function() {
add();
})
d3.select('#updatebluesize').on('click', function() {
addblue();
})
d3.select('#resetsize').on('click', function() {
reset();
})
d3.select('#resetall').on('click', function() {
resetall();
})
var add = function() {
data.children[0].size = 140;
move();
}
var addblue = function() {
data.children[2].size = 100;
move();
}
var reset = function() {
data.children[0].size = 20;
move();
}
var resetall = function() {
data.children[0].size = 20;
data.children[2].size = 20;
move();
}
function move() {
simulation.nodes(data.children);
simulation.alpha(1);
for (var i = 0; i < 300; ++i) simulation.tick();
nodeLevel1Enter
.transition()
.duration(1600)
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.attr("r", function(d) {
return d.size
});
}
}
render();
<script src="https://d3js.org/d3.v4.min.js"></script>
<a href='javascript:;' id='updatesize'>Update red resource size</a> |
<a href='javascript:;' id='updatebluesize'>Update blue resource size</a> |
<a href='javascript:;' id='resetsize'>Reset red resource size</a> |
<a href='javascript:;' id='resetall'>Reset all</a>
这个问题是基于
我在下面使用过,但节点没有更近
simulation.nodes(nodes);
simulation.restart();
for (var i = 0; i < 300; ++i) simulation.tick();
此外,如果我尝试使用力模拟,那么其他节点的位置就会完全改变,请在此处观看视频http://recordit.co/797i1E8ocT
d3.forceSimulation(nodes)
.force('x', d3.forceX(plot.x))
.force('y', d3.forceY(plot.y))
提前致谢。
节点没有靠近,因为您的模拟 "speed",称为 alpha
值,已耗尽(衰减)。只需更换
simulation.restart();
你不需要
simulation.alpha(1);
节点将正常运行。
但是,正如您所注意到的,在几次扩展和折叠之后,节点可能会从其初始位置显着移动。这个问题可以通过多种方式解决。您可以使用某种确定性算法来计算节点位置(例如树布局),但在展开和折叠节点时很难实现平滑过渡。另一种方法是使用额外的力 "pin" 节点到它们的初始位置,这将每个节点吸引到它的第一个计算位置。
要实现它,您可以在模拟初始化及其第一个 运行:
之后保存初始位置for (let d of data.children) {
d.initialX = d.x;
d.initialY = d.y;
}
然后用将每个节点吸引到其初始位置的力替换x
和y
力:
simulation
.force("x", d3.forceX(d => d.initialX).strength(0.2))
.force("y", d3.forceY(d => d.initialY).strength(0.2));
强度决定了碰撞力和钉扎力之间的平衡。强度越大,节点将越积极地尝试占据其初始位置。
也可能需要使用点吸引而不是 x
和 y
的总和 — 看看 d3-force-attract 包。
以下代码段说明了所描述的方法。
var w = 650,
h = 650;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var color = d3.scaleOrdinal(d3.schemeCategory10)
var data = {
name: "root",
children: [{
label: 'RED1',
size: 20,
color: 'red'
}, {
label: 'RAD2',
size: 20,
color: '#c99700'
}, {
label: 'BIL3',
size: 20,
color: 'blue'
}, {
label: 'EEN4',
size: 10,
color: '#007377'
}, {
label: 'INO5',
size: 40,
color: '#b4975a'
}, {
label: 'RAD6',
size: 40,
color: '#c99700'
}, {
label: 'BIL7',
size: 30,
color: '#008ce6'
}, {
label: 'INO8',
size: 30,
color: '#b4975a'
}, {
label: 'INO9',
size: 40,
color: '#b4975a'
}, {
label: 'RAD10',
size: 40,
color: '#c99700'
}, {
label: 'BIL11',
size: 30,
color: '#008ce6'
}, {
label: 'INO12',
size: 30,
color: '#b4975a'
}]
};
var render = function() {
var simulation = d3.forceSimulation(data.children)
.force("x", d3.forceX(w / 2))
.force("y", d3.forceY(h / 2))
.force("collide", d3.forceCollide(function(d) {
return d.size + 20
}))
.stop();
for (var i = 0; i < 100; ++i) simulation.tick();
for (let d of data.children) {
d.initialX = d.x;
d.initialY = d.y;
}
simulation
.force("x", d3.forceX(d => d.initialX).strength(0.2))
.force("y", d3.forceY(d => d.initialY).strength(0.2));
let nodeLevel1 = svg.selectAll('circle')
.data(data.children, (d) => {
// Attaching key for uniqueness
return d.label;
});
nodeLevel1.exit().remove();
let nodeLevel1Enter = nodeLevel1
.enter()
.append("circle")
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.attr("r", function(d) {
return d.size
})
.style("fill", function(d) {
return d.color;
})
nodeLevel1Enter = nodeLevel1Enter
.merge(nodeLevel1)
nodeLevel1Enter
.transition()
.duration(1600)
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.attr("r", function(d) {
return d.size
})
.style("fill", function(d) {
return d.color;
})
d3.select('#updatesize').on('click', function() {
add();
})
d3.select('#updatebluesize').on('click', function() {
addblue();
})
d3.select('#resetsize').on('click', function() {
reset();
})
d3.select('#resetall').on('click', function() {
resetall();
})
var add = function() {
data.children[0].size = 140;
move();
}
var addblue = function() {
data.children[2].size = 100;
move();
}
var reset = function() {
data.children[0].size = 20;
move();
}
var resetall = function() {
data.children[0].size = 20;
data.children[2].size = 20;
move();
}
function move() {
simulation.nodes(data.children);
simulation.alpha(1);
for (var i = 0; i < 300; ++i) simulation.tick();
nodeLevel1Enter
.transition()
.duration(1600)
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.attr("r", function(d) {
return d.size
});
}
}
render();
<script src="https://d3js.org/d3.v4.min.js"></script>
<a href='javascript:;' id='updatesize'>Update red resource size</a> |
<a href='javascript:;' id='updatebluesize'>Update blue resource size</a> |
<a href='javascript:;' id='resetsize'>Reset red resource size</a> |
<a href='javascript:;' id='resetall'>Reset all</a>