# teaching machines

## FabLearn 2020 Demo

A collaborator and I had a short paper on programmatic fabrication accepted at FabLearn 2020, and we’re presenting that work in a demo session on Zoom next weekend. Herein I walk through some of the example programs that we plan to work through with our visitors.

### Button

Let’s start with a practical example—one inspired by a real-life incident. Suppose you have a pair of pants whose button has popped off somewhere in the antics of your day. You look around home for a spare, but none can be found. You do, however, have fabrication tools, and you decide to make your own button.

Probably you open up Illustrator or Inkscape or GravitPro. These tools help create a design file quickly, but you are intellectually dissatisfied because the design is brittle. If you tweak a size or a position of one of the holes, you must repeat your work for the three other holes.

You would benefit from an editor that supports parametric design—like Twoville, the one we are presenting today. Twoville lets you create SVG design files using code. Our first draft of a Twoville program to make our button might look something like this:



with circle()
center = [50, 50]
stroke.color = :black
stroke.size = 1
opacity = 0

with circle()
center = [40, 40]
stroke.color = :black
stroke.size = 1
opacity = 0

with circle()
center = [60, 60]
stroke.color = :black
stroke.size = 1
opacity = 0

with circle()
center = [40, 60]
stroke.color = :black
stroke.size = 1
opacity = 0

with circle()
center = [60, 40]
stroke.color = :black
stroke.size = 1
opacity = 0

var twovilleDiv = jQuery('#twoville_button-brittle');
twovilleDiv.closest('pre').replaceWith(twovilleDiv);
document.getElementById('twoville_form_button-brittle').submit();

with circle()
center = [50, 50]
stroke.color = :black
stroke.size = 1
opacity = 0

with circle()
center = [40, 40]
stroke.color = :black
stroke.size = 1
opacity = 0

with circle()
center = [60, 60]
stroke.color = :black
stroke.size = 1
opacity = 0

with circle()
center = [40, 60]
stroke.color = :black
stroke.size = 1
opacity = 0

with circle()
center = [60, 40]
stroke.color = :black
stroke.size = 1
opacity = 0



This is code, yeah, but it’s just as brittle as a non-parametric design file. And it’s very verbose. We can improve upon it with a little abstraction. Let’s turn the circle drawing code into a reusable routine named ring:



with circle()
center = center
stroke.color = :black
stroke.size = 1
opacity = 0

ring([50, 50], 50)
ring([40, 40], 6)
ring([60, 60], 6)
ring([60, 40], 6)
ring([40, 60], 6)

var twovilleDiv = jQuery('#twoville_button-abstraction');
twovilleDiv.closest('pre').replaceWith(twovilleDiv);
document.getElementById('twoville_form_button-abstraction').submit();

with circle()
center = center
stroke.color = :black
stroke.size = 1
opacity = 0

ring([50, 50], 50)
ring([40, 40], 6)
ring([60, 60], 6)
ring([60, 40], 6)
ring([40, 60], 6)



The verbosity is gone, but the design is still brittle because the parametric relationship of the holes is not established. Let’s establish some parameters and relate them with some arithmetic:



with circle()
center = center
stroke.color = :black
stroke.size = 1
opacity = 0

middle = [50, 50]

ring(middle + [-1, -1] * gap, smallRadius)
ring(middle + [ 1,  1] * gap, smallRadius)
ring(middle + [-1,  1] * gap, smallRadius)
ring(middle + [ 1, -1] * gap, smallRadius)

var twovilleDiv = jQuery('#twoville_button-parametric');
twovilleDiv.closest('pre').replaceWith(twovilleDiv);
document.getElementById('twoville_form_button-parametric').submit();

with circle()
center = center
stroke.color = :black
stroke.size = 1
opacity = 0

middle = [50, 50]

ring(middle + [-1, -1] * gap, smallRadius)
ring(middle + [ 1,  1] * gap, smallRadius)
ring(middle + [-1,  1] * gap, smallRadius)
ring(middle + [ 1, -1] * gap, smallRadius)



You tweak gap and smallRadius to alter all four button holes at once. The buttons always stay centered. You are satisfied. You export the SVG, load it up in your laser cutter, extract the cut button, and find some needle and thread. But what if you can’t find those?

### Moon

For our next example, let’s make a crescent moon. The best learning starts, in my opinion, with graph paper. If I were working with students on this exercise, I’d ask them to plot a few different moons and identify some properties of the ones they like liked best. These properties include:

• What are the coordinates of the two points?
• How many degrees are in the small circle?
• How many degrees are in the big circle?

With rough values worked out for these properties, we can use the path command to trace out the perimeter of the crescent moon:



with path()
stroke.color = :black
stroke.size = 2
color = :yellow
with jump()
position = [10, 20]
with arc()
position = [10, 80]
degrees = 280
with arc()
position = [10, 20]
degrees = -240

var twovilleDiv = jQuery('#twoville_moon');
twovilleDiv.closest('pre').replaceWith(twovilleDiv);
document.getElementById('twoville_form_moon').submit();

with path()
stroke.color = :black
stroke.size = 2
color = :yellow
with jump()
position = [10, 20]
with arc()
position = [10, 80]
degrees = 280
with arc()
position = [10, 20]
degrees = -240



### Star

The previous examples demonstrate abstraction and parametricity. Coding also facilitates repetition through looping. Let’s use a loop to plot a star. In an educational setting, I would ask my students to draw a star on some polar graph paper and then to identify the angles and radii of each of the ten points.

Based on their answers, we would write a loop that iterates 5 times, plotting an external and an internal point on each iteration. Between iterations it jumps 72 degrees. The loop might look something like this:



for i to 360 by 72
print([50, i])
print([20, i + 36])

var twovilleDiv = jQuery('#twoville_star-loop');
twovilleDiv.closest('pre').replaceWith(twovilleDiv);
document.getElementById('twoville_form_star-loop').submit();

for i to 360 by 72
print([50, i])
print([20, i + 36])



We use this loop to produce the vertices of a polygon, turning the polar coordinates into Cartesian.



with viewport
center = :zero2

with ungon()
rounding = 0.1
color = :cornflower
for i to 360 by 72
with vertex()
position = [50, i].toCartesian()
with vertex()
position = [20, i + 36].toCartesian()

var twovilleDiv = jQuery('#twoville_star');
twovilleDiv.closest('pre').replaceWith(twovilleDiv);
document.getElementById('twoville_form_star').submit();

with viewport
center = :zero2

with ungon()
rounding = 0.1
color = :cornflower
for i to 360 by 72
with vertex()
position = [50, i].toCartesian()
with vertex()
position = [20, i + 36].toCartesian()



An ungon is a polygon that has been rounded off.

### Raindrop

Code brings certain advantages, but the design process is not always so algorithmic. Ideally, our editors should support both parametric and aesthetic design. We are working on that in Twoville. Let’s demonstrate its hybridity as we model a raindrop.

Again, I would start by sketching on graph paper and getting a rough feel for the coordinates of the points. The drop is symmetric, and we can save ourselves some work by only thinking about one half and letting Twoville mirror the rest. Really we just have two salient points: the one at the top of the drop and one on the bulb.

There aren’t any straight lines connecting these points. Rather, we will need a Bezier curve, with two control points shaping the path between the points.

Putting all these ideas together, we end up with something like this:



with path()
closed = false
color = [0.3, 0.3, 1]
with jump()
position = [50, 99]
with cubic()
position = [25, 20]
control1 = [44, 51]
control2 = [20, 45]
with mirror()
position = [50, 0]
axis = :up

var twovilleDiv = jQuery('#twoville_raindrop');
twovilleDiv.closest('pre').replaceWith(twovilleDiv);
document.getElementById('twoville_form_raindrop').submit();

with path()
closed = false
color = [0.3, 0.3, 1]
with jump()
position = [50, 99]
with cubic()
position = [25, 20]
control1 = [44, 51]
control2 = [20, 45]
with mirror()
position = [50, 0]
axis = :up