WIP: preparations for multi-output edge processor.

This commit is contained in:
Matthias Koefferlein 2020-06-14 14:16:15 +02:00
parent 4390a80dda
commit 688425c31d
2 changed files with 428 additions and 73 deletions

View File

@ -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> &cutpoints, std::vector
void
EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op)
{
std::vector<std::pair<db::EdgeSink *, db::EdgeEvaluatorBase *> > 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<db::Coord>::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<db::Coord>::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<std::pair<db::EdgeSink *, db::EdgeEvaluatorBase *> > &procs)
: m_selects_edges (false), m_prefer_touch (false)
{
m_states.reserve (procs.size ());
for (std::vector<std::pair<db::EdgeSink *, db::EdgeEvaluatorBase *> >::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<EdgeProcessorState>::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<EdgeProcessorState>::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<EdgeProcessorState>::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<EdgeProcessorState>::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<EdgeProcessorState>::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<EdgeProcessorState>::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<EdgeProcessorState>::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<EdgeProcessorState>::iterator s = m_states.begin (); s != m_states.end (); ++s) {
s->next_vertex (x);
}
}
/**
* @brief Finishes the vertex
*/
void end_vertex ()
{
for (std::vector<EdgeProcessorState>::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<EdgeProcessorState>::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<EdgeProcessorState>::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<EdgeProcessorState>::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<EdgeProcessorState>::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<EdgeProcessorState>::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<EdgeProcessorState>::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<EdgeProcessorState>::iterator s = m_states.begin (); s != m_states.end (); ++s) {
s->skip_n (n);
}
}
private:
std::vector<EdgeProcessorState> m_states;
bool m_selects_edges, m_prefer_touch;
};
}
void
EdgeProcessor::process (const std::vector<std::pair<db::EdgeSink *, db::EdgeEvaluatorBase *> > &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 <WorkEdge>::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<db::Coord> ());
@ -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 <WorkEdge>::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 <WorkEdge>::iterator cc = c; cc != f; ) {
std::vector <WorkEdge>::iterator e = mp_work_edges->end ();
gs.next_coincident ();
int pn = 0, ps = 0;
std::vector <WorkEdge>::iterator e = mp_work_edges->end ();
std::vector <WorkEdge>::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 <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
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<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;
}
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<db::Coord>::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 ();
}

View File

@ -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<std::pair<db::EdgeSink *, db::EdgeEvaluatorBase *> > &gen);
/**
* @brief Merge the given polygons in a simple "non-zero wrapcount" fashion
*