Making of Impacts – Programming ⋂ Art
Draw Voronoi diagrams on spherical surfaces in a scalable vector graphic file format to help depict planet-wide fissures from colossal collisions.
Introduction
For my Impacts Project, illustrator Anna Ferguson imagined the early, proto-Earth—née Tellus, short for Tellus Mater meaning “Mother Earth”—in a scalable vector graphic (SVG) format as follows:

Notice how the fissures on all impactors wrap around the sphereical bodies. Zooming in we see:

Here are the problems we’ll tackle to recreate those lines:
- create cracks based on Voronoi Diagrams; and
- curve the cracks by converting Cartesian to spherical coördinates.
Further, the problems are constrained as follows:
- the output must be in SVG format;
- only free, open-source tools may be used; and
- timebox building custom software to a few hours.
We could write some software to: map Cartesian coördinates onto spherical coördinates, integrate a Voronoi diagram algorithm, provide a graphical user interface to tweak the inputs, inject parameterized randomness, and finally offer an SVG export function.
That would be a lot of time invested for a one-off piece of art when less laborious solutions exist. Instead, we’ll limit development efforts using the following tool chain:
- Inkscape (export
.svg
) → - Java OpenStreetMap Editor (import
.svg
, export.geojson
) → - D3.js (import
.geojson
) → - Browser (render
.svg
)
Requirements
Download and install the following applications:
Download the following application and move the Java Archive (.jar
) file to a known location, such as $HOME/bin/josm-latest.jar
:
- Java OpenStreetMap Editor (JOSM)
We’re also going to write some JavaScript using the D3.js library, but there’s no need to download it, yet.
Voronoi Diagrams
Voronoi partitions appear throughout nature: cracks in dried mud, breaks in ice floes, cell arrangement in plant leaves, tortoise shells, giraffe pelts, and other instances abound. Such patterns are also apparent in lava, like the following view of Mount Kīlauea’s summit:

Anna’s illustration depicts three impactors: the largest, Earth; the second largest, perhaps Theia; and a minor planetesimal. (Aside, watch NASA’s Evolution of the Moon for time-lapse videos of the moon’s formation.) Our goal is to create a Voronoi Diagram that resembles the following:
Notice the smaller clusters circled in the above figure. We’ll see similar clusters in the subsequent sections.
Drawing Voronoi Partitions
Start Inkscape to begin drawing the Voronoi partitions. Big picture, we want to perform the following tasks:
- Spray paint some circles.
- Distribute the circles.
- Draw a Voronoi Diagram around the circles.
Spray Paint Circles
Spray circles around the canvas as follows:
- Press
e
to select the Ellipse tool. - Hold down
Ctrl
. - Click and drag the mouse to draw a small circle.
- Stop moving the mouse.
- Release the
Ctrl
key. - Press
Ctrl+a
to select the circle. - Press
a
to select the spray tool. - Change the spray tool settings (along the top):
- Set Width to:
50
- Set Amount to:
50
- Set Rotation to:
0
- Set Scale to:
0
- Set Scatter to:
100
- Set Width to:
- Click and drag the mouse around the canvas.
- Fill the canvas with hundreds of small circles.
- Optionally, make a few small clusters.
Here’s an example picture:
Distribute Circles
Socially distance the circles by separating them from each other:
- Press
Ctrl+Shift+A
to display the Align and Distribute panel. - Change the Remove overlaps settings:
- Set H to:
10
- Set V to:
10
- Set H to:
- Click the Remove overlaps button icon.
The result will resemble the following picture:
Voronoi Diagram
Using a version of Inkscape that has a working Voronoi Diagram extension, apply the following setps:
- Click
Ctrl+a
to select all the circles. - Click Extensions → Generate from Path → Voronoi Diagrams.
- Click Apply.
- Click Close.
- Press
Delete
to delete the circles. - Click File → Save As.
- Set File name to:
voronoi-diagram.svg
. - Set Files of type to:
Plain SVG
. - Click Save.
A picture similar to the following is saved:
Adjusting the location of the small circles sprayed onto the canvas provides fine-grained control over the resulting Voronoi Diagram. For simplicity’s sake, use metric-friendly image dimensions, such as 100 cm2.
Cartesian to Spherical
JOSM, a program for editing geographical data, can:
- import vector graphic files;
- convert coördinate systems; and
- export data in GeoJSON format.
Using it requires making or downloading a vector graphic, starting the application, installing a plugin, importing the Voronoi Diagram, then exporting the result as GeoJSON.
Download
Create a Voronoi Diagram as detailed above, or download the following file, which is nearly identical to Figure 4, but without the collision areas marked:
- Right-click voronoi-cracks.svg.
- Save the file.
Remember where the file is saved, we’ll import it into JOSM shortly.
Run JOSM
Run JOSM as follows:
java -jar josm-latest.jar
Accept any and all prompts that appear.
Install Plugin
Install the import vector graphic plugin as follows:
Click File → Preferences.
Click the plugin icon.
JOSM Plugins Click Download to download a list of all plugins.
Set Search to:
vec
Check importvec (see the plugin documentation).
JOSM Import Vector Graphics Plugin Click OK.
The plugin is installed.
Import Voronoi Diagram
Import the vector graphic as follows:
- Click File → New Layer.
- Click File → Open.
- Browse to and select
voronoi-cracks.svg
. - Click Open.
At this point, you may encounter a bug in JOSM whereby the Opening files dialog hides the Import vector graphic dialog:

