Introduction: Making a Centipede Fossile.

Following instructable describes how to create a centipede-like fossile. 

The graphic part was done in NodeBox 3, an open source application for generative design (and much more).
The engraving part was done on a cnc cutter (Trotec - Speedy 100) at Fab Lab Genk (thx a lot Danny!).

I tried out a few on wood before engaging in the stone cutting. 
Some stone types were not as good as others, lots had to do with the density. The cnc cutter was set to engraving.


Step 1: Drawing the Centipede.

The centipede is a generative design done in NodeBox 3, a free, open source functional programming language which comes with a modular interface. For more information on the software, go to their documentation files or download it.

The centipede was made by using mainly core nodes and functionality. The math behind the skeleton was done over an additional python file that renders Voronoi segmentation. The skeleton itself is the medial axis path derived from that segmentation. NodeBox allows you to add bits of code to the system. 

The generative principle allows to produce an infinite amount of different centipedes.

Step 2: Construct the Arm.

It all starts with a number..

The principle behind the arm is fairly simple. I want to be able to define the amount of segments on an arm and have the possibility to rotate each segment separately.

* Create a number node and a range node. Send number1 to End port of range1.
* Create a reverse node to reverse the range. Send range1 to reverse1.
* Create a rect node. Send reverse1 to width and height port of rect1.
* Create an align node to align each rectangle to the bottom of the origin point. Set VAlign to Bottom.


Making the stack.

NodeBox made a lot of shapes on top of each other so the next step is to create a list of points on which each rectangle will be placed upon. The reverse node makes sure that the first rect is the biggest and the last one the smallest. I want to stack them as a pile so i will have to make the sum of all previous elements to calculate the position of an element.

* Create a slice node. Connect reverse1 to it's list port.
* Create a sum node. Connect slice1 to it.


Select both nodes and make  a subnetwork out of them. Call it something like running totals or accumulation. Go back in the network and publish the size port of slice1. Render sum1 and go back to the root network.

* Connect range1 to the published size port of accumulation.

This returns the running totals which we can use to create a set of points.

* Create a negate node and send accumulation to it.
* Create a make point node and connect negate1 to Y.


This returns a list of points going upwards and getting closer to each other near the end.

* Create a translate node. Connect align1 to Shape and make point1 to translate.

Now you should see the stack of rectangles. Note that there is no space between them.

Transformations.

Let's add a few nodes so we can rotate  each segment a bit and rotate, translate this new object as a whole.

* Create a rotate node and connect translate1 to it.
* Create a multiply node. Connect range1 to value1. Connect multiply1 to rotate1.


This rotates each segment a bit. Change the second value of multiply1 to see the difference. Negative numbers return a rotation to the left, positive one a rotation to the right.

* Create another rotate node  to rotate the whole object. Connect rotate1 to shape.
* Create a translate node. Connect rotate2 to shape.
* Create a group node and send translate2 to it
.

The arm node.

* Select all nodes and create a subnetwork from them. Call it arm.

Go back in the arm network and publish:

* value of number1. Call it segments
* translate of translate2. Call it location
* value2 of multiply1. Call it segment rotation
* angle of rotate2. Call it angle.


Go back to the root network.

A screenshot of the accumulation network and the arm network is at the top of this step.

Step 3: Construct the Segment.

A circle and a couple of points.

The root network now contains an arm node with 4 parameters/ports. The idea is to create a body segment which contains two arms and a shape holding them together. This step will lead you through that process.

A first step is to create a body segment and define two points on it. These points will hold the arms attached to the body and will be useful for rotating them in a natural way.

* Create an ellipse node and set its dimensions to 35*45.
* Create a resample node. Set Method to 'by amount' and points to 2. Send ellipse1 to it.
* Create a point node. Send resample1 to it.


Use the points.

We can connect use these points as translate points for the arm node. That will create 2 arms because we are dealing with 2 points.

* Connect point1 to Location of arm.

We can also use them to rotate the arms so the grow in a direction away from the origin.

* Create an angle node. Send point1 to Value2.
* Create a subtract node. Send angle1 to Value1 and set Value2 to -90.
* Connect subtract1 to Angle of the arm node.


The arms are in an opposite direction now.

Glue together.

Next is to make the body segment appear as one object which can be replaced and rotated.

