diff --git a/src/tuple.nim b/src/tuple.nim new file mode 100644 index 0000000..5f0f297 --- /dev/null +++ b/src/tuple.nim @@ -0,0 +1,116 @@ +from math import sqrt + +type + Tuple4* = object + x*, y*, z*, w*: float + +proc point*(x: float, y: float, z: float): Tuple4 = Tuple4(x: x, y: y, z: z, w: 1) + +proc vector*(x: float, y: float, z: float): Tuple4 = Tuple4(x: x, y: y, z: z, w: 0) + +proc isPoint*(target: Tuple4): bool = target.w == 1 +proc isVector*(target: Tuple4): bool = target.w == 0 + +proc `+`*(lhs: Tuple4, rhs: Tuple4): Tuple4 = Tuple4(x: lhs.x + rhs.x, y: lhs.y + rhs.y, z: lhs.z + rhs.z, w: lhs.w + rhs.w) +proc `-`*(lhs: Tuple4, rhs: Tuple4): Tuple4 = Tuple4(x: lhs.x - rhs.x, y: lhs.y - rhs.y, z: lhs.z - rhs.z, w: lhs.w - rhs.w) +proc `-`*(tup: Tuple4): Tuple4 = Tuple4(x: -tup.x, y: -tup.y, z: -tup.z, w: -tup.w) +proc `*`*(tup: Tuple4, mag: float): Tuple4 = Tuple4(x: tup.x * mag, y: tup.y * mag, z: tup.z * mag, w: tup.w * mag) +proc `/`*(tup: Tuple4, mag: float): Tuple4 = Tuple4(x: tup.x / mag, y: tup.y / mag, z: tup.z / mag, w: tup.w / mag) + +proc magnitude*(tup: Tuple4): float = sqrt(tup.x * tup.x + tup.y * tup.y + tup.z * tup.z + tup.w * tup.w) +proc normalize*(tup: Tuple4): Tuple4 = + let mag = tup.magnitude() + Tuple4(x: tup.x / mag, y: tup.y / mag, z: tup.z / mag, w: tup.w / mag) +proc dot*(lhs: Tuple4, rhs: Tuple4): float = lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z + lhs.w * rhs.w +proc cross*(lhs: Tuple4, rhs: Tuple4): Tuple4 = vector(lhs.y * rhs.z - lhs.z * rhs. y, + lhs.z * rhs.x - lhs.x * rhs.z, + lhs.x * rhs.y - lhs.y * rhs.x) + +when isMainModule: + import unittest + suite "tuple": + test "Manual Tuple4 is point": + let target = Tuple4(x: 2, y: 3, z: 4, w: 1) + check(target.isPoint()) + + test "Manual Tuple4 is vector": + let target = Tuple4(x: 2, y: 3, z: 4, w: 0) + check(target.isVector()) + + test "Point creation": + let target = point(2, 3, 4) + check(target == Tuple4(x: 2, y: 3, z: 4, w: 1)) + + test "Vector creation": + let target = vector(2, 3, 4) + check(target == Tuple4(x: 2, y: 3, z: 4, w: 0)) + + test "Tuple addition": + let t1 = Tuple4(x: 3, y: -2, z: 5, w: 1) + let t2 = Tuple4(x: -2, y: 3, z: 1, w: 0) + let result = Tuple4(x: 1, y: 1, z: 6, w: 1) + check(t1 + t2 == result) + + test "Point subtraction": + let t1 = point(3, 2, 1) + let t2 = point(5, 6, 7) + check(t1 - t2 == vector(-2, -4, -6)) + + test "Subtract vector from point": + let t1 = point(3, 2, 1) + let t2 = vector(5, 6, 7) + check(t1 - t2 == point(-2, -4, -6)) + + test "Vector subtraction": + let t1 = vector(3, 2, 1) + let t2 = vector(5, 6, 7) + check(t1 - t2 == vector(-2, -4, -6)) + + test "Negation": + let t1 = Tuple4(x: 1, y: -2, z: 3, w: -4) + check(-t1 == Tuple4(x: -1, y: 2, z: -3, w: 4)) + + test "Multiply tuple by scalar": + let t1 = Tuple4(x: 1, y: -2, z: 3, w: -4) + check(t1 * 3.5 == Tuple4(x: 3.5, y: -7, z: 10.5, w: -14)) + + test "Multiple tuple by fraction": + let t1 = Tuple4(x: 1, y: -2, z: 3, w: -4) + check(t1 * 0.5 == Tuple4(x: 0.5, y: -1, z: 1.5, w: -2)) + + test "Divide tuple by scalar": + let t1 = Tuple4(x: 1, y: -2, z: 3, w: -4) + check(t1 / 2 == Tuple4(x: 0.5, y: -1, z: 1.5, w: -2)) + + test "Magnitude of vector(1, 0, 0)": + check(vector(1, 0, 0).magnitude() == 1) + + test "Magnitude of vector(0, 1, 0)": + check(vector(0, 1, 0).magnitude() == 1) + + test "Magnitude of vector(0, 0, 1)": + check(vector(0, 0, 1).magnitude() == 1) + + test "Magnitude of vector(1, 2, 3)": + check(vector(1, 2, 3).magnitude() == sqrt(14.0)) + + test "Magnitude of vector(-1, -2, -3)": + check(vector(-1, -2, -3).magnitude() == sqrt(14.0)) + + test "Normalize vector(4, 0, 0)": + check(vector(4, 0, 0).normalize() == vector(1, 0, 0)) + + test "Normalize vector(1, 2, 3)": + check(vector(1, 2, 3).normalize() == vector(1/sqrt(14.0), 2/sqrt(14.0), 3/sqrt(14.0))) + + test "Magnitude of normalized vector": + check(vector(1, 2, 3).normalize().magnitude() == 1) + + test "Dot product": + check(vector(1, 2, 3).dot(vector(2, 3, 4)) == 20) + + test "Vector cross product": + let t1 = vector(1, 2, 3) + let t2 = vector(2, 3, 4) + check(t1.cross(t2) == vector(-1, 2, -1)) + check(t2.cross(t1) == vector(1, -2, 1)) \ No newline at end of file