region: add rotate method

This commit is contained in:
Danny Robson 2020-08-17 11:49:15 +10:00
parent 81a7b83dfe
commit bf3ae14f25
2 changed files with 115 additions and 6 deletions

View File

@ -201,6 +201,25 @@ namespace cruft {
};
///////////////////////////////////////////////////////////////////////////
template <typename T> using region2 = region<2,T>;
template <typename T> using region3 = region<3,T>;
//-------------------------------------------------------------------------
using region2u = region2<unsigned>;
using region2i = region2<int>;
using region2f = region2<float>;
using region2d = region2<double>;
//-------------------------------------------------------------------------
extern template struct region<2, unsigned>;
extern template struct region<2, int>;
extern template struct region<2, float>;
extern template struct region<2, double>;
///////////////////////////////////////////////////////////////////////////
/// constructs the minimal region that encompasses a region and a point.
template <typename T, size_t S>
@ -324,14 +343,54 @@ namespace cruft {
///////////////////////////////////////////////////////////////////////////
template <typename T> using region2 = region<2,T>;
template <typename T> using region3 = region<3,T>;
/// Returns a region rotated clockwise about the base point in `steps`
/// multiples of 90 degrees.
///
/// `steps` must lie in the range [0, 4) so we can avoid an expensive
/// modulus in the typical case.
template <typename T>
cruft::region<2,T>
rotate (cruft::region<2,T> obj, int steps)
{
CHECK_LIMIT (steps, 0, 3);
switch (steps) {
case 0:
return obj;
using region2u = region2<unsigned>;
using region2i = region2<int>;
using region2f = region2<float>;
using region2d = region2<double>;
case 1:
return region2<T> {
point2<T> {
obj.p.x - obj.e.h,
obj.p.y
},
extent2<T> {
obj.e.h,
obj.e.w,
}
};
case 2:
return region2<T> {
point2<T> {
obj.p.x,
obj.p.y - obj.e.h,
},
obj.e,
};
case 3:
return region2<T> {
obj.p,
extent2<T> {
obj.e.h,
obj.e.w,
},
};
}
unreachable();
}
///////////////////////////////////////////////////////////////////////////

View File

@ -183,5 +183,55 @@ main (int, char **)
tap.expect_eq (intersection (a, b), res, "region-region interesection: %!", message);
}
// Test rotate behaves as expected.
//
// It's important that the following constraints apply to the test data:
// * no point lies at the origin because we want to catch
// rotate-about-point errors that need a translation.
// * the size is non-square so we can differentiate between rotated
// values
// * use floating point values so we can test for loss of precision.
{
cruft::point2f const base { 1, 2 };
cruft::extent2f const size { 3, 4 };
cruft::region2f const orig { base, size };
struct {
int rotation;
cruft::region2f res;
} const TESTS[] {
{
.rotation = 0,
.res = orig,
},
{
.rotation = 1,
.res = {
cruft::point2f { -3, 2 },
cruft::extent2f { 4, 3 },
},
},
{
.rotation = 2,
.res = {
cruft::point2f { 1, -2 },
cruft::extent2f { 3, 4 },
},
},
{
.rotation = 3,
.res = {
cruft::point2f { 1, 2 },
cruft::extent2f { 4, 3 },
},
}
};
for (auto const &obj: TESTS) {
auto const computed = rotate (orig, obj.rotation);
tap.expect_eq (computed, obj.res, "%! rotation", obj.rotation);
}
}
return tap.status ();
}