mirror of
https://github.com/SleepingInsomniac/pixelfaucet
synced 2025-01-18 22:26:32 +01:00
Consolidate vector classes
This commit is contained in:
parent
7ddb90c040
commit
257090e927
29 changed files with 292 additions and 342 deletions
|
@ -5,6 +5,7 @@ require "../src/transform2d"
|
|||
module PF
|
||||
class Affine < Game
|
||||
@bricks : Sprite
|
||||
@top_left : Vector2(Int32) = Vector[0, 0]
|
||||
@transform : Transform2d = Transform2d.new
|
||||
@angle = 0.0
|
||||
@size = 1.0
|
||||
|
@ -39,7 +40,7 @@ module PF
|
|||
b1.y.upto(b2.y) do |y|
|
||||
b1.x.upto(b2.x) do |x|
|
||||
point = @transform.apply(x, y).to_i
|
||||
if point >= Vector[0, 0] && point < @bricks.size
|
||||
if point >= @top_left && point < @bricks.size
|
||||
draw_point(x.to_i, y.to_i, @bricks.peak(point))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ module PF
|
|||
class Ball < Entity
|
||||
include CircleCollision
|
||||
|
||||
getter frame : Array(Vector(Float64, 2))
|
||||
getter frame : Array(Vector2(Float64))
|
||||
getter color = Pixel.random
|
||||
|
||||
def initialize(size : Float64)
|
||||
|
@ -31,10 +31,10 @@ module PF
|
|||
end
|
||||
|
||||
def add_ball
|
||||
position = Vector(Float64, 2).new(rand(0.0_f64..@width.to_f64), rand(0.0_f64..@height.to_f64))
|
||||
position = Vector2(Float64).new(rand(0.0_f64..@width.to_f64), rand(0.0_f64..@height.to_f64))
|
||||
ball = Ball.new(rand(10.0..30.0))
|
||||
ball.position = position
|
||||
ball.velocity = Vector(Float64, 2).new(rand(-50.0..50.0), rand(-50.0..50.0))
|
||||
ball.velocity = Vector2(Float64).new(rand(-50.0..50.0), rand(-50.0..50.0))
|
||||
@balls << ball
|
||||
end
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ class Wind
|
|||
@step : Float64?
|
||||
|
||||
struct Gust
|
||||
property position : PF::Vector(Float64, 2)
|
||||
property strength : PF::Vector(Float64, 2)
|
||||
property position : PF::Vector2(Float64)
|
||||
property strength : PF::Vector2(Float64)
|
||||
|
||||
def initialize(@position, @strength)
|
||||
end
|
||||
|
@ -34,7 +34,7 @@ class Wind
|
|||
while y < @height
|
||||
x = step / 2
|
||||
while x < @width
|
||||
@gusts << Gust.new(PF::Vector(Float64, 2).new(x, y), PF::Vector(Float64, 2).new(rand(-1.0..1.0), rand(-1.0..1.0)))
|
||||
@gusts << Gust.new(PF::Vector2(Float64).new(x, y), PF::Vector2(Float64).new(rand(-1.0..1.0), rand(-1.0..1.0)))
|
||||
x += step
|
||||
end
|
||||
y += step
|
||||
|
@ -44,12 +44,12 @@ end
|
|||
|
||||
class Flake
|
||||
property shape : UInt8
|
||||
property position : PF::Vector(Float64, 2)
|
||||
property position : PF::Vector2(Float64)
|
||||
property z_pos : Float64
|
||||
property velocity : PF::Vector(Float64, 2)
|
||||
property velocity : PF::Vector2(Float64)
|
||||
|
||||
def initialize(@position, @shape = rand(0_u8..2_u8), @z_pos = rand(0.0..1.0), velocity : PF::Vector(Float64, 2)? = nil)
|
||||
@velocity = velocity || PF::Vector(Float64, 2).new(rand(-2.0..2.0), rand(0.0..20.0))
|
||||
def initialize(@position, @shape = rand(0_u8..2_u8), @z_pos = rand(0.0..1.0), velocity : PF::Vector2(Float64)? = nil)
|
||||
@velocity = velocity || PF::Vector2(Float64).new(rand(-2.0..2.0), rand(0.0..20.0))
|
||||
end
|
||||
|
||||
def update(dt)
|
||||
|
|
|
@ -6,10 +6,10 @@ require "../src/shape"
|
|||
require "../src/vector"
|
||||
|
||||
class Triangle < PF::Entity
|
||||
property frame : Array(PF::Vector(Float64, 2))
|
||||
property frame : Array(PF::Vector2(Float64))
|
||||
|
||||
def initialize(*args, **kwargs)
|
||||
@frame = [] of PF::Vector(Float64, 2)
|
||||
@frame = [] of PF::Vector2(Float64)
|
||||
end
|
||||
|
||||
def update(dt)
|
||||
|
|
|
@ -8,7 +8,7 @@ describe Vector do
|
|||
it "multiplies 2 vectors" do
|
||||
v1 = Vector[1, 2]
|
||||
v2 = Vector[2, 2]
|
||||
(v1 * v2).should eq(Vector(Int32, 2).new(2, 4))
|
||||
(v1 * v2).should eq(Vector2(Int32).new(2, 4))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -93,4 +93,30 @@ describe Vector do
|
|||
v.to_i32.should eq(Vector[1, 2])
|
||||
end
|
||||
end
|
||||
|
||||
describe "Matrix multiplication" do
|
||||
it "returns the scaled value when multiplied by an identity matrix" do
|
||||
v = Vector[1, 2]
|
||||
m = Matrix[
|
||||
1, 0,
|
||||
0, 1,
|
||||
]
|
||||
(v * m).should eq(v)
|
||||
m = Matrix[
|
||||
2, 0,
|
||||
0, 1,
|
||||
]
|
||||
(v * m).should eq(Vector[2, 2])
|
||||
end
|
||||
|
||||
it "multiplies correctly" do
|
||||
v = Vector[2, 1, 3]
|
||||
m = Matrix[
|
||||
1, 2, 3,
|
||||
4, 5, 6,
|
||||
7, 8, 9,
|
||||
]
|
||||
(v * m).should eq(Vector[13, 31, 49])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
require "./vec3d"
|
||||
require "../transform3d"
|
||||
require "../vector"
|
||||
require "./mat4"
|
||||
|
||||
module PF
|
||||
class Camera
|
||||
property position : Vec3d(Float64) = Vec3d.new(0.0, 0.0, 0.0)
|
||||
property up : Vec3d(Float64) = Vec3d.new(0.0, 1.0, 0.0)
|
||||
property rotation : Vec3d(Float64) = Vec3d.new(0.0, 0.0, 0.0)
|
||||
property position : Vector3(Float64) = Vector3.new(0.0, 0.0, 0.0)
|
||||
property up : Vector3(Float64) = Vector3.new(0.0, 1.0, 0.0)
|
||||
property rotation : Vector3(Float64) = Vector3.new(0.0, 0.0, 0.0)
|
||||
|
||||
# Rotation about the X axis
|
||||
def pitch
|
||||
|
@ -35,15 +36,15 @@ module PF
|
|||
end
|
||||
|
||||
def forward_vector
|
||||
Vec3d.new(0.0, 0.0, 1.0) * rotation_matrix
|
||||
Transform3d.apply(Vector3.new(0.0, 0.0, 1.0), rotation_matrix)
|
||||
end
|
||||
|
||||
def strafe_vector
|
||||
Vec3d.new(1.0, 0.0, 0.0) * rotation_matrix
|
||||
Transform3d.apply(Vector3.new(1.0, 0.0, 0.0), rotation_matrix)
|
||||
end
|
||||
|
||||
def up_vector
|
||||
Vec3d.new(0.0, 1.0, 0.0) * rotation_matrix
|
||||
Transform3d.apply(Vector3.new(0.0, 1.0, 0.0), rotation_matrix)
|
||||
end
|
||||
|
||||
def matrix
|
||||
|
|
|
@ -13,7 +13,7 @@ module PF
|
|||
])
|
||||
end
|
||||
|
||||
def self.point_at(position : Vec3d, target : Vec3d, up : Vec3d = Vec3d.new(0.0, 1.0, 0.0))
|
||||
def self.point_at(position : Vector3, target : Vector3, up : Vector3 = Vector3.new(0.0, 1.0, 0.0))
|
||||
new_forward = (target - position).normalized
|
||||
new_up = (up - new_forward * up.dot(new_forward)).normalized
|
||||
new_right = new_up.cross(new_forward)
|
||||
|
@ -59,11 +59,11 @@ module PF
|
|||
])
|
||||
end
|
||||
|
||||
def self.rotation(r : Vec3d)
|
||||
def self.rotation(r : Vector3)
|
||||
Mat4.rot_x(r.x) * Mat4.rot_y(r.y) * Mat4.rot_z(r.z)
|
||||
end
|
||||
|
||||
def self.translation(pos : Vec3d)
|
||||
def self.translation(pos : Vector3)
|
||||
new(Slice[
|
||||
1.0, 0.0, 0.0, pos.x,
|
||||
0.0, 1.0, 0.0, pos.y,
|
||||
|
@ -119,7 +119,7 @@ module PF
|
|||
result
|
||||
end
|
||||
|
||||
def translate(pos : Vec3d)
|
||||
def translate(pos : Vector3)
|
||||
self * Mat4.translation(pos)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
module PF
|
||||
class Mesh
|
||||
setter tris = [] of Tri
|
||||
property origin = Vec3d(Float64).new(0.0, 0.0, 0.0)
|
||||
property rotation = Vec3d(Float64).new(0.0, 0.0, 0.0)
|
||||
property position = Vec3d(Float64).new(0.0, 0.0, 0.0)
|
||||
property origin = Vector3(Float64).new(0.0, 0.0, 0.0)
|
||||
property rotation = Vector3(Float64).new(0.0, 0.0, 0.0)
|
||||
property position = Vector3(Float64).new(0.0, 0.0, 0.0)
|
||||
|
||||
# Load an obj file
|
||||
def self.load_obj(path, use_normals : Bool = false)
|
||||
verticies = [] of Vec3d(Float64)
|
||||
texture_verticies = [] of Vec3d(Float64)
|
||||
normal_verticies = [] of Vec3d(Float64)
|
||||
verticies = [] of Vector3(Float64)
|
||||
texture_verticies = [] of Vector3(Float64)
|
||||
normal_verticies = [] of Vector3(Float64)
|
||||
tris = [] of Tri
|
||||
|
||||
line_no = 0
|
||||
|
@ -21,18 +21,18 @@ module PF
|
|||
case parts[0]
|
||||
when "v"
|
||||
w = parts[4]?.try { |n| n.to_f64 }
|
||||
verticies << Vec3d.new(x: parts[1].to_f64, y: parts[2].to_f64, z: parts[3].to_f64, w: w)
|
||||
verticies << Vector3.new(x: parts[1].to_f64, y: parts[2].to_f64, z: parts[3].to_f64)
|
||||
when "vt"
|
||||
v = parts[2]?.try { |n| n.to_f64 } || 0.0
|
||||
w = parts[3]?.try { |n| n.to_f64 } || 0.0
|
||||
texture_verticies << Vec3d.new(parts[1].to_f64, v, w)
|
||||
texture_verticies << Vector3.new(parts[1].to_f64, v, w)
|
||||
when "vn"
|
||||
if use_normals
|
||||
normal_verticies << Vec3d.new(parts[1].to_f64, parts[2].to_f64, parts[3].to_f64)
|
||||
normal_verticies << Vector3.new(parts[1].to_f64, parts[2].to_f64, parts[3].to_f64)
|
||||
end
|
||||
when "f"
|
||||
face_verts = [] of Vec3d(Float64)
|
||||
normal : Vec3d(Float64)? = nil
|
||||
face_verts = [] of Vector3(Float64)
|
||||
normal : Vector3(Float64)? = nil
|
||||
parts[1..].each do |part|
|
||||
face = part.split('/')
|
||||
face_verts << verticies[face[0].to_i - 1]
|
||||
|
|
|
@ -10,14 +10,14 @@ module PF
|
|||
getter fov = 90.0
|
||||
property aspect_ratio : Float64?
|
||||
property camera : Camera
|
||||
property light : Vec3d(Float64) = Vec3d.new(0.0, 0.0, -1.0).normalized
|
||||
property light : Vector3(Float64) = Vector3.new(0.0, 0.0, -1.0).normalized
|
||||
property mat_proj : Mat4?
|
||||
property clipping_plane_near : Vec3d(Float64)
|
||||
property near_plane_normal : Vec3d(Float64) = Vec3d.new(0.0, 0.0, 1.0)
|
||||
property clipping_plane_near : Vector3(Float64)
|
||||
property near_plane_normal : Vector3(Float64) = Vector3.new(0.0, 0.0, 1.0)
|
||||
@fov_rad : Float64?
|
||||
|
||||
def initialize(@width, @height, @camera = Camera.new)
|
||||
@clipping_plane_near = Vec3d.new(0.0, 0.0, @near)
|
||||
@clipping_plane_near = Vector3.new(0.0, 0.0, @near)
|
||||
end
|
||||
|
||||
def mat_proj
|
||||
|
@ -68,15 +68,11 @@ module PF
|
|||
shade = (tri.normal.dot(light) + 1.0) / 2 # light should be normalized
|
||||
tri.color = tri.color * shade.clamp(0.0..1.0)
|
||||
|
||||
tri.p1 *= mat_view
|
||||
tri.p2 *= mat_view
|
||||
tri.p3 *= mat_view
|
||||
tri *= mat_view
|
||||
|
||||
# Clip against the near plane
|
||||
tri.clip(plane: clipping_plane_near, plane_normal: near_plane_normal).each do |tri|
|
||||
tri.p1 *= mat_proj
|
||||
tri.p2 *= mat_proj
|
||||
tri.p3 *= mat_proj
|
||||
tri *= mat_proj
|
||||
|
||||
# Invert the y axis
|
||||
tri.p1.y = tri.p1.y * -1.0
|
||||
|
@ -104,10 +100,10 @@ module PF
|
|||
|
||||
# Clip against the edges of the screen
|
||||
{
|
||||
{Vec3d.new(0.0, 0.0, 0.0), Vec3d.new(0.0, 1.0, 0.0)},
|
||||
{Vec3d.new(0.0, height - 1.0, 0.0), Vec3d.new(0.0, -1.0, 0.0)},
|
||||
{Vec3d.new(0.0, 0.0, 0.0), Vec3d.new(1.0, 0.0, 0.0)},
|
||||
{Vec3d.new(width - 1.0, 0.0, 0.0), Vec3d.new(-1.0, 0.0, 0.0)},
|
||||
{Vector3.new(0.0, 0.0, 0.0), Vector3.new(0.0, 1.0, 0.0)},
|
||||
{Vector3.new(0.0, height - 1.0, 0.0), Vector3.new(0.0, -1.0, 0.0)},
|
||||
{Vector3.new(0.0, 0.0, 0.0), Vector3.new(1.0, 0.0, 0.0)},
|
||||
{Vector3.new(width - 1.0, 0.0, 0.0), Vector3.new(-1.0, 0.0, 0.0)},
|
||||
}.each do |clip|
|
||||
0.upto(tris.size - 1) do
|
||||
tri = tris.pop
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
require "../transform3d"
|
||||
require "../pixel"
|
||||
require "../g3d"
|
||||
|
||||
module PF
|
||||
struct Tri
|
||||
property p1 : Vec3d(Float64)
|
||||
property p2 : Vec3d(Float64)
|
||||
property p3 : Vec3d(Float64)
|
||||
property p1 : Vector3(Float64)
|
||||
property p2 : Vector3(Float64)
|
||||
property p3 : Vector3(Float64)
|
||||
property color : PF::Pixel
|
||||
|
||||
setter normal : Vec3d(Float64)?
|
||||
setter normal : Vector3(Float64)?
|
||||
|
||||
def initialize(@p1 : Vec3d(Float64), @p2 : Vec3d(Float64), @p3 : Vec3d(Float64), @color = PF::Pixel.white, @normal = nil)
|
||||
def initialize(@p1 : Vector3(Float64), @p2 : Vector3(Float64), @p3 : Vector3(Float64), @color = PF::Pixel.white, @normal = nil)
|
||||
end
|
||||
|
||||
def initialize(p1x : Float64, p1y : Float64, p1z : Float64, p2x : Float64, p2y : Float64, p2z : Float64, p3x : Float64, p3y : Float64, p3z : Float64, @color = PF::Pixel.white)
|
||||
@p1 = Vec3d(Float64).new(p1x, p1y, p1z)
|
||||
@p2 = Vec3d(Float64).new(p2x, p2y, p2z)
|
||||
@p3 = Vec3d(Float64).new(p3x, p3y, p3z)
|
||||
@p1 = Vector3(Float64).new(p1x, p1y, p1z)
|
||||
@p2 = Vector3(Float64).new(p2x, p2y, p2z)
|
||||
@p3 = Vector3(Float64).new(p3x, p3y, p3z)
|
||||
end
|
||||
|
||||
# Return the normal assuming clockwise pointing winding
|
||||
|
@ -45,19 +46,24 @@ module PF
|
|||
|
||||
# Multiply all points by a Mat4, returning a new Tri
|
||||
def *(mat : Mat4)
|
||||
Tri.new(@p1 * mat, @p2 * mat, @p3 * mat)
|
||||
Tri.new(
|
||||
Transform3d.apply(@p1, mat),
|
||||
Transform3d.apply(@p2, mat),
|
||||
Transform3d.apply(@p3, mat),
|
||||
@color
|
||||
)
|
||||
end
|
||||
|
||||
# Split the triangle based on which points are inside of a given plane
|
||||
# Returns a tuple of 0-2 triangles
|
||||
def clip(plane : Vec3d, plane_normal : Vec3d)
|
||||
def clip(plane : Vector3, plane_normal : Vector3)
|
||||
# Make sure plane normal is indeed normal
|
||||
plane_normal = plane_normal.normalized
|
||||
|
||||
# Create two temporary storage arrays to classify points either side of plane
|
||||
inside_points = StaticArray(Vec3d(Float64), 3).new(Vec3d(Float64).new(0.0, 0.0, 0.0))
|
||||
inside_points = StaticArray(Vector3(Float64), 3).new(Vector3(Float64).new(0.0, 0.0, 0.0))
|
||||
inside_count = 0
|
||||
outside_points = StaticArray(Vec3d(Float64), 3).new(Vec3d(Float64).new(0.0, 0.0, 0.0))
|
||||
outside_points = StaticArray(Vector3(Float64), 3).new(Vector3(Float64).new(0.0, 0.0, 0.0))
|
||||
outside_count = 0
|
||||
|
||||
# Classify each point as inside or outside of the plane
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
module PF
|
||||
struct Vec3d(T)
|
||||
property x : T
|
||||
property y : T
|
||||
property z : T
|
||||
property w : T
|
||||
|
||||
def initialize(@x : T, @y : T, @z : T, w = nil)
|
||||
@w = w || T.new(1)
|
||||
end
|
||||
|
||||
# Standard operations
|
||||
{% for op in %w[* / // + - %] %}
|
||||
def {{ op.id }}(other : Vec3d)
|
||||
Vec3d.new(@x {{op.id}} other.x, @y {{op.id}} other.y, @z {{op.id}} other.z)
|
||||
end
|
||||
|
||||
def {{ op.id }}(n : (Int | Float))
|
||||
Vec3d.new(@x {{op.id}} n, @y {{op.id}} n, @z {{op.id}} n)
|
||||
end
|
||||
{% end %}
|
||||
|
||||
{% for op in %w[- abs] %}
|
||||
def {{op.id}}
|
||||
Vec3d.new(@x.{{op.id}}, @y.{{op.id}}, @z.{{op.id}})
|
||||
end
|
||||
{% end %}
|
||||
|
||||
def *(matrix : Mat4)
|
||||
vec = Vec3d.new(
|
||||
@x * matrix[0, 0] + @y * matrix[1, 0] + @z * matrix[2, 0] + matrix[3, 0],
|
||||
@x * matrix[0, 1] + @y * matrix[1, 1] + @z * matrix[2, 1] + matrix[3, 1],
|
||||
@x * matrix[0, 2] + @y * matrix[1, 2] + @z * matrix[2, 2] + matrix[3, 2]
|
||||
)
|
||||
w = @x * matrix[0, 3] + @y * matrix[1, 3] + @z * matrix[2, 3] + matrix[3, 3]
|
||||
vec /= w # unless w == 0.0
|
||||
vec
|
||||
end
|
||||
|
||||
def cross(other : Vec3d)
|
||||
Vec3d.new(
|
||||
x: @y * other.z - @z * other.y,
|
||||
y: @z * other.x - @x * other.z,
|
||||
z: @x * other.y - @y * other.x
|
||||
)
|
||||
end
|
||||
|
||||
# Geth the length using pythagorean
|
||||
def magnitude
|
||||
Math.sqrt(@x ** 2 + @y ** 2 + @z ** 2)
|
||||
end
|
||||
|
||||
def normalized
|
||||
l = magnitude
|
||||
Vec3d.new(@x / l, @y / l, @z / l)
|
||||
end
|
||||
|
||||
# Returns the dot product
|
||||
def dot(other : Vec3d)
|
||||
@x * other.x + @y * other.y + @z * other.z
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,8 +5,8 @@ module PF
|
|||
class Entity
|
||||
property sprite : Sprite? = nil
|
||||
|
||||
property position : Vector(Float64, 2) = Vector[0.0, 0.0]
|
||||
property velocity : Vector(Float64, 2) = Vector[0.0, 0.0]
|
||||
property position : Vector2(Float64) = Vector[0.0, 0.0]
|
||||
property velocity : Vector2(Float64) = Vector[0.0, 0.0]
|
||||
property rotation : Float64 = 0.0
|
||||
property rotation_speed : Float64 = 0.0
|
||||
property mass : Float64 = 1.0
|
||||
|
|
|
@ -28,7 +28,7 @@ module PF
|
|||
|
||||
# Calculate the new velocities
|
||||
normal_vec = (position - other.position) / d
|
||||
tangental_vec = Vector(Float64, 2).new(-normal_vec.y, normal_vec.x)
|
||||
tangental_vec = Vector2(Float64).new(-normal_vec.y, normal_vec.x)
|
||||
|
||||
# Dot product of velocity with the tangent
|
||||
# (the direction in which to bounce towards)
|
||||
|
|
|
@ -3,7 +3,7 @@ module PF
|
|||
# Given a point on a plane *plane_point*, and a normal to the plane *plane_normal*,
|
||||
# see if a line from *line_start* to *line_end* intersects a plane, and return the
|
||||
# point at intersection
|
||||
def self.line_intersects_plane(plane_point : Vector(Float64, 3), plane_normal : Vector(Float64, 3), line_start : Vector(Float64, 3), line_end : Vector(Float64, 3))
|
||||
def self.line_intersects_plane(plane_point : Vector3(Float64), plane_normal : Vector3(Float64), line_start : Vector3(Float64), line_end : Vector3(Float64))
|
||||
plane_normal = plane_normal.normalized
|
||||
plane_dot_product = -plane_normal.dot(plane_point)
|
||||
ad = line_start.dot(plane_normal)
|
||||
|
|
|
@ -15,7 +15,7 @@ module PF
|
|||
getter width : Int32
|
||||
getter height : Int32
|
||||
|
||||
@viewport : Vector(Int32, 2)? = nil
|
||||
@viewport : Vector2(Int32)? = nil
|
||||
|
||||
delegate :draw_point, :draw_line, :draw_circle, :draw_triangle, :draw_rect, :draw_shape,
|
||||
:fill_triangle, :fill_rect, :fill_shape, to: @screen
|
||||
|
|
|
@ -2,9 +2,9 @@ require "./vector"
|
|||
|
||||
module PF
|
||||
struct Line(T)
|
||||
property p1 : Vector(T, 2), p2 : Vector(T, 2)
|
||||
property p1 : Vector2(T), p2 : Vector2(T)
|
||||
|
||||
def initialize(@p1 : Vector(T, 2), @p2 : Vector(T, 2))
|
||||
def initialize(@p1 : Vector2(T), @p2 : Vector2(T))
|
||||
end
|
||||
|
||||
def rise
|
||||
|
|
10
src/shape.cr
10
src/shape.cr
|
@ -12,7 +12,7 @@ module PF
|
|||
end
|
||||
|
||||
# Rotate points by *rotation*
|
||||
def self.rotate(points : Enumerable(Vector), rotation : Float64)
|
||||
def self.rotate(points : Enumerable(Vector2), rotation : Float64)
|
||||
rc = Math.cos(rotation)
|
||||
rs = Math.sin(rotation)
|
||||
|
||||
|
@ -22,22 +22,22 @@ module PF
|
|||
end
|
||||
|
||||
# Translate points by *translation*
|
||||
def self.translate(points : Enumerable(Vector), translation : Vector)
|
||||
def self.translate(points : Enumerable(Vector2), translation : Vector2)
|
||||
points.map { |p| p + translation }
|
||||
end
|
||||
|
||||
# ditto
|
||||
def self.translate(*points : Vector, translation : Vector)
|
||||
def self.translate(*points : Vector2, translation : Vector2)
|
||||
self.translation(points, translation: translation)
|
||||
end
|
||||
|
||||
# Scale points by a certain *amount*
|
||||
def self.scale(points : Enumerable(Vector), amount : Vector)
|
||||
def self.scale(points : Enumerable(Vector2), amount : Vector2)
|
||||
points.map { |p| p * amount }
|
||||
end
|
||||
|
||||
# calculate length from center for all points, and then get the average
|
||||
def self.average_radius(points : Enumerable(Vector))
|
||||
def self.average_radius(points : Enumerable(Vector2))
|
||||
points.map(&.length).reduce { |t, p| t + p } / points.size
|
||||
end
|
||||
end
|
||||
|
|
|
@ -55,7 +55,7 @@ module PF
|
|||
end
|
||||
|
||||
# ditto
|
||||
def draw_to(dest : SDL::Surface | Sprite, at : Vector(Int, 2))
|
||||
def draw_to(dest : SDL::Surface | Sprite, at : Vector2(Int))
|
||||
draw_to(dest, at.x, at.y)
|
||||
end
|
||||
|
||||
|
@ -70,7 +70,7 @@ module PF
|
|||
end
|
||||
|
||||
# ditto
|
||||
def peak(point : Vector(Int, 2))
|
||||
def peak(point : Vector2(Int))
|
||||
pixel_pointer(point.x, point.y).value
|
||||
end
|
||||
|
||||
|
@ -87,7 +87,7 @@ module PF
|
|||
end
|
||||
|
||||
# ditto
|
||||
def sample(point : Vector(Int, 2))
|
||||
def sample(point : Vector2(Int))
|
||||
sample(point.x, point.y)
|
||||
end
|
||||
|
||||
|
@ -105,7 +105,7 @@ module PF
|
|||
end
|
||||
|
||||
# ditto
|
||||
def sample(point : Vector(Int, 2), alpha = true)
|
||||
def sample(point : Vector2(Int), alpha = true)
|
||||
sample(point.x, point.y, true)
|
||||
end
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ module PF
|
|||
end
|
||||
end
|
||||
|
||||
def draw_circle(c : Vector(Int, 2), r : Int, pixel : Pixel = Pixel.new)
|
||||
def draw_circle(c : Vector2(Int), r : Int, pixel : Pixel = Pixel.new)
|
||||
draw_circle(c.x, c.y, r, pixel)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,12 +36,12 @@ module PF
|
|||
end
|
||||
|
||||
# ditto
|
||||
def draw_line(p1 : Vector(Int, 2), p2 : Vector(Int, 2), pixel : Pixel = Pixel.new)
|
||||
def draw_line(p1 : Vector2(Int), p2 : Vector2(Int), pixel : Pixel = Pixel.new)
|
||||
draw_line(p1.x, p1.y, p2.x, p2.y, pixel)
|
||||
end
|
||||
|
||||
# ditto
|
||||
def draw_line(p1 : Vector(Float, 2), p2 : Vector(Float, 2), pixel : Pixel = Pixel.new)
|
||||
def draw_line(p1 : Vector2(Float), p2 : Vector2(Float), pixel : Pixel = Pixel.new)
|
||||
draw_line(p1.to_i32, p2.to_i32, pixel)
|
||||
end
|
||||
|
||||
|
|
|
@ -13,12 +13,12 @@ module PF
|
|||
end
|
||||
|
||||
# ditto
|
||||
def draw_point(point : Vector(Int, 2), pixel : Pixel = Pixel.new)
|
||||
def draw_point(point : Vector2(Int), pixel : Pixel = Pixel.new)
|
||||
draw_point(point.x, point.y, pixel)
|
||||
end
|
||||
|
||||
# ditto
|
||||
def draw_point(point : Vector(Float, 2), pixel : Pixel = Pixel.new)
|
||||
def draw_point(point : Vector2(Float), pixel : Pixel = Pixel.new)
|
||||
draw_point(point.to_i32, pixel)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,12 +18,12 @@ module PF
|
|||
end
|
||||
|
||||
# ditto
|
||||
def draw_rect(p1 : PF::Vector(Int, 2), p2 : PF::Vector(Int, 2), pixel : Pixel = Pixel.new)
|
||||
def draw_rect(p1 : PF::Vector2(Int), p2 : PF::Vector2(Int), pixel : Pixel = Pixel.new)
|
||||
draw_rect(p1.x, p1.y, p2.x, p2.y, pixel)
|
||||
end
|
||||
|
||||
# ditto
|
||||
def draw_rect(size : PF::Vector(Int, 2), pixel : Pixel = Pixel.new)
|
||||
def draw_rect(size : PF::Vector2(Int), pixel : Pixel = Pixel.new)
|
||||
draw_rect(0, 0, size.x, size.y, pixel)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
module PF
|
||||
class Sprite
|
||||
# Draw lines enclosing a shape
|
||||
def draw_shape(points : Enumerable(Vector), pixel : Pixel = Pixel.new)
|
||||
def draw_shape(points : Enumerable(Vector2), pixel : Pixel = Pixel.new)
|
||||
0.upto(points.size - 1) do |n|
|
||||
draw_line(points[n], points[(n + 1) % points.size], pixel)
|
||||
end
|
||||
end
|
||||
|
||||
# Ditto
|
||||
def draw_shape(*points : Vector, color : Pixel = Pixel.new)
|
||||
def draw_shape(*points : Vector2, color : Pixel = Pixel.new)
|
||||
draw_shape(points, color)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module PF
|
||||
class Sprite
|
||||
# Draws 3 lines
|
||||
def draw_triangle(p1 : Vector, p2 : Vector, p3 : Vector, pixel : Pixel = Pixel.new)
|
||||
def draw_triangle(p1 : Vector2, p2 : Vector2, p3 : Vector2, pixel : Pixel = Pixel.new)
|
||||
draw_line(p1, p2, pixel)
|
||||
draw_line(p2, p3, pixel)
|
||||
draw_line(p3, p1, pixel)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module PF
|
||||
class Sprite
|
||||
# Fill an abitrary polygon. Expects a clockwise winding of points
|
||||
def fill_shape(points : Enumerable(Vector), color : Pixel = Pixel.new)
|
||||
def fill_shape(points : Enumerable(Vector2), color : Pixel = Pixel.new)
|
||||
return if points.empty?
|
||||
return draw_point(points[0], color) if points.size == 1
|
||||
return draw_line(points[0], points[1], color) if points.size == 2
|
||||
|
@ -61,7 +61,7 @@ module PF
|
|||
end
|
||||
end
|
||||
|
||||
def fill_shape(*points : Vector, color : Pixel = Pixel.new)
|
||||
def fill_shape(*points : Vector2, color : Pixel = Pixel.new)
|
||||
fill_shape(points, color)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@ require "../line"
|
|||
|
||||
module PF
|
||||
class Sprite
|
||||
def fill_triangle(p1 : Vector, p2 : Vector, p3 : Vector, pixel : Pixel = Pixel.new)
|
||||
def fill_triangle(p1 : Vector2, p2 : Vector2, p3 : Vector2, pixel : Pixel = Pixel.new)
|
||||
# Sort points from top to bottom
|
||||
p1, p2 = p2, p1 if p2.y < p1.y
|
||||
p1, p3 = p3, p1 if p3.y < p1.y
|
||||
|
@ -54,7 +54,7 @@ module PF
|
|||
end
|
||||
end
|
||||
|
||||
def fill_triangle(points : Enumerable(Vector), pixel : Pixel = Pixel.new)
|
||||
def fill_triangle(points : Enumerable(Vector2), pixel : Pixel = Pixel.new)
|
||||
fill_triangle(points[0], points[1], points[2], pixel)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -33,7 +33,7 @@ module PF
|
|||
self
|
||||
end
|
||||
|
||||
def translate(to : Vector)
|
||||
def translate(to : Vector2)
|
||||
translate(to.x, to.y)
|
||||
end
|
||||
|
||||
|
@ -106,11 +106,11 @@ module PF
|
|||
end
|
||||
|
||||
def apply(x : Float | Int, y : Float | Int)
|
||||
result = Vector[x, y, 1.0] * @matrix
|
||||
result = Vector[x, y, typeof(x, y).new(1)] * @matrix
|
||||
Vector[result.x, result.y]
|
||||
end
|
||||
|
||||
def apply(point : Vector)
|
||||
def apply(point : Vector2)
|
||||
apply(point.x, point.y)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,7 +48,7 @@ module PF
|
|||
self.rot_x(x) * self.rot_y(y) * self.rot_z(z)
|
||||
end
|
||||
|
||||
def self.rotation(angle : Vector(Float64, 3))
|
||||
def self.rotation(angle : Vector3(Float64))
|
||||
self.rotation(angle.x, angle.y, angle.z)
|
||||
end
|
||||
|
||||
|
@ -61,7 +61,7 @@ module PF
|
|||
]
|
||||
end
|
||||
|
||||
def self.translation(pos : Vector(Float64, 3))
|
||||
def self.translation(pos : Vector3(Float64))
|
||||
self.translation(pos.x, pos.y, pos.z)
|
||||
end
|
||||
|
||||
|
@ -78,7 +78,7 @@ module PF
|
|||
matrix
|
||||
end
|
||||
|
||||
def self.point_at(position : Vector(Float64, 3), target : Vector(Float64, 3), up : Vector(Float64, 3) = Vector[0.0, 1.0, 0.0])
|
||||
def self.point_at(position : Vector3(Float64), target : Vector3(Float64), up : Vector3(Float64) = Vector[0.0, 1.0, 0.0])
|
||||
new_forward = (target - position).normalized
|
||||
new_up = (up - new_forward * up.dot(new_forward)).normalized
|
||||
new_right = new_up.cross(new_forward)
|
||||
|
@ -92,7 +92,7 @@ module PF
|
|||
end
|
||||
|
||||
# TODO: Optionally return the result of w in some way (pointer / tuple?)
|
||||
def self.apply(point : Vector(Float64, 3), matrix : Matrix(Float64, 4, 4))
|
||||
def self.apply(point : Vector3(Float64), matrix : Matrix(Float64, 4, 4))
|
||||
vector = Vector[point.x, point.y, point.z, 1.0] * matrix
|
||||
if vector.w == 0.0
|
||||
Vector[vector.x, vector.y, vector.z]
|
||||
|
@ -101,6 +101,17 @@ module PF
|
|||
end
|
||||
end
|
||||
|
||||
def self.apply(point : Vector3(Float64), matrix : Mat4)
|
||||
vec = Vector3.new(
|
||||
point.x * matrix[0, 0] + point.y * matrix[1, 0] + point.z * matrix[2, 0] + matrix[3, 0],
|
||||
point.x * matrix[0, 1] + point.y * matrix[1, 1] + point.z * matrix[2, 1] + matrix[3, 1],
|
||||
point.x * matrix[0, 2] + point.y * matrix[1, 2] + point.z * matrix[2, 2] + matrix[3, 2]
|
||||
)
|
||||
w = point.x * matrix[0, 3] + point.y * matrix[1, 3] + point.z * matrix[2, 3] + matrix[3, 3]
|
||||
vec /= w unless w == 0.0
|
||||
vec
|
||||
end
|
||||
|
||||
def initialize
|
||||
@matrix = PF::Transform3d.identity
|
||||
end
|
||||
|
@ -128,14 +139,14 @@ module PF
|
|||
self
|
||||
end
|
||||
|
||||
def self.rotate(r : Vector(Float64, 3))
|
||||
def self.rotate(r : Vector3(Float64))
|
||||
rot_x(r.x)
|
||||
rot_y(r.y)
|
||||
rot_z(r.z)
|
||||
self
|
||||
end
|
||||
|
||||
def translate(pos : Vector(Float64, 3))
|
||||
def translate(pos : Vector3(Float64))
|
||||
@matrix = PF::Transform3d.translation * @matrix
|
||||
self
|
||||
end
|
||||
|
@ -147,13 +158,8 @@ module PF
|
|||
end
|
||||
|
||||
# TODO: Optionally return the result of w in some way (pointer / tuple?)
|
||||
def apply(point : Vector(Float64, 3))
|
||||
vector = Vector[point.x, point.y.point.z, 1.0] * @matrix
|
||||
if vector.w == 0.0
|
||||
Vector[vector.x, vector.y, vector.z]
|
||||
else
|
||||
Vector[vector.x, vector.y, vector.z] / vector.w
|
||||
end
|
||||
def apply(point : Vector3(Float64))
|
||||
PF::Transform3d.apply(point, @matrix)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
319
src/vector.cr
319
src/vector.cr
|
@ -1,183 +1,160 @@
|
|||
require "./matrix"
|
||||
|
||||
module PF
|
||||
struct Vector(T, S)
|
||||
property values : Slice(T)
|
||||
|
||||
module Vector
|
||||
# Creates a new `Vector` with the given *args*
|
||||
#
|
||||
# ```
|
||||
# v = Vector[1, 2]
|
||||
# v[0] # => 1
|
||||
# v[1] # => 2
|
||||
# v.class # => Vector(Int32, 2)
|
||||
# PF::Vector[1, 2] # => PF::Vec2(Int32)(@x=1, @y=2)
|
||||
# ```
|
||||
macro [](*args)
|
||||
%values = Slice(typeof({{*args}})).new({{args.size}}, typeof({{*args}}).new(0))
|
||||
{% for arg, i in args %}
|
||||
%values.to_unsafe[{{i}}] = {{arg}}
|
||||
{% end %}
|
||||
PF::Vector(typeof({{*args}}), {{args.size}}).new(%values)
|
||||
PF::Vector{{args.size}}(typeof({{*args}})).new(
|
||||
{% for arg in args %}
|
||||
{{ arg }},
|
||||
{% end %}
|
||||
)
|
||||
end
|
||||
|
||||
# Creates a new unitialized `Vector`
|
||||
def initialize
|
||||
@values = Slice(T).new(S, T.new(0))
|
||||
end
|
||||
|
||||
# Creates a new `Vector` from a `Slice`
|
||||
def initialize(@values)
|
||||
end
|
||||
|
||||
# Create a new `Vector` with the given values
|
||||
def initialize(*nums : T)
|
||||
@values = Slice(T).new(S) { |i| nums[i] }
|
||||
end
|
||||
|
||||
def size
|
||||
S
|
||||
end
|
||||
|
||||
{% for char, index in %w[x y z w] %}
|
||||
# Return positional values by common use index
|
||||
def {{ char.id }}
|
||||
values[{{ index }}]
|
||||
end
|
||||
|
||||
# Set positional values by common use index
|
||||
def {{ char.id }}=(value : T)
|
||||
values[{{ index }}] = value
|
||||
end
|
||||
{% end %}
|
||||
|
||||
def [](index : Int)
|
||||
values[index]
|
||||
end
|
||||
|
||||
def []=(index : Int, value : T)
|
||||
values[index] = value
|
||||
end
|
||||
|
||||
def ==(other : Vector)
|
||||
values == other.values
|
||||
end
|
||||
|
||||
# Standard operations
|
||||
{% for op in %w[* / // + - %] %}
|
||||
def {{ op.id }}(other : Vector)
|
||||
Vector(T, S).new(values.map_with_index { |v, i| v {{ op.id }} other[i] })
|
||||
end
|
||||
|
||||
def {{ op.id }}(n : (Int | Float))
|
||||
v = values.map { |v| v {{ op.id }} n }
|
||||
Vector(typeof(v.first), S).new(values.map { |v| v {{ op.id }} n })
|
||||
end
|
||||
{% end %}
|
||||
|
||||
# Comparison methods
|
||||
{% for op in %w[> < >= <=] %}
|
||||
def {{ op.id }}(other : Vector)
|
||||
values.zip(other.values).all? { |a, b| a {{ op.id }} b }
|
||||
end
|
||||
|
||||
def {{ op.id }}(n : (Int | Float))
|
||||
values.all? { |v| v {{ op.id }} n }
|
||||
end
|
||||
{% end %}
|
||||
|
||||
{% for op in %w[- abs] %}
|
||||
# Return a new vector with {{op}} applied to each value
|
||||
def {{op.id}}
|
||||
Vector(T, S).new(values.map(&.{{op.id}}))
|
||||
end
|
||||
{% end %}
|
||||
|
||||
# The length or magnitude of the vector calculated by the Pythagorean theorem
|
||||
def magnitude
|
||||
Math.sqrt(values.reduce(T.new(0)) { |m, v| m + v ** 2 })
|
||||
end
|
||||
|
||||
# ditto
|
||||
def length
|
||||
magnitude
|
||||
end
|
||||
|
||||
# Returns a new normalized unit `Vector`
|
||||
def normalized
|
||||
m = magnitude
|
||||
return self if m == 0.0
|
||||
i = (1.0 / m)
|
||||
Vector(Float64, S).new(values.map { |v| v * i })
|
||||
end
|
||||
|
||||
# Returns the dot product of this vector and another
|
||||
def dot(other : Vector)
|
||||
(self * other).values.reduce { |m, v| m + v }
|
||||
end
|
||||
|
||||
# Calculates the cross product of this vector and another based on the vector size
|
||||
def cross(other : Vector)
|
||||
{% if S == 2 %}
|
||||
Vector[
|
||||
x * other.y - y * other.x,
|
||||
y * other.x - x * other.y,
|
||||
]
|
||||
{% elsif S == 3 %}
|
||||
Vector[
|
||||
y * other.z - z * other.y,
|
||||
z * other.x - x * other.z,
|
||||
x * other.y - y * other.x,
|
||||
]
|
||||
{% elsif S == 4 %}
|
||||
Vector[
|
||||
y * other.z - z * other.y,
|
||||
z * other.x - x * other.z,
|
||||
x * other.y - y * other.x,
|
||||
T.new(0),
|
||||
]
|
||||
{% else %}
|
||||
raise "Cannot compute cross product of Vector size {{ S }}"
|
||||
{% end %}
|
||||
end
|
||||
|
||||
# Returns normalized value at a normal to the current vector
|
||||
def normal(other : Vector)
|
||||
cross(other).normalized
|
||||
end
|
||||
|
||||
# Returns the distance between two Vectors
|
||||
def distance(other : Vector)
|
||||
(self - other).magnitude
|
||||
end
|
||||
|
||||
def *(matrix : Matrix)
|
||||
# a b c x ax + by + cz
|
||||
# d e f * y = dx + ey + fz
|
||||
# g h i z gx + hy + iz
|
||||
new_values = matrix.values.each_slice(S)
|
||||
.map { |slice| slice.map_with_index { |v, i| v * values[i] }.reduce { |m, v| m + v } }
|
||||
new_vec = Vector(typeof(new_values.first), S).new
|
||||
new_values.each_with_index { |v, i| new_vec[i] = v }
|
||||
new_vec
|
||||
end
|
||||
|
||||
# Type conversion methods
|
||||
{% for method, type in {
|
||||
to_i: Int32, to_u: UInt32, to_f: Float64,
|
||||
to_i8: Int8, to_i16: Int16, to_i32: Int32, to_i64: Int64, to_i128: Int128,
|
||||
to_u8: UInt8, to_u16: UInt16, to_u32: UInt32, to_u64: UInt64, to_u128: UInt128,
|
||||
to_f32: Float32, to_f64: Float64,
|
||||
} %}
|
||||
def {{ method }}
|
||||
Vector({{ type }}, S).new(values.map(&.{{ method }}))
|
||||
end
|
||||
{% end %}
|
||||
end
|
||||
|
||||
alias Vec2 = Vector(Int32, 2)
|
||||
alias Vec2f = Vector(Float64, 2)
|
||||
alias Vec3 = Vector(Int32, 3)
|
||||
alias Vec3f = Vector(Float64, 3)
|
||||
alias Vec4 = Vector(Int32, 4)
|
||||
alias Vec4f = Vector(Float64, 4)
|
||||
{% for i in 2..4 %}
|
||||
{% vars = %w[x y z w] %}
|
||||
struct Vector{{i}}(T)
|
||||
{% for arg in 0...i %}
|
||||
property {{vars[arg].id}} : T
|
||||
{% end %}
|
||||
|
||||
def initialize({% for arg in 0...i %} @{{vars[arg].id}} : T, {% end %})
|
||||
end
|
||||
|
||||
# Returns the size of this vector
|
||||
# ```
|
||||
# PF::Vec2.new(1, 2).size => 2
|
||||
# ```
|
||||
def size
|
||||
{{ i.id }}
|
||||
end
|
||||
|
||||
# Converts this Vector into a `StaticArray(T, {{i}})`
|
||||
def to_a
|
||||
StaticArray[{% for arg in 0...i %} @{{vars[arg].id}}, {% end %}]
|
||||
end
|
||||
|
||||
{% for op in %w[> < >= <= ==] %}
|
||||
# Tests if all components of each vector meet the `{{op.id}}` condition
|
||||
def {{ op.id }}(other : Vector{{i}})
|
||||
{% for arg in 0...i %}
|
||||
return false unless @{{vars[arg].id}} {{op.id}} other.{{vars[arg].id}}
|
||||
{% end %}
|
||||
true
|
||||
end
|
||||
|
||||
# Tests if all components of this vector meet the `{{op.id}}` condition with the given *n*
|
||||
def {{ op.id }}(n : (Int | Float))
|
||||
{% for arg in 0...i %}
|
||||
return false unless @{{vars[arg].id}} {{op.id}} n
|
||||
{% end %}
|
||||
true
|
||||
end
|
||||
{% end %}
|
||||
|
||||
{% for op in %w[- abs] %}
|
||||
# Calls `{{ op.id }}` on all components of this vector
|
||||
def {{op.id}}
|
||||
Vector{{i}}(T).new({% for arg in 0...i %} @{{vars[arg].id}}.{{op.id}}, {% end %})
|
||||
end
|
||||
{% end %}
|
||||
|
||||
{% for op in %w[* / // + - %] %}
|
||||
# Applies `{{op.id}}` to all component of this vector with the corresponding component of *other*
|
||||
def {{ op.id }}(other : Vector{{i}})
|
||||
Vector[{% for arg in 0...i %} @{{vars[arg].id}} {{op.id}} other.{{vars[arg].id}}, {% end %}]
|
||||
end
|
||||
|
||||
# Applies `{{op.id}}` to all component of this vector with *n*
|
||||
def {{ op.id }}(n : (Int | Float))
|
||||
Vector[{% for arg in 0...i %} @{{vars[arg].id}} {{op.id}} n, {% end %}]
|
||||
end
|
||||
{% end %}
|
||||
|
||||
# The length or magnitude of the vector calculated by the Pythagorean theorem
|
||||
def magnitude
|
||||
Math.sqrt({% for arg in 0...i %} @{{vars[arg].id}} ** 2 {% if arg != i - 1 %} + {% end %}{% end %})
|
||||
end
|
||||
|
||||
# Returns a new normalized unit `Vector{{i}}`
|
||||
def normalized
|
||||
m = magnitude
|
||||
return self if m == 0
|
||||
i = (1.0 / m)
|
||||
Vector[{% for arg in 0...i %} @{{vars[arg].id}} * i, {% end %}]
|
||||
end
|
||||
|
||||
# Returns the dot product of this vector and *other*
|
||||
def dot(other : Vector{{i}})
|
||||
{% for arg in 0...i %} @{{vars[arg].id}} * other.{{vars[arg].id}} {% if arg != i - 1 %} + {% end %}{% end %}
|
||||
end
|
||||
|
||||
# Calculates the cross product of this vector and *other*
|
||||
def cross(other : Vector{{i}})
|
||||
{% if i == 2 %}
|
||||
Vector[
|
||||
x * other.y - y * other.x,
|
||||
y * other.x - x * other.y,
|
||||
]
|
||||
{% elsif i == 3 %}
|
||||
Vector[
|
||||
y * other.z - z * other.y,
|
||||
z * other.x - x * other.z,
|
||||
x * other.y - y * other.x,
|
||||
]
|
||||
{% elsif i == 4 %}
|
||||
Vector[
|
||||
y * other.z - z * other.y,
|
||||
z * other.x - x * other.z,
|
||||
x * other.y - y * other.x,
|
||||
T.new(0),
|
||||
]
|
||||
{% end %}
|
||||
end
|
||||
|
||||
# Returns normalized value at a normal to the current vector
|
||||
def normal(other : Vector{{i}})
|
||||
cross(other).normalized
|
||||
end
|
||||
|
||||
# Returns the distance between this vector and *other*
|
||||
def distance(other : Vector{{i}})
|
||||
(self - other).magnitude
|
||||
end
|
||||
|
||||
# Multiply this vector by a *matrix*
|
||||
#
|
||||
# ```
|
||||
# v = PF::Vector[1, 2, 3]
|
||||
# m = PF::Matrix[
|
||||
# 1, 0, 0,
|
||||
# 0, 2, 0,
|
||||
# 0, 0, 1,
|
||||
# ]
|
||||
# # => PF::Vec3(Int32)(@x=1, @y=4, @z=3)
|
||||
# ```
|
||||
def *(matrix : Matrix)
|
||||
PF::Vector[{% for col in 0...i %}
|
||||
{% for row in 0...i %} @{{ vars[row].id }} * matrix[{{row}}, {{col}}] {% if row != i - 1 %} + {% end %}{% end %},
|
||||
{% end %}]
|
||||
end
|
||||
|
||||
{% for method, type in {
|
||||
to_i: Int32, to_u: UInt32, to_f: Float64,
|
||||
to_i8: Int8, to_i16: Int16, to_i32: Int32, to_i64: Int64, to_i128: Int128,
|
||||
to_u8: UInt8, to_u16: UInt16, to_u32: UInt32, to_u64: UInt64, to_u128: UInt128,
|
||||
to_f32: Float32, to_f64: Float64,
|
||||
} %}
|
||||
# Convert this vector to {{ type }}
|
||||
def {{ method }}
|
||||
Vector{{i}}({{ type }}).new({% for arg in 0...i %} @{{vars[arg].id}}.{{method}}, {% end %})
|
||||
end
|
||||
{% end %}
|
||||
end
|
||||
{% end %}
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue