vmath/src/vmath.nim
2023-03-04 12:57:17 -08:00

1750 lines
45 KiB
Nim

##[
This library has no dependencies other than the Nim standard libarary.
Your one stop shop for vector math routines for 2d and 3d graphics.
* Pure Nim with no dependencies.
* Very similar to GLSL Shader Language with extra stuff.
* Extensively benchmarked.
====== =========== ===================================================
Type Constructor Description
====== =========== ===================================================
BVec# bvec# a vector of booleans
IVec# ivec# a vector of signed integers
UVec# uvec# a vector of unsigned integers
Vec# vec# a vector of single-precision floating-point numbers
DVec# dvec# a vector of double-precision floating-point numbers
====== =========== ===================================================
You can use these constructors to make them:
======= ====== ===== ===== ===== ===== ===== =====
NIM GLSL 2 3 4 9 16 4
======= ====== ===== ===== ===== ===== ===== =====
bool bool BVec2 BVec3 BVec4
int32 int IVec2 IVec3 IVec4
uint32 uint UVec2 UVec3 UVec4
float32 float Vec2 Vec3 Vec4 Mat3 Mat4 Quat
float64 double DVec2 DVec3 DVec4 DMat3 DMat4 DQuat
======= ====== ===== ===== ===== ===== ===== =====
]##
import macros, math, strutils
export math except isNan
{.push inline.}
when defined(release):
{.push noinit, checks: off.}
when defined(vmathArrayBased):
type
GVec2*[T] = array[2, T]
GVec3*[T] = array[3, T]
GVec4*[T] = array[4, T]
GVec34[T] = GVec3[T] | GVec4[T]
GVec234[T] = GVec2[T] | GVec3[T] | GVec4[T]
template gvec2*[T](x, y: T): GVec2[T] = [T(x), T(y)]
template gvec3*[T](x, y, z: T): GVec3[T] = [T(x), T(y), T(z)]
template gvec4*[T](x, y, z, w: T): GVec4[T] = [T(x), T(y), T(z), T(w)]
template x*[T](a: GVec2[T]): T = a[0]
template x*[T](a: GVec3[T]): T = a[0]
template x*[T](a: GVec4[T]): T = a[0]
template y*[T](a: GVec2[T]): T = a[1]
template y*[T](a: GVec3[T]): T = a[1]
template y*[T](a: GVec4[T]): T = a[1]
template z*[T](a: GVec2[T]): T = {.error: "using .z on a Vec2".}
template z*[T](a: GVec3[T]): T = a[2]
template z*[T](a: GVec4[T]): T = a[2]
template w*[T](a: GVec2[T]): T = {.error: "using .w on a Vec2".}
template w*[T](a: GVec3[T]): T = {.error: "using .w on a Vec3".}
template w*[T](a: GVec4[T]): T = a[3]
template x*[T](a: var GVec2[T]): var T = a[0]
template x*[T](a: var GVec3[T]): var T = a[0]
template x*[T](a: var GVec4[T]): var T = a[0]
template y*[T](a: var GVec2[T]): var T = a[1]
template y*[T](a: var GVec3[T]): var T = a[1]
template y*[T](a: var GVec4[T]): var T = a[1]
template z*[T](a: var GVec3[T]): var T = a[2]
template z*[T](a: var GVec4[T]): var T = a[2]
template w*[T](a: var GVec4[T]): var T = a[3]
template `x=`*[T](a: var GVec234[T], value: T) = a[0] = value
template `y=`*[T](a: var GVec234[T], value: T) = a[1] = value
template `z=`*[T](a: var GVec34[T], value: T) = a[2] = value
template `w=`*[T](a: var GVec4[T], value: T) = a[3] = value
type
GMat2*[T] {.bycopy.} = array[2, GVec2[T]]
GMat3*[T] {.bycopy.} = array[3, GVec3[T]]
GMat4*[T] {.bycopy.} = array[4, GVec4[T]]
GMat234[T] = GMat2[T] | GMat3[T] | GMat4[T]
proc gmat2*[T](
m00, m01,
m10, m11: T
): GMat2[T] =
[
[m00, m01],
[m10, m11]
]
proc gmat3*[T](
m00, m01, m02,
m10, m11, m12,
m20, m21, m22: T
): GMat3[T] =
[
[m00, m01, m02],
[m10, m11, m12],
[m20, m21, m22]
]
proc gmat4*[T](
m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
m30, m31, m32, m33: T
): GMat4[T] =
[
[m00, m01, m02, m03],
[m10, m11, m12, m13],
[m20, m21, m22, m23],
[m30, m31, m32, m33]
]
template `[]`*[T](a: GMat234[T], i, j: int): T = a[i][j]
template `[]=`*[T](a: var GMat2[T], i, j: int, v: T) =
cast[ptr T](cast[uint64](a.addr) + (i * 2 + j) * sizeof(T))[] = v
template `[]=`*[T](a: var GMat3[T], i, j: int, v: T) =
cast[ptr T](cast[uint64](a.addr) + (i * 3 + j) * sizeof(T))[] = v
template `[]=`*[T](a: var GMat4[T], i, j: int, v: T) =
cast[ptr T](cast[uint64](a.addr) + (i * 4 + j) * sizeof(T))[] = v
elif defined(vmathObjBased):
type
GVec2*[T] = object
x*, y*: T
GVec3*[T] = object
x*, y*, z*: T
GVec4*[T] = object
x*, y*, z*, w*: T
GVec34[T] = GVec3[T] | GVec4[T]
GVec234[T] = GVec2[T] | GVec3[T] | GVec4[T]
template gvec2*[T](mx, my: T): GVec2[T] =
GVec2[T](x: mx, y: my)
template gvec3*[T](mx, my, mz: T): GVec3[T] =
GVec3[T](x: mx, y: my, z: mz)
template gvec4*[T](mx, my, mz, mw: T): GVec4[T] =
GVec4[T](x: mx, y: my, z: mz, w: mw)
template `[]`*[T](a: GVec2[T], i: int): T = cast[array[2, T]](a)[i]
template `[]`*[T](a: GVec3[T], i: int): T = cast[array[3, T]](a)[i]
template `[]`*[T](a: GVec4[T], i: int): T = cast[array[4, T]](a)[i]
template `[]=`*[T](a: var GVec2[T], i: int, v: T) =
cast[ptr T](cast[uint64](a.addr) + i * sizeof(T))[] = v
template `[]=`*[T](a: var GVec3[T], i: int, v: T) =
cast[ptr T](cast[uint64](a.addr) + i * sizeof(T))[] = v
template `[]=`*[T](a: var GVec4[T], i: int, v: T) =
cast[ptr T](cast[uint64](a.addr) + i * sizeof(T))[] = v
type
GMat2*[T] {.bycopy.} = object
m00*, m01*: T
m10*, m11*: T
GMat3*[T] {.bycopy.} = object
m00*, m01*, m02*: T
m10*, m11*, m12*: T
m20*, m21*, m22*: T
GMat4*[T] {.bycopy.} = object
m00*, m01*, m02*, m03*: T
m10*, m11*, m12*, m13*: T
m20*, m21*, m22*, m23*: T
m30*, m31*, m32*, m33*: T
proc gmat2*[T](
m00, m01,
m10, m11: T
): GMat2[T] =
result.m00 = m00; result.m01 = m01
result.m10 = m10; result.m11 = m11
proc gmat3*[T](
m00, m01, m02,
m10, m11, m12,
m20, m21, m22: T
): GMat3[T] =
result.m00 = m00; result.m01 = m01; result.m02 = m02
result.m10 = m10; result.m11 = m11; result.m12 = m12
result.m20 = m20; result.m21 = m21; result.m22 = m22
proc gmat4*[T](
m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
m30, m31, m32, m33: T
): GMat4[T] =
result.m00 = m00; result.m01 = m01; result.m02 = m02; result.m03 = m03
result.m10 = m10; result.m11 = m11; result.m12 = m12; result.m13 = m13
result.m20 = m20; result.m21 = m21; result.m22 = m22; result.m23 = m23
result.m30 = m30; result.m31 = m31; result.m32 = m32; result.m33 = m33
template `[]`*[T](a: GMat2[T], i, j: int): T =
cast[array[4, T]](a)[i * 2 + j]
template `[]`*[T](a: GMat3[T], i, j: int): T =
cast[array[9, T]](a)[i * 3 + j]
template `[]`*[T](a: GMat4[T], i, j: int): T =
cast[array[16, T]](a)[i * 4 + j]
template `[]=`*[T](a: var GMat2[T], i, j: int, v: T) =
cast[ptr T](cast[uint64](a.addr) + (i * 2 + j) * sizeof(T))[] = v
template `[]=`*[T](a: var GMat3[T], i, j: int, v: T) =
cast[ptr T](cast[uint64](a.addr) + (i * 3 + j) * sizeof(T))[] = v
template `[]=`*[T](a: var GMat4[T], i, j: int, v: T) =
cast[ptr T](cast[uint64](a.addr) + (i * 4 + j) * sizeof(T))[] = v
template `[]`*[T](a: GMat2[T], i: int): GVec2[T] =
gvec2[T](
a[i, 0],
a[i, 1]
)
template `[]`*[T](a: GMat3[T], i: int): GVec3[T] =
gvec3[T](
a[i, 0],
a[i, 1],
a[i, 2]
)
template `[]`*[T](a: GMat4[T], i: int): GVec4[T] =
gvec4[T](
a[i, 0],
a[i, 1],
a[i, 2],
a[i, 3]
)
elif true or defined(vmathObjArrayBased):
type
GVec2*[T] = object
arr: array[2, T]
GVec3*[T] = object
arr: array[3, T]
GVec4*[T] = object
arr: array[4, T]
GVec34[T] = GVec3[T] | GVec4[T]
GVec234[T] = GVec2[T] | GVec3[T] | GVec4[T]
template gvec2*[T](x, y: T): GVec2[T] =
GVec2[T](arr: [T(x), T(y)])
template gvec3*[T](x, y, z: T): GVec3[T] =
GVec3[T](arr: [T(x), T(y), T(z)])
template gvec4*[T](x, y, z, w: T): GVec4[T] =
GVec4[T](arr: [T(x), T(y), T(z), T(w)])
template x*[T](a: var GVec2[T]): var T = a.arr[0]
template y*[T](a: var GVec2[T]): var T = a.arr[1]
template x*[T](a: var GVec3[T]): var T = a.arr[0]
template y*[T](a: var GVec3[T]): var T = a.arr[1]
template z*[T](a: var GVec3[T]): var T = a.arr[2]
template x*[T](a: var GVec4[T]): var T = a.arr[0]
template y*[T](a: var GVec4[T]): var T = a.arr[1]
template z*[T](a: var GVec4[T]): var T = a.arr[2]
template w*[T](a: var GVec4[T]): var T = a.arr[3]
template x*[T](a: GVec2[T]): T = a.arr[0]
template x*[T](a: GVec3[T]): T = a.arr[0]
template x*[T](a: GVec4[T]): T = a.arr[0]
template y*[T](a: GVec2[T]): T = a.arr[1]
template y*[T](a: GVec3[T]): T = a.arr[1]
template y*[T](a: GVec4[T]): T = a.arr[1]
template z*[T](a: GVec3[T]): T = a.arr[2]
template z*[T](a: GVec4[T]): T = a.arr[2]
template w*[T](a: GVec4[T]): T = a.arr[3]
template `x=`*[T](a: var GVec234[T], value: T) = a.arr[0] = value
template `y=`*[T](a: var GVec234[T], value: T) = a.arr[1] = value
template `z=`*[T](a: var GVec34[T], value: T) = a.arr[2] = value
template `w=`*[T](a: var GVec4[T], value: T) = a.arr[3] = value
template `[]`*[T](a: GVec234[T], i: int): T = a.arr[i]
template `[]=`*[T](a: var GVec234[T], i: int, v: T) = a.arr[i] = v
type
GMat2*[T] {.bycopy.} = object
arr: array[4, T]
GMat3*[T] {.bycopy.} = object
arr: array[9, T]
GMat4*[T] {.bycopy.} = object
arr: array[16, T]
proc gmat2*[T](
m00, m01,
m10, m11: T
): GMat2[T] =
GMat2[T](arr: [
m00, m01,
m10, m11
])
proc gmat3*[T](
m00, m01, m02,
m10, m11, m12,
m20, m21, m22: T
): GMat3[T] =
GMat3[T](arr: [
m00, m01, m02,
m10, m11, m12,
m20, m21, m22
])
proc gmat4*[T](
m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
m30, m31, m32, m33: T
): GMat4[T] =
GMat4[T](arr: [
m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
m30, m31, m32, m33
])
template `[]`*[T](a: GMat2[T], i, j: int): T = a.arr[i * 2 + j]
template `[]`*[T](a: GMat3[T], i, j: int): T = a.arr[i * 3 + j]
template `[]`*[T](a: GMat4[T], i, j: int): T = a.arr[i * 4 + j]
template `[]=`*[T](a: var GMat2[T], i, j: int, v: T) = a.arr[i * 2 + j] = v
template `[]=`*[T](a: var GMat3[T], i, j: int, v: T) = a.arr[i * 3 + j] = v
template `[]=`*[T](a: var GMat4[T], i, j: int, v: T) = a.arr[i * 4 + j] = v
template `[]`*[T](a: GMat2[T], i: int): GVec2[T] =
gvec2[T](
a[i, 0],
a[i, 1]
)
template `[]`*[T](a: GMat3[T], i: int): GVec3[T] =
gvec3[T](
a[i, 0],
a[i, 1],
a[i, 2]
)
template `[]`*[T](a: GMat4[T], i: int): GVec4[T] =
gvec4[T](
a[i, 0],
a[i, 1],
a[i, 2],
a[i, 3]
)
type
BVec2* = GVec2[bool]
BVec3* = GVec3[bool]
BVec4* = GVec4[bool]
IVec2* = GVec2[int32]
IVec3* = GVec3[int32]
IVec4* = GVec4[int32]
UVec2* = GVec2[uint32]
UVec3* = GVec3[uint32]
UVec4* = GVec4[uint32]
Vec2* = GVec2[float32]
Vec3* = GVec3[float32]
Vec4* = GVec4[float32]
DVec2* = GVec2[float64]
DVec3* = GVec3[float64]
DVec4* = GVec4[float64]
proc `~=`*[T: SomeFloat](a, b: T): bool =
## Almost equal.
const epsilon = 0.000001
abs(a - b) <= epsilon
proc between*[T](value, min, max: T): bool =
## Returns true if value is between min and max or equal to them.
(value >= min) and (value <= max)
proc sign*[T](v: T): T =
## Returns the sign of a number, -1 or 1.
if v >= 0: 1 else: -1
proc quantize*[T: SomeFloat](v, n: T): T =
## Makes v be multiple of n. Rounding to integer quantize by 1.0.
trunc(v / n) * n
proc frac*[T: SomeFloat](v: T): T =
## Returns fractional part of a number.
## 3.14 -> 0.14
## -3.14 -> 0.14
result = abs(v)
result = result - trunc(result)
proc fractional*[T: SomeFloat](v: T): T {.deprecated: "Use frac() insetad"} =
## Returns fractional part of a number.
frac(v)
proc inversesqrt*[T: float32|float64](v: T): T =
## Returns inverse square root.
1/sqrt(v)
proc mix*[T: SomeFloat](a, b, v: T): T =
## Interpolates value between a and b.
## * 0 -> a
## * 1 -> b
## * 0.5 -> between a and b
v * (b - a) + a
proc fixAngle*[T: SomeFloat](angle: T): T =
## Normalize the angle be from -PI to PI radians.
result = angle
while result > PI:
result -= PI * 2
while result <= -PI:
result += PI * 2
proc angleBetween*[T: SomeFloat](a, b: T): T =
## Angle between angle a and angle b.
fixAngle(b - a)
proc turnAngle*[T: SomeFloat](a, b, speed: T): T =
## Move from angle a to angle b with step of v.
var
turn = fixAngle(b - a)
if abs(turn) < speed:
return b
elif turn > speed:
turn = speed
elif turn < -speed:
turn = -speed
a + turn
proc toRadians*[T: SomeFloat](deg: T): T =
## Convert degrees to radians.
PI * deg / 180.0
proc toDegrees*[T: SomeFloat](rad: T): T =
## Convert radians to degrees.
180.0 * rad / PI
proc toRadians*(deg: SomeInteger): float32 =
## Convert degrees to radians.
deg.float32.toRadians
proc toDegrees*(deg: SomeInteger): float32 =
## Convert degrees to radians.
deg.float32.toDegrees
proc isNan*(x: SomeFloat): bool =
## Returns true if number is a NaN.
x != 0.0 and (x != x or x * 0.5 == x)
proc `zmod`*(a, b: float32): float32 =
## Float point mod.
return a - b * floor(a/b)
template lowerType(a: typed): string =
($type(a)).toLowerAscii()
template genVecConstructor*(lower, upper, typ: untyped) =
## Generate vector constructor for your own type.
proc `lower 2`*(): `upper 2` = gvec2[typ](typ(0), typ(0))
proc `lower 3`*(): `upper 3` = gvec3[typ](typ(0), typ(0), typ(0))
proc `lower 4`*(): `upper 4` = gvec4[typ](typ(0), typ(0), typ(0), typ(0))
proc `lower 2`*(x, y: typ): `upper 2` = gvec2[typ](x, y)
proc `lower 3`*(x, y, z: typ): `upper 3` = gvec3[typ](x, y, z)
proc `lower 4`*(x, y, z, w: typ): `upper 4` = gvec4[typ](x, y, z, w)
proc `lower 2`*(x: typ): `upper 2` = gvec2[typ](x, x)
proc `lower 3`*(x: typ): `upper 3` = gvec3[typ](x, x, x)
proc `lower 4`*(x: typ): `upper 4` = gvec4[typ](x, x, x, x)
proc `lower 2`*[T](x: GVec2[T]): `upper 2` =
gvec2[typ](typ(x[0]), typ(x[1]))
proc `lower 3`*[T](x: GVec3[T]): `upper 3` =
gvec3[typ](typ(x[0]), typ(x[1]), typ(x[2]))
proc `lower 4`*[T](x: GVec4[T]): `upper 4` =
gvec4[typ](typ(x[0]), typ(x[1]), typ(x[2]), typ(x[3]))
proc `lower 3`*[T](x: GVec2[T], z: T = 0): `upper 3` =
gvec3[typ](typ(x[0]), typ(x[1]), z)
proc `lower 4`*[T](x: GVec3[T], w: T = 0): `upper 4` =
gvec4[typ](typ(x[0]), typ(x[1]), typ(x[2]), w)
proc `lower 4`*[T](a, b: GVec2[T]): `upper 4` =
gvec4[typ](typ(a[0]), typ(a[1]), typ(b[0]), typ(b[1]))
proc `$`*(a: `upper 2`): string =
lowerType(a) & "(" & $a.x & ", " & $a.y & ")"
proc `$`*(a: `upper 3`): string =
lowerType(a) & "(" & $a.x & ", " & $a.y & ", " & $a.z & ")"
proc `$`*(a: `upper 4`): string =
lowerType(a) & "(" & $a.x & ", " & $a.y & ", " & $a.z & ", " & $a.w & ")"
genVecConstructor(bvec, BVec, bool)
genVecConstructor(ivec, IVec, int32)
genVecConstructor(uvec, UVec, uint32)
genVecConstructor(vec, Vec, float32)
genVecConstructor(dvec, DVec, float64)
proc vec2*(ivec2: Ivec2): Vec2 =
vec2(ivec2.x.float32, ivec2.y.float32)
proc vec2*(uvec2: Uvec2): Vec2 =
vec2(uvec2.x.float32, uvec2.y.float32)
proc ivec2*(uvec2: Uvec2): Ivec2 =
ivec2(uvec2.x.int32, uvec2.y.int32)
proc uvec2*(ivec2: Ivec2): Uvec2 =
uvec2(ivec2.x.uint32, ivec2.y.uint32)
proc vec3*(ivec3: Ivec3): Vec3 =
vec3(ivec3.x.float32, ivec3.y.float32, ivec3.z.float32)
proc vec3*(uvec3: Uvec3): Vec3 =
vec3(uvec3.x.float32, uvec3.y.float32, uvec3.z.float32)
proc ivec3*(uvec3: Uvec3): Ivec3 =
ivec3(uvec3.x.int32, uvec3.y.int32, uvec3.z.int32)
proc uvec3*(ivec3: Ivec3): Uvec3 =
uvec3(ivec3.x.uint32, ivec3.y.uint32, ivec3.z.uint32)
proc vec4*(ivec4: Ivec4): Vec4 =
vec4(ivec4.x.float32, ivec4.y.float32, ivec4.z.float32, ivec4.w.float32)
proc vec4*(uvec4: Uvec4): Vec4 =
vec4(uvec4.x.float32, uvec4.y.float32, uvec4.z.float32, uvec4.w.float32)
proc ivec4*(uvec4: Uvec4): Ivec4 =
ivec4(uvec4.x.int32, uvec4.y.int32, uvec4.z.int32, uvec4.w.int32)
proc uvec4*(ivec4: Ivec4): Uvec4 =
uvec4(ivec4.x.uint32, ivec4.y.uint32, ivec4.z.uint32, ivec4.w.uint32)
when not defined(nimdoc) or not isMainModule:
# TODO when https://github.com/nim-lang/Nim/issues/13063 is fixed use macros.
include vmath/swizzle
proc `==`*[T](a, b: GVec2[T]): bool =
a.x == b.x and a.y == b.y
proc `==`*[T](a, b: GVec3[T]): bool =
a.x == b.x and a.y == b.y and a.z == b.z
proc `==`*[T](a, b: GVec4[T]): bool =
a.x == b.x and a.y == b.y and a.z == b.z and a.w == b.w
proc `!=`*[T](a, b: GVec2[T]): bool =
a.x != b.x or a.y != b.y
proc `!=`*[T](a, b: GVec3[T]): bool =
a.x != b.x or a.y != b.y or a.z != b.z
proc `!=`*[T](a, b: GVec4[T]): bool =
a.x != b.x or a.y != b.y or a.z != b.z or a.w != b.w
template genOp(op: untyped) =
proc op*[T](a, b: GVec2[T]): GVec2[T] =
gvec2[T](
op(a[0], b[0]),
op(a[1], b[1])
)
proc op*[T](a, b: GVec3[T]): GVec3[T] =
gvec3[T](
op(a[0], b[0]),
op(a[1], b[1]),
op(a[2], b[2])
)
proc op*[T](a, b: GVec4[T]): GVec4[T] =
gvec4[T](
op(a[0], b[0]),
op(a[1], b[1]),
op(a[2], b[2]),
op(a[3], b[3])
)
proc op*[T](a: GVec2[T], b: T): GVec2[T] =
gvec2[T](
op(a[0], b),
op(a[1], b)
)
proc op*[T](a: GVec3[T], b: T): GVec3[T] =
gvec3[T](
op(a[0], b),
op(a[1], b),
op(a[2], b)
)
proc op*[T](a: GVec4[T], b: T): GVec4[T] =
gvec4[T](
op(a[0], b),
op(a[1], b),
op(a[2], b),
op(a[3], b)
)
proc op*[T](a: T, b: GVec2[T]): GVec2[T] =
gvec2[T](
op(a, b[0]),
op(a, b[1])
)
proc op*[T](a: T, b: GVec3[T]): GVec3[T] =
gvec3[T](
op(a, b[0]),
op(a, b[1]),
op(a, b[2])
)
proc op*[T](a: T, b: GVec4[T]): GVec4[T] =
gvec4[T](
op(a, b[0]),
op(a, b[1]),
op(a, b[2]),
op(a, b[3])
)
genOp(`+`)
genOp(`-`)
genOp(`*`)
genOp(`/`)
genOp(`mod`)
genOp(`div`)
genOp(`zmod`)
template genEqOp(op: untyped) =
proc op*[T](a: var GVec2[T], b: GVec2[T]) =
op(a.x, b.x)
op(a.y, b.y)
proc op*[T](a: var GVec3[T], b: GVec3[T]) =
op(a.x, b.x)
op(a.y, b.y)
op(a.z, b.z)
proc op*[T](a: var GVec4[T], b: GVec4[T]) =
op(a.x, b.x)
op(a.y, b.y)
op(a.z, b.z)
op(a.w, b.w)
proc op*[T](a: var GVec2[T], b: T) =
op(a.x, b)
op(a.y, b)
proc op*[T](a: var GVec3[T], b: T) =
op(a.x, b)
op(a.y, b)
op(a.z, b)
proc op*[T](a: var GVec4[T], b: T) =
op(a.x, b)
op(a.y, b)
op(a.z, b)
op(a.w, b)
genEqOp(`+=`)
genEqOp(`-=`)
genEqOp(`*=`)
genEqOp(`/=`)
template genMathFn(fn: untyped) =
proc fn*[T](v: GVec2[T]): GVec2[T] =
gvec2[T](
fn(v[0]),
fn(v[1])
)
proc fn*[T](v: GVec3[T]): GVec3[T] =
gvec3[T](
fn(v[0]),
fn(v[1]),
fn(v[2])
)
proc fn*[T](v: GVec4[T]): GVec4[T] =
gvec4[T](
fn(v[0]),
fn(v[1]),
fn(v[2]),
fn(v[3])
)
genMathFn(`-`)
genMathFn(sin)
genMathFn(cos)
genMathFn(tan)
genMathFn(arcsin)
genMathFn(arccos)
genMathFn(arctan)
genMathFn(sinh)
genMathFn(cosh)
genMathFn(tanh)
genMathFn(exp2)
genMathFn(inversesqrt)
genMathFn(exp)
genMathFn(ln)
genMathFn(log2)
genMathFn(sqrt)
genMathFn(floor)
genMathFn(ceil)
genMathFn(abs)
genMathFn(trunc)
genMathFn(frac)
genMathFn(quantize)
genMathFn(toRadians)
genMathFn(toDegrees)
template genBoolFn(fn, op: untyped) =
proc fn*[T](a, b: GVec2[T]): BVec2 =
result[0] = op(a[0], b[0])
result[1] = op(a[1], b[1])
proc fn*[T](a, b: GVec3[T]): BVec3 =
result[0] = op(a[0], b[0])
result[1] = op(a[1], b[1])
result[2] = op(a[2], b[2])
proc fn*[T](a, b: GVec4[T]): BVec4 =
result[0] = op(a[0], b[0])
result[1] = op(a[1], b[1])
result[2] = op(a[2], b[2])
result[3] = op(a[3], b[3])
genBoolFn(lessThan, `<`)
genBoolFn(lessThanEqual, `<=`)
genBoolFn(greaterThan, `>`)
genBoolFn(greaterThanEqual, `>=`)
genBoolFn(equal, `==`)
genBoolFn(notEqual, `!=`)
proc `~=`*[T](a, b: GVec2[T]): bool =
## Almost equal.
a.x ~= b.x and a.y ~= b.y
proc `~=`*[T](a, b: GVec3[T]): bool =
## Almost equal.
a.x ~= b.x and a.y ~= b.y and a.z ~= b.z
proc `~=`*[T](a, b: GVec4[T]): bool =
## Almost equal.
a.x ~= b.x and a.y ~= b.y and a.z ~= b.z and a.w ~= b.w
proc length*[T](a: GVec2[T]): T =
sqrt(a.x*a.x + a.y*a.y)
proc length*[T](a: GVec3[T]): T =
sqrt(a.x*a.x + a.y*a.y + a.z*a.z)
proc length*[T](a: GVec4[T]): T =
sqrt(a.x*a.x + a.y*a.y + a.z*a.z + a.w*a.w)
proc lengthSq*[T](a: GVec2[T]): T =
a.x*a.x + a.y*a.y
proc lengthSq*[T](a: GVec3[T]): T =
a.x*a.x + a.y*a.y + a.z*a.z
proc lengthSq*[T](a: GVec4[T]): T =
a.x*a.x + a.y*a.y + a.z*a.z + a.w*a.w
proc normalize*[T](a: GVec234[T]): type(a) =
a / a.length
proc mix*[T: SomeFloat](a, b: GVec234[T], v: T): type(a) =
a * (1.0 - v) + b * v
proc lerp*[V, T](a, b: V, v: T): type(a) {.deprecated: "use mix instead".} =
mix(a, b, v)
proc dot*[T](a, b: GVec2[T]): T =
a.x * b.x + a.y * b.y
proc dot*[T](a, b: GVec3[T]): T =
a.x * b.x + a.y * b.y + a.z * b.z
proc dot*[T](a, b: GVec4[T]): T =
a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
proc mix*[T: SomeFloat](a, b, v: GVec2[T]): type(a) =
result.x = a.x * (1.0 - v.x) + b.x * v.x
result.y = a.y * (1.0 - v.y) + b.y * v.y
proc mix*[T: SomeFloat](a, b, v: GVec3[T]): type(a) =
result.x = a.x * (1.0 - v.x) + b.x * v.x
result.y = a.y * (1.0 - v.y) + b.y * v.y
result.z = a.z * (1.0 - v.z) + b.z * v.z
proc mix*[T: SomeFloat](a, b, v: GVec4[T]): type(a) =
result.x = a.x * (1.0 - v.x) + b.x * v.x
result.y = a.y * (1.0 - v.y) + b.y * v.y
result.z = a.z * (1.0 - v.z) + b.z * v.z
result.w = a.w * (1.0 - v.w) + b.w * v.w
proc cross*[T](a, b: GVec3[T]): GVec3[T] =
gvec3(
a.y * b.z - a.z * b.y,
a.z * b.x - a.x * b.z,
a.x * b.y - a.y * b.x
)
proc dist*[T](at, to: GVec234[T]): T =
(at - to).length
proc distSq*[T](at, to: GVec234[T]): T =
(at - to).lengthSq
proc dir*[T](at, to: GVec234[T]): type(to) =
(at - to).normalize
proc dir*[T](angle: T): GVec2[T] =
gvec2(
cos(angle),
sin(angle),
)
proc min*(a, b: Vec2): Vec2 =
vec2(min(a.x, b.x), min(a.y, b.y))
proc min*(a, b: Vec3): Vec3 =
vec3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z))
proc min*(a, b: Vec4): Vec4 =
vec4(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z), min(a.w, b.w))
proc max*(a, b: Vec2): Vec2 =
vec2(max(a.x, b.x), max(a.y, b.y))
proc max*(a, b: Vec3): Vec3 =
vec3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z))
proc max*(a, b: Vec4): Vec4 =
vec4(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z), max(a.w, b.w))
type
Mat2* = GMat2[float32]
Mat3* = GMat3[float32]
Mat4* = GMat4[float32]
DMat2* = GMat2[float64]
DMat3* = GMat3[float64]
DMat4* = GMat4[float64]
proc matToString[T](a: T, n: int): string =
result = ($type(a)).toLowerAscii()
result.add "(\n"
for x in 0 ..< n:
result.add " "
for y in 0 ..< n:
result.add $a[x, y] & ", "
result.setLen(result.len - 1)
result.add "\n"
result.setLen(result.len - 2)
result.add "\n)"
template genMatConstructor*(lower, upper, T: untyped) =
## Generate matrix constructor for your own type.
proc `lower 2`*(
m00, m01,
m10, m11: T
): `upper 2` =
result[0, 0] = m00; result[0, 1] = m01
result[1, 0] = m10; result[1, 1] = m11
proc `lower 3`*(
m00, m01, m02,
m10, m11, m12,
m20, m21, m22: T
): `upper 3` =
result[0, 0] = m00; result[0, 1] = m01; result[0, 2] = m02
result[1, 0] = m10; result[1, 1] = m11; result[1, 2] = m12
result[2, 0] = m20; result[2, 1] = m21; result[2, 2] = m22
proc `lower 4`*(
m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
m30, m31, m32, m33: T
): `upper 4` =
result[0, 0] = m00; result[0, 1] = m01
result[0, 2] = m02; result[0, 3] = m03
result[1, 0] = m10; result[1, 1] = m11
result[1, 2] = m12; result[1, 3] = m13
result[2, 0] = m20; result[2, 1] = m21
result[2, 2] = m22; result[2, 3] = m23
result[3, 0] = m30; result[3, 1] = m31
result[3, 2] = m32; result[3, 3] = m33
proc `lower 2`*(a, b: GVec2[T]): `upper 2` =
gmat2[T](
a.x, a.y,
b.x, b.y
)
proc `lower 3`*(a, b, c: GVec3[T]): `upper 3` =
gmat3[T](
a.x, a.y, a.z,
b.x, b.y, b.z,
c.x, c.y, c.z,
)
proc `lower 4`*(a, b, c, d: GVec4[T]): `upper 4` =
gmat4[T](
a.x, a.y, a.z, a.w,
b.x, b.y, b.z, b.w,
c.x, c.y, c.z, c.w,
d.x, d.y, d.z, d.w,
)
proc `lower 2`*(): `upper 2` =
gmat2[T](
1.T, 0.T,
0.T, 1.T
)
proc `lower 3`*(): `upper 3` =
gmat3[T](
1.T, 0.T, 0.T,
0.T, 1.T, 0.T,
0.T, 0.T, 1.T
)
proc `lower 4`*(): `upper 4` =
gmat4[T](
1.T, 0.T, 0.T, 0.T,
0.T, 1.T, 0.T, 0.T,
0.T, 0.T, 1.T, 0.T,
0.T, 0.T, 0.T, 1.T
)
proc `$`*(a: `upper 2`): string = matToString(a, 2)
proc `$`*(a: `upper 3`): string = matToString(a, 3)
proc `$`*(a: `upper 4`): string = matToString(a, 4)
genMatConstructor(mat, Mat, float32)
genMatConstructor(dmat, DMat, float64)
proc `~=`*[T](a, b: GMat2[T]): bool =
a[0] ~= b[0] and a[1] ~= b[1]
proc `~=`*[T](a, b: GMat3[T]): bool =
a[0] ~= b[0] and a[1] ~= b[1] and a[2] ~= b[2]
proc `~=`*[T](a, b: GMat4[T]): bool =
a[0] ~= b[0] and a[1] ~= b[1] and a[2] ~= b[2] and a[3] ~= b[3]
proc pos*[T](a: GMat3[T]): GVec2[T] =
gvec2[T](a[2].x, a[2].y)
proc `pos=`*[T](a: var GMat3[T], pos: GVec2[T]) =
a[2, 0] = pos.x
a[2, 1] = pos.y
proc forward*[T](a: GMat4[T]): GVec3[T] {.inline.} =
## Vector facing +Z.
result.x = a[2, 0]
result.y = a[2, 1]
result.z = a[2, 2]
proc back*[T](a: GMat4[T]): GVec3[T] {.inline.} =
## Vector facing -Z.
-a.forward()
proc left*[T](a: GMat4[T]): GVec3[T] {.inline.} =
## Vector facing +X.
result.x = a[0, 0]
result.y = a[0, 1]
result.z = a[0, 2]
proc right*[T](a: GMat4[T]): GVec3[T] {.inline.} =
## Vector facing -X.
-a.left()
proc up*[T](a: GMat4[T]): GVec3[T] {.inline.} =
## Vector facing +Y.
result.x = a[1, 0]
result.y = a[1, 1]
result.z = a[1, 2]
proc down*[T](a: GMat4[T]): GVec3[T] {.inline.} =
## Vector facing -X.
-a.up()
proc pos*[T](a: GMat4[T]): GVec3[T] =
## Position of the matrix.
gvec3[T](a[3].x, a[3].y, a[3].z)
proc `pos=`*[T](a: var GMat4[T], pos: GVec3[T]) =
## See the position of the matrix.
a[3, 0] = pos.x
a[3, 1] = pos.y
a[3, 2] = pos.z
proc `*`*[T](a, b: GMat3[T]): GMat3[T] =
result[0, 0] = b[0, 0] * a[0, 0] + b[0, 1] * a[1, 0] + b[0, 2] * a[2, 0]
result[0, 1] = b[0, 0] * a[0, 1] + b[0, 1] * a[1, 1] + b[0, 2] * a[2, 1]
result[0, 2] = b[0, 0] * a[0, 2] + b[0, 1] * a[1, 2] + b[0, 2] * a[2, 2]
result[1, 0] = b[1, 0] * a[0, 0] + b[1, 1] * a[1, 0] + b[1, 2] * a[2, 0]
result[1, 1] = b[1, 0] * a[0, 1] + b[1, 1] * a[1, 1] + b[1, 2] * a[2, 1]
result[1, 2] = b[1, 0] * a[0, 2] + b[1, 1] * a[1, 2] + b[1, 2] * a[2, 2]
result[2, 0] = b[2, 0] * a[0, 0] + b[2, 1] * a[1, 0] + b[2, 2] * a[2, 0]
result[2, 1] = b[2, 0] * a[0, 1] + b[2, 1] * a[1, 1] + b[2, 2] * a[2, 1]
result[2, 2] = b[2, 0] * a[0, 2] + b[2, 1] * a[1, 2] + b[2, 2] * a[2, 2]
proc `*`*[T](a: GMat2[T], b: GVec2[T]): GVec2[T] =
gvec2[T](
a[0, 0] * b.x + a[1, 0] * b.y,
a[0, 1] * b.x + a[1, 1] * b.y
)
proc `*`*[T](a: GMat3[T], b: GVec2[T]): GVec2[T] =
gvec2[T](
a[0, 0] * b.x + a[1, 0] * b.y + a[2, 0],
a[0, 1] * b.x + a[1, 1] * b.y + a[2, 1]
)
proc `*`*[T](a: GMat3[T], b: GVec3[T]): GVec3[T] =
gvec3[T](
a[0, 0] * b.x + a[1, 0] * b.y + a[2, 0] * b.z,
a[0, 1] * b.x + a[1, 1] * b.y + a[2, 1] * b.z,
a[0, 2] * b.x + a[1, 2] * b.y + a[2, 2] * b.z,
)
proc `*`*[T](a, b: GMat4[T]): GMat4[T] =
let
a00 = a[0, 0]
a01 = a[0, 1]
a02 = a[0, 2]
a03 = a[0, 3]
a10 = a[1, 0]
a11 = a[1, 1]
a12 = a[1, 2]
a13 = a[1, 3]
a20 = a[2, 0]
a21 = a[2, 1]
a22 = a[2, 2]
a23 = a[2, 3]
a30 = a[3, 0]
a31 = a[3, 1]
a32 = a[3, 2]
a33 = a[3, 3]
let
b00 = b[0, 0]
b01 = b[0, 1]
b02 = b[0, 2]
b03 = b[0, 3]
b10 = b[1, 0]
b11 = b[1, 1]
b12 = b[1, 2]
b13 = b[1, 3]
b20 = b[2, 0]
b21 = b[2, 1]
b22 = b[2, 2]
b23 = b[2, 3]
b30 = b[3, 0]
b31 = b[3, 1]
b32 = b[3, 2]
b33 = b[3, 3]
result[0, 0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30
result[0, 1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31
result[0, 2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32
result[0, 3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33
result[1, 0] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30
result[1, 1] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31
result[1, 2] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32
result[1, 3] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33
result[2, 0] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30
result[2, 1] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31
result[2, 2] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32
result[2, 3] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33
result[3, 0] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30
result[3, 1] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31
result[3, 2] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32
result[3, 3] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33
proc `*`*[T](a: GMat4[T], b: GVec3[T]): GVec3[T] =
gvec3[T](
a[0, 0] * b.x + a[1, 0] * b.y + a[2, 0] * b.z + a[3, 0],
a[0, 1] * b.x + a[1, 1] * b.y + a[2, 1] * b.z + a[3, 1],
a[0, 2] * b.x + a[1, 2] * b.y + a[2, 2] * b.z + a[3, 2]
)
proc `*`*[T](a: GMat4[T], b: GVec4[T]): GVec4[T] =
gvec4[T](
a[0, 0] * b.x + a[1, 0] * b.y + a[2, 0] * b.z + a[3, 0] * b.w,
a[0, 1] * b.x + a[1, 1] * b.y + a[2, 1] * b.z + a[3, 1] * b.w,
a[0, 2] * b.x + a[1, 2] * b.y + a[2, 2] * b.z + a[3, 2] * b.w,
a[0, 3] * b.x + a[1, 3] * b.y + a[2, 3] * b.z + a[3, 3] * b.w
)
proc transpose*[T](a: GMat3[T]): GMat3[T] =
## Return an transpose of the matrix.
gmat3[T](
a[0, 0], a[1, 0], a[2, 0],
a[0, 1], a[1, 1], a[2, 1],
a[0, 2], a[1, 2], a[2, 2]
)
proc transpose*[T](a: GMat4[T]): GMat4[T] =
## Return an transpose of the matrix.
gmat4[T](
a[0, 0], a[1, 0], a[2, 0], a[3, 0],
a[0, 1], a[1, 1], a[2, 1], a[3, 1],
a[0, 2], a[1, 2], a[2, 2], a[3, 2],
a[0, 3], a[1, 3], a[2, 3], a[3, 3]
)
proc determinant*[T](a: GMat3[T]): T =
## Compute a determinant of the matrix.
(
a[0, 0] * (a[1, 1] * a[2, 2] - a[2, 1] * a[1, 2]) -
a[0, 1] * (a[1, 0] * a[2, 2] - a[1, 2] * a[2, 0]) +
a[0, 2] * (a[1, 0] * a[2, 1] - a[1, 1] * a[2, 0])
)
proc determinant*[T](a: GMat4[T]): T =
## Compute a determinant of the matrix.
let
a00 = a[0, 0]
a01 = a[0, 1]
a02 = a[0, 2]
a03 = a[0, 3]
a10 = a[1, 0]
a11 = a[1, 1]
a12 = a[1, 2]
a13 = a[1, 3]
a20 = a[2, 0]
a21 = a[2, 1]
a22 = a[2, 2]
a23 = a[2, 3]
a30 = a[3, 0]
a31 = a[3, 1]
a32 = a[3, 2]
a33 = a[3, 3]
(
a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 +
a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 +
a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 +
a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 +
a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 +
a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33
)
proc inverse*[T](a: GMat3[T]): GMat3[T] =
## Return an inverse of the matrix.
let
invDet = 1 / a.determinant
result[0, 0] = +(a[1, 1] * a[2, 2] - a[2, 1] * a[1, 2]) * invDet
result[0, 1] = -(a[0, 1] * a[2, 2] - a[0, 2] * a[2, 1]) * invDet
result[0, 2] = +(a[0, 1] * a[1, 2] - a[0, 2] * a[1, 1]) * invDet
result[1, 0] = -(a[1, 0] * a[2, 2] - a[1, 2] * a[2, 0]) * invDet
result[1, 1] = +(a[0, 0] * a[2, 2] - a[0, 2] * a[2, 0]) * invDet
result[1, 2] = -(a[0, 0] * a[1, 2] - a[1, 0] * a[0, 2]) * invDet
result[2, 0] = +(a[1, 0] * a[2, 1] - a[2, 0] * a[1, 1]) * invDet
result[2, 1] = -(a[0, 0] * a[2, 1] - a[2, 0] * a[0, 1]) * invDet
result[2, 2] = +(a[0, 0] * a[1, 1] - a[1, 0] * a[0, 1]) * invDet
proc inverse*[T](a: GMat4[T]): GMat4[T] =
## Return an inverse of the matrix.
let
a00 = a[0, 0]
a01 = a[0, 1]
a02 = a[0, 2]
a03 = a[0, 3]
a10 = a[1, 0]
a11 = a[1, 1]
a12 = a[1, 2]
a13 = a[1, 3]
a20 = a[2, 0]
a21 = a[2, 1]
a22 = a[2, 2]
a23 = a[2, 3]
a30 = a[3, 0]
a31 = a[3, 1]
a32 = a[3, 2]
a33 = a[3, 3]
let
b00 = a00 * a11 - a01 * a10
b01 = a00 * a12 - a02 * a10
b02 = a00 * a13 - a03 * a10
b03 = a01 * a12 - a02 * a11
b04 = a01 * a13 - a03 * a11
b05 = a02 * a13 - a03 * a12
b06 = a20 * a31 - a21 * a30
b07 = a20 * a32 - a22 * a30
b08 = a20 * a33 - a23 * a30
b09 = a21 * a32 - a22 * a31
b10 = a21 * a33 - a23 * a31
b11 = a22 * a33 - a23 * a32
# Calculate the inverse determinant.
let invDet = 1 / a.determinant
result[0, 0] = (+a11 * b11 - a12 * b10 + a13 * b09) * invDet
result[0, 1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet
result[0, 2] = (+a31 * b05 - a32 * b04 + a33 * b03) * invDet
result[0, 3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet
result[1, 0] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet
result[1, 1] = (+a00 * b11 - a02 * b08 + a03 * b07) * invDet
result[1, 2] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet
result[1, 3] = (+a20 * b05 - a22 * b02 + a23 * b01) * invDet
result[2, 0] = (+a10 * b10 - a11 * b08 + a13 * b06) * invDet
result[2, 1] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet
result[2, 2] = (+a30 * b04 - a31 * b02 + a33 * b00) * invDet
result[2, 3] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet
result[3, 0] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet
result[3, 1] = (+a00 * b09 - a01 * b07 + a02 * b06) * invDet
result[3, 2] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet
result[3, 3] = (+a20 * b03 - a21 * b01 + a22 * b00) * invDet
proc scale*[T](v: GVec2[T]): GMat3[T] =
## Create scale matrix.
gmat3[T](
v.x, 0, 0,
0, v.y, 0,
0, 0, 1
)
proc scale*[T](v: GVec3[T]): GMat4[T] =
## Create scale matrix.
gmat4[T](
v.x, 0, 0, 0,
0, v.y, 0, 0,
0, 0, v.z, 0,
0, 0, 0, 1
)
proc translate*[T](v: GVec2[T]): GMat3[T] =
## Create translation matrix.
gmat3[T](
1, 0, 0,
0, 1, 0,
v.x, v.y, 1
)
proc translate*[T](v: GVec3[T]): GMat4[T] =
## Create translation matrix.
gmat4[T](
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
v.x, v.y, v.z, 1
)
proc rotate*[T](angle: T): GMat3[T] =
## Create a 2d rotation matrix by an angle.
let
sin = sin(angle)
cos = cos(angle)
gmat3[T](
cos, -sin, 0,
sin, cos, 0,
0, 0, 1
)
proc rotationOnly*[T](a: GMat4[T]): GMat4[T] {.inline.} =
## Clears the positional component and returns rotation only.
## Assumes matrix has not been scaled.
result = a
result.pos = gvec3(0, 0, 0)
proc rotateX*[T](angle: T): GMat4[T] =
## Return a rotation matrix around X with angle.
result[0, 0] = 1
result[0, 1] = 0
result[0, 2] = 0
result[0, 3] = 0
result[1, 0] = 0
result[1, 1] = cos(angle)
result[1, 2] = -sin(angle)
result[1, 3] = 0
result[2, 0] = 0
result[2, 1] = sin(angle)
result[2, 2] = cos(angle)
result[2, 3] = 0
result[3, 0] = 0
result[3, 1] = 0
result[3, 2] = 0
result[3, 3] = 1
proc rotateY*[T](angle: T): GMat4[T] =
## Return a rotation matrix around Y with angle.
result[0, 0] = cos(angle)
result[0, 1] = 0
result[0, 2] = sin(angle)
result[0, 3] = 0
result[1, 0] = 0
result[1, 1] = 1
result[1, 2] = 0
result[1, 3] = 0
result[2, 0] = -sin(angle)
result[2, 1] = 0
result[2, 2] = cos(angle)
result[2, 3] = 0
result[3, 0] = 0
result[3, 1] = 0
result[3, 2] = 0
result[3, 3] = 1
proc rotateZ*[T](angle: T): GMat4[T] =
## Return a rotation matrix around Z with angle.
result[0, 0] = cos(angle)
result[0, 1] = -sin(angle)
result[0, 2] = 0
result[0, 3] = 0
result[1, 0] = sin(angle)
result[1, 1] = cos(angle)
result[1, 2] = 0
result[1, 3] = 0
result[2, 0] = 0
result[2, 1] = 0
result[2, 2] = 1
result[2, 3] = 0
result[3, 0] = 0
result[3, 1] = 0
result[3, 2] = 0
result[3, 3] = 1
proc toAngles*[T](a: GVec3[T]): GVec3[T] =
## Given a 3d vector, computes euler angles: pitch and yaw
## pitch (x rotation)
## yaw (y rotation)
## roll (z rotation) - always 0 in vector case
if a == vec3(0, 0, 0):
return
let
yaw = -arctan2(a.x, a.z)
pitch = -arctan2(sqrt(a.x*a.x + a.z*a.z), a.y) + T(PI/2)
result.x = pitch.fixAngle
result.y = yaw.fixAngle
proc toAngles*[T](origin, target: GVec3[T]): GVec3[T] =
## Gives euler angles from origin to target
## pitch (x rotation)
## yaw (y rotation)
## roll (z rotation) - always 0 in vector case
toAngles(target - origin)
proc toAngles*[T](m: GMat4[T]): GVec3[T] =
## Decomposes the matrix into euler angles:
## pitch (x rotation)
## yaw (y rotation)
## roll (z rotation)
## Assumes matrix has not been scaled.
result.x = arcsin(m[2,1])
if result.x > PI/2:
# Degenerate case over north pole.
result.y = arctan2(m[0, 2], m[0, 0])
elif result.x < -PI/2:
# Degenerate case over south pole.
result.y = arctan2(m[0, 2], m[0, 0])
else:
# Normal case.
result.y = -arctan2(m[2, 0], m[2, 2])
result.z = -arctan2(m[0, 1], m[1, 1])
proc fromAngles*[T](a: GVec3[T]): GMat4[T] =
## Takes a vector containing euler angles and returns a matrix.
rotateY(a.y) * rotateX(a.x) * rotateZ(a.z)
proc frustum*[T](left, right, bottom, top, near, far: T): GMat4[T] =
## Create a frustum matrix.
let
rl = (right - left)
tb = (top - bottom)
fn = (far - near)
result[0, 0] = (near * 2) / rl
result[0, 1] = 0
result[0, 2] = 0
result[0, 3] = 0
result[1, 0] = 0
result[1, 1] = (near * 2) / tb
result[1, 2] = 0
result[1, 3] = 0
result[2, 0] = (right + left) / rl
result[2, 1] = (top + bottom) / tb
result[2, 2] = -(far + near) / fn
result[2, 3] = -1
result[3, 0] = 0
result[3, 1] = 0
result[3, 2] = -(far * near * 2) / fn
result[3, 3] = 0
proc perspective*[T](fovy, aspect, near, far: T): GMat4[T] =
## Create a perspective matrix.
let
top: T = near * tan(fovy * PI.float32 / 360.0)
right: T = top * aspect
frustum(-right, right, -top, top, near, far)
proc ortho*[T](left, right, bottom, top, near, far: T): GMat4[T] =
## Create an orthographic matrix.
let
rl: T = (right - left)
tb: T = (top - bottom)
fn: T = (far - near)
result[0, 0] = T(2 / rl)
result[0, 1] = 0
result[0, 2] = 0
result[0, 3] = 0
result[1, 0] = 0
result[1, 1] = T(2 / tb)
result[1, 2] = 0
result[1, 3] = 0
result[2, 0] = 0
result[2, 1] = 0
result[2, 2] = T(-2 / fn)
result[2, 3] = 0
result[3, 0] = T(-(left + right) / rl)
result[3, 1] = T(-(top + bottom) / tb)
result[3, 2] = T(-(far + near) / fn)
result[3, 3] = 1
proc lookAt*[T](eye, center, up: GVec3[T]): GMat4[T]
{.deprecated: "Wrong coordinate system. " &
"Use toAngles(eye, center).fromAngles() instead to get " &
"right-handed-z-forward coordinate system".} =
## Create a matrix that would convert eye pos to looking at center.
let
eyex = eye[0]
eyey = eye[1]
eyez = eye[2]
upx = up[0]
upy = up[1]
upz = up[2]
centerx = center[0]
centery = center[1]
centerz = center[2]
if eyex == centerx and eyey == centery and eyez == centerz:
return
var
# vec3.direction(eye, center, z)
z0 = eyex - center[0]
z1 = eyey - center[1]
z2 = eyez - center[2]
# normalize (no check needed for 0 because of early return)
var len = 1 / sqrt(z0 * z0 + z1 * z1 + z2 * z2)
z0 *= len
z1 *= len
z2 *= len
var
# vec3.normalize(vec3.cross(up, z, x))
x0 = upy * z2 - upz * z1
x1 = upz * z0 - upx * z2
x2 = upx * z1 - upy * z0
len = sqrt(x0 * x0 + x1 * x1 + x2 * x2)
if len == 0:
x0 = 0
x1 = 0
x2 = 0
else:
len = 1 / len
x0 *= len
x1 *= len
x2 *= len
var
# vec3.normalize(vec3.cross(z, x, y))
y0 = z1 * x2 - z2 * x1
y1 = z2 * x0 - z0 * x2
y2 = z0 * x1 - z1 * x0
len = sqrt(y0 * y0 + y1 * y1 + y2 * y2)
if len == 0:
y0 = 0
y1 = 0
y2 = 0
else:
len = 1/len
y0 *= len
y1 *= len
y2 *= len
result[0, 0] = x0
result[0, 1] = y0
result[0, 2] = z0
result[0, 3] = 0
result[1, 0] = x1
result[1, 1] = y1
result[1, 2] = z1
result[1, 3] = 0
result[2, 0] = x2
result[2, 1] = y2
result[2, 2] = z2
result[2, 3] = 0
result[3, 0] = -(x0 * eyex + x1 * eyey + x2 * eyez)
result[3, 1] = -(y0 * eyex + y1 * eyey + y2 * eyez)
result[3, 2] = -(z0 * eyex + z1 * eyey + z2 * eyez)
result[3, 3] = 1
proc lookAt*[T](eye, center: GVec3[T]): GMat4[T]
{.deprecated: "Wrong coordinate system. " &
"Use toAngles(eye, center).fromAngles() instead to get " &
"right-handed-z-forward coordinate system".} =
## Look center from eye with default UP vector.
lookAt(eye, center, gvec3(T(0), 0, 1))
proc angle*[T](a: GVec2[T]): T =
## Angle of a Vec2.
arctan2(a.y, a.x)
proc angle*[T](a, b: GVec2[T]): T =
## Angle between 2 Vec2.
fixAngle(arctan2(a.y - b.y, a.x - b.x))
proc angle*[T](a, b: GVec3[T]): T =
## Angle between 2 Vec3.
var dot = dot(a, b)
dot = dot / (a.length * b.length)
arccos(dot)
type
Quat* = GVec4[float32]
DQuat* = GVec4[float64]
template genQuatConstructor*(lower, upper, typ: untyped) =
## Generate quaternion constructor for your own type.
proc `lower`*(): `upper` = gvec4[typ](0, 0, 0, 1)
proc `lower`*(x, y, z, w: typ): `upper` = gvec4[typ](x, y, z, w)
proc `lower`*(x: typ): `upper` = gvec4[typ](x, x, x, x)
proc `lower`*[T](x: GVec4[T]): `upper` =
gvec4[typ](typ(x[0]), typ(x[1]), typ(x[2]), typ(x[3]))
genQuatConstructor(quat, Quat, float32)
genQuatConstructor(dquat, DQuat, float64)
proc fromAxisAngle*[T](axis: GVec3[T], angle: T): GVec4[T] =
## Create a quaternion from axis and angle.
let
a = axis.normalize()
s = sin(angle / 2)
gvec4[T](
a.x * s,
a.y * s,
a.z * s,
cos(angle / 2)
)
proc toAxisAngle*[T](q: GVec4[T]): (GVec3[T], T) =
## Convert a quaternion to axis and angle.
let cosAngle = q.w
let angle = arccos(cosAngle) * 2.0
var
sinAngle = sqrt(1.0 - cosAngle * cosAngle)
axis: GVec4[T]
if abs(sinAngle) < 0.0005:
sinAngle = 1.0
axis.x = [
q.x / sinAngle,
q.y / sinAngle,
q.z / sinAngle
]
return (axis, angle)
proc orthogonal*[T](v: GVec3[T]): GVec3[T] =
## Returns orthogonal vector to given vector.
let
v = abs(v)
other: type(v) =
if v.x < v.y:
if v.x < v.z:
gvec3(T(1), 0, 0) # X_AXIS
else:
gvec3(T(0), 0, 1) # Z_AXIS
elif v.y < v.z:
gvec3(T(0), 1, 0) # Y_AXIS
else:
gvec3(T(0), 0, 1) # Z_AXIS
return cross(v, other)
proc fromTwoVectors*[T](a, b: GVec3[T]): GVec4[T] =
## Return a quat that would take a and rotate it into b.
# It is important that the inputs are of equal length when
# calculating the half-way vector.
let
u = b.normalize()
v = a.normalize()
# Unfortunately, we have to check for when u == -v, as u + v
# in this case will be (0, 0, 0), which cannot be normalized.
if (u == -v):
# 180 degree rotation around any orthogonal vector
let q = normalize(orthogonal(u))
return gvec4(q.x, q.y, q.z, 0)
let
half = normalize(u + v)
q = cross(u, half)
w = dot(u, half)
return gvec4(q.x, q.y, q.z, w)
proc nlerp*(a: Quat, b: Quat, v: float32): Quat =
if dot(a, b) < 0:
(-a * (1.0 - v) + b * v).normalize()
else:
(a * (1.0 - v) + b * v).normalize()
proc quat*[T](m: GMat4[T]): GVec4[T] =
## Create a quaternion from matrix.
let
m00 = m[0, 0]
m01 = m[1, 0]
m02 = m[2, 0]
m10 = m[0, 1]
m11 = m[1, 1]
m12 = m[2, 1]
m20 = m[0, 2]
m21 = m[1, 2]
m22 = m[2, 2]
var
q: GVec4[T]
t: T
if m22 < 0:
if m00 > m11:
t = 1 + m00 - m11 - m22
q = gvec4(t, m01 + m10, m20 + m02, m12 - m21)
else:
t = 1 - m00 + m11 - m22
q = gvec4(m01 + m10, t, m12 + m21, m20 - m02)
else:
if m00 < - m11:
t = 1 - m00 - m11 + m22
q = gvec4(m20 + m02, m12 + m21, t, m01 - m10)
else:
t = 1 + m00 + m11 + m22
q = gvec4(m12 - m21, m20 - m02, m01 - m10, t)
q = q * (0.5 / sqrt(t))
if abs(q.length - 1.0) > 0.001:
return gvec4(T(0), 0, 0, 1)
return q
proc mat4*[T](q: GVec4[T]): GMat4[T] =
let
xx = q.x * q.x
xy = q.x * q.y
xz = q.x * q.z
xw = q.x * q.w
yy = q.y * q.y
yz = q.y * q.z
yw = q.y * q.w
zz = q.z * q.z
zw = q.z * q.w
result[0, 0] = 1 - 2 * (yy + zz)
result[0, 1] = 0 + 2 * (xy - zw)
result[0, 2] = 0 + 2 * (xz + yw)
result[0, 3] = 0
result[1, 0] = 0 + 2 * (xy + zw)
result[1, 1] = 1 - 2 * (xx + zz)
result[1, 2] = 0 + 2 * (yz - xw)
result[1, 3] = 0
result[2, 0] = 0 + 2 * (xz - yw)
result[2, 1] = 0 + 2 * (yz + xw)
result[2, 2] = 1 - 2 * (xx + yy)
result[2, 3] = 0
result[3, 0] = 0
result[3, 1] = 0
result[3, 2] = 0
result[3, 3] = 1.0
proc rotate*[T](angle: T, axis: GVec3[T]): GMat4[T] =
## Return a rotation matrix with axis and angle.
fromAxisAngle(axis, angle).mat4()
when defined(release):
{.pop.}
{.pop.}