* Create a combine node. Send ellipse1 to List1 and arm to List2.
* Create a rotate node. Send combine1 to Shape.
* Create a scale node. Send rotate1 to Shape.
* Create a translate node. Send scale1 to Shape.
* Create a group node. Send translate1 to it.


You can play around with the parameters for rotating, scaling and translating the segment as a whole. The idea is to create a subnetwork again and to publish these and a few more parameters.

The segment node.

* Select all nodes and create a subnetwork from them. Call it segment.

Go back in the segment network and publish:

* translate of translate1. Call it location.
* angle of angle1. Call it angle.
* segments of arm. Call it segments.
* segment rotation of arm. Call it segment rotation.
* scale of scale1. Call it scale.


Go back to the root network.

It contains 1 segment node which can be varied in a number of ways by controlling the parameters for number of segments, angle for each segment, rotation, translate, scaling etc.  Try it out.

A screenshot of the segment network is at the top of this step.

Step 4: Construct the Body.

Drawing the body.

A freehand node  was used to be able to draw the basic shape of the centipede myself but it could be any shape. Key is to create points on this shape and use them as translate point of the segment.

* Create a freehand node. Draw a simple curled line in the viewer.
* Create a resample node. Set Method to 'by length' and Length to 50. Send freehand1 to Shape.
* Create a point node. Connect resample1 to it.
* Connect this point node to Translate of the segment node.


You should see a segment on each point of the point node. They are poorly rotated and all of the same dimension.

Variations for the arms.

We held a parameter for changing the segment rotation for each arm. This step will address this to create different rotations for each arm. We will also change the rotation of each arm based on the previous one.

* Create a count node. Connect point1 to it.
* Create a random numbers node. Connect count1 to Amount. Set Min and Max to -1 and 1.
* Connect random numbers1 to Segment_rotation of the segment node.
* Create a shift node. Connect point1 to it. Set Amount to 1.
* Create an angle node. Connect point1 to Point1 and shift1 to Point2.
* Create a subtract node. Connect angle1 to Value1. Set Value2 to 90.
* Connect subtract to Angle of the segment node.


Variations for the segment.

There are still two open ports on the segment node. The first one - segments - which contains the number of segments for each arm can be set to 16.

The second however is more interesting. It scales down the segment. The idea is to create a wave which will make it possible to have variations in thickness. From large to small back to large or that procedure a few time after each other.

* Create a range node. Connect count1 to End. For the moment leave Step at 1.
* Create a wave node. Connect range1 to Frame. Set Min, Max to 5 and 10. Set speed to 60.
* Create a multiply node. Connect wave1 to Value1. Set Value2 to 25.
* Create a abs node. Connect multiply1 to it.
* Create a make point node. Connect abs1 to both X and Y.
* Connect make_point1 to Scale of the segment node.


This network is still playable. Change Seed of random numbers1 to change the segment rotation.  Or  clear the path in the freehand node to draw a new one.

A screenshot of the network is at the top of this step.

Step 5: Construct Head and Tail.

This is the segment node right?

Constructing the head and tail are pretty similar to the construction done in the segment node. The difference is that we will create more than two points to connect an arm node to. And yes: the arm node will be used again…

* Create an ellipse node. Set its dimensions to 180 * 200.
* Create a resample node. Set Method to 'by length' and set Length to 40. Connect ellipse1 to Shape.
* Create a point node. Connect resample1 to it.


Select the top points.

I want to select the points on the top side of the shape. NodeBox allows a few sorting procedure, one of them being a sort of the Y axis.

* Create a (grey) sort node. Set Order by to Y. Connect point1 to it.
* Create a slice node. Set Amount to 5 and connect sort1 to List.
* Copy the arm node in the segment network. You can copy paste nodes by selecting them and using the shortcuts cmd-c / cmd-v | ctrl-c / ctrl-v
* Connect slice1 to Location of this arm node.


I want to use the points in the same way as done before in the segment network: as a location for an arm and as a way to rotate them away from the origin and as a number for the segment rotation.

* Create an angle node. Connect slice1 to Point2. Set Point1 to the origin (0,0).
* Create a subtract node. Connect angle1 to Value1 and set Value2 to -90.
* Connect subtract1 to Angle of the arm node.

* Create a count node. Connect slice1 to it.
* Create a random numbers node. Connect count1 to Amount. Set min / max to -0.5, 0.5.
* Connect random_numbers1 to Segment rotation of the arm node.


