Here is my revised ruby saucer animation, very similar to what I produced before using java processing, there are 3 classes, two using the Processing::Proxy mixin that provides the access to Processing api that use of an inner class does for the java implementation:-
The Saucer class ('saucer.rb')
# saucer.rb
class Saucer
# makes use of mixin to replace java inner class
include Processing::Proxy
R1 = 4.0 # class constants that together
R2 = 3.0 # with PI constants are used
R3 = 6.0 # to define the Saucer shape
A1 = 0.25
A2 = 0.60
A3 = 0.0
MAX_THETA = QUARTER_PI/2
# read accessors
attr_reader :acc, :loc, :vel, :my_scale, :tilt, :wdth, :hght
def initialize xpos, ypos, acc_y, acc_x, width, height
@my_scale = 0.0
@tilt = 0
@loc = PVector.new xpos, ypos
@vel = PVector.new 0, 0
@acc = PVector.new acc_x, acc_y
@focus = PVector.new xpos, ypos
@wdth = width # frame width
@hght = height # frame height
end
def height # copes with tilting saucer
# avoided using ternary statement here
wd = R3 * 2 * Math.sin(tilt) * my_scale
ht = R2 * 2 * Math.sin(tilt) * my_scale
if (ht > wd)
return ht
else
return wd
end
end
def width # copes with tilting saucer
return R3 * 2 * Math.cos(tilt) * my_scale
end
def distance
@focus.dist loc
end
def get_point radius, theta
x = loc.x + (radius * Math.cos(theta + tilt)) *my_scale
y = loc.y + (radius * Math.sin(theta + tilt)) *my_scale
return x, y
end
def set_tilt(theta)
theta = Saucer::MAX_THETA if theta > Saucer::MAX_THETA
theta = -Saucer::MAX_THETA if theta < -Saucer::MAX_THETA
@tilt = theta
end
def set_position x, y
@loc.x = x
@loc.y = y
end
def set_scale(scale)
@my_scale = scale unless scale < 0
end
def render
smooth
curve_tightness(-0.05)
begin_shape
x, y = get_point(Saucer::R1, Saucer::A1)
curve_vertex(x, y)
x, y = get_point(Saucer::R2, TWO_PI - Saucer::A2)
curve_vertex(x, y)
x, y = get_point(Saucer::R2, PI + Saucer::A2)
curve_vertex(x, y)
x, y = get_point(Saucer::R1, PI - Saucer::A1)
curve_vertex(x, y)
x, y = get_point(Saucer::R2, PI + Saucer::A2)
vertex(x, y)
x, y = get_point(Saucer::R3, PI)
vertex(x, y)
vertex(x, y)
x, y = get_point(Saucer::R1, PI - Saucer::A1)
vertex(x, y)
vertex(x, y)
x, y = get_point(Saucer::R1, Saucer::A1)
vertex(x, y)
vertex(x, y)
x, y = get_point(Saucer::R3, Saucer::A3)
vertex(x, y)
vertex(x, y)
end_shape CLOSE
end
def update
@vel.x += acc.x
@vel.y += acc.y
@loc.x += vel.x
@loc.y += vel.y
set_scale distance/30
set_tilt vel.x * 0.04
end
def reset_x
@vel.x = 0
# ternary statement gets the job done, best used for assignment?
acc.x < 0 ? @loc.x = self.width/2 : @loc.x = (self.wdth - self.width/2)
@acc.x *= -1
end
def reset_y
@vel.y = 0
acc.y < 0 ? @loc.y = self.height/2 : @loc.y =(self.hght - self.height/2)
@acc.y *= -1
end
end
The starfield animation Star class star.rb
# star.rb author Martin Prout
class Star
# makes use of mixin to replace java inner class
include Processing::Proxy
attr_reader :acc, :loc, :star_size, :vel, :focus # NB accessors used by animation class
def initialize(sz = 0.0, acc = nil, x = 0.0, y = 0.0)
@star_size = sz
@vel = PVector.new
@acc = acc
@focus = PVector.new X_FOCUS, Y_FOCUS
# ensure stars are spread out from focus initially in correct direction
@loc = PVector.new(X_FOCUS + acc.x * 8000, Y_FOCUS + acc.y * 5000)
end
def distance # from focus
@focus.dist loc
end
def brightness # star brightness
distance * 1.3
end
def update
@vel.x += acc.x
@vel.y += acc.y
@loc.x += vel.x
@loc.y += vel.y
end
# reset_x == reset_y included to have common
# interface with Saucer class
def reset_x
@loc.x = focus.x
@loc.y = focus.y
@vel.x = acc.x
@vel.y = acc.y
end
def reset_y
reset_x
end
def width
star_size * 2
end
def height
star_size * 2
end
def render
fill 255, 255, 255, brightness
rect loc.x, loc.y, star_size, star_size
end
end
Heres the code that drives the saucer animation
=begin
starfield animation, makes use of Processing::Proxy mixin
to have separate Star and Saucer classes with access to Processing
ide revised version as of 3rd July 2009
author Martin Prout
=end
require 'star'
require 'saucer'
# global constants
PHI = (1 + Math.sqrt(5))/2
STARS = 150
X_FOCUS = 800/PHI
Y_FOCUS = 150
class StarfieldSketch < Processing::App
attr_reader :loc, :stars, :saucer
def setup
size 800, 500
smooth
no_stroke
frame_rate 60
start_x, start_y = get_acceleration 0.005, 0.007
if start_x.abs < 0.001 || start_y.abs < 0.0005 # guard against v. boring saucer
start_x = 0.007 # only slightly boring saucer moves right and down initially
start_y = 0.005
end
@saucer = Saucer.new X_FOCUS, Y_FOCUS, start_x, start_y, width, height
@stars = []
STARS.times { # populate with stars, some bigger
start_x, start_y = get_acceleration 0.002, 0.03
@stars << Star.new(1.5, PVector.new(start_x, start_y)) unless stars.size % 3 == 0
@stars << Star.new(2.2, PVector.new(start_x, start_y)) if stars.size % 3 == 0
}
end
def draw
background 22, 22, 80
stars.each { |star|
star.render
star.update
boundary star
}
fill 255, 0, 0
@saucer.render
@saucer.update
boundary @saucer
end
# returns a pair of random accelerations for use in PVector
def get_acceleration min, max
magnitude = rand * (max - min) + min
theta = rand * TWO_PI # randomise direction using ruby rand function
return magnitude*Math.cos(theta), magnitude*Math.sin(theta)
end
def boundary(star)
star.reset_x unless star.loc.x < width - star.width/2
star.reset_y unless star.loc.y < height - star.height/2
star.reset_x unless star.loc.x > star.width/2
star.reset_y unless star.loc.y > star.height/2
end
end
To see the animation in action visit http://myweb.tiscali.co.uk/monkstone/animated_saucer/animated_ruby_saucer.html patience is required as whole jruby library is loaded ~9 Mb
Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0
Thursday, 7 May 2009
Subscribe to:
Post Comments (Atom)
Followers
About Me
- monkstone
- I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2
No comments:
Post a Comment