Skip to content

Commit

Permalink
Implement 3d vectors
Browse files Browse the repository at this point in the history
  • Loading branch information
ffoxdd committed Jun 18, 2018
1 parent ec14a9b commit 812856e
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 77 deletions.
107 changes: 107 additions & 0 deletions spec/vector3_spec.cr
@@ -0,0 +1,107 @@
require "./spec_helper"

require "benchmark"

describe Vector3 do
describe ".new" do
it "defaults to a point at the origin" do
Vector3.new.should eq(Vector3.new(0.0, 0.0, 0.0))
end

it "allows initializing components" do
Vector3.new(1.0, 2.0, 3.0).should eq(Vector3.new(1.0, 2.0, 3.0))
end
end

pending ".random" do
it "builds a random point around the origin" do
# vector = Vector3.random(3.0)
# vector.magnitude.should be_close(3.0, 1.0e-10)
end
end

describe "comparison operators" do
describe "#<" do
it "returns true if all components are < than the rhs components" do
(Vector3.new(2.0, 1.0, 1.0) < Vector3.new(2.0, 2.0, 2.0)).should eq(false)
(Vector3.new(1.0, 2.0, 1.0) < Vector3.new(2.0, 2.0, 2.0)).should eq(false)
(Vector3.new(1.0, 1.0, 2.0) < Vector3.new(2.0, 2.0, 2.0)).should eq(false)
(Vector3.new(1.0, 1.0, 1.0) < Vector3.new(2.0, 2.0, 2.0)).should eq(true)
end
end

describe "#>" do
it "returns true if all components are > than the rhs components" do
(Vector3.new(2.0, 3.0, 3.0) > Vector3.new(2.0, 2.0, 2.0)).should eq(false)
(Vector3.new(3.0, 2.0, 3.0) > Vector3.new(2.0, 2.0, 2.0)).should eq(false)
(Vector3.new(3.0, 3.0, 2.0) > Vector3.new(2.0, 2.0, 2.0)).should eq(false)
(Vector3.new(3.0, 3.0, 3.0) > Vector3.new(2.0, 2.0, 2.0)).should eq(true)
end
end

describe "#<=" do
it "returns true if all components are < than the rhs components" do
(Vector3.new(1.0, 1.0, 3.0) <= Vector3.new(2.0, 2.0, 2.0)).should eq(false)
(Vector3.new(1.0, 1.0, 2.0) <= Vector3.new(2.0, 2.0, 2.0)).should eq(true)
end
end

describe "#>=" do
it "returns true if all components are < than the rhs components" do
(Vector3.new(3.0, 3.0, 1.0) >= Vector3.new(2.0, 2.0, 2.0)).should eq(false)
(Vector3.new(3.0, 3.0, 2.0) >= Vector3.new(2.0, 2.0, 2.0)).should eq(true)
end
end
end

describe "#min" do
it "returns a vector that has the minimum of each component" do
vector_0 = Vector3.new(-1.0, 2.0, 3.0)
vector_1 = Vector3.new(3.0, -4.0, 2.0)

vector_0.min(vector_1).should eq(Vector3.new(-1.0, -4.0, 2.0))
end
end

describe "#max" do
it "returns a vector that has the maximum of each component" do
vector_0 = Vector3.new(-1.0, 2.0, 3.0)
vector_1 = Vector3.new(3.0, -4.0, 2.0)

vector_0.max(vector_1).should eq(Vector3.new(3.0, 2.0, 3.0))
end
end

describe "#magnitude" do
it "returns the magnitude of the vector" do
vector = Vector3.new(1.0, 2.0, 3.0)
vector.magnitude.should be_close(3.74165, 1e-5)
end
end

describe "#+/-" do
it "adds/subtracts vectors" do
vector_1 = Vector3.new(-3.0, 4.0, 1.0)
vector_2 = Vector3.new(8.0, 1.0, 1.0)

(vector_1 + vector_2).should eq(Vector3.new(5.0, 5.0, 2.0))
(vector_1 - vector_2).should eq(Vector3.new(-11.0, 3.0, 0.0))
end

it "adds/subtracts scalars" do
vector = Vector3.new(1.0, 2.0, 3.0)

(vector + 3.0).should eq(Vector3.new(4.0, 5.0, 6.0))
(vector - 2.0).should eq(Vector3.new(-1.0, 0.0, 1.0))
end
end

describe "#*/" do
it "performs scalar multiplication and division" do
vector = Vector3.new(2.0, -4.0, 3.0)

(vector * 3.0).should eq(Vector3.new(6.0, -12.0, 9.0))
(vector / 2.0).should eq(Vector3.new(1.0, -2.0, 1.5))
end
end
end
53 changes: 53 additions & 0 deletions src/vector.cr
@@ -0,0 +1,53 @@
module Vector(V)
def ==(rhs)
component_test(rhs) { |c0, c1| c0 == c1 }
end

