Current section
Files
Jump to
Current section
Files
lib/math/vector.ex
defmodule SpaceDust.Math.Vector.Vector3D do
@moduledoc """
A 3D vector
"""
defstruct [
:x,
:y,
:z
]
end
defmodule SpaceDust.Math.Vector do
@moduledoc """
Vector operations
"""
alias SpaceDust.Math.Vector.Vector3D, as: Vector3D
alias SpaceDust.Math.Matrix, as: Matrix
@type vector() :: %Vector3D{
x: number,
y: number,
z: number
}
@doc "create a 3D vector from a list"
@spec fromList([number]) :: vector()
def fromList([x, y, z]) do
%Vector3D{
x: x,
y: y,
z: z
}
end
@spec add(vector(), vector()) :: vector()
@doc "add two 3D vectors"
def add(a, b) do
%Vector3D{
x: a.x + b.x,
y: a.y + b.y,
z: a.z + b.z
}
end
@spec subtract(vector(), vector()) :: vector()
@doc "subtract two 3D vectors"
def subtract(a, b) do
%Vector3D{
x: a.x - b.x,
y: a.y - b.y,
z: a.z - b.z
}
end
@spec dot(vector(), vector()) :: number
@doc "dot product of two 3D vectors"
def dot(a, b) do
a.x * b.x + a.y * b.y + a.z * b.z
end
@spec cross(vector(), vector()) :: vector()
@doc "cross product of two 3D vectors"
def cross(a, b) do
%Vector3D{
x: a.y * b.z - a.z * b.y,
y: a.z * b.x - a.x * b.z,
z: a.x * b.y - a.y * b.x
}
end
@spec magnitude(vector()) :: number
@doc "magnitude of a 3D vector"
def magnitude(a) do
:math.sqrt(a.x * a.x + a.y * a.y + a.z * a.z)
end
@spec normalize(vector()) :: vector()
@doc "normalize a 3D vector"
def normalize(a) do
m = magnitude(a)
%Vector3D{
x: a.x / m,
y: a.y / m,
z: a.z / m
}
end
@spec scale(vector(), number()) :: vector()
@doc "scale a 3D vector"
def scale(a, s) do
%Vector3D{
x: a.x * s,
y: a.y * s,
z: a.z * s
}
end
@spec angle(vector(), vector()) :: number
@doc "angle between two 3D vectors"
def angle(a, b) do
:math.acos(dot(a, b))
end
@spec rotate(vector(), Matrix.matrix()) :: vector()
@doc "rotate a 3D vector by a 3x3 matrix"
def rotate(a, m) do
%Vector3D{
x: a.x * m.m11 + a.y * m.m12 + a.z * m.m13,
y: a.x * m.m21 + a.y * m.m22 + a.z * m.m23,
z: a.x * m.m31 + a.y * m.m32 + a.z * m.m33
}
end
@spec rotateAroundAxis(vector(), vector(), number) :: vector()
@doc "rotate a 3D vector around an axis by an angle"
def rotateAroundAxis(a, axis, angle) do
c = :math.cos(angle)
s = :math.sin(angle)
t = 1 - c
%Vector3D{
x:
(t * axis.x * axis.x + c) * a.x + (t * axis.x * axis.y - s * axis.z) * a.y +
(t * axis.x * axis.z + s * axis.y) * a.z,
y:
(t * axis.x * axis.y + s * axis.z) * a.x + (t * axis.y * axis.y + c) * a.y +
(t * axis.y * axis.z - s * axis.x) * a.z,
z:
(t * axis.x * axis.z - s * axis.y) * a.x + (t * axis.y * axis.z + s * axis.x) * a.y +
(t * axis.z * axis.z + c) * a.z
}
end
@spec rotateZ(vector(), number) :: vector()
@doc "rotate a 3D vector about the z-axis"
def rotateZ(a, angle) do
rotateAroundAxis(a, fromList([0, 0, 1]), angle)
end
@spec rotateY(vector(), number) :: vector()
@doc "rotate a 3D vector about the y-axis"
def rotateY(a, angle) do
rotateAroundAxis(a, fromList([0, 1, 0]), angle)
end
@spec rotateX(vector(), number) :: vector()
@doc "rotate a 3D vector about the x-axis"
def rotateX(a, angle) do
rotateAroundAxis(a, fromList([1, 0, 0]), angle)
end
end