Get it together and create a subnet.

This step will get all shapes together and making it controllable over rotation and translation.

* Create a combine node. Connect ellipse1 to List1 and arm to List2.
* Create a rotate node. Connect combine1 to Shape.
* Create a translate node. Connect rotate1 to Shape.


Select all nodes and create a subnet. Call it head.
Go back in it's children and publish:

* translate of translate1, call it location
* angle of rotate1, call it angle
* segments of arm, call it segments
* seed of random_numbers1, call it segments seed.


Transformations.

Next step is to place the head on the first point of the body and rotate it based on the angle compared to the second point.

* Create a first node. Connect point1 node to it.
* Connect this first1 to the Location port of the head node.
* Create a second node. Connect point1 node to it too.
* Create a angle node. Connect first1 to Point1 port and second1 to Point2 port.
* Create a subtract node. Connect angle2 to Value1 and set Value2 to 90.
* Connect subtract2 to the Angle port of the head node.

The tail is more of the same.

Take a copy of the head node and call it tail. Go in the subnetwork and change a few parameters:

* Less points on the size of slice1. Meaning it has less arms.
* Change the size of the basic ellipse. The tail is smaller as the head.
* Invent a parameter yourself and implement it.
* Go back to the root network.

For the positioning and rotating part it is not possible to use the first and second point. In stead we will shift the original point list and use a last node twice: a first time on the list and a second time on the shifted list.

* Create a shift node. Connect point1 to List. Set Amount to -1.
* Create two last node. Connect point1 to the first one and shift1 to the second one.
* Connect last1 to the Location port of the tail node.
* Create an angle node. Connect last1 to Point1 and last2 to Point2.
* Create a subtract node. Connect angle3 to Value1 and set Value2 to 90.
* Connect subtract3 to the Angle port of tail.

All layers combined.

* Create a combine node.
* Connect segment to List1.
* Connect head to List2.
* Connect tail to List3.
* Render it.
* Create a group node. Connect combine1 to it.
* Create an align node. Set Halign and Valign to Middle / Centre.

The two final nodes are to group the shape into one geometry and to align it to origin.

Screenshots of the networks are at the top of this step.

Step 6: Construct the Skeleton.

Medial Axis.

The skeleton is based on the Medial Axis. I used an existing Voronoi diagram calculator. It was originally written by Steven Fortune at Bell Lab and translated to Python by Bill Simons. The Voronoi diagram is the base for creating the Medial Axis structure.

The python file (triangulate.py) is included and reference to the creators is at the top op the script. It can be used into NodeBox after adding a function which will be called for by a node. The medial axis is based on the Voronoi diagram of a set on points in that sense that all points within the shape (on which the diagram was calculated) are kept. All the ones outside the shape are not.

I added an example on a green maple leaf which shows the different results for Voronoi and Medial Axis based on a shape (original).

Its a good practice to think in advance about the arguments which can be send to an 'outside' function. In this case i can send the shape (the complete centipede) and a list of points from that shape (from a resample node f.i).
Below is the function i wrote to call for in NodeBox. This function is the last one in the triangulate.py script.


def medianAxis(shape,points):
    from nodebox.graphics import Point
    sl = SiteList(points)
    c = Context()
    voronoi(sl, c)
    all = []
    for l, v1, v2 in c.edges:
        if v1 == -1 or v2 == -1: continue
        x1, y1 = c.vertices[v1]
        x2, y2 = c.vertices[v2]
        p1 = Point(x1,y1)
        p2 = Point(x2,y2)
        if not shape.contains(x1,y1): continue
        if not shape.contains(x2,y2): continue

        psub = [Point(p1.x,p1.y),Point(p2.x,p2.y)]
        for i,point in enumerate(psub):
            all.append(point)

    return all


What comes out is a list points which can be connected as pairs (1 -2, 3 -4 , 5-6, …) So far for the coding part.

Using the function in NodeBox.

Now that i have a function to call for, it can be implemented in NodeBox. First we need to import the python file into the program.

* Go to File >> Code Libraries. It will open a new small window with in the upper right corner a '+' sign.
* Press '+' and select Python from the Python / Closure option. Browse to triangulate.py.
* If you can see a reference to it you can close the window.


