Attempt to fix a scanline issue.

This commit is contained in:
Matthias Koefferlein 2018-07-22 18:49:17 +02:00
parent b10c64907a
commit efd9e47c1f
4 changed files with 161 additions and 6 deletions

View File

@ -23,6 +23,33 @@
#include "dbEdge.h"
namespace db
{
/**
* @brief Computes the gcd of two numbers
*/
template <class C>
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<db::Coord>::area_type b, db::coord_traits<db::Coord>::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
{

View File

@ -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<db::Coord>::area_type b, db::coord_traits<db::Coord>::area_type d);
/**
* @brief An overload of div_exact for double types
*/
inline db::DCoord div_exact (db::DCoord a, db::coord_traits<db::DCoord>::area_type b, db::coord_traits<db::DCoord>::area_type d)
{
return db::coord_traits<db::DCoord>::rounded (double (a) * double (b) / double (d));
}
template <class C> 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<C> (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<C> (x, y));

View File

@ -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<db::Coord>::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<bool, db::Point> 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");
}

View File

@ -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<db::Polygon> 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));
}