D3网格中的力仿真 [英] D3 Force simulation in a grid
问题描述
我想知道如何修改迈克·波斯托克(Mike Bostock)的多力布局示例以便尝试获得力布局以将网格中的节点分组.
I was wondering how it would be possible to modify Mike Bostock's example of a multi-force layout in order to try and get the force layout to group nodes in a grid.
所以让我们想象一下,我们有以下csv:
So let us imagine that we have the following csv:
Name, Category1, Category2
1,1,1
2,1,2
3,1,1
4,2,2
5,3,1
6,1,4
7,5,5
8,1,5
9,2,4
10,3,3
11,4,4
12,4,5
13,3,4
14,1,2
15,1,1
16,2,2
17,3,1
18,2,1
19,4,5
20,3,1
对于他的数据,我希望将Category 1的所有可能值都作为列,而将Category 2的所有可能值都作为行,并且希望我的节点根据它们的值自动分组在适当的"单元格中用于类别1和类别2.
For his kind of data I would like to have all the possible values of Category 1 as columns and all the possible values of Category 2 as rows and would like my nodes to automatically group in the "proper" cell depending on their values for Category 1 and Category 2.
我刚刚开始使用D3,但真的不知道从哪里开始.我所指出的示例很有用,但是由于代码几乎没有注释,因此很难知道要修改什么.
I am just getting started with D3 and don't really know where to start. The example I pointed to is useful, but it's hard to know what to modify as the code has close to no comments.
任何帮助将不胜感激.
推荐答案
忘了这个例子:它使用D3 v3,这使节点的定位更加复杂.
Forget that example: it uses D3 v3, which makes positioning the nodes way more complicated.
在D3 v4/v5中,有两种便捷的方法, forceX 和 forceY .
In D3 v4/v5 there are two convenient methods, forceX and forceY.
您需要做的就是创建刻度,例如使用点刻度(我认为这是最好的选择):
All you need to do is creating your scales, for instance using a point scale (the best choice here in my opinion):
var columnScale = d3.scalePoint()
.domain(["1", "2", "3", "4", "5"])
.range([min, max]);
var rowScale = d3.scalePoint()
.domain(["1", "2", "3", "4", "5"])
.range([min, max]);
然后在仿真中使用这些比例尺:
And then use those scales in the simulation:
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) {
return columnScale(d.Category1)
}))
.force("y", d3.forceY(function(d) {
return rowScale(d.Category2)
}))
这是共享数据的基本演示(我使用色标突出显示网格上的不同位置):
Here is a basic demo with the data you shared (I'm using a colour scale to highlight the different positions on the grid):
var csv = `Name,Category1,Category2
1,1,1
2,1,2
3,1,1
4,2,2
5,3,1
6,1,4
7,5,5
8,1,5
9,2,4
10,3,3
11,4,4
12,4,5
13,3,4
14,1,2
15,1,1
16,2,2
17,3,1
18,2,1
19,4,5
20,3,1`;
var data = d3.csvParse(csv);
var w = 250,
h = 250;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var columnScale = d3.scalePoint()
.domain(dataRange(data, 'Category1')) // or ["1", "2", "3", "4", "5"]
.range([30, w - 10])
.padding(0.5);
var rowScale = d3.scalePoint()
.domain(dataRange(data, 'Category2')) // or ["1", "2", "3", "4", "5"]
.range([30, h - 10])
.padding(0.5);
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) {
return columnScale(d.Category1)
}))
.force("y", d3.forceY(function(d) {
return rowScale(d.Category2)
}))
.force("collide", d3.forceCollide(6))
var nodes = svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("r", 5)
.attr("fill", function(d) {
return color(d.Category1 + d.Category2)
});
var xAxis = d3.axisTop(columnScale)(svg.append("g").attr("transform", "translate(0,30)"));
var yAxis = d3.axisLeft(rowScale)(svg.append("g").attr("transform", "translate(30,0)"));
simulation.on("tick", function() {
nodes.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
});
function dataRange(records, field) {
var min = d3.min(records.map(record => parseInt(record[field], 10)));
var max = d3.max(records.map(record => parseInt(record[field], 10)));
return d3.range(min, max + 1);
};
svg {
background-color: floralwhite;
border: 1px solid gray;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
PS:在两种比例尺下,我都在域中使用字符串,因为 d3.csv
会将数据加载为字符串,而不是数字.根据需要进行更改.
PS: In both scales I'm using strings in the domain because d3.csv
will load your data as strings, not numbers. Change that according to your needs.
这篇关于D3网格中的力仿真的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!