A couple of really neat features you should experiment with when using ruby-processing. One is the interactive editing mode 'rp5 watch my_program.rb' where you can update the code whilst the program is running. Another is the feature $app.find_method("ellipse"), well any other method besides "ellipse" if you will. Heres the code of a little app I was running with the above query, and all the ellipse methods were listed (including the camel-case variants such as ellipseMode ...).
class AppTest < Processing::App
def setup
size 100, 100
puts $app.find_method("vertex")
end
end
I edited my code to the above substituting "vertex" for ellipse, and on saving the vertex methods (and their camel-case variants) were listed. Pretty cool eh!!
Note that you should avoid camel-case as I get the feeling it is there for backward compatibility, and ruby methods should be preferred over processing where both APIs could be used...
PS: find_method search must be of regex variety, if you try "tan" for example you get 'curveTangent' as well as 'instance_of?' methods listed.
Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0
Thursday, 28 May 2009
Interactive editing using watch
Labels:
camel-case,
find_method,
interactive mode,
watch
Tuesday, 12 May 2009
Nautilus Shell Using Ruby Processing (no magic numbers)
Here we create a nautilus like shape using vertex and line functions, without any magic numbers, well apart from the A and B constants (which are empirically chosen so no magic really). This now old-style, as we can write applets without explicitly including the Processing::App wrapper. The code is implicitly wrapped in an applet which is more like the way things work in the java processing ide see later post (Interactive Ruby and Ruby Processing), I kind of like this style though.
# nautilus.rb
require 'ruby-processing'
class Nautilus < Processing::App
A = 0.8 # pitch constant
B = 1.4 # radius constant
def setup
translate 230, 120 # offset x and y from origin
rotate QUARTER_PI + HALF_PI # initial rotation
smooth
background 0
stroke_weight 1
stroke 255
for z in 8 .. 40 do # draw the radial lines first (it looks nicer)
line(get_x(z*A), get_y(z*A), get_x((z-8)*A), get_y((z-8)*A))
end
no_fill
begin_shape # begin spiral 'shell' shape
stroke_weight 4
stroke 255, 0, 0
for i in 0 .. 40 do
vertex(get_x(i*A), get_y(i*A))
end
end_shape
save_frame "nautilus.png"
end
def get_x theta
A*Math.cos(theta)*Math.exp(theta/Math.tan(B))
end
def get_y theta
A*Math.sin(theta)*Math.exp(theta/Math.tan(B))
end
end
Nautilus.new :width => 360, :height => 300, :title => "Approximate Nautilus"
Here is the result:-
# nautilus.rb
require 'ruby-processing'
class Nautilus < Processing::App
A = 0.8 # pitch constant
B = 1.4 # radius constant
def setup
translate 230, 120 # offset x and y from origin
rotate QUARTER_PI + HALF_PI # initial rotation
smooth
background 0
stroke_weight 1
stroke 255
for z in 8 .. 40 do # draw the radial lines first (it looks nicer)
line(get_x(z*A), get_y(z*A), get_x((z-8)*A), get_y((z-8)*A))
end
no_fill
begin_shape # begin spiral 'shell' shape
stroke_weight 4
stroke 255, 0, 0
for i in 0 .. 40 do
vertex(get_x(i*A), get_y(i*A))
end
end_shape
save_frame "nautilus.png"
end
def get_x theta
A*Math.cos(theta)*Math.exp(theta/Math.tan(B))
end
def get_y theta
A*Math.sin(theta)*Math.exp(theta/Math.tan(B))
end
end
Nautilus.new :width => 360, :height => 300, :title => "Approximate Nautilus"
Here is the result:-
Labels:
algorithm,
line strip,
nautilus,
ruby-processing,
vertex
Nautilus shell using vertex function
Here is sketch produced using 'magic numbers' calculated using a spreadsheet, iffy method but I like the result.
# vert.rb
require 'ruby-processing'
class Vert < Processing::App
def setup
no_fill
smooth
stroke_weight 4
stroke 255, 0, 0
background 0
begin_shape # begin spiral 'shell' shape
vertex 202.5, 200
vertex 202.07, 202.07
vertex 200, 203.42
vertex 197.17, 202.83
vertex 195.31, 200
vertex 196.12, 196.12
vertex 200, 193.58
vertex 205.31, 194.69
vertex 208.78, 200
vertex 207.27, 207.27
vertex 200, 212.03
vertex 190.05, 209.95
vertex 183.53, 200
vertex 186.38, 186.38
vertex 200, 177.46
vertex 218.65, 181.35
vertex 230.86, 200
vertex 225.54, 225.54
vertex 200, 242.26
vertex 165.04, 234.96
vertex 142.15, 200
vertex 152.13, 152.13
vertex 200, 120.79
vertex 265.53, 134.47
vertex 308.44, 200
vertex 289.72, 289.72
vertex 200, 348.47
vertex 77.16, 322.84
end_shape # end spiral 'shell' shape
stroke_weight 1 # draw the segment lines
stroke 255 # in lighter color and lighter weight
line 77.16, 322.84, 165.04, 234.96
line 200, 242.26, 200, 348.47
line 289.72, 289.72, 225.54, 225.54
line 308.44, 200, 230.86, 200
line 265.53, 134.47, 218.65, 181.35
line 200, 177.46, 200, 120.79
line 186.38, 186.38, 152.13, 152.13
line 183.53, 200, 142.15, 200
line 190.05, 209.95, 165.04, 234.96
line 200, 212.03, 200, 242.26
line 207.27, 207.27, 225.54, 225.54
line 230.86, 200, 208.78, 200
line 205.31, 194.69, 218.65, 181.35
line 200, 193.58, 200, 177.46
line 196.12, 196.12, 186.38, 186.38
line 195.31, 200, 183.53, 200
line 197.17, 202.83, 190.05, 209.95
save_frame "spiral.png"
end
end
Vert.new :width => 350, :height => 350, :title => "Nautilus?"
Here is the image, which I think manages that nautilus shell look:-
# vert.rb
require 'ruby-processing'
class Vert < Processing::App
def setup
no_fill
smooth
stroke_weight 4
stroke 255, 0, 0
background 0
begin_shape # begin spiral 'shell' shape
vertex 202.5, 200
vertex 202.07, 202.07
vertex 200, 203.42
vertex 197.17, 202.83
vertex 195.31, 200
vertex 196.12, 196.12
vertex 200, 193.58
vertex 205.31, 194.69
vertex 208.78, 200
vertex 207.27, 207.27
vertex 200, 212.03
vertex 190.05, 209.95
vertex 183.53, 200
vertex 186.38, 186.38
vertex 200, 177.46
vertex 218.65, 181.35
vertex 230.86, 200
vertex 225.54, 225.54
vertex 200, 242.26
vertex 165.04, 234.96
vertex 142.15, 200
vertex 152.13, 152.13
vertex 200, 120.79
vertex 265.53, 134.47
vertex 308.44, 200
vertex 289.72, 289.72
vertex 200, 348.47
vertex 77.16, 322.84
end_shape # end spiral 'shell' shape
stroke_weight 1 # draw the segment lines
stroke 255 # in lighter color and lighter weight
line 77.16, 322.84, 165.04, 234.96
line 200, 242.26, 200, 348.47
line 289.72, 289.72, 225.54, 225.54
line 308.44, 200, 230.86, 200
line 265.53, 134.47, 218.65, 181.35
line 200, 177.46, 200, 120.79
line 186.38, 186.38, 152.13, 152.13
line 183.53, 200, 142.15, 200
line 190.05, 209.95, 165.04, 234.96
line 200, 212.03, 200, 242.26
line 207.27, 207.27, 225.54, 225.54
line 230.86, 200, 208.78, 200
line 205.31, 194.69, 218.65, 181.35
line 200, 193.58, 200, 177.46
line 196.12, 196.12, 186.38, 186.38
line 195.31, 200, 183.53, 200
line 197.17, 202.83, 190.05, 209.95
save_frame "spiral.png"
end
end
Vert.new :width => 350, :height => 350, :title => "Nautilus?"
Here is the image, which I think manages that nautilus shell look:-
Labels:
logarithmic spiral,
ruby-processing,
vertex
Thursday, 7 May 2009
Animation of a ruby Saucer
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
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
Labels:
animation,
ruby-processing
Tuesday, 5 May 2009
Inner classes, workaround for ruby processing
Developments are moving a pace over on the processing implementation discourse http://processing.org/discourse/yabb2/YaBB.pl?num=1238452139. As you may have realized that classes in processing are generally inner classes, and so have access to methods of the enclosing PApplet class. This is not the same in ruby however Jeremy Ashkenas aka jashkenas has been busy developing a workaround for ruby processing (including processing as a mixin) which seems quite interesting. Here is an example using my Saucer class, I'm not super keen about my render method it doesn't look too ruby like (inherited from my java processing code):-
# saucer.rb
include Math
class Saucer
include Processing::Proxy
# include processing as Mixin
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
attr_reader :xpos, :ypos, :my_scale, :tilt
def initialize xpos, ypos
@xpos = xpos
@ypos = ypos
@my_scale = 10.0
@tilt = 0
end
def get_point radius, theta
x = xpos + (radius * Math.cos(theta + tilt)) *my_scale
y = ypos + (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_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
end
Here is the main code:-
require 'saucer'
class MixinsTest < Processing::App
def setup
@saucer = Saucer.new 70, 50
@saucer.set_tilt QUARTER_PI/2
@saucer1 = Saucer.new 130, 170
@saucer1.set_scale 15
@saucer1.set_tilt QUARTER_PI/3
@saucer2 = Saucer.new 210, 340
@saucer2.set_scale 25
end
def draw
background 11, 17, 67
fill 116, 39, 39
@saucer.render
fill 255, 0, 0
@saucer1.render
@saucer2.render
save_frame("saucer.png")
end
end
MixinsTest.new :width => 400, :height => 400, :title => "Inner Test"
Heres the result of running the code 'rp5 run main.rb'
# saucer.rb
include Math
class Saucer
include Processing::Proxy
# include processing as Mixin
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
attr_reader :xpos, :ypos, :my_scale, :tilt
def initialize xpos, ypos
@xpos = xpos
@ypos = ypos
@my_scale = 10.0
@tilt = 0
end
def get_point radius, theta
x = xpos + (radius * Math.cos(theta + tilt)) *my_scale
y = ypos + (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_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
end
Here is the main code:-
require 'saucer'
class MixinsTest < Processing::App
def setup
@saucer = Saucer.new 70, 50
@saucer.set_tilt QUARTER_PI/2
@saucer1 = Saucer.new 130, 170
@saucer1.set_scale 15
@saucer1.set_tilt QUARTER_PI/3
@saucer2 = Saucer.new 210, 340
@saucer2.set_scale 25
end
def draw
background 11, 17, 67
fill 116, 39, 39
@saucer.render
fill 255, 0, 0
@saucer1.render
@saucer2.render
save_frame("saucer.png")
end
end
MixinsTest.new :width => 400, :height => 400, :title => "Inner Test"
Heres the result of running the code 'rp5 run main.rb'
Labels:
inner class,
jashkenas,
ruby processing
Subscribe to:
Posts (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