具有特定目标目的地的 D3js 强制模拟
D3js force simulation with specific targeted destination
我正在尝试渲染 d3js 力模拟,但我想确保我的节点不会传递虚假信息。
使用以下代码显示节点,但由于力布局的动态特性,它有时会将一些节点推出其适当的 x 坐标位置。
inOrder(){
this.simulation
.force("x", d3.forceX(d => this.xScale(d.value)))
.force("y", d3.forceY(this.height / 2))
.alpha(1).restart();
},
这是发生这种情况的一个令人震惊的例子:
数字应按从左到右的顺序排列。
我尝试在节点上使用 fx 属性 来锁定位置:
inOrder(){
this.releases.forEach(x => {
x.fx = this.xScale(x.value)
})
this.simulation
.force("x", d3.forceX(d => this.xScale(d.value)))
.force("y", d3.forceY(this.height / 2))
.alpha(1).restart();
},
这是为了保留 x 位置而设计的,但是当调用 inOrder
方法时,节点会立即跳转到它们最终的 x 位置。这破坏了力模拟的流体和动态特性。
有没有办法两全其美?也许通过使用 .on("end", () => {})
或 .on("tick", () => {})
?事件处理程序?
Mike Bostock (https://whosebug.com/users/365814/mbostock) 和 Shan Carter 创作的一些作品为我在这里尝试做的事情提供了灵感:
在“更改”和“部门总计”选项卡之间单击
https://archive.nytimes.com/www.nytimes.com/interactive/2012/02/13/us/politics/2013-budget-proposal-graphic.html?hp
在“整体情况”和“按行业查看”选项卡之间单击
https://archive.nytimes.com/www.nytimes.com/interactive/2013/05/25/sunday-review/corporate-taxes.html
我可能在这里遗漏了一些东西,但是调整 x 定位力(和 y)的强度可以帮助确保您的排序正确完成。 forceX或forceY的默认强度为0.1,强度实现如下:
a value of 0.1 indicates that the node should move a tenth of the way from its current x-position to the target x-position with each application. Higher values moves nodes more quickly to the target position, often at the expense of other forces or constraints. A value outside the range [0,1] is not recommended. (docs)
所以我们可以增加 forceX 强度,并且为了允许节点在 x 轴上更自由地移动,我们可以减少 forceY - 允许节点更轻松地相互跳跃 - 降低碰撞强度也可能有所帮助。
我没有标记下面的圆圈(相反,它们是按顺序着色的),但我 运行 检查它们是否有序(在模拟结束时记录到控制台),下面片段仅修改 x 和 y 力(不是碰撞力):
var height = 300;
var width = 500;
var data = d3.range(30).map(function(d,i) {
return { size: Math.random()+1, index: i}
});
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
var x = d3.scaleLinear()
.domain([1,30])
.range([50,width-50]);
var color = d3.scaleLinear()
.domain([0,29])
.range(["#ccc","#000"])
var simulation = d3.forceSimulation()
.force("x", d3.forceX(d => x(d.index)).strength(0.20))
.force("y", d3.forceY(height / 2).strength(0.05))
.force("collide", d3.forceCollide().radius(d=> d.size*10))
.alpha(1).restart();
var circles = svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("r", function(d) { return d.size * 10; })
.attr("fill", function(d,i) { return color(i); })
simulation.nodes(data)
.on("tick",tick)
.on("end",verify);
function tick() {
circles
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
}
function verify() {
var n = 0;
for(var i = 0; i < data.length - 1; i++) {
if(data[i].x > data[i+1].x) n++;
}
console.log(n + " out of place");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
该代码片段在 500x300 的区域中放置了 30 个圆圈,没有太大问题:我已经测试了几次,其中 0 个不合适。在这里放置 100 个圆圈会导致问题:这些圆圈将无法在如此狭窄的区域中改变位置:可能需要进一步修改力,但更大尺寸的图可能更受欢迎(与小片段视图相反)也是。
另一种选择是在整个模拟成熟过程中修改力:从强大的 x 力强度和低碰撞力强度开始,然后慢慢增加碰撞力,以便将随后的推挤降至最低。这是在 tick 函数中修改力的 example - 虽然,这个例子是针对 link 长度而不是在 x 上的位置 - 但适应应该不会太难。
另一种可能性是保持 alpha 高,直到所有圆圈在 x 轴上正确排序,然后开始冷却,同样这必须在 tick 函数中发生。
我正在尝试渲染 d3js 力模拟,但我想确保我的节点不会传递虚假信息。
使用以下代码显示节点,但由于力布局的动态特性,它有时会将一些节点推出其适当的 x 坐标位置。
inOrder(){
this.simulation
.force("x", d3.forceX(d => this.xScale(d.value)))
.force("y", d3.forceY(this.height / 2))
.alpha(1).restart();
},
这是发生这种情况的一个令人震惊的例子:
数字应按从左到右的顺序排列。
我尝试在节点上使用 fx 属性 来锁定位置:
inOrder(){
this.releases.forEach(x => {
x.fx = this.xScale(x.value)
})
this.simulation
.force("x", d3.forceX(d => this.xScale(d.value)))
.force("y", d3.forceY(this.height / 2))
.alpha(1).restart();
},
这是为了保留 x 位置而设计的,但是当调用 inOrder
方法时,节点会立即跳转到它们最终的 x 位置。这破坏了力模拟的流体和动态特性。
有没有办法两全其美?也许通过使用 .on("end", () => {})
或 .on("tick", () => {})
?事件处理程序?
Mike Bostock (https://whosebug.com/users/365814/mbostock) 和 Shan Carter 创作的一些作品为我在这里尝试做的事情提供了灵感:
在“更改”和“部门总计”选项卡之间单击 https://archive.nytimes.com/www.nytimes.com/interactive/2012/02/13/us/politics/2013-budget-proposal-graphic.html?hp
在“整体情况”和“按行业查看”选项卡之间单击 https://archive.nytimes.com/www.nytimes.com/interactive/2013/05/25/sunday-review/corporate-taxes.html
我可能在这里遗漏了一些东西,但是调整 x 定位力(和 y)的强度可以帮助确保您的排序正确完成。 forceX或forceY的默认强度为0.1,强度实现如下:
a value of 0.1 indicates that the node should move a tenth of the way from its current x-position to the target x-position with each application. Higher values moves nodes more quickly to the target position, often at the expense of other forces or constraints. A value outside the range [0,1] is not recommended. (docs)
所以我们可以增加 forceX 强度,并且为了允许节点在 x 轴上更自由地移动,我们可以减少 forceY - 允许节点更轻松地相互跳跃 - 降低碰撞强度也可能有所帮助。
我没有标记下面的圆圈(相反,它们是按顺序着色的),但我 运行 检查它们是否有序(在模拟结束时记录到控制台),下面片段仅修改 x 和 y 力(不是碰撞力):
var height = 300;
var width = 500;
var data = d3.range(30).map(function(d,i) {
return { size: Math.random()+1, index: i}
});
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
var x = d3.scaleLinear()
.domain([1,30])
.range([50,width-50]);
var color = d3.scaleLinear()
.domain([0,29])
.range(["#ccc","#000"])
var simulation = d3.forceSimulation()
.force("x", d3.forceX(d => x(d.index)).strength(0.20))
.force("y", d3.forceY(height / 2).strength(0.05))
.force("collide", d3.forceCollide().radius(d=> d.size*10))
.alpha(1).restart();
var circles = svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("r", function(d) { return d.size * 10; })
.attr("fill", function(d,i) { return color(i); })
simulation.nodes(data)
.on("tick",tick)
.on("end",verify);
function tick() {
circles
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
}
function verify() {
var n = 0;
for(var i = 0; i < data.length - 1; i++) {
if(data[i].x > data[i+1].x) n++;
}
console.log(n + " out of place");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
该代码片段在 500x300 的区域中放置了 30 个圆圈,没有太大问题:我已经测试了几次,其中 0 个不合适。在这里放置 100 个圆圈会导致问题:这些圆圈将无法在如此狭窄的区域中改变位置:可能需要进一步修改力,但更大尺寸的图可能更受欢迎(与小片段视图相反)也是。
另一种选择是在整个模拟成熟过程中修改力:从强大的 x 力强度和低碰撞力强度开始,然后慢慢增加碰撞力,以便将随后的推挤降至最低。这是在 tick 函数中修改力的 example - 虽然,这个例子是针对 link 长度而不是在 x 上的位置 - 但适应应该不会太难。
另一种可能性是保持 alpha 高,直到所有圆圈在 x 轴上正确排序,然后开始冷却,同样这必须在 tick 函数中发生。