blob: 8b34f6648ac34d698dc31e2ed2d88335b10d33cb [file] [log] [blame]
/*
* Copyright 2022 Rive
*/
#include <rive/math/aabb.hpp>
#include <rive/math/raw_path.hpp>
#include <catch.hpp>
#include <cstdio>
using namespace rive;
TEST_CASE("rawpath-basics", "[rawpath]") {
RawPath path;
REQUIRE(path.empty());
REQUIRE(path.bounds() == AABB{0, 0, 0, 0});
path.move({1, 2});
REQUIRE(!path.empty());
REQUIRE(path.bounds() == AABB{1, 2, 1, 2});
path = RawPath();
REQUIRE(path.empty());
REQUIRE(path.bounds() == AABB{0, 0, 0, 0});
path.move({1, -2});
path.line({3, 4});
path.line({-1, 5});
REQUIRE(!path.empty());
REQUIRE(path.bounds() == AABB{-1, -2, 3, 5});
}
TEST_CASE("rawpath-add-helpers", "[rawpath]") {
RawPath path;
path.addRect({1, 1, 5, 6});
REQUIRE(!path.empty());
REQUIRE(path.bounds() == AABB{1, 1, 5, 6});
REQUIRE(path.points().size() == 4);
REQUIRE(path.verbs().size() == 5); // move, line, line, line, close
path = RawPath();
path.addOval({0, 0, 3, 6});
REQUIRE(!path.empty());
REQUIRE(path.bounds() == AABB{0, 0, 3, 6});
REQUIRE(path.points().size() == 13);
REQUIRE(path.verbs().size() == 6); // move, cubic, cubic, cubic, cubic, close
const Vec2D pts[] = {
{1, 2},
{4, 5},
{3, 2},
{100, -100},
};
constexpr auto size = sizeof(pts) / sizeof(pts[0]);
for (auto isClosed : {false, true}) {
path = RawPath();
path.addPoly({pts, size}, isClosed);
REQUIRE(path.bounds() == AABB{1, -100, 100, 5});
REQUIRE(path.points().size() == size);
REQUIRE(path.verbs().size() == size + isClosed);
for (size_t i = 0; i < size; ++i) {
REQUIRE(path.points()[i] == pts[i]);
}
REQUIRE(path.verbs()[0] == PathVerb::move);
for (size_t i = 1; i < size; ++i) {
REQUIRE(path.verbs()[i] == PathVerb::line);
}
if (isClosed) {
REQUIRE(path.verbs()[size] == PathVerb::close);
}
}
}
//////////////////////////////////////////////////////////////////////////
static bool is_move(const RawPath::Iter::Rec& rec) {
if (rec.verb == PathVerb::move) {
REQUIRE(rec.count == 1);
return true;
}
return false;
}
static bool is_line(const RawPath::Iter::Rec& rec) {
if (rec.verb == PathVerb::line) {
REQUIRE(rec.count == 1);
return true;
}
return false;
}
static bool is_quad(const RawPath::Iter::Rec& rec) {
if (rec.verb == PathVerb::quad) {
REQUIRE(rec.count == 2);
return true;
}
return false;
}
static bool is_cubic(const RawPath::Iter::Rec& rec) {
if (rec.verb == PathVerb::cubic) {
REQUIRE(rec.count == 3);
return true;
}
return false;
}
static bool is_close(const RawPath::Iter::Rec& rec) {
if (rec.verb == PathVerb::close) {
REQUIRE(rec.count == 0);
return true;
}
return false;
}
// clang-format off
static inline bool eq(Vec2D p, float x, float y) {
return p.x == x && p.y == y;
}
// clang-format on
TEST_CASE("rawpath-iter", "[rawpath]") {
{
RawPath rp;
RawPath::Iter iter(rp);
REQUIRE(iter.next() == false);
REQUIRE(iter.next() == false); // should be safe to call again
}
{
RawPath rp;
rp.moveTo(1, 2);
rp.lineTo(3, 4);
rp.quadTo(5, 6, 7, 8);
rp.cubicTo(9, 10, 11, 12, 13, 14);
rp.close();
RawPath::Iter iter(rp);
auto rec = iter.next();
REQUIRE((rec && is_move(rec) && eq(rec.pts[0], 1, 2)));
rec = iter.next();
REQUIRE((rec && is_line(rec) && eq(rec.pts[0], 3, 4)));
rec = iter.next();
REQUIRE((rec && is_quad(rec) && eq(rec.pts[0], 5, 6) && eq(rec.pts[1], 7, 8)));
rec = iter.next();
REQUIRE((rec && is_cubic(rec) && eq(rec.pts[0], 9, 10) && eq(rec.pts[1], 11, 12) &&
eq(rec.pts[2], 13, 14)));
rec = iter.next();
REQUIRE((rec && is_close(rec)));
rec = iter.next();
REQUIRE(rec == false);
REQUIRE(iter.next() == false); // should be safe to call again
}
}
TEST_CASE("isDone", "[rawpath::iter]") {
RawPath rp;
rp.moveTo(1, 2);
rp.lineTo(3, 4);
RawPath::Iter iter(rp);
REQUIRE(!iter.isDone()); // moveTo
REQUIRE(iter.next());
REQUIRE(!iter.isDone()); // lineTo
REQUIRE(iter.next());
REQUIRE(iter.isDone()); // now we're done
REQUIRE(!iter.next());
REQUIRE(iter.isDone()); // ensure we 'still' think we're done
}
TEST_CASE("reset", "[rawpath]") {
RawPath path;
path.moveTo(1, 2);
path.lineTo(3, 4);
RawPath::Iter iter(path);
auto rec = iter.next();
REQUIRE((rec && is_move(rec) && eq(rec.pts[0], 1, 2)));
rec = iter.next();
REQUIRE((rec && is_line(rec) && eq(rec.pts[0], 3, 4)));
REQUIRE(!iter.next());
// now change the path (not required for the test per-se)
path = RawPath();
path.moveTo(0, 0);
path.close();
iter.reset(path);
rec = iter.next();
REQUIRE((rec && is_move(rec) && eq(rec.pts[0], 0, 0)));
rec = iter.next();
REQUIRE((rec && is_close(rec)));
REQUIRE(!iter.next());
}
TEST_CASE("backup", "[rawpath]") {
RawPath rp;
rp.moveTo(1, 2);
rp.lineTo(3, 4);
rp.close();
RawPath::Iter iter(rp);
auto rec = iter.next();
REQUIRE((rec && is_move(rec) && eq(rec.pts[0], 1, 2)));
const Vec2D* move_pts = rec.pts;
rec = iter.next();
REQUIRE((rec && is_line(rec) && eq(rec.pts[0], 3, 4)));
const Vec2D* line_pts = rec.pts;
rec = iter.next();
REQUIRE((rec && is_close(rec)));
rec = iter.next();
REQUIRE(!rec);
// Now try backing up
iter.backUp(); // go back to 'close'
rec = iter.next();
REQUIRE((rec && is_close(rec)));
iter.backUp(); // go back to 'close'
iter.backUp(); // go back to 'line'
rec = iter.next();
REQUIRE((rec && is_line(rec) && eq(rec.pts[0], 3, 4)));
REQUIRE(rec.pts == line_pts);
iter.backUp(); // go back to 'line'
iter.backUp(); // go back to 'move'
rec = iter.next();
REQUIRE((rec && is_move(rec) && eq(rec.pts[0], 1, 2)));
REQUIRE(rec.pts == move_pts);
}