mirror of https://github.com/KLayout/klayout.git
Issue 718 (#719)
* WIP: first part of fix - generation of hole cutlines First problem was that hole cutlines got extended over the whole length and sometimes lead to coincident edges which are difficult to resolve for the polygon cutter. * Refined solution, fixed #718 - disabled elaborate hole insertion procedure for now as the performance impact has to be assessed yet and the new scheme will break many tests - stricter rejection of ambiguous configurations in the polygon cutter - fallback is boolean AND now since there is no need to re-invoke the polygon cutter (we can't do so as we made it more strict). Performance-wise we replace a merge by an AND step which may even be faster the output is smaller and the polygon cutter does not need to be re-invoked. * Compatibility with other STLs
This commit is contained in:
parent
10eee4d895
commit
4134829304
|
|
@ -1060,6 +1060,14 @@ EdgeProcessor::insert (const db::Edge &e, EdgeProcessor::property_type p)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
EdgeProcessor::insert (const db::SimplePolygon &q, EdgeProcessor::property_type p)
|
||||
{
|
||||
for (db::SimplePolygon::polygon_edge_iterator e = q.begin_edge (); ! e.at_end (); ++e) {
|
||||
insert (*e, p);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EdgeProcessor::insert (const db::Polygon &q, EdgeProcessor::property_type p)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -633,6 +633,11 @@ public:
|
|||
*/
|
||||
void insert (const db::Polygon &q, property_type p = 0);
|
||||
|
||||
/**
|
||||
* @brief Insert a simple polygon
|
||||
*/
|
||||
void insert (const db::SimplePolygon &q, property_type p = 0);
|
||||
|
||||
/**
|
||||
* @brief Insert a polygon reference
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -90,6 +90,8 @@ public:
|
|||
void push_front (const db::Point &p) { m_contour.push_front (p); }
|
||||
void pop_back () { m_contour.pop_back (); }
|
||||
void pop_front () { m_contour.pop_front (); }
|
||||
iterator erase (iterator i) { return m_contour.erase (i); }
|
||||
iterator insert (iterator i, const db::Point &p) { return m_contour.insert (i, p); }
|
||||
bool empty () const { return m_contour.empty (); }
|
||||
size_t size () const { return m_contour.size (); }
|
||||
|
||||
|
|
@ -136,9 +138,12 @@ public:
|
|||
}
|
||||
|
||||
template <class I>
|
||||
void insert (iterator at, I from, I to)
|
||||
iterator insert (iterator at, I from, I to)
|
||||
{
|
||||
// NOTE: in some STL m_contour.insert already returns the new iterator
|
||||
size_t index_at = at - m_contour.begin ();
|
||||
m_contour.insert (at, from, to);
|
||||
return m_contour.begin () + index_at;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -725,27 +730,56 @@ PolygonGenerator::join_contours (db::Coord x)
|
|||
PGPolyContour &cprev = (*mp_contours) [iprev];
|
||||
|
||||
tl_assert (cprev.size () >= 2);
|
||||
tl_assert (c1.size () >= 2);
|
||||
|
||||
PGPolyContour::iterator ins = cprev.end ();
|
||||
db::Coord xprev = 0;
|
||||
db::Edge eprev;
|
||||
|
||||
#if 1
|
||||
// shallow analysis: insert the cutline at the end of the sequence - this may
|
||||
// cut lines collinear with contour edges
|
||||
|
||||
eprev = db::Edge (ins[-2], ins[-1]);
|
||||
xprev = db::coord_traits<db::Coord>::rounded (edge_xaty (db::Edge (ins[-2], ins[-1]), m_y));
|
||||
#else
|
||||
// deep analysis: determine insertion point: pick the one where the cutline is shortest
|
||||
|
||||
for (PGPolyContour::iterator i = ins; i > cprev.begin () + 1; --i) {
|
||||
|
||||
db::Edge ecut (i[-2], i[-1]);
|
||||
db::Coord xcut = db::coord_traits<db::Coord>::rounded (edge_xaty (db::Edge (i[-2], i[-1]), m_y));
|
||||
|
||||
if (ins == i || (i[-1].y () >= m_y && i[-2].y () < m_y && xcut < c1.back ().x () && xcut > xprev)) {
|
||||
xprev = xcut;
|
||||
eprev = ecut;
|
||||
ins = i;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
// compute intersection point with next edge
|
||||
db::Edge eprev (cprev.end ()[-2], cprev.back ());
|
||||
db::Coord xprev = db::coord_traits<db::Coord>::rounded (edge_xaty (eprev, m_y));
|
||||
db::Point pprev (xprev, m_y);
|
||||
|
||||
// remove collinear edges along the cut line
|
||||
cprev.back () = pprev;
|
||||
while (cprev.size () > 1 && cprev.end ()[-2].y () == m_y && cprev.end ()[-1].y () == m_y) {
|
||||
cprev.pop_back ();
|
||||
ins[-1] = pprev;
|
||||
while (ins - cprev.begin () > 1 && ins[-2].y () == m_y && ins[-1].y () == m_y) {
|
||||
ins = cprev.erase (ins - 1);
|
||||
}
|
||||
|
||||
tl_assert (c1.size () >= 2);
|
||||
if ((c1.begin () + 1)->y () == m_y) {
|
||||
cprev.insert (cprev.end (), c1.begin () + 1, c1.end ());
|
||||
ins = cprev.insert (ins, c1.begin () + 1, c1.end ());
|
||||
ins += c1.size () - 1;
|
||||
} else {
|
||||
cprev.insert (cprev.end (), c1.begin (), c1.end ());
|
||||
ins = cprev.insert (ins, c1.begin (), c1.end ());
|
||||
ins += c1.size ();
|
||||
}
|
||||
cprev.push_back (pprev);
|
||||
|
||||
ins = cprev.insert (ins, pprev);
|
||||
++ins;
|
||||
if (eprev.p2 () != pprev) {
|
||||
cprev.push_back (eprev.p2 ());
|
||||
cprev.insert (ins, eprev.p2 ());
|
||||
}
|
||||
|
||||
mp_contours->free (i1);
|
||||
|
|
|
|||
|
|
@ -185,10 +185,19 @@ public:
|
|||
return db::vprod_sign (edge (), other.edge ()) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator== (const loose_end_struct<CuttingEdgeType> &other) const
|
||||
{
|
||||
if (! db::coord_traits<double>::equal (proj (), other.proj ())) {
|
||||
return false;
|
||||
} else {
|
||||
return db::vprod_sign (edge (), other.edge ()) == 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class PolygonType, class Edge>
|
||||
static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line)
|
||||
static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, cut_polygon_receiver_base<PolygonType> *right_of_line)
|
||||
{
|
||||
typedef typename PolygonType::point_type point_type;
|
||||
typedef typename PolygonType::coord_type coord_type;
|
||||
|
|
@ -259,6 +268,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C
|
|||
// tie together last and first partial segments.
|
||||
if (cutting_segments[nfirst].segment < 0) {
|
||||
cutting_segments[nfirst].enter = cutting_segments.back ().enter;
|
||||
cutting_segments[nfirst].segment = cutting_segments.back ().segment;
|
||||
cutting_segments.pop_back ();
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +278,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C
|
|||
|
||||
if (nc == 0) {
|
||||
// the hull is fully on the right side -> just output the input polygon and that's it.
|
||||
right_of_line->put (&input);
|
||||
right_of_line->put (input);
|
||||
return true;
|
||||
} else {
|
||||
// remember hole contours for later assignment
|
||||
|
|
@ -279,7 +289,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C
|
|||
} else {
|
||||
PolygonType poly;
|
||||
poly.assign_hull (contour.begin (), contour.end ());
|
||||
right_of_line->put (&poly);
|
||||
right_of_line->put (poly);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -297,23 +307,28 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C
|
|||
|
||||
std::stable_sort (loose_ends.begin (), loose_ends.end ());
|
||||
|
||||
// bring the points in a strict enter/leave order if possible
|
||||
// we allow single pairs of collinear entry/leave edges (cut lines) and bring them in the right order
|
||||
|
||||
bool enter = false;
|
||||
for (typename std::vector<loose_end_struct<cut_polygon_edge_type> >::iterator i = loose_ends.begin (); i != loose_ends.end (); ++i) {
|
||||
if (i + 1 != loose_ends.end () && i[1] == i[0]) {
|
||||
if (i + 2 != loose_ends.end () && i[2] == i[0]) {
|
||||
// triple collinear
|
||||
return false;
|
||||
}
|
||||
if (i[0].enter != enter && i[1].enter == enter) {
|
||||
std::swap (i[0], i[1]);
|
||||
}
|
||||
}
|
||||
enter = !enter;
|
||||
}
|
||||
|
||||
// the points now have to be in strict enter/leave order - otherwise fallback to merge
|
||||
|
||||
enter = false;
|
||||
for (typename std::vector<loose_end_struct<cut_polygon_edge_type> >::iterator i = loose_ends.begin (); i != loose_ends.end (); ++i) {
|
||||
if (i->enter != enter) {
|
||||
typename std::vector<loose_end_struct<cut_polygon_edge_type> >::iterator j = i + 1;
|
||||
typename std::vector<loose_end_struct<cut_polygon_edge_type> >::iterator jj = loose_ends.end ();
|
||||
for ( ; j != loose_ends.end () && !(*j < *i) && !(*i < *j); ++j) {
|
||||
if (j->enter == enter) {
|
||||
jj = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (jj == loose_ends.end ()) {
|
||||
return false; // cannot cut (self-overlapping, self-intersecting)
|
||||
}
|
||||
std::swap (*jj, *i);
|
||||
return false;
|
||||
}
|
||||
enter = !enter;
|
||||
}
|
||||
|
|
@ -410,7 +425,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C
|
|||
// it might happen in some cases, that cut pieces may vanish (i.e. all points on a line). Thus we check, if that
|
||||
// is the case and do not produce a polygon then.
|
||||
if (poly.vertices () > 0) {
|
||||
right_of_line->put (&poly);
|
||||
right_of_line->put (poly);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -441,7 +456,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C
|
|||
}
|
||||
}
|
||||
|
||||
right_of_line->put (&*hull);
|
||||
right_of_line->put (*hull);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -452,7 +467,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C
|
|||
// we assign to a PolygonType, this check is not possible.
|
||||
for (typename std::vector<PolygonType>::iterator hole = hole_polygons.begin (); hole != hole_polygons.end (); ++hole) {
|
||||
if (hole->vertices () > 0) {
|
||||
right_of_line->put (&*hole);
|
||||
right_of_line->put (*hole);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -474,23 +489,22 @@ namespace
|
|||
/**
|
||||
* @brief A polygon sink for the edge processor that feeds the polygon into the cut algorithm
|
||||
*/
|
||||
template <class Sink, class PolygonType, class Edge>
|
||||
struct cut_polygon_sink
|
||||
template <class Sink, class PolygonType>
|
||||
struct cut_polygon_bool_sink
|
||||
: public Sink
|
||||
{
|
||||
cut_polygon_sink (const Edge &_line, CutPolygonReceiverBase *_right_of_line)
|
||||
: line (_line), right_of_line (_right_of_line)
|
||||
cut_polygon_bool_sink (cut_polygon_receiver_base<PolygonType> *_right_of_line)
|
||||
: right_of_line (_right_of_line)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual void put (const PolygonType &poly)
|
||||
{
|
||||
tl_assert (_cut_polygon_internal (poly, line, right_of_line));
|
||||
right_of_line->put (poly);
|
||||
}
|
||||
|
||||
Edge line;
|
||||
CutPolygonReceiverBase *right_of_line;
|
||||
cut_polygon_receiver_base<PolygonType> *right_of_line;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -500,21 +514,32 @@ namespace
|
|||
* fallback.
|
||||
*/
|
||||
template <class PolygonType, class Edge>
|
||||
void cut_polygon_internal_int (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line)
|
||||
void cut_polygon_internal_int (const PolygonType &input, const Edge &line, cut_polygon_receiver_base<PolygonType> *right_of_line)
|
||||
{
|
||||
bool ok = _cut_polygon_internal (input, line, right_of_line);
|
||||
if (! ok) {
|
||||
|
||||
// If the cut operation fails on the plain input, merge the input polygon and try again
|
||||
// If the fast cut operation fails, use boolean AND to perform the cut operation
|
||||
|
||||
PolygonType clip (input.box ());
|
||||
std::vector<PolygonType> mask;
|
||||
cut_polygon (clip, line, std::back_inserter (mask));
|
||||
|
||||
if (! mask.empty ()) {
|
||||
|
||||
db::EdgeProcessor ep;
|
||||
ep.insert_sequence (input.begin_edge ());
|
||||
db::SimpleMerge op;
|
||||
ep.insert_sequence (input.begin_edge (), 0);
|
||||
ep.insert_sequence (mask.begin (), mask.end (), 1);
|
||||
|
||||
cut_polygon_sink<typename get_sink_type<PolygonType>::result, PolygonType, Edge> sink (line, right_of_line);
|
||||
db::BooleanOp op (BooleanOp::And);
|
||||
|
||||
cut_polygon_bool_sink<typename get_sink_type<PolygonType>::result, PolygonType> sink (right_of_line);
|
||||
db::PolygonGenerator pg (sink);
|
||||
ep.process (pg, op);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -524,14 +549,14 @@ namespace
|
|||
*/
|
||||
template <class PolygonType, class IPolygonType>
|
||||
class cut_polygon_receiver_double_impl
|
||||
: public CutPolygonReceiverBase
|
||||
: public cut_polygon_receiver_base<IPolygonType>
|
||||
{
|
||||
public:
|
||||
cut_polygon_receiver_double_impl ()
|
||||
: mp_next (0)
|
||||
{ }
|
||||
|
||||
void set_next (CutPolygonReceiverBase *next)
|
||||
void set_next (cut_polygon_receiver_base<PolygonType> *next)
|
||||
{
|
||||
mp_next = next;
|
||||
}
|
||||
|
|
@ -541,14 +566,14 @@ namespace
|
|||
m_tr = tr;
|
||||
}
|
||||
|
||||
virtual void put (const void *p)
|
||||
virtual void put (const IPolygonType &p)
|
||||
{
|
||||
PolygonType pp = ((const IPolygonType *) p)->transformed (m_tr, false);
|
||||
mp_next->put ((void *) &pp);
|
||||
PolygonType pp = p.transformed (m_tr, false);
|
||||
mp_next->put (pp);
|
||||
}
|
||||
|
||||
private:
|
||||
CutPolygonReceiverBase *mp_next;
|
||||
cut_polygon_receiver_base<PolygonType> *mp_next;
|
||||
db::CplxTrans m_tr;
|
||||
};
|
||||
|
||||
|
|
@ -565,7 +590,7 @@ namespace
|
|||
* transform the polygon to int. On output, the polygon is transformed back to double.
|
||||
*/
|
||||
template <class PolygonType, class Edge>
|
||||
void cut_polygon_internal_double (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line)
|
||||
void cut_polygon_internal_double (const PolygonType &input, const Edge &line, cut_polygon_receiver_base<PolygonType> *right_of_line)
|
||||
{
|
||||
db::DBox bbox = input.box ();
|
||||
bbox += db::DBox (0, 0, 0, 0);
|
||||
|
|
@ -585,22 +610,22 @@ namespace
|
|||
|
||||
}
|
||||
|
||||
template<> DB_PUBLIC void cut_polygon_internal (const db::Polygon &polygon, const db::Polygon::edge_type &line, CutPolygonReceiverBase *right_of_line)
|
||||
template<> DB_PUBLIC void cut_polygon_internal (const db::Polygon &polygon, const db::Polygon::edge_type &line, cut_polygon_receiver_base<db::Polygon> *right_of_line)
|
||||
{
|
||||
cut_polygon_internal_int (polygon, line, right_of_line);
|
||||
}
|
||||
|
||||
template<> DB_PUBLIC void cut_polygon_internal (const db::SimplePolygon &polygon, const db::SimplePolygon::edge_type &line, CutPolygonReceiverBase *right_of_line)
|
||||
template<> DB_PUBLIC void cut_polygon_internal (const db::SimplePolygon &polygon, const db::SimplePolygon::edge_type &line, cut_polygon_receiver_base<db::SimplePolygon> *right_of_line)
|
||||
{
|
||||
cut_polygon_internal_int (polygon, line, right_of_line);
|
||||
}
|
||||
|
||||
template<> DB_PUBLIC void cut_polygon_internal (const db::DPolygon &polygon, const db::DPolygon::edge_type &line, CutPolygonReceiverBase *right_of_line)
|
||||
template<> DB_PUBLIC void cut_polygon_internal (const db::DPolygon &polygon, const db::DPolygon::edge_type &line, cut_polygon_receiver_base<db::DPolygon> *right_of_line)
|
||||
{
|
||||
cut_polygon_internal_double (polygon, line, right_of_line);
|
||||
}
|
||||
|
||||
template<> DB_PUBLIC void cut_polygon_internal (const db::DSimplePolygon &polygon, const db::DSimplePolygon::edge_type &line, CutPolygonReceiverBase *right_of_line)
|
||||
template<> DB_PUBLIC void cut_polygon_internal (const db::DSimplePolygon &polygon, const db::DSimplePolygon::edge_type &line, cut_polygon_receiver_base<db::DSimplePolygon> *right_of_line)
|
||||
{
|
||||
cut_polygon_internal_double (polygon, line, right_of_line);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,25 +74,26 @@ private:
|
|||
|
||||
// Some helper classes and functions for implementing cut_polygon
|
||||
|
||||
class DB_PUBLIC CutPolygonReceiverBase
|
||||
template <class Polygon>
|
||||
class DB_PUBLIC cut_polygon_receiver_base
|
||||
{
|
||||
public:
|
||||
virtual ~CutPolygonReceiverBase () { }
|
||||
virtual void put (const void *) = 0;
|
||||
virtual ~cut_polygon_receiver_base () { }
|
||||
virtual void put (const Polygon &) = 0;
|
||||
};
|
||||
|
||||
template <class OutputIter, class Polygon>
|
||||
class cut_polygon_receiver
|
||||
: public CutPolygonReceiverBase
|
||||
: public cut_polygon_receiver_base<Polygon>
|
||||
{
|
||||
public:
|
||||
cut_polygon_receiver (const OutputIter &iter)
|
||||
: m_iter (iter)
|
||||
{ }
|
||||
|
||||
virtual void put (const void *polygon)
|
||||
virtual void put (const Polygon &polygon)
|
||||
{
|
||||
*m_iter++ = *((const Polygon *) polygon);
|
||||
*m_iter++ = polygon;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -100,7 +101,7 @@ private:
|
|||
};
|
||||
|
||||
template <class PolygonType, class Edge>
|
||||
void DB_PUBLIC cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line);
|
||||
void DB_PUBLIC cut_polygon_internal (const PolygonType &input, const Edge &line, cut_polygon_receiver_base<PolygonType> *right_of_line);
|
||||
|
||||
/**
|
||||
* @brief Polygon cut function
|
||||
|
|
|
|||
|
|
@ -2570,6 +2570,67 @@ TEST(102)
|
|||
EXPECT_EQ (out[0].to_string (), "(0,0;0,200;100,200;100,100;200,100;200,200;500,200;500,100;600,100;600,200;0,200;0,1000;1000,1000;1000,0)");
|
||||
}
|
||||
|
||||
TEST(103)
|
||||
{
|
||||
db::EdgeProcessor ep;
|
||||
|
||||
{
|
||||
db::Point pts[] = {
|
||||
db::Point (0, 0),
|
||||
db::Point (0, 500),
|
||||
db::Point (1500, 500),
|
||||
db::Point (1500, 0),
|
||||
db::Point (1000, 0),
|
||||
db::Point (1000, 400),
|
||||
db::Point (500, 400),
|
||||
db::Point (500, 0)
|
||||
};
|
||||
db::Polygon p;
|
||||
p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]);
|
||||
ep.insert (p, 0);
|
||||
}
|
||||
|
||||
{
|
||||
db::Point pts[] = {
|
||||
db::Point (100, 100),
|
||||
db::Point (100, 400),
|
||||
db::Point (400, 400),
|
||||
db::Point (400, 100)
|
||||
};
|
||||
db::Polygon p;
|
||||
p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]);
|
||||
ep.insert (p, 1);
|
||||
}
|
||||
|
||||
{
|
||||
db::Point pts[] = {
|
||||
db::Point (1100, 100),
|
||||
db::Point (1100, 400),
|
||||
db::Point (1400, 400),
|
||||
db::Point (1400, 100)
|
||||
};
|
||||
db::Polygon p;
|
||||
p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]);
|
||||
ep.insert (p, 1);
|
||||
}
|
||||
|
||||
std::vector<db::Polygon> out;
|
||||
db::PolygonContainer pc (out);
|
||||
db::PolygonGenerator pg (pc, true, true);
|
||||
db::BooleanOp op (db::BooleanOp::ANotB);
|
||||
|
||||
ep.process (pg, op);
|
||||
|
||||
EXPECT_EQ (out.size (), size_t (1));
|
||||
#if 1
|
||||
// fast hole treatment
|
||||
EXPECT_EQ (out[0].to_string (), "(0,0;0,400;100,400;100,100;400,100;400,400;1100,400;1100,100;1400,100;1400,400;0,400;0,500;1500,500;1500,0;1000,0;1000,400;500,400;500,0)");
|
||||
#else
|
||||
// elaborate hole treatment
|
||||
EXPECT_EQ (out[0].to_string (), "(0,0;0,400;100,400;100,100;400,100;400,400;0,400;0,500;1500,500;1500,0;1000,0;1000,400;1100,400;1100,100;1400,100;1400,400;500,400;500,0)");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Bug 134
|
||||
TEST(134)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -559,15 +559,18 @@ TEST(9c)
|
|||
std::vector<db::Polygon> right_of;
|
||||
|
||||
db::cut_polygon (in, db::Edge (db::Point (15835, 0), db::Point (15835, 1)), std::back_inserter (right_of));
|
||||
EXPECT_EQ (right_of.size (), size_t (3));
|
||||
EXPECT_EQ (right_of[0].to_string (), "(17335,8265;16335,9265;15835,9265;15835,9765;17335,9765)");
|
||||
EXPECT_EQ (right_of[1].to_string (), "(15835,9765;16002,9932;15835,10015;17335,10015;17335,9765)");
|
||||
EXPECT_EQ (right_of[2].to_string (), "(15835,10015;15835,10265;17335,10265;17335,10015)");
|
||||
EXPECT_EQ (right_of.size (), size_t (1));
|
||||
EXPECT_EQ (right_of[0].to_string (), "(17335,8265;16335,9265;15835,9265;15835,9765;16002,9932;15835,10015;15835,10265;17335,10265)");
|
||||
|
||||
right_of.clear ();
|
||||
db::cut_polygon (in, db::Edge (db::Point (15835, 1), db::Point (15835, 0)), std::back_inserter (right_of));
|
||||
EXPECT_EQ (right_of.size (), size_t (1));
|
||||
EXPECT_EQ (right_of[0].to_string (), "(14335,8265;14335,10265;15335,10265;15335,9765;15668,9932;15335,10265;15835,10265;15835,10015;15668,9932;15835,9765;15335,9765;14335,9265;15335,9265;15335,9765;15835,9765;15835,9265;15335,9265)");
|
||||
EXPECT_EQ (right_of.size (), size_t (4));
|
||||
if (right_of.size () >= 4) {
|
||||
EXPECT_EQ (right_of[0].to_string (), "(14335,8265;14335,9265;15335,9265)");
|
||||
EXPECT_EQ (right_of[1].to_string (), "(15335,9265;15335,9765;15668,9932;15835,9765;15835,9265)");
|
||||
EXPECT_EQ (right_of[2].to_string (), "(14335,9265;14335,10265;15335,10265;15335,9765)");
|
||||
EXPECT_EQ (right_of[3].to_string (), "(15668,9932;15335,10265;15835,10265;15835,10015)");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(9d)
|
||||
|
|
@ -2373,6 +2376,23 @@ TEST(404)
|
|||
}
|
||||
}
|
||||
|
||||
TEST(405)
|
||||
{
|
||||
db::Polygon poly;
|
||||
std::string s ("(0,0;0,1126;30,1126;30,30;3044,30;3044,1126;5782,1126;5782,30;8796,30;8796,1126;0,1126;0,1141;3009,1141;3009,1156;3194,1156;3194,1141;8826,1141;8826,0;5742,0;5742,1126;3084,1126;3084,0)");
|
||||
tl::Extractor ex (s.c_str ());
|
||||
ex.read (poly);
|
||||
|
||||
std::vector<db::Polygon> sp;
|
||||
db::split_polygon (poly, sp);
|
||||
|
||||
EXPECT_EQ (sp.size (), size_t (2));
|
||||
if (sp.size () >= 2) {
|
||||
EXPECT_EQ (sp[0].to_string (), "(5742,0;5742,1126;5782,1126;5782,30;8796,30;8796,1126;3194,1126;3194,1141;8826,1141;8826,0)");
|
||||
EXPECT_EQ (sp[1].to_string (), "(0,0;0,1126;30,1126;30,30;3044,30;3044,1126;0,1126;0,1141;3009,1141;3009,1156;3194,1156;3194,1126;3084,1126;3084,0)");
|
||||
}
|
||||
}
|
||||
|
||||
static db::Polygon str2poly (const std::string &s)
|
||||
{
|
||||
db::Polygon poly;
|
||||
|
|
@ -2382,7 +2402,7 @@ static db::Polygon str2poly (const std::string &s)
|
|||
}
|
||||
|
||||
// self-overlapping, non-orientable check
|
||||
TEST(405)
|
||||
TEST(500)
|
||||
{
|
||||
std::string ps;
|
||||
std::vector<db::Polygon> parts;
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue