Ruby Resources

As you might have noticed by now, these aren’t very good lessons, and I’m not very good at ruby (in comparison with what’s out there in the rest of the world).

There are heaps of other places to go to learn how to do stuff with Ruby, here’s a few.

If you find anything else that’s useful, pop it in the comments

Lesson 4

A heavy duty (100 ton plus, perhaps) roadheader from the Ukraine.This lesson introduces us to some heavy duty concepts. We've been taking it relatively easy so far, so strap yourself  in and get ready to twist your melon.

We'll cover:

  • hash tables
  • indirection
  • modules
  • error catching

These will all become clear as we go along, so lets go!


Take a look over the code. It has grown up a bit, moved out of home and had a baby!

We're about to get into what all of this means, but for the moment you can just have a look through the code and see what looks new.

Open these both in a tab, and flick back and forth between this explanantion, and the code.

The bulk of our code from lesson 4 remains unchanged in the middle of this, but it is wrapped in all sorts of other exciting stuff. Lets start from the very begining, I hear that it is a very good place to start.

Modules – a quick peck on the cheek

module BVNoffice
    …
end

Modules do a lot of things in ruby, a lot of very complicated things, but also a few simple things. The simple way to think about them for our needs is like a picket fence around our code. Imagine that the plot that we've built our bit of code on is a house in an American suburb, and that all the houses are fenced in neatly painted picket module fences. This means that we can be safe inside our module. We don't need to worry about which bathroom we are talking about when we say 'the bathroom'. We know that if there is only one in our module (we are only allowed one), and that we are inside our module that we can't get confused. The BVNoffice module is just a fence around our code. We will use modules in an ever so slightly more powerful way in a bit, so keep this bubbling in your head.


The next line is also new, on the surface it's pretty clear what it does! begin shouldn’t keep you up at night just yet, it gets us moving along. Again, we are going to use this in a slightly more complicated way in a little while.

Modules – a little séance

This next chunk takes us back to modules. We are refering to three external files here. The first is like a toolbox, it's a place to keep our refactored code. The other two are just for tidying up, they are large datasets, so they are out in their own little module just to keep them tidy.

The helper functions are here, People is here and Seats is here, download them and put them in your SketchUp plugins folder to keep things simple.

Toolbox module

In the last lesson (and for your homework!) we refactored the big chunk of code so that it was easier to read, logical sections were moved to the outside of the main chunk so that they could be refered to just by a useful name. This use for modules is similar, except it puts those bits of code into a tidy external file so that they are available whenever you want them.

module BVN_Functions
  
  def self.test()
    puts 'this seems to be working'
  end

end

This is a cut down version of the helper module; lets step through it. module BVN_Functions tells ruby that it is a module and that BVN_Functions is its name. Bear in mind that whenever you are going to talk about anything in this module that you'll need to type out the name of the module before the name of the thing in the module, so whilst My_awesome_module_that_I_wrote_last_week_when_I_was_very_very_drunk is descriptive, and I’d like to encourage descriptive naming wherever possible, it's a bit of a mouthful, something a bit shorter would be good. The other thing is that module names must start with a capital letter, so My_functions is great, my_functions would fail.

We've seen def used to define functions before, but we've never seen self used. self is a special word that has a whole load of powers, but as, sadly, we've started to get accustomed to, we're just going to scratch the surface of what it does. To put things very crudely self registers the function to the module, and makes it available from outside by using the dot operator on the name of the module. In this case, if we load module and that BVN_Functions into another file then we’ll be able to call BVN_Functions.test and it will print this seems to be working to the console.

We'll see this used fo shizz in the real code, so hang on and we'll get there soon.

Tidying modules

One of these is a list of the seats in the Sydney BVN Studio, and the other is a list of the people.

Require loads an external file full of code into your project. It is a bit like an Xref in Autocad, or a reference in Microstation.

  1. #import the functions
  2. require '2011_02_26FunctionsForLesson4.rb'
  3. #import the data files
  4. require '2010_11_11peopleData' #information about people
  5. require '2010_11_11seatPositions.rb' #information about the seats
  6. seats = Seats::Positions #/__localise the information so
  7. people = BVNpeople::Data #\ that it is in a nice local variable

Localisation

Constants

Constants are like variables, except that they don't vary. It's pretty simple really!

You'd use one to declare something that you don't want to be able to change, something like the value of Pi, that's not ever going to change, so you can declare it as a constant.

Ruby knows that something is a constant because it's name starts with a capital letter.

In the top of the BVNpeople file it defines a the module, then it declares a constant:

module BVNpeople

     Data = …..

There is a hash table after that which has a heap of data in it, but we'll come back to that in a second, for the moment lets focus on importing that data into our programme. The require statement makes the module available inside our code, we could stop there and refer to the constant in it as Seats::Positions each time we wanted to refer it it, but I’m lazy so I’ve localised it into a variable that is defined withing the scope of this program called seats.

So,  if we skip on a few more lines we start to use this in ernest.

Hash – no, really, you'll want some

  1. xCoord = seats[person[1][:new_Seat_101015]][😡].mm
  2. yCoord = seats[person[1][:new_Seat_101015]][:y].mm

a hyperventilating womanOK, deep breaths! If that line didn't make you hyper-ventilate a bit then you are either wastign your time reading this, or you weren't paying attention! there is an enormous amount going on here crammed into each of these little lines. We are going to need to wind back a long way to make all of this make sense.

The first bit is simple, it is a variable declaration, so your pulse should be slowing a little. We've seen the very last bit before too, converting the number from inches to milimeters.

That just leaves us with this: seats[person[1][:new_Seat_101015]][😡] which we’ll unpick carefully now.

seats is a hash table. Hash tables are a lot like arrays, but it has the advantage that it can be accessed directly by using a key rather than an index. This means that the order of things in it is pretty much irrelavent, just as long as the key-value pairs are right.

{😡 => 2500,:y => 14500}

:symbols

Remember when we started we talked a lot about strings? Well strings are heavy weight things, they take a lot of power to heft all those words around. If you never need to use the contents of the string, and you are just using it to describe things then you can use a symbol. Symbols are like super-lightweight strings. You can make a symbol by just sticking a colon to the begining of a word like :marmite or :vegemite

Back to the hash

Hashes can be keyed with strings of symbols. We're going to use both, but the hash above keys with symbols. The pattern is: key => value. The symbol in the middle is called a hash rocket!

B1‘ => {😡 => 2500,:y => 14500}

This is actually a nested hash the key ‘B1‘ has a value that is actually another hash.

Indirection

We are about to learn about a very neat and elegant trick called indirection. It means to hold a value in an array which isn't actually the final value, but just another clue on the trail of where to look. This comes from programming really low level languages where things were all about values, or pointers to values, but we are so far away from that we can just sigh and nod about how quaint it was. I first learnt about indirection from Paul Coates, who helped me out when the only way I could figure out to wore something was to write about 1000 lines. He said, in his wise way "Use indirection dude" and an hour later I had 200 lines of really readable code!

