| /* |
| * 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); |
| } |