|
A large home for a prosperous owner, this four-square sports a ground-floor addition (possibly an original feature?) in the kitchen/dining area topped by a bay window, likely in the master suite. What really catches the eye, however, are the ionic columns around the front porch. (Actually, Tuscan columns with ionic capitals, as Greek columns would have been fluted.) In contrast to the columns, the upper panes of the dormer windows are a diamond-shaped Tudor style. Warwick, NY, c. 1920. |
|
|
Chapter 15—Animation with the Transformation Matrix |
Animation! Here we'll take SketchUp into the fourth dimension: time. We'll put components into motion. You will never again see SketchUp as just a 3D tool.
Uh, oh. The transformation matrix. If you think that sounds complicated, you are right. It is complicated. Fortunately, the SketchUp engineers have programmed all the complicated stuff and given us an API that couldn't be much easier to use. Here I'll give you the basics.
How does it do all this? See Appendix T. That was originally slated for the beginning of this chapter as the Google docs somewhat unhelpfully state, "Use of the transformation class requires a knowledge of geometrical transformations in 3 dimensions which is covered extensively on the Internet."
Happily, I found that the use of the Transformation class requires no such knowledge. It just requires one little factoid: the r,g,b coordinates of the instance are found at locations 12, 13 and 14 of the matrix. You can go to Appendix T now if you're afraid you might miss something. We'll wait for you.
martins_sketch_talk.rb has sprouted lots of new functions and classes. I hope you can say the same for your_sketch_talk.rb. Here I'll mention two: the circle function and the Column class. Before we get to them let me mention (but not provide a link!) the download package.
The Tutorial Companion Package includes an updated sketch_talk.rb. The classes are now in sketch_talk_classes.rb. (When your source code file starts to get large, it starts to get inconvenient. Two smaller files can be much handier.) It also includes updated documentation including all the new commands. You can download the new docs without the code. (Extract these files—one HTML and three graphics—to any convenient directory. Open the HTML in any convenient browser.)
I'll provide a link to the full package at the end of this chapter. At this point it would be good to split your own your_sketch_talk.rb into functions and classes. My functions load 'sketch_talk_classes.rb'. In finished code you should require, not load. During development that doesn't work. You improve your classes, and reload your_sketch_talk.rb. It sees require and does not load the improved classes. Make a note in the code to switch later.
The new API you need for a circle is the Entities method add_circle(). Its arguments are:
That normal? It's just like a face's normal. Here's a support function that I've added, which may explain it.
def make_normal( plane_or_normal )
if plane_or_normal.is_a?( String )
case plane_or_normal
when 'rg' then normal = [0,0,1]
when 'rb' then normal = [0,1,0]
when 'gb' then normal = [1,0,0]
else
raise "Plane must be 'rg', 'rb', 'gb' or a normal vector."
end
else
normal = plane_or_normal
end
return normal
end # of make_normal()
col() command in SketchUp. It's first four arguments are the circle. The last is the PushPull distance.
Yes, the tower leans just under four meters. (The Pisans are working on it. It used to lean more than five meters.)
On to more API. The Entities.add_circle() method returns an array of edges: the edges that make up the circle. You need these as adding a circle does not add a face. (Do you think it should? I think it should. Adding coplanar edges that bound a face should add a face, to be consistent.) You have to add the face:
def draw( ents )
@group = ents.add_group()
ents = @group.entities()
@edges = ents.add_circle( @center, @normal, @radius, @nsides )
@face = ents.add_face( @edges )
orient( @face )
ents.add_face( @edges )
Sketchup.active_model().selection().add( @group )
return @group
end # of draw()
With that, I've already shown you way too much code. Time for you to add a c() function and a Circle class. Good luck! (Hint: my class constructor does nothing except save the inputs into instance variables. The whole class is the constructor and the draw() method.)
col() command adds one parameter: the distance to pushpull() after you've drawn your circle. Add your own function and class. (Hint: if your Column's draw() method goes more than three lines, you are working too hard.)
| RS | RS | RS | U |
| RS | RS | RS | U |
| RS | RS | RS | U |
| Xt | Yt | Zt | Wt |
| RS | Rotation and Scale Matrix |
| U | Unused or application-specific use (may always be zero) |
| Xt ... | Translation Vector (Wt may always be one) |
Here's a ComponentInstance and its xform matrix in the Ruby Console:
The first thing I learned when I began looking at the xform matrix was that the Ruby Console didn't give you a good picture. I wanted a nice picture, so I frogged up a little WebDialog. That is the topic of Chapter 19, so I just give you the code here and invite you to copy it, not type it, into your_sketch_talk.rb. First, the function xf() goes into your SketchTalk command functions.
def xf( *args ) # display selected object's transformation matrix
=begin
No args? Launch dialog showing selected object's transformation matrix
One arg? Arg is an xform matrix.
Apply it to selection, redraw and display it in dialog.
=end
if args.length == 0
xform( "
<html>
<body>
<table align=left bgcolor=#f0f0ff border=1 cellpadding=3>
<tr>
<td id=c0 align=right> </td>
<td id=c1 align=right> </td>
<td id=c2 align=right> </td>
<td id=c3 align=right> </td>
</tr>
<tr>
<td id=c4 align=right> </td>
<td id=c5 align=right> </td>
<td id=c6 align=right> </td>
<td id=c7 align=right> </td>
</tr>
<tr>
<td id=c8 align=right> </td>
<td id=c9 align=right> </td>
<td id=c10 align=right> </td>
<td id=c11 align=right> </td>
</tr>
<tr>
<td id=c12 align=right> </td>
<td id=c13 align=right> </td>
<td id=c14 align=right> </td>
<td id=c15 align=right> </td>
</tr>
<script type='text/javascript'>
function set_vals( vals ) {
for ( var i = 0; i < 16; i++ ) {
td = document.getElementById( 'c' + i );
td.innerHTML = vals[i];
}
}
vals =
" )
else # end of display new dialog showing xform matrix
sel.move!( args[0] )
draw()
xf()
end
end # of xf()
sprintf()
Going right back to C, reappearing here in Ruby, the printf() function outputs to the Ruby Console and the sprintf() function outputs a string. Both can take arguments that direct the formatting of numbers. If we leave the formatting to the Ruby default, we'll get fractions with 15 decimal places. This is out of hand.
Here we'll use this format specifier: '%6.3f' to specify floating point, 6 places total, 3 places after the decimal point. (Ask Google for additional sprintf() documentation.)
xform() function, in your support functions section:
def xform( html )
model = Sketchup.active_model()
sel = model.selection()
if sel.length() == 0
puts 'Nothing selected.'
return
end
thing = sel[0]
if thing.is_a?( Sketchup::ComponentInstance ) ||
thing.is_a?( Sketchup::Group )
trans = thing.transformation()
else
puts 'model.selection()[0] is not a Group or ComponentInstance.'
return
end
# we don't really want stuff like 0.707106781186548
nums = trans.to_a()
snums = []
for i in 0..2 do snums.push( sprintf('%6.3f', nums[i]) ) end
snums.push( nums[3].to_s() )
for i in 4..6 do snums.push( sprintf('%6.3f', nums[i]) ) end
snums.push( nums[7].to_s() )
for i in 8..10 do snums.push( sprintf('%6.3f', nums[i]) ) end
snums.push( nums[11].to_s() )
for i in 12..15 do snums.push( nums[i].to_s() ) end
html += snums.inspect()
html += "
set_vals( vals );