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.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s