Current section

Files

Jump to
graphmath lib Mat44.ex
Raw

lib/Mat44.ex

defmodule Graphmath.Mat44 do
@moduledoc """
This is the 3D mathematics library for graphmath.
This submodule handles 4x4 matrices using tuples of floats.
"""
@type mat44 :: { float, float, float, float,
float, float, float, float,
float, float, float, float,
float, float, float, float}
@type vec4 :: { float, float, float, float }
@type vec3 :: { float, float, float }
@doc"""
`identity()` creates an identity `mat44`.
This returns an identity `mat44`.
"""
@spec identity() :: mat44
def identity() do
{ 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1}
end
@doc"""
`zero()` creates a zeroed `mat44`.
This returns a zeroed `mat44`.
"""
@spec zero() :: mat44
def zero() do
{ 0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0}
end
@doc"""
`add(a,b)` adds one `mat44` to another `mat44`.
`a` is the first `mat44`.
`b` is the second `mat44`.
This returns a `mat44` which is the element-wise sum of `a` and `b`.
"""
@spec add( mat44, mat44) :: mat44
def add( a, b ) do
{ a11, a12, a13, a14,
a21, a22, a23, a24,
a31, a32, a33, a34,
a41, a42, a43, a44 } = a
{ b11, b12, b13, b14,
b21, b22, b23, b24,
b31, b32, b33, b34,
b41, b42, b43, b44} = b
{ a11 + b11, a12 + b12, a13 + b13, a14 + b14,
a21 + b21, a22 + b22, a23 + b23, a24 + b24,
a31 + b31, a32 + b32, a33 + b33, a34 + b34,
a41 + b41, a42 + b42, a43 + b43, a44 + b44 }
end
@doc"""
`subtract(a,b)` subtracts one `mat44` from another `mat44`.
`a` is the minuend.
`b` is the subtraherd.
This returns a `mat44` formed by the element-wise subtraction of `b` from `a`.
"""
@spec subtract( mat44, mat44) :: mat44
def subtract( a, b ) do
{ a11, a12, a13, a14,
a21, a22, a23, a24,
a31, a32, a33, a34,
a41, a42, a43, a44 } = a
{ b11, b12, b13, b14,
b21, b22, b23, b24,
b31, b32, b33, b34,
b41, b42, b43, b44} = b
{ a11 - b11, a12 - b12, a13 - b13, a14 - b14,
a21 - b21, a22 - b22, a23 - b23, a24 - b24,
a31 - b31, a32 - b32, a33 - b33, a34 - b34,
a41 - b41, a42 - b42, a43 - b43, a44 - b44 }
end
@doc"""
`scale( a, k )` scales every element in a `mat44` by a coefficient k.
`a` is the `mat44` to scale.
`k` is the float to scale by.
This returns a `mat44` `a` scaled element-wise by `k`.
"""
@spec scale( mat44, float) :: mat44
def scale( a, k) do
{ a11, a12, a13, a14,
a21, a22, a23, a24,
a31, a32, a33, a34,
a41, a42, a43, a44 } = a
{ a11 * k, a12 * k, a13 * k, a14 * k,
a21 * k, a22 * k, a23 * k, a24 * k,
a31 * k, a32 * k, a33 * k, a34 * k,
a41 * k, a42 * k, a43 * k, a44 * k }
end
@doc"""
`make_scale( k )` creates a `mat44` that uniformly scales.
`k` is the float value to scale by.
This returns a `mat44` whose diagonal is all `k`s.
"""
@spec make_scale( float ) :: mat44
def make_scale( k ) do
{ k, 0, 0, 0,
0, k, 0, 0,
0, 0, k, 0,
0, 0, 0, k }
end
@doc"""
`make_scale( sx, sy, sz, sw )` creates a `mat44` that scales each axis independently.
`sx` is a float for scaling the x-axis.
`sy` is a float for scaling the y-axis.
`sz` is a float for scaling the z-axis.
`sw` is a float for scaling the w-axis.
This returns a `mat44` whose diagonal is `{ sx, sy, sz, sw }`.
Note that, when used with `vec3`s via the *transform* methods, `sw` will have no effect.
"""
@spec make_scale( float, float, float, float ) :: mat44
def make_scale( sx, sy, sz, sw ) do
{ sx, 0, 0, 0,
0, sy, 0, 0,
0, 0, sz, 0,
0, 0, 0, sw}
end
@doc"""
`make_translate( tx, ty, tz )` creates a mat44 that translates a point by tx, ty, and tz.
`make_translate( tx, ty, tz )` creates a mat44 that translates a vec3 by (tx, ty, tz).
`tx` is a float for translating along the x-axis.
`ty` is a float for translating along the y-axis.
`tz` is a float for translating along the z-axis.
This returns a `mat44` which translates by a `vec3` `{ tx, ty, tz }`.
"""
@spec make_translate( float, float, float ) :: mat44
def make_translate( tx, ty, tz ) do
{ 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
tx, ty, tz, 1 }
end
@doc"""
`make_rotate_x( theta )` creates a `mat44` that rotates a `vec3` by `theta` radians about the +X axis.
`theta` is the float of the number of radians of rotation the matrix will provide.
This returns a `mat44` which rotates by `theta` radians about the +X axis.
"""
@spec make_rotate_x( float ) :: mat44
def make_rotate_x( theta ) do
st = :math.sin(theta)
ct = :math.cos(theta)
{ 1, 0, 0, 0,
0, ct, st, 0,
0, -st, ct, 0,
0, 0, 0, 1 }
end
@doc"""
`make_rotate_y( theta )` creates a `mat44` that rotates a `vec3` by `theta` radians about the +Y axis.
`theta` is the float of the number of radians of rotation the matrix will provide.
This returns a `mat44` which rotates by `theta` radians about the +Y axis.
"""
@spec make_rotate_y( float ) :: mat44
def make_rotate_y( theta ) do
st = :math.sin(theta)
ct = :math.cos(theta)
{ ct, 0, st, 0,
0, 1, 0, 0,
-st, 0, ct, 0,
0, 0, 0, 1 }
end
@doc"""
`make_rotate_Z( theta )` creates a `mat44` that rotates a `vec3` by `theta` radians about the +Z axis.
`theta` is the float of the number of radians of rotation the matrix will provide.
This returns a `mat44` which rotates by `theta` radians about the +Z axis.
"""
@spec make_rotate_z( float ) :: mat44
def make_rotate_z( theta ) do
st = :math.sin(theta)
ct = :math.cos(theta)
{ ct, st, 0, 0,
-st, ct, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 }
end
@doc"""
`round( a, sigfigs )` rounds every element of a `mat44` to some number of decimal places.
`a` is the `mat44` to round.
`sigfigs` is an integer on [0,15] of the number of decimal places to round to.
This returns a `mat44` which is the result of rounding `a`.
"""
@spec round( mat44, 0..15 ) :: mat44
def round( a, sigfigs ) do
{ a11, a12, a13, a14,
a21, a22, a23, a24,
a31, a32, a33, a34,
a41, a42, a43, a44 } = a
{
Float.round( 1.0*a11, sigfigs),
Float.round( 1.0*a12, sigfigs),
Float.round( 1.0*a13, sigfigs),
Float.round( 1.0*a14, sigfigs),
Float.round( 1.0*a21, sigfigs),
Float.round( 1.0*a22, sigfigs),
Float.round( 1.0*a23, sigfigs),
Float.round( 1.0*a24, sigfigs),
Float.round( 1.0*a31, sigfigs),
Float.round( 1.0*a32, sigfigs),
Float.round( 1.0*a33, sigfigs),
Float.round( 1.0*a34, sigfigs),
Float.round( 1.0*a41, sigfigs),
Float.round( 1.0*a42, sigfigs),
Float.round( 1.0*a43, sigfigs),
Float.round( 1.0*a44, sigfigs)
}
end
@doc"""
`multiply( a, b )` multiply two matrices a and b together.
`a` is the `mat44` multiplicand.
`b` is the `mat44` multiplier.
This returns the `mat44` product of the `a` and `b`.
"""
@spec multiply( mat44, mat44 ) :: mat44
def multiply( a, b ) do
{ a11, a12, a13, a14,
a21, a22, a23, a24,
a31, a32, a33, a34,
a41, a42, a43, a44 } = a
{ b11, b12, b13, b14,
b21, b22, b23, b24,
b31, b32, b33, b34,
b41, b42, b43, b44} = b
{
(a11*b11) + (a12*b21) + (a13*b31) + (a14*b41),
(a11*b12) + (a12*b22) + (a13*b32) + (a14*b42),
(a11*b13) + (a12*b23) + (a13*b33) + (a14*b43),
(a11*b14) + (a12*b24) + (a13*b34) + (a14*b44),
(a21*b11) + (a22*b21) + (a23*b31) + (a24*b41),
(a21*b12) + (a22*b22) + (a23*b32) + (a24*b42),
(a21*b13) + (a22*b23) + (a23*b33) + (a24*b43),
(a21*b14) + (a22*b24) + (a23*b34) + (a24*b44),
(a31*b11) + (a32*b21) + (a33*b31) + (a34*b41),
(a31*b12) + (a32*b22) + (a33*b32) + (a34*b42),
(a31*b13) + (a32*b23) + (a33*b33) + (a34*b43),
(a31*b14) + (a32*b24) + (a33*b34) + (a34*b44),
(a41*b11) + (a42*b21) + (a43*b31) + (a44*b41),
(a41*b12) + (a42*b22) + (a43*b32) + (a44*b42),
(a41*b13) + (a42*b23) + (a43*b33) + (a44*b43),
(a41*b14) + (a42*b24) + (a43*b34) + (a44*b44),
}
end
@doc"""
`multiply_transpose( a, b )` multiply two matrices a and b<sup>T</sup> together.
`a` is the `mat44` multiplicand.
`b` is the `mat44` multiplier.
This returns the `mat44` product of the `a` and `b`<sup>T</sup>.
"""
@spec multiply_transpose( mat44, mat44 ) :: mat44
def multiply_transpose( a, b ) do
{ a11, a12, a13, a14,
a21, a22, a23, a24,
a31, a32, a33, a34,
a41, a42, a43, a44 } = a
{ b11, b21, b31, b41,
b12, b22, b32, b42,
b13, b23, b33, b43,
b14, b24, b34, b44} = b
{
(a11*b11) + (a12*b21) + (a13*b31) + (a14*b41),
(a11*b12) + (a12*b22) + (a13*b32) + (a14*b42),
(a11*b13) + (a12*b23) + (a13*b33) + (a14*b43),
(a11*b14) + (a12*b24) + (a13*b34) + (a14*b44),
(a21*b11) + (a22*b21) + (a23*b31) + (a24*b41),
(a21*b12) + (a22*b22) + (a23*b32) + (a24*b42),
(a21*b13) + (a22*b23) + (a23*b33) + (a24*b43),
(a21*b14) + (a22*b24) + (a23*b34) + (a24*b44),
(a31*b11) + (a32*b21) + (a33*b31) + (a34*b41),
(a31*b12) + (a32*b22) + (a33*b32) + (a34*b42),
(a31*b13) + (a32*b23) + (a33*b33) + (a34*b43),
(a31*b14) + (a32*b24) + (a33*b34) + (a34*b44),
(a41*b11) + (a42*b21) + (a43*b31) + (a44*b41),
(a41*b12) + (a42*b22) + (a43*b32) + (a44*b42),
(a41*b13) + (a42*b23) + (a43*b33) + (a44*b43),
(a41*b14) + (a42*b24) + (a43*b34) + (a44*b44),
}
end
@doc"""
`column0( a )` selects the first column of a `mat44`.
`a` is the `mat44` to take the first column of.
This returns a `vec4` representing the first column of `a`.
"""
@spec column0( mat44 ) :: vec4
def column0( a ) do
{ a11, _, _, _,
a21, _, _, _,
a31, _, _, _,
a41, _, _, _} = a
{a11,a21,a31,a41}
end
@doc"""
`column1( a )` selects the second column of a `mat44`.
`a` is the `mat44` to take the second column of.
This returns a `vec4` representing the second column of `a`.
"""
@spec column1( mat44 ) :: vec4
def column1( a ) do
{ _, a12, _, _,
_, a22, _, _,
_, a32, _, _,
_, a42, _, _} = a
{a12,a22,a32,a42}
end
@doc"""
`column2( a )` selects the third column of a `mat44`.
`a` is the `mat44` to take the third column of.
This returns a `vec4` representing the third column of `a`.
"""
@spec column2( mat44 ) :: vec4
def column2( a ) do
{ _, _, a13, _,
_, _, a23, _,
_, _, a33, _,
_, _, a43, _} = a
{a13,a23,a33,a43}
end
@doc"""
`column3( a )` selects the fourth column of a `mat44`.
`a` is the `mat44` to take the fourth column of.
This returns a `vec4` representing the fourth column of `a`.
"""
@spec column3( mat44 ) :: vec4
def column3( a ) do
{ _, _, _, a14,
_, _, _, a24,
_, _, _, a34,
_, _, _, a44} = a
{a14,a24,a34,a44}
end
@doc"""
`row0( a )` selects the first row of a `mat44`.
`a` is the `mat44` to take the first row of.
This returns a `vec4` representing the first row of `a`.
"""
@spec row0( mat44 ) :: vec4
def row0( a ) do
{ a11, a12, a13, a14,
_, _, _, _,
_, _, _, _,
_, _, _, _} = a
{a11,a12,a13,a14}
end
@doc"""
`row1( a )` selects the second row of a `mat44`.
`a` is the `mat44` to take the second row of.
This returns a `vec4` representing the second row of `a`.
"""
@spec row1( mat44 ) :: vec4
def row1( a ) do
{ _, _, _, _,
a21, a22, a23, a24,
_, _, _, _,
_, _, _, _} = a
{a21,a22,a23,a24}
end
@doc"""
`row2( a )` selects the third row of a `mat44`.
`a` is the `mat44` to take the third row of.
This returns a `vec4` representing the third row of `a`.
"""
@spec row2( mat44 ) :: vec4
def row2( a ) do
{ _, _, _, _,
_, _, _, _,
a31, a32, a33, a34,
_, _, _, _} = a
{a31,a32,a33,a34}
end
@doc"""
`row3( a )` selects the fourth row of a `mat44`.
`a` is the `mat44` to take the fourth row of.
This returns a `vec4` representing the fourth row of `a`.
"""
@spec row3( mat44 ) :: vec4
def row3( a ) do
{ _, _, _, _,
_, _, _, _,
_, _, _, _,
a41, a42, a43, a44} = a
{a41,a42,a43,a44}
end
@doc"""
`diag( a )` selects the diagonal of a `mat44`.
`a` is the `mat44` to take the diagonal of.
This returns a `vec4` representing the diagonal of `a`.
"""
@spec diag( mat44 ) :: vec4
def diag( a ) do
{ a11, _, _, _,
_, a22, _, _,
_, _, a33, _,
_, _, _, a44} = a
{a11,a22,a33,a44}
end
@doc"""
`at( a, i, j)` selects an element of a `mat44`.
`a` is the `mat44` to index.
`i` is the row integer index [0,3].
`j` is the column integer index [0,3].
This returns a float from the matrix at row `i` and column `j`.
"""
@spec at( mat44, Integer, Integer ) :: float
def at( a, i, j ) do
elem( a, 4*i + j )
end
@doc"""
`apply( a, v )` transforms a `vec4` by a `mat44`.
`a` is the `mat44` to transform by.
`v` is the `vec4` to be transformed.
This returns a `vec4` representing **A****v**.
This is the "full" application of a matrix, and uses all elements.
"""
@spec apply( mat44, vec4 ) :: vec4
def apply( a, v ) do
{ a11, a12, a13, a14,
a21, a22, a23, a24,
a31, a32, a33, a34,
a41, a42, a43, a44} = a
{ x, y, z, w } = v
{
(a11*x)+(a12*y)+(a13*z)+(a14*w),
(a21*x)+(a22*y)+(a23*z)+(a24*w),
(a31*x)+(a32*y)+(a33*z)+(a34*w),
(a41*x)+(a42*y)+(a43*z)+(a44*w)
}
end
@doc"""
`apply_transpose( a, v )` transforms a `vec4` by a a transposed `mat44`.
`a` is the `mat44` to transform by.
`v` is the `vec4` to be transformed.
This returns a `vec4` representing **A**<sup>T</sup>**v**.
This is the "full" application of a matrix, and uses all elements.
"""
@spec apply_transpose( mat44, vec4 ) :: vec4
def apply_transpose( a, v ) do
{ a11, a21, a31, a41,
a12, a22, a32, a42,
a13, a23, a33, a43,
a14, a24, a34, a44} = a
{ x, y, z, w } = v
{
(a11*x)+(a12*y)+(a13*z)+(a14*w),
(a21*x)+(a22*y)+(a23*z)+(a24*w),
(a31*x)+(a32*y)+(a33*z)+(a34*w),
(a41*x)+(a42*y)+(a43*z)+(a44*w)
}
end
@doc"""
`apply_left( v, a )` transforms a `vec4` by a `mat44`, applied on the left.
`a` is the `mat44` to transform by.
`v` is the `vec4` to be transformed.
This returns a `vec4` representing **v****A**.
This is the "full" application of a matrix, and uses all elements.
"""
@spec apply_left( vec4, mat44 ) :: vec4
def apply_left( v, a ) do
{ a11, a12, a13, a14,
a21, a22, a23, a24,
a31, a32, a33, a34,
a41, a42, a43, a44} = a
{ x, y, z, w } = v
{
(a11*x)+(a21*y)+(a31*z)+(a41*w),
(a12*x)+(a22*y)+(a32*z)+(a42*w),
(a13*x)+(a23*y)+(a33*z)+(a43*w),
(a14*x)+(a24*y)+(a34*z)+(a44*w)
}
end
@doc"""
`apply_left_transpose( v, a )` transforms a `vec3` by a transposed `mat33`, applied on the left.
`a` is the `mat44` to transform by.
`v` is the `vec4` to be transformed.
This returns a `vec4` representing **v****A**<sup>T</sup>.
This is the "full" application of a matrix, and uses all elements.
"""
@spec apply_left_transpose( vec4, mat44 ) :: vec4
def apply_left_transpose( v, a ) do
{ a11, a21, a31, a41,
a12, a22, a32, a42,
a13, a23, a33, a43,
a14, a24, a34, a44} = a
{ x, y, z, w } = v
{
(a11*x)+(a21*y)+(a31*z)+(a41*w),
(a12*x)+(a22*y)+(a32*z)+(a42*w),
(a13*x)+(a23*y)+(a33*z)+(a43*w),
(a14*x)+(a24*y)+(a34*z)+(a44*w)
}
end
@doc"""
`transform_point( a, v )` transforms a `vec3` point by a `mat44`.
`a` is a `mat44` used to transform the point.
`v` is a `vec3` to be transformed.
This returns a `vec3` representing the application of `a` to `v`.
The point `a` is internally treated as having a fourth coordinate equal to 1.0.
Note that transforming a point will work for all transforms.
"""
@spec transform_point(mat44, vec3) :: vec3
def transform_point( a, v ) do
{ a11, a21, a31, _,
a12, a22, a32, _,
a13, a23, a33, _,
a14, a24, a34, _ } = a
{ x, y, z } = v
{
(a11*x)+(a12*y)+(a13*z) + (a14),
(a21*x)+(a22*y)+(a23*z) + (a24),
(a31*x)+(a32*y)+(a33*z) + (a34)
}
end
@doc"""
`transform_vector( a, v )` transforms a `vec3` vector by a `mat44`.
`a` is a `mat44` used to transform the point.
`v` is a `vec3` to be transformed.
This returns a `vec3` representing the application of `a` to `v`.
The point `a` is internally treated as having a fourth coordinate equal to 0.0.
Note that transforming a vector will work for only rotations, scales, and shears.
"""
@spec transform_vector(mat44, vec3) :: vec3
def transform_vector( a, v ) do
{ a11, a21, a31, _,
a12, a22, a32, _,
a13, a23, a33, _,
_, _, _, _ } = a
{ x, y, z } = v
{
(a11*x)+(a12*y)+(a13*z),
(a21*x)+(a22*y)+(a23*z),
(a31*x)+(a32*y)+(a33*z)
}
end
end