Skip to content

Instantly share code, notes, and snippets.

@BitPuffin
Created March 10, 2014 11:51
Show Gist options
  • Save BitPuffin/9463633 to your computer and use it in GitHub Desktop.
Save BitPuffin/9463633 to your computer and use it in GitHub Desktop.
## Author: Isak Andersson
from math import pow, sqrt
from macros import error
type
TVector*[T; D: static[int]] = array[0..D-1, T] ## T = Type, I = Indices - will possibly later be changed to Dimension (number)
TVec2* = TVector[float32, 2]
TVec3* = TVector[float32, 3]
TVec4* = TVector[float32, 4]
TDVec2* = TVector[float64, 2]
TDVec3* = TVector[float64, 3]
TDVec4* = TVector[float64, 4]
TIVec2* = TVector[int32, 2]
TIVec3* = TVector[int32, 3]
TIVec4* = TVector[int32, 4]
TUVec2 = TVector[uint32, 2]
TUVec3 = TVector[uint32, 3]
TUvec4 = TVector[uint32, 4]
proc `==`*(a, b: TVector): bool {.noSideEffect.} =
for i in low(a)..high(a):
if a[i] != b[i]:
return false
return true
## TODO: change tolerance to TReal once regression in compiler has been fixed
proc `~=`*(a, b: TVector, tolerance: float = 1.0e-5): bool {.noSideEffect.} =
## Approximately equal
## Takes in to account that floating points are not usually the same
for i in low(a)..high(a):
if abs(a[i] - b[i]) >= tolerance:
return false
return true
template `+`*(a: TVector): expr =
a
proc `+`*(a, b: TVector): TVector {.noSideEffect.} =
for i in low(a)..high(a):
result[i] = a[i] + b[i]
proc `-`*(a: TVector): TVector {.noSideEffect.} =
for i in low(a)..high(a):
result[i] = -a[i]
proc `-`*(a, b: TVector): TVector {.noSideEffect.} =
for i in low(a)..high(a):
result[i] = a[i] - b[i]
proc mag*(a: TVector): float =
for i in low(a)..high(a):
when TVector.T is int:
result += pow(toFloat(a[i]), 2.0)
elif TVector.T is float:
result += pow(a[i], 2.0)
else:
{.fatal: "Cannot pow that datatype".}
result = sqrt(result)
proc `*.`*(a, b: TVector): TVector.T {.noSideEffect.} =
## Vector dot product
for i in low(a)..high(a):
result += a[i] * b[i]
proc `*+`*(a, b: TVector): TVector {.noSideEffect.} =
## Vector cross product
when len(a) != 3:
{.fatal: "Cross product only works with 3 dimensional vectors".}
result =
[a[1] * b[2] - b[1] * a[2],
a[2] * b[0] - b[2] * a[0],
a[0] * b[1] - b[0] * a[1]]
proc dist*(a, b: TVector): float {.noSideEffect.} =
result = (a - b).mag
proc `*`*[T](a: T; b: TVector): TVector {.noSideEffect.} =
for i in low(b)..high(b):
result[i] = a * b[i]
template `*`*[T](a: TVector; b: T): expr =
b * a
# Kind of sucks for ints currently, perhaps convert an int vector to float in this case?
proc normalized*(a: TVector): TVector =
let m = mag(a)
for i in low(a)..high(a):
result[i] = a[i] / m
const
xSwizzleChars = {'x', 'X', 'r', 'R', 's', 'S'}
ySwizzleChars = {'y', 'Y', 'g', 'G', 't', 'T'}
zSwizzleChars = {'z', 'Z', 'b', 'B', 'p', 'P'}
wSwizzleChars = {'w', 'W', 'a', 'A', 'q', 'Q'}
swizzleChars = xSwizzleChars + ySwizzleChars + zSwizzleChars + wSwizzleChars
proc swizzleImpl[I](str: string): array[I, int] {.compileTime.} =
for i, ch in str:
if ch in xSwizzleChars:
result[i] = 0
elif ch in ySwizzleChars:
result[i] = 1
elif ch in zSwizzleChars:
result[i] = 2
elif ch in wSwizzleChars:
result[i] = 3
from strutils import contains
template `{}`*(vec: TVector, str: string{lit}): expr {.deprecated.} =
## returns a vector where the element type is `T` and the dimension is `str.len`.
## The value of each element is that of the index of `vec` that each respective character in `str`
## corresponds to.
## The corresponding index will be 0 if the character is present in xSwizzleChars, 1 if it's
## present in ySwizzleChars and so on.
## If any index is larger than that of `I` - 1, then an assertion error will be raised,
## though this will eventually be a compile-time error.
## Each character must be present in `swizzleChars`, and the length of `str` must be > 0.
## Example:
##
## .. code-block:: Nimrod
## var vec3: TVec3i = [1, 3, 37]
## assert vec3{"rgbbg"} == [1, 3, 37, 37, 3]
## var vec4: TVec4i = [1, 3, 37, 4]
## assert vec4{"wWaAqQSTPy"} == [4, 4, 4, 4, 4, 4, 1, 3, 37, 3]
when str.contains({char(0)..char(255)} - swizzleChars):
{.fatal: "Valid swizzle characters: " & $swizzleChars & ". Got: " & str.}
when str.len == 0:
{.fatal: "Cannot swizzle zero components".}
var result: TVector[vec[0].type, range[0.. <str.len]]
for i, component in swizzleImpl[0.. <str.len](str):
assert component < vec.len, "Invalid swizzle character found for " & $vec.len &
"-dimensional vector. Got: " & str
result[i] = vec[component]
result
proc `$`*(a: TVector): string {.noSideEffect.} =
result = ""
result &= "["
var h = high(a)
for i in low(a)..h:
result &= $a[i]
if i != h:
result &= ", "
result &= "]"
when isMainModule:
var vec3: TVector[int, 3] = [1, 3, 37]
#assert vec3{"rgbbg"} == [1, 3, 37, 37, 3]
#assert vec3{"rrbbggr"} == [1, 1, 37, 37, 3, 3, 1]
var vec4: TVector[int, 4] = [1, 3, 37, 4]
#assert vec4{"wWaAqQSTPy"} == [4, 4, 4, 4, 4, 4, 1, 3, 37, 3]
#assert vec4{"rgbbg"} == [1, 3, 37, 37, 3]
#assert vec4{"rrbaggr"} == [1, 1, 37, 4, 3, 3, 1]
assert($vec3 == "[1, 3, 37]")
assert($vec4 == "[1, 3, 37, 4]")
var vec3f: TVec3 = [2.0'f32, 4.0'f32, 5.0'f32]
assert vec3f ~= vec3f
assert(vec3f != ([2.0'f32, 4.0'f32, 5.001'f32]))
assert(vec3f != ([2.0'f32, 4.001'f32, 5.0'f32]))
assert(not (vec3f == ([2.0'f32, 4.001'f32, 5.0'f32])))
assert(vec3f ~= ([2.0'f32, 4.0000001'f32, 5.0'f32]))
assert(vec3f ~= ([2.0000001'f32, 4.0'f32, 5.0'f32]))
assert(not (vec3f ~= ([2.001'f32, 4.0'f32, 5.0'f32])))
var vec2f: TVector[float, range[0..1]] = [3.2, 83.28]
discard vec2f.mag
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment