182 lines
3.3 KiB
Crystal
182 lines
3.3 KiB
Crystal
require "math"
|
|
|
|
include Math
|
|
|
|
module Funfriend
|
|
struct Vec2
|
|
property x : Float64
|
|
property y : Float64
|
|
|
|
def initialize(@x = 0.0, @y = 0.0)
|
|
end
|
|
def initialize(xy : Int32)
|
|
@x = @y = xy
|
|
end
|
|
def initialize(xy : Float64)
|
|
@x = @y = xy
|
|
end
|
|
def initialize(xy : NamedTuple(x: Int32, y: Int32))
|
|
initialize(xy[:x], xy[:y])
|
|
end
|
|
def initialize(xy : NamedTuple(x: Float64, y: Float64))
|
|
initialize(xy[:x], xy[:y])
|
|
end
|
|
def initialize(wh : NamedTuple(width: Int32, height: Int32))
|
|
initialize(wh[:width], wh[:height])
|
|
end
|
|
|
|
# swizzles
|
|
|
|
def xy
|
|
{x: @x, y: @y}
|
|
end
|
|
def yx
|
|
{x: @y, y: @x}
|
|
end
|
|
|
|
# int swizzles
|
|
|
|
def xy_i
|
|
{x: @x.to_i, y: @y.to_i}
|
|
end
|
|
def yx_i
|
|
{x: @y.to_i, y: @x.to_i}
|
|
end
|
|
|
|
def x_i
|
|
x.to_i
|
|
end
|
|
def y_i
|
|
y.to_i
|
|
end
|
|
|
|
# simpler swizzle
|
|
|
|
def values
|
|
{x, y}
|
|
end
|
|
|
|
def dot(other : Vec2)
|
|
x * other.x + y * other.y
|
|
end
|
|
|
|
def cross(other : Vec2)
|
|
Vec2.new(
|
|
self.x * other.y - self.y * other.x,
|
|
self.y * other.x - self.x * other.y
|
|
)
|
|
end
|
|
|
|
def angle
|
|
atan2(y, x)
|
|
end
|
|
|
|
def len
|
|
sqrt(x ** 2 + y ** 2)
|
|
end
|
|
def square_len
|
|
x ** 2 + y ** 2
|
|
end
|
|
|
|
def angle(other : Vec2)
|
|
self ** other / (self.length * other.length)
|
|
end
|
|
|
|
def +(other : Vec2)
|
|
Vec2.new(self.x + other.x, self.y + other.y)
|
|
end
|
|
def +(other : Number)
|
|
Vec2.new(self.x + other, self.y + other)
|
|
end
|
|
def -(other : Vec2)
|
|
Vec2.new(self.x - other.x, self.y - other.y)
|
|
end
|
|
def -(other : Number)
|
|
Vec2.new(self.x - other, self.y - other)
|
|
end
|
|
def -
|
|
Vec2.new(-self.x, -self.y)
|
|
end
|
|
def *(other : Vec2)
|
|
Vec2.new(self.x * other.x, self.y * other.y)
|
|
end
|
|
def *(other : Number)
|
|
Vec2.new(self.x * other, self.y * other)
|
|
end
|
|
def /(other : Vec2)
|
|
Vec2.new(self.x / other.x, self.y / other.y)
|
|
end
|
|
def /(other : Number)
|
|
Vec2.new(self.x / other, self.y / other)
|
|
end
|
|
|
|
def clone
|
|
Vec2.new(self.x, self.y)
|
|
end
|
|
|
|
def normalize!
|
|
m = length
|
|
unless m == 0
|
|
inverse = 1.0 / m
|
|
self.x *= inverse
|
|
self.y *= inverse
|
|
end
|
|
self
|
|
end
|
|
|
|
def normalize
|
|
clone.normalize!
|
|
end
|
|
|
|
def scale!(scale : Float64)
|
|
normalize!
|
|
self.x *= scale
|
|
self.y *= scale
|
|
end
|
|
|
|
def scale(scale : Float64)
|
|
clone.scale!(scale)
|
|
end
|
|
|
|
def dist(other : Vec2)
|
|
(self - other).len
|
|
end
|
|
def square_dist(other : Vec2)
|
|
(self - other).square_len
|
|
end
|
|
|
|
def ==(other : Vec2)
|
|
self.x == other.x && self.y == other.y
|
|
end
|
|
|
|
def !=(other : Vec2)
|
|
self.x != other.x || self.y != other.y
|
|
end
|
|
|
|
def to_s(io : IO)
|
|
io << "(#{x}, #{y})"
|
|
end
|
|
def inspect(io : IO)
|
|
io << "(x: #{x.inspect}, y: #{y.inspect})"
|
|
end
|
|
|
|
def self.zero
|
|
Vec2.new(0.0)
|
|
end
|
|
def self.additive_identity
|
|
Vec2.new(0.0)
|
|
end
|
|
def self.multiplicative_identity
|
|
Vec2.new(1.0)
|
|
end
|
|
|
|
def self.from_polar(angle : Float64, length : Float64)
|
|
Vec2.new(cos(angle) * length, sin(angle) * length)
|
|
end
|
|
|
|
def self.rand(length : Range = (0 .. 1))
|
|
from_polar((0..360).sample, length.sample)
|
|
end
|
|
end
|
|
end
|