Plotting Polar Coordinates in CSS

For the demonstrative purposes of explaining my polar style-difficulty chart of music, I decided I wanted to show off what the chart looks like.

It’s missing the hundreds of entries, slices, and other infographics that my notebook catalogue has, but it’s a compelling display nonetheless, I hope.

Gathering Data Points #

Firstly, I needed to gather the data points to display. As I already had the points drawn on paper, I just needed to digitalise each point’s position. To this end, I opened up GeoGebra in my browser, and started adding points.

Each point can have a name, and so I set each point’s name to be Artist - Album. After placing down enough points to satisfy me, it was time to get a usable, serialised output.

Luckily, GeoGebra has exposed a JavaScript API that you can access through the console. ggbApplet.getAllObjectNames() will return a list of all the names of objects, and ggbApplet.get{X,Y}coord(name) will get you the respective coordinate. A few maps later, and I had all the points in my clipboard as a JSON list.

As you might know (or have guessed), I generate this blog using Jekyll, which belongs to the Ruby/YAML era of web development A nice alternative to Node and PHP, in my humble opinion: there’s definitely something more magical about it.

Secondly, Jekyll supports data files (in a variety of formats), inside the /_data folder. These files are then read and available to use inside the site.data variable.

It’s important to note that the coordinates are given in their Cartesian forms, not polar forms. Although the conversion between the two is trivial, this is what GeoGebra defaulted to, and it makes handling them a bit easier later on.

Displaying The Points in HTML #

In Liquid template code, before generating any HTML, I normalise each point’s coordinates to be within between 0 and 1, with an added 20% padding from the edge. For reference, they’re mostly between -1 and 1; so it’s a scale + translation operation.

Then, I iterate through all the data points, and place a display: contents container for each point. In it, are the hidden popup, helper radius, and dot. The latter two are placed absolutely (in a relative container) using left: calc(100% * {normalised_x}).

The container uses the display: contents trick to not create a new box around its children, but still wrap them so as to give a :hover selector on a unique common ancestor. It’s how I highlight footnotes on both their reference and footnote simultaneously. Try me!

The circle element is a bit different: its radius I need to calculate (x2+y2)\left(\sqrt{x^2 + y^2}\right), which I do in Liquid. I needed to write a Ruby plugin to implement the sqrt filter, though:

module Sqrt
  def sqrt(num)
    Math.sqrt(num)
  end
end

Liquid::Template.register_filter(Sqrt)

The circle needs to have its center at (50%,50%)(50\%, 50\%) of the container. We know it has radius rr, and we’re moving it by the top-left corner. That means we need to place it halfway, and subtract the radius; for both coordinates.

There seems to be a bit of rounding error when setting the circle radius, and it doesn’t always line up with the dot it’s supposed to pass through: but it works overwhelmingly well.

Since you can see the CSS yourself, it’s best you inspect it yourself too; my words can only substitute the experience up to a point — and I’ve given you the missing Liquid glue code you don’t see.

Calculating Trigonometric Functions in CSS #

Although it wasn’t necessary here, and it should be avoided if possible, you can calculate trig function values in pure CSS using Taylor/McLaurin series representations of the respective functions (which is how computers calculate the values of these functions, too), you can write the terms using CSS variables:

.sin {
  --sin-term1: var(--angle);
  --sin-term2: calc((var(--angle) * var(--angle) * var(--angle)) / 6);
  --sin-term3: calc((var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle)) / 120);
  --sin-term4: calc((var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle)) / 5040);
  --sin-term5: calc((var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle)) / 362880);
  --sin: calc(var(--sin-term1) - var(--sin-term2) + var(--sin-term3) - var(--sin-term4) + var(--sin-term5));
}

Not very pretty, or fast; but it does the job. I’ve lifted this code from this gist, where you can find out the spelled out CSS for sine, cosine, and tangent.