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:

Earth Accretion by Anna Ferguson

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

Earth Accretion Close-Up

Here are the problems we’ll tackle to recreate those lines:

Further, the problems are constrained as follows:

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:

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:

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:

Halemaʻumaʻu Lava Lake

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:

Voronoi Diagram Planetesimal Collisions

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 Circles

Spray circles around the canvas as follows:

  1. Press e to select the Ellipse tool.
  2. Hold down Ctrl.
  3. Click and drag the mouse to draw a small circle.
  4. Stop moving the mouse.
  5. Release the Ctrl key.
  6. Press Ctrl+a to select the circle.
  7. Press a to select the spray tool.
  8. 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
  9. Click and drag the mouse around the canvas.
  10. Fill the canvas with hundreds of small circles.
  11. Optionally, make a few small clusters.

Here’s an example picture:

Smattering of Circles

Distribute Circles

Socially distance the circles by separating them from each other:

  1. Press Ctrl+Shift+A to display the Align and Distribute panel.
  2. Change the Remove overlaps settings:
    • Set H to: 10
    • Set V to: 10
  3. Click the Remove overlaps button icon.

The result will resemble the following picture:

Isolated Circles

Voronoi Diagram

Using a version of Inkscape that has a working Voronoi Diagram extension, apply the following setps:

  1. Click Ctrl+a to select all the circles.
  2. Click ExtensionsGenerate from PathVoronoi Diagrams.
  3. Click Apply.
  4. Click Close.
  5. Press Delete to delete the circles.
  6. Click FileSave As.
  7. Set File name to: voronoi-diagram.svg.
  8. Set Files of type to: Plain SVG.
  9. Click Save.

A picture similar to the following is saved:

Voronoi Diagram

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:

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:

  1. Right-click voronoi-cracks.svg.
  2. 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:

  1. Click FilePreferences.

  2. Click the plugin icon.

    JOSM Plugins
  3. Click Download to download a list of all plugins.

  4. Set Search to: vec

  5. Check importvec (see the plugin documentation).

    JOSM Import Vector Graphics Plugin
  6. Click OK.

The plugin is installed.

Import Voronoi Diagram

Import the vector graphic as follows:

  1. Click FileNew Layer.
  2. Click FileOpen.
  3. Browse to and select voronoi-cracks.svg.
  4. Click Open.

At this point, you may encounter a bug in JOSM whereby the Opening files dialog hides the Import vector graphic dialog:

JOSM Import Vector Graphic Bug

Continue as follows:

  1. Click the dialog’s upper-right × to dismiss the Opening files dialog box—clicking Cancel has no effect.

  2. Set the Scale unit(s) to: 400,000.

    JOSM Scale Dialog
  3. 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.
  4. 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:

  1. Click FileSave As.
  2. Set File Name to: voronoi-cracks.geojson
  3. 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:

Once finished, we will cheat to extract the final vector graphic.

Web Page

You’ll need a place for the files, such as:

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:

  1. In the URL, type: about:config
  2. Press Enter.
  3. Confirm being careful, if prompted
  4. Search for: security.fileuri.strict_origin_policy
  5. Change the value from true to false.

Be sure to revert the value when finished.

When ready, complete the following steps:

  1. Create a directory for all the forthcoming files.

  2. Create a new file named index.html.

  3. 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>
  4. 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:

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:

  1. Right-click the projection.
  2. Click Inspect Element (or Inspect or its equivalent).
  3. Right-click the svg element.
  4. Click CopyOuter HTML.
  5. Open a text editor.
  6. Paste the SVG code.
  7. Save the file.

The spherically projected Voronoi Diagram is saved.

Summary

We walked through:

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.