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

No comments: