From 1c2887e809932d5d2eeb46e713487a3d1d5604c6 Mon Sep 17 00:00:00 2001 From: neviyn Date: Sun, 8 Nov 2020 21:45:23 +0000 Subject: [PATCH] Added transformation operations --- src/transformations.nim | 129 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/transformations.nim diff --git a/src/transformations.nim b/src/transformations.nim new file mode 100644 index 0000000..259d2ff --- /dev/null +++ b/src/transformations.nim @@ -0,0 +1,129 @@ +import "./tuple" +import "./matrix" +import math + +proc translation*(x, y, z: float): Matrix = matrix(@[1.0, 0.0, 0.0, x, 0.0, 1.0, 0.0, y, 0.0, 0.0, 1.0, z, 0.0, 0.0, 0.0, 1.0], 4, 4) + +proc scaling*(x, y, z: float): Matrix = matrix(@[x, 0.0, 0.0, 0.0, 0.0, y, 0.0, 0.0, 0.0, 0.0, z, 0.0, 0.0, 0.0, 0.0, 1.0], 4, 4) + +proc rotationX*(radians: float): Matrix = matrix(@[1.0, 0.0, 0.0, 0.0, 0.0, cos(radians), -sin(radians), 0.0, 0.0, sin(radians), cos(radians), 0.0, 0.0, 0.0, 0.0, 1.0], 4, 4) + +proc rotationY*(radians: float): Matrix = matrix(@[cos(radians), 0.0, sin(radians), 0.0, 0.0, 1.0, 0.0, 0.0, -sin(radians), 0.0, cos(radians), 0.0, 0.0, 0.0, 0.0, 1.0], 4, 4) + +proc rotationZ*(radians: float): Matrix = matrix(@[cos(radians), -sin(radians), 0.0, 0.0, sin(radians), cos(radians), 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0], 4, 4) + +proc shearing*(x_y, x_z, y_x, y_z, z_x, z_y: float): Matrix = matrix(@[1.0, x_y, x_z, 0.0, y_x, 1.0, y_z, 0.0, z_x, z_y, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0], 4, 4) + +when isMainModule: + import unittest + suite "transformations": + test "Multiplying by a translation matrix": + let transform = translation(5.0, -3.0, 2.0) + let p = point(-3.0, 4.0, 5.0) + check(transform * p == point(2.0, 1.0, 7.0)) + + test "Multiplying by the inverse of a translation matrix": + let transform = translation(5.0, -3.0, 2.0).inverse() + let p = point(-3.0, 4.0, 5.0) + check(transform * p == point(-8.0, 7.0, 3.0)) + + test "Translation does not affect vectors": + let transform = translation(5.0, -3.0, 2.0) + let v = vector(-3.0, 4.0, 5.0) + check(transform * v == v) + + test "Scaling matrix applied to a point": + let transform = scaling(2.0, 3.0, 4.0) + let p = point(-4.0, 6.0, 8.0) + check(transform * p == point(-8.0, 18.0, 32.0)) + + test "Scaling matrix applied to a vector": + let transform = scaling(2.0, 3.0, 4.0) + let v = vector(-4.0, 6.0, 8.0) + check(transform * v == vector(-8.0, 18.0, 32.0)) + + test "Multiplying by the inverse of a scaling matrix": + let transform = scaling(2.0, 3.0, 4.0).inverse() + let v = vector(-4.0, 6.0, 8.0) + check(transform * v == vector(-2.0, 2.0, 2.0)) + + test "Reflection is scaling by a negative value": + let transform = scaling(-1.0, 1.0, 1.0) + let p = point(2.0, 3.0, 4.0) + check(transform * p == point(-2.0, 3.0, 4.0)) + + test "Rotation around the X axis": + let p = point(0.0, 1.0, 0.0) + let halfQuarter = rotationX(PI / 4) + let fullQuarter = rotationX(PI / 2) + check(halfQuarter * p == point(0.0, sqrt(2.0)/2.0, sqrt(2.0)/2.0)) + check(fullQuarter * p == point(0.0, 0.0, 1.0)) + + test "Inverse of a rotation rotates in the opposite direction": + let p = point(0.0, 1.0, 0.0) + let halfQuarter = rotationX(PI / 4).inverse() + check(halfQuarter * p == point(0.0, sqrt(2.0)/2.0, -sqrt(2.0)/2.0)) + + test "Rotation around the Y axis": + let p = point(0.0, 0.0, 1.0) + let halfQuarter = rotationY(PI / 4) + let fullQuarter = rotationY(PI / 2) + check(halfQuarter * p == point(sqrt(2.0)/2.0, 0.0, sqrt(2.0)/2.0)) + check(fullQuarter * p == point(1.0, 0.0, 0.0)) + + test "Rotation around the Z axis": + let p = point(0.0, 1.0, 0.0) + let halfQuarter = rotationZ(PI / 4) + let fullQuarter = rotationZ(PI / 2) + check(halfQuarter * p == point(-sqrt(2.0)/2.0, sqrt(2.0)/2.0, 0.0)) + check(fullQuarter * p == point(-1.0, 0.0, 0.0)) + + test "Shearing transformation moves X in proportion to Y": + let t = shearing(1.0, 0.0, 0.0, 0.0, 0.0, 0.0) + let p = point(2.0, 3.0, 4.0) + check(t * p == point(5.0, 3.0, 4.0)) + + test "Shearing transformation moves X in proportion to Z": + let t = shearing(0.0, 1.0, 0.0, 0.0, 0.0, 0.0) + let p = point(2.0, 3.0, 4.0) + check(t * p == point(6.0, 3.0, 4.0)) + + test "Shearing transformation moves Y in proportion to X": + let t = shearing(0.0, 0.0, 1.0, 0.0, 0.0, 0.0) + let p = point(2.0, 3.0, 4.0) + check(t * p == point(2.0, 5.0, 4.0)) + + test "Shearing transformation moves Y in proportion to Z": + let t = shearing(0.0, 0.0, 0.0, 1.0, 0.0, 0.0) + let p = point(2.0, 3.0, 4.0) + check(t * p == point(2.0, 7.0, 4.0)) + + test "Shearing transformation moves Z in proportion to X": + let t = shearing(0.0, 0.0, 0.0, 0.0, 1.0, 0.0) + let p = point(2.0, 3.0, 4.0) + check(t * p == point(2.0, 3.0, 6.0)) + + test "Shearing transformation moves Z in proportion to Y": + let t = shearing(0.0, 0.0, 0.0, 0.0, 0.0, 1.0) + let p = point(2.0, 3.0, 4.0) + check(t * p == point(2.0, 3.0, 7.0)) + + test "Individual transformations applied in sequence": + let p = point(1.0, 0.0, 1.0) + let r = rotationX(PI / 2) + let s = scaling(5.0, 5.0, 5.0) + let t = translation(10.0, 5.0, 7.0) + let p2 = r * p + check(p2 == point(1.0, -1.0, 0.0)) + let p3 = s * p2 + check(p3 == point(5.0, -5.0, 0.0)) + let p4 = t * p3 + check(p4 == point(15.0, 0.0, 7.0)) + + test "Chained transformations are applied in reverse order": + let p = point(1.0, 0.0, 1.0) + let r = rotationX(PI / 2) + let s = scaling(5.0, 5.0, 5.0) + let t = translation(10.0, 5.0, 7.0) + let combo = t * s * r + check(combo * p == point(15.0, 0.0, 7.0)) \ No newline at end of file