Lesson 5 – Heavy duty

This lesson introduces us to some heavy duty concepts. We've been taking it easy so far, so strap in and get ready to twist your melon.

We'll cover:

  • hash tables
  • indirection
  • modules
  • functions
  • error catching

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

Take a look over the code. It is a bit longer, so it now lives in a little castle all of its own, here →

Open this 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.

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 baout when we say 'the bathroom', we know that if there is only one in our module, and that we are inside our module that we won'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, it's pretty clear what it does though! begin shouldn’t keep you up at night, it gets us moving along. Again, we are going to use this in a slightly more complicated way in a little while.

This is where things get fruity. We need to define some functions. 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, lets say bake a cake (this example comes up all the time, and recipes are easy to imagine). 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 bakign 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 language 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.

This is analogous 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.

Now we know a little about functions, lets skip merily on, and tryst the contents of the functions to behave the way it ought to for the moment.

This next chunk takes us back to modules. We are refering to two external files here. One is a list of the seats in the office, and the other is a list of the people in the office. and People is here and Seats is here, download them and put them in your sketchup lugins folder to keep things simple.

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 data files
  2. require ‘2010_11_11peopleData’       #information about people
  3. require ‘2010_11_11seatPositions.rb’ #information about the seats
  4. seats  = Seats::Positions #/__localise the information so
  5. people = BVNpeople::Data  #\  that it is in a nice local variable

In the top of the people 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 make 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:

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

OK, 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, it is a lot like an array, 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}

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.  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.

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.

If we unpack that statement and reslove 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:


'BenDoherty' => {
                 :surname => 'Doherty',
                 :name => 'Ben',

                 :usedName => 'Ben',

                 :date_hired => '5/07/10',

                 :initial_seat => 'V8',

                 :new_Seat_101015 => 'W3',

                 :title => 'unknown',

                 :Nationality => 'English',

                 :Gender => 'Male',

                 :Glasses => 'FALSE',

                 :Height => 1.8,

                 :image_fileName => 'BenDoherty_0024.jpg'
                },

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.

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.

  1. points = [Geom::Point3d.new(0         , 0,          0),
  2.           Geom::Point3d.new(edgeLength, 0,          0),
  3.           Geom::Point3d.new(edgeLength, edgeLength, 0),
  4.           Geom::Point3d.new(0         , edgeLength, 0)]

You'll notice that the block of 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 next line with a transformation.

  1. newOriginOffset = Geom::Point3d.new(xCoord, yCoord, 0)
  2. t = Geom::Transformation.new newOriginOffset
  3. for i in (0..3)
  4.   points[i] = t*points[i]
  5. 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 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 come sup 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.

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. If anything goes wrong it pushes a message through to the console tellign 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 rahter than just blowing up and 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 4 – part 2 – the wombats

So in true Blue Peter style, Here's one I made earlier, try to read it:

  1. #setup stuff
  2. mod = Sketchup.active_model
  3.  
  4. ent = mod.entities
  5. sel = mod.selection
  6. materials = mod.materials
  7.  
  8. #some cube variables
  9. edgeLength = 1000.mm
  10. gap = 100.mm
  11. offset = 0
  12.  
  13. #tell ruby where our textures are
  14. Dir.chdir (‘ C:\Users\bdoherty\Desktop\BVN_Photos‘)
  15.  
  16. Dir.foreach(“.“) { |file|
  17.   type = file.split(‘.‘)[1]
  18.   unless type == nil
  19.     type.downcase!
  20.     if (type ==’png‘) or (type==’jpg‘)
  21.       puts file
  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.       #add the bottom face
  30.       myCubeBase = ent.add_face points
  31.       myCubeBase.reverse! #flip it. ! means -in place- i.e. myCubeBase = myCubeBase.reverse
  32.       #do a push pull to make it into a cube**********
  33.       # Adds a material to the “in-use” material pallet.
  34.       materialName = file.chomp(‘.jpg‘)
  35.       m = materials.add materialName
  36.       # pulls a texture out of a folder
  37.       m.texture = file
  38.       unless m.texture == nil
  39.         m.texture.size = edgeLength
  40.       end
  41.       myCubeBase.material = m
  42.       myCube = myCubeBase.pushpull(edgeLength, true)
  43.       offset += edgeLength + gap
  44.     end
  45.   end
  46. }

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.

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.

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 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 first part of this 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…

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.

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.

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


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 cauught 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 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. The defensive programing won't allow things that pass that test through, so we have a better chance that there will be fewer errors within that block.

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.

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'. This is to give 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.

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 precess to change things outside the loop each time we go around. Imagine 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.

some faces mapped onto boxes

And that, dearly beloved, is how to make a lot of boxes with faces on them.