Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0

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.
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

No comments:

Post a Comment

Followers

Blog Archive

About Me

My photo
I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2