Drawing radial shapes in D3.js

This post demystifies one of the most feared vector functions available in D3.js: the radial line, or d3.radialLine(). Radial lines are constructed with only two attributes: an angle and a radius. The product of the function is a line, but unlike the basic line function, there are no x and y co-ordinates. I fundamentally misunderstood the radial line logic the first time I used it – in fact I had to bring in my boyfriend one late Thursday evening to help me get it right. This guide should help you avoid my mistakes.

The radial line syntax is as follows:

var radialLineGenerator = d3.radialLine();

var points = [
  [0, r],
  [..., ...],
  [x, r]
];

var pathData = radialLineGenerator(points);

Where the pair [0, r] describes the line drawn at 0 degrees, r is the radius length, and x is the angle. The easiest way to get your head around the radial line function is by drawing a circle-based shape. Good examples of such are a pentagram, an octagon, or a doughnut with a hole in it. In my example I’ll go with a five-pointed star, like the one below.

A star is an easy shape to construct with D3.radialLine()

The reason a pentagram is a good example is that all of its vertices can be plotted on just two circles: an outer circle for the spikes, and an inner circle for the elbows. To make things even easier, all of its angles are the same.

2circles

All vertices of a pentagram fit on 2 circles

A pentagram has 10 vertices in total, 5 sitting on the inner circle, and 5 on the outer. The radialLine() function will be fed the coordinates of these vertices and in return draw a line connecting them. The line’s length is calculated by the function: we only need to specify the circle’s location. Let’s review the code below to see how this is defined.

var radialLineGenerator = d3.radialLine();

var r1 = 15;
var r2 = 6;

var radialpoints = [
[0, r1],
[Math.PI * 0.2, r2],
[Math.PI * 0.4, r1],
[Math.PI * 0.6, r2],
[Math.PI * 0.8, r1],
[Math.PI * 1, r2],     
[Math.PI * 1.2, r1],
[Math.PI * 1.4, r2],
[Math.PI * 1.6, r1],
[Math.PI * 1.8, r2],
[Math.PI * 2, r1]
];

var radialData = radialLineGenerator(radialpoints);
var radial = svg.select("path").attr("class", "radial").attr("d", radialData);

The key to understanding the radialLine() function is the array of angles and radiuses, here called radialpoints. Each vertex in our star can be described by those two attributes:

  • The radius value is corresponding with the radius of a circle that hosts that particular point. In my example it can only take two values: r1 if the vertex is on the outer circle, and r2 if on the inner.
  • The angle value is the angle between the ray at 0° and the radius going from the circle’s center to our point’s location on the circle. Importantly, the angle measure is in radians (not degrees!). One radian is equal to 180/π degrees. So, to convert from degrees to radians you would multiply the degree by π/180. For example, 90° translates to ½*π, and 0° to 0.

Let’s put this in practice and draw the star line by line. Going clock-wise, my first point lays on the outer circle, so we’ll set its radius to r1. The angle is 0°. This vertex is represented by [0, r1] in my points array. If my radialpoints array had only this entry, we’d end up with a line going from the center straight up north.

0degree

The first vertex is located at the other circle on 0°

My next point is located on the star’s elbow – i.e. on the inner circle. All angles in a pentagon are the same and measure 36°. That translates to 0.2*π radians. This vertex is represented by the [Math.PI * 0.2, r2] entry in my radialpoints array.

36degree

The second point sits on the inner circle on 36°

Once you understand the logic, defining the next points are a child’s play. You just interchangeably pick the inner and the outer circle, and make sure that the angle is set to 0.2*π radians. You close the shape when the point at 360° – essentially the same as 0° – is reached.

Easy, right? Next time I’ll show you how to work with other fun D3 shapes – make sure to follow me on Twitter for my post updates!