From 98792c55de74aabd85ee985d6d643617e4d1ab77 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 2 Jan 2021 19:34:46 +0100 Subject: [PATCH] WIP: attempt to implement negative edge output on width and space --- src/db/db/dbCompoundOperation.cc | 177 ++++++++---------- src/db/db/dbCompoundOperation.h | 20 ++ src/db/db/dbRegionUtils.cc | 153 ++++++++++----- src/db/db/dbRegionUtils.h | 8 + src/db/db/gsiDeclDbCompoundOperation.cc | 82 +++++++- src/db/unit_tests/dbRegionUtilsTests.cc | 237 ++++++++++++++++++++++++ src/db/unit_tests/unit_tests.pro | 1 + 7 files changed, 527 insertions(+), 151 deletions(-) create mode 100644 src/db/unit_tests/dbRegionUtilsTests.cc diff --git a/src/db/db/dbCompoundOperation.cc b/src/db/db/dbCompoundOperation.cc index 573b8de29..45852d596 100644 --- a/src/db/db/dbCompoundOperation.cc +++ b/src/db/db/dbCompoundOperation.cc @@ -566,41 +566,16 @@ run_poly_bool (CompoundRegionGeometricalBoolOperationNode::GeometricalOp op, db: // TODO: it's more efficient to feed the EP directly for polygon-to-polygon bools db::Region ra, rb; init_region (ra, a); + init_region (rb, b); - if (ra.empty ()) { - - if (op == CompoundRegionGeometricalBoolOperationNode::And || op == CompoundRegionGeometricalBoolOperationNode::Not) { - write_result (layout, res, ra); - } else if (op == CompoundRegionGeometricalBoolOperationNode::Or || op == CompoundRegionGeometricalBoolOperationNode::Xor) { - init_region (rb, b); - write_result (layout, res, rb); - } - - } else { - - init_region (rb, b); - if (rb.empty ()) { - - if (op == CompoundRegionGeometricalBoolOperationNode::And) { - write_result (layout, res, rb); - } else { - write_result (layout, res, ra); - } - - } else { - - if (op == CompoundRegionGeometricalBoolOperationNode::And) { - write_result (layout, res, ra & rb); - } else if (op == CompoundRegionGeometricalBoolOperationNode::Or) { - write_result (layout, res, ra + rb); - } else if (op == CompoundRegionGeometricalBoolOperationNode::Xor) { - write_result (layout, res, ra ^ rb); - } else if (op == CompoundRegionGeometricalBoolOperationNode::Not) { - write_result (layout, res, ra - rb); - } - - } - + if (op == CompoundRegionGeometricalBoolOperationNode::And) { + write_result (layout, res, ra & rb); + } else if (op == CompoundRegionGeometricalBoolOperationNode::Or) { + write_result (layout, res, ra + rb); + } else if (op == CompoundRegionGeometricalBoolOperationNode::Xor) { + write_result (layout, res, ra ^ rb); + } else if (op == CompoundRegionGeometricalBoolOperationNode::Not) { + write_result (layout, res, ra - rb); } } @@ -629,21 +604,12 @@ run_poly_vs_edge_bool (CompoundRegionGeometricalBoolOperationNode::GeometricalOp init_region (ra, a); db::Edges eb; + init_edges (eb, b); - if (ra.empty ()) { - + if (eb.empty ()) { write_result (layout, res, eb); - } else { - - init_edges (eb, b); - - if (eb.empty ()) { - write_result (layout, res, eb); - } else { - write_result (layout, res, eb & ra); - } - + write_result (layout, res, eb & ra); } } @@ -670,22 +636,14 @@ run_edge_vs_poly_bool (CompoundRegionGeometricalBoolOperationNode::GeometricalOp db::Edges ea; init_edges (ea, a); - if (ea.empty ()) { - - write_result (layout, res, ea); - - } else { - - // TODO: it's more efficient to feed the EP directly for polygon-to-polygon bools - db::Region rb; - init_region (rb, b); - - if (op == CompoundRegionGeometricalBoolOperationNode::And) { - write_result (layout, res, ea & rb); - } else if (op == CompoundRegionGeometricalBoolOperationNode::Not) { - write_result (layout, res, ea - rb); - } + // TODO: it's more efficient to feed the EP directly for polygon-to-polygon bools + db::Region rb; + init_region (rb, b); + if (op == CompoundRegionGeometricalBoolOperationNode::And) { + write_result (layout, res, ea & rb); + } else if (op == CompoundRegionGeometricalBoolOperationNode::Not) { + write_result (layout, res, ea - rb); } } @@ -706,45 +664,30 @@ run_bool (CompoundRegionGeometricalBoolOperationNode::GeometricalOp op, db::Layo { db::Edges ea, eb; init_edges (ea, a); + init_edges (eb, b); - if (ea.empty ()) { - - if (op == CompoundRegionGeometricalBoolOperationNode::And || op == CompoundRegionGeometricalBoolOperationNode::Not) { - write_result (layout, res, ea); - } else if (op == CompoundRegionGeometricalBoolOperationNode::Or || op == CompoundRegionGeometricalBoolOperationNode::Xor) { - init_edges (eb, b); - write_result (layout, res, eb); - } - - } else { - - init_edges (eb, b); - - if (eb.empty ()) { - - if (op == CompoundRegionGeometricalBoolOperationNode::And) { - write_result (layout, res, eb); - } else { - write_result (layout, res, ea); - } - - } else { - - if (op == CompoundRegionGeometricalBoolOperationNode::And) { - write_result (layout, res, ea & eb); - } else if (op == CompoundRegionGeometricalBoolOperationNode::Or) { - write_result (layout, res, ea + eb); - } else if (op == CompoundRegionGeometricalBoolOperationNode::Xor) { - write_result (layout, res, ea ^ eb); - } else if (op == CompoundRegionGeometricalBoolOperationNode::Not) { - write_result (layout, res, ea - eb); - } - - } - + if (op == CompoundRegionGeometricalBoolOperationNode::And) { + write_result (layout, res, ea & eb); + } else if (op == CompoundRegionGeometricalBoolOperationNode::Or) { + write_result (layout, res, ea + eb); + } else if (op == CompoundRegionGeometricalBoolOperationNode::Xor) { + write_result (layout, res, ea ^ eb); + } else if (op == CompoundRegionGeometricalBoolOperationNode::Not) { + write_result (layout, res, ea - eb); } } +template +static void copy_results (std::vector > &res, const std::vector > &in) +{ + res.front ().insert (in.front ().begin (), in.front ().end ()); +} + +template +static void copy_results (std::vector > &, const std::vector > &) +{ + // don't copy +} template void @@ -756,13 +699,45 @@ CompoundRegionGeometricalBoolOperationNode::implement_bool (db::Layout *layout, shape_interactions computed_a; child (0)->compute_local (layout, interactions_for_child (interactions, 0, computed_a), one_a, max_vertex_count, area_ratio); - std::vector > one_b; - one_b.push_back (std::unordered_set ()); + if (one_a.front ().empty ()) { - shape_interactions computed_b; - child (1)->compute_local (layout, interactions_for_child (interactions, 1, computed_b), one_b, max_vertex_count, area_ratio); + if (m_op == GeometricalOp::And || m_op == GeometricalOp::Not) { - run_bool (m_op, layout, one_a.front (), one_b.front (), results.front ()); + // .. no results .. + + } else { + + std::vector > one_b; + one_b.push_back (std::unordered_set ()); + + shape_interactions computed_b; + child (1)->compute_local (layout, interactions_for_child (interactions, 1, computed_b), one_b, max_vertex_count, area_ratio); + + copy_results (results, one_b); + + } + + } else { + + std::vector > one_b; + one_b.push_back (std::unordered_set ()); + + shape_interactions computed_b; + child (1)->compute_local (layout, interactions_for_child (interactions, 1, computed_b), one_b, max_vertex_count, area_ratio); + + if (one_b.front ().empty ()) { + + if (m_op != GeometricalOp::And) { + copy_results (results, one_a); + } + + } else { + + run_bool (m_op, layout, one_a.front (), one_b.front (), results.front ()); + + } + + } } template diff --git a/src/db/db/dbCompoundOperation.h b/src/db/db/dbCompoundOperation.h index c9ee71d21..fdbe0028c 100644 --- a/src/db/db/dbCompoundOperation.h +++ b/src/db/db/dbCompoundOperation.h @@ -245,6 +245,26 @@ struct compound_operation_type_traits }; +class DB_PUBLIC CompoundRegionOperationEmptyNode + : public CompoundRegionOperationNode +{ +public: + CompoundRegionOperationEmptyNode (ResultType type) + : CompoundRegionOperationNode (), m_type (type) + { } + + virtual ~CompoundRegionOperationEmptyNode () + { } + + virtual std::vector inputs () const { return std::vector (); } + virtual db::Coord dist () const { return 0; } + virtual ResultType result_type () const { return m_type; } + +private: + ResultType m_type; +}; + + class DB_PUBLIC CompoundRegionOperationPrimaryNode : public CompoundRegionOperationNode { diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc index 8cfc5c176..8927422c1 100644 --- a/src/db/db/dbRegionUtils.cc +++ b/src/db/db/dbRegionUtils.cc @@ -34,7 +34,7 @@ namespace db Edge2EdgeCheckBase::Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding) : mp_check (&check), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons), - m_with_shielding (with_shielding), m_has_edge_pair_output (true), m_has_negative_edge_output (false), m_pass (0) + m_first_pseudo (std::numeric_limits::max ()), m_with_shielding (with_shielding), m_has_edge_pair_output (true), m_has_negative_edge_output (false), m_pass (0) { m_distance = check.distance (); } @@ -46,13 +46,20 @@ Edge2EdgeCheckBase::prepare_next_pass () if (m_pass == 1) { - if ((m_with_shielding || m_has_negative_edge_output) && ! m_ep.empty ()) { + m_first_pseudo = m_ep.size (); + + if (m_with_shielding && ! m_ep.empty ()) { m_ep_discarded.resize (m_ep.size (), false); // second pass: return true; + } else if (m_has_negative_edge_output) { + + // second pass: + return true; + } } @@ -61,7 +68,7 @@ Edge2EdgeCheckBase::prepare_next_pass () std::vector::const_iterator d = m_ep_discarded.begin (); std::vector::const_iterator ep = m_ep.begin (); - while (ep != m_ep.end ()) { + while (ep != m_ep.end () && size_t (ep - m_ep.begin ()) < m_first_pseudo) { bool use_result = true; if (d != m_ep_discarded.end ()) { use_result = ! *d; @@ -96,12 +103,58 @@ static inline bool shields (const db::EdgePair &ep, const db::Edge &q) void Edge2EdgeCheckBase::finish (const Edge *o, const size_t &p) { - if (m_has_negative_edge_output && m_pass == 1) { + if (m_has_negative_edge_output && m_pass == 1 && m_pseudo_edges.find (std::make_pair (*o, p)) == m_pseudo_edges.end ()) { - // no interaction at all: create a single-edged edge pair - int l = int (p & size_t (1)); - put_negative (*o, l); + std::pair k (*o, p); + std::multimap, size_t>::const_iterator i0 = m_e2ep.find (k); + bool fully_removed = false; + bool any = false; + for (std::multimap, size_t>::const_iterator i = i0; ! fully_removed && i != m_e2ep.end () && i->first == k; ++i) { + size_t n = i->second / 2; + if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) { + any = true; + fully_removed = (((i->second & 1) == 0 ? m_ep [n].first () : m_ep [n].second ()) == *o); + } + } + + if (! any) { + + put_negative (*o, p); + + } else if (! fully_removed) { + + std::set partial_edges; + + db::EdgeBooleanCluster > ec (&partial_edges, db::EdgeNot); + ec.add (o, 0); + + for (std::multimap, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) { + size_t n = i->second / 2; + if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) { + ec.add (((i->second & 1) == 0 ? &m_ep [n].first () : &m_ep [n].second ()), 1); + } + } + + ec.finish (); + + for (std::set::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) { + put_negative (*e, p); + } + + } + + } +} + +void +Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner &scanner) +{ + if (m_pass == 1) { + for (std::set >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) { +// @@@printf("@@@ PSEUDO %s[%d]\n", e->first.to_string().c_str(), int(e->second)); fflush(stdout); + scanner.insert (&e->first, e->second); + } } } @@ -118,16 +171,26 @@ Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size int l1 = int (p1 & size_t (1)); int l2 = int (p2 & size_t (1)); + if (l1 > l2) { + std::swap (o1, o2); + std::swap (p1, p2); + } + db::EdgePair ep; - if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { + if (mp_check->check (*o1, *o2, &ep)) { // found a violation: store inside the local buffer for now. In the second // pass we will eliminate those which are shielded completely (with shielding) // and/or compute the negative edges. size_t n = m_ep.size (); m_ep.push_back (ep); - m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n)); - m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n)); + m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2)); + m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1)); + + if (m_has_negative_edge_output) { + m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p1)); + m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p2)); + } } @@ -152,7 +215,10 @@ Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size std::pair k (*o1, p1); for (std::multimap, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) { - n1.push_back (i->second); + size_t n = i->second / 2; + if (n < m_first_pseudo && ! m_ep_discarded [n]) { + n1.push_back (n); + } } std::sort (n1.begin (), n1.end ()); @@ -169,11 +235,9 @@ Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn)); for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { - if (! m_ep_discarded [*i]) { - db::EdgePair ep = m_ep [*i].normalized (); - if (shields (ep, *o2)) { - m_ep_discarded [*i] = true; - } + db::EdgePair ep = m_ep [*i].normalized (); + if (shields (ep, *o2)) { + m_ep_discarded [*i] = true; } } @@ -185,45 +249,34 @@ Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size } - // prepare the negative edge output - if (m_has_negative_edge_output) { + // for negative output edges are cancelled by short interactions perpendicular to them + if (m_has_negative_edge_output && + (m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end () || m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ()) && + ! (m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end () && m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ())) { +//printf("@@@ PASS %d -> %s[%d] x %s[%d]\n", m_pass, o1->to_string().c_str(), int(p1), o2->to_string().c_str(), int(p2)); fflush(stdout); // Overlap or inside checks require input from different layers if ((! m_different_polygons || p1 != p2) && (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0)) { - for (int p = 0; p < 2; ++p) { + // ensure that the first check argument is of layer 1 and the second of + // layer 2 (unless both are of the same layer) + int l1 = int (p1 & size_t (1)); + int l2 = int (p2 & size_t (1)); - std::pair k (*o1, p1); - std::multimap, size_t>::const_iterator i0 = m_e2ep.find (k); + if (l1 > l2) { + std::swap (o1, o2); + std::swap (p1, p2); + } +//printf("@@@1\n"); fflush(stdout); - bool fully_removed = false; - for (std::multimap, size_t>::const_iterator i = i0; ! fully_removed && i != m_e2ep.end () && i->first == k; ++i) { - fully_removed = (m_ep [i->second].first () == *o1); - } + db::EdgePair ep; + if (mp_check->check (*o1, *o2, &ep)) { - if (! fully_removed) { - - std::set partial_edges; - - db::EdgeBooleanCluster > ec (&partial_edges, db::EdgeNot); - ec.add (o1, 0); - - for (std::multimap, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) { - if (i->second >= m_ep_discarded.size () || !m_ep_discarded [i->second]) { - ec.add (&m_ep [i->second].first (), 1); - } - } - - ec.finish (); - - for (std::set::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) { - put_negative (*e, p); - } - - std::swap (o1, o2); - std::swap (p1, p2); - - } + size_t n = m_ep.size (); + m_ep.push_back (ep); + m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2)); + m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1)); +//printf("@@@ CANCEL %s\n", ep.to_string().c_str()); fflush(stdout); } @@ -326,6 +379,8 @@ poly2poly_check_base::enter (const PolygonType &o, size_t p) m_scanner.insert (& m_edges.back (), p); } + mp_output->feed_pseudo_edges (m_scanner); + tl_assert (m_edges.size () == vertices (o)); m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); @@ -362,6 +417,8 @@ poly2poly_check_base::enter (const PolygonType &o1, size_t p1, cons m_scanner.insert (& m_edges.back (), p2); } + mp_output->feed_pseudo_edges (m_scanner); + tl_assert (m_edges.size () == vertices (o1) + vertices (o2)); // temporarily disable intra-polygon check in that step .. we do that later in finish() diff --git a/src/db/db/dbRegionUtils.h b/src/db/db/dbRegionUtils.h index 8c1024d48..a9253c1c3 100644 --- a/src/db/db/dbRegionUtils.h +++ b/src/db/db/dbRegionUtils.h @@ -482,6 +482,12 @@ public: */ bool prepare_next_pass (); + /** + * @brief Before the scanner is run, this method must be called to feed additional edges into the scanner + * (required for negative edge output - cancellation of perpendicular edges) + */ + void feed_pseudo_edges (db::box_scanner &scanner); + /** * @brief Reimplementation of the box_scanner_receiver interface */ @@ -551,6 +557,8 @@ private: EdgeRelationFilter::distance_type m_distance; std::vector m_ep; std::multimap, size_t> m_e2ep; + std::set > m_pseudo_edges; + size_t m_first_pseudo; std::vector m_ep_discarded; bool m_with_shielding; bool m_has_edge_pair_output; diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index 018d5cca6..0bf764ea2 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -28,10 +28,29 @@ #include "dbEdgesUtils.h" #include "dbRegionLocalOperations.h" #include "dbShapeCollectionUtils.h" +#include "tlString.h" namespace gsi { +template +static void check_non_null (P *p, const char *arg) +{ + if (!p) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Argument %s must not be null")), arg)); + } +} + +template +static void check_non_null (const std::vector

&pp, const char *arg) +{ + for (typename std::vector

::const_iterator p = pp.begin (); p != pp.end (); ++p) { + if (!*p) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Arguments %s must not be null")), arg)); + } + } +} + static db::CompoundRegionOperationNode *new_primary () { return new db::CompoundRegionOperationPrimaryNode (); @@ -39,16 +58,25 @@ static db::CompoundRegionOperationNode *new_primary () static db::CompoundRegionOperationNode *new_secondary (db::Region *region) { + check_non_null (region, "region"); return new db::CompoundRegionOperationSecondaryNode (region); } +static db::CompoundRegionOperationNode *new_empty (db::CompoundRegionOperationNode::ResultType type) +{ + return new db::CompoundRegionOperationEmptyNode (type); +} + static db::CompoundRegionOperationNode *new_logical_boolean (db::CompoundRegionLogicalBoolOperationNode::LogicalOp op, bool invert, const std::vector &inputs) { + check_non_null (inputs, "inputs"); return new db::CompoundRegionLogicalBoolOperationNode (op, invert, inputs); } static db::CompoundRegionOperationNode *new_geometrical_boolean (db::CompoundRegionGeometricalBoolOperationNode::GeometricalOp op, db::CompoundRegionOperationNode *a, db::CompoundRegionOperationNode *b) { + check_non_null (a, "a"); + check_non_null (b, "b"); // TODO: is this correct? if ((a->result_type () != db::CompoundRegionOperationNode::Region && a->result_type () != db::CompoundRegionOperationNode::Edges) || (b->result_type () != db::CompoundRegionOperationNode::Region && b->result_type () != db::CompoundRegionOperationNode::Edges)) { @@ -59,6 +87,8 @@ static db::CompoundRegionOperationNode *new_geometrical_boolean (db::CompoundReg static db::CompoundRegionOperationNode *new_interacting (db::CompoundRegionOperationNode *a, db::CompoundRegionOperationNode *b, bool inverse, size_t min_count, size_t max_count) { + check_non_null (a, "a"); + check_non_null (b, "b"); // TODO: is this correct? if (a->result_type () != db::CompoundRegionOperationNode::Region) { throw tl::Exception ("Primary input for interaction compound operation must be of Region type"); @@ -74,6 +104,8 @@ static db::CompoundRegionOperationNode *new_interacting (db::CompoundRegionOpera static db::CompoundRegionOperationNode *new_overlapping (db::CompoundRegionOperationNode *a, db::CompoundRegionOperationNode *b, bool inverse, size_t min_count, size_t max_count) { + check_non_null (a, "a"); + check_non_null (b, "b"); // TODO: is this correct? if (a->result_type () != db::CompoundRegionOperationNode::Region) { throw tl::Exception ("Primary input for interaction compound operation must be of Region type"); @@ -87,6 +119,8 @@ static db::CompoundRegionOperationNode *new_overlapping (db::CompoundRegionOpera static db::CompoundRegionOperationNode *new_enclosing (db::CompoundRegionOperationNode *a, db::CompoundRegionOperationNode *b, bool inverse, size_t min_count, size_t max_count) { + check_non_null (a, "a"); + check_non_null (b, "b"); // TODO: is this correct? if (a->result_type () != db::CompoundRegionOperationNode::Region) { throw tl::Exception ("Primary input for interaction compound operation must be of Region type"); @@ -100,6 +134,8 @@ static db::CompoundRegionOperationNode *new_enclosing (db::CompoundRegionOperati static db::CompoundRegionOperationNode *new_inside (db::CompoundRegionOperationNode *a, db::CompoundRegionOperationNode *b, bool inverse) { + check_non_null (a, "a"); + check_non_null (b, "b"); // TODO: is this correct? if (a->result_type () != db::CompoundRegionOperationNode::Region) { throw tl::Exception ("Primary input for interaction compound operation must be of Region type"); @@ -113,6 +149,8 @@ static db::CompoundRegionOperationNode *new_inside (db::CompoundRegionOperationN static db::CompoundRegionOperationNode *new_outside (db::CompoundRegionOperationNode *a, db::CompoundRegionOperationNode *b, bool inverse) { + check_non_null (a, "a"); + check_non_null (b, "b"); // TODO: is this correct? if (a->result_type () != db::CompoundRegionOperationNode::Region) { throw tl::Exception ("Primary input for interaction compound operation must be of Region type"); @@ -126,46 +164,55 @@ static db::CompoundRegionOperationNode *new_outside (db::CompoundRegionOperation static db::CompoundRegionOperationNode *new_hulls (db::CompoundRegionOperationNode *input) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::HullExtractionProcessor (), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_holes (db::CompoundRegionOperationNode *input) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::HolesExtractionProcessor (), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_strange_polygons_filter (db::CompoundRegionOperationNode *input) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::StrangePolygonCheckProcessor (), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_smoothed (db::CompoundRegionOperationNode *input, db::Coord d) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::SmoothingProcessor (d), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_rounded_corners (db::CompoundRegionOperationNode *input, double rinner, double router, unsigned int n) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::RoundedCornersProcessor (rinner, router, n), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_case (const std::vector &inputs) { + check_non_null (inputs, "inputs"); return new db::CompoundRegionLogicalCaseSelectOperationNode (inputs); } static db::CompoundRegionOperationNode *new_corners_as_rectangles (db::CompoundRegionOperationNode *input, double angle_start, double angle_end, db::Coord dim = 1) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::CornersAsRectangles (angle_start, angle_end, dim), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_corners_as_dots (db::CompoundRegionOperationNode *input, double angle_start, double angle_end) { + check_non_null (input, "input"); return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, angle_end), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_extents (db::CompoundRegionOperationNode *input, db::Coord e) { + check_non_null (input, "input"); if (input->result_type () == db::CompoundRegionOperationNode::EdgePairs) { return new db::CompoundRegionEdgePairToPolygonProcessingOperationNode (new db::extents_processor (e, e), input, true /*processor is owned*/); } else if (input->result_type () == db::CompoundRegionOperationNode::EdgePairs) { @@ -180,56 +227,67 @@ static db::CompoundRegionOperationNode *new_extents (db::CompoundRegionOperation static db::CompoundRegionOperationNode *new_relative_extents (db::CompoundRegionOperationNode *input, double fx1, double fy1, double fx2, double fy2, db::Coord dx, db::Coord dy) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::RelativeExtents (fx1, fy1, fx2, fy2, dx, dy), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_relative_extents_as_edges (db::CompoundRegionOperationNode *input, double fx1, double fy1, double fx2, double fy2) { + check_non_null (input, "input"); return new db::CompoundRegionToEdgeProcessingOperationNode (new db::RelativeExtentsAsEdges (fx1, fy1, fx2, fy2), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_convex_decomposition (db::CompoundRegionOperationNode *input, db::PreferredOrientation mode) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::ConvexDecomposition (mode), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_trapezoid_decomposition (db::CompoundRegionOperationNode *input, db::TrapezoidDecompositionMode mode) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::TrapezoidDecomposition (mode), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_polygon_breaker (db::CompoundRegionOperationNode *input, size_t max_vertex_count, double max_area_ratio) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::PolygonBreaker (max_vertex_count, max_area_ratio), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_sized (db::CompoundRegionOperationNode *input, db::Coord dx, db::Coord dy, unsigned int mode) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::PolygonSizer (dx, dy, mode), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_minkowsky_sum_node1 (db::CompoundRegionOperationNode *input, const db::Edge &e) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::minkowsky_sum_computation (e), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_minkowsky_sum_node2 (db::CompoundRegionOperationNode *input, const db::Polygon &p) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::minkowsky_sum_computation (p), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_minkowsky_sum_node3 (db::CompoundRegionOperationNode *input, const db::Box &p) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::minkowsky_sum_computation (p), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_minkowsky_sum_node4 (db::CompoundRegionOperationNode *input, const std::vector &p) { + check_non_null (input, "input"); return new db::CompoundRegionProcessingOperationNode (new db::minkowsky_sum_computation > (p), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_edges (db::CompoundRegionOperationNode *input) { + check_non_null (input, "input"); if (input->result_type () == db::CompoundRegionOperationNode::EdgePairs) { return new db::CompoundRegionEdgePairToEdgeProcessingOperationNode (new db::EdgePairToEdgesProcessor (), input, true /*processor is owned*/); } else if (input->result_type () == db::CompoundRegionOperationNode::Region) { @@ -242,16 +300,19 @@ static db::CompoundRegionOperationNode *new_edges (db::CompoundRegionOperationNo static db::CompoundRegionOperationNode *new_edge_length_filter (db::CompoundRegionOperationNode *input, bool inverse, db::Edge::distance_type lmin, db::Edge::distance_type lmax) { + check_non_null (input, "input"); return new db::CompoundRegionEdgeFilterOperationNode (new db::EdgeLengthFilter (lmin, lmax, inverse), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_edge_orientation_filter (db::CompoundRegionOperationNode *input, bool inverse, double amin, double amax) { + check_non_null (input, "input"); return new db::CompoundRegionEdgeFilterOperationNode (new db::EdgeOrientationFilter (amin, amax, inverse), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_polygons (db::CompoundRegionOperationNode *input, db::Coord e) { + check_non_null (input, "input"); if (input->result_type () == db::CompoundRegionOperationNode::EdgePairs) { return new db::CompoundRegionEdgePairToPolygonProcessingOperationNode (new db::EdgePairToPolygonProcessor (e), input, true /*processor is owned*/); } else if (input->result_type () == db::CompoundRegionOperationNode::Edges) { @@ -264,26 +325,31 @@ static db::CompoundRegionOperationNode *new_polygons (db::CompoundRegionOperatio static db::CompoundRegionOperationNode *new_extended (db::CompoundRegionOperationNode *input, db::Coord ext_b, db::Coord ext_e, db::Coord ext_o, db::Coord ext_i) { + check_non_null (input, "input"); return new db::CompoundRegionEdgeToPolygonProcessingOperationNode (new db::ExtendedEdgeProcessor (ext_b, ext_e, ext_o, ext_i), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_extended_in (db::CompoundRegionOperationNode *input, db::Coord e) { + check_non_null (input, "input"); return new db::CompoundRegionEdgeToPolygonProcessingOperationNode (new db::ExtendedEdgeProcessor (0, 0, 0, e), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_extended_out (db::CompoundRegionOperationNode *input, db::Coord e) { + check_non_null (input, "input"); return new db::CompoundRegionEdgeToPolygonProcessingOperationNode (new db::ExtendedEdgeProcessor (0, 0, e, 0), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_edge_pair_to_first_edges (db::CompoundRegionOperationNode *input) { + check_non_null (input, "input"); return new db::CompoundRegionEdgePairToEdgeProcessingOperationNode (new db::EdgePairToFirstEdgesProcessor (), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_edge_pair_to_second_edges (db::CompoundRegionOperationNode *input) { + check_non_null (input, "input"); return new db::CompoundRegionEdgePairToEdgeProcessingOperationNode (new db::EdgePairToSecondEdgesProcessor (), input, true /*processor is owned*/); } @@ -303,6 +369,7 @@ static db::CompoundRegionOperationNode *new_check_node (db::edge_relation_type r static db::CompoundRegionOperationNode *new_check_node (db::CompoundRegionOperationNode *input, db::edge_relation_type rel, bool different_polygons, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter) { + check_non_null (input, "input"); return new db::CompoundRegionCheckOperationNode (input, rel, different_polygons, d, db::RegionCheckOptions (whole_edges, metrics, @@ -364,41 +431,49 @@ static db::CompoundRegionOperationNode *new_inside_check (db::CompoundRegionOper static db::CompoundRegionOperationNode *new_perimeter_filter (db::CompoundRegionOperationNode *input, bool inverse, db::coord_traits::perimeter_type pmin, db::coord_traits::perimeter_type pmax) { + check_non_null (input, "input"); return new db::CompoundRegionFilterOperationNode (new db::RegionPerimeterFilter (pmin, pmax, inverse), input, true); } static db::CompoundRegionOperationNode *new_area_filter (db::CompoundRegionOperationNode *input, bool inverse, db::coord_traits::area_type amin, db::coord_traits::area_type amax) { + check_non_null (input, "input"); return new db::CompoundRegionFilterOperationNode (new db::RegionAreaFilter (amin, amax, inverse), input, true); } static db::CompoundRegionOperationNode *new_rectilinear_filter (db::CompoundRegionOperationNode *input, bool inverse) { + check_non_null (input, "input"); return new db::CompoundRegionFilterOperationNode (new db::RectilinearFilter (inverse), input, true); } static db::CompoundRegionOperationNode *new_rectangle_filter (db::CompoundRegionOperationNode *input, bool inverse) { + check_non_null (input, "input"); return new db::CompoundRegionFilterOperationNode (new db::RectangleFilter (inverse), input, true); } static db::CompoundRegionOperationNode *new_bbox_filter (db::CompoundRegionOperationNode *input, db::RegionBBoxFilter::parameter_type parameter, bool inverse, db::coord_traits::distance_type vmin, db::coord_traits::distance_type vmax) { + check_non_null (input, "input"); return new db::CompoundRegionFilterOperationNode (new db::RegionBBoxFilter (vmin, vmax, inverse, parameter), input, true); } static db::CompoundRegionOperationNode *new_start_segments (db::CompoundRegionOperationNode *input, db::Edges::length_type length, double fraction) { + check_non_null (input, "input"); return new db::CompoundRegionEdgeProcessingOperationNode (new db::EdgeSegmentSelector (-1, length, fraction), input, true); } static db::CompoundRegionOperationNode *new_end_segments (db::CompoundRegionOperationNode *input, db::Edges::length_type length, double fraction) { + check_non_null (input, "input"); return new db::CompoundRegionEdgeProcessingOperationNode (new db::EdgeSegmentSelector (1, length, fraction), input, true); } static db::CompoundRegionOperationNode *new_centers (db::CompoundRegionOperationNode *input, db::Edges::length_type length, double fraction) { + check_non_null (input, "input"); return new db::CompoundRegionEdgeProcessingOperationNode (new db::EdgeSegmentSelector (0, length, fraction), input, true); } @@ -582,6 +657,9 @@ Class decl_CompoundRegionOperationNode ("db", " gsi::constructor ("new_extended_out", &new_extended_out, gsi::arg ("input"), gsi::arg ("e"), "@brief Creates a node delivering a polygonized, inside-extended version of the edges.\n" ) + + gsi::constructor ("new_empty", &new_empty, gsi::arg ("type"), + "@brief Creates a node delivering an empty result of the given type\n" + ) + method ("description=", &db::CompoundRegionOperationNode::set_description, gsi::arg ("d"), "@brief Sets the description for this node" ) + @@ -613,10 +691,10 @@ Class decl_CompoundRegionOperationNode ("db", " ); gsi::EnumIn decl_dbCompoundRegionLogicalBoolOperationNode_LogicalOp ("db", "LogicalOp", - gsi::enum_const ("And", db::CompoundRegionLogicalBoolOperationNode::LogicalOp::And, + gsi::enum_const ("LogAnd", db::CompoundRegionLogicalBoolOperationNode::LogicalOp::And, "@brief Indicates a logical '&&' (and)." ) + - gsi::enum_const ("Or", db::CompoundRegionLogicalBoolOperationNode::LogicalOp::Or, + gsi::enum_const ("LogOr", db::CompoundRegionLogicalBoolOperationNode::LogicalOp::Or, "@brief Indicates a logical '||' (or)." ), "@brief This class represents the CompoundRegionOperationNode::LogicalOp enum\n" diff --git a/src/db/unit_tests/dbRegionUtilsTests.cc b/src/db/unit_tests/dbRegionUtilsTests.cc new file mode 100644 index 000000000..e23fa3116 --- /dev/null +++ b/src/db/unit_tests/dbRegionUtilsTests.cc @@ -0,0 +1,237 @@ + + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "tlUnitTest.h" +#include "tlString.h" + +#include "dbRegionUtils.h" + +namespace tl +{ + +template +std::string to_string (Iter b, Iter e) +{ + std::string res; + for (Iter i = b; i != e; ++i) { + if (i != b) { + res += ","; + } + res += tl::to_string (*i); + } + return res; +} + +template +std::string to_string (const std::vector &v) +{ + return to_string (v.begin (), v.end ()); +} + +template +std::string to_string (const std::list &v) +{ + return to_string (v.begin (), v.end ()); +} + +template +std::string to_string (const std::set &v) +{ + return to_string (v.begin (), v.end ()); +} + +} + +TEST(1_SimpleLShape) +{ + std::set ep; + std::set ee1, ee2; + + db::EdgeRelationFilter er (db::WidthRelation, 1001, db::Projection); + + db::edge2edge_check_with_negative_output, std::set > e2e (er, ep, ee1, ee2, false, false, false); + + db::Point pts[] = { + db::Point (0, 0), + db::Point (0, 2000), + db::Point (2000, 2000), + db::Point (2000, 1000), + db::Point (1000, 1000), + db::Point (1000, 0) + }; + + db::Polygon poly; + poly.assign_hull (pts, pts + sizeof (pts) / sizeof (pts[0])); + + db::poly2poly_check_base poly_check (e2e); + + do { + // single polygon check + poly_check.enter (poly, 0); + } while (e2e.prepare_next_pass ()); + + EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)/(1000,1000;1000,0),(1000,2000;2000,2000)/(2000,1000;1000,1000)"); + EXPECT_EQ (tl::to_string (ee1), ""); + EXPECT_EQ (tl::to_string (ee2), ""); +} + +TEST(2_SimpleLWithBigPart) +{ + std::set ep; + std::set ee1, ee2; + + db::EdgeRelationFilter er (db::WidthRelation, 1001, db::Projection); + + db::edge2edge_check_with_negative_output, std::set > e2e (er, ep, ee1, ee2, false, false, false); + + db::Point pts[] = { + db::Point (0, 0), + db::Point (0, 2500), + db::Point (2000, 2500), + db::Point (2000, 1000), + db::Point (1000, 1000), + db::Point (1000, 0) + }; + + db::Polygon poly; + poly.assign_hull (pts, pts + sizeof (pts) / sizeof (pts[0])); + + db::poly2poly_check_base poly_check (e2e); + + do { + // single polygon check + poly_check.enter (poly, 0); + } while (e2e.prepare_next_pass ()); + + EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)/(1000,1000;1000,0)"); + EXPECT_EQ (tl::to_string (ee1), "(0,1000;0,2500),(2000,1000;1000,1000),(0,2500;2000,2500),(2000,2500;2000,1000)"); + EXPECT_EQ (tl::to_string (ee2), ""); +} + +TEST(3_SimpleTWithBigPart) +{ + std::set ep; + std::set ee1, ee2; + + db::EdgeRelationFilter er (db::WidthRelation, 1001, db::Projection); + + db::edge2edge_check_with_negative_output, std::set > e2e (er, ep, ee1, ee2, false, false, false); + + db::Point pts[] = { + db::Point (0, 0), + db::Point (0, 3500), + db::Point (1000, 3500), + db::Point (1000, 2500), + db::Point (2000, 2500), + db::Point (2000, 1000), + db::Point (1000, 1000), + db::Point (1000, 0) + }; + + db::Polygon poly; + poly.assign_hull (pts, pts + sizeof (pts) / sizeof (pts[0])); + + db::poly2poly_check_base poly_check (e2e); + + do { + // single polygon check + poly_check.enter (poly, 0); + } while (e2e.prepare_next_pass ()); + + EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)/(1000,1000;1000,0),(0,2500;0,3500)/(1000,3500;1000,2500)"); + EXPECT_EQ (tl::to_string (ee1), "(0,1000;0,2500),(2000,1000;1000,1000),(1000,2500;2000,2500),(2000,2500;2000,1000)"); + EXPECT_EQ (tl::to_string (ee2), ""); +} + +TEST(4_SimpleNotch) +{ + std::set ep; + std::set ee1, ee2; + + db::EdgeRelationFilter er (db::SpaceRelation, 1001, db::Projection); + + db::edge2edge_check_with_negative_output, std::set > e2e (er, ep, ee1, ee2, false, false, false); + + db::Point pts[] = { + db::Point (0, 0), + db::Point (0, 3000), + db::Point (2000, 3000), + db::Point (2000, 2000), + db::Point (1000, 2000), + db::Point (1000, 1000), + db::Point (2000, 1000), + db::Point (2000, 0) + }; + + db::Polygon poly; + poly.assign_hull (pts, pts + sizeof (pts) / sizeof (pts[0])); + + db::poly2poly_check_base poly_check (e2e); + + do { + // single polygon check + poly_check.enter (poly, 0); + } while (e2e.prepare_next_pass ()); + + EXPECT_EQ (tl::to_string (ep), "(2000,2000;1000,2000)/(1000,1000;2000,1000)"); + EXPECT_EQ (tl::to_string (ee1), "(0,0;0,3000),(2000,0;0,0),(2000,1000;2000,0),(0,3000;2000,3000),(2000,3000;2000,2000)"); + EXPECT_EQ (tl::to_string (ee2), ""); +} + +TEST(5_LShapeNotch) +{ + std::set ep; + std::set ee1, ee2; + + db::EdgeRelationFilter er (db::SpaceRelation, 1001, db::Projection); + + db::edge2edge_check_with_negative_output, std::set > e2e (er, ep, ee1, ee2, false, false, false); + + db::Point pts[] = { + db::Point (0, 0), + db::Point (0, 3000), + db::Point (2000, 3000), + db::Point (2000, 1500), + db::Point (1500, 1500), + db::Point (1500, 2500), + db::Point (500, 2500), + db::Point (500, 500), + db::Point (2000, 500), + db::Point (2000, 0) + }; + + db::Polygon poly; + poly.assign_hull (pts, pts + sizeof (pts) / sizeof (pts[0])); + + db::poly2poly_check_base poly_check (e2e); + + do { + // single polygon check + poly_check.enter (poly, 0); + } while (e2e.prepare_next_pass ()); + + EXPECT_EQ (tl::to_string (ep), "(0,0;0,3000),(2000,0;0,0),(2000,500;2000,0),(0,3000;2000,3000),(2000,3000;2000,1500)"); + EXPECT_EQ (tl::to_string (ee1), ""); + EXPECT_EQ (tl::to_string (ee2), ""); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index f2ac4297b..5ff79a744 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ dbCompoundOperationTests.cc \ + dbRegionUtilsTests.cc \ dbWriterTools.cc \ dbLoadLayoutOptionsTests.cc \ dbSaveLayoutOptionsTests.cc \