changeset 474:d4750f2e0760

add: vector/matrix modules, tests, nim config script
author Sam <sam@basx.dev>
date Wed, 28 Dec 2022 11:42:21 +0700
parents 04b8471bdab4
children dc9c038cb31a
files Makefile config.nims src/matrix.nim src/vector.nim src/vertex.nim tests/megatest.nim tests/test_matrix.nim tests/test_vector.nim
diffstat 7 files changed, 894 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Tue Dec 27 20:47:48 2022 +0700
+++ b/Makefile	Wed Dec 28 11:42:21 2022 +0700
@@ -5,28 +5,42 @@
 WINDOWS_DEBUG_OPTIONS := --cc:vcc --passC:'/MDd' --passL:'ucrtd.lib' 
 WINDOWS_RELEASE_OPTIONS := --cc:vcc --passC:'/MD' --passL:'ucrt.lib' 
 
-# build
-build/debug/linux/hello_triangle: ${SOURCES}
-	mkdir -p $$( dirname $@ )
+make_dirs: 
+	mkdir -p build/debug/linux
+	mkdir -p build/release/linux
+	mkdir -p build/debug/windows
+	mkdir -p build/release/windows
+
+# build hello_triangle
+build/debug/linux/hello_triangle: ${SOURCES} make_dirs
 	nim c ${COMPILE_OPTIONS} ${DEBUG_OPTIONS} -o:$@ examples/hello_triangle.nim
-build/release/linux/hello_triangle: ${SOURCES}
-	mkdir -p $$( dirname $@ )
+build/release/linux/hello_triangle: ${SOURCES} make_dirs
 	nim c ${COMPILE_OPTIONS} ${RELEASE_OPTIONS} -o:$@ examples/hello_triangle.nim
-build/debug/windows/hello_triangle.exe:  ${SOURCES}
-	mkdir -p $$( dirname $@ )
+build/debug/windows/hello_triangle.exe:  ${SOURCES} make_dirs
 	nim c ${COMPILE_OPTIONS} ${DEBUG_OPTIONS} ${WINDOWS_DEBUG_OPTIONS} -o:$@ examples/hello_triangle.nim
-build/release/windows/hello_triangle.exe: ${SOURCES}
-	mkdir -p $$( dirname $@ )
+build/release/windows/hello_triangle.exe: ${SOURCES} make_dirs
 	nim c ${COMPILE_OPTIONS} ${RELEASE_OPTIONS} ${WINDOWS_RELEASE_OPTIONS} -o:$@ examples/hello_triangle.nim
 
 build_all_linux: build/debug/linux/hello_triangle build/release/linux/hello_triangle
 build_all_windows: build/debug/windows/hello_triangle.exe build/release/windows/hello_triangle.exe
+build_all: build_all_linux build_all_windows
 
-build_all: build_all_linux build_all_windows
+# build maths (for testing)
+build/debug/linux/maths: ${SOURCES} make_dirs
+	nim c ${COMPILE_OPTIONS} ${DEBUG_OPTIONS} -o:$@ examples/maths.nim
+build/release/linux/maths: ${SOURCES} make_dirs
+	nim c ${COMPILE_OPTIONS} ${RELEASE_OPTIONS} -o:$@ examples/maths.nim
+build/debug/windows/maths.exe:  ${SOURCES} make_dirs
+	nim c ${COMPILE_OPTIONS} ${DEBUG_OPTIONS} ${WINDOWS_DEBUG_OPTIONS} -o:$@ examples/maths.nim
+build/release/windows/maths.exe: ${SOURCES} make_dirs
+	nim c ${COMPILE_OPTIONS} ${RELEASE_OPTIONS} ${WINDOWS_RELEASE_OPTIONS} -o:$@ examples/maths.nim
 
 clean:
 	rm -rf build
 
+tests:
+	testament p tests/
+
 # publish
 publish_linux_debug: build/debug/linux/hello_triangle
 	scp $< basx.dev:/var/www/public.basx.dev/joni/linux/debug/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/config.nims	Wed Dec 28 11:42:21 2022 +0700
