From efbc4a49ce88eb313e6220ad7d16c035f8875801 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 20 Feb 2023 22:59:23 -0800 Subject: [PATCH] Util: Add some basic geometry math --- include/mgba-util/geometry.h | 7 +- src/util/CMakeLists.txt | 2 + src/util/geometry.c | 36 ++++++ src/util/test/geometry.c | 213 +++++++++++++++++++++++++++++++++++ 4 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 src/util/geometry.c create mode 100644 src/util/test/geometry.c diff --git a/include/mgba-util/geometry.h b/include/mgba-util/geometry.h index a98693006..4ed46f0b0 100644 --- a/include/mgba-util/geometry.h +++ b/include/mgba-util/geometry.h @@ -13,10 +13,13 @@ CXX_GUARD_START struct Rectangle { int x; int y; - unsigned width; - unsigned height; + int width; + int height; }; +void RectangleUnion(struct Rectangle* dst, const struct Rectangle* add); +void RectangleCenter(const struct Rectangle* ref, struct Rectangle* rect); + CXX_GUARD_END #endif diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 9246b20c4..e0aa3f8a4 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -16,6 +16,7 @@ set(SOURCE_FILES convolve.c elf-read.c export.c + geometry.c patch.c patch-fast.c patch-ips.c @@ -33,6 +34,7 @@ set(GUI_FILES gui/menu.c) set(TEST_FILES + test/geometry.c test/sfo.c test/string-parser.c test/string-utf8.c diff --git a/src/util/geometry.c b/src/util/geometry.c new file mode 100644 index 000000000..92eb92b9a --- /dev/null +++ b/src/util/geometry.c @@ -0,0 +1,36 @@ +/* Copyright (c) 2013-2023 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include + +void RectangleUnion(struct Rectangle* dst, const struct Rectangle* add) { + int x0 = dst->x; + int y0 = dst->y; + int x1 = dst->x + dst->width; + int y1 = dst->y + dst->height; + + if (add->x < x0) { + x0 = add->x; + } + if (add->y < y0) { + y0 = add->y; + } + if (add->x + add->width > x1) { + x1 = add->x + add->width; + } + if (add->y + add->height > y1) { + y1 = add->y + add->height; + } + + dst->x = x0; + dst->y = y0; + dst->width = x1 - x0; + dst->height = y1 - y0; +} + +void RectangleCenter(const struct Rectangle* ref, struct Rectangle* rect) { + rect->x = ref->x + (ref->width - rect->width) / 2; + rect->y = ref->y + (ref->height - rect->height) / 2; +} diff --git a/src/util/test/geometry.c b/src/util/test/geometry.c new file mode 100644 index 000000000..74171e0f7 --- /dev/null +++ b/src/util/test/geometry.c @@ -0,0 +1,213 @@ +/* Copyright (c) 2013-2023 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "util/test/suite.h" + +#include + +M_TEST_DEFINE(unionRectOrigin) { + struct Rectangle a = { + .x = 0, + .y = 0, + .width = 1, + .height = 1 + }; + struct Rectangle b = { + .x = 1, + .y = 1, + .width = 1, + .height = 1 + }; + RectangleUnion(&a, &b); + assert_int_equal(a.x, 0); + assert_int_equal(a.y, 0); + assert_int_equal(a.width, 2); + assert_int_equal(a.height, 2); +} + +M_TEST_DEFINE(unionRectOriginSwapped) { + struct Rectangle a = { + .x = 1, + .y = 1, + .width = 1, + .height = 1 + }; + struct Rectangle b = { + .x = 0, + .y = 0, + .width = 1, + .height = 1 + }; + RectangleUnion(&a, &b); + assert_int_equal(a.x, 0); + assert_int_equal(a.y, 0); + assert_int_equal(a.width, 2); + assert_int_equal(a.height, 2); +} + +M_TEST_DEFINE(unionRectNonOrigin) { + struct Rectangle a = { + .x = 1, + .y = 1, + .width = 1, + .height = 1 + }; + struct Rectangle b = { + .x = 2, + .y = 2, + .width = 1, + .height = 1 + }; + RectangleUnion(&a, &b); + assert_int_equal(a.x, 1); + assert_int_equal(a.y, 1); + assert_int_equal(a.width, 2); + assert_int_equal(a.height, 2); +} + +M_TEST_DEFINE(unionRectOverlapping) { + struct Rectangle a = { + .x = 0, + .y = 0, + .width = 2, + .height = 2 + }; + struct Rectangle b = { + .x = 1, + .y = 1, + .width = 2, + .height = 2 + }; + RectangleUnion(&a, &b); + assert_int_equal(a.x, 0); + assert_int_equal(a.y, 0); + assert_int_equal(a.width, 3); + assert_int_equal(a.height, 3); +} + +M_TEST_DEFINE(unionRectSubRect) { + struct Rectangle a = { + .x = 0, + .y = 0, + .width = 3, + .height = 3 + }; + struct Rectangle b = { + .x = 1, + .y = 1, + .width = 1, + .height = 1 + }; + RectangleUnion(&a, &b); + assert_int_equal(a.x, 0); + assert_int_equal(a.y, 0); + assert_int_equal(a.width, 3); + assert_int_equal(a.height, 3); +} + +M_TEST_DEFINE(unionRectNegativeOrigin) { + struct Rectangle a = { + .x = -1, + .y = -1, + .width = 1, + .height = 1 + }; + struct Rectangle b = { + .x = 0, + .y = 0, + .width = 1, + .height = 1 + }; + RectangleUnion(&a, &b); + assert_int_equal(a.x, -1); + assert_int_equal(a.y, -1); + assert_int_equal(a.width, 2); + assert_int_equal(a.height, 2); +} + +M_TEST_DEFINE(centerRectBasic) { + struct Rectangle ref = { + .x = 0, + .y = 0, + .width = 4, + .height = 4 + }; + struct Rectangle a = { + .x = 0, + .y = 0, + .width = 2, + .height = 2 + }; + RectangleCenter(&ref, &a); + assert_int_equal(a.x, 1); + assert_int_equal(a.y, 1); +} + +M_TEST_DEFINE(centerRectRoundDown) { + struct Rectangle ref = { + .x = 0, + .y = 0, + .width = 4, + .height = 4 + }; + struct Rectangle a = { + .x = 0, + .y = 0, + .width = 1, + .height = 1 + }; + RectangleCenter(&ref, &a); + assert_int_equal(a.x, 1); + assert_int_equal(a.y, 1); +} + +M_TEST_DEFINE(centerRectRoundDown2) { + struct Rectangle ref = { + .x = 0, + .y = 0, + .width = 4, + .height = 4 + }; + struct Rectangle a = { + .x = 0, + .y = 0, + .width = 3, + .height = 2 + }; + RectangleCenter(&ref, &a); + assert_int_equal(a.x, 0); + assert_int_equal(a.y, 1); +} + +M_TEST_DEFINE(centerRectOffset) { + struct Rectangle ref = { + .x = 1, + .y = 1, + .width = 4, + .height = 4 + }; + struct Rectangle a = { + .x = 0, + .y = 0, + .width = 2, + .height = 2 + }; + RectangleCenter(&ref, &a); + assert_int_equal(a.x, 2); + assert_int_equal(a.y, 2); +} + +M_TEST_SUITE_DEFINE(Geometry, + cmocka_unit_test(unionRectOrigin), + cmocka_unit_test(unionRectOriginSwapped), + cmocka_unit_test(unionRectNonOrigin), + cmocka_unit_test(unionRectOverlapping), + cmocka_unit_test(unionRectSubRect), + cmocka_unit_test(unionRectNegativeOrigin), + cmocka_unit_test(centerRectBasic), + cmocka_unit_test(centerRectRoundDown), + cmocka_unit_test(centerRectRoundDown2), + cmocka_unit_test(centerRectOffset), +)