From 18456541aaf6e6447af2192c8456fbffce6f5977 Mon Sep 17 00:00:00 2001 From: Simon Bruder Date: Sat, 6 May 2023 16:37:48 +0200 Subject: [PATCH] u02: Implement transformation functions --- u02/include/util.h | 27 +++++++++++++++++ u02/src/tests.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++++++ u02/src/util.cpp | 49 +++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 u02/include/util.h create mode 100644 u02/src/tests.cpp create mode 100644 u02/src/util.cpp diff --git a/u02/include/util.h b/u02/include/util.h new file mode 100644 index 0000000..af19c3d --- /dev/null +++ b/u02/include/util.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +#pragma once +#include +#include + +typedef uint8_t Transformation; + +const Transformation TRANSFORM_MIRROR_X = 1 << 0; +const Transformation TRANSFORM_MIRROR_Y = 1 << 1; +const Transformation TRANSFORM_ROTATE_CW = 1 << 2; +const Transformation TRANSFORM_ROTATE_CCW = 1 << 3; + +// Applies the provided transformation to the point, mutating it in place. +// Rotation is done before mirroring. +void transform_mut(Transformation transformation, int &x, int &y); +// Applies the provided transformation to the point, +// returning the transformed point. +// Rotation is done before mirroring. +std::pair transform(Transformation transformation, int x, int y); + +// Applies the inverse transformation to the point, mutating it in place. +// Composition of this with the transformation is the identity function. +void transform_inv_mut(Transformation transformation, int &x, int &y); +// Applies the inverse transformation to the point, +// returning the transformed point. +// Composition of this with the transformation is the identity function. +std::pair transform_inv(Transformation transformation, int x, int y); diff --git a/u02/src/tests.cpp b/u02/src/tests.cpp new file mode 100644 index 0000000..aafc054 --- /dev/null +++ b/u02/src/tests.cpp @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +#include +#include +#include +#include +#include + +#include "util.h" + +TEST_CASE("Transform Mirror") { + // elementary operations + REQUIRE(transform(TRANSFORM_MIRROR_X, 10, 20) == std::make_pair(10, -20)); + REQUIRE(transform(TRANSFORM_MIRROR_Y, 10, 20) == std::make_pair(-10, 20)); + + // composite operations + REQUIRE(transform(TRANSFORM_MIRROR_X | TRANSFORM_MIRROR_Y, 10, 20) == + std::make_pair(-10, -20)); +} + +TEST_CASE("Transform Rotate") { + REQUIRE(transform(TRANSFORM_ROTATE_CW, 10, 20) == + std::make_pair(-20, 10)); // 4th quadrant + REQUIRE(transform(TRANSFORM_ROTATE_CW, -20, 10) == + std::make_pair(-10, -20)); // 3rd quadrant + REQUIRE(transform(TRANSFORM_ROTATE_CW, -10, -20) == + std::make_pair(20, -10)); // 2nd quadrant + REQUIRE(transform(TRANSFORM_ROTATE_CW, 20, -10) == + std::make_pair(10, 20)); // 1st quadrant + + REQUIRE(transform(TRANSFORM_ROTATE_CCW, 20, -10) == + std::make_pair(-10, -20)); // 1nd quadrant + REQUIRE(transform(TRANSFORM_ROTATE_CCW, -10, -20) == + std::make_pair(-20, 10)); // 2rd quadrant + REQUIRE(transform(TRANSFORM_ROTATE_CCW, -20, 10) == + std::make_pair(10, 20)); // 3th quadrant + REQUIRE(transform(TRANSFORM_ROTATE_CCW, 10, 20) == + std::make_pair(20, -10)); // 4st quadrant +} + +TEST_CASE("Transform Rotate + Mirror") { + REQUIRE(transform(TRANSFORM_ROTATE_CW | TRANSFORM_MIRROR_X, 10, 20) == + std::make_pair(-20, -10)); + REQUIRE(transform(TRANSFORM_ROTATE_CW | TRANSFORM_MIRROR_Y, 10, 20) == + std::make_pair(20, 10)); + REQUIRE( + transform(TRANSFORM_ROTATE_CW | TRANSFORM_MIRROR_X | TRANSFORM_MIRROR_Y, + 10, 20) == std::make_pair(20, -10)); + + REQUIRE(transform(TRANSFORM_ROTATE_CCW | TRANSFORM_MIRROR_X, 10, 20) == + std::make_pair(20, 10)); + REQUIRE(transform(TRANSFORM_ROTATE_CCW | TRANSFORM_MIRROR_Y, 10, 20) == + std::make_pair(-20, -10)); + REQUIRE( + transform(TRANSFORM_ROTATE_CCW | TRANSFORM_MIRROR_X | TRANSFORM_MIRROR_Y, + 10, 20) == std::make_pair(-20, 10)); +} + +TEST_CASE("Transform = Inverse Transform ○ Transform") { + int x = GENERATE(take(10, random(-100, 100))); + int y = GENERATE(take(10, random(-100, 100))); + // this iterates over all possible transformations, + // even bogus ones (like rotating cw and ccw) + for (Transformation transformation = 0; transformation < 0b10000; + transformation++) { + int xt, yt; + std::tie(xt, yt) = transform(transformation, x, y); + int xti, yti; + std::tie(xti, yti) = transform_inv(transformation, xt, yt); + REQUIRE(x == xti); + REQUIRE(y == yti); + } +} diff --git a/u02/src/util.cpp b/u02/src/util.cpp new file mode 100644 index 0000000..234fffc --- /dev/null +++ b/u02/src/util.cpp @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "util.h" +#include + +void transform_mut(Transformation transformation, int &x, int &y) { + if ((transformation & TRANSFORM_ROTATE_CW) != 0) { + std::swap(x, y); + x = -x; + } + if ((transformation & TRANSFORM_ROTATE_CCW) != 0) { + std::swap(x, y); + y = -y; + } + if ((transformation & TRANSFORM_MIRROR_X) != 0) { + y = -y; + } + if ((transformation & TRANSFORM_MIRROR_Y) != 0) { + x = -x; + } +} + +std::pair transform(Transformation transformation, int x, int y) { + transform_mut(transformation, x, y); + return std::make_pair(x, y); +} + +void transform_inv_mut(Transformation transformation, int &x, int &y) { + if ((transformation & TRANSFORM_MIRROR_Y) != 0) { + x = -x; + } + if ((transformation & TRANSFORM_MIRROR_X) != 0) { + y = -y; + } + if (transformation & TRANSFORM_ROTATE_CCW) { + // does clockwise rotation + std::swap(x, y); + x = -x; + } + if (transformation & TRANSFORM_ROTATE_CW) { + // does counterclockwise rotation + std::swap(x, y); + y = -y; + } +} + +std::pair transform_inv(Transformation transformation, int x, int y) { + transform_inv_mut(transformation, x, y); + return std::make_pair(x, y); +}