Maybe this isn't the best way to do this, but now that I've gone down the rabbit hole, I want to know how it works. I'm trying to use d3 to create a tree where the nodes will settle downwards, much like there is gravity. I'm hoping that this, along with the charge that each node has and the tension in the rope will make it so that it will end up looking as I imagine, with it balancing itself out. I'm trying to simulate this constant downward acceleration in d3, but I'm a beginner at d3, and don't know exactly how. I'm also running into the problem that the simulation stops running, and d3.timer(force.resume) doesn't help.
也许这不是最好的方法,但是现在我已经在兔子洞里了,我想知道它是如何工作的。我试着用d3来创建树,节点会向下沉降,就像重力一样。我希望这个,以及每个节点的电荷和绳子的拉力会使它最终看起来像我想象的那样,它平衡了自己。我试着在d3中模拟这个持续向下的加速度,但是我是d3的初学者,不知道具体怎么做。我也遇到了模拟停止运行的问题,d3.timer(force.resume)没有帮助。
So essentially, I want to have a root node fixed at a certain position like a pivot, with child nodes coming off of it and gravity, charges, and tension being present so that the children settle automatically and balance themselves into a tree structure.
所以本质上,我希望有一个根结点固定在某个位置,像一个枢轴,它的子节点从它和重力,电荷,和张力的存在中分离出来,这样孩子们就会自动地平衡,并平衡到树状结构中。
2 个解决方案
#1
17
Force directed simulations are a great way to layout a tree, a diagram, or a graph. This method is used in many open source and commercial libraries and applications, so D3 is just one of many. Also, this topic constantly attracts attention of applied mathematicians.
强制模拟是布局树、图或图形的好方法。这种方法在许多开放源码和商业类库和应用程序中都有使用,所以D3只是其中之一。同样,这个话题也经常引起应用数学家的注意。
I just want to tell you this is a huge topic. If one limits oneself just to D3, there are still a number of ways to do what you described in your question. Since this answer has to have a reasonable length, I'll try to show only some highlights to you, by walking you through 3 D3 examples. I'll start with a simple example of force directed tree layout (that is not really what you want, bit it is simple), and each example will build on previous, and will be closer to what you want to achieve.
我只想告诉你这是一个很大的话题。如果一个人局限于D3,那么还有很多方法可以做你在问题中描述的事情。由于这个答案必须有一个合理的长度,我将试着向您展示一些亮点,通过3个D3示例。我将从一个简单的force定向树布局示例开始(这不是您真正想要的,它是简单的),并且每个示例将在前面构建,并且将更接近您想要实现的目标。
Example 1
链接到jsfiddle
This is a basic example of a tree laid out in radial way by D3 force layout. You really need to understand ALL code that is used. I'll just mention here some highlights:
这是由D3力布局的径向排列的树的一个基本的例子。您确实需要理解所使用的所有代码。这里我要强调一下
- function getData() returns a test tree (which is a convenient subset of so called "flare" test tree);
- 函数getData()返回一个测试树(它是所谓的“耀斑”测试树的一个方便子集);
- function
flatten()
converts tree object to an array, this is format needed to initialize D3 force layout; - 函数flatten()将树对象转换为数组,这是初始化D3力布局所需要的格式;
- positions of all nodes are initialized (otherwise D3 force layout will behave a little strange and unpredictable);
- 所有节点的位置都被初始化(否则D3的强制布局会表现得有点奇怪和不可预测);
- root node position is initialized to the center of the diagram, and root node is declared fixed;
- 根节点位置初始化为图的中心,并声明根节点;
- function "ontick" is provided, and it just draws links and nodes exactly as they behave during the simulation;
- 功能“ontick”提供,它只在模拟过程中绘制链接和节点;
- D3 force layout algorithm is initialized with D3 gravity 0.2 and D3 charge -200;
- D3力布局算法初始化D3重力0.2和D3电荷-200;
- D3 force layout algorithm is started by calling its method start().
- D3力布局算法是通过调用它的方法start()开始的。
Let me know if you have additional question related to this example. I know the code looks a little weird for people without D3 experience - but, in reality, its very simple, and easy to understand, once you get D3 force layout to know. You can play with different D3 gravity and D3 charge, to understand better why specific values 0.2 and -200 are chosen.
如果你有关于这个例子的其他问题,请告诉我。我知道对于没有D3经验的人来说,代码看起来有点怪异——但是,在现实中,它非常简单,并且很容易理解,一旦你得到D3的力量布局就知道了。你可以玩不同的D3重力和D3电荷,以便更好地理解为什么选择特定值0.2和-200。
This, of course, is not what you want, but it can serve as a "canonical example" for us.
当然,这不是你想要的,但它可以作为我们的“典范”。
Example 2
链接到jsfiddle
Codewise, this example differs from previus just in two things:
Codewise,这个例子与previus的不同之处在于两件事:
- Root node position is initialized at (width/2, 100) instead of (width/2, height/2) - this is natural position of the root for "hanging" layout"
- 根节点位置初始化(宽度/ 2,100)而不是(宽度/2,高度/2)-这是“挂”布局的根的自然位置。
- A custom force is defined in "ontick" function in following lines:
- 自定义力在“ontick”函数中定义如下:
var ky = e.alpha;
肯塔基州var = e.alpha;
links.forEach(function(d, i) {
链接。forEach(函数(d i){
d.target.y += (d.target.depth * 100 - d.target.y) * 5 * ky;
d.target。y += (d.target.depth * 100 - d.target.y) * 5 * ky;
});
});
This custom force will attract the nodes of the same depth to the same horizontal line. This is enough to produce the diagram in the picture.
这种自定义力将吸引相同深度的节点到相同的水平线。这就足够产生图中的图了。
However, you can notice somwhat unnatural arangements of certain nodes in the diagram - generally, they don't look like precisely as if they are "hanging" off of parent. Next example will be an attempt to fix that.
但是,您可以注意到图中某些节点的非自然的arangements——通常情况下,它们看起来不像“挂”在父节点上。下一个例子是试图解决这个问题。
Example 3
链接到jsfiddle
This looks more natural than previous example.
这看起来比以前的例子更自然。
New in this example is just one more custom force: one that "centers" all parents to the middle of horizontal positions of their children. You can easily find the code responsible for that in the "ontick()" function.
在这个例子中,新出现的是一个更自定义的力量:“把所有的父母都集中在他们孩子的水平位置上”。您可以很容易地在“ontick()”函数中找到对此负责的代码。
This looks much closer to what what you want. I am not claiming this is an ideal layout for your needs. However, it can serve you as a good starting point. Once you understand all these examples, you will be able to modify them, and, if you want, to create different simulations, based on different custom forces.
这看起来更接近你想要的。我并不是说这是您需要的理想布局。但是,它可以为你提供一个好的起点。一旦您理解了所有这些示例,您就能够修改它们,并且,如果您愿意,可以根据不同的自定义力创建不同的模拟。
So, the last example is not simulation based on physical gravity, but it has effects similar to physical gravity. If you want to simulate physical gravity, this will require a little bit more of code - but do you really need it, if the effect is almost the same?
最后一个例子不是基于物理引力的模拟,而是类似于物理引力。如果你想模拟物理引力,这需要更多的代码——但如果效果几乎相同,你真的需要它吗?
Hope this helps. Let me know if you have a question, need a clarification, etc.
希望这个有帮助。如果你有问题,请告诉我,需要澄清。
#2
1
The easiest way to do this would be to simply tweak the "size" if the layout so that the center for the existing "gravity" force is at the bottom of the display, while the root node is fixed at the top of the display. However, you will probably have to do a lot of tweaking of parameters to get things looking nicely.
最简单的方法是简单地调整布局,使现有的“重力”力的中心在显示器的底部,而根节点则固定在显示器的顶部。然而,您可能需要做大量的参数调整以使事情看起来更好。
Example here: http://jsfiddle.net/cSn6w/13/
例子:http://jsfiddle.net/cSn6w/13/
var force = d3.layout.force()
.on("tick", tick)
.charge(-30)
.linkDistance(10)
.gravity(0.005)
.size([w, 2*h]);
Another option would be to add a "custom force", similar to what is used in the Multi-foci force layout example. The specifics of the calculation used in that demo aren't relevant, but the idea is simply that, at the beginning of each tick()
function, every data point is adjusted slightly in a certain direction. If that direction throws off any of the other constraints, it will be counter-acted in the next tick by the internal calculations of the layout.
另一种选择是添加一个“自定义的力量”,类似于多焦点部队布局的例子。共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识如果这个方向抛出任何其他约束,它将在下一个滴答被内部计算的布局。
Example here: http://jsfiddle.net/cSn6w/14/
例子:http://jsfiddle.net/cSn6w/14/
var g = 0.0005;
function gravityFunction(d,i) {
if (d.fixed) return;
//used to modify each data object's position
//at the start of every tick
var distToGround = h-d.y;
if (distToGround <= 0) d.y = h; //make "ground" solid
else d.y += g * (Math.min(distToGround, h/4) );
}
function tick() {
nodeCircles.each(gravityFunction)
//adjust each node according to the custom force
.attr("cx", function (d) {
return d.x;
})
/* etc. */
Getting the gravity function optimized for a nice layout will be the tricky part. Based on my experiments, you will probably want to keep track of the depth of each node in the tree, and have either the gravity function or the link strength correlate with depth, so that you don't end up stretching out the "trunk" of your tree too much.
为一个漂亮的布局优化重力功能将是一个棘手的部分。根据我的实验,你可能会想要跟踪树中每个节点的深度,并且有重力函数或链接强度与深度相关,这样你就不会过度地把树的“树干”拉长。
#1
17
Force directed simulations are a great way to layout a tree, a diagram, or a graph. This method is used in many open source and commercial libraries and applications, so D3 is just one of many. Also, this topic constantly attracts attention of applied mathematicians.
强制模拟是布局树、图或图形的好方法。这种方法在许多开放源码和商业类库和应用程序中都有使用,所以D3只是其中之一。同样,这个话题也经常引起应用数学家的注意。
I just want to tell you this is a huge topic. If one limits oneself just to D3, there are still a number of ways to do what you described in your question. Since this answer has to have a reasonable length, I'll try to show only some highlights to you, by walking you through 3 D3 examples. I'll start with a simple example of force directed tree layout (that is not really what you want, bit it is simple), and each example will build on previous, and will be closer to what you want to achieve.
我只想告诉你这是一个很大的话题。如果一个人局限于D3,那么还有很多方法可以做你在问题中描述的事情。由于这个答案必须有一个合理的长度,我将试着向您展示一些亮点,通过3个D3示例。我将从一个简单的force定向树布局示例开始(这不是您真正想要的,它是简单的),并且每个示例将在前面构建,并且将更接近您想要实现的目标。
Example 1
链接到jsfiddle
This is a basic example of a tree laid out in radial way by D3 force layout. You really need to understand ALL code that is used. I'll just mention here some highlights:
这是由D3力布局的径向排列的树的一个基本的例子。您确实需要理解所使用的所有代码。这里我要强调一下
- function getData() returns a test tree (which is a convenient subset of so called "flare" test tree);
- 函数getData()返回一个测试树(它是所谓的“耀斑”测试树的一个方便子集);
- function
flatten()
converts tree object to an array, this is format needed to initialize D3 force layout; - 函数flatten()将树对象转换为数组,这是初始化D3力布局所需要的格式;
- positions of all nodes are initialized (otherwise D3 force layout will behave a little strange and unpredictable);
- 所有节点的位置都被初始化(否则D3的强制布局会表现得有点奇怪和不可预测);
- root node position is initialized to the center of the diagram, and root node is declared fixed;
- 根节点位置初始化为图的中心,并声明根节点;
- function "ontick" is provided, and it just draws links and nodes exactly as they behave during the simulation;
- 功能“ontick”提供,它只在模拟过程中绘制链接和节点;
- D3 force layout algorithm is initialized with D3 gravity 0.2 and D3 charge -200;
- D3力布局算法初始化D3重力0.2和D3电荷-200;
- D3 force layout algorithm is started by calling its method start().
- D3力布局算法是通过调用它的方法start()开始的。
Let me know if you have additional question related to this example. I know the code looks a little weird for people without D3 experience - but, in reality, its very simple, and easy to understand, once you get D3 force layout to know. You can play with different D3 gravity and D3 charge, to understand better why specific values 0.2 and -200 are chosen.
如果你有关于这个例子的其他问题,请告诉我。我知道对于没有D3经验的人来说,代码看起来有点怪异——但是,在现实中,它非常简单,并且很容易理解,一旦你得到D3的力量布局就知道了。你可以玩不同的D3重力和D3电荷,以便更好地理解为什么选择特定值0.2和-200。
This, of course, is not what you want, but it can serve as a "canonical example" for us.
当然,这不是你想要的,但它可以作为我们的“典范”。
Example 2
链接到jsfiddle
Codewise, this example differs from previus just in two things:
Codewise,这个例子与previus的不同之处在于两件事:
- Root node position is initialized at (width/2, 100) instead of (width/2, height/2) - this is natural position of the root for "hanging" layout"
- 根节点位置初始化(宽度/ 2,100)而不是(宽度/2,高度/2)-这是“挂”布局的根的自然位置。
- A custom force is defined in "ontick" function in following lines:
- 自定义力在“ontick”函数中定义如下:
var ky = e.alpha;
肯塔基州var = e.alpha;
links.forEach(function(d, i) {
链接。forEach(函数(d i){
d.target.y += (d.target.depth * 100 - d.target.y) * 5 * ky;
d.target。y += (d.target.depth * 100 - d.target.y) * 5 * ky;
});
});
This custom force will attract the nodes of the same depth to the same horizontal line. This is enough to produce the diagram in the picture.
这种自定义力将吸引相同深度的节点到相同的水平线。这就足够产生图中的图了。
However, you can notice somwhat unnatural arangements of certain nodes in the diagram - generally, they don't look like precisely as if they are "hanging" off of parent. Next example will be an attempt to fix that.
但是,您可以注意到图中某些节点的非自然的arangements——通常情况下,它们看起来不像“挂”在父节点上。下一个例子是试图解决这个问题。
Example 3
链接到jsfiddle
This looks more natural than previous example.
这看起来比以前的例子更自然。
New in this example is just one more custom force: one that "centers" all parents to the middle of horizontal positions of their children. You can easily find the code responsible for that in the "ontick()" function.
在这个例子中,新出现的是一个更自定义的力量:“把所有的父母都集中在他们孩子的水平位置上”。您可以很容易地在“ontick()”函数中找到对此负责的代码。
This looks much closer to what what you want. I am not claiming this is an ideal layout for your needs. However, it can serve you as a good starting point. Once you understand all these examples, you will be able to modify them, and, if you want, to create different simulations, based on different custom forces.
这看起来更接近你想要的。我并不是说这是您需要的理想布局。但是,它可以为你提供一个好的起点。一旦您理解了所有这些示例,您就能够修改它们,并且,如果您愿意,可以根据不同的自定义力创建不同的模拟。
So, the last example is not simulation based on physical gravity, but it has effects similar to physical gravity. If you want to simulate physical gravity, this will require a little bit more of code - but do you really need it, if the effect is almost the same?
最后一个例子不是基于物理引力的模拟,而是类似于物理引力。如果你想模拟物理引力,这需要更多的代码——但如果效果几乎相同,你真的需要它吗?
Hope this helps. Let me know if you have a question, need a clarification, etc.
希望这个有帮助。如果你有问题,请告诉我,需要澄清。
#2
1
The easiest way to do this would be to simply tweak the "size" if the layout so that the center for the existing "gravity" force is at the bottom of the display, while the root node is fixed at the top of the display. However, you will probably have to do a lot of tweaking of parameters to get things looking nicely.
最简单的方法是简单地调整布局,使现有的“重力”力的中心在显示器的底部,而根节点则固定在显示器的顶部。然而,您可能需要做大量的参数调整以使事情看起来更好。
Example here: http://jsfiddle.net/cSn6w/13/
例子:http://jsfiddle.net/cSn6w/13/
var force = d3.layout.force()
.on("tick", tick)
.charge(-30)
.linkDistance(10)
.gravity(0.005)
.size([w, 2*h]);
Another option would be to add a "custom force", similar to what is used in the Multi-foci force layout example. The specifics of the calculation used in that demo aren't relevant, but the idea is simply that, at the beginning of each tick()
function, every data point is adjusted slightly in a certain direction. If that direction throws off any of the other constraints, it will be counter-acted in the next tick by the internal calculations of the layout.
另一种选择是添加一个“自定义的力量”,类似于多焦点部队布局的例子。共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识共识如果这个方向抛出任何其他约束,它将在下一个滴答被内部计算的布局。
Example here: http://jsfiddle.net/cSn6w/14/
例子:http://jsfiddle.net/cSn6w/14/
var g = 0.0005;
function gravityFunction(d,i) {
if (d.fixed) return;
//used to modify each data object's position
//at the start of every tick
var distToGround = h-d.y;
if (distToGround <= 0) d.y = h; //make "ground" solid
else d.y += g * (Math.min(distToGround, h/4) );
}
function tick() {
nodeCircles.each(gravityFunction)
//adjust each node according to the custom force
.attr("cx", function (d) {
return d.x;
})
/* etc. */
Getting the gravity function optimized for a nice layout will be the tricky part. Based on my experiments, you will probably want to keep track of the depth of each node in the tree, and have either the gravity function or the link strength correlate with depth, so that you don't end up stretching out the "trunk" of your tree too much.
为一个漂亮的布局优化重力功能将是一个棘手的部分。根据我的实验,你可能会想要跟踪树中每个节点的深度,并且有重力函数或链接强度与深度相关,这样你就不会过度地把树的“树干”拉长。