This is a fine example of the Craftsman style applied to a mansion. Somewhat obscured by the large size and small pixel count, this mansion features hipped gables and exposed rafter ends.

It is interesting to note that architects working on large projects (this mansion, the hotel featured above Chapter 7) adopted the bungalow style, not the four-square style.

Mansion, Warwick NY, c. 1915

A large, Craftsman-era mansion.

Edges to Rubies The Complete SketchUp Tutorial


Donut component, chapter 12 icon.

Chapter 12—SketchTalk in the Basement

Curious about that donut? I'd better show you what it is so that you can concentrate on the work at hand.

Result of the donut function  

donut [0,0,0], [0,20,20], 6, 8
Put that in your Ruby Console (after you've got SketchTalk loaded).

Hope you're not too disappointed. It's the ultimate low-poly version of a doughnut. Were you hoping for something higher on the "yummy" scale?

Donuts are surprisingly useful. Suppose you wanted a window frame. Start with a donut. Suppose you want basement walls. Another donut. Once you start to use donuts, you'll see them all around.

It's nice that it comes back as a component. It won't get stuck to geometry it touches.

Now let's get to work. To begin, here are two more useful SketchTalk functions for you to type in the Ruby Console:

all
del
Got that? These are handy, if inferior to File/New. My keyboard shortcut to File/New (actually, to my equivalent but more to my taste Ruby) is N. What might "n" do in SketchTalk?

I try to make this stuff easy. There should be one, obvious way to do it. Aren't you glad I named the box-drawing method box? I could have called it rectangular_parallelepiped. In Chapter 13 you'll begin building your own SketchTalk, and you can change box to rectangular_parallelepiped if you like.

Pouring the Basement Floor

We're going to skip materials for the moment. Let's keep it simple.

If you like, you can add footings. These are the walls' floor. Common construction details (not necessarily approved code in your location—always check): footings are as deep as the walls are thick; footings are twice as wide as they are deep. You do notice that footings are a donut, don't you?

Let's use SketchTalk to pour the floor. We want it 8'4" down, 25'4" along the red and green axes, with an extra three feet and wall left of the origin along the red axis. If you're doing all your modeling in the U.S. system, you'll be happy to know that Ruby frees you from doing a lot of math.

Topic: Mathematical Expressions

The four main math operations are "+", "-", "*" and "/" (addition, subtraction, multiplication and division). 28'4", in inches, is (28*12)+4. Operations within parentheses are done first. The "-" sign is also used to negate a quantity. Our floor's b axis location will be -((8*12)+4). That's called the "unary minus". 3-1.5 is a "binary minus"—it's got two operands and it subtracts.

Almost all languages have some form of operator precedence, going back to our algebra: you multiply before you add. Forget that. All languages perform operations within parentheses first. All. If you always parenthesize, what you, the human, sees and what the computer does will be one and the same. Always.

You can combine these operations as needed. Here's another operator: "**". That stands for exponentiation in Ruby. The volume of a cube with side length length is length**3. Given that you borrow prin principle amount at monthly interest rate rate for months months, your monthly payment will be:

factor = (1 + rate)**months
payment = prin * ( (rate*factor) / (factor-1) )
Coders: Ruby uses ** for exponentiation.

Some other languages use ^. Some languages have an exponentiation function, not an operator.

For all you programmers who skipped the preceding: I swore off even looking at precedence tables sometime last century, and I'm glad I did. Parentheses are unequivocal.

Now let's use some expressions to pour the basement floor. Recall that our basic carriage house is 24 feet square plus 8 inch exterior walls and 3 feet extra on the left for stairs. Try this SketchTalk:

all
del
hgt = -((8*12)+4)
near = [-((3*12)+8),0,hgt]
far = [(25*12)+4,(25*12)+4,hgt]
box near, far, 3

Your Ruby console will remember all of those. They weren't just for the basement floor.

Topic: Variables

In the above SketchTalk, hgt, near and far are "variables". They are called that since later you may assign them different values.

Since the dawn of programming, the = has been used to the right of a variable name and to the left of the value being assigned. Read that expression "hgt is assigned ...". Do not read it "hgt equals ...". We'll come to a convincing explanation of the need to read "... is assigned ..." in the next Topic box.

Variable names can be short if the code that uses them is short. Here hgt is created three lines above where it is used so abbreviating is fine. height_at_bottom_of_basement_floor would be a better choice if we were going to use it in widely separated parts of a program.

Coders: Standard variable names work in Ruby. Other, non-standard names with special meanings are used, too. We'll meet them as we need them.

The standard for naming variables in many languages is any combination of the 26 letters "A-Z", the 26 letters "a-z", the 10 digits "0-9" and the underscore character. The name must start with a letter.

In practice, underscores are used to separate words (spaces aren't allowed within names) as in height_at_bottom_of_basement_floor. Digits are used where numbers are meaningful: height_of_floor_2.

This section was long because we covered a lot of topics. The SketchTalk for pouring the basement floor was short. The basement walls will be almost instantaneous.

Building the Basement Walls

Type the highlighted lines here into the Ruby Console:

all
del
hgt = -((8*12)+4)
near = [-((3*12)+8),0,hgt]
far = [(25*12)+4,(25*12)+4,hgt]
box near, far, 3
hgt += 3
donut near, far, 8, 8*12
Instant basement walls. Are you beginning to think that donut might be tasty after all? Ruby modeling rules!
Topic: += (Self Assignment)

You want to add 3 to hgt so the donut starts above the floor. You could write hgt = hgt + 3. (That's a perfect example of why you should read it as "hgt is assigned ..." and not "hgt equals ...".) If you a) want to look like you know what you are doing, and/or b) want to save time you will abbreviate that to hgt += 3.

You can combine the other arithmetic operators with the assignment operator, too. x -= 2 reduces x by 2. x *= 2 doubles x, and so on.

I've kept all that Ruby Console input together to make it easy for you to copy it into your text editor. (You've already typed it into the Ruby Console so here copying is OK.) Add the highlighted lines and save it.

# /r/basement.rb

require '/r/sketch_talk'

n # replaces 'all' and 'del'

hgt = -((8*12)+4)
near = [-((3*12)+8),0,hgt]
far = [(25*12)+4,(25*12)+4,hgt]
box near, far, 3
hgt += 3
donut near, far, 8*12, 8

# end of /r/basement.rb
Congratulations. You are Ruby programming! Now, if only SketchTalk were finished we could Bucket the floor with cement, Bucket the walls with concrete blocks, Save As "basement.skp", ... Well, there's a serious problem with the Bucket tool. Through the Ruby API we can only access materials that are already in the model. We'll return to this issue in Chapter 20. Until then, Bucket the old-fashioned way.

SketchTalk isn't finished and it never will be. If you're not prepared to add to it, you will quickly find its limits. On the other hand, if you know how to add to it, it has no limits!

With just a handful of commands, SketchTalk can already work wonders. Remember the stairs you modelled in Chapter 2?

The Basement Stairway

Let's do this right from the start. Begin a "stairs.rb" file. This is the starting program:

# /r/stairs.rb

require '/r/sketch_talk'

n

# end of /r/stairs.rb
Save that program. In the Ruby Console: load '/r/stairs.rb'

The n command does a Select All and a Delete. It's fast and easy. The new command does a File/New. When your Ruby is done, change n to new. From now on you'll use the up arrow a lot to repeatedly load your program as you add to it. (And every time you do so you would have to tell SketchUp that No, you don't want to save the changes to Untitled, which becomes quite a nuisance after the first 10 or 20 times.)

The goal is a 36-inch wide stairway, 7-inch rise, 9-inch run, built from 1.5-inch thick American dimensional lumber. Let's start with a riser:

(Don't replace 7-1.5 with 5.5 or pre-calculate any of the others. You'll see why in the next section.)

Begin stairway with a riser

# /r/stairs.rb

require '/r/sketch_talk'

n

box [0,0,0],[36,1.5,0], 7-1.5 # riser

# end of /r/stairs.rb

From here on, add the highlighted line, save, reload /r/stairs.rb and see if you get the result in the picture. Fix and reload after errors.

Box command adds tread

# /r/stairs.rb

require '/r/sketch_talk'

n

box [0,0,0],[36,1.5,0], 7-1.5 # riser
box [0,0,7-1.5], [36,9+1.5,7-1.5], 1.5 # tread

# end of /r/stairs.rb

The banister is 1.5 inches square. One inch is allowed between the banister and wall at the side of the stairs. The one-inch square balusters are centered in the banister. The balusters are slightly tall as they are morticed into the banister.

Another box command adds a baluster

# /r/stairs.rb

require '/r/sketch_talk'

n

box [0,0,0],[36,1.5,0], 7-1.5 # riser
box [0,0,7-1.5], [36,9+1.5,7-1.5], 1.5 # tread
box [36-2.25,1,7], [36-1.25,2,7], 
	36+2 # baluster 1

# end of /r/stairs.rb

Coders: Given two integers, Ruby does an integer divide, discarding any fractional part. 9/2 is 4. If either dividend or divisor (or both) is a real number, the result is real and retains the fractional part. 9.0/2 is 4.5.

Coders: Over-length Ruby lines may be broken any place a line could not end, such as after a comma or after a binary operator. There is no line continuation character.

The second baluster is placed one-half the run behind the first. It is one-half the rise taller than the first.

Another box command adds a another baluster

# /r/stairs.rb

require '/r/sketch_talk'

n

box [0,0,0],[36,1.5,0], 7-1.5 # riser
box [0,0,7-1.5], [36,9+1.5,7-1.5], 1.5 # tread
box [36-2.25,1,7], [36-1.25,2,7], 36+2 # baluster 1
box [36-2.25,1+(9.0/2),7], [36-1.25,2+(9.0/2),7], 
	36 + 2 + (7.0/2) # baluster 2

# end of /r/stairs.rb
"G" is the keyboard shortcut for "Make Component". It asks for a name. Here a variable named step is assigned the component named 'step'. You'll see in the next command why you need to capture the component in a variable.

You will need to click on the component to see the bounding box in your model.

All is selected and turned into a component named "step".

# /r/stairs.rb

require '/r/sketch_talk'

n

box [0,0,0],[36,1.5,0], 7-1.5 # riser
box [0,0,7-1.5], [36,9+1.5,7-1.5], 1.5 # tread
box [36-2.25,1,7], [36-1.25,2,7], 36+2 # baluster 1
box [36-2.25,1+(9.0/2),7], [36-1.25,2+(9.0/2),7], 
	36 + 2 + (7.0/2) # baluster 2

all
step = g 'step'


# end of /r/stairs.rb

Ready for a new SketchTalk command? mc is SketchTalk for Move/Copy. It takes three arguments: the entity to move/copy, a vector showing how far to move and a repeat count showing how many times to move/copy.

You can get very good results moving geometry to SketchUp's inference points. But that's dicey compared to the absolute accuracy of supplying a move/copy vector.

The component is move/copied 14 times.

# /r/stairs.rb

require '/r/sketch_talk'

n

box [0,0,0],[36,1.5,0], 7-1.5 # riser
box [0,0,7-1.5], [36,9+1.5,7-1.5], 1.5 # tread
box [36-2.25,1,7], [36-1.25,2,7], 36+2 # baluster 1
box [36-2.25,1+(9.0/2),7], [36-1.25,2+(9.0/2),7], 
	36 + 2 + (7.0/2) # baluster 2

all
step = g 'step'

mc step, [0,9,7], 14 # move/copy, vector, 14x

# end of /r/stairs.rb

Again, the banister is 1.5 inches square, one inch away from the wall.

This is our first non-orthogonal box. Unlike in SketchUp modeling, our pushpull code always pushpulls toward the positive end of the perpendicular axis (unless you specify a negative distance). That means that a box in (or parallel to) the rg plane will be pulled up. Non-orthogonal rectangles almost always pull up toward the positive end of the blue axis. The exception: Non-orthogonal rectangles that are parallel to the blue axis pull up toward the positive end of the green axis.

The banister completes the stairway.

# /r/stairs.rb

require '/r/sketch_talk'

n

box [0,0,0],[36,1.5,0], 7-1.5 # riser
box [0,0,7-1.5], [36,9+1.5,7-1.5], 1.5 # tread
box [36-2.25,1,7], [36-1.25,2,7], 36+2 # baluster 1
box [36-2.25,1+(9.0/2),7], [36-1.25,2+(9.0/2),7], 
	36 + 2 + (7.0/2) # baluster 2

all
step = g 'step'

mc step, [0,9,7], 14 # move/copy, vector, 14x

box [36-1,0,36+7],[36-2.5,9*15,36+7+(7*15)], 1.5 # banister


# end of /r/stairs.rb
Congratulations, Ruby programmer. You've got a stairway Ruby.

Creating a Flexible Ruby Program

The stairway Ruby you've got makes exactly one stairway. Can we generalize it so it can make different stairways?

Let's do it! First, a word on successful programming. The first step in writing a program is to fold your arms across your chest. Ask questions:

Our stairway was a natural. We worked one SketchTalk command at a time (well, two when it came time to make the component) and used SketchUp to view our results.

I have a lot of experience. Would I have attempted to save time by doing two or three commands at once? Absolutely not. Beginners work that way. I save time by testing every step.

Let's get started. Save your file as stairs2.rb. Don't forget to change the top/bottom comments to get the name right.

Suppose we want a stairway program where we can vary the width, the rise, the run and the number of steps. How do we do that? Begin by adding variables for the things you want to vary:

# /r/stairs2.rb

require '/r/sketch_talk'


n

number_of_steps = 15
rise = 7
run = 9
width = 36

(From now on, I'll only repeat as much of the code as you might need to be absolutely sure where the new stuff goes.) With these variables created, one at a time work them into the code. number_of_steps is easiest.

mc step, [0,9,7], number_of_steps-1 # move/copy, vector, repeat count

box [36-1,0,36+7],
	[36-2.5,9*number_of_steps,36+7+(7*number_of_steps)],-1.5 # banister
Make those changes. Save. Change the number_of_steps variable to something small (so you can count them easily) and load '/r/stairs2.rb'. Got it?

Width is next. Use your editors' search/replace features to change all the 36 to width, change width to a nice, wide 72 and test.

That's exactly what I did. When I tested I found out that 36 was also used for bannister height. Two changes got the balusters back down to size, but left the banister floating. Another change and I was back in business. You don't have to be as dumb as I am.

On to rise and run. Pick one or the other. On your own, change the 7s or 9s to rise or run. Don't forget about the 7.0 or 9.0. Then change the value of the variable (and be sure you provide a real number, not an integer). Test. Got it?

Repeat for the remaining variable. Test. Got it?

If you are working strictly for yourself, this is where you stop. Need stairs? Set those variables, run the Ruby, select all, Ctrl+C to copy, back to whatever model needs stairs, Ctrl+V to paste, Qrotate and move into position. Need stairs with no balusters but banisters both sides? Tweak the Ruby.

But suppose you want to show the world your creation. You'll need a slick UI. Maybe you just want a slick UI for yourself. The next section explains how and it's easier than you might guess.

Adding a Slick UI

The ultimate stairs Ruby has not been written. This one isn't it, either, but it will show you how to use the SketchUp UI's main tool. (Why isn't this leading to the ultimate? Who wants to build stairs out of American 2x lumber? Too expensive with hardwood. #2 common is cheap, strong and full of ugly knot holes.)

Begin by saving as stairs3.rb and changing the comments at top and bottom.

The SketchUp UI provides an inputbox function that is surprisingly useful for its simplicity. You create a list of prompts and you get an input box. You add an optional list of defaults and you get a more useful input box. Add the new code here above your variables.

n

prompts = ['Number of steps:', 'Rise, each step:', 
	'Run, each step:', 'Stairway width:']
defaults = [15, 7.0, 9.0, 36.0]
UI.inputbox( prompts, defaults )

number_of_steps = 15

An untitled inputbox  

For that bit of code you get this input box. Personally, I think the title bar looks naked. Let's add a title.

UI.inputbox( prompts, defaults, 'Stairway Builder' )

Title added to input box  

That looks better, doesn't it? Too bad it doesn't actually do anything.

We haven't connected the input fields in the box to your variables. It's not hard. The inputbox function returns an array of text strings. We need to turn those strings into numbers.

Coders: In Ruby, everything is an object, including strings. The string methods to_i and to_f convert strings to integers and floating-point numbers. '15'.to_i returns 15. '15'.to_f returns 15.0. '1.5fred'.to_f returns 1.5. 'fred'.to_f returns 0.0 and does not raise an error.

We haven't met objects and methods, yet. They are part of the "object-oriented" programming paradigm. Ruby is heavily object oriented. For now, objects can have methods that they know how to perform on themselves. Saying string.to_f is the object-oriented way of saying to_f( string ).

These changes will capture and use the values from the input box.

inputs = UI.inputbox( prompts, defaults, 'Stairway Builder')

number_of_steps = inputs[0].to_f
rise = inputs[1].to_f
run = inputs[2].to_f
width = inputs[3].to_f

Make these changes and try them out. Have some fun making funky stairs.

One more wrinkle before we leave the inputbox. A final option is to have dropdown lists of choices. I'll show you an example from which you can puzzle out the details:

prompts = ['Number of steps:', 'Rise, each step:', 'Run, each step:', 
	'Stairway width:', 'Parallel to axis:']
defaults = [15, 7.0, 9.0, 36.0', 'green']
options = ['', '', '', '', 'green|red|non-orthogonal']
inputs = UI.inputbox( prompts, defaults, options, 'Stairway Builder')

Now the user has the option of choosing an axis or non-orthogonal stairway. The programmer, however, hasn't provided these options. Regardless of choice, the user's stairway will still come out parallel to the green axis because that's the way we programmed it.

What we should do is stick in a fork in the road so that if the user chooses something not yet programmed, we at least pop up a "Sorry, about that" message. Forks in the programming road are discussed in Chapter 13. You experienced programmers will find that Ruby goes beyond what you're used to and in a very useful way.


Hello, World! View of apartment contents. Defining functions in SketchTalk.