klayout/src/db/dbPolygonGenerators.cc

1121 lines
27 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2017 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dbPolygonGenerators.h"
#include <vector>
#include <deque>
#include <memory>
#if 0
#define DEBUG_POLYGON_GENERATOR
#endif
namespace db
{
// -------------------------------------------------------------------------------
// PolygonGenerator implementation
struct PGPoint
{
PGPoint () : point (), contour (0), first (false) { }
PGPoint (const db::Point &p, size_t i, bool f) : point (p), contour (i), first (f) { }
db::Point point;
size_t contour;
bool first;
};
class PGPolyContour
{
public:
typedef std::deque <db::Point> contour_type;
typedef contour_type::const_iterator const_iterator;
typedef contour_type::iterator iterator;
PGPolyContour ()
: m_is_hole (false), m_next (-1), m_last (-1)
{
// ...
}
PGPolyContour (const PGPolyContour &d)
: m_contour (d.m_contour), m_is_hole (d.m_is_hole), m_next (d.m_next), m_last (d.m_last)
{
// ...
}
PGPolyContour &operator= (const PGPolyContour &d)
{
if (this != &d) {
m_contour = d.m_contour;
m_is_hole = d.m_is_hole;
m_next = d.m_next;
m_last = d.m_last;
}
return *this;
}
const_iterator begin () const { return m_contour.begin (); }
const_iterator end () const { return m_contour.end (); }
iterator begin () { return m_contour.begin (); }
iterator end () { return m_contour.end (); }
const db::Point &front () const { return m_contour.front (); }
db::Point &front () { return m_contour.front (); }
const db::Point &back () const { return m_contour.back (); }
db::Point &back () { return m_contour.back (); }
void push_back (const db::Point &p) { m_contour.push_back (p); }
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 (); }
bool empty () const { return m_contour.empty (); }
size_t size () const { return m_contour.size (); }
void last (long n)
{
m_last = n;
}
long last () const
{
return m_last;
}
void next (long n)
{
m_next = n;
}
long next () const
{
return m_next;
}
bool is_hole () const
{
return m_is_hole;
}
void is_hole (bool hole)
{
m_is_hole = hole;
}
void clear ()
{
m_next = -1;
m_last = -1;
m_contour.clear ();
}
void erase (iterator from, iterator to)
{
m_contour.erase (from, to);
}
template <class I>
void insert (iterator at, I from, I to)
{
m_contour.insert (at, from, to);
}
private:
contour_type m_contour;
bool m_is_hole;
long m_next;
long m_last;
};
class PGContourList
{
public:
PGContourList ()
: m_free_contours (-1)
{ }
PGPolyContour &operator[] (size_t n)
{
return m_contours [n];
}
const PGPolyContour &operator[] (size_t n) const
{
return m_contours [n];
}
size_t size () const
{
return m_contours.size ();
}
size_t allocate ()
{
size_t index;
if (m_free_contours >= 0) {
index = m_free_contours;
m_free_contours = m_contours [m_free_contours].next ();
m_contours [index].next (-1);
} else {
index = m_contours.size ();
m_contours.push_back (PGPolyContour ());
}
return index;
}
void join (size_t n1, size_t n2)
{
PGPolyContour &c1 = m_contours [n1];
PGPolyContour &c2 = m_contours [n2];
if (c1.next () < 0) {
c1.next (c2.next ());
c1.last (c2.last ());
} else if (c2.next () >= 0) {
m_contours [c1.last ()].next (c2.next ());
c1.last (c2.last ());
}
c2.clear ();
c2.next (m_free_contours);
m_free_contours = long (n2);
}
void free (size_t n)
{
PGPolyContour &c = m_contours [n];
c.clear ();
c.next (m_free_contours);
m_free_contours = long (n);
}
void free_all (size_t n)
{
long i = long (n);
do {
long ii = m_contours [i].next ();
free (i);
i = ii;
} while (i >= 0);
}
void clear ()
{
m_free_contours = -1;
m_contours.clear ();
}
void append (size_t what, size_t to)
{
m_contours [m_contours [to].next () < 0 ? long (to) : m_contours [to].last ()].next (long (what));
long l = m_contours [what].last ();
m_contours [to].last (l < 0 ? long (what) : l);
}
private:
long m_free_contours;
std::vector <PGPolyContour> m_contours;
};
PolygonGenerator::PolygonGenerator (PolygonSink &psink, bool resolve_holes, bool min_coherence)
: EdgeSink (),
mp_contours (new PGContourList ()),
m_y (std::numeric_limits<db::Coord>::min ()),
m_open_pos (m_open.end ()),
mp_psink (&psink),
mp_spsink (0),
m_resolve_holes (resolve_holes),
m_open_contours (false),
m_min_coherence (min_coherence),
m_compress (true)
{
// .. nothing yet ..
}
PolygonGenerator::PolygonGenerator (SimplePolygonSink &spsink, bool min_coherence)
: EdgeSink (),
mp_contours (new PGContourList ()),
m_y (std::numeric_limits<db::Coord>::min ()),
m_open_pos (m_open.end ()),
mp_psink (0),
mp_spsink (&spsink),
m_resolve_holes (true),
m_open_contours (false),
m_min_coherence (min_coherence),
m_compress (true)
{
// .. nothing yet ..
}
PolygonGenerator::~PolygonGenerator ()
{
delete mp_contours;
mp_contours = 0;
}
void
PolygonGenerator::start ()
{
if (mp_psink) {
mp_psink->start ();
}
if (mp_spsink) {
mp_spsink->start ();
}
}
void
PolygonGenerator::flush ()
{
#ifdef DEBUG_POLYGON_GENERATOR
for (open_map_iterator_type i = m_open.begin (); i != m_open.end (); ++i) {
printf ("%d:%s%c%c ", i->contour, i->point.to_string().c_str(), i->first ? '!' : ' ', i == m_open_pos ? '*' : ' ');
}
printf ("\n");
#endif
tl_assert (m_open.empty ());
mp_contours->clear ();
m_open.clear ();
if (mp_psink) {
mp_psink->flush ();
}
if (mp_spsink) {
mp_spsink->flush ();
}
}
void
PolygonGenerator::begin_scanline (db::Coord y)
{
#ifdef DEBUG_POLYGON_GENERATOR
printf ("begin y=%d\n", y);
#endif
m_open_pos = m_open.begin ();
m_y = y;
#ifdef DEBUG_POLYGON_GENERATOR
printf ("m_open=");
for (open_map_type::const_iterator o = m_open.begin (); o != m_open.end (); ++o) {
printf ("%d:%s ", o->contour, o->point.to_string().c_str());
}
printf ("\n");
printf ("contours:\n");
for (size_t j = 0; j < mp_contours->size (); ++j) {
printf ("c%d%s: ", j, (*mp_contours)[j].is_hole () ? "H" : "");
for (size_t i = 0; i < (*mp_contours)[j].size (); ++i) {
printf ("%s ", ((*mp_contours)[j].begin () + i)->to_string().c_str ());
}
printf ("\n");
}
#endif
}
void
PolygonGenerator::end_scanline (db::Coord /*y*/)
{
join_contours (std::numeric_limits<db::Coord>::max ());
}
void
#ifdef DEBUG_POLYGON_GENERATOR
PolygonGenerator::crossing_edge (const db::Edge &e)
#else
PolygonGenerator::crossing_edge (const db::Edge & /*e*/)
#endif
{
#ifdef DEBUG_POLYGON_GENERATOR
printf ("xing(%s)\n", e.to_string().c_str());
#endif
join_contours (std::numeric_limits<db::Coord>::max ());
++m_open_pos;
}
void
PolygonGenerator::skip_n (size_t n)
{
join_contours (std::numeric_limits<db::Coord>::max ());
#ifdef DEBUG_POLYGON_GENERATOR
printf ("skip(%d)\n", n);
#endif
while (n-- > 0) {
++m_open_pos;
}
}
void
PolygonGenerator::put (const db::Edge &e)
{
#ifdef DEBUG_POLYGON_GENERATOR
printf ("put(%s) y=%d m_open(%d)=", e.to_string().c_str(),m_y,std::distance (m_open.begin (), m_open_pos));
for (open_map_iterator_type i = m_open.begin (); i != m_open.end (); ++i) {
printf ("%d:%s%c%c ", i->contour, i->point.to_string().c_str(), i->first ? '!' : ' ', i == m_open_pos ? '*' : ' ');
}
printf ("\n");
#endif
if (m_open_pos != m_open.end ()) {
db::Coord x;
if (e.p1 ().y () == m_y && e.p2 ().y () == m_y) {
x = std::min (e.p1 ().x (), e.p2 ().x ());
} else if (e.p1 ().y () == m_y) {
x = e.p1 ().x ();
} else {
x = e.p2 ().x ();
}
join_contours (x);
}
if (m_open_pos != m_open.end () && e.p1 ().y () == m_y && m_open_pos->point == e.p1 () && (! m_min_coherence || e.dy () == 0)) {
size_t ic = m_open_pos->contour;
PGPolyContour &c = (*mp_contours) [ic];
tl_assert (c.back () == e.p1 ());
c.push_back (e.p2 ());
m_open_pos->point = e.p2 ();
if (e.p2 ().y () > m_y) {
if (m_open_contours) {
eliminate_hole ();
}
++m_open_pos;
}
} else if (m_open_pos != m_open.end () && e.p2 ().y () == m_y && m_open_pos->point == e.p2 () && (m_min_coherence || e.dy () == 0)) {
size_t ic = m_open_pos->contour;
PGPolyContour &c = (*mp_contours) [ic];
tl_assert (c.front () == e.p2 ());
c.push_front (e.p1 ());
m_open_pos->point = e.p1 ();
if (e.p1 ().y () > m_y) {
if (m_open_contours) {
eliminate_hole ();
}
++m_open_pos;
}
} else {
bool hole = (e.dy () < 0);
size_t inew = mp_contours->allocate ();
PGPolyContour &cnew = (*mp_contours) [inew];
cnew.is_hole (hole);
cnew.push_back (e.p1 ());
cnew.push_back (e.p2 ());
#ifdef DEBUG_POLYGON_GENERATOR
printf ("create %s %d\n", hole ? "hole" : "hull", poly_index);
#endif
m_open.insert (m_open_pos, PGPoint (hole ? e.p1 () : e.p2 (), inew, true));
m_open.insert (m_open_pos, PGPoint (hole ? e.p2 () : e.p1 (), inew, false));
--m_open_pos;
}
#ifdef DEBUG_POLYGON_GENERATOR
for (open_map_iterator_type i = m_open.begin (); i != m_open.end (); ++i) {
printf ("%d:%s%c%c ", i->contour, i->point.to_string().c_str(), i->first ? '!' : ' ', i == m_open_pos ? '*' : ' ');
}
printf ("\n");
#endif
}
void PolygonGenerator::eliminate_hole ()
{
if (m_open_pos == m_open.end ()) {
return;
}
size_t ic = m_open_pos->contour;
PGPolyContour &c = (*mp_contours) [ic];
if (!c.is_hole () || m_open_pos->first) {
return;
}
// We found the intial edges of a new hole: connect the (partial) hole with a stitch line to the left.
// This way we can turn the hole into a non-hole contour.
tl_assert (m_open_pos != m_open.begin ());
--m_open_pos;
tl_assert (m_open_pos != m_open.begin ());
--m_open_pos;
size_t iprev = m_open_pos->contour;
PGPolyContour &cprev = (*mp_contours) [iprev];
tl_assert (cprev.size () >= 2);
// 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);
// Build a separate contour that continues the contours to the left of the hole
PGPolyContour cc = c;
cc.clear ();
cc.is_hole (false);
cc.push_back (c.front ());
cc.push_back (c.begin ()[1]);
if (pprev != cc.back ()) {
cc.push_back (pprev);
}
if (cprev.back () != cc.back ()) {
cc.push_back (cprev.back ());
}
cprev.back () = pprev;
while (cprev.size () > 2 && cprev.back ().y () == m_y && cprev.end ()[-2].y () == m_y && cprev.back ().x () <= cprev.end ()[-2].x ()) {
cprev.pop_back ();
}
cprev.insert (cprev.end (), c.end () - 2, c.end ());
c = cc;
m_open_pos->contour = ic;
++m_open_pos;
m_open_pos->first = false;
++m_open_pos;
m_open_pos->first = true;
m_open_pos->contour = iprev;
}
void
PolygonGenerator::join_contours (db::Coord x)
{
while (m_open_pos != m_open.end ()) {
open_map_iterator_type n = m_open_pos;
++n;
if (n == m_open.end () || m_open_pos->point.y () != m_y || m_open_pos->point != n->point || m_open_pos->point.x () > x) {
return;
}
open_map_iterator_type nn = n;
open_map_iterator_type next = ++nn;
// don't join, except -/+ pair (for max. coherence) or +/- pair (for min coherence)
bool minus0 = (m_open_pos->first == (*mp_contours) [m_open_pos->contour].is_hole ());
bool minus1 = (n->first == (*mp_contours) [n->contour].is_hole ());
if (! (minus0 == !m_min_coherence && minus1 == m_min_coherence)) {
// join a southward pair consisting of the next and second-next edge.
if (nn != m_open.end () && m_open_pos->point == nn->point) {
next = m_open_pos;
m_open_pos = n;
n = nn;
++nn;
} else if (m_open_pos->point.x () == x) {
return;
}
}
size_t i1 = m_open_pos->contour;
size_t i2 = n->contour;
#ifdef DEBUG_POLYGON_GENERATOR
printf ("join %d and %d\n", i1, i2);
for (open_map_iterator_type i = m_open.begin (); i != m_open.end (); ++i) {
printf ("%d:%s%c%c%c%c ", i->contour, i->point.to_string().c_str(), i->first ? '!' : ' ', i == m_open_pos ? '*' : ' ', i == n ? '+' : ' ', i == nn ? '#' : ' ');
}
printf ("\n");
printf ("--> input contours:\n");
for (size_t j = 0; j < mp_contours->size (); ++j) {
printf ("--> c%d%s: ", j, (*mp_contours)[j].is_hole () ? "H" : "");
for (size_t i = 0; i < (*mp_contours)[j].size (); ++i) {
printf ("%s ", ((*mp_contours)[j].begin () + i)->to_string().c_str ());
}
printf ("\n");
}
#endif
tl_assert (i1 < mp_contours->size ());
tl_assert (i2 < mp_contours->size ());
PGPolyContour &c1 = (*mp_contours) [i1];
PGPolyContour &c2 = (*mp_contours) [i2];
if (i1 != i2) {
tl_assert (! c2.empty ());
tl_assert (! c1.empty ());
if (m_open_contours && !c1.is_hole () && !c2.is_hole ()) {
// join with next contour by creating a stitch line
tl_assert (m_open_pos != m_open.begin ());
open_map_iterator_type np = m_open_pos;
--np;
size_t iprev = np->contour;
PGPolyContour &cprev = (*mp_contours) [iprev];
tl_assert (cprev.size () >= 2);
// 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);
tl_assert (c1.size () >= 2);
tl_assert (c2.size () >= 2);
cprev.back () = pprev;
while (cprev.size () > 2 && cprev.back ().y () == m_y && cprev.end ()[-2].y () == m_y && cprev.back ().x () <= cprev.end ()[-2].x ()) {
cprev.pop_back ();
}
if (iprev == i1) {
if (cprev.begin ()->y () == m_y && cprev.begin ()[1].y () == m_y && cprev.front ().x () >= cprev.begin ()[1].x ()) {
cprev.front () = cprev.back ();
} else {
cprev.push_front (cprev.back ());
}
produce_poly (cprev);
} else {
cprev.insert (cprev.end (), c1.begin (), c1.end ());
cprev.is_hole (false);
mp_contours->join (iprev, i1);
}
if ((c2.end () - 2)->y () == m_y) {
c2.back () = pprev;
} else {
c2.push_back (pprev);
}
c2.push_back (np->point);
if (!np->first) {
open_map_iterator_type o = np;
while (o != m_open.begin ()) {
--o;
if (o->contour == iprev) {
break;
}
}
tl_assert (o->contour == iprev);
o->first = m_open_pos->first;
o = np;
while (o != m_open.begin ()) {
--o;
if (o->contour == i1) {
break;
}
}
tl_assert (o->contour == i1);
o->contour = iprev;
}
np->contour = i2;
np->first = n->first;
} else if (! m_open_pos->first && ! n->first) {
// remove c1 from list of contours, join with c2
if (c2.is_hole ()) {
c2.insert (c2.end (), c1.begin () + 1, c1.end ());
} else {
c2.insert (c2.begin (), c1.begin (), c1.end () - 1);
}
mp_contours->join (i2, i1);
open_map_iterator_type o = m_open_pos;
do {
--o;
} while (o != m_open.begin () && o->contour != i1);
tl_assert (o != m_open.begin ());
o->contour = i2;
o->first = false;
} else {
// remove c1 from list of contours, join with c2
if (c2.is_hole ()) { // yes! c2 is correct!
c1.insert (c1.end (), c2.begin () + 1, c2.end ());
} else {
c1.insert (c1.begin (), c2.begin (), c2.end () - 1);
}
mp_contours->join (i1, i2);
open_map_iterator_type o = n;
do {
++o;
} while (o != m_open.end () && o->contour != i2);
tl_assert (o != m_open.end ());
o->contour = i1;
if (m_open_pos->first && n->first) {
o->first = true;
}
}
} else {
if (! c1.is_hole ()) {
#ifdef DEBUG_POLYGON_GENERATOR
printf ("finish %d (hull)\n", i1);
#endif
produce_poly (c1);
// remove this contour plus all associated holes from the contour list
mp_contours->free_all (i1);
} else if (m_resolve_holes) {
// join with next contour by creating a stitch line
tl_assert (m_open_pos != m_open.begin ());
open_map_iterator_type np = m_open_pos;
--np;
size_t iprev = np->contour;
PGPolyContour &cprev = (*mp_contours) [iprev];
tl_assert (cprev.size () >= 2);
// 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);
tl_assert (c1.size () >= 2);
cprev.back () = pprev;
if ((c1.begin () + 1)->y () == m_y) {
cprev.insert (cprev.end (), c1.begin () + 1, c1.end ());
} else {
cprev.insert (cprev.end (), c1.begin (), c1.end ());
}
cprev.push_back (pprev);
if (eprev.p2 () != pprev) {
cprev.push_back (eprev.p2 ());
}
mp_contours->free (i1);
} else {
// join with next contour by inserting as a hole
tl_assert (nn != m_open.end ());
mp_contours->append (i1, nn->contour);
}
}
m_open.erase (m_open_pos);
m_open.erase (n);
m_open_pos = next;
if (m_open_pos != m_open.begin () && m_open_contours) {
--m_open_pos;
eliminate_hole ();
++m_open_pos;
}
#ifdef DEBUG_POLYGON_GENERATOR
printf ("--> output contours:\n");
for (size_t j = 0; j < mp_contours->size (); ++j) {
printf ("--> c%d%s: ", j, (*mp_contours)[j].is_hole () ? "H" : "");
for (size_t i = 0; i < (*mp_contours)[j].size (); ++i) {
printf ("%s ", ((*mp_contours)[j].begin () + i)->to_string().c_str ());
}
printf ("\n");
}
#endif
}
}
bool
PolygonGenerator::ms_compress = true;
void
PolygonGenerator::produce_poly (const PGPolyContour &c)
{
size_t n = 0;
for (long inext = c.next (); inext >= 0; inext = (*mp_contours) [inext].next ()) {
++n;
}
bool reduce = m_compress && ms_compress;
if (mp_psink) {
PGPolyContour::const_iterator p0 = c.begin ();
PGPolyContour::const_iterator p1 = c.end ();
// remove duplicate point at end
tl_assert (p0 != p1);
--p1;
tl_assert (*p1 == *p0);
if (n == 0 && m_poly.holes () == 0) {
// fast mode ..
m_poly.assign_hull (p0, p1, reduce /*compress*/);
} else {
m_poly.clear ((unsigned int) n);
m_poly.assign_hull (p0, p1, reduce /*compress*/);
for (long inext = c.next (); inext >= 0; inext = (*mp_contours) [inext].next ()) {
tl_assert ((*mp_contours) [inext].is_hole ());
PGPolyContour::const_iterator p0 = (*mp_contours) [inext].begin ();
PGPolyContour::const_iterator p1 = (*mp_contours) [inext].end ();
// remove duplicate point at end
tl_assert (p0 != p1);
--p1;
tl_assert (*p1 == *p0);
m_poly.insert_hole (p0, p1, reduce /*compress*/);
}
}
mp_psink->put (m_poly);
}
if (mp_spsink) {
tl_assert (n == 0); // there should not be holes since we forced resolve_holes to true ...
m_spoly.assign_hull (c.begin (), c.end (), reduce /*compress*/);
mp_spsink->put (m_spoly);
}
}
// -------------------------------------------------------------------------------
// TrapezoidGenerator implementation
TrapezoidGenerator::TrapezoidGenerator (PolygonSink &psink)
: EdgeSink (),
m_y (std::numeric_limits<db::Coord>::min ()),
mp_psink (&psink),
mp_spsink (0)
{
// .. nothing yet ..
}
TrapezoidGenerator::TrapezoidGenerator (SimplePolygonSink &spsink)
: EdgeSink (),
m_y (std::numeric_limits<db::Coord>::min ()),
mp_psink (0),
mp_spsink (&spsink)
{
// .. nothing yet ..
}
TrapezoidGenerator::~TrapezoidGenerator ()
{
// .. nothing yet ..
}
void
TrapezoidGenerator::start ()
{
if (mp_psink) {
mp_psink->start ();
}
if (mp_spsink) {
mp_spsink->start ();
}
}
void
TrapezoidGenerator::flush ()
{
tl_assert (m_edges.empty ());
m_edges.clear ();
if (mp_psink) {
mp_psink->flush ();
}
if (mp_spsink) {
mp_spsink->flush ();
}
}
void
TrapezoidGenerator::begin_scanline (db::Coord y)
{
m_y = y;
m_current_edge = m_edges.begin ();
m_new_edges.clear ();
m_new_edge_refs.clear ();
}
void
TrapezoidGenerator::make_trap (const db::Point (&pts)[4])
{
if (mp_psink) {
m_poly.assign_hull (&pts [0], &pts [4], true);
mp_psink->put (m_poly);
} else if (mp_spsink) {
m_spoly.assign_hull (&pts [0], &pts [4], true);
mp_spsink->put (m_spoly);
}
}
void
TrapezoidGenerator::end_scanline (db::Coord y)
{
tl_assert ((m_edges.size () % 2) == 0);
tl_assert ((m_new_edges.size () % 2) == 0);
// create trapezoids for the finished pairs
std::vector<size_t>::const_iterator rr = m_new_edge_refs.begin ();
for (edge_map_type_iterator e = m_edges.begin (); e != m_edges.end (); ) {
edge_map_type_iterator e1 = e;
++e;
tl_assert (e != m_edges.end ());
edge_map_type_iterator e2 = e;
++e;
size_t r1 = std::numeric_limits<size_t>::max (), r2 = std::numeric_limits<size_t>::max ();
if (rr != m_new_edge_refs.end ()) {
r1 = *rr++;
}
if (rr != m_new_edge_refs.end ()) {
r2 = *rr++;
}
tl_assert (e1->first.dy () > 0);
tl_assert (e2->first.dy () < 0);
if (e1->second.p2 ().y () == y && e2->second.p1 ().y () == y) {
db::Point pts[4];
pts[0] = e1->second.p1 ();
pts[1] = e1->second.p2 ();
pts[2] = e2->second.p1 ();
pts[3] = e2->second.p2 ();
make_trap (pts);
} else if ((e1->second.p2 ().y () == y && e2->second.p2 ().y () < y) || (e2->second.p1 ().y () == y && e1->second.p1 ().y () < y)) {
// Create trapezoid and continue
db::Point pts[4];
pts[0] = e1->second.p1 ();
pts[1] = db::Point (db::coord_traits<db::Coord>::rounded (db::edge_xaty (e1->first, y)), y);
pts[2] = db::Point (db::coord_traits<db::Coord>::rounded (db::edge_xaty (e2->first, y)), y);
pts[3] = e2->second.p2 ();
if (r1 != std::numeric_limits<size_t>::max ()) {
tl_assert (r1 < m_new_edges.size ());
m_new_edges[r1].second.set_p1 (pts[1]);
}
if (r2 != std::numeric_limits<size_t>::max ()) {
tl_assert (r2 < m_new_edges.size ());
m_new_edges[r2].second.set_p2 (pts[2]);
}
make_trap (pts);
}
}
for (edge_map_type_iterator e = m_new_edges.begin (); e != m_new_edges.end (); ) {
edge_map_type_iterator e1 = e;
++e;
tl_assert (e != m_new_edges.end ());
edge_map_type_iterator e2 = e;
++e;
tl_assert (e1->first.dy () > 0);
tl_assert (e2->first.dy () < 0);
if (e1->second.p1 ().y () < y && e2->second.p2 ().y () == y) {
// A trapezoid that continues below a hole to the right
edge_map_type_iterator ee = e2;
++ee;
for ( ; ee != m_new_edges.end (); ++ee) {
if (ee->second.dy () < 0 && ee->second.p2 ().y () < y) {
break;
}
}
tl_assert (ee != m_new_edges.end ());
// Create trapezoid and continue
db::Point pts[4];
pts[0] = e1->second.p1 ();
pts[1] = db::Point (db::coord_traits<db::Coord>::rounded (db::edge_xaty (e1->first, y)), y);
pts[2] = db::Point (db::coord_traits<db::Coord>::rounded (db::edge_xaty (ee->first, y)), y);
pts[3] = ee->second.p2 ();
e1->second.set_p1 (pts[1]);
ee->second.set_p2 (pts[2]);
make_trap (pts);
e = ee;
++e;
}
}
m_new_edges.swap (m_edges);
}
void
TrapezoidGenerator::crossing_edge (const db::Edge &e)
{
// ignore horizontal edges
if (e.dy () == 0) {
return;
}
db::Coord x = db::coord_traits<db::Coord>::rounded (db::edge_xaty (e, m_y));
// skip edges which have terminated
while (m_current_edge != m_edges.end ()) {
db::Point pc = m_current_edge->second.dy () < 0 ? m_current_edge->second.p1 () : m_current_edge->second.p2 ();
if (pc.y () == m_y && pc.x () <= x) {
++m_current_edge;
m_new_edge_refs.push_back (std::numeric_limits<size_t>::max ());
} else {
break;
}
}
tl_assert (m_current_edge != m_edges.end ());
m_new_edge_refs.push_back (m_new_edges.size ());
m_new_edges.push_back (*m_current_edge);
++m_current_edge;
}
void
TrapezoidGenerator::skip_n (size_t n)
{
// skip those edges that have terminated
while (m_current_edge != m_edges.end () && db::edge_ymax (m_current_edge->second) == m_y) {
m_new_edge_refs.push_back (std::numeric_limits<size_t>::max ());
++m_current_edge;
}
while (n-- > 0) {
tl_assert (m_current_edge != m_edges.end ());
m_new_edge_refs.push_back (m_new_edges.size ());
m_new_edges.push_back (*m_current_edge);
++m_current_edge;
}
}
void
TrapezoidGenerator::put (const db::Edge &e)
{
db::Coord x;
if (e.dy () == 0) {
x = std::max (e.p1 ().x (), e.p2 ().x ());
} else if (e.dy () < 0) {
x = e.p2 ().x ();
} else {
x = e.p1 ().x ();
}
// skip edges which have terminated
while (m_current_edge != m_edges.end ()) {
db::Point pc = m_current_edge->second.dy () < 0 ? m_current_edge->second.p1 () : m_current_edge->second.p2 ();
if (pc.y () == m_y && pc.x () <= x) {
++m_current_edge;
m_new_edge_refs.push_back (std::numeric_limits<size_t>::max ());
} else {
break;
}
}
// ignore horizontal edges
if (e.dy () != 0) {
// create a new edge entry: the first edge will be the original (used for snapping) and
// the second the working edge (used for trapezoid generation)
m_new_edges.push_back (std::make_pair (e, e));
}
}
// -------------------------------------------------------------------------------
// SizingPolygonSink implementation
void
SizingPolygonFilter::put (const db::Polygon &polygon)
{
m_sizing_processor.clear ();
m_sizing_processor.insert (polygon.sized (m_dx, m_dy, m_mode));
// merge the resulting polygons to get the true outer contour
db::SimpleMerge op (1 /*wc>0*/);
m_sizing_processor.process (*mp_output, op);
}
} // namespace db