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