From efd9e47c1f2b033b45a2682c121fee5e2b884e9c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 22 Jul 2018 18:49:17 +0200 Subject: [PATCH] Attempt to fix a scanline issue. --- src/db/db/dbEdge.cc | 27 +++++++++++ src/db/db/dbEdge.h | 41 +++++++++++++--- src/db/unit_tests/dbEdge.cc | 71 ++++++++++++++++++++++++++++ src/db/unit_tests/dbEdgeProcessor.cc | 28 +++++++++++ 4 files changed, 161 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbEdge.cc b/src/db/db/dbEdge.cc index 07dd42ba0..5b3e1c7ae 100644 --- a/src/db/db/dbEdge.cc +++ b/src/db/db/dbEdge.cc @@ -23,6 +23,33 @@ #include "dbEdge.h" +namespace db +{ + +/** + * @brief Computes the gcd of two numbers + */ +template +inline C gcd (C a, C b) +{ + while (b != 0) { + a %= b; + std::swap (a, b); + } + return a; +} + +db::Coord div_exact (db::Coord a, db::coord_traits::area_type b, db::coord_traits::area_type d) +{ + if (a < 0) { + return -db::Coord ((__int128 (-a) * __int128 (b) + __int128 (d / 2)) / __int128 (d)); + } else { + return db::Coord ((__int128 (a) * __int128 (b) + __int128 ((d - 1) / 2)) / __int128 (d)); + } +} + +} + namespace tl { diff --git a/src/db/db/dbEdge.h b/src/db/db/dbEdge.h index 29b6bd477..15d0cbcdf 100644 --- a/src/db/db/dbEdge.h +++ b/src/db/db/dbEdge.h @@ -38,6 +38,25 @@ namespace db { +/** + * @brief A helper function for dividing integers with exact rounding + * This function computes (a*b/d) where rounding is exact in the sense of: + * a*b/d == N+0.5 => div_exact(a*b/d) = N + * b and d needs to be positive. + * a can be positive or negative. + * The implementation uses the gcd to reduce the ratios. This way we can + * represent the numbers with the area type. + */ +db::Coord DB_PUBLIC div_exact (db::Coord a, db::coord_traits::area_type b, db::coord_traits::area_type d); + +/** + * @brief An overload of div_exact for double types + */ +inline db::DCoord div_exact (db::DCoord a, db::coord_traits::area_type b, db::coord_traits::area_type d) +{ + return db::coord_traits::rounded (double (a) * double (b) / double (d)); +} + template class generic_repository; class ArrayRepository; @@ -794,10 +813,15 @@ public: } else if (res) { - double f = fabs (double (vxa)) / (fabs (double (vxa)) + fabs (double (vxb))); + if (vxa < 0) { + vxa = -vxa; + } + if (vxb < 0) { + vxb = -vxb; + } - coord_type x = m_p1.x () + coord_traits::rounded (dx () * f); - coord_type y = m_p1.y () + coord_traits::rounded (dy () * f); + coord_type x = m_p1.x () + div_exact (dx (), vxa, vxa + vxb); + coord_type y = m_p1.y () + div_exact (dy (), vxa, vxa + vxb); return std::make_pair (true, db::point (x, y)); @@ -1073,10 +1097,15 @@ public: if (res) { - double f = fabs (double (vxa)) / (fabs (double (vxa)) + fabs (double (vxb))); + if (vxa < 0) { + vxa = -vxa; + } + if (vxb < 0) { + vxb = -vxb; + } - coord_type x = e.p1 ().x () + coord_traits::rounded (e.dx () * f); - coord_type y = e.p1 ().y () + coord_traits::rounded (e.dy () * f); + coord_type x = e.p1 ().x () + div_exact (e.dx (), vxa, vxa + vxb); + coord_type y = e.p1 ().y () + div_exact (e.dy (), vxa, vxa + vxb); return std::make_pair (true, db::point (x, y)); diff --git a/src/db/unit_tests/dbEdge.cc b/src/db/unit_tests/dbEdge.cc index 91d5a77b3..44aa4822d 100644 --- a/src/db/unit_tests/dbEdge.cc +++ b/src/db/unit_tests/dbEdge.cc @@ -514,3 +514,74 @@ TEST(14) EXPECT_EQ (e.coincident (db::Edge (db::Point (49, 0), db::Point (200, 0))), false); } +// exact rounding behaviour +TEST(15) +{ + typedef db::coord_traits::area_type area_type; + // div_exact(a, b, d) computes a*b/d with exact rounding behaviour + EXPECT_EQ (db::div_exact (area_type (0), area_type (22), area_type (176)), 0); + EXPECT_EQ (db::div_exact (area_type (5), area_type (0), area_type (176)), 0); + + EXPECT_EQ (db::div_exact (area_type (3), area_type (22), area_type (176)), 0); + EXPECT_EQ (db::div_exact (area_type (4), area_type (22), area_type (176)), 0); + EXPECT_EQ (db::div_exact (area_type (5), area_type (22), area_type (176)), 1); + EXPECT_EQ (db::div_exact (area_type (7), area_type (22), area_type (176)), 1); + EXPECT_EQ (db::div_exact (area_type (8), area_type (22), area_type (176)), 1); + EXPECT_EQ (db::div_exact (area_type (12), area_type (22), area_type (176)), 1); + EXPECT_EQ (db::div_exact (area_type (13), area_type (22), area_type (176)), 2); + + EXPECT_EQ (db::div_exact (area_type (3 * 11), area_type (2), area_type (176)), 0); + EXPECT_EQ (db::div_exact (area_type (4 * 11), area_type (2), area_type (176)), 0); + EXPECT_EQ (db::div_exact (area_type (5 * 11), area_type (2), area_type (176)), 1); + EXPECT_EQ (db::div_exact (area_type (7 * 11), area_type (2), area_type (176)), 1); + EXPECT_EQ (db::div_exact (area_type (8 * 11), area_type (2), area_type (176)), 1); + EXPECT_EQ (db::div_exact (area_type (12 * 11), area_type (2), area_type (176)), 1); + EXPECT_EQ (db::div_exact (area_type (13 * 11), area_type (2), area_type (176)), 2); + + EXPECT_EQ (db::div_exact (area_type (-3), area_type (22), area_type (176)), 0); + EXPECT_EQ (db::div_exact (area_type (-4), area_type (22), area_type (176)), -1); + EXPECT_EQ (db::div_exact (area_type (-5), area_type (22), area_type (176)), -1); + EXPECT_EQ (db::div_exact (area_type (-7), area_type (22), area_type (176)), -1); + EXPECT_EQ (db::div_exact (area_type (-8), area_type (22), area_type (176)), -1); + EXPECT_EQ (db::div_exact (area_type (-12), area_type (22), area_type (176)), -2); + EXPECT_EQ (db::div_exact (area_type (-13), area_type (22), area_type (176)), -2); + + EXPECT_EQ (db::div_exact (area_type (-3 * 11), area_type (2), area_type (176)), 0); + EXPECT_EQ (db::div_exact (area_type (-4 * 11), area_type (2), area_type (176)), -1); + EXPECT_EQ (db::div_exact (area_type (-5 * 11), area_type (2), area_type (176)), -1); + EXPECT_EQ (db::div_exact (area_type (-7 * 11), area_type (2), area_type (176)), -1); + EXPECT_EQ (db::div_exact (area_type (-8 * 11), area_type (2), area_type (176)), -1); + EXPECT_EQ (db::div_exact (area_type (-12 * 11), area_type (2), area_type (176)), -2); + EXPECT_EQ (db::div_exact (area_type (-13 * 11), area_type (2), area_type (176)), -2); + + area_type f = 790014345; + + EXPECT_EQ (db::div_exact (area_type (4), area_type (22) * f, area_type (176) * f), 0); + EXPECT_EQ (db::div_exact (area_type (5), area_type (22) * f, area_type (176) * f), 1); + EXPECT_EQ (db::div_exact (area_type (8), area_type (22) * f, area_type (176) * f), 1); + + EXPECT_EQ (db::div_exact (area_type (-3), area_type (22) * f, area_type (176) * f), 0); + EXPECT_EQ (db::div_exact (area_type (-4), area_type (22) * f, area_type (176) * f), -1); + EXPECT_EQ (db::div_exact (area_type (-5), area_type (22) * f, area_type (176) * f), -1); + EXPECT_EQ (db::div_exact (area_type (-8), area_type (22) * f, area_type (176) * f), -1); + + EXPECT_EQ (db::div_exact (area_type (4) * 1000000000, area_type (22) * f, area_type (176) * f), 500000000); + EXPECT_EQ (db::div_exact (area_type (5) * 1000000000, area_type (22) * f, area_type (176) * f), 625000000); + EXPECT_EQ (db::div_exact (area_type (-4) * 1000000000, area_type (22) * f, area_type (176) * f), -500000000); + EXPECT_EQ (db::div_exact (area_type (-5) * 1000000000, area_type (22) * f, area_type (176) * f), -625000000); + + EXPECT_EQ (db::div_exact (1000000004, area_type (22) * f, area_type (176) * f), 125000000); + EXPECT_EQ (db::div_exact (1000000005, area_type (22) * f, area_type (176) * f), 125000001); + EXPECT_EQ (db::div_exact (-1000000003, area_type (22) * f, area_type (176) * f), -125000000); + EXPECT_EQ (db::div_exact (-1000000004, area_type (22) * f, area_type (176) * f), -125000001); + EXPECT_EQ (db::div_exact (-1000000005, area_type (22) * f, area_type (176) * f), -125000001); + + db::Edge e1 (db::Point (3, -3), db::Point (-8, -1)); + db::Edge e2 (db::Point (-4, -2), db::Point (13, -4)); + + std::pair ip; + ip = e1.intersect_point (e2); + EXPECT_EQ (ip.second.to_string ().c_str (), "0,-3"); + ip = e2.intersect_point (e1); + EXPECT_EQ (ip.second.to_string ().c_str (), "0,-3"); +} diff --git a/src/db/unit_tests/dbEdgeProcessor.cc b/src/db/unit_tests/dbEdgeProcessor.cc index 1cde2bbae..ced703613 100644 --- a/src/db/unit_tests/dbEdgeProcessor.cc +++ b/src/db/unit_tests/dbEdgeProcessor.cc @@ -2386,3 +2386,31 @@ TEST(134) EXPECT_EQ (out.size (), size_t (0)); } + +TEST(135) +{ + db::EdgeProcessor ep; + + db::Point pts[] = { + db::Point (0, 0), + db::Point (19, 19), + db::Point (19, 18), + db::Point (43, 32), + db::Point (37, 27) + }; + + db::Polygon p; + p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]); + p.size (-2, -2, 2); + + ep.insert (p); + + // merge the resulting polygons to get the true outer contour + std::vector out; + db::PolygonContainer pc (out); + db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/); + db::SimpleMerge op (1 /*wc>0*/); + ep.process (pg2, op); + + EXPECT_EQ (out.size (), size_t (0)); +}