The python file is imported now and the functions in it can be called for from a node.

Functions and NB3, how does it work.

Every node calls for a function, you can find out which one by selecting a node and then go to the metadata tab on top of the port/parameters window. It will open a new window with specifications of the node and its ports. If you go to 'Settings' you can find out what function it refers to. In the case of a rectangle it will say: corevector/rect, meaning that it will call the rect function in the corevector file. To call the medianAxis function in triangulate.py i will have to call triangulate/medianAxis.

I need a node where i can send a shape and a list of points to and which returns a list (of points again). We can use the centroid node to start with since it imports a shape and calculates the centroid point. We're already halfway there..

* Create a centroid node and renamed it Median Axis.
* Select the node and go to the Metadata tab.
* Go to Node >> Settings. Enter 'triangulate/medianAxis' in the 'Function' option.
* In the same window change 'Output Range' from Value to List by using the menu.
* You could also change the image of the node. Its the icon for the node in the network.


We still need a new port. For the moment the node can only receive a shape.

* In the Metadata window: click the '+' button on the lower left corner.
* Enter a port name: call it 'points'.
* Select 'point' from the Type menu and press 'OK'.


We are still in the Metadata window. You should be able to see the points port in addition to the shape port.

* Select it on the left side of the screen. (it turns dark)
* Change 'Range' on the right side from Value to List.
* Press OK.


Using the new node.

The Medial Axis node needs two inputs as described above. We already have the shape (the one in align1), we need to extract a list of points from it.

* Create a resample node. Connect align1 to Shape. Set Method to 'by length' and Length to 20.0.
* Create a point node. Connect resample2 to it. Here is the list of points.


Now we make the connections to the Medial Axis node.

* Connect align1 to Shape.
* Connect resample2 to Points.
* Render the Medial Axis node.


What comes out is a list of points which can be connected as pairs. The idea is that we will count the number of points to know how many pairs can be made. The pairs themselves will be connected to each over a tiny subnetwork.

* Create a count node.
* Create a range node. Send the previous count node to End and set Step to 2.
* Create a slice node. Connect Medial Axis to List and connect range7 to Startindex.
* Create a connect node. Connect slice1 to Points. Render it.

* Select slice1 and connect1 and create a subnetwork of it.
* Call it 'Medial Axis Edge'.
* Render it.


A screenshot of the network is at the top of this step.

Step 7: Creating Depth Over Color.

Calculating the distance between two median axis points.

For the moment the Median Axis Edge node contains a slice node and a connect node. The connect node connect the two points (a slice of 2) by a line. The idea is that we will give the line a color based on the distance between the same points. In  short: longer lines will have a different color (in this case lighter) than short one. This will give the skeleton some depth. The shorter lines are those connecting the points of the arm segments together and i want them to be lower than the lines connecting the torso and head segments together.

* Create two slice nodes. Set both Sizes to 1 and Set the Start_index of the first one to 0 and of the second one to 1. Connect the slice node that was already in the network to both of them List port.
* Create a distance node. Connect slice2 to Point1 and slice3 to Point2.
* Create a divide node. Connect distance1 to Value1 and set Value2 to 8. We might want to change that later.


Now we will use these distance values to create a gray color.

* Create a gray color node. Connect divide1 to Gray. Set Range to 4. (Changeable)
* Create a colorize node. Connect the connect1 node to Shape. Connect divide1 to Strokewidth and gray_color1 to Stroke.
* Render is and go back to the root network.


A screenshot of the network is at the top of this step.


Exporting the design.

Go to File >> Export and export is as GiveMeAName.png
I added an other rect node as background. The color set to black.

Engraving is time consuming.

I wrote a simple visualizer in Processing as to see what the result might like beforehand. Engraving it took mostly a few hours (engraving into mdf took about 10% of the time to engrave in stone.) and this bit of code made it possible to have a quick look. I'm shure there are faster ways in doing it but it works well, i can rotate the model etc. The code is below: centipreview.zip


Downloads:

People that don't want to build this network themselves can download the nodebox file below. Download Nodebox3 at their website.
You have to manually add the triangulate.py library because that seems to be a problem sometimes. It should open but will not calculate the median axis. The instructions on how to add a python library to nodebox are at step 6.




UP! Contest

Participated in the
UP! Contest

Instructables Design Competition

Participated in the
Instructables Design Competition