essay on programming languages, computer science, information techonlogies and all.

Tuesday, April 15, 2014

Heartbeat of SketchUp animation pumping Ruby script

The heartbeat is a timer that ticks regularly. This tick runs Ruby script block, which increments position or angle of SketchUp's instance toward target. Refer below code snippet for a timer usage and block execution by the timer tick.

def run( model, period )
timer_id = UI.start_timer( period, true) do # for every beat of lub dub lub dub, run below block
@actions.delete_if do |a|
a.tick( period ) # move around instance
a.arrived? # remove if instance arrived at target
end
model.active_view.invalidate # update scene
if @actions.empty? then
UI.stop_timer timer_id # rest in peace if nothing left
end
end
end

In the code, I said the heart beats at precise interval of 'period' and you can say that I should somehow replace the timer with atom clock to get that behaviour. Well, we may meet in the middle by measuring duration; We can't make consistent period but we can get elapsed time between consequent timer calls. Refer below code.

attr_reader :last_time
def initialize
@last_time = 0.0
end
def run( model, period )
timer_id = UI.start_timer( period, true) {
if @last_time == 0.0
@last_time = Time.now.to_f
elapsed_time = period
else
current_time = Time.now.to_f
elapsed_time = current_time - @last_time
@last_time = current_time
end
@actions.delete_if do |a|
a.tick( elapsed_time )
a.arrived?
end
# snip snip
end


Below is code for object translation. 'entity.transform!' updates the position. This change will be shown by model.active_view.update - search up above code.

Updating position in the middle is easy as it is moving in constant speed. Difficulty lies at the boundaries - reminding me of studying differential equations, asking for great care on boundary condition at all times. In here I have to figure out where and when to stop. I am just checking the remaining distance to the target position in here.



class Translation < Action
attr_accessor :target, :velocity, :speed
def tick( duration )
return if arrived?
ds = @speed * duration # distance to cover in this tick
d = (@target - @entity.transformation.origin).normalize # direction vector in unit distance
d.x, d.y, d.z = d.x*ds, d.y*ds, d.z*ds # distance vector toward target
s = @entity.transformation.origin # start position
e = s + d # end position
dt = s.distance @target # distance between start and end
if ds >= dt # if reached the target
d = @target - s; # update distance vector with remaining distance
@is_arrived = true
end
@entity.transform! Geom::Transformation.new( d ) # update position of SketchUp instance
...
end
end

This simple approach - constant speed - makes unrealistic movements at the boundaries. It starts and stops in infinite acceleration. Better simulation should involve smooth change at ends.

Reference : Animate Yo' Cheese

Tuesday, April 8, 2014

One step further with Sketchup and RubyAPI

Trying to impress audience, I have gone further on this simulation and created this glass handling robot interacting with AOI and cassette.



Apparent complex movement are all come down to either rotation or translation in a certain axis. And then these simple motions are chained to each other or set to run concurrently. In the end, it is all about designing class to easily create, link and launch motions.