mirror of https://github.com/KLayout/klayout.git
2285 lines
64 KiB
C++
2285 lines
64 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 "dbEdgeProcessor.h"
|
|
#include "dbPolygonGenerators.h"
|
|
#include "dbLayout.h"
|
|
#include "tlTimer.h"
|
|
#include "tlProgress.h"
|
|
#include "gsi.h"
|
|
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <memory>
|
|
|
|
#if 0
|
|
#define DEBUG_MERGEOP
|
|
#define DEBUG_BOOLEAN
|
|
#define DEBUG_EDGE_PROCESSOR
|
|
#endif
|
|
|
|
// #define DEBUG_SIZE_INTERMEDIATE
|
|
|
|
namespace db
|
|
{
|
|
|
|
// -------------------------------------------------------------------------------
|
|
// Some utilities ..
|
|
|
|
struct NonZeroInsideFunc
|
|
{
|
|
inline bool operator() (int wc) const
|
|
{
|
|
return wc != 0;
|
|
}
|
|
};
|
|
|
|
struct ProjectionCompare
|
|
{
|
|
ProjectionCompare (const db::Edge &e)
|
|
: m_e (e) { }
|
|
|
|
bool operator () (const db::Point &a, const db::Point &b) const
|
|
{
|
|
db::coord_traits<db::Coord>::area_type sp1 = db::sprod (m_e, db::Edge (m_e.p1 (), a));
|
|
db::coord_traits<db::Coord>::area_type sp2 = db::sprod (m_e, db::Edge (m_e.p1 (), b));
|
|
if (sp1 != sp2) {
|
|
return sp1 < sp2;
|
|
} else {
|
|
return a < b;
|
|
}
|
|
}
|
|
|
|
private:
|
|
db::Edge m_e;
|
|
};
|
|
|
|
struct PolyMapCompare
|
|
{
|
|
PolyMapCompare (db::Coord y)
|
|
: m_y (y) { }
|
|
|
|
bool operator() (const std::pair<db::Edge, size_t> &a, const std::pair<db::Edge, size_t> &b) const
|
|
{
|
|
// simple cases ..
|
|
if (a.first.dx () == 0 && b.first.dx () == 0) {
|
|
return a.first.p1 ().x () < b.first.p1 ().x ();
|
|
} else if (edge_xmax (a.first) < edge_xmin (b.first)) {
|
|
return true;
|
|
} else if (edge_xmin (a.first) > edge_xmax (b.first)) {
|
|
return false;
|
|
} else {
|
|
|
|
// complex case:
|
|
double xa = edge_xaty (a.first, m_y);
|
|
double xb = edge_xaty (b.first, m_y);
|
|
|
|
if (xa != xb) {
|
|
return xa < xb;
|
|
} else {
|
|
|
|
// compare by angle of normalized edges
|
|
|
|
db::Edge ea (a.first);
|
|
db::Edge eb (b.first);
|
|
|
|
if (ea.dy () < 0) {
|
|
ea.swap_points ();
|
|
}
|
|
if (eb.dy () < 0) {
|
|
eb.swap_points ();
|
|
}
|
|
|
|
return db::vprod_sign (ea, eb) < 0;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
db::Coord m_y;
|
|
};
|
|
|
|
inline bool
|
|
is_point_on_fuzzy (const db::Edge &e, const db::Point &pt)
|
|
{
|
|
// exclude the start and end point
|
|
if (pt == e.p1 () || pt == e.p2 ()) {
|
|
|
|
return false;
|
|
|
|
} else if (pt.x () < db::edge_xmin (e) || pt.x () > db::edge_xmax (e) ||
|
|
pt.y () < db::edge_ymin (e) || pt.y () > db::edge_ymax (e)) {
|
|
|
|
return false;
|
|
|
|
} else if (e.dy () == 0 || e.dx () == 0) {
|
|
|
|
// shortcut for orthogonal edges
|
|
return true;
|
|
|
|
} else {
|
|
|
|
db::Vector offset;
|
|
if ((e.dx () < 0 && e.dy () > 0) || (e.dx () > 0 && e.dy () < 0)) {
|
|
offset = db::Vector (1, 1);
|
|
} else {
|
|
offset = db::Vector (-1, 1);
|
|
}
|
|
|
|
db::Vector pp1 = pt - e.p1 ();
|
|
|
|
typedef db::coord_traits<db::Point::coord_type>::area_type area_type;
|
|
area_type a1 = 2 * db::vprod (pp1, e.d ());
|
|
if (a1 < 0) { a1 = -a1; }
|
|
area_type a2 = db::vprod (offset, e.d ());
|
|
if (a2 < 0) { a2 = -a2; }
|
|
return a1 <= a2;
|
|
|
|
}
|
|
}
|
|
|
|
inline bool
|
|
is_point_on_exact (const db::Edge &e, const db::Point &pt)
|
|
{
|
|
if (pt.x () < db::edge_xmin (e) || pt.x () > db::edge_xmax (e) ||
|
|
pt.y () < db::edge_ymin (e) || pt.y () > db::edge_ymax (e)) {
|
|
|
|
return false;
|
|
|
|
} else if (e.dy () == 0 || e.dx () == 0) {
|
|
|
|
// shortcut for orthogonal edges
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return db::vprod_sign (pt - e.p1 (), e.p2 () - e.p1 ()) == 0;
|
|
|
|
}
|
|
}
|
|
|
|
// A intersection test that is more robust numerically.
|
|
// In some cases (i.e. (3,-3;-8,-1) and (-4,-2;13,-4)), the intersection test gives different results
|
|
// for the intersection point if the edges are swapped. This test is robust since it operates on ordered edges.
|
|
inline std::pair<bool, db::Point> safe_intersect_point (const db::Edge &e1, const db::Edge &e2)
|
|
{
|
|
if (e1 < e2) {
|
|
return e1.intersect_point (e2);
|
|
} else {
|
|
return e2.intersect_point (e1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief A structure for storing data in the first phase of the scanline algorithm
|
|
*
|
|
* This data are the cut points (intersection points with other edges). The has_cutpoints
|
|
* flag indicates that the edge has cutpoints which the edge must follow. If the edge has strong
|
|
* cutpoints (strong_cutpoints), they will change the edge, hence non-cutpoints (attractors) must be
|
|
* included.
|
|
*/
|
|
struct CutPoints
|
|
{
|
|
std::vector <db::Point> cut_points;
|
|
std::vector <std::pair<db::Point, size_t> > attractors;
|
|
bool has_cutpoints : 8;
|
|
bool strong_cutpoints : 8;
|
|
|
|
CutPoints ()
|
|
: has_cutpoints (false), strong_cutpoints (false)
|
|
{ }
|
|
|
|
void add_attractor (const db::Point &p, size_t next)
|
|
{
|
|
if (strong_cutpoints) {
|
|
cut_points.push_back (p);
|
|
} else {
|
|
attractors.push_back (std::make_pair (p, next));
|
|
}
|
|
}
|
|
|
|
void add (const db::Point &p, std::vector <CutPoints> *cpvector, bool strong = true)
|
|
{
|
|
has_cutpoints = true;
|
|
if (strong && !strong_cutpoints) {
|
|
|
|
strong_cutpoints = true;
|
|
if (! attractors.empty ()) {
|
|
|
|
std::vector <std::pair<db::Point, size_t> > attr;
|
|
attractors.swap (attr);
|
|
|
|
cut_points.reserve (cut_points.size () + attr.size ());
|
|
for (std::vector <std::pair<db::Point, size_t> >::const_iterator a = attr.begin (); a != attr.end (); ++a) {
|
|
(*cpvector) [a->second].add (a->first, cpvector, true);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cut_points.push_back (p);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* @brief A data object for the scanline algorithm
|
|
*/
|
|
struct WorkEdge
|
|
: public db::Edge
|
|
{
|
|
WorkEdge ()
|
|
: db::Edge (), data (0), prop (0)
|
|
{ }
|
|
|
|
WorkEdge (const db::Edge &e, EdgeProcessor::property_type p = 0, size_t d = 0)
|
|
: db::Edge (e), data (d), prop (p)
|
|
{ }
|
|
|
|
WorkEdge (const WorkEdge &d)
|
|
: db::Edge (d), data (d.data), prop (d.prop)
|
|
{ }
|
|
|
|
WorkEdge &operator= (const WorkEdge &d)
|
|
{
|
|
if (this != &d) {
|
|
db::Edge::operator= (d);
|
|
data = d.data;
|
|
prop = d.prop;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
WorkEdge &operator= (const db::Edge &d)
|
|
{
|
|
db::Edge::operator= (d);
|
|
return *this;
|
|
}
|
|
|
|
CutPoints *make_cutpoints (std::vector <CutPoints> &cutpoints)
|
|
{
|
|
if (! data) {
|
|
cutpoints.push_back (CutPoints ());
|
|
data = cutpoints.size ();
|
|
}
|
|
return &cutpoints [data - 1];
|
|
}
|
|
|
|
size_t data;
|
|
db::EdgeProcessor::property_type prop;
|
|
};
|
|
|
|
/**
|
|
* @brief Compare edges by property ID
|
|
*/
|
|
struct EdgePropCompare
|
|
{
|
|
bool operator() (const db::WorkEdge &a, const db::WorkEdge &b) const
|
|
{
|
|
return a.prop < b.prop;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Compare edges by property ID (reverse)
|
|
*/
|
|
struct EdgePropCompareReverse
|
|
{
|
|
bool operator() (const db::WorkEdge &a, const db::WorkEdge &b) const
|
|
{
|
|
return b.prop < a.prop;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A compare operator for edged
|
|
* This operator will compare edges by their x position on the scanline
|
|
*/
|
|
struct EdgeXAtYCompare
|
|
{
|
|
EdgeXAtYCompare (db::Coord y)
|
|
: m_y (y) { }
|
|
|
|
bool operator() (const db::Edge &a, const db::Edge &b) const
|
|
{
|
|
// simple cases ..
|
|
if (a.dx () == 0 && b.dx () == 0) {
|
|
return a.p1 ().x () < b.p1 ().x ();
|
|
} else if (edge_xmax (a) < edge_xmin (b)) {
|
|
return true;
|
|
} else if (edge_xmin (a) > edge_xmax (b)) {
|
|
return false;
|
|
} else {
|
|
|
|
// complex case:
|
|
// HINT: "volatile" forces xa and xb into memory and disables FPU register optimisation.
|
|
// That way, we can exactly compare doubles afterwards.
|
|
volatile double xa = edge_xaty (a, m_y);
|
|
volatile double xb = edge_xaty (b, m_y);
|
|
|
|
if (xa != xb) {
|
|
return xa < xb;
|
|
} else if (a.dy () == 0) {
|
|
return false;
|
|
} else if (b.dy () == 0) {
|
|
return true;
|
|
} else {
|
|
|
|
// compare by angle of normalized edges
|
|
|
|
db::Edge ea (a);
|
|
db::Edge eb (b);
|
|
|
|
if (ea.dy () < 0) {
|
|
ea.swap_points ();
|
|
}
|
|
if (eb.dy () < 0) {
|
|
eb.swap_points ();
|
|
}
|
|
|
|
return db::vprod_sign (ea, eb) < 0;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
bool equal (const db::Edge &a, const db::Edge &b) const
|
|
{
|
|
// simple cases ..
|
|
if (a.dx () == 0 && b.dx () == 0) {
|
|
return a.p1 ().x () == b.p1 ().x ();
|
|
} else if (edge_xmax (a) < edge_xmin (b)) {
|
|
return false;
|
|
} else if (edge_xmin (a) > edge_xmax (b)) {
|
|
return false;
|
|
} else {
|
|
|
|
// complex case:
|
|
// HINT: "volatile" forces xa and xb into memory and disables FPU register optimisation.
|
|
// That way, we can exactly compare doubles afterwards.
|
|
volatile double xa = edge_xaty (a, m_y);
|
|
volatile double xb = edge_xaty (b, m_y);
|
|
|
|
if (xa != xb) {
|
|
return false;
|
|
} else if (a.dy () == 0 || b.dy () == 0) {
|
|
return (a.dy () == 0) == (b.dy () == 0);
|
|
} else {
|
|
|
|
// compare by angle of normalized edges
|
|
|
|
db::Edge ea (a);
|
|
db::Edge eb (b);
|
|
|
|
if (ea.dy () < 0) {
|
|
ea.swap_points ();
|
|
}
|
|
if (eb.dy () < 0) {
|
|
eb.swap_points ();
|
|
}
|
|
|
|
return db::vprod_sign (ea, eb) == 0;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
db::Coord m_y;
|
|
};
|
|
|
|
/**
|
|
* @brief A compare operator for edges used within EdgeXAtYCompare2
|
|
* This operator is an extension of db::edge_xaty and will deliver
|
|
* the minimum x if the edge is horizontal.
|
|
*/
|
|
static inline double edge_xaty2 (db::Edge e, db::Coord y)
|
|
{
|
|
if (e.p1 ().y () > e.p2 ().y ()) {
|
|
e.swap_points ();
|
|
}
|
|
|
|
if (y <= e.p1 ().y ()) {
|
|
if (y == e.p2 ().y ()) {
|
|
return std::min (e.p1 ().x (), e.p2 ().x ());
|
|
} else {
|
|
return e.p1 ().x ();
|
|
}
|
|
} else if (y >= e.p2 ().y ()) {
|
|
return e.p2 ().x ();
|
|
} else {
|
|
return double (e.p1 ().x ()) + double (e.dx ()) * double (y - e.p1 ().y ()) / double (e.dy ());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief A compare operator for edged
|
|
* This operator will compare edges by their x position on the scanline
|
|
* In Addition to EdgeXAtYCompare, this operator will also compare the
|
|
* direction of the edges. Edges are equal if the position at which they cross
|
|
* the scanline and their direction is identical.
|
|
*/
|
|
struct EdgeXAtYCompare2
|
|
{
|
|
EdgeXAtYCompare2 (db::Coord y)
|
|
: m_y (y) { }
|
|
|
|
bool operator() (const db::Edge &a, const db::Edge &b) const
|
|
{
|
|
// simple cases ..
|
|
if (a.dx () == 0 && b.dx () == 0) {
|
|
return a.p1 ().x () < b.p1 ().x ();
|
|
} else if (edge_xmax (a) < edge_xmin (b)) {
|
|
return true;
|
|
} else if (edge_xmin (a) > edge_xmax (b)) {
|
|
return false;
|
|
} else {
|
|
|
|
// complex case:
|
|
// HINT: "volatile" forces xa and xb into memory and disables FPU register optimisation.
|
|
// That way, we can exactly compare doubles afterwards.
|
|
volatile double xa = edge_xaty2 (a, m_y);
|
|
volatile double xb = edge_xaty2 (b, m_y);
|
|
|
|
if (xa != xb) {
|
|
return xa < xb;
|
|
} else if (a.dy () == 0) {
|
|
return false;
|
|
} else if (b.dy () == 0) {
|
|
return true;
|
|
} else {
|
|
|
|
// In that case the edges will not intersect but rather touch in one point. This defines
|
|
// a sorting which preserves the scanline order of the edges when y advances.
|
|
|
|
db::Edge ea (a);
|
|
db::Edge eb (b);
|
|
|
|
if (ea.dy () < 0) {
|
|
ea.swap_points ();
|
|
}
|
|
if (eb.dy () < 0) {
|
|
eb.swap_points ();
|
|
}
|
|
|
|
bool fa = ea.p2 ().y () > m_y;
|
|
bool fb = eb.p2 ().y () > m_y;
|
|
|
|
if (fa && fb) {
|
|
// Both edges advance
|
|
return db::vprod_sign (ea, eb) < 0;
|
|
} else if (fa || fb) {
|
|
// Only one edge advances - equality
|
|
return false;
|
|
} else {
|
|
return db::vprod_sign (ea, eb) > 0;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
bool equal (const db::Edge &a, const db::Edge &b) const
|
|
{
|
|
// simple cases ..
|
|
if (a.dx () == 0 && b.dx () == 0) {
|
|
return a.p1 ().x () == b.p1 ().x ();
|
|
} else if (edge_xmax (a) < edge_xmin (b)) {
|
|
return false;
|
|
} else if (edge_xmin (a) > edge_xmax (b)) {
|
|
return false;
|
|
} else {
|
|
|
|
// complex case:
|
|
// HINT: "volatile" forces xa and xb into memory and disables FPU register optimisation.
|
|
// That way, we can exactly compare doubles afterwards.
|
|
volatile double xa = edge_xaty2 (a, m_y);
|
|
volatile double xb = edge_xaty2 (b, m_y);
|
|
|
|
if (xa != xb) {
|
|
return false;
|
|
} else if (a.dy () == 0 || b.dy () == 0) {
|
|
return (a.dy () == 0) == (b.dy () == 0);
|
|
} else {
|
|
|
|
// compare by angle of normalized edges
|
|
|
|
db::Edge ea (a);
|
|
db::Edge eb (b);
|
|
|
|
if (ea.dy () < 0) {
|
|
ea.swap_points ();
|
|
}
|
|
if (eb.dy () < 0) {
|
|
eb.swap_points ();
|
|
}
|
|
|
|
return db::vprod_sign (ea, eb) == 0;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
db::Coord m_y;
|
|
};
|
|
|
|
// -------------------------------------------------------------------------------
|
|
// EdgePolygonOp implementation
|
|
|
|
EdgePolygonOp::EdgePolygonOp (bool outside, bool include_touching, int polygon_mode)
|
|
: m_outside (outside), m_include_touching (include_touching),
|
|
m_function (polygon_mode),
|
|
m_wcp_n (0), m_wcp_s (0)
|
|
{
|
|
}
|
|
|
|
void EdgePolygonOp::reset ()
|
|
{
|
|
m_wcp_n = m_wcp_s = 0;
|
|
}
|
|
|
|
bool EdgePolygonOp::select_edge (bool horizontal, property_type p)
|
|
{
|
|
if (p == 0) {
|
|
|
|
return false;
|
|
|
|
} else if (horizontal) {
|
|
|
|
bool res;
|
|
if (m_include_touching) {
|
|
res = (m_function (m_wcp_n) || m_function (m_wcp_s));
|
|
} else {
|
|
res = (m_function (m_wcp_n) && m_function (m_wcp_s));
|
|
}
|
|
|
|
return m_outside ? !res : res;
|
|
|
|
} else {
|
|
|
|
return m_outside ? !m_function (m_wcp_n) : m_function (m_wcp_n);
|
|
|
|
}
|
|
}
|
|
|
|
int EdgePolygonOp::edge (bool north, bool enter, property_type p)
|
|
{
|
|
if (p == 0) {
|
|
int *wc = north ? &m_wcp_n : &m_wcp_s;
|
|
if (enter) {
|
|
++*wc;
|
|
} else {
|
|
--*wc;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool EdgePolygonOp::is_reset () const
|
|
{
|
|
return (m_wcp_n == 0 && m_wcp_s == 0);
|
|
}
|
|
|
|
bool EdgePolygonOp::prefer_touch () const
|
|
{
|
|
return m_include_touching;
|
|
}
|
|
|
|
bool EdgePolygonOp::selects_edges () const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------
|
|
// InteractionDetector implementation
|
|
|
|
InteractionDetector::InteractionDetector (int mode, property_type container_id)
|
|
: m_mode (mode), m_include_touching (true), m_container_id (container_id)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
InteractionDetector::reset ()
|
|
{
|
|
m_wcv_n.clear ();
|
|
m_wcv_s.clear ();
|
|
m_inside.clear ();
|
|
}
|
|
|
|
void
|
|
InteractionDetector::reserve (size_t n)
|
|
{
|
|
m_wcv_n.clear ();
|
|
m_wcv_s.clear ();
|
|
m_wcv_n.resize (n, 0);
|
|
m_wcv_s.resize (n, 0);
|
|
m_inside.clear ();
|
|
}
|
|
|
|
int
|
|
InteractionDetector::edge (bool north, bool enter, property_type p)
|
|
{
|
|
tl_assert (p < m_wcv_n.size () && p < m_wcv_s.size ());
|
|
|
|
int *wcv = north ? &m_wcv_n [p] : &m_wcv_s [p];
|
|
|
|
bool inside_before = (*wcv != 0);
|
|
*wcv += (enter ? 1 : -1);
|
|
bool inside_after = (*wcv != 0);
|
|
|
|
// In "interacting" mode we need to handle both north and south events because
|
|
// we have to catch interactions between objects north and south to the scanline
|
|
if (north || (m_mode == 0 && m_include_touching)) {
|
|
|
|
if (inside_after < inside_before) {
|
|
|
|
m_inside.erase (p);
|
|
|
|
if (m_mode != 0) {
|
|
|
|
// the container objects are delivered last of all coincident edges
|
|
// (due to prefer_touch == true and the sorting of coincident edges by property id)
|
|
// hence every remaining parts count as non-interacting (outside)
|
|
if (p == m_container_id) {
|
|
for (std::set <property_type>::const_iterator i = m_inside.begin (); i != m_inside.end (); ++i) {
|
|
if (*i != m_container_id) {
|
|
m_non_interactions.insert (*i);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
} else if (inside_after > inside_before) {
|
|
|
|
if (m_mode != 0) {
|
|
|
|
// in inside/outside mode we are only interested in interactions with the container_id property
|
|
if (p != m_container_id) {
|
|
// note that the container parts will be delivered first of all coincident
|
|
// edges hence we can check whether the container is present even for coincident
|
|
// edges
|
|
if (m_inside.find (m_container_id) != m_inside.end ()) {
|
|
m_interactions.insert (std::make_pair (m_container_id, p));
|
|
} else {
|
|
m_non_interactions.insert (p);
|
|
}
|
|
} else {
|
|
for (std::set <property_type>::const_iterator i = m_inside.begin (); i != m_inside.end (); ++i) {
|
|
if (*i != m_container_id) {
|
|
m_interactions.insert (std::make_pair (m_container_id, *i));
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
for (std::set <property_type>::const_iterator i = m_inside.begin (); i != m_inside.end (); ++i) {
|
|
if (*i < p) {
|
|
m_interactions.insert (std::make_pair (*i, p));
|
|
} else if (*i > p) {
|
|
m_interactions.insert (std::make_pair (p, *i));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
m_inside.insert (p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InteractionDetector::compare_ns () const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
InteractionDetector::finish ()
|
|
{
|
|
if (m_mode < 0) {
|
|
|
|
// In inside mode remove those objects which have a non-interaction with the container_id property
|
|
for (std::set<property_type>::const_iterator p = m_non_interactions.begin (); p != m_non_interactions.end (); ++p) {
|
|
m_interactions.erase (std::make_pair (m_container_id, *p));
|
|
}
|
|
|
|
m_non_interactions.clear ();
|
|
|
|
} else if (m_mode > 0) {
|
|
|
|
// In outside mode leave those objects which don't participate in an interaction
|
|
for (iterator pp = begin (); pp != end (); ++pp) {
|
|
m_non_interactions.erase (pp->second);
|
|
}
|
|
|
|
m_interactions.clear ();
|
|
for (std::set<property_type>::const_iterator p = m_non_interactions.begin (); p != m_non_interactions.end (); ++p) {
|
|
m_interactions.insert (m_interactions.end (), std::make_pair (m_container_id, *p));
|
|
}
|
|
|
|
m_non_interactions.clear ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------
|
|
// MergeOp implementation
|
|
|
|
MergeOp::MergeOp (unsigned int min_wc)
|
|
: m_wc_n (0), m_wc_s (0), m_min_wc (min_wc), m_zeroes (0)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
MergeOp::reset ()
|
|
{
|
|
m_wcv_n.clear ();
|
|
m_wcv_s.clear ();
|
|
m_wc_n = 0;
|
|
m_wc_n = 0;
|
|
m_zeroes = 0;
|
|
}
|
|
|
|
void
|
|
MergeOp::reserve (size_t n)
|
|
{
|
|
m_wcv_n.clear ();
|
|
m_wcv_s.clear ();
|
|
m_wcv_n.resize (n, 0);
|
|
m_wcv_s.resize (n, 0);
|
|
m_zeroes = 2 * n;
|
|
}
|
|
|
|
static inline
|
|
bool result_by_mode (int wc, unsigned int min_wc)
|
|
{
|
|
return wc > int (min_wc);
|
|
}
|
|
|
|
int
|
|
MergeOp::edge (bool north, bool enter, property_type p)
|
|
{
|
|
tl_assert (p < m_wcv_n.size () && p < m_wcv_s.size ());
|
|
|
|
int *wcv = north ? &m_wcv_n [p] : &m_wcv_s [p];
|
|
int *wc = north ? &m_wc_n : &m_wc_s;
|
|
|
|
bool inside_before = (*wcv != 0);
|
|
*wcv += (enter ? 1 : -1);
|
|
bool inside_after = (*wcv != 0);
|
|
m_zeroes += (!inside_after) - (!inside_before);
|
|
#ifdef DEBUG_MERGEOP
|
|
printf ("north=%d, enter=%d, prop=%d -> %d\n", north, enter, p, m_zeroes);
|
|
#endif
|
|
tl_assert (long (m_zeroes) >= 0);
|
|
|
|
bool res_before = result_by_mode (*wc, m_min_wc);
|
|
if (inside_before != inside_after) {
|
|
*wc += (inside_after - inside_before);
|
|
}
|
|
bool res_after = result_by_mode (*wc, m_min_wc);
|
|
|
|
return res_after - res_before;
|
|
}
|
|
|
|
int
|
|
MergeOp::compare_ns () const
|
|
{
|
|
return result_by_mode (m_wc_n, m_min_wc) - result_by_mode (m_wc_s, m_min_wc);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------
|
|
// BooleanOp implementation
|
|
|
|
BooleanOp::BooleanOp (BoolOp mode)
|
|
: m_wc_na (0), m_wc_nb (0), m_wc_sa (0), m_wc_sb (0), m_mode (mode), m_zeroes (0)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
BooleanOp::reset ()
|
|
{
|
|
m_wcv_n.clear ();
|
|
m_wcv_s.clear ();
|
|
m_wc_na = m_wc_sa = 0;
|
|
m_wc_nb = m_wc_sb = 0;
|
|
m_zeroes = 0;
|
|
}
|
|
|
|
void
|
|
BooleanOp::reserve (size_t n)
|
|
{
|
|
m_wcv_n.clear ();
|
|
m_wcv_s.clear ();
|
|
m_wcv_n.resize (n, 0);
|
|
m_wcv_s.resize (n, 0);
|
|
m_zeroes = 2 * n;
|
|
}
|
|
|
|
template <class InsideFunc>
|
|
inline bool
|
|
BooleanOp::result (int wca, int wcb, const InsideFunc &inside_a, const InsideFunc &inside_b) const
|
|
{
|
|
switch (m_mode) {
|
|
case BooleanOp::And:
|
|
return inside_a (wca) && inside_b (wcb);
|
|
case BooleanOp::ANotB:
|
|
return inside_a (wca) && ! inside_b (wcb);
|
|
case BooleanOp::BNotA:
|
|
return ! inside_a (wca) && inside_b (wcb);
|
|
case BooleanOp::Xor:
|
|
return (inside_a (wca) && ! inside_b (wcb)) || (! inside_a (wca) && inside_b (wcb));
|
|
case BooleanOp::Or:
|
|
return inside_a (wca) || inside_b (wcb);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
template <class InsideFunc>
|
|
inline int
|
|
BooleanOp::edge_impl (bool north, bool enter, property_type p, const InsideFunc &inside_a, const InsideFunc &inside_b)
|
|
{
|
|
tl_assert (p < m_wcv_n.size () && p < m_wcv_s.size ());
|
|
|
|
int *wcv = north ? &m_wcv_n [p] : &m_wcv_s [p];
|
|
int *wca = north ? &m_wc_na : &m_wc_sa;
|
|
int *wcb = north ? &m_wc_nb : &m_wc_sb;
|
|
|
|
bool inside_before = ((p % 2) == 0 ? inside_a (*wcv) : inside_b (*wcv));
|
|
*wcv += (enter ? 1 : -1);
|
|
bool inside_after = ((p % 2) == 0 ? inside_a (*wcv) : inside_b (*wcv));
|
|
m_zeroes += (!inside_after) - (!inside_before);
|
|
#ifdef DEBUG_BOOLEAN
|
|
printf ("north=%d, enter=%d, prop=%d -> %d\n", north, enter, p, m_zeroes);
|
|
#endif
|
|
tl_assert (long (m_zeroes) >= 0);
|
|
|
|
bool res_before = result (*wca, *wcb, inside_a, inside_b);
|
|
if (inside_before != inside_after) {
|
|
if ((p % 2) == 0) {
|
|
*wca += (inside_after - inside_before);
|
|
} else {
|
|
*wcb += (inside_after - inside_before);
|
|
}
|
|
}
|
|
bool res_after = result (*wca, *wcb, inside_a, inside_b);
|
|
|
|
return res_after - res_before;
|
|
}
|
|
|
|
template <class InsideFunc>
|
|
inline int
|
|
BooleanOp::compare_ns_impl (const InsideFunc &inside_a, const InsideFunc &inside_b) const
|
|
{
|
|
return result (m_wc_na, m_wc_nb, inside_a, inside_b) - result (m_wc_sa, m_wc_sb, inside_a, inside_b);
|
|
}
|
|
|
|
int
|
|
BooleanOp::edge (bool north, bool enter, property_type p)
|
|
{
|
|
NonZeroInsideFunc inside;
|
|
return edge_impl (north, enter, p, inside, inside);
|
|
}
|
|
|
|
int
|
|
BooleanOp::compare_ns () const
|
|
{
|
|
NonZeroInsideFunc inside;
|
|
return compare_ns_impl (inside, inside);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------
|
|
// BooleanOp2 implementation
|
|
|
|
BooleanOp2::BooleanOp2 (BoolOp op, int wc_mode_a, int wc_mode_b)
|
|
: BooleanOp (op), m_wc_mode_a (wc_mode_a), m_wc_mode_b (wc_mode_b)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
int
|
|
BooleanOp2::edge (bool north, bool enter, property_type p)
|
|
{
|
|
ParametrizedInsideFunc inside_a (m_wc_mode_a);
|
|
ParametrizedInsideFunc inside_b (m_wc_mode_b);
|
|
return edge_impl (north, enter, p, inside_a, inside_b);
|
|
}
|
|
|
|
int
|
|
BooleanOp2::compare_ns () const
|
|
{
|
|
ParametrizedInsideFunc inside_a (m_wc_mode_a);
|
|
ParametrizedInsideFunc inside_b (m_wc_mode_b);
|
|
return compare_ns_impl (inside_a, inside_b);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------
|
|
// EdgeProcessor implementation
|
|
|
|
EdgeProcessor::EdgeProcessor (bool report_progress, const std::string &progress_desc)
|
|
: m_report_progress (report_progress), m_progress_desc (progress_desc)
|
|
{
|
|
mp_work_edges = new std::vector <WorkEdge> ();
|
|
mp_cpvector = new std::vector <CutPoints> ();
|
|
}
|
|
|
|
EdgeProcessor::~EdgeProcessor ()
|
|
{
|
|
if (mp_work_edges) {
|
|
delete mp_work_edges;
|
|
mp_work_edges = 0;
|
|
}
|
|
if (mp_cpvector) {
|
|
delete mp_cpvector;
|
|
mp_cpvector = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::disable_progress ()
|
|
{
|
|
m_report_progress = false;
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::enable_progress (const std::string &progress_desc)
|
|
{
|
|
m_report_progress = true;
|
|
m_progress_desc = progress_desc;
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::reserve (size_t n)
|
|
{
|
|
mp_work_edges->reserve (n);
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::insert (const db::Edge &e, EdgeProcessor::property_type p)
|
|
{
|
|
if (e.p1 () != e.p2 ()) {
|
|
mp_work_edges->push_back (WorkEdge (e, p));
|
|
}
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::insert (const db::Polygon &q, EdgeProcessor::property_type p)
|
|
{
|
|
for (db::Polygon::polygon_edge_iterator e = q.begin_edge (); ! e.at_end (); ++e) {
|
|
insert (*e, p);
|
|
}
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::clear ()
|
|
{
|
|
mp_work_edges->clear ();
|
|
mp_cpvector->clear ();
|
|
}
|
|
|
|
static void
|
|
get_intersections_per_band_90 (std::vector <CutPoints> &cutpoints, std::vector <WorkEdge>::iterator current, std::vector <WorkEdge>::iterator future, db::Coord y, db::Coord yy, bool with_h)
|
|
{
|
|
std::sort (current, future, edge_xmin_compare<db::Coord> ());
|
|
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("y=%d..%d (90 degree)\n", y, yy);
|
|
printf ("edges:");
|
|
for (std::vector <WorkEdge>::iterator c1 = current; c1 != future; ++c1) {
|
|
printf (" %s", c1->to_string().c_str ());
|
|
}
|
|
printf ("\n");
|
|
#endif
|
|
db::Coord x = edge_xmin (*current);
|
|
|
|
std::vector <WorkEdge>::iterator f = current;
|
|
for (std::vector <WorkEdge>::iterator c = current; c != future; ) {
|
|
|
|
while (f != future && edge_xmin (*f) <= x) {
|
|
++f;
|
|
}
|
|
|
|
db::Coord xx = std::numeric_limits <db::Coord>::max ();
|
|
if (f != future) {
|
|
xx = edge_xmin (*f);
|
|
}
|
|
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("edges %d..%d:", x, xx);
|
|
for (std::vector <WorkEdge>::iterator c1 = c; c1 != f; ++c1) {
|
|
printf (" %s", c1->to_string().c_str ());
|
|
}
|
|
printf ("\n");
|
|
#endif
|
|
|
|
if (std::distance (c, f) > 1) {
|
|
|
|
db::Box cell (x, y, xx, yy);
|
|
|
|
for (std::vector <WorkEdge>::iterator c1 = c; c1 != f; ++c1) {
|
|
|
|
bool c1p1_in_cell = cell.contains (c1->p1 ());
|
|
bool c1p2_in_cell = cell.contains (c1->p2 ());
|
|
|
|
for (std::vector <WorkEdge>::iterator c2 = c; c2 != f; ++c2) {
|
|
|
|
if (c1 == c2) {
|
|
continue;
|
|
}
|
|
|
|
if (c2->dy () == 0) {
|
|
|
|
if ((with_h || c1->dy () != 0) && c1 < c2 && c1->p1 () != c2->p1 () && c1->p2 () != c2->p1 () &&
|
|
c1->p1 () != c2->p2 () && c1->p2 () != c2->p2 ()) {
|
|
|
|
std::pair <bool, db::Point> cp = c1->intersect_point (*c2);
|
|
if (cp.first) {
|
|
|
|
// add a cut point to c1 and c2 (c2 only if necessary)
|
|
c1->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, true);
|
|
if (with_h) {
|
|
c2->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, true);
|
|
}
|
|
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("intersection point %s between %s and %s (1).\n", cp.second.to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ());
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (c1->dy () == 0) {
|
|
|
|
if (c1 < c2 && c1->p1 () != c2->p1 () && c1->p2 () != c2->p1 () &&
|
|
c1->p1 () != c2->p2 () && c1->p2 () != c2->p2 ()) {
|
|
|
|
std::pair <bool, db::Point> cp = c1->intersect_point (*c2);
|
|
if (cp.first) {
|
|
|
|
// add a cut point to c1 and c2
|
|
c2->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, true);
|
|
if (with_h) {
|
|
c1->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, true);
|
|
}
|
|
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("intersection point %s between %s and %s (2).\n", cp.second.to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ());
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (c1->p1 ().x () == c2->p1 ().x ()) {
|
|
|
|
// both edges are coincident - produce the ends of the edges involved as cut points
|
|
if (c1p1_in_cell && c1->p1 ().y () > db::edge_ymin (*c2) && c1->p1 ().y () < db::edge_ymax (*c2)) {
|
|
c2->make_cutpoints (cutpoints)->add (c1->p1 (), &cutpoints, true);
|
|
}
|
|
if (c1p2_in_cell && c1->p2 ().y () > db::edge_ymin (*c2) && c1->p2 ().y () < db::edge_ymax (*c2)) {
|
|
c2->make_cutpoints (cutpoints)->add (c1->p2 (), &cutpoints, true);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
x = xx;
|
|
for (std::vector <WorkEdge>::iterator cc = c; cc != f; ++cc) {
|
|
if (edge_xmax (*cc) < x) {
|
|
if (c != cc) {
|
|
std::swap (*cc, *c);
|
|
}
|
|
++c;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Computes the x value of an edge at the given y value
|
|
*
|
|
* HINT: for application in the scanline algorithm
|
|
* it is important that this method delivers exactly (!) the same x for the same edge
|
|
* (after normalization to dy()>0) and same y!
|
|
*/
|
|
template <class C>
|
|
inline double edge_xaty_double (db::edge<C> e, double y)
|
|
{
|
|
if (e.p1 ().y () > e.p2 ().y ()) {
|
|
e.swap_points ();
|
|
}
|
|
|
|
if (y <= e.p1 ().y ()) {
|
|
return e.p1 ().x ();
|
|
} else if (y >= e.p2 ().y ()) {
|
|
return e.p2 ().x ();
|
|
} else {
|
|
return double (e.p1 ().x ()) + double (e.dx ()) * (y - double (e.p1 ().y ())) / double (e.dy ());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Computes the left bound of the edge geometry for a given band [y1..y2].
|
|
*/
|
|
template <class C>
|
|
inline C edge_xmin_at_yinterval_double (const db::edge<C> &e, double y1, double y2)
|
|
{
|
|
if (e.dx () == 0) {
|
|
return e.p1 ().x ();
|
|
} else if (e.dy () == 0) {
|
|
return std::min (e.p1 ().x (), e.p2 ().x ());
|
|
} else {
|
|
return C (floor (edge_xaty_double (e, ((e.dy () < 0) ^ (e.dx () < 0)) == 0 ? y1 : y2)));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Computes the right bound of the edge geometry for a given band [y1..y2].
|
|
*/
|
|
template <class C>
|
|
inline C edge_xmax_at_yinterval_double (const db::edge<C> &e, double y1, double y2)
|
|
{
|
|
if (e.dx () == 0) {
|
|
return e.p1 ().x ();
|
|
} else if (e.dy () == 0) {
|
|
return std::max (e.p1 ().x (), e.p2 ().x ());
|
|
} else {
|
|
return C (ceil (edge_xaty_double (e, ((e.dy () < 0) ^ (e.dx () < 0)) != 0 ? y1 : y2)));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Functor that compares two edges by their left bound for a given interval [y1..y2].
|
|
*
|
|
* This function is intended for use in scanline scenarious to determine what edges are
|
|
* interacting in a certain y interval.
|
|
*/
|
|
template <class C>
|
|
struct edge_xmin_at_yinterval_double_compare
|
|
{
|
|
edge_xmin_at_yinterval_double_compare (double y1, double y2)
|
|
: m_y1 (y1), m_y2 (y2)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
bool operator() (const db::edge<C> &a, const db::edge<C> &b) const
|
|
{
|
|
if (edge_xmax (a) < edge_xmin (b)) {
|
|
return true;
|
|
} else if (edge_xmin (a) >= edge_xmax (b)) {
|
|
return false;
|
|
} else {
|
|
C xa = edge_xmin_at_yinterval_double (a, m_y1, m_y2);
|
|
C xb = edge_xmin_at_yinterval_double (b, m_y1, m_y2);
|
|
if (xa != xb) {
|
|
return xa < xb;
|
|
} else {
|
|
return a < b;
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
double m_y1, m_y2;
|
|
};
|
|
|
|
static void
|
|
get_intersections_per_band_any (std::vector <CutPoints> &cutpoints, std::vector <WorkEdge>::iterator current, std::vector <WorkEdge>::iterator future, db::Coord y, db::Coord yy, bool with_h)
|
|
{
|
|
std::vector <WorkEdge *> p1_weak; // holds weak interactions of edge endpoints with other edges
|
|
std::vector <WorkEdge *> ip_weak;
|
|
double dy = y - 0.5;
|
|
double dyy = yy + 0.5;
|
|
|
|
std::sort (current, future, edge_xmin_at_yinterval_double_compare<db::Coord> (dy, dyy));
|
|
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("y=%d..%d\n", y, yy);
|
|
printf ("edges:");
|
|
for (std::vector <WorkEdge>::iterator c1 = current; c1 != future; ++c1) {
|
|
printf (" %s", c1->to_string().c_str ());
|
|
}
|
|
printf ("\n");
|
|
#endif
|
|
db::Coord x = edge_xmin_at_yinterval_double (*current, dy, dyy);
|
|
|
|
std::vector <WorkEdge>::iterator f = current;
|
|
for (std::vector <WorkEdge>::iterator c = current; c != future; ) {
|
|
|
|
while (f != future && edge_xmin_at_yinterval_double (*f, dy, dyy) <= x) {
|
|
++f;
|
|
}
|
|
|
|
db::Coord xx = std::numeric_limits <db::Coord>::max ();
|
|
if (f != future) {
|
|
xx = edge_xmin_at_yinterval_double (*f, dy, dyy);
|
|
}
|
|
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("edges %d..%d:", x, xx);
|
|
for (std::vector <WorkEdge>::iterator c1 = c; c1 != f; ++c1) {
|
|
printf (" %s", c1->to_string().c_str ());
|
|
}
|
|
printf ("\n");
|
|
#endif
|
|
|
|
if (std::distance (c, f) > 1) {
|
|
|
|
db::Box cell (x, y, xx, yy);
|
|
|
|
for (std::vector <WorkEdge>::iterator c1 = c; c1 != f; ++c1) {
|
|
|
|
p1_weak.clear ();
|
|
|
|
bool c1p1_in_cell = cell.contains (c1->p1 ());
|
|
bool c1p2_in_cell = cell.contains (c1->p2 ());
|
|
|
|
for (std::vector <WorkEdge>::iterator c2 = c; c2 != f; ++c2) {
|
|
|
|
if (c1 == c2) {
|
|
continue;
|
|
}
|
|
|
|
if (c2->dy () == 0) {
|
|
|
|
if ((with_h || c1->dy () != 0) && c1 < c2 && c1->p1 () != c2->p1 () && c1->p2 () != c2->p1 () &&
|
|
c1->p1 () != c2->p2 () && c1->p2 () != c2->p2 ()) {
|
|
|
|
std::pair <bool, db::Point> cp = safe_intersect_point (*c1, *c2);
|
|
if (cp.first) {
|
|
|
|
bool on_edge1 = is_point_on_exact (*c1, cp.second);
|
|
|
|
// add a cut point to c1 and c2 (points not on the edge give strong attractors)
|
|
c1->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, !on_edge1);
|
|
if (with_h) {
|
|
c2->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, false);
|
|
}
|
|
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
if (on_edge1) {
|
|
printf ("weak intersection point %s between %s and %s.\n", cp.second.to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ());
|
|
} else {
|
|
printf ("intersection point %s between %s and %s.\n", cp.second.to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ());
|
|
}
|
|
#endif
|
|
|
|
// The new cutpoint must be inserted into other edges as well.
|
|
// If the cutpoint is exactly on the edge and there is just one other edge
|
|
// the cutpoint will be a weak attractor - that is an optional cutpoint.
|
|
// In that case we can skip the cutpoint because no related edge will move.
|
|
ip_weak.clear ();
|
|
for (std::vector <WorkEdge>::iterator cc = c; cc != f; ++cc) {
|
|
if ((with_h || cc->dy () != 0) && cc != c1 && cc != c2 && is_point_on_fuzzy (*cc, cp.second)) {
|
|
ip_weak.push_back (&*cc);
|
|
}
|
|
}
|
|
for (std::vector <WorkEdge *>::iterator icc = ip_weak.begin (); icc != ip_weak.end (); ++icc) {
|
|
if (ip_weak.size () > 1 || !on_edge1) {
|
|
(*icc)->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, true);
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("intersection point %s gives cutpoint in %s.\n", cp.second.to_string ().c_str (), (*icc)->to_string ().c_str ());
|
|
#endif
|
|
} else {
|
|
CutPoints *cpp = (*icc)->make_cutpoints (cutpoints);
|
|
cpp->add_attractor (cp.second, (*icc)->data - 1);
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("intersection point %s gives weak attractor in %s.\n", cp.second.to_string ().c_str (), (*icc)->to_string ().c_str ());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (c1->parallel (*c2) && c1->side_of (c2->p1 ()) == 0) {
|
|
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("%s and %s are parallel.\n", c1->to_string ().c_str (), c2->to_string ().c_str ());
|
|
#endif
|
|
|
|
// both edges are coincident - produce the ends of the edges involved as cut points
|
|
if (c1p1_in_cell && c2->contains (c1->p1 ()) && c2->p1 () != c1->p1 () && c2->p2 () != c1->p1 ()) {
|
|
c2->make_cutpoints (cutpoints)->add (c1->p1 (), &cutpoints, !is_point_on_exact(*c2, c1->p1 ()));
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
if (! is_point_on_exact(*c2, c1->p1 ())) {
|
|
printf ("intersection point %s between %s and %s.\n", c1->p1 ().to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ());
|
|
} else {
|
|
printf ("weak intersection point %s between %s and %s.\n", c1->p1 ().to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ());
|
|
}
|
|
#endif
|
|
}
|
|
if (c1p2_in_cell && c2->contains (c1->p2 ()) && c2->p1 () != c1->p2 () && c2->p2 () != c1->p2 ()) {
|
|
c2->make_cutpoints (cutpoints)->add (c1->p2 (), &cutpoints, !is_point_on_exact(*c2, c1->p2 ()));
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
if (! is_point_on_exact(*c2, c1->p2 ())) {
|
|
printf ("intersection point %s between %s and %s.\n", c1->p2 ().to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ());
|
|
} else {
|
|
printf ("weak intersection point %s between %s and %s.\n", c1->p2 ().to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
} else {
|
|
|
|
if (c1 < c2 && c1->p1 () != c2->p1 () && c1->p2 () != c2->p1 () &&
|
|
c1->p1 () != c2->p2 () && c1->p2 () != c2->p2 ()) {
|
|
|
|
std::pair <bool, db::Point> cp = safe_intersect_point (*c1, *c2);
|
|
if (cp.first) {
|
|
|
|
bool on_edge1 = true;
|
|
bool on_edge2 = is_point_on_exact (*c2, cp.second);
|
|
|
|
// add a cut point to c1 and c2
|
|
if (with_h || c1->dy () != 0) {
|
|
on_edge1 = is_point_on_exact (*c1, cp.second);
|
|
c1->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, !on_edge1);
|
|
}
|
|
|
|
c2->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, !on_edge2);
|
|
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
if (!on_edge1 || !on_edge2) {
|
|
printf ("intersection point %s between %s and %s.\n", cp.second.to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ());
|
|
} else {
|
|
printf ("weak intersection point %s between %s and %s.\n", cp.second.to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ());
|
|
}
|
|
#endif
|
|
|
|
// The new cutpoint must be inserted into other edges as well.
|
|
// If the cutpoint is exactly on the edge and there is just one other edge
|
|
// the cutpoint will be a weak attractor - that is an optional cutpoint.
|
|
// In that case we can skip the cutpoint because no related edge will move.
|
|
ip_weak.clear ();
|
|
for (std::vector <WorkEdge>::iterator cc = c; cc != f; ++cc) {
|
|
if ((with_h || cc->dy () != 0) && cc != c1 && cc != c2 && is_point_on_fuzzy (*cc, cp.second)) {
|
|
ip_weak.push_back (&*cc);
|
|
}
|
|
}
|
|
for (std::vector <WorkEdge *>::iterator icc = ip_weak.begin (); icc != ip_weak.end (); ++icc) {
|
|
if (ip_weak.size () > 1 || !on_edge1 || !on_edge2) {
|
|
(*icc)->make_cutpoints (cutpoints)->add (cp.second, &cutpoints, true);
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("intersection point %s gives cutpoint in %s.\n", cp.second.to_string ().c_str (), (*icc)->to_string ().c_str ());
|
|
#endif
|
|
} else {
|
|
CutPoints *cpp = (*icc)->make_cutpoints (cutpoints);
|
|
cpp->add_attractor (cp.second, (*icc)->data - 1);
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("intersection point %s gives weak attractor in %s.\n", cp.second.to_string ().c_str (), (*icc)->to_string ().c_str ());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// The endpoints of the other edge must be inserted into the edge
|
|
// if they are within the modification range (but only then).
|
|
// We first collect these endpoints because we have to decide whether that can be
|
|
// a weak attractor or, if it affects two or more edges in which case it will become a strong attractor.
|
|
// It's sufficient to do this for p1 only because we made sure we caught all edges
|
|
// in the +-0.5DBU vicinity by choosing the cell large enough (.._double operators).
|
|
// For end points exactly on the line we insert a cutpoint to ensure we use the
|
|
// endpoints as cutpoints in any case.
|
|
if (c1p1_in_cell && is_point_on_fuzzy (*c2, c1->p1 ())) {
|
|
if (is_point_on_exact (*c2, c1->p1 ())) {
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("end point %s gives intersection point between %s and %s.\n", c1->p1 ().to_string ().c_str (), c1->to_string ().c_str (), c2->to_string ().c_str ());
|
|
#endif
|
|
c2->make_cutpoints (cutpoints)->add (c1->p1 (), &cutpoints, true);
|
|
} else {
|
|
p1_weak.push_back (&*c2);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! p1_weak.empty ()) {
|
|
|
|
bool strong = false;
|
|
for (std::vector<WorkEdge *>::const_iterator cp = p1_weak.begin (); cp != p1_weak.end () && ! strong; ++cp) {
|
|
if ((*cp)->data > 0 && cutpoints [(*cp)->data - 1].strong_cutpoints) {
|
|
strong = true;
|
|
}
|
|
}
|
|
|
|
p1_weak.back ()->make_cutpoints (cutpoints);
|
|
size_t n = p1_weak.back ()->data - 1;
|
|
for (std::vector<WorkEdge *>::const_iterator cp = p1_weak.begin (); cp != p1_weak.end (); ++cp) {
|
|
|
|
(*cp)->make_cutpoints (cutpoints);
|
|
size_t nn = (*cp)->data - 1;
|
|
if (strong) {
|
|
cutpoints [nn].add (c1->p1 (), &cutpoints);
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("Insert strong attractor %s in %s.\n", c1->p1 ().to_string ().c_str (), (*cp)->to_string ().c_str ());
|
|
#endif
|
|
} else {
|
|
cutpoints [nn].add_attractor (c1->p1 (), n);
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("Insert weak attractor %s in %s.\n", c1->p1 ().to_string ().c_str (), (*cp)->to_string ().c_str ());
|
|
#endif
|
|
}
|
|
|
|
n = nn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
x = xx;
|
|
for (std::vector <WorkEdge>::iterator cc = c; cc != f; ++cc) {
|
|
if (edge_xmax (*cc) < x || edge_xmax_at_yinterval_double (*cc, dy, dyy) < x) {
|
|
if (c != cc) {
|
|
std::swap (*cc, *c);
|
|
}
|
|
++c;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 31, "EdgeProcessor: process");
|
|
|
|
bool prefer_touch = op.prefer_touch ();
|
|
bool selects_edges = op.selects_edges ();
|
|
|
|
db::Coord y;
|
|
std::vector <WorkEdge>::iterator future;
|
|
|
|
// step 1: preparation
|
|
|
|
if (mp_work_edges->empty ()) {
|
|
es.start ();
|
|
es.flush ();
|
|
return;
|
|
}
|
|
|
|
mp_cpvector->clear ();
|
|
|
|
property_type n_props = 0;
|
|
for (std::vector <WorkEdge>::iterator e = mp_work_edges->begin (); e != mp_work_edges->end (); ++e) {
|
|
if (e->prop > n_props) {
|
|
n_props = e->prop;
|
|
}
|
|
}
|
|
++n_props;
|
|
|
|
size_t todo_max = 1000000;
|
|
|
|
std::auto_ptr<tl::AbsoluteProgress> progress (0);
|
|
if (m_report_progress) {
|
|
if (m_progress_desc.empty ()) {
|
|
progress.reset (new tl::AbsoluteProgress (tl::to_string (QObject::tr ("Processing")), 1000));
|
|
} else {
|
|
progress.reset (new tl::AbsoluteProgress (m_progress_desc, 1000));
|
|
}
|
|
progress->set_format (tl::to_string (QObject::tr ("%.0f%%")));
|
|
progress->set_unit (todo_max / 100);
|
|
}
|
|
|
|
size_t todo_next = 0;
|
|
size_t todo = todo_next;
|
|
todo_next += (todo_max - todo) / 5;
|
|
|
|
|
|
// step 2: find intersections
|
|
std::sort (mp_work_edges->begin (), mp_work_edges->end (), edge_ymin_compare<db::Coord> ());
|
|
|
|
y = edge_ymin ((*mp_work_edges) [0]);
|
|
future = mp_work_edges->begin ();
|
|
|
|
for (std::vector <WorkEdge>::iterator current = mp_work_edges->begin (); current != mp_work_edges->end (); ) {
|
|
|
|
if (m_report_progress) {
|
|
double p = double (std::distance (mp_work_edges->begin (), current)) / double (mp_work_edges->size ());
|
|
progress->set (size_t (double (todo_next - todo) * p) + todo);
|
|
}
|
|
|
|
size_t n = std::distance (current, future);
|
|
db::Coord yy = y;
|
|
|
|
// Use as many scanlines as to fetch approx. 50% new edges into the scanline (this
|
|
// is an empirically determined factor)
|
|
do {
|
|
|
|
while (future != mp_work_edges->end () && edge_ymin (*future) <= yy) {
|
|
++future;
|
|
}
|
|
|
|
if (future != mp_work_edges->end ()) {
|
|
yy = edge_ymin (*future);
|
|
} else {
|
|
yy = std::numeric_limits <db::Coord>::max ();
|
|
}
|
|
|
|
} while (future != mp_work_edges->end () && std::distance (current, future) < long (n + n / 2));
|
|
|
|
bool is90 = true;
|
|
|
|
if (current != future) {
|
|
|
|
for (std::vector <WorkEdge>::iterator c = current; c != future && is90; ++c) {
|
|
if (c->dx () != 0 && c->dy () != 0) {
|
|
is90 = false;
|
|
}
|
|
}
|
|
|
|
if (is90) {
|
|
get_intersections_per_band_90 (*mp_cpvector, current, future, y, yy, selects_edges);
|
|
} else {
|
|
get_intersections_per_band_any (*mp_cpvector, current, future, y, yy, selects_edges);
|
|
}
|
|
|
|
}
|
|
|
|
y = yy;
|
|
for (std::vector <WorkEdge>::iterator c = current; c != future; ++c) {
|
|
// Hint: we have to keep the edges ending a y (the new lower band limit) in the all angle case because these edges
|
|
// may receive cutpoints because the enter the -0.5DBU region below the band
|
|
if ((!is90 && edge_ymax (*c) < y) || (is90 && edge_ymax (*c) <= y)) {
|
|
if (current != c) {
|
|
std::swap (*current, *c);
|
|
}
|
|
++current;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// step 3: create new edges from the ones with cutpoints
|
|
//
|
|
// Hint: when we create the edges from the cutpoints we use the projection to sort the cutpoints along the
|
|
// edge. However, we have some freedom to connect the points which we use to avoid "z" configurations which could
|
|
// create new intersections in a 1x1 pixel box.
|
|
|
|
todo = todo_next;
|
|
todo_next += (todo_max - todo) / 5;
|
|
|
|
size_t n_work = mp_work_edges->size ();
|
|
size_t nw = 0;
|
|
for (size_t n = 0; n < n_work; ++n) {
|
|
|
|
if (m_report_progress) {
|
|
double p = double (n) / double (n_work);
|
|
progress->set (size_t (double (todo_next - todo) * p) + todo);
|
|
}
|
|
|
|
WorkEdge &ew = (*mp_work_edges) [n];
|
|
|
|
CutPoints *cut_points = ew.data ? & ((*mp_cpvector) [ew.data - 1]) : 0;
|
|
ew.data = 0;
|
|
|
|
if (ew.dy () == 0 && ! selects_edges) {
|
|
|
|
// don't care about horizontal edges
|
|
|
|
} else if (cut_points) {
|
|
|
|
if (cut_points->has_cutpoints && ! cut_points->cut_points.empty ()) {
|
|
|
|
db::Edge e = ew;
|
|
property_type p = ew.prop;
|
|
std::sort (cut_points->cut_points.begin (), cut_points->cut_points.end (), ProjectionCompare (e));
|
|
|
|
db::Point pll = e.p1 ();
|
|
db::Point pl = e.p1 ();
|
|
|
|
for (std::vector <db::Point>::iterator cp = cut_points->cut_points.begin (); cp != cut_points->cut_points.end (); ++cp) {
|
|
if (*cp != pl) {
|
|
WorkEdge ne = WorkEdge (db::Edge (pl, *cp), p);
|
|
if (pl.y () == pll.y () && ne.p2 ().x () != pl.x () && ne.p2 ().x () == pll.x ()) {
|
|
ne = db::Edge (pll, ne.p2 ());
|
|
} else if (pl.x () == pll.x () && ne.p2 ().y () != pl.y () && ne.p2 ().y () == pll.y ()) {
|
|
ne = db::Edge (ne.p1 (), pll);
|
|
} else {
|
|
pll = pl;
|
|
}
|
|
pl = *cp;
|
|
if (selects_edges || ne.dy () != 0) {
|
|
if (nw <= n) {
|
|
(*mp_work_edges) [nw++] = ne;
|
|
} else {
|
|
mp_work_edges->push_back (ne);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cut_points->cut_points.back () != e.p2 ()) {
|
|
WorkEdge ne = WorkEdge (db::Edge (pl, e.p2 ()), p);
|
|
if (pl.y () == pll.y () && ne.p2 ().x () != pl.x () && ne.p2 ().x () == pll.x ()) {
|
|
ne = db::Edge (pll, ne.p2 ());
|
|
} else if (pl.x () == pll.x () && ne.p2 ().y () != pl.y () && ne.p2 ().y () == pll.y ()) {
|
|
ne = db::Edge (ne.p1 (), pll);
|
|
}
|
|
if (selects_edges || ne.dy () != 0) {
|
|
if (nw <= n) {
|
|
(*mp_work_edges) [nw++] = ne;
|
|
} else {
|
|
mp_work_edges->push_back (ne);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
if (nw < n) {
|
|
(*mp_work_edges) [nw] = (*mp_work_edges) [n];
|
|
}
|
|
++nw;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (nw < n) {
|
|
(*mp_work_edges) [nw] = (*mp_work_edges) [n];
|
|
}
|
|
++nw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (nw != n_work) {
|
|
mp_work_edges->erase (mp_work_edges->begin () + nw, mp_work_edges->begin () + n_work);
|
|
}
|
|
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("Output edges:\n");
|
|
for (std::vector <WorkEdge>::iterator c1 = mp_work_edges->begin (); c1 != mp_work_edges->end (); ++c1) {
|
|
printf ("%s\n", c1->to_string().c_str ());
|
|
}
|
|
#endif
|
|
|
|
|
|
tl::SelfTimer timer2 (tl::verbosity () >= 41, "EdgeProcessor: production");
|
|
|
|
// step 4: compute the result edges
|
|
|
|
es.start (); // call this as late as possible. This way, input containers can be identical with output containers ("clear" is done after the input is read)
|
|
|
|
op.reset ();
|
|
op.reserve (n_props);
|
|
|
|
std::sort (mp_work_edges->begin (), mp_work_edges->end (), edge_ymin_compare<db::Coord> ());
|
|
|
|
y = edge_ymin ((*mp_work_edges) [0]);
|
|
size_t skip_unit = 1;
|
|
|
|
future = mp_work_edges->begin ();
|
|
for (std::vector <WorkEdge>::iterator current = mp_work_edges->begin (); current != mp_work_edges->end (); ) {
|
|
|
|
if (m_report_progress) {
|
|
double p = double (std::distance (mp_work_edges->begin (), current)) / double (mp_work_edges->size ());
|
|
progress->set (size_t (double (todo_max - todo_next) * p) + todo_next);
|
|
}
|
|
|
|
std::vector <WorkEdge>::iterator f0 = future;
|
|
while (future != mp_work_edges->end () && edge_ymin (*future) <= y) {
|
|
tl_assert (future->data == 0); // HINT: for development
|
|
++future;
|
|
}
|
|
std::sort (f0, future, EdgeXAtYCompare2 (y));
|
|
|
|
db::Coord yy = std::numeric_limits <db::Coord>::max ();
|
|
if (future != mp_work_edges->end ()) {
|
|
yy = edge_ymin (*future);
|
|
}
|
|
for (std::vector <WorkEdge>::const_iterator c = current; c != future; ++c) {
|
|
if (edge_ymax (*c) > y) {
|
|
yy = std::min (yy, edge_ymax (*c));
|
|
}
|
|
}
|
|
|
|
db::Coord ysl = y;
|
|
es.begin_scanline (y);
|
|
|
|
tl_assert (op.is_reset ()); // HINT: for development
|
|
|
|
if (current != future) {
|
|
|
|
std::inplace_merge (current, f0, future, EdgeXAtYCompare2 (y));
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("y=%d ", y);
|
|
for (std::vector <WorkEdge>::iterator c = current; c != future; ++c) {
|
|
printf ("%ld-", long (c->data));
|
|
}
|
|
printf ("\n");
|
|
#endif
|
|
|
|
db::Coord hx = 0;
|
|
int ho = 0;
|
|
|
|
size_t new_skip_unit = std::distance (current, future);
|
|
|
|
for (std::vector <WorkEdge>::iterator c = current; c != future; ) {
|
|
|
|
size_t skip = c->data % skip_unit;
|
|
size_t skip_res = c->data / skip_unit;
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("X %ld->%d,%d\n", long(c->data), skip, skip_res);
|
|
#endif
|
|
|
|
if (skip != 0 && (c + skip >= future || (c + skip)->data != 0)) {
|
|
|
|
tl_assert (c + skip <= future);
|
|
|
|
es.skip_n (skip_res);
|
|
|
|
c->data = skip + new_skip_unit * skip_res;
|
|
|
|
// skip this interval - has not changed
|
|
c += skip;
|
|
|
|
} else {
|
|
|
|
std::vector <WorkEdge>::iterator c0 = c;
|
|
size_t n_res = 0;
|
|
|
|
do {
|
|
|
|
c->data = 0;
|
|
std::vector <WorkEdge>::iterator f = c + 1;
|
|
|
|
// HINT: "volatile" forces x and xx into memory and disables FPU register optimisation.
|
|
// That way, we can exactly compare doubles afterwards.
|
|
volatile double x = edge_xaty (*c, y);
|
|
|
|
while (f != future) {
|
|
volatile double xx = edge_xaty (*f, y);
|
|
if (xx != x) {
|
|
break;
|
|
}
|
|
f->data = 0;
|
|
++f;
|
|
}
|
|
|
|
// compute edges that occure at this vertex
|
|
|
|
bool vertex = false;
|
|
|
|
// treat all edges crossing the scanline in a certain point
|
|
for (std::vector <WorkEdge>::iterator cc = c; cc != f; ) {
|
|
|
|
std::vector <WorkEdge>::iterator e = mp_work_edges->end ();
|
|
|
|
int pn = 0, ps = 0;
|
|
|
|
std::vector <WorkEdge>::iterator cc0 = cc;
|
|
|
|
std::vector <WorkEdge>::iterator fc = cc;
|
|
do {
|
|
++fc;
|
|
} while (fc != f && EdgeXAtYCompare2 (y).equal (*fc, *cc));
|
|
|
|
// sort the coincident edges by property ID - that will
|
|
// simplify algorithms like "inside" and "outside".
|
|
if (fc - cc > 1) {
|
|
// for prefer_touch we first deliver the opening edges in ascending
|
|
// order, in the other case we the other way round so that the opening
|
|
// edges are always delivered with ascending property ID order.
|
|
if (prefer_touch) {
|
|
std::sort (cc, fc, EdgePropCompare ());
|
|
} else {
|
|
std::sort (cc, fc, EdgePropCompareReverse ());
|
|
}
|
|
}
|
|
|
|
// treat all coincident edges
|
|
do {
|
|
|
|
if (cc->dy () != 0) {
|
|
|
|
if (e == mp_work_edges->end () && edge_ymax (*cc) > y) {
|
|
e = cc;
|
|
}
|
|
|
|
if ((cc->dy () > 0) == prefer_touch) {
|
|
if (edge_ymax (*cc) > y) {
|
|
pn += op.edge (true, prefer_touch, cc->prop);
|
|
}
|
|
if (edge_ymin (*cc) < y) {
|
|
ps += op.edge (false, prefer_touch, cc->prop);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
++cc;
|
|
|
|
} while (cc != fc);
|
|
|
|
// Give the edge selection operator a chance to select edges now
|
|
if (selects_edges) {
|
|
|
|
for (std::vector <WorkEdge>::iterator sc = cc0; sc != fc; ++sc) {
|
|
if (edge_ymin (*sc) == y && op.select_edge (sc->dy () == 0, sc->prop)) {
|
|
es.put (*sc);
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("put(%s)\n", sc->to_string().c_str());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// report the closing or opening edges in the opposite order
|
|
// than the other ones (see previous loop). Hence we have some
|
|
// symmetry of events which simplify implementatin of the
|
|
// InteractionDetector for example.
|
|
do {
|
|
|
|
--fc;
|
|
|
|
if (fc->dy () != 0 && (fc->dy () > 0) != prefer_touch) {
|
|
if (edge_ymax (*fc) > y) {
|
|
pn += op.edge (true, ! prefer_touch, fc->prop);
|
|
}
|
|
if (edge_ymin (*fc) < y) {
|
|
ps += op.edge (false, ! prefer_touch, fc->prop);
|
|
}
|
|
}
|
|
|
|
} while (fc != cc0);
|
|
|
|
if (! vertex && (ps != 0 || pn != 0)) {
|
|
|
|
if (ho != 0) {
|
|
db::Edge he (db::Point (hx, y), db::Point (db::coord_traits<db::Coord>::rounded (x), y));
|
|
if (ho > 0) {
|
|
he.swap_points ();
|
|
}
|
|
es.put (he);
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("put(%s)\n", he.to_string().c_str());
|
|
#endif
|
|
}
|
|
|
|
vertex = true;
|
|
|
|
}
|
|
|
|
if (e != mp_work_edges->end ()) {
|
|
|
|
db::Edge edge (*e);
|
|
|
|
if ((pn > 0 && edge.dy () < 0) || (pn < 0 && edge.dy () > 0)) {
|
|
edge.swap_points ();
|
|
}
|
|
|
|
if (pn != 0) {
|
|
++n_res;
|
|
if (edge_ymin (edge) == y) {
|
|
es.put (edge);
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("put(%s)\n", edge.to_string().c_str());
|
|
#endif
|
|
} else {
|
|
es.crossing_edge (edge);
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
printf ("xing(%s)\n", edge.to_string().c_str());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (vertex) {
|
|
hx = db::coord_traits<db::Coord>::rounded (x);
|
|
ho = op.compare_ns ();
|
|
}
|
|
|
|
c = f;
|
|
|
|
} while (c != future && ! op.is_reset ());
|
|
|
|
// TODO: assert that there is no overflow here:
|
|
c0->data = size_t (std::distance (c0, c) + new_skip_unit * n_res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
skip_unit = new_skip_unit;
|
|
|
|
y = yy;
|
|
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
for (std::vector <WorkEdge>::iterator c = current; c != future; ++c) {
|
|
printf ("%ld-", long (c->data));
|
|
}
|
|
printf ("\n");
|
|
#endif
|
|
std::vector <WorkEdge>::iterator c0 = current;
|
|
std::vector <WorkEdge>::iterator last_interval = future;
|
|
current = future;
|
|
|
|
bool valid = true;
|
|
|
|
for (std::vector <WorkEdge>::iterator c = future; c != c0; ) {
|
|
|
|
--c;
|
|
|
|
bool start_interval = (c->data != 0);
|
|
size_t data = c->data;
|
|
c->data = 0;
|
|
|
|
db::Coord ymax = edge_ymax (*c);
|
|
if (ymax >= y) {
|
|
--current;
|
|
if (current != c) {
|
|
*current = *c;
|
|
}
|
|
}
|
|
if (ymax <= y) {
|
|
// an edge ends now. The interval is not valid, i.e. cannot be skipped easily.
|
|
valid = false;
|
|
}
|
|
|
|
if (start_interval && current != future) {
|
|
current->data = valid ? data : 0;
|
|
last_interval = current;
|
|
valid = true;
|
|
}
|
|
|
|
}
|
|
#ifdef DEBUG_EDGE_PROCESSOR
|
|
for (std::vector <WorkEdge>::iterator c = current; c != future; ++c) {
|
|
printf ("%ld-", long (c->data));
|
|
}
|
|
printf ("\n");
|
|
#endif
|
|
|
|
}
|
|
|
|
tl_assert (op.is_reset ()); // HINT: for development (second)
|
|
|
|
es.end_scanline (ysl);
|
|
|
|
}
|
|
|
|
es.flush ();
|
|
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::simple_merge (const std::vector<db::Edge> &in, std::vector <db::Edge> &edges, int mode)
|
|
{
|
|
clear ();
|
|
reserve (in.size ());
|
|
insert_sequence (in.begin (), in.end ());
|
|
|
|
db::SimpleMerge op (mode);
|
|
db::EdgeContainer out (edges);
|
|
process (out, op);
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::simple_merge (const std::vector<db::Edge> &in, std::vector <db::Polygon> &polygons, bool resolve_holes, bool min_coherence, int mode)
|
|
{
|
|
clear ();
|
|
reserve (in.size ());
|
|
insert_sequence (in.begin (), in.end ());
|
|
|
|
db::SimpleMerge op (mode);
|
|
db::PolygonContainer pc (polygons);
|
|
db::PolygonGenerator out (pc, resolve_holes, min_coherence);
|
|
process (out, op);
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::simple_merge (const std::vector<db::Polygon> &in, std::vector <db::Edge> &edges, int mode)
|
|
{
|
|
clear ();
|
|
reserve (count_edges (in));
|
|
for (std::vector<db::Polygon>::const_iterator q = in.begin (); q != in.end (); ++q) {
|
|
insert (*q);
|
|
}
|
|
|
|
db::SimpleMerge op (mode);
|
|
db::EdgeContainer out (edges);
|
|
process (out, op);
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::simple_merge (const std::vector<db::Polygon> &in, std::vector <db::Polygon> &out, bool resolve_holes, bool min_coherence, int mode)
|
|
{
|
|
clear ();
|
|
reserve (count_edges (in));
|
|
|
|
if (&in == &out) {
|
|
while (! out.empty ()) {
|
|
insert (out.back ());
|
|
out.pop_back ();
|
|
}
|
|
} else {
|
|
for (std::vector<db::Polygon>::const_iterator q = in.begin (); q != in.end (); ++q) {
|
|
insert (*q);
|
|
}
|
|
}
|
|
|
|
db::SimpleMerge op (mode);
|
|
db::PolygonContainer pc (out);
|
|
db::PolygonGenerator pg (pc, resolve_holes, min_coherence);
|
|
process (pg, op);
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::merge (const std::vector<db::Polygon> &in, std::vector <db::Edge> &edges, unsigned int min_wc)
|
|
{
|
|
clear ();
|
|
reserve (count_edges (in));
|
|
|
|
size_t n = 0;
|
|
for (std::vector<db::Polygon>::const_iterator q = in.begin (); q != in.end (); ++q, ++n) {
|
|
insert (*q, n);
|
|
}
|
|
|
|
db::MergeOp op (min_wc);
|
|
db::EdgeContainer out (edges);
|
|
process (out, op);
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::merge (const std::vector<db::Polygon> &in, std::vector <db::Polygon> &out, unsigned int min_wc, bool resolve_holes, bool min_coherence)
|
|
{
|
|
clear ();
|
|
reserve (count_edges (in));
|
|
|
|
if (&in == &out) {
|
|
size_t n = 0;
|
|
while (! out.empty ()) {
|
|
insert (out.back (), n);
|
|
out.pop_back ();
|
|
++n;
|
|
}
|
|
} else {
|
|
size_t n = 0;
|
|
for (std::vector<db::Polygon>::const_iterator q = in.begin (); q != in.end (); ++q, ++n) {
|
|
insert (*q, n);
|
|
}
|
|
}
|
|
|
|
db::MergeOp op (min_wc);
|
|
db::PolygonContainer pc (out);
|
|
db::PolygonGenerator pg (pc, resolve_holes, min_coherence);
|
|
process (pg, op);
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::size (const std::vector<db::Polygon> &in, db::Coord dx, db::Coord dy, std::vector <db::Edge> &out, unsigned int mode)
|
|
{
|
|
clear ();
|
|
reserve (count_edges (in));
|
|
|
|
size_t n = 0;
|
|
for (std::vector<db::Polygon>::const_iterator q = in.begin (); q != in.end (); ++q, n += 2) {
|
|
insert (*q, n);
|
|
}
|
|
|
|
// Merge the polygons and feed them into the sizing filter
|
|
db::EdgeContainer ec (out);
|
|
db::SizingPolygonFilter siz (ec, dx, dy, mode);
|
|
db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/);
|
|
db::BooleanOp op (db::BooleanOp::Or);
|
|
process (pg, op);
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::size (const std::vector<db::Polygon> &in, db::Coord dx, db::Coord dy, std::vector <db::Polygon> &out, unsigned int mode, bool resolve_holes, bool min_coherence)
|
|
{
|
|
clear ();
|
|
reserve (count_edges (in));
|
|
|
|
if (&in == &out) {
|
|
size_t n = 0;
|
|
while (! out.empty ()) {
|
|
insert (out.back (), n);
|
|
out.pop_back ();
|
|
n += 2;
|
|
}
|
|
} else {
|
|
size_t n = 0;
|
|
for (std::vector<db::Polygon>::const_iterator q = in.begin (); q != in.end (); ++q, n += 2) {
|
|
insert (*q, n);
|
|
}
|
|
}
|
|
|
|
// Merge the polygons and feed them into the sizing filter
|
|
#if ! defined(DEBUG_SIZE_INTERMEDIATE)
|
|
db::PolygonContainer pc (out);
|
|
db::PolygonGenerator pg2 (pc, resolve_holes, min_coherence);
|
|
db::SizingPolygonFilter siz (pg2, dx, dy, mode);
|
|
db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/);
|
|
db::BooleanOp op (db::BooleanOp::Or);
|
|
process (pg, op);
|
|
#else
|
|
// Intermediate output for debugging
|
|
db::PolygonContainer pc (out);
|
|
db::PolygonGenerator pg2 (pc, false, false);
|
|
db::BooleanOp op (db::BooleanOp::Or);
|
|
process (pg2, op);
|
|
for (std::vector <db::Polygon>::iterator p = out.begin (); p != out.end (); ++p) {
|
|
*p = p->sized (dx, dy, mode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::boolean (const std::vector<db::Polygon> &a, const std::vector<db::Polygon> &b, std::vector <db::Edge> &out, int mode)
|
|
{
|
|
clear ();
|
|
reserve (count_edges (a) + count_edges (b));
|
|
|
|
size_t n;
|
|
|
|
n = 0;
|
|
for (std::vector<db::Polygon>::const_iterator q = a.begin (); q != a.end (); ++q, n += 2) {
|
|
insert (*q, n);
|
|
}
|
|
|
|
n = 1;
|
|
for (std::vector<db::Polygon>::const_iterator q = b.begin (); q != b.end (); ++q, n += 2) {
|
|
insert (*q, n);
|
|
}
|
|
|
|
db::BooleanOp op ((db::BooleanOp::BoolOp) mode);
|
|
db::EdgeContainer ec (out);
|
|
process (ec, op);
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::boolean (const std::vector<db::Polygon> &a, const std::vector<db::Polygon> &b, std::vector <db::Polygon> &out, int mode, bool resolve_holes, bool min_coherence)
|
|
{
|
|
clear ();
|
|
reserve (count_edges (a) + count_edges (b));
|
|
|
|
size_t n;
|
|
|
|
n = 0;
|
|
if (&a == &out && &b != &out) {
|
|
while (! out.empty ()) {
|
|
insert (out.back (), n);
|
|
out.pop_back ();
|
|
n += 2;
|
|
}
|
|
} else {
|
|
for (std::vector<db::Polygon>::const_iterator q = a.begin (); q != a.end (); ++q, n += 2) {
|
|
insert (*q, n);
|
|
}
|
|
}
|
|
|
|
n = 1;
|
|
if (&b == &out) {
|
|
while (! out.empty ()) {
|
|
insert (out.back (), n);
|
|
out.pop_back ();
|
|
n += 2;
|
|
}
|
|
} else {
|
|
for (std::vector<db::Polygon>::const_iterator q = b.begin (); q != b.end (); ++q, n += 2) {
|
|
insert (*q, n);
|
|
}
|
|
}
|
|
|
|
db::BooleanOp op ((db::BooleanOp::BoolOp) mode);
|
|
db::PolygonContainer pc (out);
|
|
db::PolygonGenerator pg (pc, resolve_holes, min_coherence);
|
|
process (pg, op);
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::boolean (const std::vector<db::Edge> &a, const std::vector<db::Edge> &b, std::vector <db::Edge> &out, int mode)
|
|
{
|
|
clear ();
|
|
reserve (a.size () + b.size ());
|
|
|
|
insert_sequence (a.begin (), a.end (), 0);
|
|
insert_sequence (b.begin (), b.end (), 1);
|
|
|
|
db::BooleanOp op ((db::BooleanOp::BoolOp) mode);
|
|
db::EdgeContainer ec (out);
|
|
process (ec, op);
|
|
}
|
|
|
|
void
|
|
EdgeProcessor::boolean (const std::vector<db::Edge> &a, const std::vector<db::Edge> &b, std::vector <db::Polygon> &out, int mode, bool resolve_holes, bool min_coherence)
|
|
{
|
|
clear ();
|
|
reserve (a.size () + b.size ());
|
|
|
|
insert_sequence (a.begin (), a.end (), 0);
|
|
insert_sequence (b.begin (), b.end (), 1);
|
|
|
|
db::BooleanOp op ((db::BooleanOp::BoolOp) mode);
|
|
db::PolygonContainer pc (out);
|
|
db::PolygonGenerator pg (pc, resolve_holes, min_coherence);
|
|
process (pg, op);
|
|
}
|
|
|
|
} // namespace db
|
|
|