Transcript from the "Force Layout" Lesson
>> And so finally, what I want to talk to you about is D3 force. So the D3 force layout is a slight bit different from what we had talked through just now with the shapes and hierarchies in that it's, one, it directly mutates the original data array. And you'll see in a little bit why.
[00:00:28] And also, that it requires upwards of thousands of calculations to arrive at its final position, instead of just that one single calculation that we saw with the shape in hierarchy. But it's probably one of my most favorite layout modules, my favorite layout tool, because of how powerful it is.
[00:00:56] It's most traditionally used to calculate positions for a node and link graph, but because of all of the different functionality it has, you can actually get quite creative with how you apply the force layout. So this is actually a very classic example of a D3 force layout, and the dataset is Les Mis, and each of these are characters, each of these nodes are characters in the play or musical and school play.
[00:01:45] And then each of the links are the characters' co-occurrence with each other, and I believe after each chapter. And I think they're colored by the family. What you can see is that with the nodes, the first layout asks for an array of nodes. And so this is with ID.
[00:02:15] And these objects have just the ID, which is their name, and the group they belong to. And optionally, the force layout asks for an array of links. And so in the array of links, it says the source, and that source ties directly back to the ID attribute in the nodes.
[00:02:41] And target similarly ties directly back to the ID element and some value. And so the way that we use the force layout is we say, this is the relevant piece of code from that Les Mis example. Which is that we create force simulation and pass in our nodes, and we say, the first force that I want to apply.
[00:03:08] Actually, I don't think it matters as much, the order for these forces. And if it does matter, I haven't quite noticed that big of a difference in how to order them. But certainly, if things are not turning out the way you expect, just try moving them around a little bit.
[00:03:36] But to my knowledge, from all of my experience, I don't think the order of the forces that you specify in this chain matters. And I'm saying that take that with a grain of salt, please [LAUGH]. But anyways, we're saying that we want to first apply a force of link and we're passing our array of links.
[00:04:02] And we're telling D3 that in this array of links, the way that it maps to our nodes is through this ID attribute in our nodes. And alternatively, if you pass in an array of links that already directly references the nodes, then you don't have to specify this ID.
[00:04:24] And you'll see that in our example below. So then we can say we want to put some charge in this particular force. Charge is called forceManyBody. The way that I think about forceManyBody is you can either, I think it defaults to a negative 30 strength. And so a negative value for forceManyBody makes each of the nodes repel from each other, and if you specify a positive charge, it attracts the nodes to each other.
[00:05:03] So I think of forceManyBody as like planets or like stars or just like the universe. And then I just think of it like the planets, and the closer they get to each other, the more, I think, attracted they are, and the farther apart they are, the weaker the attractive force.
[00:05:26] And that's how I think of forceManyBody and how it's been implemented. But for what we need, you can just remember it as by default, it's a negative charge that repels nodes away from each other. And then it has this force that just says, we want each of our nodes to be attracted to the center.
[00:05:53] And this is only a subset of the forces that are available with D3 force, and you can see the rest of it in the force module reference. And you can actually also write your own force, your own custom force. So I think they detail how to do that in the module reference also.
[00:06:17] And finally, the most important part is being able to update the node and link positions on each tick. So what does all of this mean, and what is D3 doing underneath the hood? So what it's doing is once it's passed the array of nodes and links, it first goes and assigns a random x,y position to each of those nodes.
[00:06:47] Then it loops through each of the nodes and applies the series of forces that we specified up here. There's three of them here. You can have more or less. And we apply the series of forces so that with attractive forces, the nodes pull together, pull towards each other.
[00:07:10] So it actually kinda loops through each node, and it then loops through all of the other nodes close by, and then it applies the force on that particular node. And so the most common attractive force is when nodes are linked. And so that will make them get positioned closer to each other, nudged closer to each other.
[00:07:38] Negative forces are those that push the nodes apart to avoid overlap. And so that's what I had mentioned earlier with the negative charge. Or there is another force called collision that also helps with that. And all of these forces are applied to each individual node, and nudges them, slightly nudges the x,y positions slightly, and it loops through each of the nodes doing that.
[00:08:10] And this series of calculations happen in what D3 calls one tick and each tick. And D3 underneath the hood runs thousands of ticks until all of the nodes are nudged to their quote, unquote, optimal positions. And there's some other settings you can toggle and play with with the force layout that we won't really go into in here, but will kind of help you get closer to the quote, unquote, optimal layout, and that you can also read in the reference.
[00:08:52] And finally, to use the calculated node and link positions, we update the corresponding DOM elements either on each tick, so we do that in .on tick, or on the whole simulation ending and we have that be a callback function on .on end. And so this is what I mentioned earlier about the force layout is a little bit different, because not only does it calculate the positions thousands of times, it also mutates the original data directly.
[00:09:30] Because it takes the original data set you passed in, and then on each tick, it mutates it, and then on the next tick, it uses that mutated one as reference and then calculates new positions. And then our next tick uses those positions, calculates new positions, and it kinda goes in that series of ticks.
[00:09:57] And that's why it mutates the data array directly.