@@ -0,0 +1,1 @@
+switch("path", "src")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/matrix.nim	Wed Dec 28 11:42:21 2022 +0700
@@ -0,0 +1,151 @@
+import random
+import std/strutils
+import std/typetraits
+
+import ./vector
+
+type
+  # layout is row-first
+  # having an object instead of directly aliasing the array seems a bit ugly at
+  # first, but is necessary to be able to work correctly with distinguished
+  # types (i.e. Mat23 and Mat32 would be an alias for the same type array[6, T]
+  # which prevents the type system from identifying the correct type at times)
+  #
+  # Though, great news is that objects have zero overhead!
+  Mat22*[T: SomeNumber] = object
+    data: array[4, T]
+  Mat23*[T: SomeNumber] = object
+    data: array[6, T]
+  Mat32*[T: SomeNumber] = object
+    data: array[6, T]
+  Mat33*[T: SomeNumber] = object
+    data: array[9, T]
+  Mat34*[T: SomeNumber] = object
+    data: array[12, T]
+  Mat43*[T: SomeNumber] = object
+    data: array[12, T]
+  Mat44*[T: SomeNumber] = object
+    data: array[16, T]
+  MatMM* = Mat22|Mat33|Mat44
+  MatMN* = Mat23|Mat32|Mat34|Mat43
+  Mat* = MatMM|MatMN
+  IntegerMat = Mat22[SomeInteger]|Mat33[SomeInteger]|Mat44[SomeInteger]|Mat23[SomeInteger]|Mat32[SomeInteger]|Mat34[SomeInteger]|Mat43[SomeInteger]
+  FloatMat = Mat22[SomeFloat]|Mat33[SomeFloat]|Mat44[SomeFloat]|Mat23[SomeFloat]|Mat32[SomeFloat]|Mat34[SomeFloat]|Mat43[SomeFloat]
+
+
+func rowCount*(m: Mat22): int = 2
+func columnCount*(m: Mat22): int = 2
+func rowCount*(m: Mat23): int = 2
+func columnCount*(m: Mat23): int = 3
+func rowCount*(m: Mat32): int = 3
+func columnCount*(m: Mat32): int = 2
+func rowCount*(m: Mat33): int = 3
+func columnCount*(m: Mat33): int = 3
+func rowCount*(m: Mat34): int = 3
+func columnCount*(m: Mat34): int = 4
+func rowCount*(m: Mat43): int = 4
+func columnCount*(m: Mat43): int = 3
+func rowCount*(m: Mat44): int = 4
+func columnCount*(m: Mat44): int = 4
+
+
+func toString[T](value: T): string =
+  var
+    strvalues: seq[string]
+    maxwidth = 0
+
+  for n in value.data:
+    let strval = $n
+    strvalues.add(strval)
+    if strval.len > maxwidth:
+      maxwidth = strval.len
+
+  for i in 0 ..< strvalues.len:
+    let filler = " ".repeat(maxwidth - strvalues[i].len)
+    if i mod value.columnCount == value.columnCount - 1:
+      result &= filler & strvalues[i] & "\n"
+    else:
+      if i mod value.columnCount == 0:
+        result &= "  "
+      result &= filler & strvalues[i] & "  "
+  result = $T & "\n" & result
+
+func `$`*(v: Mat22[SomeNumber]): string = toString[Mat22[SomeNumber]](v)
+func `$`*(v: Mat23[SomeNumber]): string = toString[Mat23[SomeNumber]](v)
+func `$`*(v: Mat32[SomeNumber]): string = toString[Mat32[SomeNumber]](v)
+func `$`*(v: Mat33[SomeNumber]): string = toString[Mat33[SomeNumber]](v)
+func `$`*(v: Mat34[SomeNumber]): string = toString[Mat34[SomeNumber]](v)
+func `$`*(v: Mat43[SomeNumber]): string = toString[Mat43[SomeNumber]](v)
+func `$`*(v: Mat44[SomeNumber]): string = toString[Mat44[SomeNumber]](v)
+
+func `[]`*[T: Mat](m: T, row, col: int): auto = m.data[col + row * m.columnCount]
+proc `[]=`*[T: Mat, U](m: var T, row, col: int, value: U) = m.data[col + row * m.columnCount] = value
+
+func row*[T: Mat22](m: T, i: 0..1): auto = Vec2([m[i, 0], m[i, 1]])
+func row*[T: Mat32](m: T, i: 0..2): auto = Vec2([m[i, 0], m[i, 1]])
+func row*[T: Mat23](m: T, i: 0..1): auto = Vec3([m[i, 0], m[i, 1], m[i, 2]])
+func row*[T: Mat33](m: T, i: 0..2): auto = Vec3([m[i, 0], m[i, 1], m[i, 2]])
+func row*[T: Mat43](m: T, i: 0..3): auto = Vec3([m[i, 0], m[i, 1], m[i, 2]])
+func row*[T: Mat34](m: T, i: 0..2): auto = Vec4([m[i, 0], m[i, 1], m[i, 2], m[i, 3]])
+func row*[T: Mat44](m: T, i: 0..3): auto = Vec4([m[i, 0], m[i, 1], m[i, 2], m[i, 3]])
+
+func col*[T: Mat22](m: T, i: 0..1): auto = Vec2([m[0, i], m[1, i]])
+func col*[T: Mat23](m: T, i: 0..2): auto = Vec2([m[0, i], m[1, i]])
+func col*[T: Mat32](m: T, i: 0..1): auto = Vec3([m[0, i], m[1, i], m[2, i]])
+func col*[T: Mat33](m: T, i: 0..2): auto = Vec3([m[0, i], m[1, i], m[2, i]])
+func col*[T: Mat34](m: T, i: 0..3): auto = Vec3([m[0, i], m[1, i], m[2, i]])
+func col*[T: Mat43](m: T, i: 0..2): auto = Vec4([m[0, i], m[1, i], m[2, i], m[3, i]])
+func col*[T: Mat44](m: T, i: 0..3): auto = Vec4([m[0, i], m[1, i], m[2, i], m[3, i]])
+
+func transposed*[T](m: Mat22[T]): Mat22[T] = Mat22[T](data: [
+  m[0, 0], m[1, 0],
+  m[0, 1], m[1, 1],
+])
+func transposed*[T](m: Mat23[T]): Mat32[T] = Mat32[T](data: [
+  m[0, 0], m[1, 0],
+  m[0, 1], m[1, 1],
+  m[0, 2], m[1, 2],
+])
+func transposed*[T](m: Mat32[T]): Mat23[T] = Mat23[T](data: [
+  m[0, 0], m[1, 0], m[2, 0],
+  m[0, 1], m[1, 1], m[2, 1],
+])
+func transposed*[T](m: Mat33[T]): Mat33[T] = Mat33[T](data: [
+  m[0, 0], m[1, 0], m[2, 0],
+  m[0, 1], m[1, 1], m[2, 1],
+  m[0, 2], m[1, 2], m[2, 2],
+])
+func transposed*[T](m: Mat43[T]): Mat34[T] = Mat34[T](data: [
+  m[0, 0], m[1, 0], m[2, 0], m[3, 0],
+  m[0, 1], m[1, 1], m[2, 1], m[3, 1],
+  m[0, 2], m[1, 2], m[2, 2], m[3, 2],
+])
+func transposed*[T](m: Mat34[T]): Mat43[T] = Mat43[T](data: [
+  m[0, 0], m[1, 0], m[2, 0],
+  m[0, 1], m[1, 1], m[2, 1],
+  m[0, 2], m[1, 2], m[2, 2],
+  m[0, 3], m[1, 3], m[2, 3],
+])
+func transposed*[T](m: Mat44[T]): Mat44[T] = Mat44[T](data: [
+  m[0, 0], m[1, 0], m[2, 0], m[3, 0],
+  m[0, 1], m[1, 1], m[2, 1], m[3, 1],
+  m[0, 2], m[1, 2], m[2, 2], m[3, 2],
+  m[0, 3], m[1, 3], m[2, 3], m[3, 3],
+])
+
+# call e.g. Mat32[int]().initRandom() to get a random matrix
+template makeRandomInit(mattype: typedesc) =
+    proc randomized*[T: SomeInteger](m: mattype[T]): mattype[T] =
+      for i in 0 ..< result.data.len:
+        result.data[i] = rand(low(typeof(m.data[0])) .. high(typeof(m.data[0])))
+    proc randomized*[T: SomeFloat](m: mattype[T]): mattype[T] =
+      for i in 0 ..< result.data.len:
+        result.data[i] = rand(1.0)
+
+makeRandomInit(Mat22)
+makeRandomInit(Mat23)
+makeRandomInit(Mat32)
+makeRandomInit(Mat33)
+makeRandomInit(Mat34)
+makeRandomInit(Mat43)
+makeRandomInit(Mat44)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/vector.nim	Wed Dec 28 11:42:21 2022 +0700
@@ -0,0 +1,203 @@
+import std/math
+import std/strutils
+import std/macros
+import std/typetraits
+import std/tables
+
+
+type
+  Vec2*[T: SomeNumber] = array[2, T]
+  Vec3*[T: SomeNumber] = array[3, T]
+  Vec4*[T: SomeNumber] = array[4, T]
+  Vec* = Vec2|Vec3|Vec4
+
+# define some often used constants
+func ConstX[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(1), T(0), T(0)])
+func ConstY[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(0), T(1), T(0)])
+func ConstZ[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(0), T(0), T(1)])
+func ConstR[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(1), T(0), T(0)])
+func ConstG[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(0), T(1), T(0)])
+func ConstB[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(0), T(0), T(1)])
+
+# generates constants: Xf, Xf32, Xf64, Xi, Xi8, Xi16, Xi32, Xi64
+# Also for Y, Z, R, G, B
+# not sure if this is necessary or even a good idea...
+macro generateAllConsts() =
+  result = newStmtList()
+  for component in ["X", "Y", "Z", "R", "G", "B"]:
+    for theType in ["int", "int8", "int16", "int32", "int64", "float", "float32", "float64"]:
+      var typename = theType[0 .. 0]
+      if theType[^2].isDigit:
+        typename = typename & theType[^2]
+      if theType[^1].isDigit:
+        typename = typename & theType[^1]
+      result.add(
+        newConstStmt(
+          postfix(ident(component & typename), "*"),
+          newCall(nnkBracketExpr.newTree(ident("Const" & component), ident(theType)))
+        )
+      )
+
+generateAllConsts()
+
+const X* = ConstX[float]()
+const Y* = ConstY[float]()
+const Z* = ConstZ[float]()
+
+func newVec2*[T](x, y: T): auto = Vec2([x, y])
+func newVec3*[T](x, y, z: T): auto = Vec3([x, y, z])
+func newVec4*[T](x, y, z, w: T): auto = Vec4([x, y, z, w])
+
+func to*[T](v: Vec2): auto = Vec2([T(v[0]), T(v[1])])
+func to*[T](v: Vec3): auto = Vec3([T(v[0]), T(v[1]), T(v[2])])
+func to*[T](v: Vec4): auto = Vec4([T(v[0]), T(v[1]), T(v[2]), T(v[3])])
+
+func toString[T](value: T): string =
+  var items: seq[string]
+  for item in value:
+    items.add($item)
+  $T & "(" & join(items, "  ") & ")"
+
+func `$`*(v: Vec2[SomeNumber]): string = toString[Vec2[SomeNumber]](v)
+func `$`*(v: Vec3[SomeNumber]): string = toString[Vec3[SomeNumber]](v)
+func `$`*(v: Vec4[SomeNumber]): string = toString[Vec4[SomeNumber]](v)
+
+func length*(vec: Vec2[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[1])
+func length*(vec: Vec2[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[1] * vec[1]))
+func length*(vec: Vec3[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2])
+func length*(vec: Vec3[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]))
+func length*(vec: Vec4[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2] + vec[3] * vec[3])
+func length*(vec: Vec4[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2] + vec[3] * vec[3]))
+
+func normalized*[T](vec: Vec2[T]): auto =
+  let l = vec.length
+  when T is SomeFloat:
+    Vec2[T]([vec[0] / l, vec[1] / l])
+  else:
+    Vec2[float]([float(vec[0]) / l, float(vec[1]) / l])
+func normalized*[T](vec: Vec3[T]): auto =
+  let l = vec.length
+  when T is SomeFloat:
+    Vec3[T]([vec[0] / l, vec[1] / l, vec[2] / l])
+  else:
+    Vec3[float]([float(vec[0]) / l, float(vec[1]) / l, float(vec[2]) / l])
+func normalized*[T](vec: Vec4[T]): auto =
+  let l = vec.length
+  when T is SomeFloat:
+    Vec4[T]([vec[0] / l, vec[1] / l, vec[2] / l, vec[3] / l])
+  else:
+    Vec4[float]([float(vec[0]) / l, float(vec[1]) / l, float(vec[2]) / l, float(vec[3]) / l])
+
+# scalar operations
+func `+`*(a: Vec2, b: SomeNumber): auto = Vec2([a[0] + b, a[1] + b])
+func `+`*(a: Vec3, b: SomeNumber): auto = Vec3([a[0] + b, a[1] + b, a[2] + b])
+func `+`*(a: Vec4, b: SomeNumber): auto = Vec4([a[0] + b, a[1] + b, a[2] + b, a[3] + b])
+func `-`*(a: Vec2, b: SomeNumber): auto = Vec2([a[0] - b, a[1] - b])
+func `-`*(a: Vec3, b: SomeNumber): auto = Vec3([a[0] - b, a[1] - b, a[2] - b])
+func `-`*(a: Vec4, b: SomeNumber): auto = Vec4([a[0] - b, a[1] - b, a[2] - b, a[3] - b])
+func `*`*(a: Vec2, b: SomeNumber): auto = Vec2([a[0] * b, a[1] * b])
+func `*`*(a: Vec3, b: SomeNumber): auto = Vec3([a[0] * b, a[1] * b, a[2] * b])
+func `*`*(a: Vec4, b: SomeNumber): auto = Vec4([a[0] * b, a[1] * b, a[2] * b, a[3] * b])
+func `/`*[T: SomeInteger](a: Vec2[T], b: SomeInteger): auto = Vec2([a[0] div b, a[1] div b])
+func `/`*[T: SomeFloat](a: Vec2[T], b: SomeFloat): auto = Vec2([a[0] / b, a[1] / b])
+func `/`*[T: SomeInteger](a: Vec3[T], b: SomeInteger): auto = Vec3([a[0] div b, a[1] div b, a[2] div b])
+func `/`*[T: SomeFloat](a: Vec3[T], b: SomeFloat): auto = Vec3([a[0] / b, a[1] / b, a[2] / b])
+func `/`*[T: SomeInteger](a: Vec4[T], b: SomeInteger): auto = Vec4([a[0] div b, a[1] div b, a[2] div b, a[3] div b])
+func `/`*[T: SomeFloat](a: Vec4[T], b: SomeFloat): auto = Vec4([a[0] / b, a[1] / b, a[2] / b, a[3] / b])
+
+func `+`*(a: SomeNumber, b: Vec2): auto = Vec2([a + b[0], a + b[1]])
+func `+`*(a: SomeNumber, b: Vec3): auto = Vec3([a + b[0], a + b[1], a + b[2]])
+func `+`*(a: SomeNumber, b: Vec4): auto = Vec4([a + b[0], a + b[1], a + b[2], a + b[3]])
+func `-`*(a: SomeNumber, b: Vec2): auto = Vec2([a - b[0], a - b[1]])
+func `-`*(a: SomeNumber, b: Vec3): auto = Vec3([a - b[0], a - b[1], a - b[2]])
+func `-`*(a: SomeNumber, b: Vec4): auto = Vec4([a - b[0], a - b[1], a - b[2], a - b[3]])
+func `*`*(a: SomeNumber, b: Vec2): auto = Vec2([a * b[0], a * b[1]])
+func `*`*(a: SomeNumber, b: Vec3): auto = Vec3([a * b[0], a * b[1], a * b[2]])
+func `*`*(a: SomeNumber, b: Vec4): auto = Vec4([a * b[0], a * b[1], a * b[2], a * b[3]])
+func `/`*[T: SomeInteger](a: SomeInteger, b: Vec2[T]): auto = Vec2([a div b[0], a div b[1]])
+func `/`*[T: SomeFloat](a: SomeFloat, b: Vec2[T]): auto = Vec2([a / b[0], a / b[1]])
+func `/`*[T: SomeInteger](a: SomeInteger, b: Vec3[T]): auto = Vec3([a div b[0], a div b[1], a div b[2]])
+func `/`*[T: SomeFloat](a: SomeFloat, b: Vec3[T]): auto = Vec3([a / b[0], a / b[1], a / b[2]])
+func `/`*[T: SomeInteger](a: SomeInteger, b: Vec4[T]): auto = Vec4([a div b[0], a div b[1], a div b[2], a div b[3]])
+func `/`*[T: SomeFloat](a: SomeFloat, b: Vec4[T]): auto = Vec4([a / b[0], a / b[1], a / b[2], a / b[3]])
+
+# compontent-wise operations
+func `+`*(a, b: Vec2): auto = Vec2([a[0] + b[0], a[1] + b[1]])
+func `+`*(a, b: Vec3): auto = Vec3([a[0] + b[0], a[1] + b[1], a[2] + b[2]])
+func `+`*(a, b: Vec4): auto = Vec4([a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]])
+func `-`*(a: Vec2): auto = Vec2([-a[0], -a[1]])
+func `-`*(a: Vec3): auto = Vec3([-a[0], -a[1], -a[2]])
+func `-`*(a: Vec4): auto = Vec4([-a[0], -a[1], -a[2], -a[3]])
+func `-`*(a, b: Vec2): auto = Vec2([a[0] - b[0], a[1] - b[1]])
+func `-`*(a, b: Vec3): auto = Vec3([a[0] - b[0], a[1] - b[1], a[2] - b[2]])
+func `-`*(a, b: Vec4): auto = Vec4([a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]])
+func `*`*(a, b: Vec2): auto = Vec2([a[0] * b[0], a[1] * b[1]])
+func `*`*(a, b: Vec3): auto = Vec3([a[0] * b[0], a[1] * b[1], a[2] * b[2]])
+func `*`*(a, b: Vec4): auto = Vec4([a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]])
+func `/`*[T: SomeInteger](a, b: Vec2[T]): auto = Vec2([a[0] div b[0], a[1] div b[1]])
+func `/`*[T: SomeFloat](a, b: Vec2[T]): auto = Vec2([a[0] / b[0], a[1] / b[1]])
+func `/`*[T: SomeInteger](a, b: Vec3[T]): auto = Vec3([a[0] div b[0], a[1] div b[1], a[2] div b[2]])
+func `/`*[T: SomeFloat](a, b: Vec3[T]): auto = Vec3([a[0] / b[0], a[1] / b[1], a[2] / b[2]])
+func `/`*[T: SomeInteger](a, b: Vec4[T]): auto = Vec4([a[0] div b[0], a[1] div b[1], a[2] div b[2], a[3] div b[3]])
+func `/`*[T: SomeFloat](a, b: Vec4[T]): auto = Vec4([a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]])
+
+# special operations
+func dot*(a, b: Vec2): auto = a[0] * b[0] + a[1] * b[1]
+func dot*(a, b: Vec3): auto = a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
+func dot*(a, b: Vec4): auto = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
+func cross*(a, b: Vec3): auto = Vec3([
+  a[1] * b[2] - a[2] * b[1],
+  a[2] * b[0] - a[0] * b[2],
+  a[0] * b[1] - a[1] * b[0],
+])
+
+
+# macro to allow creation of new vectors by specifying vector components as attributes
+# e.g. myVec.xxy will return a new Vec3 that contains the components x, x an y of the original vector
+# (instead of x, y, z for a simple copy)
+proc vectorAttributeAccessor(accessor: string): NimNode =
+  const ACCESSOR_INDICES = {
+    'x': 0,
+    'y': 1,
+    'z': 2,
+    'w': 3,
+    'r': 0,
+    'g': 1,
+    'b': 2,
+    'a': 3,
+  }.toTable
+  var ret: NimNode
+  let accessorvalue = accessor
+
+  if accessorvalue.len == 0:
+    raise newException(Exception, "empty attribute")
+  elif accessorvalue.len == 1:
+    ret = nnkBracket.newTree(ident("value"), newLit(ACCESSOR_INDICES[accessorvalue[0]]))
+  if accessorvalue.len > 1:
+    var attrs = nnkBracket.newTree()
+    for attrname in accessorvalue:
+      attrs.add(nnkBracketExpr.newTree(ident("value"), newLit(ACCESSOR_INDICES[attrname])))
+    ret = nnkCall.newTree(ident("Vec" & $accessorvalue.len), attrs)
+
+  newProc(
+    name=nnkPostfix.newTree(ident("*"), ident(accessor)),
+    params=[ident("auto"), nnkIdentDefs.newTree(ident("value"), ident("Vec"), newEmptyNode())],
+    body=newStmtList(ret),
+    procType = nnkFuncDef,
+  )
+
+macro createVectorAttribAccessorFuncs() =
+  const COORD_ATTRS = ["x", "y", "z", "w"]
+  const COLOR_ATTRS = ["r", "g", "b", "a"]
+  result = nnkStmtList.newTree()
+  for attlist in [COORD_ATTRS, COLOR_ATTRS]:
+    for i in attlist:
+      result.add(vectorAttributeAccessor(i))
+      for j in attlist:
+        result.add(vectorAttributeAccessor(i & j))
+        for k in attlist:
+          result.add(vectorAttributeAccessor(i & j & k))
+          for l in attlist:
+            result.add(vectorAttributeAccessor(i & j & k & l))
+
+createVectorAttribAccessorFuncs()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/vertex.nim	Wed Dec 28 11:42:21 2022 +0700
@@ -0,0 +1,5 @@
+type
+  VertexAttribute = object
+
+  VertexType = object
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test_matrix.nim	Wed Dec 28 11:42:21 2022 +0700
@@ -0,0 +1,255 @@
+import random
+import math
+
+import vector
+import matrix
+
+
+proc echoInfo(v: Vec) =
+  echo v
+  echo "  Length: ", v.length
+  echo "  Normlized: ", v.normalized
+  echo "  negated: ", -v
+
+proc echoAdd[T, U](v1: T, v2: U) =
+  echo v1, " + ", v2, " = ", v1 + v2
+proc echoSub[T, U](v1: T, v2: U) =
+  echo v1, " - ", v2, " = ", v1 - v2
+proc echoMul[T, U](v1: T, v2: U) =
+  echo v1, " * ", v2, " = ", v1 * v2
+proc echoDiv[T, U](v1: T, v2: U) =
+  echo v1, " / ", v2, " = ", v1 / v2
+proc echoDot[T, U](v1: T, v2: U) =
+  echo v1, " o ", v2, " = ", v1.dot(v2)
+proc echoCross[T, U](v1: T, v2: U) =
+  echo v1, " x ", v2, " = ", v1.cross(v2)
+
+proc randVec2I(): auto = newVec2(rand(1 .. 10), rand(1 .. 10))
+proc randVec2F(): auto = newVec2(rand(10'f) + 0.01, rand(10'f) + 0.01)
+proc randVec3I(): auto = newVec3(rand(1 .. 10), rand(1 .. 10), rand(1 .. 10))
+proc randVec3F(): auto = newVec3(rand(10'f) + 0.01, rand(10'f) + 0.01, rand(10'f) + 0.01)
+proc randVec4I(): auto = newVec4(rand(1 .. 10), rand(1 .. 10), rand(1 .. 10), rand(1 .. 10))
+proc randVec4F(): auto = newVec4(rand(10'f) + 0.01, rand(10'f) + 0.01, rand(10'f) + 0.01, rand(10'f) + 0.01)
+
+
+proc testVector() =
+  echoInfo(randVec2I())
+  echoInfo(randVec2F())
+  echoInfo(randVec3I())
+  echoInfo(randVec3F())
+  echoInfo(randVec4I())
+  echoInfo(randVec4F())
+
+  # test math operations vector-vector
+  echoAdd(randVec2I(), randVec2I())
+  echoAdd(randVec2F(), randVec2F())
+  echoAdd(randVec3I(), randVec3I())
+  echoAdd(randVec3F(), randVec3F())
+  echoAdd(randVec4I(), randVec4I())
+  echoAdd(randVec4F(), randVec4F())
+  echoSub(randVec2I(), randVec2I())
+  echoSub(randVec2F(), randVec2F())
+  echoSub(randVec3I(), randVec3I())
+  echoSub(randVec3F(), randVec3F())
+  echoSub(randVec4I(), randVec4I())
+  echoSub(randVec4F(), randVec4F())
+  echoMul(randVec2I(), randVec2I())
+  echoMul(randVec2F(), randVec2F())
+  echoMul(randVec3I(), randVec3I())
+  echoMul(randVec3F(), randVec3F())
+  echoMul(randVec4I(), randVec4I())
+  echoMul(randVec4F(), randVec4F())
+  echoDiv(randVec2I(), randVec2I())
+  echoDiv(randVec2F(), randVec2F())
+  echoDiv(randVec3I(), randVec3I())
+  echoDiv(randVec3F(), randVec3F())
+  echoDiv(randVec4I(), randVec4I())
+  echoDiv(randVec4F(), randVec4F())
+  echoDot(randVec2I(), randVec2I())
+  echoDot(randVec2F(), randVec2F())
+  echoDot(randVec3I(), randVec3I())
+  echoDot(randVec3F(), randVec3F())
+  echoDot(randVec4I(), randVec4I())
+  echoDot(randVec4F(), randVec4F())
+  echoCross(randVec3I(), randVec3I())
+  echoCross(randVec3F(), randVec3F())
+
+
+  # test math operations vector-scalar
+  echoAdd(randVec2I(), rand(1 .. 10))
+  echoAdd(randVec2F(), rand(10'f))
+  echoAdd(randVec3I(), rand(1 .. 10))
+  echoAdd(randVec3F(), rand(10'f))
+  echoAdd(randVec4I(), rand(1 .. 10))
+  echoAdd(randVec4F(), rand(10'f))
+  echoSub(randVec2I(), rand(1 .. 10))
+  echoSub(randVec2F(), rand(10'f))
+  echoSub(randVec3I(), rand(1 .. 10))
+  echoSub(randVec3F(), rand(10'f))
+  echoSub(randVec4I(), rand(1 .. 10))
+  echoSub(randVec4F(), rand(10'f))
+  echoMul(randVec2I(), rand(1 .. 10))
+  echoMul(randVec2F(), rand(10'f))
+  echoMul(randVec3I(), rand(1 .. 10))
+  echoMul(randVec3F(), rand(10'f))
+  echoMul(randVec4I(), rand(1 .. 10))
+  echoMul(randVec4F(), rand(10'f))
+  echoDiv(randVec2I(), rand(1 .. 10))
+  echoDiv(randVec2F(), rand(10'f))
+  echoDiv(randVec3I(), rand(1 .. 10))
+  echoDiv(randVec3F(), rand(10'f))
+  echoDiv(randVec4I(), rand(1 .. 10))
+  echoDiv(randVec4F(), rand(10'f))
+
+  # test math operations scalar-vector
+  echoAdd(rand(1 .. 10), randVec2I())
+  echoAdd(rand(10'f), randVec2F())
+  echoAdd(rand(1 .. 10), randVec3I())
+  echoAdd(rand(10'f), randVec3F())
+  echoAdd(rand(1 .. 10), randVec4I())
+  echoAdd(rand(10'f), randVec4F())
+  echoSub(rand(1 .. 10), randVec2I())
+  echoSub(rand(10'f), randVec2F())
+  echoSub(rand(1 .. 10), randVec3I())
+  echoSub(rand(10'f), randVec3F())
+  echoSub(rand(1 .. 10), randVec4I())
+  echoSub(rand(10'f), randVec4F())
+  echoMul(rand(1 .. 10), randVec2I())
+  echoMul(rand(10'f), randVec2F())
+  echoMul(rand(1 .. 10), randVec3I())
+  echoMul(rand(10'f), randVec3F())
+  echoMul(rand(1 .. 10), randVec4I())
+  echoMul(rand(10'f), randVec4F())
+  echoDiv(rand(1 .. 10), randVec2I())
+  echoDiv(rand(10'f), randVec2F())
+  echoDiv(rand(1 .. 10), randVec3I())
+  echoDiv(rand(10'f), randVec3F())
+  echoDiv(rand(1 .. 10), randVec4I())
+  echoDiv(rand(10'f), randVec4F())
+
+  # test attribute syntax sugar
+  echo "float2int ", to[int](randVec2F())
+  echo "int2float ", to[float](randVec2I())
+  echo "float2int ", to[int](randVec3F())
+  echo "int2float ", to[float](randVec3I())
+  echo "float2int ", to[int](randVec3F())
+  echo "int2float ", to[float](randVec3I())
+
+  echo "V2I.xx: ", randVec2I().xx
+  echo "V2I.yx: ", randVec2I().xy
+  echo "V2F.xx: ", randVec2F().xx
+  echo "V2F.yx: ", randVec2F().yx
+  echo "V2I.rr: ", randVec2I().rr
+  echo "V2I.gr: ", randVec2I().gr
+  echo "V2F.rr: ", randVec2F().rr
+  echo "V2F.gr: ", randVec2F().gr
+
+  echo "V3I.yyy: ", randVec3I().yyy
+  echo "V3I.yxz: ", randVec3I().xyz
+  echo "V3F.yyy: ", randVec3F().yyy
+  echo "V3F.yxz: ", randVec3F().yxz
+  echo "V3I.ggg: ", randVec3I().ggg
+  echo "V3I.grb: ", randVec3I().grb
+  echo "V3F.ggg: ", randVec3F().ggg
+  echo "V3F.grb: ", randVec3F().grb
+
+  echo "V4I.zzzz: ", randVec4I().zzzz
+  echo "V4I.yxzw: ", randVec4I().xyzw
+  echo "V4F.zzzz: ", randVec4F().zzzz
+  echo "V4F.yxzw: ", randVec4F().yxzw
+  echo "V4I.bbbb: ", randVec4I().bbbb
+  echo "V4I.grba: ", randVec4I().grba
+  echo "V4F.bbbb: ", randVec4F().bbbb
+  echo "V4F.grba: ", randVec4F().grba
+
+  echo "X: ", X
+  echo "Y: ", Y
+  echo "Z: ", Z
+  echo "X: ", Xi
+  echo "Y: ", Yi
+  echo "Z: ", Zi
+
+
+template withAllIntegerMats(stuff: untyped) =
+  stuff(Mat22[int32])
+  stuff(Mat23[int32])
+  stuff(Mat32[int32])
+  stuff(Mat33[int32])
+  stuff(Mat34[int32])
+  stuff(Mat43[int32])
+  stuff(Mat44[int32])
+  stuff(Mat22[int64])
+  stuff(Mat23[int64])
+  stuff(Mat32[int64])
+  stuff(Mat33[int64])
+  stuff(Mat34[int64])
+  stuff(Mat43[int64])
+  stuff(Mat44[int64])
+
+template withAllFloatMats(stuff: untyped) =
+  stuff(Mat22[float32])
+  stuff(Mat23[float32])
+  stuff(Mat32[float32])
+  stuff(Mat33[float32])
+  stuff(Mat34[float32])
+  stuff(Mat43[float32])
+  stuff(Mat44[float32])
+  stuff(Mat22[float64])
+  stuff(Mat23[float64])
+  stuff(Mat32[float64])
+  stuff(Mat33[float64])
+  stuff(Mat34[float64])
+  stuff(Mat43[float64])
+  stuff(Mat44[float64])
+
+template withAllMats(stuff: untyped) =
+  stuff(Mat22[int])
+  stuff(Mat23[int])
+  stuff(Mat32[int])
+  stuff(Mat33[int])
+  stuff(Mat34[int])
+  stuff(Mat43[int])
+  stuff(Mat44[int])
+  stuff(Mat22[float])
+  stuff(Mat23[float])
+  stuff(Mat32[float])
+  stuff(Mat33[float])
+  stuff(Mat34[float])
+  stuff(Mat43[float])
+  stuff(Mat44[float])
+
+template testTranspose(t: typedesc) =
+  echo "testTranspose: ", t
+  let m = t().randomized()
+  assert m == m.transposed().transposed()
+
+template testAssignI(t: typedesc) =
+  echo "testAssignI: ", t
+  var m = t()
+  for i in 0 ..< t.data.len:
+    m[rand(0 ..< m.rowCount), rand(0 ..< m.columnCount)] = rand(0'i32 .. 100'i32)
+
+template testAssignF(t: typedesc) =
+  echo "testAssignF: ", t
+  var m = t()
+  for i in 0 ..< t.data.len:
+    m[rand(0 ..< m.rowCount), rand(0 ..< m.columnCount)] = rand(100'f)
+
+template testRowCols(t: typedesc) =
+  echo "testRowCols: ", t
+  var m = t().randomized()
+  for i in 0 ..< m.rowCount:
+    echo m.row(i)
+  for i in 0 ..< m.columnCount:
+    echo m.col(i)
+
+
+proc testMatrix() =
+  withAllMats(testTranspose)
+  withAllIntegerMats(testAssignI)
+  withAllFloatMats(testAssignF)
+  withAllMats(testRowCols)
+
+randomize()
+testVector()
+testMatrix()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test_vector.nim	Wed Dec 28 11:42:21 2022 +0700
@@ -0,0 +1,255 @@
+import random
+import math
+
+import vector
+import matrix
+
+
+proc echoInfo(v: Vec) =
+  echo v
+  echo "  Length: ", v.length
+  echo "  Normlized: ", v.normalized
+  echo "  negated: ", -v
+
+proc echoAdd[T, U](v1: T, v2: U) =
+  echo v1, " + ", v2, " = ", v1 + v2
+proc echoSub[T, U](v1: T, v2: U) =
+  echo v1, " - ", v2, " = ", v1 - v2
+proc echoMul[T, U](v1: T, v2: U) =
+  echo v1, " * ", v2, " = ", v1 * v2
+proc echoDiv[T, U](v1: T, v2: U) =
+  echo v1, " / ", v2, " = ", v1 / v2
+proc echoDot[T, U](v1: T, v2: U) =
+  echo v1, " o ", v2, " = ", v1.dot(v2)
+proc echoCross[T, U](v1: T, v2: U) =
+  echo v1, " x ", v2, " = ", v1.cross(v2)
+
+proc randVec2I(): auto = newVec2(rand(1 .. 10), rand(1 .. 10))
+proc randVec2F(): auto = newVec2(rand(10'f) + 0.01, rand(10'f) + 0.01)
+proc randVec3I(): auto = newVec3(rand(1 .. 10), rand(1 .. 10), rand(1 .. 10))
+proc randVec3F(): auto = newVec3(rand(10'f) + 0.01, rand(10'f) + 0.01, rand(10'f) + 0.01)
+proc randVec4I(): auto = newVec4(rand(1 .. 10), rand(1 .. 10), rand(1 .. 10), rand(1 .. 10))
+proc randVec4F(): auto = newVec4(rand(10'f) + 0.01, rand(10'f) + 0.01, rand(10'f) + 0.01, rand(10'f) + 0.01)
+
+
+proc testVector() =
+  echoInfo(randVec2I())
+  echoInfo(randVec2F())
+  echoInfo(randVec3I())
+  echoInfo(randVec3F())
+  echoInfo(randVec4I())
+  echoInfo(randVec4F())
+
+  # test math operations vector-vector
+  echoAdd(randVec2I(), randVec2I())
+  echoAdd(randVec2F(), randVec2F())
+  echoAdd(randVec3I(), randVec3I())
+  echoAdd(randVec3F(), randVec3F())
+  echoAdd(randVec4I(), randVec4I())
+  echoAdd(randVec4F(), randVec4F())
+  echoSub(randVec2I(), randVec2I())
+  echoSub(randVec2F(), randVec2F())
+  echoSub(randVec3I(), randVec3I())
+  echoSub(randVec3F(), randVec3F())
+  echoSub(randVec4I(), randVec4I())
+  echoSub(randVec4F(), randVec4F())
+  echoMul(randVec2I(), randVec2I())
+  echoMul(randVec2F(), randVec2F())
+  echoMul(randVec3I(), randVec3I())
+  echoMul(randVec3F(), randVec3F())
+  echoMul(randVec4I(), randVec4I())
+  echoMul(randVec4F(), randVec4F())
+  echoDiv(randVec2I(), randVec2I())
+  echoDiv(randVec2F(), randVec2F())
+  echoDiv(randVec3I(), randVec3I())
+  echoDiv(randVec3F(), randVec3F())
+  echoDiv(randVec4I(), randVec4I())
+  echoDiv(randVec4F(), randVec4F())
+  echoDot(randVec2I(), randVec2I())
+  echoDot(randVec2F(), randVec2F())
+  echoDot(randVec3I(), randVec3I())
+  echoDot(randVec3F(), randVec3F())
+  echoDot(randVec4I(), randVec4I())
+  echoDot(randVec4F(), randVec4F())
+  echoCross(randVec3I(), randVec3I())
+  echoCross(randVec3F(), randVec3F())
+
+
+  # test math operations vector-scalar
+  echoAdd(randVec2I(), rand(1 .. 10))
+  echoAdd(randVec2F(), rand(10'f))
+  echoAdd(randVec3I(), rand(1 .. 10))
+  echoAdd(randVec3F(), rand(10'f))
+  echoAdd(randVec4I(), rand(1 .. 10))
+  echoAdd(randVec4F(), rand(10'f))
+  echoSub(randVec2I(), rand(1 .. 10))
+  echoSub(randVec2F(), rand(10'f))
+  echoSub(randVec3I(), rand(1 .. 10))
+  echoSub(randVec3F(), rand(10'f))
+  echoSub(randVec4I(), rand(1 .. 10))
+  echoSub(randVec4F(), rand(10'f))
+  echoMul(randVec2I(), rand(1 .. 10))
+  echoMul(randVec2F(), rand(10'f))
+  echoMul(randVec3I(), rand(1 .. 10))
+  echoMul(randVec3F(), rand(10'f))
+  echoMul(randVec4I(), rand(1 .. 10))
+  echoMul(randVec4F(), rand(10'f))
+  echoDiv(randVec2I(), rand(1 .. 10))
+  echoDiv(randVec2F(), rand(10'f))
+  echoDiv(randVec3I(), rand(1 .. 10))
+  echoDiv(randVec3F(), rand(10'f))
+  echoDiv(randVec4I(), rand(1 .. 10))
+  echoDiv(randVec4F(), rand(10'f))
+
+  # test math operations scalar-vector
+  echoAdd(rand(1 .. 10), randVec2I())
+  echoAdd(rand(10'f), randVec2F())
+  echoAdd(rand(1 .. 10), randVec3I())
+  echoAdd(rand(10'f), randVec3F())
+  echoAdd(rand(1 .. 10), randVec4I())
+  echoAdd(rand(10'f), randVec4F())
+  echoSub(rand(1 .. 10), randVec2I())
+  echoSub(rand(10'f), randVec2F())
+  echoSub(rand(1 .. 10), randVec3I())
+  echoSub(rand(10'f), randVec3F())
+  echoSub(rand(1 .. 10), randVec4I())
+  echoSub(rand(10'f), randVec4F())
+  echoMul(rand(1 .. 10), randVec2I())
+  echoMul(rand(10'f), randVec2F())
+  echoMul(rand(1 .. 10), randVec3I())
+  echoMul(rand(10'f), randVec3F())
+  echoMul(rand(1 .. 10), randVec4I())
+  echoMul(rand(10'f), randVec4F())
+  echoDiv(rand(1 .. 10), randVec2I())
+  echoDiv(rand(10'f), randVec2F())
+  echoDiv(rand(1 .. 10), randVec3I())
+  echoDiv(rand(10'f), randVec3F())
+  echoDiv(rand(1 .. 10), randVec4I())
+  echoDiv(rand(10'f), randVec4F())
+
+  # test attribute syntax sugar
+  echo "float2int ", to[int](randVec2F())
+  echo "int2float ", to[float](randVec2I())
+  echo "float2int ", to[int](randVec3F())
+  echo "int2float ", to[float](randVec3I())
+  echo "float2int ", to[int](randVec3F())
+  echo "int2float ", to[float](randVec3I())
+
+  echo "V2I.xx: ", randVec2I().xx
+  echo "V2I.yx: ", randVec2I().xy
+  echo "V2F.xx: ", randVec2F().xx
+  echo "V2F.yx: ", randVec2F().yx
+  echo "V2I.rr: ", randVec2I().rr
+  echo "V2I.gr: ", randVec2I().gr
+  echo "V2F.rr: ", randVec2F().rr
+  echo "V2F.gr: ", randVec2F().gr
+
+  echo "V3I.yyy: ", randVec3I().yyy
+  echo "V3I.yxz: ", randVec3I().xyz
+  echo "V3F.yyy: ", randVec3F().yyy
+  echo "V3F.yxz: ", randVec3F().yxz
+  echo "V3I.ggg: ", randVec3I().ggg
+  echo "V3I.grb: ", randVec3I().grb
+  echo "V3F.ggg: ", randVec3F().ggg
+  echo "V3F.grb: ", randVec3F().grb
+
+  echo "V4I.zzzz: ", randVec4I().zzzz
+  echo "V4I.yxzw: ", randVec4I().xyzw
+  echo "V4F.zzzz: ", randVec4F().zzzz
+  echo "V4F.yxzw: ", randVec4F().yxzw
+  echo "V4I.bbbb: ", randVec4I().bbbb
+  echo "V4I.grba: ", randVec4I().grba
+  echo "V4F.bbbb: ", randVec4F().bbbb
+  echo "V4F.grba: ", randVec4F().grba
+
+  echo "X: ", X
+  echo "Y: ", Y
+  echo "Z: ", Z
+  echo "X: ", Xi
+  echo "Y: ", Yi
+  echo "Z: ", Zi
+
+
+template withAllIntegerMats(stuff: untyped) =
+  stuff(Mat22[int32])
+  stuff(Mat23[int32])
+  stuff(Mat32[int32])
+  stuff(Mat33[int32])
+  stuff(Mat34[int32])
+  stuff(Mat43[int32])
+  stuff(Mat44[int32])
+  stuff(Mat22[int64])
+  stuff(Mat23[int64])
+  stuff(Mat32[int64])
+  stuff(Mat33[int64])
+  stuff(Mat34[int64])
+  stuff(Mat43[int64])
+  stuff(Mat44[int64])
+
+template withAllFloatMats(stuff: untyped) =
+  stuff(Mat22[float32])
+  stuff(Mat23[float32])
+  stuff(Mat32[float32])
+  stuff(Mat33[float32])
+  stuff(Mat34[float32])
+  stuff(Mat43[float32])
+  stuff(Mat44[float32])
+  stuff(Mat22[float64])
+  stuff(Mat23[float64])
+  stuff(Mat32[float64])
+  stuff(Mat33[float64])
+  stuff(Mat34[float64])
+  stuff(Mat43[float64])
+  stuff(Mat44[float64])
+
+template withAllMats(stuff: untyped) =
+  stuff(Mat22[int])
+  stuff(Mat23[int])
+  stuff(Mat32[int])
+  stuff(Mat33[int])
+  stuff(Mat34[int])
+  stuff(Mat43[int])
+  stuff(Mat44[int])
+  stuff(Mat22[float])
+  stuff(Mat23[float])
+  stuff(Mat32[float])
+  stuff(Mat33[float])
+  stuff(Mat34[float])
+  stuff(Mat43[float])
+  stuff(Mat44[float])
+
+template testTranspose(t: typedesc) =
+  echo "testTranspose: ", t
+  let m = t().randomized()
+  assert m == m.transposed().transposed()
+
+template testAssignI(t: typedesc) =
+  echo "testAssignI: ", t
+  var m = t()
+  for i in 0 ..< t.data.len:
+    m[rand(0 ..< m.rowCount), rand(0 ..< m.columnCount)] = rand(0'i32 .. 100'i32)
+
+template testAssignF(t: typedesc) =
+  echo "testAssignF: ", t
+  var m = t()
+  for i in 0 ..< t.data.len:
+    m[rand(0 ..< m.rowCount), rand(0 ..< m.columnCount)] = rand(100'f)
+
+template testRowCols(t: typedesc) =
+  echo "testRowCols: ", t
+  var m = t().randomized()
+  for i in 0 ..< m.rowCount:
+    echo m.row(i)
+  for i in 0 ..< m.columnCount:
+    echo m.col(i)
+
+
+proc testMatrix() =
+  withAllMats(testTranspose)
+  withAllIntegerMats(testAssignI)
+  withAllFloatMats(testAssignF)
+  withAllMats(testRowCols)
+
+randomize()
+testVector()
+testMatrix()