seats[person[1][:new_Seat_101015]][😡]

If we unpack that statement and resolve it from the inside out, then it will all make sense. The innermost part of it is person[1][:new_Seat_101015]. We are crusing through the list of people using an each loop, the first person we get to is the legendary Matthew Middleton, but seeing as we'll be exposing lots of details lets sit tight and wait until we get to me. This is the entry for me:


  1. 'BenDoherty' => {
  2.                  :surname => 'Doherty',
  3.                  :name => 'Ben',
  4.                  :usedName => 'Ben',
  5.                  :date_hired => '5/07/10',
  6.                  :initial_seat => 'V8',
  7.                  :new_Seat_101015 => 'W3',
  8.                   :title => 'unknown',
  9.                  :Nationality => 'English',
  10.                  :Gender => 'Male',
  11.                  :Glasses => 'FALSE',
  12.                  :Height => 1.8,
  13.                  :image_fileName => 'BenDoherty_0024.jpg'
  14.                 },

If we look for the key :new_Seat_101015 and see what it opens we get 'W3' So this is what that section resolves to:

xCoord = seats['W3'][😡].mm

which is a bit easier to understand. It is asking for the x component of the position of chair w3. This needen’t all make sense right away, it will sink in eventually, and when it does it’ll feel like something from an Indiana Jones film when the sunlight streams into the cave full of treausre and illuminates the perfect ruby in the skeletons eyes.

Ok, take a breather and let that sink in.

OK, that’s enough relaxing, back to work! We need to talk about transformations.

Transformations

Creating the cubes where they actually need to be created is one option, but it'd be much neater to make the cubes at the origin and then move them to where they need to go.  Take a look in the helper module for this function:

def self.cornersOfASquare (edgeLength)
  #this returns a square of points with the bottom left one at 0,0,0
  points = [Geom::Point3d.new(0         , 0,          0),
            Geom::Point3d.new(edgeLength, 0,          0),
            Geom::Point3d.new(edgeLength, edgeLength, 0),
            Geom::Point3d.new(0         , edgeLength, 0)]
  return points
end

You'll notice that the code that makes the face is actually a little bit simpler than it was in lesson 4. We've taken out the bit that relates to the offsets and replaced it in the main code with a transformation.

  1. #make the points
  2. points = BVN_Functions.cornersOfASquare edgeLength
  3. #make a new origin for the square
  4. newOriginOffset = Geom::Point3d.new(xCoord, yCoord, 0)
  5. #make a new transform
  6. myTransform = Geom::Transformation.new newOriginOffset
  7. &#transform the points
  8. for i in (0..3)
  9.   points[i] = myTransform*points[i]
  10. end

operator overloding means that you can do things that you should only be able to do to numbers like 1+1=2 but with operator overloading, we can overload the abilities of the + operator so that we can do stuff that seems obvious, like point1 + point2. We could write a custom operator that gave batman+robin = pow
POW!

Again, there is a lot going on here. Transformation matrices are actually really powerful, but as we are doing a simple transformation so we don’t really need to worry too much about the mechanics. The sketchup API explains how to make a new transformation in this case we are using the overload on the method that takes a point and transforms something by the vector between the origin and the point: "Geom::Transformation.new(pt) creates a Transformation that translates the origin to pt." So we make a point, and then we make a new transformation that takes that point, and then by using the magic of operator overloding we loop through all four points (counting from 0 remember, 0, 1, 2, 3) to transform each one in turn. Then we can make the face on the newly transformed points, and the cube out of the face – POW!

The next big idea that we'll come across as we scan down the frame is a bit of an extention to the defensive programming that we covered in lesson 4. Back then we were trying to make sure that we only let the right type of things past so that they didn't cause trouble, but sometimes it isn't practical to interview for every possible eventuality, so we need an enforcer on the inside who can deal with a problem when one comes up without running to me with every silly problem it comes across.

  1. begin
  2.     materialName = person[1][:image_fileName].split('.')[0]  
  3.     m = materials.add materialName
  4. rescue
  5.     puts 'some sort of strangeness with the image name  ' + person[1][:image_fileName]
  6. end


Remember how we carelessly brushed past the begin and end tags? Well it makes sense, they way that they were at that point they didn’t actually do anything! They were like Salt'n'Pepa, but without Spinderella (and we all know that the back bone of hip hop is the DJ).

What we are missing is a rescue. Rescue is Terry Tate mixed with Florence Nightingale, it is there to catch errors if they show up, handle them in a graceful way, and keep the game moving along. Here's the pattern:

begin
    # the code that you want to run
rescue
    # what to do if it all goes horribly wrong
end

So in our case this tries to name the material name by taking the filename, chopping it up on full stops and then using the first part of the filename as the material name, and then tries to add a new material to the material library.

  1. begin
  2.   materialName = person[1][:image_fileName].split('.')[0]
  3.   m = materials.add materialName
  4. rescue
  5.    puts 'some sort of strangeness with the image name ' + person[1][:image_fileName]
  6. end

If anything goes wrong it pushes a message through to the console telling the user that something has gone wrong, then gets to work on the next thing it has to do. It is important to remember that if you don't tell it to do anything in the rescue section then that's what it'll do, nothing. It is often bad if there is an error, but a rescue allows you to deal with that problem gracefully rather than just blowing up and crashing or going into debug mode.

The last bit draws arcs from people's seat position when the first moved into the office to their new position as of 'the big move' because it leans on the power of the function at the begining of this program it means that all this bit of code needs to do is to make a couple of points and pass them into the function.

Right, that's enough for one day, it's time to go for a lie down in a dark room and let all these ideas float about in your brain and come together like pieces of a cosmic jigsaw puzzle.




Lesson 3

As promised in lesson 2, this week we're going to make a whole bunch of boxes, each one with a different face on them. The code is an extention of last week's lesson, so there should be some familliar stuff in there, and some new stuff. In true Blue Peter style, Here’s one I made earlier, try to read it:

  1. #setup stuff
  2. mod = Sketchup.active_model
  3. ent = mod.entities
  4. sel = mod.selection
  5. materials = mod.materials
  6.  
  7. #some cube variables
  8. edgeLength = 1000.mm
  9. gap = 100.mm
  10. offset = 0
  11.  
  12. #tell ruby where our textures are
  13. Dir.chdir (‘C:\Users\bdoherty\Desktop\BVN_Photos‘)
  14.  
  15. Dir.foreach(“.“) { |file|
  16.   type = file.split(‘.‘)[1]
  17.   unless type == nil
  18.     type.downcase!
  19.     if (type ==’png‘) or (type==’jpg‘)
  20.       puts file
  21.  
  22.       #create a cube
  23.       #setup the corner points
  24.       #                         x                    y           z
  25.       points=[Geom::Point3d.new(0 + offset,          0,          0),
  26.               Geom::Point3d.new(edgeLength + offset, 0,          0),
  27.               Geom::Point3d.new(edgeLength + offset, edgeLength, 0),
  28.               Geom::Point3d.new(0 + offset,          edgeLength, 0)]
  29.  
  30.       #add the bottom face
  31.       myCubeBase = ent.add_face points
  32.       myCubeBase.reverse! #flip it.
  33.       #the ! means -in place- i.e. myCubeBase = myCubeBase.reverse
  34.   
  35.       # makes a user friendly name for the material by removing
  36.       # the file extention. '.'+type handles .png and .jpg
  37.       materialName = file.chomp(‘.‘ + type)
  38.       # Adds a material to the “in-use” material pallet.
  39.       m = materials.add materialName
  40.       # pulls a texture out of a folder
  41.       m.texture = file
  42.       unless m.texture == nil
  43.         m.texture.size = edgeLength
  44.       end
  45.       myCubeBase.material = m
  46.       #do a push pull to make it into a cube**********
  47.       myCube = myCubeBase.pushpull(edgeLength, true)
  48.       offset += edgeLength + gap
  49.     end
  50.   end
  51. }

How did that feel? Are you starting to be able to read the code? You aren’t just learning to program, you are aquiring a new kind of literacy! (That’s a pretty big and exciting idea!)

Even to the untrained eye, there is a bit more going on here. Not a huge amount, the code in part 1 was 39 lines long, and the code in this part is 52, not an encyclopedia of code more, so lets unpick what it does.

Line 10 is the first time that anything new happens. There is a new variable that defines the gap between the boxes as we go along. With each new box we draw, we add 1 box width and 1 gap to the place that we start drawing the box. This is so we don’t just draw all the boxes on top of each other. The first time around the loop the offset from the origin is nothing, so we define offset = 0.

Now there is a furious flurry of activity, lines 14 to 21 are doing a lot of work.

  1. Dir.chdir ('C:\Users\bdoherty\Desktop\BVN_Photos')

Line 14 sets the currently active directory to be wherever you are hiding your pile of images. It is a method on the Dir class so if you want to read about it then it is in the docs. This is like the conductor raising his baton, and the moment of silence becore the cacophany starts.

foreach loops

  1. Dir.foreach(".") { |file|

Line 16 is a sneaky one, it really gets things going. Dir.foreach(".") is saying to ruby “for each thing in the current directory”. "." is old fashioned

speak for this directory “. Ahem, as I was saying, for each thing in the current directory, throw out the name of that thing

I was camping once, and there was a noise outside the tent, on poking my head out I could see that it was a wombat. He had prised the lid off our food tub, and had climbed inside. He was throwing the food out to his acomplice who was then ferrying it off to their burrow. This part (Dir.foreach(".")) of the foreach loop is like the wombat in the food tub.

If the wombat in the food tub is getting all the glory, the next bit is doing all the work. { |file| catches the filename, calls it file while it is holding it, and runs off into the burrow.

The burrow is defined by the curley braces {} they even look a bit like a burrow! While the filename is in the burrow we can call it file, but not when it is outside, ruby doesn’t think it exists outside. This is more scope. (Remember scope from the last lesson?) 

Lets follow our industrious wombat sidekick down the burrow, and see what’s inside… 


Actually, I lied a little bit when I said that the wombat in the tub was the one who did the renaming. As _Why puts it, |file| is like a chute that is just inside the burrow.

Imagine Alice falling down the rabbit hole. In Lewis Carrol’s version she is Alice when she goes in, and Alice when she gets to the bottom, but in our version there is a sign at the top of the rabbit hole that reads “become file“. Then as we are falling down the hole we reach into the cupboards and draws that surround us and pull out the f hat, the i tie, and the le jumpsuit. The inhabitants of wonderland now thing that we are the famous Mr file, and not just an imposter dressed as him.

So now we are dressed as file we need to pass some more tests. This is called defensive programming; it is important never to underestimate how many ways that programs can go wrong when you feed them duff information. Defensive programming is a bit like a border guard, checking your papers, then sending you on for an interview, then doing a retina scan, an xray, calling your references, doing an aptitude test…

In this case the '.' is the delimeter. If we were to split on  'e' then we'd get the arrays ["britn", "ynak", "d.txt"] and ["mThatch", "r_BrightonB", "ach.jpg"]

type = file.split('.')[1] is the first stage, it is like taking your laptop out of your bag at the airport. Because file is a string it can be fiddled with by the string methods as we played with in the first lesson. Split takes a file name like britneynaked.txt or mThatcher_BrightonBeach.jpg and if we ask it to split* on ‘.’ we get the arrays ['britneynaked', 'txt'] and ['mThatcher_BrightonBeach', 'jpg]. So because arrays are indexed from 0 we know that the file extention is in the 1st box. We take that information, and store it in type.

unless

Ruby’s unless blocks really are a thing of wonder. They are a part of ruby’s unparallelled readability. Try it, read it out loud: “unless type equals nil do this stuff”. What unless type == nil does is make sure that the there really is something to check for hidden inside the type.downcase! is like the helpful people in the customs queue who tell you where to stand and what to say before you get interviewed by the big scary if block. It does an in-place converstion from aNy CaSE to all lowercase. That way we can be sure that 'apples'=='apples' because ‘APPLES’ does not equal ‘apples’.

'apples'=='apples' → true

'APPLES'=='apples' → false

more conditionals

if (type == 'png') or (type == 'jpg')

The next line checks to see if type is either a .png or a .jpg. It is a conditional statement so it must eventualy resolve to a single true or false value. This is something that caught me out so may times when I was learning to program; I would always write if (type == 'png' or 'jpg') but as far as the computer is concerned this is meaningless. It helps to think of this as a tree.

Each bracket resolves to a true or false value, and then that gets resolved, and eventually we end up with a single value. A true or false value is called a bool after the 19th-centuary mathmatician and philosopher George Boole. He invented a kind of mathematical logic that was only concerned with truth values. There are other types of logic that have more than two values, but they are pretty hardcore – fuzzy and ternary to name a couple.

So after that excursion, we have made a check that the file that we are dealing with is in fact a png or a jpg. Our defensive programing won’t allow things that fail that test through, so we have a better chance that there will be fewer errors within that block.

Point objects

An object is a special programing concept, very similar indeded to its real world counterpart. In simple terms, objects have methods and properties. Methods are things that the object can do, and preoperties are facts about the object. Instantiate means that you create an instance of an object. So in this case when we instantiate a new point, we make a new point object.  

The bit where we define the corner points of the face has changed a little bit here too. Instead of using the lightweight point substiture of a three element array i.e. [xValue, yValue, zValue]. This time we are going to instantiate a point object. →

Geom::Point3d.new(xValue, yValue, zValue)

Point3d is an object in the Geom class. We won’t go into the details of what all of this means, all that matters is that it has a method called .new that makes it make a new one using the arguments provided. This line does the same thing as it did before, except this time we are instantiating the points more explicitly. This doesn’t do much from the computer’s point of view (it would be useful if we were assigning them to a variable because we could refer to myPoint.x rather than myArrayMasqueradingAsAPoint[0]) but its real use is that it makes the code readable by making it explicit what the code is doing.

The next line that is different is the one where the material gets named:

materialName = file.chomp('.jpg')

So what is going on here is that we call up the power of the chomp method (which belongs to the string class) by dangling a bit of bloody tuna carved into the shape of the word ‘.jpg‘ off the back of the plane, and waiting for the great white shark of string to come and chomp it off. We could carve anything into the argument, so if I wanted to chomp ‘plane‘ off the end of ‘aeroplane‘ then I would say ‘aeroplane'.chomp('plane') and it would give me back 'aero'. Using the chomp method gives us the name of the person in the picture, without the file extention.

We’ve seen unless before, so We can skip that and move straight down to line 48.

Counters

Remember that we are doing the stuff between the {} braces lots of times; once for each file in the folder. This means that we can use this process to change things outside the loop each time we go around.

Photo by David Lee / Rex Features ( 982851j ) 99 Flake Various - 2009Imagine that we are on a roundabout, and each time we pass the start we lean out and take an ice cream. Now imagine really really hard (because this part is totally unbelievable) that you don’t like flakes.

If you pulled out all the flakes and stacked them on the seat beside you, after a while you’d have a lot of flakes. One for each time you’d been around.

What we are doing is that each time we go around we are adding a number (in this case two numbers; gap and edgeLength) to the current offset. This means that the next time that offset is used it will be a bit bigger than last time round, and so on until there are no more photos to make boxes out of. This works because offset is defined outside the scope of the the loop. If it were to be defined inside the loop then it'd be overwritten each time we passed it.

some faces mapped onto boxes

And that, dearly beloved, is how to make a lot of boxes with faces on them. Take a deep breath, and lets dive straight back in!

Refactoring

At the moment we have a big chunk of code; it doesn't even do very much yet! It's quite hard to read it all in one go and to understand it, so imagine what it would be like to read a really big program. The way that we get around that and make our code managable is to refactor the code. This means that we take the big block, and break it up into sections. Either conceptual sections or functional sections.

This is a hard concept in the abstract, but it's easy if we talk about something that we're comfortable with. Take a look at this super tasty breakfast from the Jamie Oliver website!

kedgeree
© David Loftus

kedgeree

main courses | serves 6

This is a traditional British breakfast from colonial India and it’s a lovely little dish, with a nice balance of spicy and smoky flavours. It makes a tasty lunch or supper too – so get stuck in!

Boil the eggs for 10 minutes, then hold under cold running water. Put the fish and bay leaves in a shallow pan with enough water to cover. Bring to the boil, cover and simmer for about 5 minutes, until cooked through. Remove from pan and leave to cool. Remove the skin from fish, flake into chunks and set aside.

Cook the rice in salted water for about 10 minutes and drain. Refresh in cold water, drain again, and leave in the fridge until needed. Melt the butterghee in a pan over a low heat. Add the ginger, onion and garlic. Soften for about 5 minutes, then add the curry powder and mustard seeds. Cook for a further few minutes, then add the chopped tomatoes and lemon juice.

Quarter the eggs. Add the fish and rice to a pan and gently heat through. Add the eggs, most of the coriander and the chilli and stir gently. Place in a warm serving dish. Mix the rest of the coriander into the yoghurt and serve with the kedgeree.

ingredients

  • 2 large free-range or organic eggs
  • 680g undyed smoked haddock fillets, pinboned
  • 2 fresh bay leaves
  • 170g long grain or basmati rice
  • sea salt
  • 110 pure butterghee
  • a thumb-sized piece of fresh ginger, peeled and grated
  • 1 medium onion or 1 bunch of spring onions, finely chopped
  • 1 clove of garlic, peeled and finely chopped
  • 2 heaped tablespoons curry powder
  • 1 tablespoon mustard seeds
  • 2 tomatoes, deseeded and chopped
  • juice of 2 lemons
  • 2 good handfuls of fresh coriander, leaves picked and chopped
  • 1 fresh red chilli, finely chopped
  • a small pot of natural yoghurt

Apart from being one of my favourite brekfasts, this recipe makes extensive use of refactoring. The first line asks you to:

"Boil the eggs for 10 minutes, then hold under cold running water"

How on earth do I know what boil means? What is hold under water? We could rewrite this as Boil(eggs, 10) and HoldUnderWater(eggs, cold) to bring us back into the code world. Knowing how to boil something is assumed prior experience, probably learnt from Delia Smith or from even earlier in life. Whenever a recipe calls for us to boil something we can recall this extra set of instructions and use them. When we're done with the boiling we can return to the main set of instructions and carry on.

Functions are a way of making code reusable. Imagine trying to explain to an incredibly meticulous, but not at all bright person how to do something, this time lets say bake a cake (this example comes up all the time). To someone who knew about baking cakes all you'd need to do is provide the list of ingredients and a cooking time, their experience would do the rest. If we were teaching a young child about baking cakes we'd need to go into a lot more detail, we'd need to explain how to sift flour, how to break eggs, how to stir in a mixing bowl, the list goes on.

Computers are even less useful than children, they know nothing, but luckily they have been shown how to do some things (like adding up numbers and that kind of thing) by their designers, and then people have written progressively more compicated instruction sets and put them into libraries. Each time we called ent.add_face points we were calling on one of these library functions.

To stretch this analogy even further; to a recipe for making cakes starting with 'tense thenar muscle', this is a bit impractical, so the next cookery book would have a recipe for how to break an egg that would explain in detail how that was done, and then each time an egg needed to be broken it could say "break an egg (see p12)". at a stroke the cookery book gets 99% thinner as all of the repetitive tasks get refactored into other places.

Some functions are simple, like turn_on_oven_and_preheat(180) this has an effect, but doesn’t return anything, and some are a little more complicated, like sift(flour) which returns some sifted flour, and gets rid of all the weevels and grit. The thing in the bracket is an argument, you can pass in lots of arguments because these are what the functions use to decide what to do. You don't actuall need brackets in ruby, so it is up to you if you want to use them to improve clarity and readability.

def addNumbers(firstNumber, secondNumber)
    newNumber = firstNumber + secondNumber
    return newNumber
end

This is a pretty simple function, it takes two numbers, adds them together, and then gives back the answer. def is a keyword that tells the computer that they thing coming up is going to be a function, then the next word (addNumbers) is the name of the function, then the arguments in brackets.

def addNumbers(firstNumber, secondNumber)
    …
end

The bit between the def and the end is where the magic happens. Ruby Has some lightweight ways of doing functions, but I like to have a return statement when the function returns something, and leave it off when it doesn't, I just find it easier to read.

Refactoring in action

You can get into trouble here, but that trouble can lead you to understanding your code a bit better too; you need to take a look at the chunk of code that you want to take out and isolate the things that go in and out of it.

  1. Dir.foreach(".") { |file|
  2.  type = file.split('.')[1]
  3.  unless type == nil
  4.   type.downcase!
  5.   if (type =='png') or (type=='jpg')
  6.     puts file
  7.      draw_a_cube(edgeLength, offset, file, ent, materials)
  8.      offset += edgeLength + gap
  9.   end
  10.  end
  11. }

That’s pretty simple isn't it! just take out the painful bit in the middle and use the pre-defined draw_a_cube function. Well, it is once the function is defined. the tricky bit comes when you need to define the function. Remember, this takes the format:

def function_name(arguments)
 #the code that you need to write
end

def is the begining of the definition of the function, with it's matching end tag, then you get to flex your creative juices and make up a good name for your function. Unfortunatley chomp is taken so you might as well come up with something useful that describes what the function actually does. The bit after that is where we pass in the variables, the arguments section.

Below is the function that draws the cube. Take a look at the code and try to read through it to see where it seems different to what we have already.

def draw_a_cube(edgeLength, 
                offset, 
                materialFileName, 
                entityListToAddTo, 
                materialListToAddTo)
  type = materialFileName.split('.')[1]
  #create a cube
  #setup the corner points
  # x y z
  points=[Geom::Point3d.new(0 + offset,          0,          0),
          Geom::Point3d.new(edgeLength + offset, 0,          0),
          Geom::Point3d.new(edgeLength + offset, edgeLength, 0),
          Geom::Point3d.new(0 + offset,          edgeLength, 0)]

  #add the bottom face
  myCubeBase = entityListToAddTo.add_face points
  myCubeBase.reverse! #flip it.
  #the ! means -in place- i.e. myCubeBase = myCubeBase.reverse

  # makes a user friendly name for the material by removing
  # the file extention. '.'+type handles .png and .jpg
  materialName = materialFileName.chomp('.' + type)
  # Adds a material to the "in-use" material pallet.
  m = materialListToAddTo.add materialName
  # pulls a texture out of a folder
  m.texture = materialFileName
  unless m.texture == nil
    m.texture.size = edgeLength
  end
  myCubeBase.material = m
  #do a push pull to make it into a cube
  myCube = myCubeBase.pushpull(edgeLength, true)
end

There isn't that much that's different, but the subtleties will probably catch you out if you don't have laser concentration. The main difference is related to our old friend scope. Once you are inside the block that defines the function defend your scope gets reset. Variables that are defined in a diferent block are no longer available unless you pass them in through the arguments list. This means that we can reuse the variable names edgeLength and offset because they are in a different scope. The other thing that's worth bearing in mind is that because we are in a different scope and things that are available outside are unavailable, we need to either redefine type or pass it in through the args; in this case, type is a derivitave of materialFileName so it makes more sense to derive it inside than to pass it in and risk errors from passing in one file name and another file extention by accident.

The only other thing to note is that for the moment the function needs to be defined before it is called for the first time. To put it very simply, that means that your main bit of code needs to be at the bottom for the moment.

It'd be a bit too much like hard work to put all the code here, but look at this ruby file to see how the code is structured now that it's been refactored a little bit.

Homework

There are plenty of other oportunities for refactoring in this code. Firstly try to edit the code to use this function:

def SimpleSquareFace(edgeLength, offset, entityListToAddTo)
  points=[Geom::Point3d.new(0 + offset,          0,          0),
          Geom::Point3d.new(edgeLength + offset, 0,          0),
          Geom::Point3d.new(edgeLength + offset, edgeLength, 0),
          Geom::Point3d.new(0 + offset,          edgeLength, 0)]
  myCubeBase = entityListToAddTo.add_face points
  myCubeBase.reverse!
  return myCubeBase
end

This week's homework is in two parts:

  1. Try to implement the SimpleSquareFace function, but if you get really stuck you can see how to do it here.
  2. Refactor something else in the code. (Hint There is some vaule in looking at the materials section of the draw_a_cube function)

The gist of things

This is a series of 6 lessons that introduces some of the ideas that surround basic programming and general algorithmic thinking. It uses Ruby as the language, and SketchUp as a way to visualise things in 3d.

  1. lesson
  2. lesson
  3. lesson
  4. lesson
  5. lesson
  6. lesson

Each lesson has homework that you need to post the results in the comments of each post. Feel free to ask questions in the comments too; I’ll try to answer as soon as I can, but if you can answer someone else’s question, then please do!

I hope you enjoy it, and if you don’t ‘get’ it, or you have a question, don’t be shy, just ask!

Lesson 2

This lesson will show you how to make a box with your beautiful/terrifying face all over it!

ben's face on a cube, he looks like a moron!

In order to talk to a program like SketchUp with a programming language we need to use an application programmers interface (API). An API is a way for a programmer to interface with the core of the program, so if we want to Sketchup to draw a line we need to interface with it, and because Sketchup doesn’t speak our language we need the API to act as a go between for us. (This is a pretty inacuarate description, but it’ll get your thinkign fired up int he right direction, we can refine it later. If not knowing things is like pouring red ants into your underpants then have a look here)

Traditionally this was one of the ways to define the difference between programming and scripting, if you were writing a program from the ground up then you were programming, and if you were interacting with it through an API then you were scripting*. In the last couple of years this distinction has blurred as people have tried to rewrite their programs by using their own API as a way to make sure that it really works!

*Again, this is only half the story, there is also the difference between interrpreted and complied languages, but really the important thing to remeber is that if someone starts a conversation about what the difference between scripting and programming is then you should find the quicked way to get away from this incredibly boring person. Life’s too short to be a pedantic loser!

Here’s all the code that we’ll encounter in this lesson, see if you can read it, and I’ll break it down piece by piece afterwards

#setup stuff

mod = Sketchup.active_model

ent = mod.entities

sel = mod.selection

materials = mod.materials

#create a cube

#some cube variables

edgeLength = 1000.mm #because Sketchup is from a backward country they still use inches.

# To make Sketchup convert mm to inches for its own internal use <number>.mm

#setup the corner points

# x y z
points = [
[0, 0, 0],
[edgeLength, 0, 0],
[edgeLength, edgeLength, 0],
[0, edgeLength, 0]
] #add the bottom face

myCubeBase = ent.add_face points

myCubeBase.reverse!#flip it.

#! means ‘in place’ i.e. myCubeBase = myCubeBase.reverse

# Adds a material to the “in-use” material pallet.

m = materials.add “ben

# pulls a texture out of a folder

myPath =’C:\Users\bdoherty\Desktop\BVN_Photos\’

#remember to change this path for your computer!

myImageFilename =’BenDoherty_0024.jpg

m.texture = myPath + myImageFilename

m.texture.size = edgeLength

myCubeBase.material = m

#do a push pull to make it into a cube

myCube = myCubeBase.pushpull(edgeLength, true)

Set up

Lets step through what is going on here. the first section, marked #setup stuff stuff is common to most sketchup Ruby files. It contains a bit of short-cutting, where we make the names for things a bit shorter and easier to deal with, hence Sketchup.active_model becomes mod, and, Sketchup.active_model.entities becomes sel. This is just for convenience, and if you really wanted to, you could say Sketchup.active_model.entities every time you wanted to talk about the things in the model.

Geometry

Next up we have the beginings of the excitment. We need to define how big we want the cube to be, we could do this by desiginng a user interface, but it’s easier for now just to type it in. As it says in the comments, the edge length needs to be specified in inches for sketchup to be happy, so if we put in 1000 and leave it at that, then we’ll get 1000 inches, or almost 24 and a half metres – the size of the dome on the Taj Mahal. The way around that is by using a method on number called mm. That sounds way more complicated than it really is, so just remember to specify your units whenever you write a number.

edgeLength = 1000.mm
the taj mahal

The next line is a bit of a subterfuge, because really it is lots of lines that ruby treats as one. SketchUp is able to understand points descibed in a few ways, we can enter the points as an array of arrays of numbers. That’s a bit of a mouthful, so lets go over what it means.

A point can either be described as:

myPoint = Geom::Point3d.new(100, 200, 300)

or

myPoint = [100, 200, 300]

They both do exactly the same thing, when it comes to passing points around. (The first method allows you to talk about myPoint.x which is easier to read than myPoint[0], there are a load of other clever things you can do with real points too. In the future we’ll write points out as the first way, but for the moment, the second way is easier to understand given what we’ve covered so far.)

In the second way of writing the point, the numbers are in an array. _Why describes this as staples holding a wiggly catterpillar down (don’t the [square brackets] look like staples!) and so we can think of the array of arrays as a collecting book with big staples being used to staple a whole collection of wiggly number catterpillars to the page.

We now have the corners of a square, and because of the SketchUp-way, 2d things like squares get pushed about to turn into 3d things like cubes. So the next line actually makes the face that joins up the corners.

myCubeBase = ent.add_face points

So the collection ent gets a face added to it, and that face is called myCubeBase.

If we rush ahead and pushpull this square into a cube we’ll have to work hard to put a texture on each face, but if we put a texture on the square then the faces that are generated by SketchUp inherit the texture. Before we do that there is one more tricky little thing that I don’t fully have a handle on. regardless of the order that the points get created in, the face always extrudes downwards (don’t ask me why just yet), so we need to flip it.

myCubeBase.reverse! #flip it. !

You would have thought that the method that does this is called flip, but actually it is called reverse. The ! is a signal in ruby that something is going to happen to the object itself. Like a reflexive pronoun, I was myself, we see ourselves, you talk to yourself, I play… see, the list is endless, but it is happening ‘in place’, you are getting the object to do something to itself, rather than getting it to make a copy of itself and then do the thing to that copy.

Materials

Now we get to start being decorators.

In the setup section we defined materials as materials = mod.materials, (so ultimatly it is materials = Sketchup.active_model.materials that we are working with, but the aliasing makes our life easier).

m = materials.add "ben"

This line makes a material. There is something a bit confusing going on, because ruby calls the material m, and sketchup calls the material ben – so which is it?!

This is the first time you will come across a very confusing subject called ‘scope’. This cheesily grinning pair are going to help me out while I explain scope.

I’m pretty sure that Mrs O calls Mr O some pretty impressive things in the context of their ‘private time’, but if anyone else tried calling the commander in chief of the most powerful army in the world big boy, or my personal stallion, then it wouldn’t be long before Guantanamo re-opened.

So in the scope of the oval office, Mr O is refered to as ‘Mr President‘, but in his house he’s probably just ‘dad‘.

In the scope of the ruby code, the material is called m, but in the scope of the sketchup environment it is called ben.

That’ll do for scope for the moment, but don’t forget about it, we’ll be coming back to it!

# pulls a texture out of a folder

myPath ='C:\Users\bdoherty\Desktop\BVN_Photos\'

myImageFilename = 'BenDoherty_0024.jpg'

m.texture = myPath + myImageFilename

Now we are peering out of our box; this is where we need to interact with the world a bit. I’m keeping my folder full of people on my desktop, which becasue I’m at work it at the path above. You’ll need to look at your file path and work out where it lives (this is tricky on a mac, but you can just drag the folder into the editor and it will write out the path for you – clever things).

You’ll need a square picture of your face, a bit of clever photoshop work can remove the bags under your eyes, and while you are at it, crop it square. The picture of me is called BenDoherty_0024.jpg so I’ve put that in as a variable; you won’t have a picture of me, so use a picture of yourself.

The next line makes the texture property of m be the path joined (or concatinated with) the file name.

m.texture.size = edgeLength

This just sizes the texture so that it is the same size as the face that it is going to be applied to.

myCubeBase.material = m

…and finally, this line applies that material to the square drawn on the floor. Remember that the extruded box will inherit the material from the square that it is extruded from.

#do a push pull to make it into a cube

myCube = myCubeBase.pushpull(edgeLength, true)

All of that faffing around, just to make a box, but at last this is the command that makes the square into a box.

Many boxes will come in lesson 3.

Lesson 1

Today’s is the first of the ruby lessons. As stated we’ll be going very gently through this exciting realm, the thorny wilderness that surrounds the tower of the philosopher programmer kings.

In this class we’ll:

make sure that your armour fits

SketchUp can be downloaded from here,

and the Ruby console for SketchUp from here.

Once you have installed SketchUp you just need to unzip the console into your SketchUp plugins folder. You can find that by going to C:\Program Files (x86)\Google\Google SketchUp 7\Plugins.

say a few words to launch the adventure


To start your Ruby writing career you’ll need a place to write. You can write on a piece of paper on a tram, but it won’t compile, you’ll need to send it down to the typing pool first. The best place to write your code is into an editor. To launch the editor you go to your plugins menu and click on it, simple as that!

The editor comes with a few lines of ready written code, but essentially it is an empty place for you to jot down your ideas.


There are lots of ways that the computer can talk to you, text, sounds, pictures, moving a robot arm to pinch your bum. We are going to lean on SketchUp to do our pictures, but to see any text output from Ruby we also need to see the Ruby console. That lives in the ‘window’ menu. The console is really useful to look at, but pretty dull.

You should have three windows on your screen, arrange them so that you can see them all at the same time.


Now for your very first bit of programming! Type puts 'hello world' and press the play button in the editor.


By now you should be getting comfortable with the editor. We are going to do some text based experiments, so don’t worry too much about the SketchUp graphics window for the moment, we’ll get to that in lesson 2.

Lets learn about some of the fundamental things that we’ll need on our quest.

check what is in our saddle bags

Variables are places to store information, a bit like a jar. Lets make a jar to put our name in.

Type myName = 'the Ruby knight' or whatever your name really is, and press enter.

a picture of some marmalade from wikipedia.This puts the string the Ruby knight into the jar labled myName. This means that whenver we ask for that jar we get the words that are inside. This might seem a bit odd, but at breakfast when you ask for the marmalade, you aren't interested in the marmalade jar, but the marmalade in the jar. As long as you remember the marmalade, veriables will be simple.

On the next line type puts 'hello ' + myName and press the play button – look in the console window for the output. Notice the space after hello that's there so that when you put the two words toghther they don't bump into each other.

There are lots of different types of variables:

numbers: 1, 5000, 20, or like 4.5, -700.45

strings: 'hello world', "my style is the best"

Booleans: true, false

and lots of others, there is a bit more info here.

pack out the bandoleer

Bandolier_of_Carrots

Imagine that we have a lot of carrots, we’ll need a way of keeping them in order; we can do this with an array. I like more variety than just carrots, so try:

fruits = ['apple', 'orange', 'dragon fruit'] ← this is an array

If we want a way of saying hi in different ways we could build this array:

helloWords = ['hello', 'hi', 'wotcha', 'Greetings', 'Hey', 'Salutations', 'Hi', 'Hiya', 'Yo', 'Pip pip cheerio', 'Top o the mornin to ya', 'Hola', 'Howdy', 'Howzit']

When we need to get into the bandoleer and pull out the specific carrot that we want. We do this with an index.

puts fruits[0] will give us ‘apple

WOAH, hang on a second, index 0? WTF? Well programming languages count from 0, so 0, 1, 2, 3… get used to it.

There are 14 things in the helloWords array, so because we index from 0, the highest index is 13, and because the random number generator gives us numbers under the number we give it, we need to give it 14. It's easy once you know how, but gettign there will make your brain hurt!

Lets do a little trick. To get a random number from ruby we say rand(a number) and that gives you a random number between 0 and 1 less than that number. We can use a random number to index an array – so, putting it all together, try puts helloWords[rand(14)] + myName

At this point your code will look something like this:

puts 'hello world'
myName = 'the Ruby knight'puts 'hello ' + myName
fruits = ['apple', 'orange', 'dragon fruit']
helloWords = ['hello', 'hi', 'wotcha', 'Greetings', 'Hey', 'Salutations', 'Hi', 'Hiya', 'Yo', 'Pip pip cheerio', 'Top o the mornin to ya', 'Hola', 'Howdy', 'Howzit']
puts fruits[0]
puts helloWords[rand(14)] + myName

a postit with the word 'strings' on itexchange riddles with the cat

Strings are words, they are the way that the computer interprets things that we say and remembers it. They are a bit like post it notes with messages on them This would be a good place to introduce some help. The rubydocs to be precise. They are a repository of all there is to know about the core of Ruby. Here’s the string help. This explains all the methods that ruby uses. lets play some word games.

puts "repaid deliver".reverse
puts "restaurant".includes('aura')
puts 'mieman'.insert(2, 'nut')

By using the docs try and work out what each of these statements will do before you actually run them.

The eagle eyed amongst you might have noticed that sometimes I use ” ” and other times I use ‘ ‘ to enclose the strings. In Ruby they are more or less interchangeable, but ‘ ‘ is a but faster. There are times that you’ll need to use ” but they are rare (regular expressions).

Eat a bowl of fruit loops

The thing that makes computers worth using is that they do simple things very fast. Usually it is the same thing that they do each time, and the thing that does this is called a loop.

There are a few ways to make loops happen. The most common is the for loop.

for i in (1..10)
  puts 'kick'
  puts 'high hat'
  puts 'kick'
  puts 'snare'
end

you can also say:

10.times
{
  puts 'kick'
  puts 'high hat'
  puts 'kick'
  puts 'snare'
}

There are a load of other possibilities here but lets try to use this in a useful way,

for i in (1..10)
  puts (10 - i).to_s + ' green bottles - hanging on the wall'
end

this will write out:

9 green bottles – hanging on the wall
8 green bottles – hanging on the wall
7 green bottles – hanging on the wall
6 green bottles – hanging on the wall
5 green bottles – hanging on the wall
4 green bottles – hanging on the wall
3 green bottles – hanging on the wall
2 green bottles – hanging on the wall
1 green bottles – hanging on the wall
0 green bottles – hanging on the wall

There are a couple of tricky looking things there, so lets clear them up.

1..10 gives you all the numbers 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 whereas 1…10 with three dots gives you 1, 2, 3, 4, 5, 6, 7, 8, 9.

First of all i is a variable name that is currently a counter. It needn't be i, but convention sayst that it should be; it could be a or bobby, but people that i counts and that mack has a knife. So i counts from 1 to 10. What is happening here is that the loop processes for each value of i. It doesn’t take a genius to work out what (10 – i) gives you, when i = 1 then (10 – i) = 9 see, I told you that there was no hard maths in programming!  Remember that we could add strings to eah other – 'hello' + ' it's me again' = 'hello it's me again' but you can’t mix apples and oranges. The problem is that we can't add a number and a string.What we can do is convince an apple that it really is an orange, so by using the .to_s method converts a number into a string (to_s is short for to string).

That’s looping covered, two more things and then we’ll have programming all sewn up.

Conditional Love

If it is raining then I’ll get wet, unless I’m inside.

This is one of the places that Ruby is particularly lovely. It allows for (and you should strive for) your code to read out loud like a sentence.

it_is_raining = true

if it_is_raining
  puts "remember your umbrella"
end

We can make that a bit more complicated, and a little more useful

it_is_raining = true
i_am_inside = false

if it_is_raining
  unless i_am_inside
    puts "remember your umbrella"
  end
end

How cool is that? Read it out loud, for real – don't just pretend to, and it’ll all make sense. Try swapping the true and false values about to see what happens.

it_is_raining = true
i_am_inside = false

if it_is_raining
  unless i_am_inside
    puts "remember your umbrella"
  end
else
  puts 'slip slap slop'
end

That last snippet of code is important, it introduces the idea of what to do if it’s all going differently. The else clause tells you what to do if the first question comes up false.

There is a lot more to think about when writing conditional statements, but we’ll cover that in another lesson. This article is a pretty good way to understand conditionals.

And Finaly

There is only one last thing you need to know how to do, and you already know how to do it! When you write computer code, you need to spare a thought to your poor future brain, it needs to know what you were thinking, so you need to write comment on what your code actually does!

# this program was written on the 9/2/2011 by Ben Doherty
# it prints sartorial advice dependent on precipitation and location

# declare some variables
it_is_raining = true
i_am_inside = false

# get moving on the real meat of it all
if it_is_raining                   # if it_is_raining holds the value true then enter this block
  unless i_am_inside               # if i_am_inside holds the value false  then enter this block
    puts "remember your umbrella"  # write remember your umbrella to the console
  end                              # end the unless block
else                               # if it_is_raining holds the value false then enter this block
  puts 'slip slap slop'            # write slip slap slop to the console
end                                # end the if block

The # is the key to all of this. The compiler ignores everything from # to the end of the line. The way I like to do things is to have a header that says who and when, and then what, then following that, revision descriptions. There more you comment your code the better your life will be in the future.

Homework

Have a play with loops, conditionals and the string library and make up some complicated wordplay. Points for humor and slyness! Remember to comment extensively! Post your results in the comments below.

lesson 4 – part 1 – a box with a face

This will show you how to make a stack of boxes with the beautiful/terrifying faces
of all the people you work with

some faces mapped onto boxes

ben's face on a cube, he looks like a moron!First
we’ll make a single box with a picture on it like this:

Here’s all the code, see if you can read it, and I’ll break it down piece by piece
afterwards

#setup stuff


mod = Sketchup.active_model

ent = mod.entities
sel = mod.selection
materials = mod.materials


#create a cube


#some cube variables
edgeLength = 1000.mm
#because Sketchup is from a backward country they still
use inches.
# To make Sketchup convert mm to inches for its own internal use <number>.mm

#setup the corner points

#   x y z
points = [      
   

[0,

0, 0],
    [edgeLength, 0, 0],
    [edgeLength, edgeLength, 0],
    [0, edgeLength, 0]
  ] #add the bottom face  

myCubeBase = ent.add_face
points
myCubeBase.reverse!
#flip it. ! means ‘in place’ i.e. myCubeBase = myCubeBase.reverse


# Adds a material to the “in-use” material pallet.
m = materials.add
ben


# pulls a texture out of a folder
myPath ='C:\Users\bdoherty\Desktop\BVN_Photos\'
myImageFilename = BenDoherty_0024.jpg
m.texture = myPath +
myImageFilename

m.texture.size
= edgeLength


myCubeBase.material = m


#do a push pull to make it into a cube
myCube = myCubeBase.pushpull(edgeLength, true)

Set up

So, lets step through what is going on here. the first section, marked #setup stuff stuff is common to most sketchup
ruby files. It contains a bit of short-cutting, where we make the names for things
a bit shorter and easier to deal with, hence Sketchup.active_model
becomes mod, and therefore,
Sketchup.active_model.entities
becomes
sel
. This is just for convenience, and if you really wanted to, you
could say Sketchup.active_model.entities
every time you  wanted to talk about the things in the model.

Geometry

Next up we have the beginings of the excitment. We need to define how big we want
the cube to be, we could do this with some UI, but it's easier for now just
to type it in. As it says in the comments, the edge length needs to be specified
in inches for sketchup to be happy, so if we put in 1000 and leave it at that, then
we'll get 1000 inches, or almost 24 and a half metres. The way around that is
by using a method on number called mm. That sounds way more complicated than it
really is, so just remember to specify your units whenever you write a number.

The next line is a bit of a subterfuge, because really it is lots of lines that
ruby treats as one. Because sketchup is able to understand points descibed in a
few ways we can enter the points as an array of arrays of numbers. That's a
bit of a mouthful, so lets recap what it means.

A point can either be described as:

myPoint = Geom::Point3d.new(100,
200, 300)

or

myPoint = [100,
200
, 300]

They both do exactly the same thing, when it comes to passing points around. (The
first method allows you to talk about myPoint.x
which is easier to read than myPoint[0],
there are a ;pad of other clever things you can do with real points too. In the
future, I'd write points out as the first way, but for the moment, the second
way is easier to understand.)

So in the second way of writing the point, the numbers are in an array. _Why describes
this as staples holding a wiggly catterpillar down (don't the [square brackets]
look like staples!) and so we can think of the array of arrays as a collecting book
with big staples being used to staple a whole collection of wiggly number catterpillars
to the page.

We now have the corners of a square, and because of the Sketchup-way, 2d things
like squares get pushed about to turn into 3d things like cubes. So the next line
actually makes the face.

myCubeBase = ent.add_face
points

So the collection ent gets a face added
to it, and that face is called myCubeBase.

If we rush ahead and pushpull this square into a cube we'll have to work hard
to put a texture on each face, but if we put a texture on the square then the faces
that are generated by sketchup inherit the texture. Before we do that there
is one more tricky little thing that I don't fully have a handle on. regardless
of the order that the points get created in, the face always extrudes downwards
(don't ask me why just yet), so we need to flip it.

myCubeBase.reverse!
#flip it. ! means ‘in place’ i.e. myCubeBase = myCubeBase.reverse

You would have thought that the method that does this is called
flip
, but actually it is called reverse.
The ! is a signal
in ruby that something is going to happen to the object itself. Like a reflexive
pronoun, I was myself, we see ourselves, you talk to yourself, I play… see, the
list is endless, but it is happening ‘in place’, you are getting the object to do
something to itself, rather than getting it to make a copy of itself and then do
the thing to that copy.

Materials

Now we get to start being decorators.

In the setup section we defined materials as materials
= mod.materials
, (so ultimatly it is materials
= Sketchup.active_model.materials
that we are working with, but the
aliasing makes our life easier).

m = materials.add “ben”

This line makes a material. There is something a bit confusing going on, because
ruby calls the material m, and sketchup
calls the material ben – so which is
it?!

This is the first time you will come across
a very confusing subject called 'scope'. This cheesily grinning pair are
going to help me out while I explain scope.

I'm pretty sure that Mrs O calls Mr O some pretty impressive things in the context
of their 'private time', but if anyone else tried calling the commander
in chief of the most powerful army in the world big boy, or my personal
stallion
, then it wouldn't be long before Guantanamo re-opened.

So in the scope of the oval office, Mr O is refered to as 'Mr President',
but in his house he's probably just 'dad'.

In the scope of the ruby code, the material is called
m
, but in the scope of the sketchup environment it is called
ben
.

That'll do for scope for the moment, but don't forget about it, we'll
be coming back to it!

# pulls a texture out of a folder
myPath ='C:\Users\bdoherty\Desktop\BVN_Photos\'
myImageFilename = BenDoherty_0024.jpg
m.texture = myPath +
myImageFilename

Now we are peering out of our box; this is where we need to interact with the world
a bit. I'm keeping my folder full of people on my desktop, which becasue I'm
at work it at the path above. You'll need to look at your file path and work
out where it lives (this is tricky on a mac, but you can just drag the folder into
the editor and it will write out the path for you – clever things).

The picture of me is called BenDoherty_0024.jpg
so I’ve put that in as a variable. The next line makes the texture property of m be the path joined (or concatinated
with) the file name.

m.texture.size = edgeLength

This just sizes the texture so that it is the same size as the
face that it is going to be applied to.

myCubeBase.material = m

…and finally, this line applies that material to the square drawn on
the floor. Remember that the extruded box will inherit the material from the
square that it is extruded from.

#do a push pull to make it into a cube
myCube = myCubeBase.pushpull(edgeLength, true)

All of that faffing around, just to make a box, but at last this is the command that makes the square into a box.

Many boxes will come in part 2 of this.