An introduction to d3-force for Svelte Visualizations
An introduction to `d3-force`
This lesson preview is part of the Better Data Visualizations with Svelte course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Better Data Visualizations with Svelte, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

[00:00 - 00:12] Hey y'all, so let's dive into the first lesson within our B Swarm module. And in this lesson, we're really just going to aim to draw circles just like last time, but this time with physics.
[00:13 - 00:23] And so we're going to be using some different modules. We'll be relying on D3 a lot to draw circles that move on their own and that have dynamic layouts.
[00:24 - 00:32] So pretty exciting, but we're going to start off with some math. And the reason for that is because as we talk about drawing circles with physics, we need to use math.
[00:33 - 00:41] And what in particular we're going to be using is a force simulation. And so I would advise doing some reading on your own about this.
[00:42 - 01:01] This introduction from Ben Tannen is a really good kind of overview of how D3 force works as he says from someone who just learned how to use it. So I won't go through too much of this because I would advise you to read on your own, to play around with these interactive examples.
[01:02 - 01:22] But let's describe why we want to use force simulations, why we want to use D3 force is this library, this module. The reason is as you saw in our scatter plot, and just to review, our scatter plot looks something like this, they are kind of positioned all around the same x-axis variables.
[01:23 - 01:38] And if there's overlap, you know, if there's multiple countries at this six mark, they are then arranged on top of one another. So this is typical of a B-swarmed diagram, but what this kind of physics-based arrangement does is it adds a lot more fluidity to the chart.
[01:39 - 01:57] If these circles were arranged one on top of another, on top of another, it would look a little bit bland and geometric. This simply brings the chart to life a little bit more, and it also enables for smoother physics-based transitions whenever we do something like this, separating each country into its continent.
[01:58 - 02:24] And in order to do that, in order to bring in physics, we have to rely on D3, and D3 has a great module called D3 force that solves this for us. So you can read more about D3 force and what it does by taking a look at this GitHub repository, which includes documentation, talks about how to install, and most importantly, has the API reference for each of the specific methods that you can use within D3 force .
[02:25 - 02:32] So that's a lot of talking and not a lot of coding. Let's go ahead and get into an example of how D3 force works in the real world.
[02:33 - 02:44] So I'm going to copy this code, which is just a basic force simulation overview , into my project. And again, we're going to go into source and app.s felt.
[02:45 - 02:59] And you know, actually, let's do a little bit of a setup. So just like last time, we want our code editor to live on the left side of the screen, and then we'll open up, you know, Firefox with the actual output on the right side of our screen.
[03:00 - 03:07] So here, we can see our input on the left, our output on the right. Just like this tells us, I'm going to delete everything.
[03:08 - 03:18] If I hit save, you'll see that the page is now white. So back to our code, here's an example of how we would write a force diagram from scratch.
[03:19 - 03:30] I'm actually not going to paste this because this would require our data to have an x variable and a y variable. So what I'll instead do is explain what on a high level this code chunk would do.
[03:31 - 03:55] Okay, the first line of code imports multiple methods from the D3 force library , including force simulation, force y, force x, and force collide. Now, the first thing that we see is force simulation, which if you're curious about what force simulation is, again, you can go ahead and read the D3 force documentation and see, you know, what it is.
[03:56 - 04:08] So if I just as an example, if I looked at force simulation, I would find this information creates a new simulation with the specified array of nodes and no forces. If nodes is not specified, dehalt to the empty array.
[04:09 - 04:16] So essentially, this is how we instantiate a new simulation, right? Simple enough.
[04:17 - 04:22] Then the question is, what is within that simulation? And the answer is two different forces.
[04:23 - 04:32] Now forces can be thought of as basically what charges the physics, right? What forces are at play that are moving things around?
[04:33 - 04:37] So in the same way that like in Star Wars, there's a force and that moves things. I wouldn't know.
[04:38 - 04:51] I've never seen Star Wars, but that's my understanding. Here we're basically telling our force simulation, there's an x force, which is going to move things on the horizontal axis and a y force, which move things on the vertical axis.
[04:52 - 05:00] So here we're saying the x force, right? The thing that is moving our elements towards the horizontal axis is of the following property.
[05:01 - 05:15] It is a force x with an argument of x that takes in an accessor that calls itself x and has a strength of 0.002. Force y does the exact same thing, but on the y axis.
[05:16 - 05:22] So I know this seems complicated. I know my verbal description probably did not elucidate this much for you.
[05:23 - 05:30] So let's talk a little bit more about what's happening here. This is an array of objects or actually nodes is an array of objects.
[05:31 - 05:37] Each of these containing an x and a y property, right? That's what we're accessing here and here is the x and y.
[05:38 - 05:46] So think of this as an array. Each of those array items has at least an x and a y property.
[05:47 - 05:58] Then we're using those properties, the x and the y, to apply forces, which as I write here represents a new interaction with the overall simulation. And then we could add others as well.
[05:59 - 06:17] Like force collide is going to bring things away from one another so they don't overlap and force center will bring things towards the center. So what I would really advise here if you're confused is reading through a lot of the different forces that are present in the D3 documentation.
[06:18 - 06:30] So if I wanted to look up what does force center do, I would control F1. And it says creates a new centering force with specified x and y coordinates, if not specified, defaults to zero zero.
[06:31 - 06:46] So what that means is that this force center would essentially tell our simulation to bring the circles near the midpoint of our visualization or whatever x, y coordinate that we provide. And so as an example of what this code actually does, right?
[06:47 - 06:53] Here's like a minimal code sandbox that I have that's meant to illustrate this. And I'll go ahead and open app dots felt.
[06:54 - 06:59] I know this might be hard to read. In fact, I don't know if I can even get rid of this.
[07:00 - 07:13] Okay, cool. And what you can see here is that simulation is a force simulation and the forces that are being applied include force collide, force x and force y.
[07:14 - 07:28] So force collide is preventing overlap force x is adjusting where they live on the horizontal axis force y is doing so on the y axis. And what we can see here is that then all these circles, you know, create this B swarm shape.
[07:29 - 07:45] And as an example, if I were to delete force collide, which remember, as I mentioned, is meant to prevent collision, if I were to remove that, all of these circles would then be overlapping with one another, right? So you can see how each of these forces can be really important to creating this physics-based visual.
[07:46 - 08:02] If I added a force center, for example, all of these circles would then coales ce on the zero, zero position, which we definitely don't want for a B swarm chart. What I'm meaning to do is illustrate how each of these forces are uniquely important, how they affect the visual at hand.
[08:03 - 08:11] So in our chart, we're going to want three forces, force x, force y, and force collide. For the reasons I just described, right?
[08:12 - 08:23] We want to organize circles on the x axis according to their happiness score, right? We want to pay attention to their y position, in particular because we want it at the center of our chart.
[08:24 - 08:37] So just 50% down, our force y should basically just be static. And then force collide because we don't want our circles to overlap like this, we actually want them to pay attention to each other's shape.
[08:38 - 09:01] And so what we're going to need to do in order to bring these forces in is import some data that allows us to organize countries with these forces. So data.js is available on this link.
[09:02 - 09:11] You can go ahead and find the data for this lesson. And what I'm going to want to do from here is bring it into my code base.
[09:12 - 09:22] So again, this is app.spelt, but what we want to do is add this to our data folder. Instead of this example data, we want to replace it with the array that I just identified.
[09:23 - 09:35] So what I'd actually advise doing is opening this and just command A to select everything, command C to copy it, and then literally just paste it into your file. Okay.
[09:36 - 09:47] So we'll click save, I'll go back to this other tab. And now we notice three properties within each item in this array, country, happiness, and continent.
[09:48 - 09:58] Now continent is going to be how we fill and eventually arrange these circles on the y-axis. And happiness is how we organize things on the x-axis country.
[09:59 - 10:15] Obviously, we need an identifier for each item in the array. And so in app.spelt, now that we have this array of items in data.js, we can go ahead and import it just like we imported data in our last example.
[10:16 - 10:34] As it says here, we'll go ahead and write import data from, and then we'll find the data file using this alias data.js. Now to verify that this is in fact working, we'll go ahead and console.log data .
[10:35 - 10:48] Now I'm going to reopen my Firefox, open the console, and refresh. And as you can see here, data does in fact exist.
[10:49 - 10:54] I got nervous because I saw this error. I don't know what this is, I would ignore it if you have it.
[10:55 - 11:11] But as you can see within the actual log, the data does exist. These numbers are being read as numbers, which means our data is imported and it's transformed in the proper layout, which means we're pretty much good to proceed moving forward with the rest of this lesson.
[11:12 - 11:26] So I'm going to move this over and I'm going to talk through rather than read the rest of this lesson for the sake of brevity. So as I mentioned, happiness is how we're going to organize circles on the x- axis.
[11:27 - 11:47] continent is how we're going to fill the color of our circles and country is the unique identifier for each circle in our data. So let's go ahead and begin by using that D3 snippet that we had identified earlier to import the necessary modules that we need to arrange things with physics.
[11:48 - 12:11] So what I'm going to want to import is force simulation, which as you'll remember is how we first instantiate the entire physics based simulation. Then I want to import force X because I want things to be arranged on the x- axis, force Y for the same reason, and force collide to prevent overlap just as we had identified last time.
[12:12 - 12:26] So if you wanted you can make a note for yourself like this is for the simulation, this is for the x-axis, et cetera. Feel free to do that for your own reference because there's a lot going on in the D3 force module.
[12:27 - 12:31] And then we've imported these but the question is from where? And the answer is from D3 force.
[12:32 - 12:37] That is the name of the module that we want to target. So we have all these imported.
[12:38 - 12:45] Of course they're not being used yet as it says here so we need to actually use those. Let's go ahead and create a new simulation.
[12:46 - 12:49] You could call this anything, right? You could call this mySim, you could call this anything you want.
[12:50 - 12:58] I'm going to call it simulation. And it's going to be equal to the force simulation of data, right?
[12:59 - 13:09] instantiate a new force simulation with the only argument being the data array we want to pass in. Then we have a series of arguments that follow that basically declare each relevant force.
[13:10 - 13:16] What does that mean? I could do dot force and then the name of the force so I'm going to call it my x force.
[13:17 - 13:27] And the argument within that is kind of like what method is being applied on the x-axis. So it's going to be a force x and this is going to look complicated.
[13:28 - 13:42] I would really recommend reading through a little bit of the documentation behind forces but I'll do my best to explain now. So we're using a force x and force x takes its own property called x which then takes in a relevant data accessor.
[13:43 - 13:56] So recall that within our data array we have three variables, continent, country and happiness. You'll remember as I said that happiness is the variable that we want to use on the x-axis.
[13:57 - 14:12] So here we'll say within each d or you know any accessor we could call this j, we could call this whatever we want. Within each d, find d dot happiness because that is the relevant variable we're going to use on our x-axis.
[14:13 - 14:26] Then you could optionally add a final parameter here called strength which is basically how strong is the physics at hand here that's organizing things on our x-axis. For us we'll do 0.8.
[14:27 - 14:36] Now this will finish our x-force so we can close this entire argument. So this method is pretty complicated.
[14:37 - 14:49] There's a good amount going on but I would recommend reading it line by line and really getting a handle on what's happening here before moving to our next force which is the y-force. So this is going to take a pretty similar structure, right?
[14:50 - 15:16] The name of the force is y and then within the y-force we want to use this method that we imported and rather than taking in an x property, obviously it's going to take in a y property. Then we're going to do the same accessor but the question is how are things organized on the y-axis and let's go ahead and say it's according to their continent to see what this ends up producing.
[15:17 - 15:30] If we wanted things to be all centered at the half of the midpoint of the screen for example we could find the height of the overall chart and do half of it. For example but for now we're going to organize things by their continent.
[15:31 - 15:42] So d.continent and then yet again we have the strength and for this because I 've already done this beforehand I know the magic numbers you would have to test this. I'm going to say a strength of 0.2.
[15:43 - 15:49] Then I'm going to save and I'm going to do some formatting. I'm not sure why it's not auto formatting.
[15:50 - 16:01] Then the final force that we want is our collision force. So here we're going to name the force collide and then within the force itself we're going to pass the following method.
[16:02 - 16:12] We'll do force collide, open close and the only argument that this takes is a radius. So I'm just going to give a radius of 5.
[16:13 - 16:22] And to be safe you know rather than giving it a hard coded number here I'm going to reference this elsewhere I'm going to make it its own variable. So above simulation I'm going to const radius.
[16:23 - 16:28] I'm going to capitalize it as kind of a note to myself that this will never change right? This is an immutable variable.
[16:29 - 16:34] And I'm going to call it 5. Then I'm going to go and replace this 5 with radius.
[16:35 - 16:46] So if I update this elsewhere it will cascade downward anywhere radius is in fact referenced. Okay so we have a simulation right now it's being unused which you'll notice a query.
[16:47 - 16:56] According to your code formatter. So let's go ahead and see what it is with the simplest declaration just console .logging it.
[16:57 - 17:14] And if we console.log simulation and then go back into our console we'll see that it's an object. And that object includes a lot of different properties including alpha, alpha decay, alpha min, alpha target, fine force, nodes, on etc.
[17:15 - 17:26] Now what's of interest to us is going to be this method or this property called nodes. Now nodes is a function right now you can't really tell what's going on.
[17:27 - 17:37] So let's actually go ahead and console.log simulation.nodes to see what it looks like inside. Again it's a function so you need to access the function using this open close parentheses.
[17:38 - 18:05] Now if I refresh you'll see that we actually see 146 items just like we did within our data. But the difference is these also include an index of VX of VY and X and a Y. So this is really interesting right because we basically have our exact same data array with some new properties applied and we did not generate these right index VX VY XY.
[18:06 - 18:23] This was the code itself which means that we're probably doing something right and creating these new nodes. And so the key thing to notice here is that simulation is this object with a lot going on and you can change those properties that we had looked at earlier like alpha , alpha decay programmatically.
[18:24 - 18:37] But if you want to access the output of the simulation you're going to want to use this dot nodes function. Because as you can see here this is where the actual numbers are appearing that we're generating dynamically.
[18:38 - 18:41] Okay. So we noticed two problems.
[18:42 - 18:52] First these X values are unscaled. 2.404 for Afghanistan is identical to its actual value 2.404.
[18:53 - 19:10] We don't want to put things at their raw values because then every circle would be contained between zero and ten on your screen which is an incredibly small canvas right. So what we're going to need to do very similar to our scatter plot module is scale our data using D3 scales.
[19:11 - 19:27] You'll remember D3 scale linear D3 scale band things like that that basically take these raw values 2.404 and turn them into points on the canvas. The second issue is that our Y values VY and Y are non-existent.
[19:28 - 19:30] They're not a number. And what that means is that we did something wrong.
[19:31 - 19:38] We're basically not positioning these correctly. And the reason is D dot continent is not numeric right.
[19:39 - 19:47] It is a textual variable. So what force Y is expecting is some number to arrange by and we're giving it a string.
[19:48 - 20:13] So we need to then take our strings and arrange things according to those strings on the Y axis, which is going to also require a new scale, um, scale band to basically organize things categorically according to textual variables. But before we get into scaling, both X scale and Y scale, we should go ahead and set up the infrastructure around a basic chart.
[20:14 - 20:31] And once we have this chart, we'll then have, you know, a width and a height that we can then reference in the D3 scales that we're going to need to create. So let's go ahead and create that chart step by step using the same type of pattern that we used in the simple scatter plot last module.
[20:32 - 20:38] First one, stay and create a width variable and a height variable. I'll make both of them 400 for now.
[20:39 - 20:54] Then we want to instantiate a new margin variable where margin is meant to represent the padding around a chart. You'll recall that graphic from Amelia Wattenberger, which basically shows the padding outside the chart is considered the margin and what's inside is the inner chart .
[20:55 - 21:06] For our purposes, we're going to give it a top of zero, a right of zero, a bottom of 20 and a left of zero. Now, I know these numbers because I've worked on this, this would again be testing.
[21:07 - 21:16] You might change these numbers as you go, but I know this is what's going to be ideal for us. Then we're going to create new variables for our inner width and our inner height.
[21:17 - 21:28] And remember that these are basically the overall width and height and then subtracting the horizontal and vertical margin respectively. So here it'll be width minus the left and right margin.
[21:29 - 21:39] And for inner height, it'll be height minus the vertical margins on the top and bottom. Now, what you'll recall is that we are prefixing inner width with a dollar label.
[21:40 - 21:49] And actually, we don't need to do this for inner height. But the reason that we are instantiated inner width with a dollar label is because we want it to update if and when width updates.
[21:50 - 21:54] Now, you might be asking, okay, when does width update right now? Not at all.
[21:55 - 22:04] You're right. In an ideal world, in our responsive visualizations, width will be dynamically updating according to the window width itself.
[22:05 - 22:17] And we can go ahead and get started with this little chunk of code, which will look very familiar because it's exactly what we did in the scatter plot code. You can go ahead and create a new div and call it chart container and then give it a dimension binding.
[22:18 - 22:30] Remember that that syntax is bind client width to the name of the variable that we want to target. So here we want to update width to be equivalent to the chart containers total width if and when that updates.
[22:31 - 22:41] Let's go ahead and save that. And if we wanted to test to see if this works, we could go ahead and console dot log inner width, save and then resize our window.
[22:42 - 22:54] And what you'll notice is that inner width does in fact respond. It does in fact update, which suggests that we do already have this width binding properly updating and inner width also updating after the fact.
[22:55 - 23:16] Now the final thing that we want to do is create a new SVG inside using the width and height that we've already defined. So let's say width equals width and height equals height, or you may remember the shorthand because these names are the same as the variables that they are accepting, we can just put the variable itself in a curly bracket.
[23:17 - 23:30] So we can open and close this SVG and then close the outer div. And now we have basically a chart container that for now is 400 pixels high and however many pixels wide the screen is wide.
[23:31 - 23:43] So now that we have this basic setup and get used to it because you're going to be doing it in every module in this course and for every chart you're going to be designing, this basic setup is complete. Now we can go ahead and create our scales.
[23:44 - 24:00] Now you might recall that scales are a way to map raw values to physical points on a canvas, right? Instead of zero to 10, which is the range of potential happiness scores, we want to go from zero to width.
[24:01 - 24:09] Where width is 514 or for your screen is going to be a different size. We want to take advantage of that entire potential space, right?
[24:10 - 24:17] And what we're going to use to do that are scale linear and scale band. My autocomplete knows just as well.
[24:18 - 24:24] And scale linear is going to be for quantitative data and scale band is going to be for categorical data. Now what does that mean?
[24:25 - 24:30] Well, quantitative or numeric you can think of as any number input, right? That's simple enough.
[24:31 - 24:40] Scale band takes in categorical or string variables or inputs instead of numeric inputs. And that's the key difference between these two.
[24:41 - 24:55] Let's go ahead and start with the easy one, which is going to be our X scale. We're going to create a new scale, call it X scale, and we're prefixing it with this dollar label because as you might remember, inner width is also instantiated with the dollar label.
[24:56 - 25:09] And as you'll see soon, this is the variable that we're going to reference within the scale. So this is going to be a scale linear with a domain, which you may remember is the input and a range, which you might remember is the output.
[25:10 - 25:19] Now the output is easy. We're going to go from zero to inner width because as I said, we want all of our points to occupy from zero to the end of our screen, minus the margins.
[25:20 - 25:24] But the domain is a little bit harder. The question is, what do we want the domain to be?
[25:25 - 25:46] You could dynamically generate the domain for your X scale by basically finding the minimum and maximum value of happiness within our data set, or you could just work smarter, not harder, and know that it's roughly between zero and 10. And in my findings, I found that there's no countries under one or over nine.
[25:47 - 25:49] So I'll just make it one and nine. Okay.
[25:50 - 26:05] But if you're here because you want to know technically what's proper, what you would actually do, you would probably import extent from D three array. And then what you would do is, which basically finds the minimum and maximum within an array of values.
[26:06 - 26:19] So then you would pass extent into the domain and you would look within the data array. Remember that data is this guy right here with all of our countries within, and you would pass an accessor, which would be D dot happiness.
[26:20 - 26:29] So basically I'm looking for the minimum and maximum of happiness within the data array. So that would be effectively the same.
[26:30 - 26:41] And if you wanted to verify, you could go ahead and console dot log the extent, refresh and look, and you'll see 2.4 to 7.8. So that's never going to change within our data.
[26:42 - 26:54] So you could pass this extent in, or you could just hard code it like I want to , because again, I found that work smarter, not harder is a good strategy to abide by. So we now have an X scale.
[26:55 - 27:02] And what we can do if we want is pass this in to the above simulation. But I'm going to wait until we do the Y scale, just so we can do it all at once .
[27:03 - 27:15] Let's create a new Y scale, which is not a scale linear function, but instead a scale band. And the reason for this is because we want to map these categorical variables known as continents.
[27:16 - 27:41] And we want to map those according to positions on the canvas that are basically equally spaced from one another. And a good visual illustration of this is present right here, where essentially given a categorical domain, in this case, a, b, c, and d, passing an input that meets one of these domains will return its equivalent position in the numeric range.
[27:42 - 27:48] Right? So if I pass b into a domain that accepts a, b, c, d, that's the second index, right?
[27:49 - 27:56] That is the second element. So it's going to find the second position within the range if we split up the range into four equal parts.
[27:57 - 28:05] So hopefully that visual illustration is in fact helpful. If not, I think it'll be made more clear as we go ahead and write this continents code.
[28:06 - 28:19] But basically we want this to be a scale band function, which again takes in the exact same parameters, a domain and a range where domain is input and range is output. But here the range is quite simple, right?
[28:20 - 28:27] Just like last time, it's going to be inner height to zero. And you might remember this is because the coordinate system is flipped in SVG.
[28:28 - 28:31] But then the domain is harder, right? So what do we want?
[28:32 - 28:43] We effectively want to get a list of each of the continents present within our data array. So Asia, Africa, all the way down, finding each continent.
[28:44 - 28:50] Now you couldn't manually go through and find these and then write them one by one in an array. It's probably not the best way to do it.
[28:51 - 29:09] The safer way to do it is map through our data array where map basically retrie ves one element per item in the overall array and return the continent variable. Now this will basically return a list of continents that can then be used as the domain.
[29:10 - 29:23] And based on their positions in this domain, passing that value into the y scale will return its numerically equivalent position in the range. Sounds pretty confusing.
[29:24 - 29:32] Let's go ahead and console log y scale of Asia and see what happens. If I refresh this, you'll see 316.6.
[29:33 - 29:37] What would happen if I passed Africa? You would see 253.3.
[29:38 - 29:52] If I passed North America. You'll notice that these numbers are pretty similar in that they're actually multiples of one another because like I said, they are equivalently positioned within the same range.
[29:53 - 29:59] So we know that our y scale is in fact working. So finally, we have our scales.
[30:00 - 30:04] That was quite fun. We're actually going to change this code a bit in a few seconds.
[30:05 - 30:15] But for now, let's go ahead and sub in our data to see how close we are to where we want to be. So up in our simulation, we'll remember we're passing these x and y values.
[30:16 - 30:27] The issue was that these were unscaled. So just to review before we make this change, within the simulation array, we had these raw numbers for x, like 2.4, which is way too small.
[30:28 - 30:36] And we had not a number for y, which meant that this just wasn't working at all . So now we're using x scale and y scale to fix that issue.
[30:37 - 30:44] I'll pass a d dot happiness into x scale and d dot continent into y scale. And then I'll hit save.
[30:45 - 30:48] Now immediately you're going to see a bug. And the bug is that x scale is not a function.
[30:49 - 30:52] Now you might be saying, yes, it is. I literally instantiated it right here.
[30:53 - 30:55] What do you mean? It's definitely a function.
[30:56 - 31:06] Notice how x scale is instantiated with the dollar label. This is going to be an important pattern to pay attention to is how we declare variables in our spell applications.
[31:07 - 31:11] Here, x scale is reactive. It is instantiated with a dollar label.
[31:12 - 31:23] So whenever simulation is created at basically application runtime when the page first loads, x scale doesn't actually exist yet. This is just the internals of how it works, how it declares reactive variables.
[31:24 - 31:47] So if we want simulation to actually reflect x scale properly, it needs to be created with the same method, in this case, with a dollar label. Now if we go ahead and hit save now, we'll see that that issue might get solved , but there's another one, which is that simulation is undefined, which is because right on 28, we are console dot logging simulation.
[31:48 - 31:54] But for the same issue as prior, that doesn't exist yet. Let's prefix this with a dollar label as well and hit save.
[31:55 - 32:00] Now we see no errors other than this weird error that is totally unrelated to the code. And you can ignore it.
[32:01 - 32:04] I don't know what this is. Other than that, we're good to go.
[32:05 - 32:15] And our simulation is now working. If you look at the console log, we can verify this by opening any of the objects and looking at the VX, VY, X and Y variables.
[32:16 - 32:29] Recall that previously they were super small numbers and NAN, which stands for not a number. Now they are properly scaled numbers, 295, and actually a number, 61.9.
[32:30 - 32:43] Therefore our scaling is in fact working. We've created scales that do in fact transform our raw data, both textual string based data and numeric data into positions on a canvas.
[32:44 - 32:57] So for our final step of this lesson, let's go ahead and render these circles using their newly scaled values. Just like last time, we're going to create a G element that accepts the inner chart.
[32:58 - 33:10] And let's call it inner charts that we can remember this. And within this chart, we want to transform its position to the right, the equivalent of margin dot left, and down the equivalent of margin dot top.
[33:11 - 33:18] Again, this is a common pattern. And if this is confusing you, I would definitely recommend going back to the last scatter plot lesson and reviewing why we do this.
[33:19 - 33:25] We're going to open and close this G element. And the question is, what do we want within the each block?
[33:26 - 33:37] What do we want within the inner chart? And the answer is all 100 and however many circles it is, 146 circles rendered according to their x and y position.
[33:38 - 33:47] So we can go ahead and write each simulation dot nodes as node. Or for the sake of simplicity, we can go ahead and replace this with a variable .
[33:48 - 33:54] We could say nodes is equal to simulation dot nodes. Then in our each block, what do we want?
[33:55 - 34:11] We want one circle per node with the following properties. We wanted to have a Cx of node dot x, a CY of node dot y, an R of radius, and a fill of steel blue, because that's a much prettier color than basic red.
[34:12 - 34:27] Let's go ahead and save this and then end our each block. Now if we save and refresh, you'll notice that the circles appear in the top left corner and then on resize, they in fact appear in the right place.
[34:28 - 34:35] For now, this is sufficient. What we've done is we basically positioned each of our circles according to their x and y position.
[34:36 - 34:46] Now at runtime, these nodes are in fact undefined, which is why it's creating this weird positioning in the top left corner. But we're going to fix that in the next lesson.
[34:47 - 34:58] For now, what we've essentially done is we've created a properly scaled chart that uses physics to position these B swarm elements. But there's one other thing we want to fix here.
[34:59 - 35:08] And that is the order of these continents. So right now, the order is just passed in randomly according to the order of data in the data array that we passed.
[35:09 - 35:16] Now what does that mean? Effectively, what the continents array looks like is a list of continents as they appear in the data.
[35:17 - 35:28] So right now, it's something like Asia, Africa, and whatever the next continent to appear would be third and list, etc, etc, etc. We probably don't want to do that.
[35:29 - 35:37] We probably want to order these continents according to some meaningful variable. A good example would be like the average happiness score of each continent.
[35:38 - 35:54] And that way they'll appear within our axis more properly, like in order they 'll look visually as you would expect as you look at a chart to see these in order. And the way that we're going to do this is a very complex function that I do not expect you to understand.
[35:55 - 36:01] That looks like this. And you notice that I've commented in a few different places so it's more clear what we're doing.
[36:02 - 36:10] But we're basically generating the average for each continent so that we can sort according to that. We're using D3 arrays functions, rollups, and mean.
[36:11 - 36:29] To basically group the data by continent and within that grouping, find the mean of happiness scores. Then after we do that, we're sorting so that those with the highest happiness scores are at the top and those with the lowest happiness scores are at the bottom.
[36:30 - 36:45] And then we are mapping and retrieving the actual continent name as a value. So if I copy this continents array that we just created and paste it here, you 'll notice that the data updates slightly.
[36:46 - 36:56] And now these are actually sorted according to average happiness value. The last thing that I want to do is add some padding on the top and bottom of this chart.
[36:57 - 37:10] And so I'm going to use an additional function or method within scale band, which is called padding outer, I believe. Yes, and it's going to have a padding outer of 0.5.
[37:11 - 37:19] You can change this as you see fit, but you'll notice that it now adds some space between the top of the chart and the circles. So there's a bit more breathing room.
[37:20 - 37:32] There's also padding inner, which you could play around with, but padding outer meets the objective of this basic chart. So we have a chart, but definitely not perfect.
[37:33 - 37:37] It starts off in this top left corner. It fixes on resize, but the resize looks kind of choppy.
[37:38 - 37:40] It looks kind of ugly. So we're close.
[37:41 - 37:48] And the purpose of this lesson was just to get up, get set up drawing circles with physics. In the next lesson, we're going to make these circles reactive.
[37:49 - 37:52] So they have a bit more momentum. The physics based layout is more intuitive.
[37:53 - 38:00] And this initial rendering issue is not present. So thanks for bearing with me in this quite long lesson.
[38:01 - 38:07] Feel free to reach out with any questions or concerns. And I'm excited to see you in the next lesson where we'll make these circles reactive.
[38:08 - 38:07] Thanks.