blob: a5061a1a114bacc54391f846c8733368ccbd98bf [file] [log] [blame]
/*
* Copyright 2022 Rive
*/
#include <rive/math/contour_measure.hpp>
#include <rive/math/math_types.hpp>
#include <rive/math/raw_path.hpp>
#include <rive/math/vec2d.hpp>
#include <catch.hpp>
#include <cstdio>
using namespace rive;
static bool nearly_eq(float a, float b, float tolerance) {
assert(tolerance >= 0);
const float diff = std::abs(a - b);
const float max = std::max(std::abs(a), std::abs(b));
const float allowed = tolerance * max;
if (diff > allowed) {
printf("%g %g delta %g allowed %g\n", a, b, diff, allowed);
return false;
}
return true;
}
static bool nearly_eq(Vec2D a, Vec2D b, float tol) {
return nearly_eq(a.x, b.x, tol) && nearly_eq(a.y, b.y, tol);
}
TEST_CASE("contour-basics", "[contourmeasure]") {
const float tol = 0.000001f;
RawPath path;
ContourMeasureIter iter(path, false);
REQUIRE(iter.next() == nullptr);
path.moveTo(1, 2);
iter.reset(path, false);
REQUIRE(iter.next() == nullptr);
path.lineTo(4, 6);
iter.reset(path, false);
auto cm = iter.next();
REQUIRE(cm);
REQUIRE(nearly_eq(cm->length(), 5, tol));
REQUIRE(iter.next() == nullptr);
// check the mid-points of a rectangle
path = RawPath();
const float w = 4, h = 6;
path.addRect({0, 0, w, h}, PathDirection::cw);
iter.reset(path, false);
cm = iter.next();
REQUIRE(cm);
REQUIRE(nearly_eq(cm->length(), 2 * (w + h), tol));
const float midDistances[] = {
w / 2,
w + h / 2,
w + h + w / 2,
w + h + w + h / 2,
};
const ContourMeasure::PosTan midPoints[] = {
{{w / 2, 0}, {1, 0}},
{{w, h / 2}, {0, 1}},
{{w / 2, h}, {-1, 0}},
{{0, h / 2}, {0, -1}},
};
for (int i = 0; i < 4; ++i) {
auto rec = cm->getPosTan(midDistances[i]);
REQUIRE(nearly_eq(rec.pos, midPoints[i].pos, tol));
REQUIRE(nearly_eq(rec.tan, midPoints[i].tan, tol));
}
REQUIRE(iter.next() == nullptr);
}
TEST_CASE("multi-contours", "[contourmeasure]") {
const Vec2D pts[] = {
{0, 0},
{3, 0},
{3, 4},
};
auto span = Span(pts, sizeof(pts) / sizeof(pts[0]));
// We expect 3 measurable contours out of this: 7, 16, 7
// the others should be skipped since they are empty (len == 0)
RawPath path;
path.addPoly(span, false); // len == 7
path.addPoly(span, true); // len == 12
// should be skipped (lengh == 0)
path.moveTo(0, 0);
// should be skipped (lengh == 0)
path.moveTo(0, 0);
path.close();
// should be skipped (lengh == 0)
path.moveTo(0, 0);
path.lineTo(0, 0);
// should be skipped (lengh == 0)
path.moveTo(0, 0);
path.lineTo(0, 0);
path.close();
path.addPoly(span, false); // len == 7
ContourMeasureIter iter(path, false);
auto cm = iter.next();
REQUIRE(cm->length() == 7);
cm = iter.next();
REQUIRE(cm->length() == 12);
cm = iter.next();
REQUIRE(cm->length() == 7);
cm = iter.next();
REQUIRE(!cm);
}
TEST_CASE("contour-oval", "[contourmeasure]") {
const float tol = 0.0075f;
const float r = 10;
RawPath path;
path.addOval({-r, -r, r, r}, PathDirection::cw);
ContourMeasureIter iter(path, false);
auto cm = iter.next();
REQUIRE(nearly_eq(cm->length(), 2 * r * math::PI, tol));
REQUIRE(!iter.next());
}