Continue as follows:
Click the dialog’s upper-right × to dismiss the Opening files dialog box—clicking Cancel has no effect.
Set the Scale unit(s) to:
400,000
.JOSM Scale Dialog Click OK.
- If an error appears, it may be that the scale is too large; repeat halving the value until the vector graphic imports successfully.
Press
-
, or adjust the slider, until the entire “world” is visible:JOSM Imported Vector Graphic
The vector graphic file is imported.
Export GeoJSON File
Export the data in GeoJSON format as follows:
- Click File → Save As.
- Set File Name to:
voronoi-cracks.geojson
- Click Save.
The file is exported, you may quit JOSM.
D3.js Library
Exporting to a standard data format permits using a library that can display said data. D3.js is a JavaScript library that can present and manipulate data, including GeoJSON data. Loading, rendering, and rotating the cracked planet has a few parts, including:
- create a web page to contain the content;
- publish and minify the GeoJSON data; and
- create a script that renders the data using D3.js.
Once finished, we will cheat to extract the final vector graphic.
Web Page
You’ll need a place for the files, such as:
- a web server;
- a static web site hosted on GitHub;
- an online JavaScript editor; or
- the local file system.
If using a local file system, you’ll need to disable the web browser’s Cross-Origin file policy. In Firefox, this can be accomplished as follows:
- In the URL, type:
about:config
- Press
Enter
. - Confirm being careful, if prompted
- Search for:
security.fileuri.strict_origin_policy
- Change the value from
true
tofalse
.
Be sure to revert the value when finished.
When ready, complete the following steps:
Create a directory for all the forthcoming files.
Create a new file named
index.html
.Paste the following content:
<!DOCTYPE html> <html lang="en"> <head> <title>Spherical Projection</title> <meta charset="utf-8" /> <script crossorigin="anonymous" src="https//unpkg.com/d3@5.16.0/dist/d3.min.js"> </script> <script crossorigin="anonymous" src="https://unpkg.com/d3-geo-projection@2.4.1/dist/d3-geo-projection.min.js"> </script> <script crossorigin="anonymous" src="https://unpkg.com/versor@0.1.2/dist/versor.min.js"> </script> <style> .cracks { fill: none; stroke: #0081C2; stroke-width: 1px; } </style> </head> <body> <div id="container"></div> <script crossorigin="anonymous" src="impacts.js"></script> </body> </html>
Save the file.
Briefly, we first import all the required libraries (D3.js, the geographic projections library, and versor), which is accomplished by the <script>
elements inside the <head>
element.
When we write our integration script, the lines will be given a class attribute of .cracks
:
.cracks {
fill: none;
stroke: #0081C2;
stroke-width: 1px;
}
The last lines of importance provide a placeholder for the projected sphere and code that loads the data:
<div id="container"></div>
<script crossorigin="anonymous" src="impacts.js"></script>
Reference the impacts.js
script in the body
to ensure it executes after the scripts in the head
have been loaded. Until we actually implement impacts.js
, no data will be loaded, much less rendered.
Publish GeoJSON File
Copy voronoi-cracks.geojson
into the same directory as index.html
so that impacts.js
can load the file. If you have jq
installed, consider minifying the GeoJSON data:
jq -c . < voronoi-cracks.geojson > vc.geojson
mv vc.geojson voronoi-cracks.geojson
Integrate D3.js
The script to load, render, and interact with the GeoJSON file is fairly short. Create a new file in the same location as index.html
named impacts.js
that has the following content:
var w = 600,
h = 600,
scale = h / 3;
var projection = d3
.geoOrthographic()
.scale( scale )
.translate( [w / 2, h / 2] );
var geoPath = d3
.geoPath()
.projection( projection );
var svg = d3
.select( '#container' )
.append( 'svg' )
.attr( 'width', w )
.attr( 'height', h );
svg.call( d3
.drag()
.on( "start", dragstart )
.on( "drag", dragging )
);
d3.json('voronoi-cracks.geojson').then( function( lines ) {
svg.append( "path" )
.datum( lines )
.attr( "class", "cracks" )
.attr( "d", geoPath );
});
var v0, r0, q0;
function dragstart() {
v0 = versor.cartesian( projection.invert( d3.mouse(this) ) );
r0 = projection.rotate();
q0 = versor( r0 );
}
function dragging() {
var v1 = versor.cartesian( projection.rotate( r0 ).invert( d3.mouse(this) ) ),
q1 = versor.multiply( q0, versor.delta( v0, v1 ) ),
r1 = versor.rotation( q1 );
projection.rotate( r1 );
svg.selectAll( ".cracks" ).attr( "d", geoPath );
};
Let’s review the code. The first few lines define values for the:
- canvas width (
w
); - canvas height (
h
); and - scaling factor for the projected sphere.
var w = 600,
h = 600,
scale = h / 3;
Those values are then used to define an orthographic projection with the predefined scaling factor and a translation to the middle of the canvas (we could also call rotate
and center
to orient the initial projection, but both properties are zero by default):
var projection = d3
.geoOrthographic()
.scale( scale )
.translate( [w / 2, h / 2] );
Next, instruct D3.js to convert the projection to numerous path values that are compatible with the SVG standard:
var geoPath = d3
.geoPath()
.projection( projection );
Then create an object that couples a vector-based representation of projected paths to an HTML DOM element—the div
with id
of container
found in index.html
:
var svg = d3
.select( '#container' )
.append( 'svg' )
.attr( 'width', w )
.attr( 'height', h );
Mouse (or touch) interactivity is defined by attaching dragstart
and dragging
functions to the svg
object. From there we load the voronoi-cracks.geojson
file into D3.js, append all the lines from the file, and ensure the .cracks
class is associated with every line.
Note: We could have used libraries such as d3-geo-voronoi and d3-delaunay, but it would have meant programming the values—or developing a custom user interface—to generate the Voronoi Diagram that aligns with the collisions depicted in Anna’s illustration. Spray painting meant re-using Inkscape’s user interface rather than reinventing that wheel. Moreover, this process provides the ability to take any drawing—such as asteroid impact craters—and project them onto spheres without any additional software development.
To understand the next part of the code, visit Jason Davies’ Rotate the World where there is an excellent explanation of how three axes (Euler angles) are mapped from 2D screen space into axis-angle representations. We can take Mike Bostock’s sample versor code for dragging and tweak it:
function dragging() {
var v1 = versor.cartesian( projection.rotate( r0 ).invert( d3.mouse(this) ) ),
q1 = versor.multiply( q0, versor.delta( v0, v1 ) ),
r1 = versor.rotation( q1 );
projection.rotate( r1 );
svg.selectAll( ".cracks" ).attr( "d", geoPath );
};
My understanding is that these calculations change the values for the Euler angles λ, φ, and γ, which is what allows dragging movements in two dimensions to affect all three values.
Interactive Preview
The final step entails interacting with the now spherical Voronoi Diagram and rotating the sphere until the clusters closely match the impact sites:
Once satisified with the orientation, export the SVG by cheating:
- Right-click the projection.
- Click Inspect Element (or Inspect or its equivalent).
- Right-click the
svg
element. - Click Copy → Outer HTML.
- Open a text editor.
- Paste the SVG code.
- Save the file.
The spherically projected Voronoi Diagram is saved.
Summary
We walked through:
- using Inkscape to create custom Voronoi Diagrams;
- changing coördinate systems using JOSM to convert SVG to GeoJSON;
- integrating D3.js to spherically project GeoJSON as SVG;
- using versors to translate 2D movements into a 3D rotation; and
- “exporting” an SVG file using the brower’s “inspect element” mechanism.
Addendum
This process was repeated for scores of asteroid craters, which were also sent to the illustrator to fuse into the final illustration. The colours were selected to closely match those of blackbody radiation while keeping true to the project’s palette.
You can see a larger version of this illustration—and many others—on the preview page of the Impacts Project web site.
Contact
About the Author
My career has spanned tele- and radio communications, enterprise-level e-commerce solutions, finance, transportation, modernization projects in both health and education, and much more.
Delighted to discuss opportunities to work with revolutionary companies combatting climate change.