diff --git a/kandinsky/include/kandinsky/rect.h b/kandinsky/include/kandinsky/rect.h index b879a98f9..e5573aff8 100644 --- a/kandinsky/include/kandinsky/rect.h +++ b/kandinsky/include/kandinsky/rect.h @@ -27,6 +27,9 @@ extern KDRect KDRectZero; bool KDRectIntersect(KDRect r1, KDRect r2); KDRect KDRectIntersection(KDRect r1, KDRect r2); +// Returns the smallest rectangle containing r1 and r2 +KDRect KDRectUnion(KDRect r1, KDRect r2); + bool KDRectContains(KDRect r, KDPoint p); KDRect KDRectTranslate(KDRect r, KDPoint p); diff --git a/kandinsky/src/rect.c b/kandinsky/src/rect.c index d313c2b3a..b0d803a23 100644 --- a/kandinsky/src/rect.c +++ b/kandinsky/src/rect.c @@ -42,6 +42,54 @@ KDRect KDRectIntersection(KDRect r1, KDRect r2) { return intersection; } +static void KDRectComputeUnionBound(KDCoordinate size1, KDCoordinate size2, + KDCoordinate * outputMin, KDCoordinate * outputMax, + KDCoordinate min1, KDCoordinate min2, + KDCoordinate max1, KDCoordinate max2) +{ + if (size1 != 0) { + if (size2 != 0) { + *outputMin = min(min1, min2); + *outputMax = max(max1, max2); + } else { + *outputMin = min1; + *outputMax = max1; + } + } else { + if (size2 != 0) { + *outputMin = min2; + *outputMax = max2; + } + } +} + +KDRect KDRectUnion(KDRect r1, KDRect r2) { + /* We should ignore coordinate whose size is zero + * For example, if r1.height is zero, just ignore r1.y and r1.height. */ + + KDCoordinate resultLeft = 0; + KDCoordinate resultTop = 0; + KDCoordinate resultRight = 0; + KDCoordinate resultBottom = 0; + + KDRectComputeUnionBound(r1.width, r2.width, + &resultLeft, &resultRight, + left(r1), left(r2), + right(r1), right(r2)); + + KDRectComputeUnionBound(r1.height, r2.height, + &resultTop, &resultBottom, + top(r1), top(r2), + bottom(r1), bottom(r2)); + + return (KDRect){ + .x = resultLeft, + .y = resultTop, + .width = resultRight-resultLeft, + .height = resultBottom-resultTop + }; +} + bool KDRectContains(KDRect r, KDPoint p) { return (p.x >= r.x && p.x < (r.x+r.width) && p.y >= r.y && p.y < (r.y+r.height)); } diff --git a/kandinsky/test/rect.c b/kandinsky/test/rect.c index ce4ad9c17..c46f69a0e 100644 --- a/kandinsky/test/rect.c +++ b/kandinsky/test/rect.c @@ -24,3 +24,59 @@ QUIZ_CASE(kandinsky_rect_intersect) { assert(c.width == 5); assert(c.height == 5); } + +QUIZ_CASE(kandinsky_rect_union) { + KDRect a = { + .x = -5, .y = -5, + .width = 10, .height = 10 + }; + KDRect b = { + .x = 0, .y = 0, + .width = 10, .height = 10 + }; + + KDRect c = KDRectUnion(a,b); + assert(c.x == -5); + assert(c.y == -5); + assert(c.width == 15); + assert(c.height == 15); + + c = KDRectUnion(a,b); + assert(c.x == -5); + assert(c.y == -5); + assert(c.width == 15); + assert(c.height == 15); +} + +QUIZ_CASE(kandinsky_rect_empty_union) { + KDRect a = { + .x = 1, .y = 2, + .width = 3, .height = 4 + }; + KDRect b = { + .x = 5, .y = 6, + .width = 0, .height = 0 + }; + KDRect c = { + .x = -2, .y = -1, + .width = 0, .height = 1 + }; + + KDRect t = KDRectUnion(a,b); + assert(t.x == a.x); + assert(t.y == a.y); + assert(t.width == a.width); + assert(t.height == a.height); + + t = KDRectUnion(b,a); + assert(t.x == a.x); + assert(t.y == a.y); + assert(t.width == a.width); + assert(t.height == a.height); + + t = KDRectUnion(a,c); + assert(t.x == a.x); + assert(t.y == c.y); + assert(t.width == a.width); + assert(t.height == 7); +}