No, I'm not talking about dermatology here. I'm not that kind of doctor.* Last week in a blog about modularization of 3D printing I promised to return with more detail about that project. This is one of those installments. We're going to talk about one of my favorite aspects of OpenSCAD, the ability to use variables in 3D modeling.
This is a particularly interesting topic to me because variables probably seem second nature to those familiar with programming in other contexts, but they're not a fundamental concept to many 3D modelers, particularly those who use sculpting tools rather than scripting. It's like English versus Russian. Both tools serve the purpose of communication but a fundamental element of English, the article, doesn't even exist in Russian. That's why Boris and Natasha are always looking for moose and squirrel rather than THE moose and A squirrel.
In this example I'm going to create the box bottom used for my dice/pawn box. The catch is, I need different sizes of this box -- my Film Tycoons pawns are much taller than standard dice, so a box for the former must be taller than the latter. Also, I may want to change the width of the box to accommodate more than six dice or pawns, right? If I were doing this in a standard drag and drop visual editor I'd end up making multiple copies with slightly different sizes. Very inefficient.
Incidentally, if you develop an impromptu love of OpenSCAD and you're in the Dallas/Fort Worth area, I'm teaching an introductory course March 8th at the Maker Spot.**
Let's walk through some code! This is an example, not a tutorial, so I'm going dive into it rather than building up from scratch. If you're new to scripting I wouldn't expect you to learn the language from this. Rather, if you're interested in 3D modeling I hope this'll inspire you to consider learning OpenSCAD to pursue it.
Like many programming languages I'm going to start out defining a bunch of variables. Ignore the $fn = 50 variable.*** The next three define the width, depth, and height of the box I'm building.
Next, take a gander at the module boxbottom() declaration. Like many other programming languages, this declaration and the curly braces create a set of code which can be invoked repeatedly. In a drag and drop modeling program like Tinkercad you could click on a cube in a sidebar menu, drag it to the work environment, then click and drag handles to re-size it. In OpenSCAD I can define a shape with a whole mess of code inside the { and }, then create instances of whatever was defined by simply typing boxbottom().
You might have noticed that the module declaration contains an optional variable, ih. If the module is called without specifying a value the default of 1 will be used. We'll get to that usage later.
Line 23 is my basic shape. It's a cube with dimensions defined by three (four) variables: bbow, bbod, and bw+ih. At their current values this provides me a cube 70mm long, 50mm deep, and 9mm high. (bw is set to 4, and when I called the module in line 16 I set ih to 5.) So far I've created a block of plastic. Not very good for putting things in.
But wait! Line 23 is actually enclosed in a function, difference(). The first line of difference() defines an object -- all subsequent lines are removed from that object. So, I start with a cube in line 23, and in line 24 I take away a slightly smaller cube. Do the math; the dimensions of the second cube are 62 x 42 x 5.
Important side note: I want the second cube taken away from the center of the first cube. See that translate() function in line 24? It moves the forthcoming cube. My two cubes both align at the origin of [0, 0, 0]. I'm using the variable bw to specify the thickness of my box wall, 4mm. Translate() moves the second cube to the right by that much, then back by the same amount, then up.
Of course, the focus here is the scalability. What if I print this box and determine that 4mm isn't thick enough for the box walls? Simple -- I change the variable bw. Lines 23 and 24 adjust the outer cube and inner cubes accordingly. Want the box to be bigger? Change bbow to 100 and bbod to 75; all dimensions are adjusted accordingly.
The second part of the boxbottom() module handles the upper part of the box bottom. As you can see from the picture above the cavity of the box bottom isn't consistent -- the wall actually gets thinner at the top, which accommodates a pressure-fit overlap from the box lid. I won't walk through the code for that, but will point out the translate() function in line 27. It's outside the difference() function on the same line, so it applies to everything built in that difference() grouping. Note the use of bw and ih there -- they raise the second part of the box bottom to the appropriate position.
Just a few more notes on the importance of scalability here. First, the box bottom is only one piece of the overall "system." Other modules create the box lid, the shaped inserts, and a name plate -- each of these pieces must scale, so they all rely on the variables defined at the top. Second, I don't have to render just a single box. OpenSCAD also allows me to use for-next loops and arrays, a combination which opens up far more possibilities. More on that in a future blog; arrays are much more fun to demonstrate with Scrabble tiles or Fibonacci spirals, right? Totally with you on that.
* I first adopted the "DrUsual" handle when playing a first person WWII shooter game. People invariably asked what kind of doctor I am. I typically claim to be an Emergency Battlefield Proctologist.
** Good seats are still available.
*** Okay, I'll tell you. $fn is a special variable which determines the "roundness" of anything, well, round. Set it to 6 and every cylinder becomes hexagonal. Set it to 50 and cylinders are "pretty darn round." Set it to 100 and they're "really darn round." The higher this variable is set, though, the longer it takes for a model to render.