Welcome to MakerHome




We've completed our yearlong print-a-day project!
All new material is now at Hacktastic: www.mathgrrl.com


Tuesday, April 22, 2014

Day 239 - Six-piece ball puzzle

Today we printed althepal's Six-piece soccer ball puzzle from Thingiverse. I have a lot of puzzles like these in my office at work, and people often take them apart and then don't have enough time to figure out how put them back together again. The puzzles can be a pain for me to reassemble and don't really display well as pieces. Fortunately, with 3D-printed puzzles you can just print an extra copy for people to play with, and leave a display copy intact! This is a classic puzzle that is especially cool because it is made up of six identical pieces:


Thingiverse link: http://www.thingiverse.com/make:75005

Settings: Printed on a Replicator 2 on .3mm/low settings at 50% of original scale.

Technical notes, OpenSCAD flavor: althepal's code is simple and elegant, and I've rearranged it and added comments below so you can see how it works. After the resolution and scaling parameters are set, the object itself is made by removing notches from a long cuboid shape and then intersecting with a sphere to make a rounded exterior. To see how this works, try compiling this in OpenSCAD and adding a "#" character in front of each cube and sphere command, one at a time. This will cause the corresponding cube or sphere to be highlighted in red so that you can see how it fits into the object.

// code from althepal with comments added by mathgrrl
// http://www.thingiverse.com/thing:219502/#files

// resolution
$fn = 100;

// radius of enclosing sphere
r1 = 30;

// side length of cuboid for pieces
s1 = r1 * 13.3 / 20;

// the puzzle piece (print six times)
intersection(){
// start with a long cuboid with notches cut out
difference(){
// start with a long cuboid
translate([0,-100, 0])
cube([s1,200, s1], center=false);
// remove notch with rotated cuboid shape
translate([s1, s1 * cos(45) + 0.5, s1])
rotate([45,45,0])
cube([200, s1, s1], center=true);
// remove another notch with rotated cuboid shape
translate([s1, -s1 * cos(45) - 0.5, s1])
rotate([45,45,0])
cube([200, s1, s1], center=true);
}
// intersect with sphere to cut off and make rounded
translate([r1 * cos(45),0, r1 * cos(45)])
sphere(r = r1);
}

Stuff to change next time: Although the 50% scale model printed perfectly, it is very difficult to assemble.  Although it is very cute when tiny, I would recommend printing this model at original size.

Monday, April 21, 2014

Day 238 - Shellmaker 2

More shells! This time we printed a model to match an example from the Khan Academy, so that you can go there and learn how to set up the definite integral for this volume with shells if you have a mind to. The volume is obtained by rotating the region between y=(x-1)(x-3)^2 and the x-axis on [1,3] around the y-axis. We made an 8-shell version and a 16-shell version, plus an "exact" version made by using 100 shells and zero clearance between shells.


Here are all the pieces; I thought the 16-shell model would be difficult to reassemble after taking apart, but everything drops easily back into place without any fuss:


Thingiverse link: http://www.thingiverse.com/thing:306052

Settings: Printed on a Replicator 2 with .3mm/low settings.

Technical notes, OpenSCAD flavor: The code for the 8-shell model is below.

// mathgrrl shellmaker

// shell approximation of a solid of revolution

////////////////////////////////////////////////////////////
// parameters //////////////////////////////////////////////

// facet resolution
$fn = 48;

////////////////////////////////////////////////////////////
// function and renders ////////////////////////////////////

// define the function
function thefunction(x) = (x-3)*(x-3)*(x-1); 

// make the shell model
shellmaker(a=1,b=3,n=8,clearance=.8,width=60); 

////////////////////////////////////////////////////////////
// module for midpoint shells //////////////////////////////

