diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 743defce5..2e258f6a7 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -446,7 +446,7 @@ static inline double edge_xaty2 (db::Edge e, db::Coord y) } /** - * @brief A compare operator for edged + * @brief A compare operator for edges * 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 @@ -1539,11 +1539,398 @@ get_intersections_per_band_any (std::vector &cutpoints, std::vector void EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) +{ + std::vector > procs; + procs.push_back (std::make_pair (&es, &op)); + process (procs); +} + +namespace +{ + +class EdgeProcessorState +{ +public: + EdgeProcessorState (db::EdgeSink *es, db::EdgeEvaluatorBase *op) + : mp_es (es), mp_op (op), m_vertex (false), + m_x (0), m_y (0), m_hx (0), m_ho (0), m_pn (0), m_ps (0) + { } + + void start () + { + mp_es->start (); + } + + void flush () + { + mp_es->flush (); + } + + void reset () + { + mp_op->reset (); + } + + bool is_reset () + { + return mp_op->is_reset (); + } + + void reserve (size_t n) + { + mp_op->reserve (n); + } + + void begin_scanline (db::Coord y) + { + m_y = y; + m_x = 0; + m_hx = 0; + m_ho = 0; + m_vertex = false; + mp_es->begin_scanline (y); + } + + void end_scanline (db::Coord y) + { + mp_es->end_scanline (y); + } + + void next_vertex (double x) + { + m_x = db::coord_traits::rounded (x); + m_vertex = false; + } + + void end_vertex () + { + if (m_vertex) { + m_hx = m_x; + m_ho = mp_op->compare_ns (); + } + } + + void next_coincident () + { + m_pn = m_ps = 0; + } + + void end_coincident () + { + if (! m_vertex && (m_ps != 0 || m_pn != 0)) { + + if (m_ho != 0) { + db::Edge he (db::Point (m_hx, m_y), db::Point (db::coord_traits::rounded (m_x), m_y)); + if (m_ho > 0) { + he.swap_points (); + } + mp_es->put (he); +#ifdef DEBUG_EDGE_PROCESSOR + printf ("put(%s)\n", he.to_string ().c_str ()); +#endif + } + + m_vertex = true; + + } + } + + void north_edge (bool prefer_touch, EdgeEvaluatorBase::property_type prop) + { + m_pn += mp_op->edge (true, prefer_touch, prop); + } + + void south_edge (bool prefer_touch, EdgeEvaluatorBase::property_type prop) + { + m_ps += mp_op->edge (false, prefer_touch, prop); + } + + void select_edge (const WorkEdge &e) + { + if (mp_op->select_edge (e.dy () == 0, e.prop)) { + mp_es->put (e); +#ifdef DEBUG_EDGE_PROCESSOR + printf ("put(%s)\n", e.to_string().c_str()); +#endif + } + } + + bool push_edge (const db::Edge &e) + { + if (m_pn != 0) { + + db::Edge edge (e); + if ((m_pn > 0 && edge.dy () < 0) || (m_pn < 0 && edge.dy () > 0)) { + edge.swap_points (); + } + + if (edge_ymin (edge) == m_y) { + mp_es->put (edge); +#ifdef DEBUG_EDGE_PROCESSOR + printf ("put(%s)\n", edge.to_string().c_str()); +#endif + } else { + mp_es->crossing_edge (edge); +#ifdef DEBUG_EDGE_PROCESSOR + printf ("xing(%s)\n", edge.to_string().c_str()); +#endif + } + + return true; + + } else { + return false; + } + } + + void skip_n (size_t n) + { + mp_es->skip_n (n); + } + +private: + db::EdgeSink *mp_es; + db::EdgeEvaluatorBase *mp_op; + + bool m_vertex; + db::Coord m_x, m_y, m_hx; + int m_ho; + int m_pn, m_ps; +}; + +class EdgeProcessorStates +{ +public: + EdgeProcessorStates (const std::vector > &procs) + : m_selects_edges (false), m_prefer_touch (false) + { + m_states.reserve (procs.size ()); + for (std::vector >::const_iterator p = procs.begin (); p != procs.end (); ++p) { + + m_states.push_back (EdgeProcessorState (p->first, p->second)); + + if (p->second->selects_edges ()) { + m_selects_edges = true; + } + + if (p->second->prefer_touch ()) { + m_prefer_touch = true; + } + + } + } + + /** + * @brief Returns true if the processors want to select edges + */ + bool selects_edges () const + { + return m_selects_edges; + } + + /** + * @brief Returns true if the processors prefer touching mode + */ + bool prefer_touch () const + { + return m_prefer_touch; + } + + /** + * @brief Initial event + * This method is called when the scan is initiated + */ + void start () + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->start (); + } + } + + /** + * @brief Final event + * This method is called after the scan terminated + */ + void flush () + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->flush (); + } + } + + /** + * @brief Reset status event + * This method is to ensure the state of the operator is reset. + */ + void reset () + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->reset (); + } + } + + /** + * @brief Gets a value indicating whether all operators are reset + */ + bool is_reset () + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + if (! s->is_reset ()) { + return false; + } + } + return true; + } + + /** + * @brief Reserve memory n edges + */ + void reserve (size_t n) + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->reserve (n); + } + } + + /** + * @brief Begin scanline event + * This method is called at the beginning of a new scanline + */ + void begin_scanline (db::Coord y) + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->begin_scanline (y); + } + } + + /** + * @brief End scanline event + * This method is called at the end of a scanline + */ + void end_scanline (db::Coord y) + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->end_scanline (y); + } + } + + /** + * @brief Announces a batch of edges crossing the same point + */ + void next_vertex (double x) + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->next_vertex (x); + } + } + + /** + * @brief Finishes the vertex + */ + void end_vertex () + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->end_vertex (); + } + } + + /** + * @brief Announces a batch of edges crossing the same point and begin coincident + * This event is a sub-event of "next_vertex". + */ + void next_coincident () + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->next_coincident (); + } + } + + /** + * @brief Announces a batch of edges crossing the same point and begin coincident + * This event is a sub-event of "next_vertex". + */ + void end_coincident () + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->end_coincident (); + } + } + + /** + * @brief Announces an edge north to the scanline + */ + void north_edge (bool prefer_touch, EdgeEvaluatorBase::property_type prop) + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->north_edge (prefer_touch, prop); + } + } + + /** + * @brief Announces an edge south of the scanline + */ + void south_edge (bool prefer_touch, EdgeEvaluatorBase::property_type prop) + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->south_edge (prefer_touch, prop); + } + } + + /** + * @brief Gives the generators an opportunity to select the given edge + */ + void select_edge (const WorkEdge &e) + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->select_edge (e); + } + } + + /** + * @brief Delivers an edge to the edge sink if present + * + * This method will return true if at least one of the edge sinks received the edge + */ + bool push_edge (const db::Edge &e) + { + bool any = false; + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + if (s->push_edge (e)) { + any = true; + } + } + return any; + } + + /** + * @brief Skips n edges on the edge sink + * + * This is for optimization of the polygon generation. Stitching of edges does not happen if + * there are no news. + */ + void skip_n (size_t n) + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + s->skip_n (n); + } + } + +private: + std::vector m_states; + bool m_selects_edges, m_prefer_touch; +}; + +} + +void +EdgeProcessor::process (const std::vector > &gen) { tl::SelfTimer timer (tl::verbosity () >= m_base_verbosity, "EdgeProcessor: process"); - bool prefer_touch = op.prefer_touch (); - bool selects_edges = op.selects_edges (); + EdgeProcessorStates gs (gen); + + bool prefer_touch = gs.prefer_touch (); + bool selects_edges = gs.selects_edges (); db::Coord y; std::vector ::iterator future; @@ -1551,8 +1938,8 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) // step 1: preparation if (mp_work_edges->empty ()) { - es.start (); - es.flush (); + gs.start (); + gs.flush (); return; } @@ -1759,10 +2146,10 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) // 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) + gs.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); + gs.reset (); + gs.reserve (n_props); std::sort (mp_work_edges->begin (), mp_work_edges->end (), edge_ymin_compare ()); @@ -1795,9 +2182,9 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) } db::Coord ysl = y; - es.begin_scanline (y); + gs.begin_scanline (y); - tl_assert (op.is_reset ()); // HINT: for development + tl_assert (gs.is_reset ()); // HINT: for development if (current != future) { @@ -1810,9 +2197,6 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) 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; ) { @@ -1823,11 +2207,12 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) printf ("X %ld->%d,%d\n", long (c->data), int (skip), int (skip_res)); #endif + // @@@ can't work with the multi-output scheme!!!! if (skip != 0 && (c + skip >= future || (c + skip)->data != 0)) { tl_assert (c + skip <= future); - es.skip_n (skip_res); + gs.skip_n (skip_res); c->data = skip + new_skip_unit * skip_res; @@ -1859,14 +2244,14 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) // compute edges that occure at this vertex - bool vertex = false; + gs.next_vertex (x); // 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 (); + gs.next_coincident (); - int pn = 0, ps = 0; + std::vector ::iterator e = mp_work_edges->end (); std::vector ::iterator cc0 = cc; @@ -1899,10 +2284,10 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) if ((cc->dy () > 0) == prefer_touch) { if (edge_ymax (*cc) > y) { - pn += op.edge (true, prefer_touch, cc->prop); + gs.north_edge (prefer_touch, cc->prop); } if (edge_ymin (*cc) < y) { - ps += op.edge (false, prefer_touch, cc->prop); + gs.south_edge (prefer_touch, cc->prop); } } @@ -1914,16 +2299,11 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) // 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 + if (edge_ymin (*sc) == y) { + gs.select_edge (*sc); } } - } // report the closing or opening edges in the opposite order @@ -1936,67 +2316,30 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) if (fc->dy () != 0 && (fc->dy () > 0) != prefer_touch) { if (edge_ymax (*fc) > y) { - pn += op.edge (true, ! prefer_touch, fc->prop); + gs.north_edge (! prefer_touch, fc->prop); } if (edge_ymin (*fc) < y) { - ps += op.edge (false, ! prefer_touch, fc->prop); + gs.south_edge (! 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; - - } + gs.end_coincident (); 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) { + if (gs.push_edge (*e)) { ++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 (); - } + gs.end_vertex (); c = f; - } while (c != future && ! op.is_reset ()); + } while (c != future && ! gs.is_reset ()); // TODO: assert that there is no overflow here: c0->data = size_t (std::distance (c0, c) + new_skip_unit * n_res); @@ -2057,13 +2400,13 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) } - tl_assert (op.is_reset ()); // HINT: for development (second) + tl_assert (gs.is_reset ()); // HINT: for development (second) - es.end_scanline (ysl); + gs.end_scanline (ysl); } - es.flush (); + gs.flush (); } diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index a843e7177..7aff70ea8 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -663,10 +663,22 @@ public: void clear (); /** - * @brief Process the edges stored currently + * @brief Performs the actual processing + * + * This method will use the edges stored so far and runs it through the + * scanline algorithm. */ void process (db::EdgeSink &es, EdgeEvaluatorBase &op); + /** + * @brief Performs the actual processing + * + * This version allows giving multiple edge sinks and evaluators. + * Each evaluator is worked on separately and feeds the corresponding + * edge sink. + */ + void process (const std::vector > &gen); + /** * @brief Merge the given polygons in a simple "non-zero wrapcount" fashion *