/* 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 #include #include #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::area_type sp1 = db::sprod (m_e, db::Edge (m_e.p1 (), a)); db::coord_traits::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 &a, const std::pair &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::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 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 cut_points; std::vector > 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 *cpvector, bool strong = true) { has_cutpoints = true; if (strong && !strong_cutpoints) { strong_cutpoints = true; if (! attractors.empty ()) { std::vector > attr; attractors.swap (attr); cut_points.reserve (cut_points.size () + attr.size ()); for (std::vector >::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) { 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 ::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 ::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 ::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::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::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 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 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 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 (); mp_cpvector = new std::vector (); } 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, std::vector ::iterator current, std::vector ::iterator future, db::Coord y, db::Coord yy, bool with_h) { std::sort (current, future, edge_xmin_compare ()); #ifdef DEBUG_EDGE_PROCESSOR printf ("y=%d..%d (90 degree)\n", y, yy); printf ("edges:"); for (std::vector ::iterator c1 = current; c1 != future; ++c1) { printf (" %s", c1->to_string().c_str ()); } printf ("\n"); #endif db::Coord x = edge_xmin (*current); std::vector ::iterator f = current; for (std::vector ::iterator c = current; c != future; ) { while (f != future && edge_xmin (*f) <= x) { ++f; } db::Coord xx = std::numeric_limits ::max (); if (f != future) { xx = edge_xmin (*f); } #ifdef DEBUG_EDGE_PROCESSOR printf ("edges %d..%d:", x, xx); for (std::vector ::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 ::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 ::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 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 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 ::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 inline double edge_xaty_double (db::edge 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 inline C edge_xmin_at_yinterval_double (const db::edge &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 inline C edge_xmax_at_yinterval_double (const db::edge &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 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 &a, const db::edge &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, std::vector ::iterator current, std::vector ::iterator future, db::Coord y, db::Coord yy, bool with_h) { std::vector p1_weak; // holds weak interactions of edge endpoints with other edges std::vector ip_weak; double dy = y - 0.5; double dyy = yy + 0.5; std::sort (current, future, edge_xmin_at_yinterval_double_compare (dy, dyy)); #ifdef DEBUG_EDGE_PROCESSOR printf ("y=%d..%d\n", y, yy); printf ("edges:"); for (std::vector ::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 ::iterator f = current; for (std::vector ::iterator c = current; c != future; ) { while (f != future && edge_xmin_at_yinterval_double (*f, dy, dyy) <= x) { ++f; } db::Coord xx = std::numeric_limits ::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 ::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 ::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 ::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 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 ::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 ::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 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 ::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 ::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::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::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 ::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 ::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 ::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 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 ()); y = edge_ymin ((*mp_work_edges) [0]); future = mp_work_edges->begin (); for (std::vector ::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 ::max (); } } while (future != mp_work_edges->end () && std::distance (current, future) < long (n + n / 2)); bool is90 = true; if (current != future) { for (std::vector ::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 ::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 ::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 ::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 ()); y = edge_ymin ((*mp_work_edges) [0]); size_t skip_unit = 1; future = mp_work_edges->begin (); for (std::vector ::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 ::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 ::max (); if (future != mp_work_edges->end ()) { yy = edge_ymin (*future); } for (std::vector ::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 ::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 ::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 ::iterator c0 = c; size_t n_res = 0; do { c->data = 0; std::vector ::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 ::iterator cc = c; cc != f; ) { std::vector ::iterator e = mp_work_edges->end (); int pn = 0, ps = 0; std::vector ::iterator cc0 = cc; std::vector ::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 ::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::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::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 ::iterator c = current; c != future; ++c) { printf ("%ld-", long (c->data)); } printf ("\n"); #endif std::vector ::iterator c0 = current; std::vector ::iterator last_interval = future; current = future; bool valid = true; for (std::vector ::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 ::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 &in, std::vector &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 &in, std::vector &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 &in, std::vector &edges, int mode) { clear (); reserve (count_edges (in)); for (std::vector::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 &in, std::vector &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::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 &in, std::vector &edges, unsigned int min_wc) { clear (); reserve (count_edges (in)); size_t n = 0; for (std::vector::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 &in, std::vector &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::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 &in, db::Coord dx, db::Coord dy, std::vector &out, unsigned int mode) { clear (); reserve (count_edges (in)); size_t n = 0; for (std::vector::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 &in, db::Coord dx, db::Coord dy, std::vector &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::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 ::iterator p = out.begin (); p != out.end (); ++p) { *p = p->sized (dx, dy, mode); } #endif } void EdgeProcessor::boolean (const std::vector &a, const std::vector &b, std::vector &out, int mode) { clear (); reserve (count_edges (a) + count_edges (b)); size_t n; n = 0; for (std::vector::const_iterator q = a.begin (); q != a.end (); ++q, n += 2) { insert (*q, n); } n = 1; for (std::vector::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 &a, const std::vector &b, std::vector &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::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::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 &a, const std::vector &b, std::vector &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 &a, const std::vector &b, std::vector &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