# An example demonstrating the use of a hook to decouple subclasses in # ruby-processing. The base class here is Spin, and we have included # post_initialize as the hook (it does nothing in the base class). SpinArm # and SpinSpots both subclass Spin, but only SpinSpots requires the hook to # change the initialization. Note the use the hook means the subclass does # not need to call super def setup size 640, 360 @arm = SpinArm.new({ x: width/2, y: height/2, s: 0.01 }) @spots = SpinSpots.new({ x: width/2, y: height/2, s: -0.02, d: 90.0 }) end def draw background 204 @arm.display @spots.display end class Spin attr_accessor :x, :y, :speed attr_accessor :angle def initialize(args = {}) @x, @y = args[:x], args[:y] @speed = args[:s] @angle = args[:angle] || 0.0 post_initialize(args) # this is the hook end def update @angle += speed end def post_initialize args nil end end class SpinArm < Spin # inherit from (or "extend") class Spin # NB: initialize inherited from Spin class def display stroke_weight 1 stroke 0 push_matrix translate x, y update rotate angle line 0, 0, 165, 0 pop_matrix end end class SpinSpots < Spin attr_accessor :dim def post_initialize args @dim = args[:d] end def display no_stroke push_matrix translate x, y update rotate angle ellipse(-dim/2, 0, dim, dim) ellipse(dim/2, 0, dim, dim) pop_matrix end end
Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0
Showing posts with label inheritance. Show all posts
Showing posts with label inheritance. Show all posts
Tuesday, 24 June 2014
Decoupling Sub Classes Using Hook Messages (in ruby-processing)
About time I posted a bit more code:-
Labels:
hook messages,
inheritance,
ruby-processing,
Sandi Metz
Saturday, 5 October 2013
A custom Vector library for ruby processing
I recently got my copy of Practical Object Oriented Design in Ruby by Sandi Metz book, this got me thinking what could I could apply the ideas to in ruby-processing, and here is an early crack at it. Creating a custom (pure-ruby) vector library which can replace the hybrid RPVector (that extends PVector from processing) class of a previous post. This is very much a first crack (but it works, only difference needed is load_library :vec and use normalize! instead of normalize, much more ruby like I think) because I have ideas for extending the functionality along some of these lines, however my sentiment is with toxi re simple made easy. Further if I use vanilla processing logic the cross_product method (modulus, dist etc) could all live in Vec, but I can easily defer that decision. I haven't at this stage made use distance_squared externally, however this will be more efficient for testing boundary conditions of say a bouncing ball than regular dist. Not shown here are the rspec tests that I've been using to ensure the code behaves, I am tempted to bundle this and a Quaternion class and possibly some others as a core ruby-processing library, since the operations of PVector are not at all ruby like, and many more people seem to come to ruby-processing from ruby, not the other way round sadly.
A Little Test courtesy of Daniel Shiffman
Now interestingly the following also works so you can use the
class Vec attr_accessor :x, :y, :z EPSILON = 9.999999747378752e-05 # a value used by processing.org def initialize(x = 0 ,y = 0, z = 0) @x, @y, @z = x, y, z post_initialize end def post_initialize nil end def ==(vec) (x - vec.x).abs < EPSILON && (y - vec.y).abs < EPSILON && (z - vec.z).abs < EPSILON end end class Vec2D < Vec # Modulus of vec. Also known as length, size or norm def modulus Math.hypot(x, y) end def self.dist_squared(vec_a, vec_b) (vec_a.x - vec_b.x)**2 + (vec_a.y - vec_b.y)**2 end def self.dist(vec_a, vec_b) Math.hypot(vec_a.x - vec_b.x, vec_a.y - vec_b.y) end # vanilla processing returns a Vector, rather than Scalar (defaults to 3D result when z = 0) def cross_product(vec) x * vec.y - y * vec.x end # Scalar product, also known as inner product or dot product def dot(vec) x * vec.x + y * vec.y end def collinear_with?(vec) cross_product(vec).abs < EPSILON end def +(vec) Vec2D.new(x + vec.x, y + vec.y) end def -(vec) Vec2D.new(x - vec.x, y - vec.y) end def *(scalar) Vec2D.new(x * scalar, y * scalar) end def / (scalar) Vec2D.new(x / scalar, y / scalar) unless scalar == 0 end def normalize! @x, @y = x / modulus, y / modulus return self end alias :mag :modulus end class Vec3D < Vec def modulus Math.sqrt(x**2 + y**2 + z**2) end def self.dist_squared(vec_a, vec_b) (vec_a.x - vec_b.x)**2 + (vec_a.y - vec_b.y)**2 + (vec_a.z - vec_b.z)**2 end def self.dist(vec_a, vec_b) Math.sqrt(self.dist_squared(vec_a, vec_b)) end def cross_product(vec) xc = y * vec.z - z * vec.y yc = z * vec.x - x * vec.z zc = x * vec.y - y * vec.x Vec3D.new(xc, yc, zc) end # Scalar product, also known as inner product or dot product def dot(vec) x * vec.x + y * vec.y + z * vec.z end def collinear_with?(vec) cross_product(vec) == Vec3D.new end def +(vec) Vec3D.new(x + vec.x, y + vec.y, z + vec.z) end def -(vec) Vec3D.new(x - vec.x, y - vec.y, z - vec.z) end def * (scalar) Vec3D.new(x * scalar, y * scalar, z * scalar) end def / (scalar) Vec3D.new(x / scalar, y / scalar, z / scalar) unless scalar.abs < EPSILON end def normalize! @x, @y, @z = x / modulus, y / modulus, z / modulus return self end alias :mag :modulus end
A Little Test courtesy of Daniel Shiffman
# # Vector # by Daniel Shiffman. # # Demonstration some basic vector math: subtraction, normalization, scaling # Normalizing a vector sets its length to 1. # load_library :vec def setup size(640,360) end def draw background(0) # A vector that points to the mouse location mouse = Vec2D.new(mouse_x, mouse_y) # A vector that points to the center of the window center = Vec2D.new(width/2,height/2) # Subtract center from mouse which results in a vector that points from center to mouse mouse = mouse - center # note need assign result to mouse # Normalize the vector the ! means we are changing the value of mouse mouse.normalize! # Multiply its length by 150 (Scaling its length) mouse = mouse * 150 # note need assign the result to mouse translate(width/2,height/2) # Draw the resulting vector stroke(255) stroke_weight(4) line(0, 0, mouse.x, mouse.y) end
Now interestingly the following also works so you can use the
+=, -=, /=, and *= assignment methods ( but you cannot override += etc as a methods unlike C++ )So what this means is that in ruby += etc are just syntactic shortcuts, and not an operator in its own right (which is probably a good thing).
# # Vector # by Daniel Shiffman. # # Demonstration some basic vector math: subtraction, normalization, scaling # Normalizing a vector sets its length to 1. # load_library :vec def setup size(640,360) end def draw background(0) # A vector that points to the mouse location mouse = Vec2D.new(mouse_x, mouse_y) # A vector that points to the center of the window center = Vec2D.new(width/2,height/2) # Subtract center from mouse which results in a vector that points from center to mouse mouse -= center # note we can assign result to mouse using -= # Normalize the vector the ! means we are changing the value of mouse mouse.normalize! # Multiply its length by 150 (Scaling its length) mouse *= 150 # note we can assign result to mouse using *= translate(width/2,height/2) # Draw the resulting vector stroke(255) stroke_weight(4) line(0, 0, mouse.x, mouse.y) end
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