module shellmaker(a,b,n,clearance,width){
// delta x depends on a, b, n, and clearance
delta = (b-a)/n;

// scale depends on a, b, and width
scale = .5*(width/(b-a)); 

// shift for making clean holes
shift = .5*1;

// innermost shell does not have clearance on inside
difference(){
// large function cylinder to outside of interval
cylinder(
r = scale*(a + 1*delta) - clearance/2,
h = scale*thefunction(((a + 0*delta)+(a + 1*delta))/2)
);
// small function cylinder to inside of interval
// when a=0 this cylinder is degenerate
// when a>0 we take out the whole center piece with max height
translate([0,0,-shift/2]) 
cylinder(
r = scale*(a + 0*delta),
h = 2*scale + shift
);
}
// middle shells have clearance on both sides
for (k = [2 : n-1]){
// construct the shell with a difference of cylinders
difference(){
// large function cylinder to outside of interval
cylinder(
r = scale*(a + k*delta) - clearance/2,
h = scale*thefunction(((a + (k-1)*delta)+(a + k*delta))/2)
);
// small function cylinder to inside of interval
translate([0,0,-shift/2]) 
cylinder(
r = scale*(a + (k-1)*delta) + clearance/2,
h = scale*thefunction(((a + (k-1)*delta)+(a + k*delta))/2) + shift
);
}
}
// outermost shell does not have clearance on outside
difference(){
// large function cylinder to outside of interval
cylinder(
r = scale*(a + n*delta),
h = scale*thefunction(((a + (n-1)*delta)+(a + n*delta))/2)
);
// small function cylinder to inside of interval
translate([0,0,-shift/2]) 
cylinder(
r = scale*(a + (n-1)*delta) + clearance/2,
h = scale*thefunction(((a + (n-1)*delta)+(a + n*delta))/2) + shift
);
}
}

The 16-shell model and "exact" model from today can also be rendered from this code, with:

shellmaker(a=1,b=3,n=16,clearance=1,width=60);
shellmaker(a=1,b=3,n=100,clearance=0,width=60);

Yesterdays' 8-shell model and its "exact" counterpart were made with the same code applied to a different function:

function thefunction(x) = 4-x*x;
shellmaker(a=0,b=2,n=8,clearance=.8,width=50); 
shellmaker(a=0,b=2,n=100,clearance=0,width=50);

Stuff to change: The increase in clearance to 1mm on the 16-shell model was too much, and the model is too loose. But with less clearance the model tended to run together and adjacent shells got stuck. Next time I would reduce the clearance and print the odd shells separately from the even ones to fix the sticking problem.

Sunday, April 20, 2014

Day 237 - Shellmaker

Tomorrow in calculus we'll be discussing how to approximate volumes of solids of revolution using "shells". This is traditionally a very difficult concept for students to visualize, so having a physical model is really helpful. The trouble is, no physical models seem to exist. We've all talked about "onion skins" or brought in Quarto pieces or other shell-like objects, or even cut cakes into shells for illustration, but those things are just simplified versions of the situation. Our volumes-by-shells model on Day 144 was nice, but made in Tinkercad and thus not very customizable. Today we upgrade to an OpenSCAD model. The model shown on the left is the solid obtained by revolving the region between the graph of y=4-x^2 and the x-axis on [0,2] around the y-axis, and the model on the right is an approximation of that solid using eight shells.


The shell model comes apart into its individual shells:



Settings: Printed on a Replicator 2 with .3mm/low default settings.

Technical notes, math flavor: (Apologies for the bad math typesetting is what is to follow; I haven't had luck with WordPress and math formatting or embedding LaTeX code.) These heights of these shells were determined using the midpoints of eight subintervals from x=0 to x=2. The reason we use the midpoints is that it allows us to use a very nice formula for computing the volumes of the shells that will allow us to construct a Riemann Sum, and thus a definite integral, to express the exact volume. Suppose we've subdivided [0,2] into eight subintervals of the form [x_{k-1},x_k]. Then each shell is just a really tall washer with inner radius x_{k-1}, outer radius x_k, and height given by the function curve somehow. Let's take the height at the midpoint m_k = (x_{k-1}+x_k)/2 of the subinterval. Then our kth shell has volume
V_k = pi * (x_k)^2 * f(m_k) - pi * (x_{k-1})^2 * f(m_k).

By factoring out like terms we can turn this into:

V_k = pi * f(m_k) [ (x_k)^2 -(x_{k-1})^2].

Now using the fact that a^2-b^2 = (a+b)(a-b), together with the definition of m_k and DeltaX = x_k - x_{k-1} and a bit of algebra, we can write this volume as:

V_k = 2 * pi * m_k * f(m_k) * DeltaX.

This formula isn't any easier to use in practice, say if we were to actually calculate and add up the volumes of the eight shells pictured above; however the form of this kth volume expression - and in particular the presence of the DeltaX in the expression - allows us to construct a definite integral that represents the exact volume of the solid of revolution.

Technical notes, syllabus flavor: Calculus profs will notice that late in the semester is an odd time to talk about volumes with shells; if you're teaching Calc 2 then that's usually in mid-semester. Here at JMU we teach a two-semester Calculus I with Integrated Precalculus course that goes about a third of the way into Calc 2, so we get through Riemann sums, the Fundamental Theorem of Calculus, techniques of integration, and volumes/applications by the end of the second semester. 

Saturday, April 19, 2014

Day 236 - Conference swag x 8

Today, eight models we've seen before, optimized for printing small and fast. We'll be printing one of these tiny models for each person that comes to the Fall Meeting of the Maryland/DC/Virginia section of the Mathematical Association of America, which will be hosted here at JMU next week.

Friday, April 18, 2014

Day 235 - Friday Fail: Time Edition

There is never enough time. Over the past few weeks I have failed miserably at time, specifically at keeping up with this blog on a daily basis. I've been "a few days behind" - sometimes an entire week! - for a long time now; always printing and working every day, but always behind. But it's not like I haven't been printing things. For example, here is some of what I printed in the past few days, making giveaways for a conference (more on that tomorrow):


But none of that made a blog post for me. However things in Life are settling down now and as of today I'm caught up - or at least one day short, with it being Saturday while I post this Friday Fail - so I am going to try to keep up with time from here on out. Time, by Grabthar's hammer, I will catch you!

UPDATE 4/27/14: Surprise, I'm still behind! Just after catching up I started getting sick and I'm just starting to crawl out of that hole now. I swear I'm never going to promise to do something EVERY DAY FOR A YEAR again. On the other hand I always catch up in the end so maybe it is okay. Right?

Thursday, April 17, 2014

Day 234 - S(5,2,(1,1,-1,1))

This semester five students and I have been 3D-printing the knots through 7 crossings in both standard and special configurations, as part of our MATH 297 - Knot Theory Research and 3D Printing course in the JMU 3-SPACE classroom. One of the knots that fell to me was 7_6, which happens to be the spiral knot S(5,2,(1,1,-1,1)). This means that it can be represented as a braid that has 5 strands, one of which crosses the others in an "over, over, under, over" pattern for a repeat of 2 times; or in other words, that the knot can be obtained with braid word (abCd)^2. All that is nice, but for the purposes of this post all that matters is that the knot 7_6 can look all spirally like it does in this picture:


Thingiverse link: http://www.thingiverse.com/thing:304206

Settings: Printed on a Replicator 2 with our custom MakerWare support profile for knots from Day 110. The model has so many long pieces that if you orient vertically as in the picture below, you can get away with almost no support at all. This is the least amount of support I have ever had for any knot!


Technical notes, TopMod flavor: I am very excited to report that I actually constructed this knot by hand, one arc at a time, in TopMod. Although I did make the knot from Day 137 with TopMod, I was not trying to make that particular knot; this is the first time that I had a certain knot in mind and then constructed it in TopMod, rather than with equations or data. The reason I did that is that I don't know the equations or have any data for the spiral conformation of 7_6; I just knew what it was supposed to look like! In case anyone out there is interested in using TopMod to construct knots, here is a step-by-step breakdown of what I did, starting with a 2-dimensional projection of the conformation I wanted to build:
  1. Open TopMod and click on the cube to put down one box.
  2. Select a face of the box and use the Cubical Extrude button to extend into a connected line of boxes (I used x50 to get a line that was 50 blocks long). In the 7_6 spiral case I knew that all of the crossings would end up on one line so that was all I needed to get started. For other knots and other conformations you'll have to use Cubical Extrude to build a branching framework to build from.
  3. Use Selection/Edge Ring to select boxes to delete from your row. I deleted two boxes between every one box that I wanted to keep, plus a few more for the center. After deleting I had six separated boxes in a line, followed by a gap, followed by six more separated boxes.
  4. Select the tops of all your boxes and Cubical Extrude x8 to get some height. You will be connecting boxes at various heights to make the arcs between each crossing in your knot projection.
  5. Make the arcs of your knot by connecting faces of boxes as desired with the Add Handle (Shape Interpolation) Mode button. As part of this you will have to adjust the number of segments (for a finer or coarser look) and the weight (to determine the curvature of the connecting handle). When you select two faces to connect with a handle you will also select a corner on each face, which will determine how twisty the connecting handle is. You can also manually set the number of twists if desired.
  6. Once all your arcs are in place, delete any unneeded cubes using Selection/Edge Ring and the wonderful tool "=", which expands a selection outward one step at a time.
  7. You should now have a choppy-looking curve in the desired conformation of your knot. Use Remeshing/Corner Cutting twice to get a smoother knot model.
  8. Export to STL; this may take a minute because TopMod will have to further refine your mesh to be triangular as it is performing this export. 
Technical notes, Blender flavor: The knot produced by the method above had fairly thin strands, so to thicken them up I used Blender. Although one of our lab students wrote the excellent post Adding Thickness to your STL file at the MakerLab blog last December, I had not used this method until today. I also consulted the Interface and Navigation video at BlenderCookie to learn how to move around: scroll wheel to zoom, middle-click and drag to rotate view, and shift-middle-click and drag to pan view.
  1. A new "starting document" in Blender will contain a camera, light, and cube, which I wasn't sure what to do with. For the moment I learned how to hide them: in the upper right "Scenes" box, deselect the eye, arrow, and camera icons for all three.
  2. File/Import/STL and choose the file you want to open. You may have to zoom out a lot to see your model. 
  3. Under the "Scenes" box in the right column, choose the "wrench" icon to get to Object Modifiers. You may have to expand the width of the right column to see the wrench icon.
  4. Choose Add Modifier/Solidify with Thick changed to 2 and Offset to 0. Pressing return after setting the offset performs the action so you do not need to click "Apply". 
  5. Export your new, thickened STL file. 

Wednesday, April 16, 2014

Day 233 - Bedmaker

We don't have one bed of every size in our house but it seemed like a good idea to print all of the sizes; here we have Twin, Full, Queen, and King:


Thingiverse link: http://www.thingiverse.com/make:78011

Settings: Replicator 2 on .3mm/low, as usual.

Technical notes, OpenSCAD flavor: Picking up where we left off in yesterday's code, we're back to larger spheres at the corners to make our beds seem comfy. Nothing too interesting here, except that it is good to note that the height parameter in this code does not include the height of the pillow.

/////////////////////////////////////////////////////////////
// module for making beds ///////////////////////////////////

module bed(depth,length,height){
// mattress
hull(){
translate(s*[0,0,0]) sphere(r);
translate(s*[depth,0,0]) sphere(r);
translate(s*[depth,length,0]) sphere(r);
translate(s*[0,length,0]) sphere(r);
translate(s*[0,length,height]) sphere(r);
translate(s*[depth,length,height]) sphere(r);
translate(s*[depth,0,height]) sphere(r);
translate(s*[0,0,height]) sphere(r);
}
// pillow
hull(){
translate(s*[0,0,(5/4)*height]) sphere(r);
translate(s*[0,(1/8)*length,(5/4)*height]) sphere(r);
translate(s*[depth,(1/8)*length,(5/4)*height]) sphere(r);
translate(s*[depth,0,(5/4)*height]) sphere(r);
translate(s*[0,0,0]) sphere(r);
translate(s*[0,(1/8)*length,0]) sphere(r);
translate(s*[depth,(1/8)*length,0]) sphere(r);
translate(s*[depth,0,0]) sphere(r);
}
}

Tuesday, April 15, 2014

Day 232 - Bookcasemaker

Continuing our moving series, today we made an OpenSCAD module for making the most important type of furniture at all: bookcases! Here we have some Bondes (now discontinued, sadly) and Hemnes from IKEA, as well as a low TV stand:



Thingiverse: http://www.thingiverse.com/make:77833

Settings: MakerWare .3mm/low on a Replicator 2 with the bookcases on their backs so that support is not required.

Technical notes, OpenSCAD flavor: Again we continue from the previous day's code. The interesting thing today is that we added a parameter for number of shelves and used a for() loop to put them in. The tricky part was handling the translation correctly while putting in those shelves.

/////////////////////////////////////////////////////////////
// module for making bookcases //////////////////////////////

module bookcase(depth,length,height,shelves){
difference(){
// body of the bookcase
hull(){
translate(s*[0,0,0]) sphere(tiny);
translate(s*[length,0,0]) sphere(tiny);
translate(s*[length,height,0]) sphere(tiny);
translate(s*[0,height,0]) sphere(tiny);
translate(s*[0,0,depth]) sphere(tiny);
translate(s*[length,0,depth]) sphere(tiny);
translate(s*[length,height,depth]) sphere(tiny);
translate(s*[0,height,depth]) sphere(tiny);
}
// minus an inside
translate(s*[.1*length,.1*length,-depth/2])
cube(s*[.8*length,height-.2*length,2*depth]);
}
// put in some shelves
for (i = [1:1:shelves-1]){
translate(s*[0,i*(height-.1*length)/shelves,0]) 
cube(s*[length,.1*length,depth]);
}
}

Monday, April 14, 2014

Day 231 - Tablemaker

Continuing with our (procrastination of) planning for this summer's move, we printed our dining room table and two coffee tables.


Thingiverse link: http://www.thingiverse.com/make:77669

Settings: Printed on a Replicator 2 with MakerWare .3mm/low, upside-down so as not to require supports.

Technical notes, OpenSCAD flavor: Continuing with the same parameters as yesterday's code, today we constructed a table module. Since tables are sharper than sofas we used smaller spheres at the corners. The result is slightly nicer than just using the very sharp cube() module that we used for the legs.

/////////////////////////////////////////////////////////////
// module for making tables /////////////////////////////////

module table(depth,length,height){
// body of table
hull(){
translate(s*[0,0,0]) sphere(tiny);
translate(s*[depth,0,0]) sphere(tiny);
translate(s*[depth,length,0]) sphere(tiny);
translate(s*[0,length,0]) sphere(tiny);
translate(s*[0,length,depth/5]) sphere(tiny);
translate(s*[depth,length,depth/5]) sphere(tiny);
translate(s*[depth,0,depth/5]) sphere(tiny);
translate(s*[0,0,depth/5]) sphere(tiny);
}
// legs
translate(s*[0,0,0])
cube(s*[depth/5,depth/5,height]);
translate(s*[depth-depth/5,0,0]) 
cube(s*[depth/5,depth/5,height]);
translate(s*[0,length-depth/5,0]) 
cube(s*[depth/5,depth/5,height]);
translate(s*[depth-depth/5,length-depth/5,0]) 
cube(s*[depth/5,depth/5,height]);
}

Sunday, April 13, 2014

Day 230 - Sofamaker

It's official; we are moving to NYC this summer! This means downsizing from a house to an apartment, and some interesting furniture decisions. To help sort things out (and to procrastinate actually packing for as long as possible) I wrote a customizable sofa generator in OpenSCAD, and used it to model our sofas:




Settings: Printed on a Replicator 2 with MakerWare .3mm/low. 

Technical notes, OpenSCAD flavor: The code below makes sofas in the same way that we made knots in Day 153, by using hulls of collections of spheres. For example, the sofa code starts by forming an upright rectangular solid to make the back of the sofa, with eight spheres placed at the corners and then hull() filling in the space between by taking the convex hull of those eight spheres. Of course we could also have used the cube() command to make the rectangular solid, but taking the hull of spherical corners is what gives our sofas a rounded, comfy look. 

We used a scale of 1:50 for these models (meaning that our furniture is fifty times as long, wide, and tall as these models) and a conversion factor that allowed us to enter our dimensions in inches. For example, one of our sofas has depth 59", length 70", and height 32". To convert these to inches we have to multiply by 25.4, since there are 25.4 millimeters in an inch. Then to make the scale 1:50 we divide by scale=50.  In the future we'll add the ability to scale automatically to certain types of graph paper. 

// mathgrrl parametrizable sofa

/////////////////////////////////////////////////////////////
// parameters ///////////////////////////////////////////////

$fn = 12; // facets
scale = 50;  // enter desired scaling factor here e.g. 50 means 1:50
m = 25.4; // measurement unit conversion
//(m=25.4 does 1:1 scale with inches entered)
//(m=12*25.4 does 1:1 scale with feet entered)
//(m=10 does 1:1 scale with cm entered)
//(m=1000 does 1:1 scale with meters entered)
s = m/scale; // scaling factor 
r = 3*s;     // radius for soft bevels depends on scale
tiny = .2;   // radius for sharper edges

/////////////////////////////////////////////////////////////
// renders //////////////////////////////////////////////////

//uncomment the one you want to make

// purple loveseat
//sofa(depth=39,length=70,height=32);

// purple sofa
//sofa(depth=39,length=90,height=32);

// tan sofa
//sofa(depth=36,length=80,height=34);

// brown loveseat
//sofa(depth=36,length=54,height=34);

// spotted chair
//sofa(depth=36,length=35,height=34);

// ottoman  
//ottoman(depth=27,length=23,height=16);

/////////////////////////////////////////////////////////////
// module for making sofas //////////////////////////////////

module sofa(depth,length,height){
// back of sofa
hull(){
translate(s*[0,0,0]) sphere(r);
translate(s*[0,0,height]) sphere(r);
translate(s*[0,length,height]) sphere(r);
translate(s*[0,length,0]) sphere(r);
translate(s*[depth/4,length,0]) sphere(r);
translate(s*[depth/4,0,0]) sphere(r);
translate(s*[depth/4,0,height]) sphere(r);
translate(s*[depth/4,length,height]) sphere(r);
}
// left arm of sofa
hull(){
translate(s*[0,0,height/2]) sphere(r);
translate(s*[depth,0,height/2]) sphere(r);
translate(s*[depth,0,0]) sphere(r);
translate(s*[0,0,0]) sphere(r);
translate(s*[0,depth/4,0]) sphere(r);
translate(s*[depth,depth/4,0]) sphere(r);
translate(s*[depth,depth/4,height/2]) sphere(r);
translate(s*[0,depth/4,height/2]) sphere(r);
}
// right arm of sofa
hull(){
translate(s*[0,length,height/2]) sphere(r);
translate(s*[depth,length,height/2]) sphere(r);
translate(s*[depth,length,0]) sphere(r);
translate(s*[0,length,0]) sphere(r);
translate(s*[0,length-depth/4,0]) sphere(r);
translate(s*[depth,length-depth/4,0]) sphere(r);
translate(s*[depth,length-depth/4,height/2]) sphere(r);
translate(s*[0,length-depth/4,height/2]) sphere(r);
}
// cushions of sofa
hull(){
translate(s*[0,0,0]) sphere(r);
translate(s*[depth,0,0]) sphere(r);
translate(s*[depth,length,0]) sphere(r);
translate(s*[0,length,0]) sphere(r);
translate(s*[0,length,height/3]) sphere(r);
translate(s*[depth,length,height/3]) sphere(r);
translate(s*[depth,0,height/3]) sphere(r);
translate(s*[0,0,height/3]) sphere(r);
}
}

/////////////////////////////////////////////////////////////
// module for making ottomans ///////////////////////////////

module ottoman(depth,length,height){
hull(){
translate(s*[0,0,0]) sphere(r);
translate(s*[depth,0,0]) sphere(r);
translate(s*[depth,length,0]) sphere(r);
translate(s*[0,length,0]) sphere(r);
translate(s*[0,length,height]) sphere(r);
translate(s*[depth,length,height]) sphere(r);
translate(s*[depth,0,height]) sphere(r);
translate(s*[0,0,height]) sphere(r);
}
}

Saturday, April 12, 2014

Day 229 - Saturday Guest: kitwallace and Programming Polyhedra

Today's post is contributed by Chris Wallace, also known as kitwallace on Thingiverse, and author of The Wallace Line. He is the creator of the Rolling Knot, Mobius Strip, and Concave Polyhedra models on Thingiverse that have been featured here as well as the amazing Knot Server to OpenSCAD code (see Days 151, 153, 168, and 215). Thank you, kitwallace, for everything you have made possible, and for today's post!

The open source project OpenSCAD is the programmer's 3-D language. The language allows primitive object like cubes to be defined and combined using the Constructive Solid Geometry operations such as union, difference and intersection. Complex polyhedra can be defined in terms of points and, until the latest release, triangles. Now OpenSCAD allows faces to be used so that the programmer doesn't have to triangulate faces. So a cube can also be made with:

points = [
[ 0.5,  0.5,  0.5],
[ 0.5,  0.5, -0.5],
[ 0.5, -0.5,  0.5],
[ 0.5, -0.5, -0.5],
[-0.5,  0.5,  0.5],
[-0.5,  0.5, -0.5],
[-0.5, -0.5,  0.5],
[-0.5, -0.5, -0.5]];

faces = [
[ 4 , 5, 1, 0],
[ 2 , 6, 4, 0],
[ 1 , 3, 2, 0],
[ 6 , 2, 3, 7],
[ 5 , 4, 6, 7],
[ 3 , 1, 5, 7]];

polyhedron(points,faces);

Until the latest release of OpenSCAD it hadn't been possible to dynamically create the lists defining the polyhedra. This means that developers had to resort to quite awkward unions of primitives to build up complex objects. So one way to make a wire-fame model is to position a cylinder along each edge of each face. We can do that with a recursive module:

module make_face_edges (face,points,i=0) {
    if (i < len(face)) {
       assign( p1 = points[face[i]],
                  p2= points[face[(i + 1) % len(face)]])
       union() {
         locate(p1,p2)
             cylinder(r=wire_radius, h = norm(p2-p1));
         make_face_edges (face,points,i+1); 
       }
    }
}

module make_faces(faces,points) {
   for (i = [0:len(faces)-1])
      make_face_edges(faces[i],points);
}

wire_radius=1;
scale=20;
spoints = scale * points;
make_faces(faces,spoints);

The locate(p1,p2) module changes the coordinate system so it is centered on p1 with the z-axis pointing to p2. It was originally written to create knots. It is in the form of a transform which applies to all its children.

module locate(p1, p2) {
   assign(p = p2 - p1)
   assign(distance = norm(p)) {   
      translate(p1)
      rotate([0, 0, atan2(p[1], p[0])]) 
      rotate([0, atan2(sqrt(pow(p[0], 2)+pow(p[1], 2)),p[2]), 0])
      children();
  }
}


However, such models prove very slow to render when there are large numbers of edges. For example, here is one of the Catalan polyhedra, the Pentagonal Icositetrahedron, formed from 20-sided cylinders:


I found a different approach in the work of Paul Draghicescu (pdragy on Thingiverse), where the object is created by forming a shell made by removing the same polyhedron scaled down from itself, then removing prisms from each face. You can see it in this see-thu model:


The latest release of OpenSCAD supports a concat() function which together with recursive functions enables lists to be computed. In the script which creates this object, we have to centre the points in a face and this means subtracting the average of the points from each of the points. Until now that operation hasn't been possible but now we can write a recursive function:

function vsub(points,c,i=0) =
      i < len(points)
        ?  concat([points[i] - c], vsub(points,c,i+1))
        :  [];

Similarly we can transform every point in a list:

function transform_points(list, matrix, i = 0) = 
    i < len(list) 
       ? concat([ transform(list[i], matrix) ], transform_points(list, matrix, i + 1))
       : [];
         
where the functions vec3() and transform() are defined as:

function vec3(v) = [v.x, v.y, v.z];

function transform(v, m)  = vec3([v.x, v.y, v.z, 1] * m);

This allows us to find the centre and normal to each face, transform to the xy-plane and project to a polygon, then extrude a prism and finally reposition that prism back to the original face and remove it from the shell to create a cut-way model, here of a dodecahedron:


This is much faster to render than the wire-frame model and no nasty vertexes. The amount of cutout and the thickness of the shell can be adjusted, as can the inclination of the prism which turned out be useful when creating objects with very sharp vertexes.

Later I realized that if I made the prisms pyramids, I could add them to create stellated polyhedra or 'excavate' with then to yield convex polyhedra such as the Great Dodecahedron and Hugels' solid:


Powerful though OpenSCAD is, its ability to read data from external sources is limited to a few special case. Matrices can be loaded to define a surface, DXF files read to create a 2D object and STL files loaded. OpenSCAD code can be included, but the name of the file is static. Consequently scripts which can create multiple solids must include all the data needed and this makes these scripts quite complicated and less flexible.

In my search for coordinates for mathematical polyhedra, I came across David McCooey's wonderful site providing data and Java Applet viewers for hundreds of polyhedra. Any one of them would be interesting to convert into STL for viewing and printing. In my work with web site development I use another functional language, XQuery, developed for manipulating XML data. Using this language running on the open source eXistdb native XML database, I'm developing a web site to browse over this corpus of polyhedra, extract the coordinates from the text file which is included on the site and then generate OpenSCAD code for the selected style of object. Some coordinates transformations are done by XQuery. These include changing the winding of faces from right-haded to left-handed and computing edges from faces.

I'm fond of a 'Grook' (a kind of poetic aphorism) by the Danish designer and poet Piet Hein: Every problem you solve creates ten problems more. It is certainly true of this work: each time you try to solve one problem, like the construction of the regular solids, you are led into new areas of problems and faced with new difficulties. That's the thrill of this work and the pleasure of finding others like Laura similarly solving and posing new problems, helpful people on the OpenSCAD forum and generous people like David McCooey. I'm writing up my journey in my blog both as a log of my own work and in acknowledge of the help I've received for others. Thank you all.

UPDATES: For updates and further information see the OpenSCAD and Polyhedra post on kitwallace's blog The Wallace Line.

Friday, April 11, 2014

Day 228 - Friday Fail: Terminal edition

Seriously? An overnight print of eighteen Fidget Cubes (see Day 158) failed just before the LAST LAYER. Sigh.


Thingiverse link: http://www.thingiverse.com/thing:230139

Technical note: We now use .48 clearance instead of .5 for our 10mm Fidget Cubes; you can set your own preferred clearance at the link above.

Since there isn't much to learn from this final-layer fail except how to take a deep breath, here is a more useful fail to think about: Sometimes the MakerWare software (Mac version) for our MakerBot Replicator 2 freezes and has to be force-quit. After this happens I am unable to open the software again; it just freezes on opening and I have to force-quit again. My computer-savvy husband told me how to fix it:

  • Open the Terminal window and type ps -A to list the current processes.
  • Look for the line that has MakerBot/conveyor in it. This is a process that needs to be killed; see the highlighted line in the screenshot below.
  • Find the number at the start of the line for that process; let's call it 2222 for this example (although in the screenshot it is 14881, and on your computer it will be different every time).
  • Type sudo kill 2222 (again, use the number you found above, not this number).
  • Safety warning! Do not use ever use "sudo" unless you know what you are doing or someone smart that you completely trust recommends that you do so. It basically tells your computer "I know this is dangerous but go ahead and do it anyway."


Thursday, April 10, 2014

Day 227 - Penrose Snap Tiles

Today we printed emmett's Penrose Snap Tiles on Thingiverse, which are a genius-level modification of the already awesome Penrose P3 Tiles by pleppik. The genius is the excellent snap-together design that at the same time enforces the matching rules for aperiodic rhombic Penrose tilings. The tiles print up in no time with just two layers around the outside, and snap together surprisingly securely. You could quickly decorate an entire wall with these tiles!


Thingiverse link: http://www.thingiverse.com/make:73765

Settings: Printed on a Replicator 2 with custom profile based on the Low PLA script, with 0% infill, no roof, no floor, and two shells.