def <(rhs)
component_test(rhs) { |c0, c1| c0 < c1 }
end

def >(rhs)
component_test(rhs) { |c0, c1| c0 > c1 }
end

def <=(rhs)
component_test(rhs) { |c0, c1| c0 <= c1 }
end

def >=(rhs)
component_test(rhs) { |c0, c1| c0 >= c1 }
end

def +(rhs : V)
component_zip_map(rhs) { |c0, c1| c0 + c1 }
end

def -(rhs : V)
component_zip_map(rhs) { |c0, c1| c0 - c1 }
end

def +(scalar)
component_map { |c| c + scalar }
end

def -(scalar)
component_map { |c| c - scalar }
end

def *(scalar)
component_map { |c| c * scalar }
end

def /(scalar)
component_map { |c| c / scalar }
end

def min(rhs)
component_zip_map(rhs) { |c0, c1| Math.min(c0, c1) }
end

def max(rhs)
component_zip_map(rhs) { |c0, c1| Math.max(c0, c1) }
end
end
103 changes: 26 additions & 77 deletions src/vector2.cr
@@ -1,90 +1,39 @@
require "math"

class Vector2
getter x
getter y
include Vector(self)

def initialize(@x=0.0, @y=0.0)
end
def initialize(@x = 0.0, @y = 0.0)
end

def self.random(radius)
theta = Random.rand * 2 * Math::PI
getter x, y

self.new(
Math.sin(theta) * radius,
Math.cos(theta) * radius,
)
end
def self.random(radius)
theta = Random.rand * 2 * Math::PI

def ==(rhs)
component_test(rhs) { |c0, c1| c0 == c1 }
end
self.new(
Math.sin(theta) * radius,
Math.cos(theta) * radius,
)
end

def <(rhs)
component_test(rhs) { |c0, c1| c0 < c1 }
end
def magnitude
Math.hypot(@x, @y)
end

def >(rhs)
component_test(rhs) { |c0, c1| c0 > c1 }
end
private def component_test(rhs)
yield(@x, rhs.x) && yield(@y, rhs.y)
end

def <=(rhs)
component_test(rhs) { |c0, c1| c0 <= c1 }
end
private def component_map
Vector2.new(yield(@x), yield(@y))
end

def >=(rhs)
component_test(rhs) { |c0, c1| c0 >= c1 }
end
private def component_zip_map(rhs)
Vector2.new(yield(@x, rhs.x), yield(@y, rhs.y))
end

def +(rhs : Vector2)
component_zip_map(rhs) { |c0, c1| c0 + c1 }
end

def -(rhs : Vector2)
component_zip_map(rhs) { |c0, c1| c0 - c1 }
end

def +(scalar)
component_map { |c| c + scalar }
end

def -(scalar)
component_map { |c| c - scalar }
end

def *(scalar)
component_map { |c| c * scalar }
end

def /(scalar)
component_map { |c| c / scalar }
end

def min(rhs)
component_zip_map(rhs) { |c0, c1| Math.min(c0, c1) }
end

def max(rhs)
component_zip_map(rhs) { |c0, c1| Math.max(c0, c1) }
end

def magnitude
Math.hypot(@x, @y)
end

private def component_test(rhs)
yield(@x, rhs.x) && yield(@y, rhs.y)
end

private def component_map
Vector2.new(yield(@x), yield(@y))
end

private def component_zip_map(rhs)
Vector2.new(yield(@x, rhs.x), yield(@y, rhs.y))
end

def self.infinite(sign)
Vector2.new(Float64::INFINITY * sign, Float64::INFINITY * sign)
end
def self.infinite(sign)
Vector2.new(Float64::INFINITY * sign, Float64::INFINITY * sign)
end
end
38 changes: 38 additions & 0 deletions src/vector3.cr
@@ -0,0 +1,38 @@
require "math"

class Vector3
include Vector(self)

def initialize(@x = 0.0, @y = 0.0, @z = 0.0)
end

getter x, y, z

def self.random(radius)
# TODO
end

def magnitude
Math.sqrt(@x**2 + @y**2 + @z**2)
end

private def component_test(rhs)
yield(@x, rhs.x) && yield(@y, rhs.y) && yield(@z, rhs.z)
end

private def component_map
Vector3.new(yield(@x), yield(@y), yield(@z))
end

private def component_zip_map(rhs)
Vector3.new(yield(@x, rhs.x), yield(@y, rhs.y), yield(@z, rhs.z))
end

def self.infinite(sign)
Vector3.new(
Float64::INFINITY * sign,
Float64::INFINITY * sign,
Float64::INFINITY * sign,
)
end
end

0 comments on commit 812856e

Please sign in to comment.