diff --git a/scripts/drc_lvs_doc/create_drc_samples.rb b/scripts/drc_lvs_doc/create_drc_samples.rb index bbc97987e..2799b88e1 100644 --- a/scripts/drc_lvs_doc/create_drc_samples.rb +++ b/scripts/drc_lvs_doc/create_drc_samples.rb @@ -606,6 +606,35 @@ run_demo gen, "input.edges.with_length(0, 3.5)\n .extended(:out => 1.0)", "drc run_demo gen, "input.edges.with_length(0, 3.5)\n .extended(:out => 1.0, :joined => true)", "drc_extended3.png" run_demo gen, "input.edges.with_length(2.0)\n .extended(0.0, -0.5, 1.0, -0.5)", "drc_extended4.png" +class Gen + def produce(s1, s2) + pts = [ + RBA::Point::new(1000, 0), + RBA::Point::new(1000, 5000), + RBA::Point::new(2000, 5000), + RBA::Point::new(2000, 7000), + RBA::Point::new(4000, 7000), + RBA::Point::new(4000, 5000), + RBA::Point::new(5000, 5000), + RBA::Point::new(5000, 0), + RBA::Point::new(4000, 0), + RBA::Point::new(4000, 1000), + RBA::Point::new(2000, 1000), + RBA::Point::new(2000, 0) + ]; + s1.insert(RBA::Polygon::new(pts)) + end +end + +gen = Gen::new + +run_demo gen, "input.edges", "drc_edge_modes1.png" +run_demo gen, "input.edges(convex)", "drc_edge_modes2.png" +run_demo gen, "input.edges(concave)", "drc_edge_modes3.png" +run_demo gen, "input.edges(step)", "drc_edge_modes4.png" +run_demo gen, "input.edges(step_in)", "drc_edge_modes5.png" +run_demo gen, "input.edges(step_out)", "drc_edge_modes6.png" + class Gen def produce(s1, s2) pts = [ diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 4f38b882e..b428919c6 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -28,6 +28,7 @@ #include "dbEmptyEdges.h" #include "dbEdges.h" #include "dbEdgesUtils.h" +#include "dbEdgesLocalOperations.h" #include "dbEdgeBoolean.h" #include "dbBoxConvert.h" #include "dbRegion.h" @@ -39,6 +40,7 @@ #include "dbPolygonGenerators.h" #include "dbPolygon.h" #include "dbPath.h" +#include "dbHierProcessor.h" #include @@ -94,104 +96,105 @@ AsIfFlatEdges::to_string (size_t nmax) const return os.str (); } -EdgesDelegate * -AsIfFlatEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse) const +namespace { + +class OutputPairHolder { - // shortcuts - if (other.empty () || empty ()) { - return ((mode == EdgesOutside) == inverse) ? new EmptyEdges () : clone (); - } +public: + OutputPairHolder (int inverse, bool merged_semantics) + { + m_e1.reset (new FlatEdges (merged_semantics)); + m_results.push_back (& m_e1->raw_edges ()); - db::box_scanner2 scanner (report_progress (), progress_desc ()); - - AddressableEdgeDelivery e (begin_merged ()); - - for ( ; ! e.at_end (); ++e) { - scanner.insert1 (e.operator-> (), 0); - } - - AddressablePolygonDelivery p = (mode == EdgesInside ? other.addressable_merged_polygons () : other.addressable_polygons ()); - - for ( ; ! p.at_end (); ++p) { - scanner.insert2 (p.operator-> (), 1); - } - - std::unique_ptr output (new FlatEdges (true)); - - if (! inverse) { - - edge_to_region_interaction_filter filter (output.get (), mode); - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - - } else { - - std::set result; - edge_to_region_interaction_filter > filter (&result, mode); - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - - for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { - if (result.find (*o) == result.end ()) { - output->insert (*o); - } + if (inverse == 0) { + m_e2.reset (new FlatEdges (merged_semantics)); + m_results.push_back (& m_e2->raw_edges ()); } - } - return output.release (); + std::pair region_pair () + { + return std::make_pair (m_e1.release (), m_e2.release ()); + } + + const std::vector &results () { return m_results; } + +private: + std::unique_ptr m_e1, m_e2; + std::vector m_results; +}; + } EdgesDelegate * -AsIfFlatEdges::selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse) const +AsIfFlatEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const { + min_count = std::max (size_t (1), min_count); + // shortcuts - if (edges.empty () || empty ()) { + if (max_count < min_count || other.empty () || empty ()) { return ((mode == EdgesOutside) == inverse) ? new EmptyEdges () : clone (); } - db::box_scanner scanner (report_progress (), progress_desc ()); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + OutputPairHolder oph (inverse ? 1 : -1, merged_semantics () || is_merged ()); - AddressableEdgeDelivery e (begin_merged ()); + db::EdgesIterator edges (begin_merged ()); - for ( ; ! e.at_end (); ++e) { - scanner.insert (e.operator-> (), 0); + db::edge_to_polygon_interacting_local_operation op (mode, inverse ? db::edge_to_polygon_interacting_local_operation::Inverse : db::edge_to_polygon_interacting_local_operation::Normal, min_count, max_count); + + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); + + std::vector > others; + // NOTE: with counting the other region needs to be merged + others.push_back (counting || mode != EdgesInteract ? other.begin_merged () : other.begin ()); + + proc.run_flat (edges, others, std::vector (), &op, oph.results ()); + + return oph.region_pair ().first; +} + +EdgesDelegate * +AsIfFlatEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const +{ + min_count = std::max (size_t (1), min_count); + + // shortcuts + if (max_count < min_count || other.empty () || empty ()) { + return ((mode == EdgesOutside) == inverse) ? new EmptyEdges () : clone (); } - // NOTE: "inside" needs merged edges for the other edges as the algorithm works edge by edge - AddressableEdgeDelivery ee = (mode == EdgesInside ? edges.addressable_merged_edges () : edges.addressable_edges ()); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + OutputPairHolder oph (inverse ? 1 : -1, merged_semantics () || is_merged ()); - for ( ; ! ee.at_end (); ++ee) { - scanner.insert (ee.operator-> (), 1); - } + db::EdgesIterator edges (begin_merged ()); - std::unique_ptr output (new FlatEdges (true)); + db::Edge2EdgeInteractingLocalOperation op (mode, inverse ? db::Edge2EdgeInteractingLocalOperation::Inverse : db::Edge2EdgeInteractingLocalOperation::Normal, min_count, max_count); - if (! inverse) { + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); - edge_interaction_filter filter (*output, mode); - scanner.process (filter, 1, db::box_convert ()); + std::vector > others; + // NOTE: with counting the other edge collection needs to be merged + others.push_back (counting || mode != EdgesInteract ? other.begin_merged () : other.begin ()); - } else { + proc.run_flat (edges, others, std::vector (), &op, oph.results ()); - std::set result; - edge_interaction_filter > filter (result, mode); - scanner.process (filter, 1, db::box_convert ()); - - for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { - if (result.find (*o) == result.end ()) { - output->insert (*o); - } - } - - } - - return output.release (); + return oph.region_pair ().first; } std::pair -AsIfFlatEdges::selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode) const +AsIfFlatEdges::selected_interacting_pair_generic (const Region &other, EdgeInteractionMode mode, size_t min_count, size_t max_count) const { + min_count = std::max (size_t (1), min_count); + // shortcuts - if (region.empty () || empty ()) { + if (max_count < min_count || other.empty () || empty ()) { if (mode != EdgesOutside) { return std::make_pair (new EmptyEdges (), clone ()); } else { @@ -199,43 +202,34 @@ AsIfFlatEdges::selected_interacting_pair_generic (const Region ®ion, EdgeInte } } - db::box_scanner2 scanner (report_progress (), progress_desc ()); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + OutputPairHolder oph (0, merged_semantics () || is_merged ()); - AddressableEdgeDelivery e (begin_merged ()); + db::EdgesIterator edges (begin_merged ()); - for ( ; ! e.at_end (); ++e) { - scanner.insert1 (e.operator-> (), 0); - } + db::edge_to_polygon_interacting_local_operation op (mode, db::edge_to_polygon_interacting_local_operation::Both, min_count, max_count); - AddressablePolygonDelivery p = region.addressable_merged_polygons (); + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); - for ( ; ! p.at_end (); ++p) { - scanner.insert2 (p.operator-> (), 1); - } + std::vector > others; + // NOTE: with counting the other region needs to be merged + others.push_back (counting || mode != EdgesInteract ? other.begin_merged () : other.begin ()); - std::unique_ptr output (new FlatEdges (true)); - std::unique_ptr output2 (new FlatEdges (true)); + proc.run_flat (edges, others, std::vector (), &op, oph.results ()); - std::set result; - edge_to_region_interaction_filter > filter (&result, mode); - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - - for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { - if (result.find (*o) == result.end ()) { - output2->insert (*o); - } else { - output->insert (*o); - } - } - - return std::make_pair (output.release (), output2.release ()); + return oph.region_pair (); } std::pair -AsIfFlatEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode) const +AsIfFlatEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode, size_t min_count, size_t max_count) const { + min_count = std::max (size_t (1), min_count); + // shortcuts - if (other.empty () || empty ()) { + if (max_count < min_count || other.empty () || empty ()) { if (mode != EdgesOutside) { return std::make_pair (new EmptyEdges (), clone ()); } else { @@ -243,36 +237,25 @@ AsIfFlatEdges::selected_interacting_pair_generic (const Edges &other, EdgeIntera } } - db::box_scanner scanner (report_progress (), progress_desc ()); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + OutputPairHolder oph (0, merged_semantics () || is_merged ()); - AddressableEdgeDelivery e (begin_merged ()); + db::EdgesIterator edges (begin_merged ()); - for ( ; ! e.at_end (); ++e) { - scanner.insert (e.operator-> (), 0); - } + db::Edge2EdgeInteractingLocalOperation op (mode, db::Edge2EdgeInteractingLocalOperation::Both, min_count, max_count); - AddressableEdgeDelivery ee = other.addressable_merged_edges (); + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); - for ( ; ! ee.at_end (); ++ee) { - scanner.insert (ee.operator-> (), 1); - } + std::vector > others; + // NOTE: with counting the other edge collection needs to be merged + others.push_back (counting || mode != EdgesInteract ? other.begin_merged () : other.begin ()); - std::unique_ptr output (new FlatEdges (true)); - std::unique_ptr output2 (new FlatEdges (true)); + proc.run_flat (edges, others, std::vector (), &op, oph.results ()); - std::set results; - edge_interaction_filter > filter (results, mode); - scanner.process (filter, 1, db::box_convert ()); - - for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { - if (results.find (*o) == results.end ()) { - output2->insert (*o); - } else { - output->insert (*o); - } - } - - return std::make_pair (output.release (), output2.release ()); + return oph.region_pair (); } @@ -294,7 +277,7 @@ AsIfFlatEdges::pull_generic (const Edges &edges) const } std::unique_ptr output (new FlatEdges (true)); - edge_interaction_filter filter (*output, EdgesInteract); + edge_interaction_filter filter (*output, EdgesInteract, size_t (1), std::numeric_limits::max ()); scanner.process (filter, 1, db::box_convert ()); return output.release (); @@ -324,7 +307,7 @@ AsIfFlatEdges::pull_generic (const Region &other) const std::unique_ptr output (new FlatRegion (true)); - edge_to_region_interaction_filter filter (output.get (), EdgesInteract); + edge_to_polygon_interaction_filter filter (output.get (), EdgesInteract, size_t (1), std::numeric_limits::max ()); scanner.process (filter, 1, db::box_convert (), db::box_convert ()); return output.release (); @@ -343,111 +326,111 @@ AsIfFlatEdges::pull_interacting (const Region &other) const } EdgesDelegate * -AsIfFlatEdges::selected_interacting (const Edges &other) const +AsIfFlatEdges::selected_interacting (const Edges &other, size_t min_count, size_t max_count) const { - return selected_interacting_generic (other, EdgesInteract, false); + return selected_interacting_generic (other, EdgesInteract, false, min_count, max_count); } EdgesDelegate * -AsIfFlatEdges::selected_not_interacting (const Edges &other) const +AsIfFlatEdges::selected_not_interacting (const Edges &other, size_t min_count, size_t max_count) const { - return selected_interacting_generic (other, EdgesInteract, true); + return selected_interacting_generic (other, EdgesInteract, true, min_count, max_count); } EdgesDelegate * -AsIfFlatEdges::selected_interacting (const Region &other) const +AsIfFlatEdges::selected_interacting (const Region &other, size_t min_count, size_t max_count) const { - return selected_interacting_generic (other, EdgesInteract, false); + return selected_interacting_generic (other, EdgesInteract, false, min_count, max_count); } EdgesDelegate * -AsIfFlatEdges::selected_not_interacting (const Region &other) const +AsIfFlatEdges::selected_not_interacting (const Region &other, size_t min_count, size_t max_count) const { - return selected_interacting_generic (other, EdgesInteract, true); + return selected_interacting_generic (other, EdgesInteract, true, min_count, max_count); } std::pair -AsIfFlatEdges::selected_interacting_pair (const Region &other) const +AsIfFlatEdges::selected_interacting_pair (const Region &other, size_t min_count, size_t max_count) const { - return selected_interacting_pair_generic (other, EdgesInteract); + return selected_interacting_pair_generic (other, EdgesInteract, min_count, max_count); } std::pair -AsIfFlatEdges::selected_interacting_pair (const Edges &other) const +AsIfFlatEdges::selected_interacting_pair (const Edges &other, size_t min_count, size_t max_count) const { - return selected_interacting_pair_generic (other, EdgesInteract); + return selected_interacting_pair_generic (other, EdgesInteract, min_count, max_count); } EdgesDelegate * AsIfFlatEdges::selected_outside (const Region &other) const { - return selected_interacting_generic (other, EdgesOutside, false); + return selected_interacting_generic (other, EdgesOutside, false, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_not_outside (const Region &other) const { - return selected_interacting_generic (other, EdgesOutside, true); + return selected_interacting_generic (other, EdgesOutside, true, size_t (1), std::numeric_limits::max ()); } std::pair AsIfFlatEdges::selected_outside_pair (const Region &other) const { - return selected_interacting_pair_generic (other, EdgesOutside); + return selected_interacting_pair_generic (other, EdgesOutside, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_inside (const Region &other) const { - return selected_interacting_generic (other, EdgesInside, false); + return selected_interacting_generic (other, EdgesInside, false, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_not_inside (const Region &other) const { - return selected_interacting_generic (other, EdgesInside, true); + return selected_interacting_generic (other, EdgesInside, true, size_t (1), std::numeric_limits::max ()); } std::pair AsIfFlatEdges::selected_inside_pair (const Region &other) const { - return selected_interacting_pair_generic (other, EdgesInside); + return selected_interacting_pair_generic (other, EdgesInside, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_outside (const Edges &other) const { - return selected_interacting_generic (other, EdgesOutside, false); + return selected_interacting_generic (other, EdgesOutside, false, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_not_outside (const Edges &other) const { - return selected_interacting_generic (other, EdgesOutside, true); + return selected_interacting_generic (other, EdgesOutside, true, size_t (1), std::numeric_limits::max ()); } std::pair AsIfFlatEdges::selected_outside_pair (const Edges &other) const { - return selected_interacting_pair_generic (other, EdgesOutside); + return selected_interacting_pair_generic (other, EdgesOutside, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_inside (const Edges &other) const { - return selected_interacting_generic (other, EdgesInside, false); + return selected_interacting_generic (other, EdgesInside, false, size_t (1), std::numeric_limits::max ()); } EdgesDelegate * AsIfFlatEdges::selected_not_inside (const Edges &other) const { - return selected_interacting_generic (other, EdgesInside, true); + return selected_interacting_generic (other, EdgesInside, true, size_t (1), std::numeric_limits::max ()); } std::pair AsIfFlatEdges::selected_inside_pair (const Edges &other) const { - return selected_interacting_pair_generic (other, EdgesInside); + return selected_interacting_pair_generic (other, EdgesInside, size_t (1), std::numeric_limits::max ()); } @@ -769,6 +752,118 @@ AsIfFlatEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Co return result.release (); } +EdgesDelegate * +AsIfFlatEdges::merged () const +{ + if (empty ()) { + return new db::EmptyEdges (); + } else { + return boolean (0, EdgeOr); + } +} + +EdgesDelegate * +AsIfFlatEdges::and_with (const Edges &other) const +{ + if (empty () || other.empty ()) { + return new db::EmptyEdges (); + } else { + return boolean (&other, EdgeAnd); + } +} + +EdgesDelegate * +AsIfFlatEdges::not_with (const Edges &other) const +{ + if (empty ()) { + return new db::EmptyEdges (); + } else if (other.empty ()) { + return clone (); + } else { + return boolean (&other, EdgeNot); + } +} + +std::pair +AsIfFlatEdges::andnot_with (const Edges &other) const +{ + if (empty ()) { + return std::make_pair (new db::EmptyEdges (), new db::EmptyEdges ()); + } else if (other.empty ()) { + return std::make_pair (new db::EmptyEdges (), clone ()); + } else { + return boolean_andnot (&other); + } +} + +EdgesDelegate * +AsIfFlatEdges::and_with (const Region &other) const +{ + if (empty () || other.empty ()) { + return new db::EmptyEdges (); + } else { + return edge_region_op (other, db::EdgePolygonOp::Inside, true /*include borders*/).first; + } +} + +EdgesDelegate * +AsIfFlatEdges::not_with (const Region &other) const +{ + if (empty ()) { + return new db::EmptyEdges (); + } else if (other.empty ()) { + return clone (); + } else { + return edge_region_op (other, db::EdgePolygonOp::Outside, true /*include borders*/).first; + } +} + +std::pair +AsIfFlatEdges::andnot_with (const Region &other) const +{ + if (empty ()) { + return std::make_pair (new db::EmptyEdges (), new db::EmptyEdges ()); + } else if (other.empty ()) { + return std::make_pair (new db::EmptyEdges (), clone ()); + } else { + return edge_region_op (other, db::EdgePolygonOp::Both, true /*include borders*/); + } +} + +EdgesDelegate * +AsIfFlatEdges::xor_with (const Edges &other) const +{ + if (empty ()) { + return other.delegate ()->clone (); + } else if (other.empty ()) { + return clone (); + } else { + return boolean (&other, EdgeXor); + } +} + +EdgesDelegate * +AsIfFlatEdges::or_with (const Edges &other) const +{ + if (empty ()) { + return other.delegate ()->clone (); + } else if (other.empty ()) { + return clone (); + } else { + return boolean (&other, EdgeOr); + } +} + +EdgesDelegate * +AsIfFlatEdges::intersections (const Edges &other) const +{ + if (empty () || other.empty ()) { + return new db::EmptyEdges (); + } else { + return boolean (&other, EdgeIntersections); + } +} + EdgesDelegate * AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const { @@ -781,9 +876,7 @@ AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const AddressableEdgeDelivery e (begin ()); for ( ; ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (e.operator-> (), 0); - } + scanner.insert (e.operator-> (), 0); } AddressableEdgeDelivery ee; @@ -791,9 +884,7 @@ AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const if (other) { ee = other->addressable_edges (); for ( ; ! ee.at_end (); ++ee) { - if (! ee->is_degenerate ()) { - scanner.insert (ee.operator-> (), 1); - } + scanner.insert (ee.operator-> (), 1); } } @@ -815,9 +906,7 @@ AsIfFlatEdges::boolean_andnot (const Edges *other) const AddressableEdgeDelivery e (begin ()); for ( ; ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (e.operator-> (), 0); - } + scanner.insert (e.operator-> (), 0); } AddressableEdgeDelivery ee; @@ -825,9 +914,7 @@ AsIfFlatEdges::boolean_andnot (const Edges *other) const if (other) { ee = other->addressable_edges (); for ( ; ! ee.at_end (); ++ee) { - if (! ee->is_degenerate ()) { - scanner.insert (ee.operator-> (), 1); - } + scanner.insert (ee.operator-> (), 1); } } @@ -852,6 +939,8 @@ AsIfFlatEdges::edge_region_op (const Region &other, db::EdgePolygonOp::mode_t mo db::EdgeProcessor ep (report_progress (), progress_desc ()); + bool has_dots = false; + for (db::Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { if (p->box ().touches (bbox ())) { ep.insert (*p, 0); @@ -859,7 +948,11 @@ AsIfFlatEdges::edge_region_op (const Region &other, db::EdgePolygonOp::mode_t mo } for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { - ep.insert (*e, 1); + if (e->is_degenerate ()) { + has_dots = true; + } else { + ep.insert (*e, 1); + } } std::unique_ptr output_second; @@ -874,6 +967,36 @@ AsIfFlatEdges::edge_region_op (const Region &other, db::EdgePolygonOp::mode_t mo db::EdgePolygonOp op (mode, include_borders); ep.process (cc, op); + // process dots which are not captured by the booleans using the interaction function + + if (has_dots) { + + std::unique_ptr dots (new FlatEdges (false)); + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + if (e->is_degenerate ()) { + dots->insert (*e); + } + } + + std::pair res (0, 0); + + if (mode == EdgePolygonOp::Both) { + res = dots->selected_interacting_pair_generic (other, include_borders ? EdgesInteract : EdgesInside, size_t (1), std::numeric_limits::max ()); + } else if (mode == EdgePolygonOp::Inside) { + res.first = dots->selected_interacting_generic (other, include_borders ? EdgesInteract : EdgesInside, false, size_t (1), std::numeric_limits::max ()); + } else if (mode == EdgePolygonOp::Outside) { + res.first = dots->selected_interacting_generic (other, include_borders ? EdgesInteract : EdgesOutside, include_borders, size_t (1), std::numeric_limits::max ()); + } + + if (res.first) { + output->add_in_place (db::Edges (res.first)); + } + if (res.second) { + output_second->add_in_place (db::Edges (res.second)); + } + + } + return std::make_pair (output.release (), output_second.release ()); } diff --git a/src/db/db/dbAsIfFlatEdges.h b/src/db/db/dbAsIfFlatEdges.h index b1f9a07d4..fd07227ef 100644 --- a/src/db/db/dbAsIfFlatEdges.h +++ b/src/db/db/dbAsIfFlatEdges.h @@ -107,55 +107,25 @@ public: return merged (); } - virtual EdgesDelegate *merged () const - { - return boolean (0, EdgeOr); - } + virtual EdgesDelegate *merged () const; - virtual EdgesDelegate *and_with (const Edges &other) const - { - return boolean (&other, EdgeAnd); - } + virtual EdgesDelegate *and_with (const Edges &other) const; - virtual EdgesDelegate *not_with (const Edges &other) const - { - return boolean (&other, EdgeNot); - } + virtual EdgesDelegate *not_with (const Edges &other) const; - virtual std::pair andnot_with (const Edges &other) const - { - return boolean_andnot (&other); - } + virtual std::pair andnot_with (const Edges &other) const; - virtual EdgesDelegate *and_with (const Region &other) const - { - return edge_region_op (other, db::EdgePolygonOp::Inside, true /*include borders*/).first; - } + virtual EdgesDelegate *and_with (const Region &other) const; - virtual EdgesDelegate *not_with (const Region &other) const - { - return edge_region_op (other, db::EdgePolygonOp::Outside, true /*include borders*/).first; - } + virtual EdgesDelegate *not_with (const Region &other) const; - virtual std::pair andnot_with (const Region &other) const - { - return edge_region_op (other, db::EdgePolygonOp::Both, true /*include borders*/); - } + virtual std::pair andnot_with (const Region &other) const; - virtual EdgesDelegate *xor_with (const Edges &other) const - { - return boolean (&other, EdgeXor); - } + virtual EdgesDelegate *xor_with (const Edges &other) const; - virtual EdgesDelegate *or_with (const Edges &other) const - { - return boolean (&other, EdgeOr); - } + virtual EdgesDelegate *or_with (const Edges &other) const; - virtual EdgesDelegate *intersections (const Edges &other) const - { - return boolean (&other, EdgeIntersections); - } + virtual EdgesDelegate *intersections (const Edges &other) const; virtual EdgesDelegate *add_in_place (const Edges &other) { @@ -183,12 +153,12 @@ public: virtual EdgesDelegate *pull_interacting (const Edges &) const; virtual RegionDelegate *pull_interacting (const Region &) const; - virtual EdgesDelegate *selected_interacting (const Edges &) const; - virtual EdgesDelegate *selected_not_interacting (const Edges &) const; - virtual EdgesDelegate *selected_interacting (const Region &) const; - virtual EdgesDelegate *selected_not_interacting (const Region &) const; - virtual std::pair selected_interacting_pair (const Region &other) const; - virtual std::pair selected_interacting_pair (const Edges &other) const; + virtual EdgesDelegate *selected_interacting (const Edges &, size_t min_count, size_t max_count) const; + virtual EdgesDelegate *selected_not_interacting (const Edges &, size_t min_count, size_t max_count) const; + virtual EdgesDelegate *selected_interacting (const Region &, size_t min_count, size_t max_count) const; + virtual EdgesDelegate *selected_not_interacting (const Region &, size_t min_count, size_t max_count) const; + virtual std::pair selected_interacting_pair (const Region &other, size_t min_count, size_t max_count) const; + virtual std::pair selected_interacting_pair (const Edges &other, size_t min_count, size_t max_count) const; virtual EdgesDelegate *selected_outside (const Edges &other) const; virtual EdgesDelegate *selected_not_outside (const Edges &other) const; @@ -217,10 +187,10 @@ protected: EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, const EdgesCheckOptions &options) const; virtual EdgesDelegate *pull_generic (const Edges &edges) const; virtual RegionDelegate *pull_generic (const Region ®ion) const; - virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse) const; - virtual std::pair selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode) const; - virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, EdgeInteractionMode mode, bool inverse) const; - virtual std::pair selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode) const; + virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const; + virtual std::pair selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode, size_t min_count, size_t max_count) const; + virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const; + virtual std::pair selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode, size_t min_count, size_t max_count) const; AsIfFlatEdges &operator= (const AsIfFlatEdges &other); AsIfFlatEdges (const AsIfFlatEdges &other); diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 5a86535fd..9ee803c2a 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -143,7 +143,7 @@ AsIfFlatRegion::to_string (size_t nmax) const } EdgesDelegate * -AsIfFlatRegion::edges (const EdgeFilterBase *filter) const +AsIfFlatRegion::edges (const EdgeFilterBase *filter, const PolygonToEdgeProcessorBase *proc) const { std::unique_ptr result (new FlatEdges ()); db::PropertyMapper pm (result->properties_repository (), properties_repository ()); @@ -154,17 +154,41 @@ AsIfFlatRegion::edges (const EdgeFilterBase *filter) const } result->reserve (n); + std::vector heap; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + db::properties_id_type prop_id = p.prop_id (); - for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { - if (! filter || filter->selected (*e)) { - if (prop_id != 0) { - result->insert (db::EdgeWithProperties (*e, pm (prop_id))); - } else { - result->insert (*e); + + if (proc) { + + heap.clear (); + proc->process (*p, heap); + + for (auto e = heap.begin (); e != heap.end (); ++e) { + if (! filter || filter->selected (*e)) { + if (prop_id != 0) { + result->insert (db::EdgeWithProperties (*e, pm (prop_id))); + } else { + result->insert (*e); + } } } + + } else { + + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + if (! filter || filter->selected (*e)) { + if (prop_id != 0) { + result->insert (db::EdgeWithProperties (*e, pm (prop_id))); + } else { + result->insert (*e); + } + } + } + } + } return result.release (); diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index 5f7ecd203..bc59155a1 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -84,7 +84,7 @@ public: virtual RegionDelegate *scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy); - virtual EdgesDelegate *edges (const EdgeFilterBase *) const; + virtual EdgesDelegate *edges (const EdgeFilterBase *filter, const db::PolygonToEdgeProcessorBase *proc) const; virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter) { @@ -137,32 +137,32 @@ public: virtual RegionDelegate *selected_outside (const Region &other) const { - return selected_interacting_generic (other, 1, false, Positive, size_t (0), std::numeric_limits::max ()).first; + return selected_interacting_generic (other, 1, false, Positive, size_t (1), std::numeric_limits::max ()).first; } virtual RegionDelegate *selected_not_outside (const Region &other) const { - return selected_interacting_generic (other, 1, false, Negative, size_t (0), std::numeric_limits::max ()).first; + return selected_interacting_generic (other, 1, false, Negative, size_t (1), std::numeric_limits::max ()).first; } virtual std::pair selected_outside_pair (const Region &other) const { - return selected_interacting_generic (other, 1, false, PositiveAndNegative, size_t (0), std::numeric_limits::max ()); + return selected_interacting_generic (other, 1, false, PositiveAndNegative, size_t (1), std::numeric_limits::max ()); } virtual RegionDelegate *selected_inside (const Region &other) const { - return selected_interacting_generic (other, -1, true, Positive, size_t (0), std::numeric_limits::max ()).first; + return selected_interacting_generic (other, -1, true, Positive, size_t (1), std::numeric_limits::max ()).first; } virtual RegionDelegate *selected_not_inside (const Region &other) const { - return selected_interacting_generic (other, -1, true, Negative, size_t (0), std::numeric_limits::max ()).first; + return selected_interacting_generic (other, -1, true, Negative, size_t (1), std::numeric_limits::max ()).first; } virtual std::pair selected_inside_pair (const Region &other) const { - return selected_interacting_generic (other, -1, true, PositiveAndNegative, size_t (0), std::numeric_limits::max ()); + return selected_interacting_generic (other, -1, true, PositiveAndNegative, size_t (1), std::numeric_limits::max ()); } virtual RegionDelegate *selected_enclosing (const Region &other, size_t min_count, size_t max_count) const diff --git a/src/db/db/dbBoxScanner.h b/src/db/db/dbBoxScanner.h index bcdb9fd02..2b2b06185 100644 --- a/src/db/db/dbBoxScanner.h +++ b/src/db/db/dbBoxScanner.h @@ -1079,7 +1079,7 @@ public: m_cl.erase (cli); } - } else if (m_report_single) { + } else if (m_report_single && m_ignore_single.find (obj) == m_ignore_single.end ()) { // single-object entry: create a cluster and feed it a single-object signature Cluster cl (m_cl_template); @@ -1089,6 +1089,13 @@ public: } } + void ignore_single (const Obj *o) + { + if (m_report_single) { + m_ignore_single.insert (o); + } + } + void add_asymm (const Obj *o1, const Prop &p1, const Obj *o2, const Prop &p2) { om_iterator_type om1 = m_om.find (om_key_type (o1, p1)); @@ -1166,6 +1173,7 @@ private: bool m_report_single; cl_type m_cl; om_type m_om; + std::set m_ignore_single; }; } diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index a9608af23..c94839621 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -914,10 +914,19 @@ EdgesDelegate *DeepEdges::merged () const return res.release (); } -DeepLayer +std::pair DeepEdges::and_or_not_with (const DeepEdges *other, EdgeBoolOp op) const { + std::vector output_layers; + DeepLayer dl_out (deep_layer ().derived ()); + output_layers.push_back (dl_out.layer ()); + + DeepLayer dl_out2; + if (op == EdgeAndNot) { + dl_out2 = DeepLayer (deep_layer ().derived ()); + output_layers.push_back (dl_out2.layer ()); + } db::EdgeBoolAndOrNotLocalOperation local_op (op); @@ -927,14 +936,34 @@ DeepEdges::and_or_not_with (const DeepEdges *other, EdgeBoolOp op) const proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ()); proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ()); - proc.run (&local_op, deep_layer ().layer (), other->deep_layer ().layer (), dl_out.layer ()); + proc.run (&local_op, deep_layer ().layer (), other->deep_layer ().layer (), output_layers); - return dl_out; + return std::make_pair (dl_out, dl_out2); } std::pair DeepEdges::edge_region_op (const DeepRegion *other, EdgePolygonOp::mode_t mode, bool include_borders) const { + // first, extract dots + + DeepLayer dots (deep_layer ().derived ()); + bool has_dots = false; + + db::Layout &layout = const_cast (dots.layout ()); + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + const db::Shapes &s = c->shapes (deep_layer ().layer ()); + db::Shapes &st = c->shapes (dots.layer ()); + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) { + if (si->edge ().is_degenerate ()) { + st.insert (*si); + has_dots = true; + } + } + } + + // normal processing (dots will vanish) + std::vector output_layers; DeepLayer dl_out (deep_layer ().derived ()); @@ -956,6 +985,29 @@ DeepEdges::edge_region_op (const DeepRegion *other, EdgePolygonOp::mode_t mode, proc.run (&op, deep_layer ().layer (), other->deep_layer ().layer (), output_layers); + if (has_dots) { + + // process dots + + std::pair res (0, 0); + + if (mode == EdgePolygonOp::Both) { + res = db::DeepEdges (dots).selected_interacting_pair_generic_impl (other, include_borders ? EdgesInteract : EdgesInside, size_t (1), std::numeric_limits::max ()); + } else if (mode == EdgePolygonOp::Inside) { + res.first = db::DeepEdges (dots).selected_interacting_generic_impl (other, include_borders ? EdgesInteract : EdgesInside, false, size_t (1), std::numeric_limits::max ()); + } else if (mode == EdgePolygonOp::Outside) { + res.first = db::DeepEdges (dots).selected_interacting_generic_impl (other, include_borders ? EdgesInteract : EdgesOutside, include_borders, size_t (1), std::numeric_limits::max ()); + } + + if (res.first) { + db::DeepEdges (dl_out).add_in_place (db::Edges (res.first)); + } + if (res.second) { + db::DeepEdges (dl_out2).add_in_place (db::Edges (res.second)); + } + + } + return std::make_pair (dl_out, dl_out2); } @@ -963,17 +1015,22 @@ EdgesDelegate *DeepEdges::intersections (const Edges &other) const { const DeepEdges *other_deep = dynamic_cast (other.delegate ()); - if (empty () || other.empty ()) { + if (empty ()) { return clone (); + } else if (other.empty ()) { + + // NOTE: we do not use "EmptyEdges" as we want to maintain + return new DeepEdges (deep_layer ().derived ()); + } else if (! other_deep) { return AsIfFlatEdges::intersections (other); } else { - return new DeepEdges (and_or_not_with (other_deep, EdgeIntersections)); + return new DeepEdges (and_or_not_with (other_deep, EdgeIntersections).first); } } @@ -997,7 +1054,7 @@ EdgesDelegate *DeepEdges::and_with (const Edges &other) const } else { - return new DeepEdges (and_or_not_with (other_deep, EdgeAnd)); + return new DeepEdges (and_or_not_with (other_deep, EdgeAnd).first); } } @@ -1016,7 +1073,7 @@ EdgesDelegate *DeepEdges::not_with (const Edges &other) const } else { - return new DeepEdges (and_or_not_with (other_deep, EdgeNot)); + return new DeepEdges (and_or_not_with (other_deep, EdgeNot).first); } } @@ -1092,7 +1149,7 @@ EdgesDelegate *DeepEdges::not_with (const Region &other) const std::pair DeepEdges::andnot_with (const Edges &other) const { - const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + const DeepEdges *other_deep = dynamic_cast (other.delegate ()); if (empty ()) { @@ -1109,7 +1166,7 @@ DeepEdges::andnot_with (const Edges &other) const } else { - auto res = edge_region_op (other_deep, EdgePolygonOp::Both, true /*include borders*/); + auto res = and_or_not_with (other_deep, EdgeAndNot); return std::make_pair (new DeepEdges (res.first), new DeepEdges (res.second)); } @@ -1135,8 +1192,8 @@ EdgesDelegate *DeepEdges::xor_with (const Edges &other) const // Implement XOR as (A-B)+(B-A) - only this implementation // is compatible with the local processor scheme - DeepLayer n1 (and_or_not_with (other_deep, EdgeNot)); - DeepLayer n2 (other_deep->and_or_not_with (this, EdgeNot)); + DeepLayer n1 (and_or_not_with (other_deep, EdgeNot).first); + DeepLayer n2 (other_deep->and_or_not_with (this, EdgeNot).first); n1.add_from (n2); return new DeepEdges (n1); @@ -1354,362 +1411,8 @@ RegionDelegate *DeepEdges::extended (coord_type ext_b, coord_type ext_e, coord_t return res.release (); } -namespace -{ - -class Edge2EdgeInteractingLocalOperation - : public local_operation -{ -public: - enum output_mode_t { Normal, Inverse, Both }; - - Edge2EdgeInteractingLocalOperation (EdgeInteractionMode mode, output_mode_t output_mode) - : m_mode (mode), m_output_mode (output_mode) - { - // .. nothing yet .. - } - - virtual db::Coord dist () const - { - // touching is sufficient - return 1; - } - - virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const - { - tl_assert (results.size () == (m_output_mode == Both ? 2 : 1)); - - std::unordered_set &result = results.front (); - - std::unordered_set *result2 = 0; - if (m_output_mode == Both) { - result2 = &results[1]; - } - - db::box_scanner scanner; - - std::set others; - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.intruder_shape (*j).second); - } - } - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - const db::Edge &subject = interactions.subject_shape (i->first); - scanner.insert (&subject, 0); - } - - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - scanner.insert (o.operator-> (), 1); - } - - if (m_output_mode == Inverse || m_output_mode == Both) { - - std::unordered_set interacting; - edge_interaction_filter > filter (interacting, m_mode); - scanner.process (filter, 1, db::box_convert ()); - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - - const db::Edge &subject = interactions.subject_shape (i->first); - if (interacting.find (subject) == interacting.end ()) { - if (m_output_mode != Both) { - result.insert (subject); - } else { - result2->insert (subject); - } - } else if (m_output_mode == Both) { - result.insert (subject); - } - - } - - } else { - - edge_interaction_filter > filter (result, m_mode); - scanner.process (filter, 1, db::box_convert ()); - - } - - } - - virtual OnEmptyIntruderHint on_empty_intruder_hint () const - { - if (m_mode == EdgesOutside) { - return m_output_mode == Both ? Copy : (m_output_mode == Inverse ? Drop : Copy); - } else { - return m_output_mode == Both ? CopyToSecond : (m_output_mode == Inverse ? Copy : Drop); - } - } - - virtual std::string description () const - { - return tl::to_string (tr ("Select interacting edges")); - } - -private: - EdgeInteractionMode m_mode; - output_mode_t m_output_mode; -}; - -class Edge2EdgePullLocalOperation - : public local_operation -{ -public: - Edge2EdgePullLocalOperation () - { - // .. nothing yet .. - } - - virtual db::Coord dist () const - { - // touching is sufficient - return 1; - } - - virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const - { - tl_assert (results.size () == 1); - std::unordered_set &result = results.front (); - - db::box_scanner scanner; - - std::set others; - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.intruder_shape (*j).second); - } - } - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - const db::Edge &subject = interactions.subject_shape (i->first); - scanner.insert (&subject, 1); - } - - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - scanner.insert (o.operator-> (), 0); - } - - edge_interaction_filter > filter (result, EdgesInteract); - scanner.process (filter, 1, db::box_convert ()); - - } - - virtual OnEmptyIntruderHint on_empty_intruder_hint () const - { - return Drop; - } - - virtual std::string description () const - { - return tl::to_string (tr ("Select interacting edges from other")); - } -}; - -class Edge2PolygonInteractingLocalOperation - : public local_operation -{ -public: - enum output_mode_t { Normal, Inverse, Both }; - - Edge2PolygonInteractingLocalOperation (EdgeInteractionMode mode, output_mode_t output_mode) - : m_mode (mode), m_output_mode (output_mode) - { - // .. nothing yet .. - } - - virtual db::Coord dist () const - { - // touching is sufficient - return 1; - } - - virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const - { - tl_assert (results.size () == size_t (m_output_mode == Both ? 2 : 1)); - - std::unordered_set &result = results.front (); - - std::unordered_set *result2 = 0; - if (m_output_mode == Both) { - result2 = &results[1]; - } - - db::box_scanner2 scanner; - - std::set others; - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.intruder_shape (*j).second); - } - } - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - const db::Edge &subject = interactions.subject_shape (i->first); - scanner.insert1 (&subject, 0); - } - - std::list heap; - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - heap.push_back (o->obj ().transformed (o->trans ())); - scanner.insert2 (& heap.back (), 1); - } - - if (m_output_mode == Inverse || m_output_mode == Both) { - - std::unordered_set interacting; - edge_to_region_interaction_filter > filter (&interacting, m_mode); - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - - const db::Edge &subject = interactions.subject_shape (i->first); - - if (interacting.find (subject) == interacting.end ()) { - if (m_output_mode != Both) { - result.insert (subject); - } else { - result2->insert (subject); - } - } else if (m_output_mode == Both) { - result.insert (subject); - } - - } - - } else { - - edge_to_region_interaction_filter > filter (&result, m_mode); - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - - } - } - - virtual OnEmptyIntruderHint on_empty_intruder_hint () const - { - if (m_mode == EdgesOutside) { - return m_output_mode == Both ? Copy : (m_output_mode == Inverse ? Drop : Copy); - } else { - return m_output_mode == Both ? CopyToSecond : (m_output_mode == Inverse ? Copy : Drop); - } - } - - virtual std::string description () const - { - if (m_mode == EdgesInteract) { - if (m_output_mode == Inverse) { - return tl::to_string (tr ("Select non-interacting edges")); - } else if (m_output_mode == Normal) { - return tl::to_string (tr ("Select interacting edges")); - } else { - return tl::to_string (tr ("Select interacting and non-interacting edges")); - } - } else if (m_mode == EdgesInside) { - if (m_output_mode == Inverse) { - return tl::to_string (tr ("Select non-inside edges")); - } else if (m_output_mode == Normal) { - return tl::to_string (tr ("Select inside edges")); - } else { - return tl::to_string (tr ("Select inside and non-inside edges")); - } - } else if (m_mode == EdgesOutside) { - if (m_output_mode == Inverse) { - return tl::to_string (tr ("Select non-outside edges")); - } else if (m_output_mode == Normal) { - return tl::to_string (tr ("Select outside edges")); - } else { - return tl::to_string (tr ("Select outside and non-outside edges")); - } - } - return std::string (); - } - -private: - EdgeInteractionMode m_mode; - output_mode_t m_output_mode; -}; - -struct ResultInserter -{ - typedef db::Polygon value_type; - - ResultInserter (db::Layout *layout, std::unordered_set &result) - : mp_layout (layout), mp_result (&result) - { - // .. nothing yet .. - } - - void insert (const db::Polygon &p) - { - (*mp_result).insert (db::PolygonRef (p, mp_layout->shape_repository ())); - } - -private: - db::Layout *mp_layout; - std::unordered_set *mp_result; -}; - -class Edge2PolygonPullLocalOperation - : public local_operation -{ -public: - Edge2PolygonPullLocalOperation () - { - // .. nothing yet .. - } - - virtual db::Coord dist () const - { - // touching is sufficient - return 1; - } - - virtual void do_compute_local (db::Layout *layout, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const - { - tl_assert (results.size () == 1); - std::unordered_set &result = results.front (); - - db::box_scanner2 scanner; - - std::set others; - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.intruder_shape (*j).second); - } - } - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - const db::Edge &subject = interactions.subject_shape (i->first); - scanner.insert1 (&subject, 1); - } - - std::list heap; - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - heap.push_back (o->obj ().transformed (o->trans ())); - scanner.insert2 (& heap.back (), 0); - } - - ResultInserter inserter (layout, result); - edge_to_region_interaction_filter filter (&inserter, EdgesInteract); - scanner.process (filter, 1, db::box_convert (), db::box_convert ()); - } - - virtual OnEmptyIntruderHint on_empty_intruder_hint () const - { - return Drop; - } - - virtual std::string description () const - { - return tl::to_string (tr ("Select interacting regions")); - } -}; - -} - EdgesDelegate * -DeepEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse) const +DeepEdges::selected_interacting_generic (const Region &other, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const { std::unique_ptr dr_holder; const db::DeepRegion *other_deep = dynamic_cast (other.delegate ()); @@ -1719,23 +1422,33 @@ DeepEdges::selected_interacting_generic (const Region &other, EdgeInteractionMod other_deep = dr_holder.get (); } + return selected_interacting_generic_impl (other_deep, mode, inverse, min_count, max_count); +} + +EdgesDelegate * +DeepEdges::selected_interacting_generic_impl (const DeepRegion *other_deep, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const +{ + min_count = std::max (size_t (1), min_count); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + const db::DeepLayer &edges = merged_deep_layer (); DeepLayer dl_out (edges.derived ()); - db::Edge2PolygonInteractingLocalOperation op (mode, inverse ? db::Edge2PolygonInteractingLocalOperation::Inverse : db::Edge2PolygonInteractingLocalOperation::Normal); + db::edge_to_polygon_interacting_local_operation op (mode, inverse ? db::edge_to_polygon_interacting_local_operation::Inverse : db::edge_to_polygon_interacting_local_operation::Normal, min_count, max_count); db::local_processor proc (const_cast (&edges.layout ()), const_cast (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), edges.breakout_cells (), other_deep->deep_layer ().breakout_cells ()); proc.set_base_verbosity (base_verbosity ()); proc.set_threads (edges.store ()->threads ()); - proc.run (&op, edges.layer (), (mode == EdgesInside ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), dl_out.layer ()); + // NOTE: with counting the other region needs to be merged + proc.run (&op, edges.layer (), (counting || mode != EdgesInteract ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), dl_out.layer ()); return new db::DeepEdges (dl_out); } std::pair -DeepEdges::selected_interacting_pair_generic (const Region &other, EdgeInteractionMode mode) const +DeepEdges::selected_interacting_pair_generic (const Region &other, EdgeInteractionMode mode, size_t min_count, size_t max_count) const { std::unique_ptr dr_holder; const db::DeepRegion *other_deep = dynamic_cast (other.delegate ()); @@ -1745,6 +1458,15 @@ DeepEdges::selected_interacting_pair_generic (const Region &other, EdgeInteracti other_deep = dr_holder.get (); } + return selected_interacting_pair_generic_impl (other_deep, mode, min_count, max_count); +} + +std::pair +DeepEdges::selected_interacting_pair_generic_impl (const db::DeepRegion *other_deep, EdgeInteractionMode mode, size_t min_count, size_t max_count) const +{ + min_count = std::max (size_t (1), min_count); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + const db::DeepLayer &edges = merged_deep_layer (); DeepLayer dl_out (edges.derived ()); @@ -1755,20 +1477,24 @@ DeepEdges::selected_interacting_pair_generic (const Region &other, EdgeInteracti output_layers.push_back (dl_out.layer ()); output_layers.push_back (dl_out2.layer ()); - db::Edge2PolygonInteractingLocalOperation op (mode, db::Edge2PolygonInteractingLocalOperation::Both); + db::edge_to_polygon_interacting_local_operation op (mode, db::edge_to_polygon_interacting_local_operation::Both, min_count, max_count); db::local_processor proc (const_cast (&edges.layout ()), const_cast (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), edges.breakout_cells (), other_deep->deep_layer ().breakout_cells ()); proc.set_base_verbosity (base_verbosity ()); proc.set_threads (edges.store ()->threads ()); - proc.run (&op, edges.layer (), other_deep->merged_deep_layer ().layer (), output_layers); + // NOTE: with counting the other region needs to be merged + proc.run (&op, edges.layer (), (counting || mode != EdgesInteract ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), output_layers); return std::make_pair (new db::DeepEdges (dl_out), new db::DeepEdges (dl_out2)); } EdgesDelegate * -DeepEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode mode, bool inverse) const +DeepEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const { + min_count = std::max (size_t (1), min_count); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + std::unique_ptr dr_holder; const db::DeepEdges *other_deep = dynamic_cast (other.delegate ()); if (! other_deep) { @@ -1781,20 +1507,24 @@ DeepEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode DeepLayer dl_out (edges.derived ()); - db::Edge2EdgeInteractingLocalOperation op (mode, inverse ? db::Edge2EdgeInteractingLocalOperation::Inverse : db::Edge2EdgeInteractingLocalOperation::Normal); + db::Edge2EdgeInteractingLocalOperation op (mode, inverse ? db::Edge2EdgeInteractingLocalOperation::Inverse : db::Edge2EdgeInteractingLocalOperation::Normal, min_count, max_count); db::local_processor proc (const_cast (&edges.layout ()), const_cast (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), edges.breakout_cells (), other_deep->deep_layer ().breakout_cells ()); proc.set_base_verbosity (base_verbosity ()); proc.set_threads (edges.store ()->threads ()); - proc.run (&op, edges.layer (), (mode == EdgesInside ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), dl_out.layer ()); + // NOTE: with counting the other region needs to be merged + proc.run (&op, edges.layer (), (counting || mode != EdgesInteract ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), dl_out.layer ()); return new db::DeepEdges (dl_out); } std::pair -DeepEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode) const +DeepEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractionMode mode, size_t min_count, size_t max_count) const { + min_count = std::max (size_t (1), min_count); + bool counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + std::unique_ptr dr_holder; const db::DeepEdges *other_deep = dynamic_cast (other.delegate ()); if (! other_deep) { @@ -1813,13 +1543,14 @@ DeepEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractio output_layers.push_back (dl_out.layer ()); output_layers.push_back (dl_out2.layer ()); - db::Edge2EdgeInteractingLocalOperation op (mode, db::Edge2EdgeInteractingLocalOperation::Both); + db::Edge2EdgeInteractingLocalOperation op (mode, db::Edge2EdgeInteractingLocalOperation::Both, min_count, max_count); db::local_processor proc (const_cast (&edges.layout ()), const_cast (&edges.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), edges.breakout_cells (), other_deep->deep_layer ().breakout_cells ()); proc.set_base_verbosity (base_verbosity ()); proc.set_threads (edges.store ()->threads ()); - proc.run (&op, edges.layer (), other_deep->merged_deep_layer ().layer (), output_layers); + // NOTE: with counting the other region needs to be merged + proc.run (&op, edges.layer (), (counting || mode != EdgesInteract ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), output_layers); return std::make_pair (new db::DeepEdges (dl_out), new db::DeepEdges (dl_out2)); } diff --git a/src/db/db/dbDeepEdges.h b/src/db/db/dbDeepEdges.h index 0578d704b..91d685695 100644 --- a/src/db/db/dbDeepEdges.h +++ b/src/db/db/dbDeepEdges.h @@ -188,15 +188,17 @@ private: void init (); void ensure_merged_edges_valid () const; - DeepLayer and_or_not_with(const DeepEdges *other, EdgeBoolOp op) const; + std::pair and_or_not_with (const DeepEdges *other, EdgeBoolOp op) const; std::pair edge_region_op (const DeepRegion *other, EdgePolygonOp::mode_t op, bool include_borders) const; EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, const db::EdgesCheckOptions &options) const; virtual EdgesDelegate *pull_generic (const Edges &edges) const; virtual RegionDelegate *pull_generic (const Region ®ion) const; - virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse) const; - virtual std::pair selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode) const; - virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, EdgeInteractionMode mode, bool inverse) const; - virtual std::pair selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode) const; + virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const; + virtual std::pair selected_interacting_pair_generic (const Edges &edges, EdgeInteractionMode mode, size_t min_count, size_t max_count) const; + virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const; + virtual std::pair selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode, size_t min_count, size_t max_count) const; + EdgesDelegate *selected_interacting_generic_impl (const DeepRegion *other_deep, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const; + std::pair selected_interacting_pair_generic_impl (const DeepRegion *other_deep, EdgeInteractionMode mode, size_t min_count, size_t max_count) const; DeepEdges *apply_filter (const EdgeFilterBase &filter) const; template OutputContainer *processed_impl (const edge_processor &filter) const; diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index f44f0fca7..50173c49e 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1335,7 +1335,7 @@ DeepRegion::snapped (db::Coord gx, db::Coord gy) } EdgesDelegate * -DeepRegion::edges (const EdgeFilterBase *filter) const +DeepRegion::edges (const EdgeFilterBase *filter, const PolygonToEdgeProcessorBase *proc) const { std::unique_ptr res (new db::DeepEdges (deep_layer ().derived ())); @@ -1343,7 +1343,7 @@ DeepRegion::edges (const EdgeFilterBase *filter) const return res.release (); } - if (! filter && merged_semantics () && ! merged_polygons_available ()) { + if (! proc && ! filter && merged_semantics () && ! merged_polygons_available ()) { // Hierarchical edge detector - no pre-merge required @@ -1388,15 +1388,32 @@ DeepRegion::edges (const EdgeFilterBase *filter) const const db::Shapes &s = c->shapes (polygons.layer ()); db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + std::vector heap; + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { db::Polygon poly; si->polygon (poly); - for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { - if (! filter || filter->selected ((*e).transformed (tr))) { - st.insert (db::EdgeWithProperties (*e, pm (si->prop_id ()))); + if (proc) { + + heap.clear (); + proc->process (poly, heap); + + for (auto e = heap.begin (); e != heap.end (); ++e) { + if (! filter || filter->selected ((*e).transformed (tr))) { + st.insert (db::EdgeWithProperties (*e, pm (si->prop_id ()))); + } } + + } else { + + for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { + if (! filter || filter->selected ((*e).transformed (tr))) { + st.insert (db::EdgeWithProperties (*e, pm (si->prop_id ()))); + } + } + } } diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 229b2e696..1d5537554 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -119,7 +119,7 @@ public: virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy); - virtual EdgesDelegate *edges (const EdgeFilterBase *) const; + virtual EdgesDelegate *edges (const EdgeFilterBase *filter, const db::PolygonToEdgeProcessorBase *proc) const; virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter); virtual RegionDelegate *processed (const PolygonProcessorBase &filter) const; diff --git a/src/db/db/dbEdgeBoolean.h b/src/db/db/dbEdgeBoolean.h index 11c4e6ed7..d9600f551 100644 --- a/src/db/db/dbEdgeBoolean.h +++ b/src/db/db/dbEdgeBoolean.h @@ -86,14 +86,14 @@ struct EdgeBooleanCluster { typedef db::Edge::coord_type coord_type; - EdgeBooleanCluster (OutputContainer *output, EdgeBoolOp op) - : mp_output (output), mp_output2 (0), m_op (op) + EdgeBooleanCluster (OutputContainer *output, std::set *dots, EdgeBoolOp op) + : mp_output (output), mp_output2 (0), mp_dots (dots), mp_dots2 (0), m_op (op) { // .. nothing yet .. } - EdgeBooleanCluster (OutputContainer *output, OutputContainer *output2, EdgeBoolOp op) - : mp_output (output), mp_output2 (output2), m_op (op) + EdgeBooleanCluster (OutputContainer *output, OutputContainer *output2, std::set *dots, std::set *dots2, EdgeBoolOp op) + : mp_output (output), mp_output2 (output2), mp_dots (dots), mp_dots2 (dots2), m_op (op) { // .. nothing yet .. } @@ -106,24 +106,55 @@ struct EdgeBooleanCluster if (begin () + 1 == end ()) { if (begin ()->second == 0) { if (m_op == EdgeAndNot) { - mp_output2->insert (*(begin ()->first)); + if (begin ()->first->is_degenerate ()) { + if (mp_dots) { + mp_dots2->insert (begin ()->first->p1 ()); + } + } else if (mp_output2) { + mp_output2->insert (*(begin ()->first)); + } } else if (m_op != EdgeAnd) { - mp_output->insert (*(begin ()->first)); + if (begin ()->first->is_degenerate ()) { + if (mp_dots) { + mp_dots->insert (begin ()->first->p1 ()); + } + } else if (mp_output) { + mp_output->insert (*(begin ()->first)); + } } } else { if (m_op != EdgeAnd && m_op != EdgeNot && m_op != EdgeAndNot) { - mp_output->insert (*(begin ()->first)); + if (begin ()->first->is_degenerate ()) { + if (mp_dots) { + mp_dots->insert (begin ()->first->p1 ()); + } + } else if (mp_output) { + mp_output->insert (*(begin ()->first)); + } } } return; } - db::Edge r = *begin ()->first; + // search first non-degenerate, longest + + iterator main = end (); + for (iterator i = begin (); i != end (); ++i) { + if (! i->first->is_degenerate () && (main == end () || main->first->length () < i->first->length ())) { + main = i; + } + } + + if (main == end ()) { + return; + } + + db::Edge r = *main->first; double l1 = 0.0, l2 = r.double_length (); double n = 1.0 / l2; db::Point p1 = r.p1 (), p2 = r.p2 (); - for (iterator o = begin () + 1; o != end (); ++o) { + for (iterator o = begin (); o != end (); ++o) { double ll1 = db::sprod (db::Vector (o->first->p1 () - r.p1 ()), r.d ()) * n; double ll2 = db::sprod (db::Vector (o->first->p2 () - r.p1 ()), r.d ()) * n; if (ll1 < l1) { @@ -245,6 +276,7 @@ struct EdgeBooleanCluster private: OutputContainer *mp_output, *mp_output2; + std::set *mp_dots, *mp_dots2; db::EdgeBoolOp m_op; }; @@ -253,15 +285,8 @@ struct EdgeBooleanClusterCollector : public db::cluster_collector > { EdgeBooleanClusterCollector (OutputContainer *output, EdgeBoolOp op, OutputContainer *output2 = 0) - : db::cluster_collector > (EdgeBooleanCluster (output, output2, op == EdgeIntersections ? EdgeAnd : op), op != EdgeAnd && op != EdgeIntersections /*report single*/), - mp_output (output), mp_intersections (op == EdgeIntersections ? output : 0) - { - // .. nothing yet .. - } - - EdgeBooleanClusterCollector (OutputContainer *output, OutputContainer *intersections, EdgeBoolOp op) - : db::cluster_collector > (EdgeBooleanCluster (output, op), op != EdgeAnd /*report single*/), - mp_output (output), mp_intersections (intersections) + : db::cluster_collector > (EdgeBooleanCluster (output, output2, &m_dots, &m_dots2, op == EdgeIntersections ? EdgeAnd : op), op != EdgeAnd && op != EdgeIntersections /*report single*/), + mp_output (output), mp_output2 (output2), m_op (op) { // .. nothing yet .. } @@ -281,11 +306,78 @@ struct EdgeBooleanClusterCollector db::cluster_collector >::add (o1, p1, o2, p2); - } else if (mp_intersections && p1 != p2) { + } else { + + // dots vs. edge or dot is handled here, no need to copy dots + if (o1->is_degenerate ()) { + this->ignore_single (o1); + } + if (o2->is_degenerate ()) { + this->ignore_single (o2); + } + + if (m_op == EdgeIntersections) { + + if (p1 != p2) { + std::pair ip = o1->intersect_point (*o2); + if (ip.first) { + m_dots.insert (ip.second); + } + } + + } else if (m_op == EdgeAndNot) { + + // handle case of dot vs. edge or dot + if (p1 != p2 && (o1->is_degenerate () || o2->is_degenerate ())) { + std::pair ip = o1->intersect_point (*o2); + if (ip.first) { + m_dots.insert (ip.second); + } + if (o1->is_degenerate () && ! ip.first) { + m_dots2.insert (o1->p1 ()); + } + } + + } else if (m_op == EdgeAnd) { + + // handle case of dot vs. edge or dot + if (p1 != p2 && (o1->is_degenerate () || o2->is_degenerate ())) { + std::pair ip = o1->intersect_point (*o2); + if (ip.first) { + m_dots.insert (ip.second); + } + } + + } else if (m_op == EdgeNot) { + + // handle case of dot vs. edge or dot + if (p1 != p2 && o1->is_degenerate ()) { + std::pair ip = o1->intersect_point (*o2); + if (! ip.first) { + m_dots.insert (o1->p1 ()); + } + } + + } else if (m_op == EdgeOr) { + + // forward dots + if (o1->is_degenerate ()) { + m_dots.insert (o1->p1 ()); + } + if (o2->is_degenerate ()) { + m_dots.insert (o2->p1 ()); + } + + } else if (m_op == EdgeXor) { + + // handle case of dot vs. edge or dot + if (p1 != p2 && o1->is_degenerate () && o2->is_degenerate ()) { + if (o1->p1 () != o2->p1 ()) { + m_dots.insert (o1->p1 ()); + m_dots.insert (o2->p1 ()); + } + } - std::pair ip = o1->intersect_point (*o2); - if (ip.first) { - m_intersections.insert (ip.second); } } @@ -340,35 +432,44 @@ struct EdgeBooleanClusterCollector }; /** - * @brief Finalizes the implementation for "EdgeIntersections" + * @brief Finalizes the implementation for "sections" * This method pushes those points which don't interact with the edges to the output container * as degenerate edges. It needs to be called after the pass has been made. */ void finalize (bool) { - if (m_intersections.empty ()) { + add_orphan_dots (m_dots, mp_output); + if (mp_output2) { + add_orphan_dots (m_dots2, mp_output2); + } + } + +private: + OutputContainer *mp_output, *mp_output2; + EdgeBoolOp m_op; + std::set m_dots, m_dots2; + + static void add_orphan_dots (const std::set &dots, OutputContainer *output) + { + if (dots.empty ()) { return; } - db::box_scanner2 intersections_to_edge_scanner; - for (typename OutputContainer::const_iterator e = mp_output->begin (); e != mp_output->end (); ++e) { - intersections_to_edge_scanner.insert1 (e.operator-> (), 0); + db::box_scanner2 dots_to_edge_scanner; + for (typename OutputContainer::const_iterator e = output->begin (); e != output->end (); ++e) { + dots_to_edge_scanner.insert1 (e.operator-> (), 0); } - for (std::set::const_iterator p = m_intersections.begin (); p != m_intersections.end (); ++p) { - intersections_to_edge_scanner.insert2 (p.operator-> (), 0); + for (std::set::const_iterator p = dots.begin (); p != dots.end (); ++p) { + dots_to_edge_scanner.insert2 (p.operator-> (), 0); } std::set points_to_remove; RemovePointsOnEdges rpoe (points_to_remove); - intersections_to_edge_scanner.process (rpoe, 1, db::box_convert (), db::box_convert ()); + dots_to_edge_scanner.process (rpoe, 1, db::box_convert (), db::box_convert ()); + + std::set_difference (dots.begin (), dots.end (), points_to_remove.begin (), points_to_remove.end (), PointInserter (output)); - std::set_difference (m_intersections.begin (), m_intersections.end (), points_to_remove.begin (), points_to_remove.end (), PointInserter (mp_intersections)); } - -private: - OutputContainer *mp_output; - OutputContainer *mp_intersections; - std::set m_intersections; }; /** diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index e5317f20d..28decbbb6 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -991,9 +991,9 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_interacting (const Region &other) + Edges &select_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) { - set_delegate (mp_delegate->selected_interacting (other)); + set_delegate (mp_delegate->selected_interacting (other, min_count, max_count)); return *this; } @@ -1002,9 +1002,9 @@ public: * * This method is an out-of-place version of select_interacting. */ - Edges selected_interacting (const Region &other) const + Edges selected_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - return Edges (mp_delegate->selected_interacting (other)); + return Edges (mp_delegate->selected_interacting (other, min_count, max_count)); } /** @@ -1013,9 +1013,9 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_not_interacting (const Region &other) + Edges &select_not_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) { - set_delegate (mp_delegate->selected_not_interacting (other)); + set_delegate (mp_delegate->selected_not_interacting (other, min_count, max_count)); return *this; } @@ -1024,17 +1024,17 @@ public: * * This method is an out-of-place version of select_not_interacting. */ - Edges selected_not_interacting (const Region &other) const + Edges selected_not_interacting (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - return Edges (mp_delegate->selected_not_interacting (other)); + return Edges (mp_delegate->selected_not_interacting (other, min_count, max_count)); } /** * @brief Returns all edges of this edge set which do not overlap or touch with polygons from the region together with the ones that do not */ - std::pair selected_interacting_differential (const Region &other) const + std::pair selected_interacting_differential (const Region &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - std::pair p = mp_delegate->selected_interacting_pair (other); + std::pair p = mp_delegate->selected_interacting_pair (other, min_count, max_count); return std::pair (Edges (p.first), Edges (p.second)); } @@ -1280,9 +1280,9 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_interacting (const Edges &other) + Edges &select_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) { - set_delegate (mp_delegate->selected_interacting (other)); + set_delegate (mp_delegate->selected_interacting (other, min_count, max_count)); return *this; } @@ -1291,17 +1291,17 @@ public: * * This method is an out-of-place version of select_interacting. */ - Edges selected_interacting (const Edges &other) const + Edges selected_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - return Edges (mp_delegate->selected_interacting (other)); + return Edges (mp_delegate->selected_interacting (other, min_count, max_count)); } /** * @brief Returns all edges of this edge set which do not overlap or touch with edges from the other edge set together with the ones that do not */ - std::pair selected_interacting_differential (const Edges &other) const + std::pair selected_interacting_differential (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - std::pair p = mp_delegate->selected_interacting_pair (other); + std::pair p = mp_delegate->selected_interacting_pair (other, min_count, max_count); return std::pair (Edges (p.first), Edges (p.second)); } @@ -1311,9 +1311,9 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_not_interacting (const Edges &other) + Edges &select_not_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) { - set_delegate (mp_delegate->selected_not_interacting (other)); + set_delegate (mp_delegate->selected_not_interacting (other, min_count, max_count)); return *this; } @@ -1322,9 +1322,9 @@ public: * * This method is an out-of-place version of select_not_interacting. */ - Edges selected_not_interacting (const Edges &other) const + Edges selected_not_interacting (const Edges &other, size_t min_count = 1, size_t max_count = std::numeric_limits::max ()) const { - return Edges (mp_delegate->selected_not_interacting (other)); + return Edges (mp_delegate->selected_not_interacting (other, min_count, max_count)); } /** diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index 051d671ee..5215da02f 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -255,12 +255,12 @@ public: virtual std::pair inside_outside_part_pair (const Region &other) const = 0; virtual RegionDelegate *pull_interacting (const Region &) const = 0; virtual EdgesDelegate *pull_interacting (const Edges &) const = 0; - virtual EdgesDelegate *selected_interacting (const Region &other) const = 0; - virtual EdgesDelegate *selected_not_interacting (const Region &other) const = 0; - virtual EdgesDelegate *selected_interacting (const Edges &other) const = 0; - virtual EdgesDelegate *selected_not_interacting (const Edges &other) const = 0; - virtual std::pair selected_interacting_pair (const Region &other) const = 0; - virtual std::pair selected_interacting_pair (const Edges &other) const = 0; + virtual EdgesDelegate *selected_interacting (const Region &other, size_t min_count, size_t max_count) const = 0; + virtual EdgesDelegate *selected_not_interacting (const Region &other, size_t min_count, size_t max_count) const = 0; + virtual EdgesDelegate *selected_interacting (const Edges &other, size_t min_count, size_t max_count) const = 0; + virtual EdgesDelegate *selected_not_interacting (const Edges &other, size_t min_count, size_t max_count) const = 0; + virtual std::pair selected_interacting_pair (const Region &other, size_t min_count, size_t max_count) const = 0; + virtual std::pair selected_interacting_pair (const Edges &other, size_t min_count, size_t max_count) const = 0; virtual EdgesDelegate *selected_outside (const Region &other) const = 0; virtual EdgesDelegate *selected_not_outside (const Region &other) const = 0; diff --git a/src/db/db/dbEdgesLocalOperations.cc b/src/db/db/dbEdgesLocalOperations.cc index e201df8b5..3572b131c 100644 --- a/src/db/db/dbEdgesLocalOperations.cc +++ b/src/db/db/dbEdgesLocalOperations.cc @@ -94,6 +94,9 @@ EdgeBoolAndOrNotLocalOperation::do_compute_local (db::Layout * /*layout*/, db::C if (! is_and) { result.insert (subject); } + if (result2) { + result2->insert (subject); + } } else { scanner.insert (&subject, 0); any_subject = true; @@ -199,5 +202,357 @@ EdgeToPolygonLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell } } +// --------------------------------------------------------------------------------------------- +// Edge2EdgeInteractingLocalOperation implementation + +Edge2EdgeInteractingLocalOperation::Edge2EdgeInteractingLocalOperation (EdgeInteractionMode mode, output_mode_t output_mode, size_t min_count, size_t max_count) + : m_mode (mode), m_output_mode (output_mode), m_min_count (min_count), m_max_count (max_count) +{ + // .. nothing yet .. +} + +db::Coord Edge2EdgeInteractingLocalOperation::dist () const +{ + // touching is sufficient + return 1; +} + +void Edge2EdgeInteractingLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const +{ + tl_assert (results.size () == (m_output_mode == Both ? 2 : 1)); + + std::unordered_set &result = results.front (); + + std::unordered_set *result2 = 0; + if (m_output_mode == Both) { + result2 = &results[1]; + } + + db::box_scanner scanner; + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::Edge &subject = interactions.subject_shape (i->first); + scanner.insert (&subject, 0); + } + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + scanner.insert (o.operator-> (), 1); + } + + if (m_output_mode == Inverse || m_output_mode == Both) { + + std::unordered_set interacting; + edge_interaction_filter > filter (interacting, m_mode, m_min_count, m_max_count); + scanner.process (filter, 1, db::box_convert ()); + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + const db::Edge &subject = interactions.subject_shape (i->first); + if (interacting.find (subject) == interacting.end ()) { + if (m_output_mode != Both) { + result.insert (subject); + } else { + result2->insert (subject); + } + } else if (m_output_mode == Both) { + result.insert (subject); + } + + } + + } else { + + edge_interaction_filter > filter (result, m_mode, m_min_count, m_max_count); + scanner.process (filter, 1, db::box_convert ()); + + } + +} + +OnEmptyIntruderHint Edge2EdgeInteractingLocalOperation::on_empty_intruder_hint () const +{ + if (m_mode == EdgesOutside) { + return m_output_mode == Both ? Copy : (m_output_mode == Inverse ? Drop : Copy); + } else { + return m_output_mode == Both ? CopyToSecond : (m_output_mode == Inverse ? Copy : Drop); + } +} + +std::string Edge2EdgeInteractingLocalOperation::description () const +{ + return tl::to_string (tr ("Select interacting edges")); +} + +// --------------------------------------------------------------------------------------------- +// Edge2EdgePullLocalOperation implementation + +Edge2EdgePullLocalOperation::Edge2EdgePullLocalOperation () +{ + // .. nothing yet .. +} + +db::Coord Edge2EdgePullLocalOperation::dist () const +{ + // touching is sufficient + return 1; +} + +void Edge2EdgePullLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const +{ + tl_assert (results.size () == 1); + std::unordered_set &result = results.front (); + + db::box_scanner scanner; + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::Edge &subject = interactions.subject_shape (i->first); + scanner.insert (&subject, 1); + } + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + scanner.insert (o.operator-> (), 0); + } + + edge_interaction_filter > filter (result, EdgesInteract, size_t (1), std::numeric_limits::max ()); + scanner.process (filter, 1, db::box_convert ()); + +} + +OnEmptyIntruderHint Edge2EdgePullLocalOperation::on_empty_intruder_hint () const +{ + return Drop; +} + +std::string Edge2EdgePullLocalOperation::description () const +{ + return tl::to_string (tr ("Select interacting edges from other")); +} + + +// --------------------------------------------------------------------------------------------- +// Edge2EdgePullLocalOperation implementation + +template +edge_to_polygon_interacting_local_operation::edge_to_polygon_interacting_local_operation (EdgeInteractionMode mode, output_mode_t output_mode, size_t min_count, size_t max_count) + : m_mode (mode), m_output_mode (output_mode), m_min_count (min_count), m_max_count (max_count) +{ + // .. nothing yet .. +} + +template +db::Coord edge_to_polygon_interacting_local_operation::dist () const +{ + // touching is sufficient + return 1; +} + +static const db::Polygon *deref (const db::Polygon &poly, std::list &) +{ + return &poly; +} + +static const db::Polygon *deref (const db::PolygonRef &pref, std::list &heap) +{ + heap.push_back (pref.obj ().transformed (pref.trans ())); + return & heap.back (); +} + +template +void edge_to_polygon_interacting_local_operation::do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const +{ + tl_assert (results.size () == size_t (m_output_mode == Both ? 2 : 1)); + + std::unordered_set &result = results.front (); + + std::unordered_set *result2 = 0; + if (m_output_mode == Both) { + result2 = &results[1]; + } + + db::box_scanner2 scanner; + + std::set others; + for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (typename shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::Edge &subject = interactions.subject_shape (i->first); + scanner.insert1 (&subject, 0); + } + + std::list heap; + for (typename std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + scanner.insert2 (deref (*o, heap), 1); + } + + if (m_output_mode == Inverse || m_output_mode == Both) { + + std::unordered_set interacting; + edge_to_polygon_interaction_filter > filter (&interacting, m_mode, m_min_count, m_max_count); + scanner.process (filter, 1, db::box_convert (), db::box_convert ()); + + for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + const db::Edge &subject = interactions.subject_shape (i->first); + + if (interacting.find (subject) == interacting.end ()) { + if (m_output_mode != Both) { + result.insert (subject); + } else { + result2->insert (subject); + } + } else if (m_output_mode == Both) { + result.insert (subject); + } + + } + + } else { + + edge_to_polygon_interaction_filter > filter (&result, m_mode, m_min_count, m_max_count); + scanner.process (filter, 1, db::box_convert (), db::box_convert ()); + + } +} + +template +OnEmptyIntruderHint edge_to_polygon_interacting_local_operation::on_empty_intruder_hint () const +{ + if (m_mode == EdgesOutside) { + return m_output_mode == Both ? Copy : (m_output_mode == Inverse ? Drop : Copy); + } else { + return m_output_mode == Both ? CopyToSecond : (m_output_mode == Inverse ? Copy : Drop); + } +} + +template +std::string edge_to_polygon_interacting_local_operation::description () const +{ + if (m_mode == EdgesInteract) { + if (m_output_mode == Inverse) { + return tl::to_string (tr ("Select non-interacting edges")); + } else if (m_output_mode == Normal) { + return tl::to_string (tr ("Select interacting edges")); + } else { + return tl::to_string (tr ("Select interacting and non-interacting edges")); + } + } else if (m_mode == EdgesInside) { + if (m_output_mode == Inverse) { + return tl::to_string (tr ("Select non-inside edges")); + } else if (m_output_mode == Normal) { + return tl::to_string (tr ("Select inside edges")); + } else { + return tl::to_string (tr ("Select inside and non-inside edges")); + } + } else if (m_mode == EdgesOutside) { + if (m_output_mode == Inverse) { + return tl::to_string (tr ("Select non-outside edges")); + } else if (m_output_mode == Normal) { + return tl::to_string (tr ("Select outside edges")); + } else { + return tl::to_string (tr ("Select outside and non-outside edges")); + } + } + return std::string (); +} + +template class edge_to_polygon_interacting_local_operation; +template class edge_to_polygon_interacting_local_operation; + +// --------------------------------------------------------------------------------------------- +// Edge2EdgePullLocalOperation implementation + +namespace { + +struct ResultInserter +{ + typedef db::Polygon value_type; + + ResultInserter (db::Layout *layout, std::unordered_set &result) + : mp_layout (layout), mp_result (&result) + { + // .. nothing yet .. + } + + void insert (const db::Polygon &p) + { + (*mp_result).insert (db::PolygonRef (p, mp_layout->shape_repository ())); + } + +private: + db::Layout *mp_layout; + std::unordered_set *mp_result; +}; + +} + +Edge2PolygonPullLocalOperation::Edge2PolygonPullLocalOperation () +{ + // .. nothing yet .. +} + +db::Coord Edge2PolygonPullLocalOperation::dist () const +{ + // touching is sufficient + return 1; +} + +void Edge2PolygonPullLocalOperation::do_compute_local (db::Layout *layout, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const +{ + tl_assert (results.size () == 1); + std::unordered_set &result = results.front (); + + db::box_scanner2 scanner; + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::Edge &subject = interactions.subject_shape (i->first); + scanner.insert1 (&subject, 1); + } + + std::list heap; + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + heap.push_back (o->obj ().transformed (o->trans ())); + scanner.insert2 (& heap.back (), 0); + } + + ResultInserter inserter (layout, result); + edge_to_polygon_interaction_filter filter (&inserter, EdgesInteract, size_t (1), std::numeric_limits::max ()); + scanner.process (filter, 1, db::box_convert (), db::box_convert ()); +} + +OnEmptyIntruderHint Edge2PolygonPullLocalOperation::on_empty_intruder_hint () const +{ + return Drop; +} + +std::string Edge2PolygonPullLocalOperation::description () const +{ + return tl::to_string (tr ("Select interacting regions")); +} + } diff --git a/src/db/db/dbEdgesLocalOperations.h b/src/db/db/dbEdgesLocalOperations.h index 9a5f9bdc4..b0e6f82f4 100644 --- a/src/db/db/dbEdgesLocalOperations.h +++ b/src/db/db/dbEdgesLocalOperations.h @@ -31,6 +31,7 @@ #include "dbEdgeBoolean.h" #include "dbEdgeProcessor.h" #include "dbLocalOperation.h" +#include "dbEdgesUtils.h" namespace db { @@ -79,6 +80,81 @@ private: bool m_include_borders; }; +/** + * @brief Implements edge-to-edge interactions + */ +class DB_PUBLIC Edge2EdgeInteractingLocalOperation + : public local_operation +{ +public: + enum output_mode_t { Normal, Inverse, Both }; + + Edge2EdgeInteractingLocalOperation (EdgeInteractionMode mode, output_mode_t output_mode, size_t min_count, size_t max_count); + + virtual db::Coord dist () const; + virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + EdgeInteractionMode m_mode; + output_mode_t m_output_mode; + size_t m_min_count, m_max_count; +}; + +/** + * @brief Implements edge-to-edge interactions (pull mode) + */ +class DB_PUBLIC Edge2EdgePullLocalOperation + : public local_operation +{ +public: + Edge2EdgePullLocalOperation (); + + virtual db::Coord dist () const; + virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; +}; + +/** + * @brief Implements edge-to-polygon interactions + */ +template +class DB_PUBLIC edge_to_polygon_interacting_local_operation + : public local_operation +{ +public: + enum output_mode_t { Normal, Inverse, Both }; + + edge_to_polygon_interacting_local_operation (EdgeInteractionMode mode, output_mode_t output_mode, size_t min_count, size_t max_count); + + virtual db::Coord dist () const; + virtual void do_compute_local (db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + EdgeInteractionMode m_mode; + output_mode_t m_output_mode; + size_t m_min_count, m_max_count; +}; + +/** + * @brief Implements edge-to-polygon interactions (pull mode) + */ +class DB_PUBLIC Edge2PolygonPullLocalOperation + : public local_operation +{ +public: + Edge2PolygonPullLocalOperation (); + + virtual db::Coord dist () const; + virtual void do_compute_local (db::Layout *layout, db::Cell * /*cell*/, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; +}; + } #endif diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index 686cce8c9..56d345b2a 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -189,17 +189,22 @@ EdgeSegmentSelector::process (const db::Edge &edge, std::vector &res) { double l = std::max (edge.double_length () * m_fraction, double (m_length)); + db::DVector ds; + if (! edge.is_degenerate ()) { + ds = db::DVector (edge.d ()) * (l / edge.double_length ()); + } + if (m_mode < 0) { - res.push_back (db::Edge (edge.p1 (), db::Point (db::DPoint (edge.p1 ()) + db::DVector (edge.d ()) * (l / edge.double_length ())))); + res.push_back (db::Edge (edge.p1 (), db::Point (db::DPoint (edge.p1 ()) + ds))); } else if (m_mode > 0) { - res.push_back (db::Edge (db::Point (db::DPoint (edge.p2 ()) - db::DVector (edge.d ()) * (l / edge.double_length ())), edge.p2 ())); + res.push_back (db::Edge (db::Point (db::DPoint (edge.p2 ()) - ds), edge.p2 ())); } else { - db::DVector dl = db::DVector (edge.d ()) * (0.5 * l / edge.double_length ()); + db::DVector dl = ds * 0.5; db::DPoint center = db::DPoint (edge.p1 ()) + db::DVector (edge.p2 () - edge.p1 ()) * 0.5; res.push_back (db::Edge (db::Point (center - dl), db::Point (center + dl))); @@ -403,16 +408,24 @@ struct DetectTagEdgeSink static bool edge_is_inside_or_outside (bool outside, const db::Edge &a, const db::Polygon &b) { - db::EdgeProcessor ep; - ep.insert (b, 0); + if (a.is_degenerate ()) { - ep.insert (a, 1); + return ((db::inside_poly (b.begin_edge (), a.p1 ()) <= 0) == outside); - DetectTagEdgeSink es (outside ? 1 : 2); // 2 is the "outside" tag in "Both" mode -> this makes inside fail - db::EdgePolygonOp op (db::EdgePolygonOp::Both, true /*include borders*/); - ep.process (es, op); + } else { - return es.result; + db::EdgeProcessor ep; + ep.insert (b, 0); + + ep.insert (a, 1); + + DetectTagEdgeSink es (outside ? 1 : 2); // 2 is the "outside" tag in "Both" mode -> this makes inside fail + db::EdgePolygonOp op (db::EdgePolygonOp::Both, !outside /*include borders in inside*/); + ep.process (es, op); + + return es.result; + + } } bool edge_is_inside (const db::Edge &a, const db::Polygon &b) diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h index 939cd6140..531a18fca 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -339,16 +339,39 @@ class edge_interaction_filter : public db::box_scanner_receiver { public: - edge_interaction_filter (OutputContainer &output, EdgeInteractionMode mode) - : mp_output (&output), m_mode (mode) + edge_interaction_filter (OutputContainer &output, EdgeInteractionMode mode, size_t min_count, size_t max_count) + : mp_output (&output), m_mode (mode), m_min_count (min_count), m_max_count (max_count) { - // .. nothing yet .. + // NOTE: "counting" does not really make much sense in Outside mode ... + m_counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + tl_assert (!m_counting || mode != EdgesOutside); } void finish (const db::Edge *o, size_t p) { - if (p == 0 && m_mode == EdgesOutside && m_seen.find (o) == m_seen.end ()) { - mp_output->insert (*o); + if (p != 0) { + return; + } + + if (m_counting) { + + size_t count = 0; + auto i = m_counts.find (o); + if (i != m_counts.end ()) { + count = i->second; + } + + bool match = (count >= m_min_count && count <= m_max_count); + if (match == (m_mode != EdgesOutside)) { + mp_output->insert (*o); + } + + } else { + + if (m_mode == EdgesOutside && m_seen.find (o) == m_seen.end ()) { + mp_output->insert (*o); + } + } } @@ -363,14 +386,22 @@ public: if ((m_mode == EdgesInteract && db::edge_interacts (*o, *oo)) || (m_mode == EdgesInside && db::edge_is_inside (*o, *oo))) { - if (m_seen.insert (o).second) { - mp_output->insert (*o); + if (m_counting) { + m_counts[o] += 1; + } else { + if (m_seen.insert (o).second) { + mp_output->insert (*o); + } } } else if (m_mode == EdgesOutside && ! db::edge_is_outside (*o, *oo)) { // In this case we need to collect edges which are outside always - we report those on "finished". - m_seen.insert (o); + if (m_counting) { + m_counts[o] += 1; + } else { + m_seen.insert (o); + } } @@ -380,7 +411,10 @@ public: private: OutputContainer *mp_output; std::set m_seen; + std::map m_counts; EdgeInteractionMode m_mode; + size_t m_min_count, m_max_count; + bool m_counting; }; /** @@ -408,20 +442,39 @@ DB_PUBLIC bool edge_is_outside (const db::Edge &a, const db::Polygon &b); * There is a special box converter which is able to sort that out as well. */ template -class edge_to_region_interaction_filter +class edge_to_polygon_interaction_filter : public db::box_scanner_receiver2 { public: - edge_to_region_interaction_filter (OutputContainer *output, EdgeInteractionMode mode) - : mp_output (output), m_mode (mode) + edge_to_polygon_interaction_filter (OutputContainer *output, EdgeInteractionMode mode, size_t min_count, size_t max_count) + : mp_output (output), m_mode (mode), m_min_count (min_count), m_max_count (max_count) { - // .. nothing yet .. + // NOTE: "counting" does not really make much sense in Outside mode ... + m_counting = !(min_count == 1 && max_count == std::numeric_limits::max ()); + tl_assert (!m_counting || mode != EdgesOutside); } void finish (const OutputType *o) { - if (m_mode == EdgesOutside && m_seen.find (o) == m_seen.end ()) { - mp_output->insert (*o); + if (m_counting) { + + size_t count = 0; + auto i = m_counts.find (o); + if (i != m_counts.end ()) { + count = i->second; + } + + bool match = (count >= m_min_count && count <= m_max_count); + if (match == (m_mode != EdgesOutside)) { + mp_output->insert (*o); + } + + } else { + + if (m_mode == EdgesOutside && m_seen.find (o) == m_seen.end ()) { + mp_output->insert (*o); + } + } } @@ -448,7 +501,18 @@ public: const OutputType *ep = 0; tl::select (ep, e, p); - if (m_seen.find (ep) == m_seen.end ()) { + if (m_counting) { + + if ((m_mode == EdgesInteract && db::edge_interacts (*e, *p)) || + (m_mode == EdgesInside && db::edge_is_inside (*e, *p)) || + (m_mode == EdgesOutside && ! db::edge_is_outside (*e, *p))) { + + // we report the result on "finish" here. + m_counts[ep] += 1; + + } + + } else if (m_seen.find (ep) == m_seen.end ()) { if ((m_mode == EdgesInteract && db::edge_interacts (*e, *p)) || (m_mode == EdgesInside && db::edge_is_inside (*e, *p))) { @@ -468,8 +532,11 @@ public: private: OutputContainer *mp_output; + std::map m_counts; std::set m_seen; EdgeInteractionMode m_mode; + size_t m_min_count, m_max_count; + bool m_counting; }; /** diff --git a/src/db/db/dbEmptyEdges.h b/src/db/db/dbEmptyEdges.h index 92bab3379..ec265156e 100644 --- a/src/db/db/dbEmptyEdges.h +++ b/src/db/db/dbEmptyEdges.h @@ -94,12 +94,12 @@ public: virtual RegionDelegate *pull_interacting (const Region &) const; virtual EdgesDelegate *pull_interacting (const Edges &) const { return new EmptyEdges (); } - virtual EdgesDelegate *selected_interacting (const Edges &) const { return new EmptyEdges (); } - virtual EdgesDelegate *selected_not_interacting (const Edges &) const { return new EmptyEdges (); } - virtual EdgesDelegate *selected_interacting (const Region &) const { return new EmptyEdges (); } - virtual EdgesDelegate *selected_not_interacting (const Region &) const { return new EmptyEdges (); } - virtual std::pair selected_interacting_pair (const Region &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); } - virtual std::pair selected_interacting_pair (const Edges &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); } + virtual EdgesDelegate *selected_interacting (const Edges &, size_t, size_t) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_not_interacting (const Edges &, size_t, size_t) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_interacting (const Region &, size_t, size_t) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_not_interacting (const Region &, size_t, size_t) const { return new EmptyEdges (); } + virtual std::pair selected_interacting_pair (const Region &, size_t, size_t) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); } + virtual std::pair selected_interacting_pair (const Edges &, size_t, size_t) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); } virtual EdgesDelegate *selected_outside (const Region &) const { return new EmptyEdges (); } virtual EdgesDelegate *selected_not_outside (const Region &) const { return new EmptyEdges (); } diff --git a/src/db/db/dbEmptyRegion.cc b/src/db/db/dbEmptyRegion.cc index 49f87313b..a9b5e36e0 100644 --- a/src/db/db/dbEmptyRegion.cc +++ b/src/db/db/dbEmptyRegion.cc @@ -176,7 +176,7 @@ EmptyRegion::angle_check (double, double, bool) const } EdgesDelegate * -EmptyRegion::edges (const EdgeFilterBase *) const +EmptyRegion::edges (const EdgeFilterBase *, const PolygonToEdgeProcessorBase *) const { return new EmptyEdges (); } diff --git a/src/db/db/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h index 874945a7b..086ea9837 100644 --- a/src/db/db/dbEmptyRegion.h +++ b/src/db/db/dbEmptyRegion.h @@ -82,7 +82,7 @@ public: virtual RegionDelegate *scaled_and_snapped_in_place (db::Coord, db::Coord, db::Coord, db::Coord, db::Coord, db::Coord) { return this; } virtual RegionDelegate *scaled_and_snapped (db::Coord, db::Coord, db::Coord, db::Coord, db::Coord, db::Coord) { return new EmptyRegion (); } - virtual EdgesDelegate *edges (const EdgeFilterBase *) const; + virtual EdgesDelegate *edges (const EdgeFilterBase *, const PolygonToEdgeProcessorBase *) const; virtual RegionDelegate *filter_in_place (const PolygonFilterBase &) { return this; } virtual RegionDelegate *filtered (const PolygonFilterBase &) const { return new EmptyRegion (); } virtual RegionDelegate *process_in_place (const PolygonProcessorBase &) { return this; } diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index 085bb255f..8062902fb 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -133,9 +133,7 @@ FlatEdges::ensure_merged_edges_valid () const scanner.reserve (mp_edges->size ()); for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (&*e, 0); - } + scanner.insert (&*e, 0); } scanner.process (cluster_collector, 1, db::box_convert ()); @@ -145,9 +143,7 @@ FlatEdges::ensure_merged_edges_valid () const std::map > edges_by_props; for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - edges_by_props [e.prop_id ()].push_back (e.operator-> ()); - } + edges_by_props [e.prop_id ()].push_back (e.operator-> ()); } for (auto s2p = edges_by_props.begin (); s2p != edges_by_props.end (); ++s2p) { diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 75e1ad5b2..b2b5ae467 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -2575,6 +2575,7 @@ template class DB_PUBLIC local_processor_cell_contexts; template class DB_PUBLIC local_processor_cell_contexts; template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; template class DB_PUBLIC local_processor_cell_contexts; template class DB_PUBLIC local_processor_cell_contexts; @@ -2591,6 +2592,7 @@ template class DB_PUBLIC shape_interactions; template class DB_PUBLIC shape_interactions; template class DB_PUBLIC shape_interactions; template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; template class DB_PUBLIC shape_interactions; template class DB_PUBLIC shape_interactions; @@ -2621,6 +2623,7 @@ template class DB_PUBLIC local_processor_context_computation_task; template class DB_PUBLIC local_processor_context_computation_task; template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; template class DB_PUBLIC local_processor_context_computation_task; template class DB_PUBLIC local_processor_context_computation_task; @@ -2644,6 +2647,7 @@ template class DB_PUBLIC local_processor_result_computation_task; template class DB_PUBLIC local_processor_result_computation_task; template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; template class DB_PUBLIC local_processor_result_computation_task; // explicit instantiations @@ -2671,6 +2675,7 @@ template class DB_PUBLIC local_processor template class DB_PUBLIC local_processor; template class DB_PUBLIC local_processor; template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; template class DB_PUBLIC local_processor; template class DB_PUBLIC local_processor; template class DB_PUBLIC local_processor; diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index 48961bf32..24b7f4786 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -101,6 +101,7 @@ template class DB_PUBLIC local_operation template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 731047698..488fbc460 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -556,16 +556,23 @@ Region::texts_as_dots (const std::string &pat, bool pattern, db::DeepShapeStore fill_texts (si.first, pat, pattern, dot_delivery (), res.get (), si.second, dr); - return Edges (res.release ()); + Edges edges (res.release ()); + edges.set_merged_semantics (false); + return edges; } + db::Edges edges; + text_shape_receiver > pipe = text_shape_receiver > (dot_delivery (), pat, pattern, dr); if (dr && dr->deep_layer ().store () == &store) { - return Edges (new db::DeepEdges (store.create_copy (dr->deep_layer (), &pipe))); + edges = Edges (new db::DeepEdges (store.create_copy (dr->deep_layer (), &pipe))); } else { - return Edges (new db::DeepEdges (store.create_custom_layer (si.first, &pipe, si.second))); + edges = Edges (new db::DeepEdges (store.create_custom_layer (si.first, &pipe, si.second))); } + + edges.set_merged_semantics (false); + return edges; } Region diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index ee62f4390..39880b2ca 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -770,7 +770,7 @@ public: */ Edges edges () const { - return Edges (mp_delegate->edges (0)); + return Edges (mp_delegate->edges (0, 0)); } /** @@ -783,7 +783,34 @@ public: */ Edges edges (const EdgeFilterBase &filter) const { - return mp_delegate->edges (&filter); + return mp_delegate->edges (&filter, 0); + } + + /** + * @brief Returns an edge set containing all edges of the polygons in this region + * + * Merged semantics applies. In merged semantics, only full, outer edges are delivered. + * This version allows specifying a polygon to edge processor with additional features + * like extraction of convex edges only. + */ + Edges edges (const db::PolygonToEdgeProcessorBase &proc) const + { + return Edges (mp_delegate->edges (0, &proc)); + } + + /** + * @brief Returns an edge set containing all edges of the polygons in this region + * + * This version allows one to specify a filter by which the edges are filtered before they are + * returned. + * + * Merged semantics applies. In merged semantics, only full, outer edges are delivered. + * This version allows specifying a polygon to edge processor with additional features + * like extraction of convex edges only. + */ + Edges edges (const EdgeFilterBase &filter, const db::PolygonToEdgeProcessorBase &proc) const + { + return mp_delegate->edges (&filter, &proc); } /** diff --git a/src/db/db/dbRegionCheckUtils.cc b/src/db/db/dbRegionCheckUtils.cc index 8b7dbf249..9ace8d834 100644 --- a/src/db/db/dbRegionCheckUtils.cc +++ b/src/db/db/dbRegionCheckUtils.cc @@ -133,7 +133,7 @@ Edge2EdgeCheckBase::finish (const Edge *o, size_t p) std::set partial_edges; - db::EdgeBooleanCluster > ec (&partial_edges, db::EdgeNot); + db::EdgeBooleanCluster > ec (&partial_edges, 0, db::EdgeNot); ec.add (o, 0); for (std::multimap, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) { diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index b1c32aad0..cfc627cf7 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -211,7 +211,7 @@ public: virtual RegionDelegate *scaled_and_snapped_in_place (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) = 0; virtual RegionDelegate *scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) = 0; - virtual EdgesDelegate *edges (const EdgeFilterBase *filter) const = 0; + virtual EdgesDelegate *edges (const EdgeFilterBase *filter, const db::PolygonToEdgeProcessorBase *proc) const = 0; virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) = 0; virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const = 0; virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter) = 0; diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc index ce809ac93..63fcdea3b 100644 --- a/src/db/db/dbRegionProcessors.cc +++ b/src/db/db/dbRegionProcessors.cc @@ -136,10 +136,104 @@ bool RelativeExtentsAsEdges::result_must_not_be_merged () const // ----------------------------------------------------------------------------------- // PolygonToEdgeProcessor implementation +PolygonToEdgeProcessor::PolygonToEdgeProcessor (PolygonToEdgeProcessor::EdgeMode mode) + : m_mode (mode) +{ + // .. nothing yet .. +} + +inline void +next (db::Polygon::contour_type::simple_iterator &iter, const db::Polygon::contour_type &contour) +{ + if (++iter == contour.end ()) { + iter = contour.begin (); + } +} + +static void +contour_to_edges (const db::Polygon::contour_type &contour, PolygonToEdgeProcessor::EdgeMode mode, std::vector &result) +{ + if (contour.size () < 3) { + return; + } + + db::Polygon::contour_type::simple_iterator pm1 = contour.begin (); + db::Polygon::contour_type::simple_iterator p0 = pm1; + next (p0, contour); + db::Polygon::contour_type::simple_iterator p1 = p0; + next (p1, contour); + db::Polygon::contour_type::simple_iterator p2 = p1; + next (p2, contour); + + while (pm1 != contour.end ()) { + + int s1 = db::vprod_sign (*p0 - *pm1, *p1 - *p0); + int s2 = db::vprod_sign (*p1 - *p0, *p2 - *p1); + + bool take = true; + + switch (mode) { + case PolygonToEdgeProcessor::All: + default: + break; + case PolygonToEdgeProcessor::Convex: + take = s1 < 0 && s2 < 0; + break; + case PolygonToEdgeProcessor::NotConvex: + take = ! (s1 < 0 && s2 < 0); + break; + case PolygonToEdgeProcessor::Concave: + take = s1 > 0 && s2 > 0; + break; + case PolygonToEdgeProcessor::NotConcave: + take = ! (s1 > 0 && s2 > 0); + break; + case PolygonToEdgeProcessor::StepOut: + take = s1 > 0 && s2 < 0; + break; + case PolygonToEdgeProcessor::NotStepOut: + take = ! (s1 > 0 && s2 < 0); + break; + case PolygonToEdgeProcessor::StepIn: + take = s1 < 0 && s2 > 0; + break; + case PolygonToEdgeProcessor::NotStepIn: + take = ! (s1 < 0 && s2 > 0); + break; + case PolygonToEdgeProcessor::Step: + take = s1 * s2 < 0; + break; + case PolygonToEdgeProcessor::NotStep: + take = ! (s1 * s2 < 0); + break; + } + + if (take) { + result.push_back (db::Edge (*p0, *p1)); + } + + ++pm1; + next (p0, contour); + next (p1, contour); + next (p2, contour); + + } +} + void PolygonToEdgeProcessor::process (const db::Polygon &poly, std::vector &result) const { - for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { - result.push_back (*e); + if (m_mode == All) { + + for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { + result.push_back (*e); + } + + } else { + + for (unsigned int i = 0; i < poly.holes () + 1; ++i) { + contour_to_edges (poly.contour (i), m_mode, result); + } + } } diff --git a/src/db/db/dbRegionProcessors.h b/src/db/db/dbRegionProcessors.h index 74f1a5100..08553ca25 100644 --- a/src/db/db/dbRegionProcessors.h +++ b/src/db/db/dbRegionProcessors.h @@ -293,12 +293,15 @@ class DB_PUBLIC PolygonToEdgeProcessor : public db::PolygonToEdgeProcessorBase { public: - PolygonToEdgeProcessor () - { - // .. nothing yet .. - } + enum EdgeMode { All = 0, Convex, Concave, StepIn, StepOut, Step, + NotConvex, NotConcave, NotStepIn, NotStepOut, NotStep }; + + PolygonToEdgeProcessor (EdgeMode mode = All); void process (const db::Polygon &poly, std::vector &result) const; + +private: + EdgeMode m_mode; }; /** diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index 782151553..0ee676e20 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -316,13 +316,13 @@ static db::CompoundRegionOperationNode *new_minkowski_sum_node4 (db::CompoundReg return new db::CompoundRegionProcessingOperationNode (new db::minkowski_sum_computation > (p), input, true /*processor is owned*/); } -static db::CompoundRegionOperationNode *new_edges (db::CompoundRegionOperationNode *input) +static db::CompoundRegionOperationNode *new_edges (db::CompoundRegionOperationNode *input, db::PolygonToEdgeProcessor::EdgeMode edge_mode) { 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) { - return new db::CompoundRegionToEdgeProcessingOperationNode (new db::PolygonToEdgeProcessor (), input, true /*processor is owned*/); + return new db::CompoundRegionToEdgeProcessingOperationNode (new db::PolygonToEdgeProcessor (edge_mode), input, true /*processor is owned*/); } else { input->keep (); return input; @@ -567,13 +567,13 @@ Class decl_CompoundRegionOperationNode ("db", " gsi::constructor ("new_geometrical_boolean", &new_geometrical_boolean, gsi::arg ("op"), gsi::arg ("a"), gsi::arg ("b"), "@brief Creates a node representing a geometrical boolean operation between the inputs.\n" ) + - gsi::constructor ("new_interacting", &new_interacting, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), gsi::arg ("min_count", size_t (0)), gsi::arg ("max_count", std::numeric_limits::max (), "unlimited"), + gsi::constructor ("new_interacting", &new_interacting, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", std::numeric_limits::max (), "unlimited"), "@brief Creates a node representing an interacting selection operation between the inputs.\n" ) + - gsi::constructor ("new_overlapping", &new_overlapping, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), gsi::arg ("min_count", size_t (0)), gsi::arg ("max_count", std::numeric_limits::max (), "unlimited"), + gsi::constructor ("new_overlapping", &new_overlapping, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", std::numeric_limits::max (), "unlimited"), "@brief Creates a node representing an overlapping selection operation between the inputs.\n" ) + - gsi::constructor ("new_enclosing", &new_enclosing, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), gsi::arg ("min_count", size_t (0)), gsi::arg ("max_count", std::numeric_limits::max (), "unlimited"), + gsi::constructor ("new_enclosing", &new_enclosing, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", std::numeric_limits::max (), "unlimited"), "@brief Creates a node representing an inside selection operation between the inputs.\n" ) + gsi::constructor ("new_inside", &new_inside, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("inverse", false), @@ -746,8 +746,12 @@ Class decl_CompoundRegionOperationNode ("db", " "@brief Creates a node filtering the input for rectangular or square shapes.\n" "If 'is_square' is true, only squares will be selected. If 'inverse' is true, the non-rectangle/non-square shapes are returned.\n" ) + - gsi::constructor ("new_edges", &new_edges, gsi::arg ("input"), + gsi::constructor ("new_edges", &new_edges, gsi::arg ("input"), gsi::arg ("mode", db::PolygonToEdgeProcessor::All, "All"), "@brief Creates a node converting polygons into its edges.\n" + "The 'mode' argument allows selecting specific edges when generating edges from a polygon. " + "See \\EdgeMode for the various options. By default, all edges are generated from polygons.\n" + "\n" + "The 'mode' argument has been added in version 0.29." ) + gsi::constructor ("new_edge_length_filter", &new_edge_length_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("lmin", 0), gsi::arg ("lmax", std::numeric_limits::max (), "max"), "@brief Creates a node filtering edges by their length.\n" diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 8b1041622..d325dfa26 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -656,14 +656,14 @@ static std::vector split_outside_with_region (const db::Edges *r, con return as_2edges_vector (r->selected_outside_differential (other)); } -static std::vector split_interacting_with_edges (const db::Edges *r, const db::Edges &other) +static std::vector split_interacting_with_edges (const db::Edges *r, const db::Edges &other, size_t min_count, size_t max_count) { - return as_2edges_vector (r->selected_interacting_differential (other)); + return as_2edges_vector (r->selected_interacting_differential (other, min_count, max_count)); } -static std::vector split_interacting_with_region (const db::Edges *r, const db::Region &other) +static std::vector split_interacting_with_region (const db::Edges *r, const db::Region &other, size_t min_count, size_t max_count) { - return as_2edges_vector (r->selected_interacting_differential (other)); + return as_2edges_vector (r->selected_interacting_differential (other, min_count, max_count)); } @@ -1233,61 +1233,107 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "The 'join_with' alias has been introduced in version 0.28.12." ) + - method ("interacting", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_interacting, gsi::arg ("other"), + method ("interacting", (db::Edges (db::Edges::*) (const db::Edges &, size_t, size_t) const) &db::Edges::selected_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Returns the edges of this edge collection which overlap or touch edges from the other edge collection\n" "\n" "@return A new edge collection containing the edges overlapping or touching edges from the other edge collection\n" + "\n" + "'min_count' and 'max_count' impose a constraint on the number of times an edge of this collection " + "has to interact with (different) edges of the other collection to make the edge selected. An edge is " + "selected by this method if the number of edges interacting with an edge of this collection is between min_count and max_count " + "(including max_count).\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." ) + - method ("not_interacting", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_not_interacting, gsi::arg ("other"), + method ("not_interacting", (db::Edges (db::Edges::*) (const db::Edges &, size_t, size_t) const) &db::Edges::selected_not_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Returns the edges of this edge collection which do not overlap or touch edges from the other edge collection\n" "\n" "@return A new edge collection containing the edges not overlapping or touching edges from the other edge collection\n" - ) + - method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_interacting, gsi::arg ("other"), + "\n" + "'min_count' and 'max_count' impose a constraint on the number of times an edge of this collection " + "has to interact with (different) edges of the other collection to make the edge selected. An edge is " + "not selected by this method if the number of edges interacting with an edge of this collection is between min_count and max_count " + "(including max_count).\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Edges &, size_t, size_t)) &db::Edges::select_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Selects the edges from this edge collection which overlap or touch edges from the other edge collection\n" "\n" "@return The edge collection after the edges have been selected (self)\n" - ) + - method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_not_interacting, gsi::arg ("other"), + "\n" + "This is the in-place version of \\interacting - i.e. self is modified rather than a new collection is returned.\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Edges &, size_t, size_t)) &db::Edges::select_not_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Selects the edges from this edge collection which do not overlap or touch edges from the other edge collection\n" "\n" "@return The edge collection after the edges have been selected (self)\n" - ) + - method_ext ("split_interacting", &split_interacting_with_edges, gsi::arg ("other"), + "\n" + "This is the in-place version of \\not_interacting - i.e. self is modified rather than a new collection is returned.\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method_ext ("split_interacting", &split_interacting_with_edges, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Selects the edges from this edge collection which do and do not interact with edges from the other collection\n" "\n" "@return A two-element list of edge collections (first: interacting, second: non-interacting)\n" "\n" "This method provides a faster way to compute both interacting and non-interacting edges compared to using separate methods. " - "It has been introduced in version 0.28." + "It has been introduced in version 0.28.\n" + "'min_count' and 'max_count' have been introduced in version 0.29." ) + - method ("interacting", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_interacting, gsi::arg ("other"), + method ("interacting", (db::Edges (db::Edges::*) (const db::Region &, size_t, size_t) const) &db::Edges::selected_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Returns the edges from this edge collection which overlap or touch polygons from the region\n" "\n" "@return A new edge collection containing the edges overlapping or touching polygons from the region\n" - ) + - method ("not_interacting", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_not_interacting, gsi::arg ("other"), + "\n" + "'min_count' and 'max_count' impose a constraint on the number of times an edge of this collection " + "has to interact with (different) polygons of the other region to make the edge selected. An edge is " + "selected by this method if the number of polygons interacting with an edge of this collection is between min_count and max_count " + "(including max_count).\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method ("not_interacting", (db::Edges (db::Edges::*) (const db::Region &, size_t, size_t) const) &db::Edges::selected_not_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Returns the edges from this edge collection which do not overlap or touch polygons from the region\n" "\n" "@return A new edge collection containing the edges not overlapping or touching polygons from the region\n" - ) + - method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_interacting, gsi::arg ("other"), + "\n" + "'min_count' and 'max_count' impose a constraint on the number of times an edge of this collection " + "has to interact with (different) polygons of the other region to make the edge selected. An edge is " + "not selected by this method if the number of polygons interacting with an edge of this collection is between min_count and max_count " + "(including max_count).\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Region &, size_t, size_t)) &db::Edges::select_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Selects the edges from this edge collection which overlap or touch polygons from the region\n" "\n" "@return The edge collection after the edges have been selected (self)\n" - ) + - method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_not_interacting, gsi::arg ("other"), + "\n" + "This is the in-place version of \\interacting - i.e. self is modified rather than a new collection is returned.\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Region &, size_t, size_t)) &db::Edges::select_not_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Selects the edges from this edge collection which do not overlap or touch polygons from the region\n" "\n" "@return The edge collection after the edges have been selected (self)\n" - ) + - method_ext ("split_interacting", &split_interacting_with_region, gsi::arg ("other"), + "\n" + "This is the in-place version of \\not_interacting - i.e. self is modified rather than a new collection is returned.\n" + "\n" + "'min_count' and 'max_count' have been introduced in version 0.29." + ) + + method_ext ("split_interacting", &split_interacting_with_region, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits::max ()), "unlimited"), "@brief Selects the edges from this edge collection which do and do not interact with polygons from the other region\n" "\n" "@return A two-element list of edge collections (first: interacting, second: non-interacting)\n" "\n" "This method provides a faster way to compute both interacting and non-interacting edges compared to using separate methods. " - "It has been introduced in version 0.28." + "It has been introduced in version 0.28.\n" + "'min_count' and 'max_count' have been introduced in version 0.29." ) + method ("inside", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_inside, gsi::arg ("other"), "@brief Returns the edges of this edge collection which are inside (completely covered by) edges from the other edge collection\n" diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index 0c3960bb7..9cfcb1cf6 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -31,6 +31,39 @@ namespace gsi { +template +static std::vector split_poly (const C *p) +{ + std::vector parts; + db::split_polygon (*p, parts); + return parts; +} + +template +static void break_polygon (const C &poly, size_t max_vertex_count, double max_area_ratio, std::vector &result) +{ + if ((max_vertex_count > 0 && poly.vertices () > max_vertex_count) || + (max_area_ratio > 0 && poly.area_ratio () > max_area_ratio)) { + + std::vector split_polygons; + db::split_polygon (poly, split_polygons); + for (auto p = split_polygons.begin (); p != split_polygons.end (); ++p) { + break_polygon (*p, max_vertex_count, max_area_ratio, result); + } + + } else { + result.push_back (poly); + } +} + +template +static std::vector break_poly (const C *p, size_t max_vertex_count, double max_area_ratio) +{ + std::vector parts; + break_polygon (*p, max_vertex_count, max_area_ratio, parts); + return parts; +} + // --------------------------------------------------------------- // simple polygon binding @@ -245,13 +278,6 @@ struct simple_polygon_defs return db::interact (*p, spoly); } - static std::vector split_poly (const C *p) - { - std::vector parts; - db::split_polygon (*p, parts); - return parts; - } - static gsi::Methods methods () { return @@ -508,7 +534,7 @@ struct simple_polygon_defs "\n" "This method was introduced in version 0.25.\n" ) + - method_ext ("split", &split_poly, + method_ext ("split", &split_poly, "@brief Splits the polygon into two or more parts\n" "This method will break the polygon into parts. The exact breaking algorithm is unspecified, the " "result are smaller polygons of roughly equal number of points and 'less concave' nature. " @@ -521,6 +547,20 @@ struct simple_polygon_defs "\n" "This method has been introduced in version 0.25.3." ) + + method_ext ("break", &break_poly, gsi::arg ("max_vertex_count"), gsi::arg ("max_area_ratio"), + "@brief Splits the polygon into parts with a maximum vertex count and area ratio\n" + "The area ratio is the ratio between the bounding box area and the polygon area. Higher values " + "mean more 'skinny' polygons.\n" + "\n" + "This method will split the input polygon into pieces having a maximum of 'max_vertex_count' vertices " + "and an area ratio less than 'max_area_ratio'. 'max_vertex_count' can be zero. In this case the " + "limit is ignored. Also 'max_area_ratio' can be zero, in which case it is ignored as well.\n" + "\n" + "The method of splitting is unspecified. The algorithm will apply 'split' recursively until the " + "parts satisfy the limits.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method_ext ("area", &area, "@brief Gets the area of the polygon\n" "The area is correct only if the polygon is not self-overlapping and the polygon is oriented clockwise." @@ -1098,13 +1138,6 @@ struct polygon_defs return db::interact (*p, spoly); } - static std::vector split_spoly (const C *p) - { - std::vector parts; - db::split_polygon (*p, parts); - return parts; - } - static gsi::Methods methods () { return @@ -1520,7 +1553,7 @@ struct polygon_defs "\n" "This method was introduced in version 0.25.\n" ) + - method_ext ("split", &split_spoly, + method_ext ("split", &split_poly, "@brief Splits the polygon into two or more parts\n" "This method will break the polygon into parts. The exact breaking algorithm is unspecified, the " "result are smaller polygons of roughly equal number of points and 'less concave' nature. " @@ -1533,6 +1566,20 @@ struct polygon_defs "\n" "This method has been introduced in version 0.25.3." ) + + method_ext ("break", &break_poly, gsi::arg ("max_vertex_count"), gsi::arg ("max_area_ratio"), + "@brief Splits the polygon into parts with a maximum vertex count and area ratio\n" + "The area ratio is the ratio between the bounding box area and the polygon area. Higher values " + "mean more 'skinny' polygons.\n" + "\n" + "This method will split the input polygon into pieces having a maximum of 'max_vertex_count' vertices " + "and an area ratio less than 'max_area_ratio'. 'max_vertex_count' can be zero. In this case the " + "limit is ignored. Also 'max_area_ratio' can be zero, in which case it is ignored as well.\n" + "\n" + "The method of splitting is unspecified. The algorithm will apply 'split' recursively until the " + "parts satisfy the limits.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method_ext ("area", &area, "@brief Gets the area of the polygon\n" "The area is correct only if the polygon is not self-overlapping and the polygon is oriented clockwise." diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 6135f6e9f..f978a7c4f 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -1002,6 +1002,18 @@ size_dvm (db::Region *region, const db::Vector &dv, unsigned int mode) return *region; } +static db::Edges +edges (const db::Region *region, db::PolygonToEdgeProcessor::EdgeMode mode) +{ + if (mode != db::PolygonToEdgeProcessor::All) { + db::PolygonToEdgeProcessor proc (mode); + return region->edges (proc); + } else { + // this version is more efficient in the hierarchical case + return region->edges (); + } +} + static std::vector > rasterize2 (const db::Region *region, const db::Point &origin, const db::Vector &pixel_distance, const db::Vector &pixel_size, unsigned int nx, unsigned int ny) { @@ -2493,15 +2505,20 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "If the region is not merged, this method may return false even\n" "if the merged region would be a box.\n" ) + - method ("edges", (db::Edges (db::Region::*) () const) &db::Region::edges, + method_ext ("edges", &edges, gsi::arg ("mode", db::PolygonToEdgeProcessor::All, "All"), "@brief Returns an edge collection representing all edges of the polygons in this region\n" "This method will decompose the polygons into the individual edges. Edges making up the hulls " "of the polygons are oriented clockwise while edges making up the holes are oriented counterclockwise.\n" "\n" + "The 'mode' parameter allows selecting specific edges, such as convex or concave ones. By default, " + "all edges are selected.\n" + "\n" "The edge collection returned can be manipulated in various ways. See \\Edges for a description of the " - "possibilities of the edge collection.\n" + "features of the edge collection.\n" "\n" "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" + "\n" + "The mode argument has been added in version 0.29." ) + factory_ext ("decompose_convex", &decompose_convex, gsi::arg ("preferred_orientation", po_any (), "\\Polygon#PO_any"), "@brief Decomposes the region into convex pieces.\n" @@ -2924,7 +2941,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "The 'shielded' and 'negative' options have been introduced in version 0.27. " "'property_constraint' has been added in version 0.28.4.\n" - "'zero_distance_mode' has been added in version 0.29." + "'zero_distance_mode' has been added in version 0.28.16." ) + method_ext ("space_check", &space2, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), gsi::arg ("zero_distance_mode", db::IncludeZeroDistanceWhenTouching, "IncludeZeroDistanceWhenTouching"), "@brief Performs a space check with options\n" @@ -3577,6 +3594,48 @@ gsi::Enum decl_Metrics ("db", "Metrics", gsi::ClassExt inject_Metrics_in_Region (decl_Metrics.defs ()); gsi::ClassExt inject_Metrics_in_Edges (decl_Metrics.defs ()); +gsi::Enum decl_EdgeMode ("db", "EdgeMode", + gsi::enum_const ("All", db::PolygonToEdgeProcessor::All, + "@brief Selects all edges\n" + ) + + gsi::enum_const ("Concave", db::PolygonToEdgeProcessor::Concave, + "@brief Selects only concave edges\n" + ) + + gsi::enum_const ("NotConcave", db::PolygonToEdgeProcessor::NotConcave, + "@brief Selects only edges which are not concave\n" + ) + + gsi::enum_const ("Convex", db::PolygonToEdgeProcessor::Convex, + "@brief Selects only convex edges\n" + ) + + gsi::enum_const ("NotConvex", db::PolygonToEdgeProcessor::NotConvex, + "@brief Selects only edges which are not convex\n" + ) + + gsi::enum_const ("Step", db::PolygonToEdgeProcessor::Step, + "@brief Selects only step edges leading inside or outside\n" + ) + + gsi::enum_const ("NotStep", db::PolygonToEdgeProcessor::NotStep, + "@brief Selects only edges which are not steps\n" + ) + + gsi::enum_const ("StepIn", db::PolygonToEdgeProcessor::StepIn, + "@brief Selects only step edges leading inside\n" + ) + + gsi::enum_const ("NotStepIn", db::PolygonToEdgeProcessor::NotStepIn, + "@brief Selects only edges which are not steps leading inside\n" + ) + + gsi::enum_const ("StepOut", db::PolygonToEdgeProcessor::StepOut, + "@brief Selects only step edges leading outside\n" + ) + + gsi::enum_const ("NotStepOut", db::PolygonToEdgeProcessor::NotStepOut, + "@brief Selects only edges which are not steps leading outside\n" + ), + "@brief This class represents the edge mode type for \\Region#edges.\n" + "\n" + "This enum has been introduced in version 0.29." +); + +// Inject the Region::EdgeMode declarations into Region: +gsi::ClassExt inject_EdgeMode_in_Region (decl_EdgeMode.defs ()); + gsi::Enum decl_ZeroDistanceMode ("db", "ZeroDistanceMode", gsi::enum_const ("NeverIncludeZeroDistance", db::NeverIncludeZeroDistance, "@brief Specifies that check functions should never include edges with zero distance.\n" @@ -3590,7 +3649,7 @@ gsi::Enum decl_ZeroDistanceMode ("db", "ZeroDistanceMode gsi::enum_const ("IncludeZeroDistanceWhenTouching", db::IncludeZeroDistanceWhenTouching, "@brief Specifies that check functions should include edges when they touch\n" "With this specification, the check functions will also check edges if they share at least one common point. " - "This is the mode that includes checking the 'kissing corner' cases. This mode is default for version 0.29 and later. " + "This is the mode that includes checking the 'kissing corner' cases. This mode is default for version 0.28.16 and later. " ) + gsi::enum_const ("IncludeZeroDistanceWhenCollinearAndTouching", db::IncludeZeroDistanceWhenCollinearAndTouching, "@brief Specifies that check functions should include edges when they are collinear and touch\n" @@ -3609,7 +3668,7 @@ gsi::Enum decl_ZeroDistanceMode ("db", "ZeroDistanceMode "if they share at least one common point (\\IncludeZeroDistanceWhenTouching). The latter mode allows activating checks for " "the 'kissing corner' case and is the default mode in most checks." "\n" - "This enum has been introduced in version 0.29." + "This enum has been introduced in version 0.28.16." ); // Inject the Region::ZeroDistanceMode declarations into Region and Edges: diff --git a/src/db/unit_tests/dbDeepEdgesTests.cc b/src/db/unit_tests/dbDeepEdgesTests.cc index 1d9f24594..7d992997e 100644 --- a/src/db/unit_tests/dbDeepEdgesTests.cc +++ b/src/db/unit_tests/dbDeepEdgesTests.cc @@ -133,7 +133,7 @@ TEST(3_Edge2EdgeBooleans) db::Layout ly; { std::string fn (tl::testdata ()); - fn += "/algo/deep_region_l1.gds"; + fn += "/algo/deep_edges_l1.gds"; tl::InputStream stream (fn); db::Reader reader (stream); reader.read (ly); @@ -145,15 +145,23 @@ TEST(3_Edge2EdgeBooleans) db::DeepShapeStore dss; unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l21 = ly.get_layer (db::LayerProperties (2, 1)); unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int lempty = ly.insert_layer (); db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r21 (db::RecursiveShapeIterator (ly, top_cell, l21), dss); db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); db::Region r2and3 = r2 & r3; db::Edges e2 = r2.edges (); + db::Edges e21 = r21.edges (); db::Edges e3 = r3.edges (); + db::Edges e3copy = r3.edges (); db::Edges e2and3 = r2and3.edges (); + db::Edges eempty (db::RecursiveShapeIterator (ly, top_cell, lempty), dss); + db::Edges edots = e2and3.processed (db::EdgeSegmentSelector (-1, 0, 0)); + db::Edges edotscopy = e2and3.processed (db::EdgeSegmentSelector (-1, 0, 0)); db::Layout target; unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); @@ -162,11 +170,70 @@ TEST(3_Edge2EdgeBooleans) target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), r3); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e3); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), edots.merged ()); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), e3 & e2and3); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), e3 - e2and3); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), e3 ^ e2and3); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), e3.intersections(e2and3)); - target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), e3.intersections(e2)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), e3 & edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), e3 & eempty); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), e3 & e3copy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), eempty & e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), edots & edotscopy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 0)), edots & e2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (27, 0)), e21 & edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (28, 0)), edots & e21); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), e3 - e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), e3 - edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), e3 - eempty); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (33, 0)), e3 - e3copy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (34, 0)), eempty - e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (35, 0)), edots - edotscopy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (36, 0)), edots - e2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (37, 0)), e21 - edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (38, 0)), edots - e21); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), e3 ^ e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), e3 ^ edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 0)), e3 ^ eempty); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (43, 0)), e3 ^ e3copy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (44, 0)), eempty ^ e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (45, 0)), edots ^ edotscopy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (46, 0)), edots ^ e2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (47, 0)), e21 ^ edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (48, 0)), edots ^ e21); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (50, 0)), e3.andnot(e2and3).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (51, 0)), e3.andnot(edots).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (52, 0)), e3.andnot(eempty).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (53, 0)), e3.andnot(e3copy).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (54, 0)), eempty.andnot(e2and3).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (55, 0)), edots.andnot(edotscopy).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (56, 0)), edots.andnot(e2).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (57, 0)), e21.andnot(edots).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (58, 0)), edots.andnot(e21).first); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (60, 0)), e3.andnot(e2and3).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (61, 0)), e3.andnot(edots).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (62, 0)), e3.andnot(eempty).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (63, 0)), e3.andnot(e3copy).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (64, 0)), eempty.andnot(e2and3).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (65, 0)), edots.andnot(edotscopy).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (66, 0)), edots.andnot(e2).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (67, 0)), e21.andnot(edots).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (68, 0)), edots.andnot(e21).second); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (70, 0)), e3.intersections(e2and3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (71, 0)), e3.intersections(edots)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (72, 0)), e3.intersections(eempty)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (73, 0)), e3.intersections(e3copy)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (74, 0)), eempty.intersections(e2and3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (75, 0)), edots.intersections(edotscopy)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (76, 0)), edots.intersections(e2)); + // test, whether dots are not merged + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (77, 0)), edots.intersections(e2).select_interacting(e2)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (78, 0)), e21.intersections(edots)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (79, 0)), edots.intersections(e21)); CHECKPOINT(); db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_edges_au3.gds"); @@ -1293,6 +1360,152 @@ TEST(20_in_and_out) db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_edges_au20.gds"); } +TEST(21_EdgeMergeWithDots) +{ + db::Edges e; + e.insert (db::Edge (db::Point(0, 0), db::Point (100, 0))); + e.insert (db::Edge (db::Point(110, 0), db::Point (110, 0))); + + db::Edges ee = e; + ee.insert (db::Edge (db::Point(100, 0), db::Point (110, 0))); + + db::Edges eee; + eee.insert (db::Edge (db::Point(110, 0), db::Point (110, 0))); + eee.insert (db::Edge (db::Point(110, 0), db::Point (110, 0))); + + // make deep + + db::DeepShapeStore dss; + + db::Layout ly; + ly.add_cell ("TOP"); + unsigned int l1 = ly.insert_layer (); + unsigned int l2 = ly.insert_layer (); + unsigned int l3 = ly.insert_layer (); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + e.insert_into (&ly, top_cell.cell_index (), l1); + e = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + + ee.insert_into (&ly, top_cell.cell_index (), l2); + ee = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + + eee.insert_into (&ly, top_cell.cell_index (), l3); + eee = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + + EXPECT_EQ (e.merged ().to_string (), "(0,0;100,0);(110,0;110,0)"); + // dots do not participate in merge + EXPECT_EQ (ee.merged ().to_string (), "(0,0;110,0)"); + // dots do not participate in merge + EXPECT_EQ (eee.merged ().to_string (), "(110,0;110,0)"); +} + +TEST(22_InteractingWithCount) +{ + db::Edges e; + e.insert (db::Edge (db::Point (0, 0), db::Point (100, 0))); + e.insert (db::Edge (db::Point (100, 0), db::Point (200, 0))); + e.insert (db::Edge (db::Point (0, 10), db::Point (200, 10))); + e.insert (db::Edge (db::Point (0, 20), db::Point (200, 20))); + e.insert (db::Edge (db::Point (0, 30), db::Point (200, 30))); + + db::Edges e2; + e2.insert (db::Edge (db::Point (100, 0), db::Point (100, 10))); + e2.insert (db::Edge (db::Point (100, 0), db::Point (100, 30))); + e2.insert (db::Edge (db::Point (110, 10), db::Point (110, 30))); + e2.merge (); + e2.insert (db::Edge (db::Point (120, 20), db::Point (120, 20))); + e2.insert (db::Edge (db::Point (130, 30), db::Point (130, 30))); + e2.set_merged_semantics (false); + + db::Region r2; + r2.insert (db::Box (db::Point (99, 0), db::Point (101, 10))); + r2.insert (db::Box (db::Point (99, 0), db::Point (101, 30))); + r2.insert (db::Box (db::Point (109, 10), db::Point (111, 30))); + r2.insert (db::Box (db::Point (119, 19), db::Point (121, 21))); + r2.insert (db::Box (db::Point (129, 29), db::Point (131, 31))); + + // make deep + + db::DeepShapeStore dss; + + db::Layout ly; + ly.add_cell ("TOP"); + unsigned int l1 = ly.insert_layer (); + unsigned int l2 = ly.insert_layer (); + unsigned int l3 = ly.insert_layer (); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + e.insert_into (&ly, top_cell.cell_index (), l1); + e = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + + e2.insert_into (&ly, top_cell.cell_index (), l2); + e2 = db::Edges (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + // because it has dots + e2.set_merged_semantics (false); + + r2.insert_into (&ly, top_cell.cell_index (), l3); + r2 = db::Region (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + + + db::Edges edup; + + EXPECT_EQ (db::compare (e.selected_interacting (e2), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (2)), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (2), size_t(2)), "(0,10;200,10)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (2), size_t(3)), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (3)), "(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (e2, size_t (4)), ""), true); + + edup = e; + edup.select_interacting (e2, size_t (2), size_t(3)); + EXPECT_EQ (db::compare (edup, "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + + EXPECT_EQ (db::compare (e.selected_not_interacting (e2), ""), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (2)), "(0,0;200,0)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (2), size_t(2)), "(0,0;200,0);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (2), size_t(3)), "(0,0;200,0)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (3)), "(0,0;200,0);(0,10;200,10)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (e2, size_t (4)), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + + edup = e; + edup.select_not_interacting (e2, size_t (2), size_t(3)); + EXPECT_EQ (db::compare (edup, "(0,0;200,0)"), true); + + EXPECT_EQ (db::compare (e.selected_interacting_differential (e2, size_t (2), size_t(3)).first, "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting_differential (e2, size_t (2), size_t(3)).second, "(0,0;200,0)"), true); + + EXPECT_EQ (db::compare (e.selected_interacting (r2), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (2)), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (2), size_t(2)), "(0,10;200,10)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (2), size_t(3)), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (3)), "(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting (r2, size_t (4)), ""), true); + + edup = e; + edup.select_interacting (r2, size_t (2), size_t(3)); + EXPECT_EQ (db::compare (edup, "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + + EXPECT_EQ (db::compare (e.selected_not_interacting (r2), ""), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (2)), "(0,0;200,0)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (2), size_t(2)), "(0,0;200,0);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (2), size_t(3)), "(0,0;200,0)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (3)), "(0,0;200,0);(0,10;200,10)"), true); + EXPECT_EQ (db::compare (e.selected_not_interacting (r2, size_t (4)), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + + edup = e; + edup.select_not_interacting (r2, size_t (2), size_t(3)); + EXPECT_EQ (db::compare (edup, "(0,0;200,0)"), true); + + EXPECT_EQ (db::compare (e.selected_interacting_differential (r2, size_t (2), size_t(3)).first, "(0,10;200,10);(0,20;200,20);(0,30;200,30)"), true); + EXPECT_EQ (db::compare (e.selected_interacting_differential (r2, size_t (2), size_t(3)).second, "(0,0;200,0)"), true); +} + + TEST(deep_edges_and_cheats) { db::Layout ly; diff --git a/src/db/unit_tests/dbEdgesTests.cc b/src/db/unit_tests/dbEdgesTests.cc index 132aec5e1..97f0d890c 100644 --- a/src/db/unit_tests/dbEdgesTests.cc +++ b/src/db/unit_tests/dbEdgesTests.cc @@ -22,12 +22,14 @@ #include "tlUnitTest.h" +#include "tlStream.h" #include "dbEdges.h" #include "dbEdgesUtils.h" #include "dbPolygonTools.h" #include "dbRegion.h" #include "dbTestSupport.h" +#include "dbReader.h" #include @@ -787,27 +789,27 @@ TEST(20) EXPECT_EQ (r2.has_valid_edges (), false); db::Region rr1 (db::RecursiveShapeIterator (ly, ly.cell (top), lp1), db::ICplxTrans (), false); EXPECT_EQ (rr1.has_valid_polygons (), false); - EXPECT_EQ ((r1 & r2).to_string (100), "(80,70;80,40)"); - EXPECT_EQ ((r1 + r2).to_string (100), "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(50,70;80,70);(80,70;80,40);(80,40;50,40);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(80,40;80,70);(80,70;110,70);(110,70;110,40);(110,40;80,40);(110,40;110,70);(110,70;140,70);(140,70;140,40);(140,40;110,40)"); - EXPECT_EQ ((r1 + r2).merged ().to_string (100), "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(50,70;140,70);(140,70;140,40);(140,40;50,40)"); - EXPECT_EQ ((r1 | r2).to_string (100), "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(50,70;140,70);(140,70;140,40);(140,40;50,40)"); - EXPECT_EQ ((r1 ^ r2).to_string (100), "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(50,70;140,70);(140,70;140,40);(140,40;50,40)"); - EXPECT_EQ ((r1 ^ r1).to_string (100), ""); - EXPECT_EQ ((r1 - r2).to_string (100), "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(50,70;80,70);(80,40;50,40)"); - EXPECT_EQ ((r1 - r1).to_string (100), ""); - EXPECT_EQ (r2.merged ().to_string (100), "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,70;140,40);(140,40;80,40)"); - EXPECT_EQ (rr1.to_string (100), "(0,0;0,30;30,30;30,0);(50,0;50,30;80,30;80,0);(50,40;50,70;80,70;80,40)"); - EXPECT_EQ (r2.selected_interacting (rr1).to_string (100), "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,40;80,40)"); - EXPECT_EQ (r2.selected_interacting_differential (rr1).first.to_string (100), "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,40;80,40)"); - EXPECT_EQ (r2.selected_not_interacting (rr1).to_string (100), "(10,40;40,40);(40,40;40,10);(140,70;140,40)"); - EXPECT_EQ (r2.selected_interacting_differential (rr1).second.to_string (100), "(10,40;40,40);(40,40;40,10);(140,70;140,40)"); + EXPECT_EQ (db::compare (r1 & r2, "(80,70;80,40)"), true); + EXPECT_EQ (db::compare (r1 + r2, "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(50,70;80,70);(80,70;80,40);(80,40;50,40);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(80,40;80,70);(80,70;110,70);(110,70;110,40);(110,40;80,40);(110,40;110,70);(110,70;140,70);(140,70;140,40);(140,40;110,40)"), true); + EXPECT_EQ (db::compare ((r1 + r2).merged (), "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(50,70;140,70);(140,70;140,40);(140,40;50,40)"), true); + EXPECT_EQ (db::compare (r1 | r2, "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(50,70;140,70);(140,70;140,40);(140,40;50,40)"), true); + EXPECT_EQ (db::compare (r1 ^ r2, "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(50,70;140,70);(140,70;140,40);(140,40;50,40)"), true); + EXPECT_EQ (db::compare (r1 ^ r1, ""), true); + EXPECT_EQ (db::compare (r1 - r2, "(0,0;0,30);(0,30;30,30);(30,30;30,0);(30,0;0,0);(50,0;50,30);(50,30;80,30);(80,30;80,0);(80,0;50,0);(50,40;50,70);(50,70;80,70);(80,40;50,40)"), true); + EXPECT_EQ (db::compare (r1 - r1, ""), true); + EXPECT_EQ (db::compare (r2.merged (), "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(10,40;40,40);(40,40;40,10);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,70;140,40);(140,40;80,40)"), true); + EXPECT_EQ (db::compare (rr1, "(0,0;0,30;30,30;30,0);(50,0;50,30;80,30;80,0);(50,40;50,70;80,70;80,40)"), true); + EXPECT_EQ (db::compare (r2.selected_interacting (rr1), "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,40;80,40)"), true); + EXPECT_EQ (db::compare (r2.selected_interacting_differential (rr1).first, "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,40;80,40)"), true); + EXPECT_EQ (db::compare (r2.selected_not_interacting (rr1), "(10,40;40,40);(40,40;40,10);(140,70;140,40)"), true); + EXPECT_EQ (db::compare (r2.selected_interacting_differential (rr1).second, "(10,40;40,40);(40,40;40,10);(140,70;140,40)"), true); db::Edges r2dup = r2; r2.select_interacting (rr1); EXPECT_EQ (db::compare (r2, "(60,10;60,20);(60,20;70,20);(70,20;70,10);(70,10;60,10);(10,10;10,40);(40,10;10,10);(80,40;80,70);(80,70;140,70);(140,40;80,40)"), true); r2 = r2dup; r2.select_not_interacting (rr1); - EXPECT_EQ (r2.to_string (100), "(10,40;40,40);(40,40;40,10);(140,70;140,40)"); + EXPECT_EQ (db::compare (r2, "(10,40;40,40);(40,40;40,10);(140,70;140,40)"), true); r2 = db::Edges (db::RecursiveShapeIterator (ly, ly.cell (top), l2), false); EXPECT_EQ (r2.has_valid_edges (), false); @@ -895,9 +897,9 @@ TEST(22) ee.insert (db::Edge (4000,0,4000,-2000)); ee.insert (db::Edge (4000,-2000,-2000,-2000)); - EXPECT_EQ (db::compare ((e & ee), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"), true); - EXPECT_EQ (db::compare (e.andnot(ee).first, "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"), true); - EXPECT_EQ (db::compare (e.intersections (ee), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"), true); + EXPECT_EQ (db::compare ((e & ee), "(400,0;-2000,0);(500,-173;400,0);(1000,0;900,-174);(4000,0;1000,0)"), true); + EXPECT_EQ (db::compare (e.andnot(ee).first, "(400,0;-2000,0);(500,-173;400,0);(1000,0;900,-174);(4000,0;1000,0)"), true); + EXPECT_EQ (db::compare (e.intersections (ee), "(400,0;-2000,0);(500,-173;400,0);(1000,0;900,-174);(4000,0;1000,0)"), true); // Edge/edge intersections ee.clear (); @@ -1157,6 +1159,214 @@ TEST(28) EXPECT_EQ (db::compare (ee.in (e, true), "(0,0;0,2000);(100,1000;0,2000)"), true); } +// edge merge with dots -> dots are merged, but are retained +TEST(29) +{ + db::Edges e; + e.insert (db::Edge (db::Point(0, 0), db::Point (100, 0))); + e.insert (db::Edge (db::Point(110, 0), db::Point (110, 0))); + EXPECT_EQ (e.merged ().to_string (), "(0,0;100,0);(110,0;110,0)"); + + e.insert (db::Edge (db::Point(100, 0), db::Point (110, 0))); + // dots do not participate in merge + EXPECT_EQ (e.merged ().to_string (), "(0,0;110,0)"); + + e.clear (); + e.insert (db::Edge (db::Point(110, 0), db::Point (110, 0))); + e.insert (db::Edge (db::Point(110, 0), db::Point (110, 0))); + // dots do not participate in merge + EXPECT_EQ (e.merged ().to_string (), "(110,0;110,0)"); +} + +// interacting with count +TEST(30) +{ + db::Edges e; + e.insert (db::Edge (db::Point (0, 0), db::Point (100, 0))); + e.insert (db::Edge (db::Point (100, 0), db::Point (200, 0))); + e.insert (db::Edge (db::Point (0, 10), db::Point (200, 10))); + e.insert (db::Edge (db::Point (0, 20), db::Point (200, 20))); + e.insert (db::Edge (db::Point (0, 30), db::Point (200, 30))); + + db::Edges e2; + e2.insert (db::Edge (db::Point (100, 0), db::Point (100, 10))); + e2.insert (db::Edge (db::Point (100, 0), db::Point (100, 30))); + e2.insert (db::Edge (db::Point (110, 10), db::Point (110, 30))); + e2.merge (); + e2.insert (db::Edge (db::Point (120, 20), db::Point (120, 20))); + e2.insert (db::Edge (db::Point (130, 30), db::Point (130, 30))); + e2.set_merged_semantics (false); + + db::Edges edup; + + EXPECT_EQ (e.selected_interacting (e2).to_string (), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"); + EXPECT_EQ (e.selected_interacting (e2, size_t (2)).to_string (), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"); + EXPECT_EQ (e.selected_interacting (e2, size_t (2), size_t(2)).to_string (), "(0,10;200,10)"); + EXPECT_EQ (e.selected_interacting (e2, size_t (2), size_t(3)).to_string (), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"); + EXPECT_EQ (e.selected_interacting (e2, size_t (3)).to_string (), "(0,20;200,20);(0,30;200,30)"); + EXPECT_EQ (e.selected_interacting (e2, size_t (4)).to_string (), ""); + + edup = e; + edup.select_interacting (e2, size_t (2), size_t(3)); + EXPECT_EQ (edup.to_string (), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"); + + EXPECT_EQ (e.selected_not_interacting (e2).to_string (), ""); + EXPECT_EQ (e.selected_not_interacting (e2, size_t (2)).to_string (), "(0,0;200,0)"); + EXPECT_EQ (e.selected_not_interacting (e2, size_t (2), size_t(2)).to_string (), "(0,0;200,0);(0,20;200,20);(0,30;200,30)"); + EXPECT_EQ (e.selected_not_interacting (e2, size_t (2), size_t(3)).to_string (), "(0,0;200,0)"); + EXPECT_EQ (e.selected_not_interacting (e2, size_t (3)).to_string (), "(0,0;200,0);(0,10;200,10)"); + EXPECT_EQ (e.selected_not_interacting (e2, size_t (4)).to_string (), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"); + + edup = e; + edup.select_not_interacting (e2, size_t (2), size_t(3)); + EXPECT_EQ (edup.to_string (), "(0,0;200,0)"); + + EXPECT_EQ (e.selected_interacting_differential (e2, size_t (2), size_t(3)).first.to_string (), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"); + EXPECT_EQ (e.selected_interacting_differential (e2, size_t (2), size_t(3)).second.to_string (), "(0,0;200,0)"); + + db::Region r2; + r2.insert (db::Box (db::Point (99, 0), db::Point (101, 10))); + r2.insert (db::Box (db::Point (99, 0), db::Point (101, 30))); + r2.insert (db::Box (db::Point (109, 10), db::Point (111, 30))); + r2.insert (db::Box (db::Point (119, 19), db::Point (121, 21))); + r2.insert (db::Box (db::Point (129, 29), db::Point (131, 31))); + + EXPECT_EQ (e.selected_interacting (r2).to_string (), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"); + EXPECT_EQ (e.selected_interacting (r2, size_t (2)).to_string (), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"); + EXPECT_EQ (e.selected_interacting (r2, size_t (2), size_t(2)).to_string (), "(0,10;200,10)"); + EXPECT_EQ (e.selected_interacting (r2, size_t (2), size_t(3)).to_string (), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"); + EXPECT_EQ (e.selected_interacting (r2, size_t (3)).to_string (), "(0,20;200,20);(0,30;200,30)"); + EXPECT_EQ (e.selected_interacting (r2, size_t (4)).to_string (), ""); + + edup = e; + edup.select_interacting (r2, size_t (2), size_t(3)); + EXPECT_EQ (edup.to_string (), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"); + + EXPECT_EQ (e.selected_not_interacting (r2).to_string (), ""); + EXPECT_EQ (e.selected_not_interacting (r2, size_t (2)).to_string (), "(0,0;200,0)"); + EXPECT_EQ (e.selected_not_interacting (r2, size_t (2), size_t(2)).to_string (), "(0,0;200,0);(0,20;200,20);(0,30;200,30)"); + EXPECT_EQ (e.selected_not_interacting (r2, size_t (2), size_t(3)).to_string (), "(0,0;200,0)"); + EXPECT_EQ (e.selected_not_interacting (r2, size_t (3)).to_string (), "(0,0;200,0);(0,10;200,10)"); + EXPECT_EQ (e.selected_not_interacting (r2, size_t (4)).to_string (), "(0,0;200,0);(0,10;200,10);(0,20;200,20);(0,30;200,30)"); + + edup = e; + edup.select_not_interacting (r2, size_t (2), size_t(3)); + EXPECT_EQ (edup.to_string (), "(0,0;200,0)"); + + EXPECT_EQ (e.selected_interacting_differential (r2, size_t (2), size_t(3)).first.to_string (), "(0,10;200,10);(0,20;200,20);(0,30;200,30)"); + EXPECT_EQ (e.selected_interacting_differential (r2, size_t (2), size_t(3)).second.to_string (), "(0,0;200,0)"); +} + +// borrowed from deep edges tests +TEST(31) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_edges_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l21 = ly.get_layer (db::LayerProperties (2, 1)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int lempty = ly.insert_layer (); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + db::Region r21 (db::RecursiveShapeIterator (ly, top_cell, l21)); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3)); + db::Region r2and3 = r2 & r3; + + db::Edges e2 = r2.edges (); + db::Edges e21 = r21.edges (); + db::Edges e3 = r3.edges (); + db::Edges e3copy = r3.edges (); + db::Edges e2and3 = r2and3.edges (); + db::Edges eempty (db::RecursiveShapeIterator (ly, top_cell, lempty)); + db::Edges edots = e2and3.processed (db::EdgeSegmentSelector (-1, 0, 0)); + db::Edges edotscopy = e2and3.processed (db::EdgeSegmentSelector (-1, 0, 0)); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), edots.merged ()); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), e3 & e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), e3 & edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), e3 & eempty); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), e3 & e3copy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), eempty & e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), edots & edotscopy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 0)), edots & e2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (27, 0)), e21 & edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (28, 0)), edots & e21); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), e3 - e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), e3 - edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), e3 - eempty); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (33, 0)), e3 - e3copy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (34, 0)), eempty - e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (35, 0)), edots - edotscopy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (36, 0)), edots - e2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (37, 0)), e21 - edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (38, 0)), edots - e21); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), e3 ^ e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), e3 ^ edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 0)), e3 ^ eempty); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (43, 0)), e3 ^ e3copy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (44, 0)), eempty ^ e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (45, 0)), edots ^ edotscopy); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (46, 0)), edots ^ e2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (47, 0)), e21 ^ edots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (48, 0)), edots ^ e21); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (50, 0)), e3.andnot(e2and3).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (51, 0)), e3.andnot(edots).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (52, 0)), e3.andnot(eempty).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (53, 0)), e3.andnot(e3copy).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (54, 0)), eempty.andnot(e2and3).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (55, 0)), edots.andnot(edotscopy).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (56, 0)), edots.andnot(e2).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (57, 0)), e21.andnot(edots).first); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (58, 0)), edots.andnot(e21).first); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (60, 0)), e3.andnot(e2and3).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (61, 0)), e3.andnot(edots).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (62, 0)), e3.andnot(eempty).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (63, 0)), e3.andnot(e3copy).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (64, 0)), eempty.andnot(e2and3).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (65, 0)), edots.andnot(edotscopy).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (66, 0)), edots.andnot(e2).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (67, 0)), e21.andnot(edots).second); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (68, 0)), edots.andnot(e21).second); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (70, 0)), e3.intersections(e2and3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (71, 0)), e3.intersections(edots)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (72, 0)), e3.intersections(eempty)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (73, 0)), e3.intersections(e3copy)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (74, 0)), eempty.intersections(e2and3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (75, 0)), edots.intersections(edotscopy)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (76, 0)), edots.intersections(e2)); + // test, whether dots are not merged + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (77, 0)), edots.intersections(e2).select_interacting(e2)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (78, 0)), e21.intersections(edots)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (79, 0)), edots.intersections(e21)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_edges_au3_flat.gds"); +} + // GitHub issue #72 (Edges/Region NOT issue) TEST(100) { diff --git a/src/db/unit_tests/dbEdgesUtilsTests.cc b/src/db/unit_tests/dbEdgesUtilsTests.cc index 86a0ad53d..bf201b5f1 100644 --- a/src/db/unit_tests/dbEdgesUtilsTests.cc +++ b/src/db/unit_tests/dbEdgesUtilsTests.cc @@ -83,5 +83,25 @@ TEST(1) EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 11), db::Point (10, 11)), db::Polygon (db::Box (0, 0, 10, 10))), true); EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 5), db::Point (10, 5)), db::Polygon (db::Box (0, 0, 10, 10))), false); EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-5, 5), db::Point (15, 5)), db::Polygon (db::Box (0, 0, 10, 10))), false); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 5), db::Point (0, 5)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 0), db::Point (10, 0)), db::Polygon (db::Box (0, 0, 10, 10))), true); + + db::Point pts[] = { + db::Point (0, 0), + db::Point (0, 10), + db::Point (20, 10), + db::Point (20, -10), + db::Point (10, -10), + db::Point (10, 0) + }; + + db::Polygon poly; + poly.assign_hull (pts + 0, pts + sizeof(pts) / sizeof(pts[0])); + + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 10), db::Point (20, 10)), poly), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, -10), db::Point (20, -10)), poly), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 0), db::Point (11, 0)), poly), false); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 0), db::Point (10, 0)), poly), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 0), db::Point (9, 0)), poly), true); } diff --git a/src/db/unit_tests/dbRegionProcessorTests.cc b/src/db/unit_tests/dbRegionProcessorTests.cc new file mode 100644 index 000000000..da534f5a9 --- /dev/null +++ b/src/db/unit_tests/dbRegionProcessorTests.cc @@ -0,0 +1,80 @@ + + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 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 "dbRegionProcessors.h" + + +TEST(1_RegionToEdgesProcessor) +{ + db::Point hull[] = { + db::Point (0, 0), + db::Point (0, 1000), + db::Point (1000, 1000), + db::Point (1000, 2000), + db::Point (2000, 2000), + db::Point (2000, 1000), + db::Point (3000, 1000), + db::Point (3000, 0) + }; + + db::Point hole[] = { + db::Point (100, 100), + db::Point (2900, 100), + db::Point (2900, 900), + db::Point (100, 900) + }; + + db::Polygon poly; + poly.assign_hull (hull + 0, hull + sizeof (hull) / sizeof (hull[0])); + poly.insert_hole (hole + 0, hole + sizeof (hole) / sizeof (hole[0])); + + std::vector result; + + result.clear (); + db::PolygonToEdgeProcessor ().process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(0,0;0,1000);(0,1000;1000,1000);(1000,1000;1000,2000);(1000,2000;2000,2000);(2000,2000;2000,1000);(2000,1000;3000,1000);(3000,1000;3000,0);(3000,0;0,0);(100,100;2900,100);(2900,100;2900,900);(2900,900;100,900);(100,900;100,100)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::Concave).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(2900,100;2900,900);(2900,900;100,900);(100,900;100,100);(100,100;2900,100)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::Convex).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(1000,2000;2000,2000);(3000,1000;3000,0);(3000,0;0,0);(0,0;0,1000)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::Step).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(0,1000;1000,1000);(1000,1000;1000,2000);(2000,2000;2000,1000);(2000,1000;3000,1000)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::StepOut).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(1000,1000;1000,2000);(2000,1000;3000,1000)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::StepIn).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(0,1000;1000,1000);(2000,2000;2000,1000)"); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 2884fc920..a5ef9690e 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -87,6 +87,7 @@ SOURCES = \ dbDeepTextsTests.cc \ dbNetShapeTests.cc \ dbHierNetsProcessorTests.cc \ + dbRegionProcessorTests.cc \ dbAsIfFlatRegionTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/doc/doc/about/drc_ref_drc.xml b/src/doc/doc/about/drc_ref_drc.xml index 7f121c94a..6b080c952 100644 --- a/src/doc/doc/about/drc_ref_drc.xml +++ b/src/doc/doc/about/drc_ref_drc.xml @@ -455,6 +455,7 @@ out = in.drc(covering(other) > 2)

Usage:

  • expression.edges
  • +
  • expression.edges(mode)

Polygons will be separated into edges forming their contours. Edge pairs will be @@ -468,6 +469,20 @@ for the edges:

 out = in.drc(primary.edges)
 
+

+The "mode" argument allows selecting specific edges from polygons. +Allowed values are: "convex", "concave", "step", "step_in" and "step_out". +"step" generates edges only if they provide a step between two other +edges. "step_in" creates edges that make a step towards the inside of +the polygon and "step_out" creates edges that make a step towards the +outside: +

+

+out = in.drc(primary.edges(convex))
+
+

+The mode argument is ignored when translating other objects than +polygons.

"end_segments" - Returns the part at the end of each edge of the input

diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml index 35af6a5b4..7db8dd897 100644 --- a/src/doc/doc/about/drc_ref_layer.xml +++ b/src/doc/doc/about/drc_ref_layer.xml @@ -783,6 +783,11 @@ apply to this method.

"edges" - Decomposes the layer into single edges

+

Usage:

+
    +
  • layer.edges
  • +
  • layer.edges(mode)
  • +

Edge pair collections are decomposed into the individual edges that make up the edge pairs. Polygon layers are decomposed into the edges making up the @@ -791,6 +796,36 @@ is called on.

Merged semantics applies, i.e. the result reflects merged polygons rather than individual ones unless raw mode is chosen. +

+The "mode" argument allows selecting specific edges from polygons. +Allowed values are: "convex", "concave", "step", "step_in" and "step_out". +"step" generates edges only if they provide a step between two other +edges. "step_in" creates edges that make a step towards the inside of +the polygon and "step_out" creates edges that make a step towards the +outside: +

+

+out = in.edges(convex)
+
+

+This feature is only available for polygon layers. +

+The following images show the effect of the mode argument: +

+ + + + + + + + + + + + + +

"edges?" - Returns true, if the layer is an edge layer

diff --git a/src/doc/doc/images/drc_edge_modes1.png b/src/doc/doc/images/drc_edge_modes1.png new file mode 100644 index 000000000..f5270d944 Binary files /dev/null and b/src/doc/doc/images/drc_edge_modes1.png differ diff --git a/src/doc/doc/images/drc_edge_modes2.png b/src/doc/doc/images/drc_edge_modes2.png new file mode 100644 index 000000000..5005e888d Binary files /dev/null and b/src/doc/doc/images/drc_edge_modes2.png differ diff --git a/src/doc/doc/images/drc_edge_modes3.png b/src/doc/doc/images/drc_edge_modes3.png new file mode 100644 index 000000000..cbc9c978b Binary files /dev/null and b/src/doc/doc/images/drc_edge_modes3.png differ diff --git a/src/doc/doc/images/drc_edge_modes4.png b/src/doc/doc/images/drc_edge_modes4.png new file mode 100644 index 000000000..94cf50140 Binary files /dev/null and b/src/doc/doc/images/drc_edge_modes4.png differ diff --git a/src/doc/doc/images/drc_edge_modes5.png b/src/doc/doc/images/drc_edge_modes5.png new file mode 100644 index 000000000..706c18fa4 Binary files /dev/null and b/src/doc/doc/images/drc_edge_modes5.png differ diff --git a/src/doc/doc/images/drc_edge_modes6.png b/src/doc/doc/images/drc_edge_modes6.png new file mode 100644 index 000000000..e90b4380f Binary files /dev/null and b/src/doc/doc/images/drc_edge_modes6.png differ diff --git a/src/doc/docDRCLVSResources.qrc b/src/doc/docDRCLVSResources.qrc index e4449b7ed..905f12d19 100644 --- a/src/doc/docDRCLVSResources.qrc +++ b/src/doc/docDRCLVSResources.qrc @@ -59,6 +59,12 @@ doc/images/drc_extended2.png doc/images/drc_extended3.png doc/images/drc_extended4.png + doc/images/drc_edge_modes1.png + doc/images/drc_edge_modes2.png + doc/images/drc_edge_modes3.png + doc/images/drc_edge_modes4.png + doc/images/drc_edge_modes5.png + doc/images/drc_edge_modes6.png doc/images/drc_extents1.png doc/images/drc_extents2.png doc/images/drc_inside.png diff --git a/src/drc/drc/built-in-macros/_drc_complex_ops.rb b/src/drc/drc/built-in-macros/_drc_complex_ops.rb index aa9e9e7d3..3f093df45 100644 --- a/src/drc/drc/built-in-macros/_drc_complex_ops.rb +++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb @@ -991,6 +991,7 @@ CODE # @name edges # @brief Converts the input shapes into edges # @synopsis expression.edges + # @synopsis expression.edges(mode) # # Polygons will be separated into edges forming their contours. Edge pairs will be # decomposed into individual edges. @@ -1003,9 +1004,38 @@ CODE # @code # out = in.drc(primary.edges) # @/code + # + # The "mode" argument allows selecting specific edges from polygons. + # Allowed values are: "convex", "concave", "step", "step_in" and "step_out". + # "step" generates edges only if they provide a step between two other + # edges. "step_in" creates edges that make a step towards the inside of + # the polygon and "step_out" creates edges that make a step towards the + # outside: + # + # @code + # out = in.drc(primary.edges(convex)) + # @/code + # + # In addition, "not_.." variants are available which selects edges + # not qualifying for the specific mode: + # + # @code + # out = in.drc(primary.edges(not_convex)) + # @/code + # + # The mode argument is ignored when translating other objects than + # polygons. - def edges - return DRCOpNodeFilter::new(@engine, self, :new_edges, "edges") + def edges(mode = nil) + if mode + if ! mode.is_a?(DRC::DRCEdgeMode) + raise "The mode argument needs to be a mode type (convex, concave, step, step_in or step_out)" + end + mode = mode.value + else + mode = RBA::EdgeMode::All + end + return DRCOpNodeFilter::new(@engine, self, :new_edges, "edges", mode) end # %DRC% diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 14eb0c842..e5f2ffa26 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -255,6 +255,46 @@ module DRC DRCJoinFlag::new(true) end + def convex + DRCEdgeMode::new(RBA::EdgeMode::Convex) + end + + def not_convex + DRCEdgeMode::new(RBA::EdgeMode::NotConvex) + end + + def concave + DRCEdgeMode::new(RBA::EdgeMode::Concave) + end + + def not_concave + DRCEdgeMode::new(RBA::EdgeMode::NotConcave) + end + + def step_in + DRCEdgeMode::new(RBA::EdgeMode::StepIn) + end + + def not_step_in + DRCEdgeMode::new(RBA::EdgeMode::NotStepIn) + end + + def step_out + DRCEdgeMode::new(RBA::EdgeMode::StepOut) + end + + def not_step_out + DRCEdgeMode::new(RBA::EdgeMode::NotStepOut) + end + + def step + DRCEdgeMode::new(RBA::EdgeMode::Step) + end + + def not_step + DRCEdgeMode::new(RBA::EdgeMode::NotStep) + end + def padding_zero DRCDensityPadding::new(:zero) end diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 24c2cbb2e..870591330 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -3387,6 +3387,8 @@ CODE # %DRC% # @name edges # @brief Decomposes the layer into single edges + # @synopsis layer.edges + # @synopsis layer.edges(mode) # # Edge pair collections are decomposed into the individual edges that make up # the edge pairs. Polygon layers are decomposed into the edges making up the @@ -3395,13 +3397,61 @@ CODE # # Merged semantics applies, i.e. the result reflects merged polygons rather than # individual ones unless raw mode is chosen. + # + # The "mode" argument allows selecting specific edges from polygons. + # Allowed values are: "convex", "concave", "step", "step_in" and "step_out". + # "step" generates edges only if they provide a step between two other + # edges. "step_in" creates edges that make a step towards the inside of + # the polygon and "step_out" creates edges that make a step towards the + # outside: + # + # @code + # out = in.edges(convex) + # @/code + # + # In addition, "not_.." variants are available which selects edges + # not qualifying for the specific mode: + # + # @code + # out = in.edges(not_convex) + # @/code + # + # The mode argument is only available for polygon layers. + # + # The following images show the effect of the mode argument: + # + # @table + # @tr + # @td @img(/images/drc_edge_modes1.png) @/td + # @td @img(/images/drc_edge_modes2.png) @/td + # @/tr + # @tr + # @td @img(/images/drc_edge_modes3.png) @/td + # @td @img(/images/drc_edge_modes4.png) @/td + # @/tr + # @tr + # @td @img(/images/drc_edge_modes5.png) @/td + # @td @img(/images/drc_edge_modes6.png) @/td + # @/tr + # @/table %w(edges).each do |f| eval <<"CODE" - def #{f} + def #{f}(mode = nil) + if mode + if ! mode.is_a?(DRC::DRCEdgeMode) + raise "The mode argument needs to be a mode type (convex, concave, step, step_in or step_out)" + end + if ! self.data.is_a?(RBA::Region) + raise "The mode argument is only available for polygon layers" + end + mode = mode.value + else + mode = RBA::EdgeMode::All + end @engine._context("#{f}") do if self.data.is_a?(RBA::Region) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :#{f})) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :#{f}, mode)) elsif self.data.is_a?(RBA::EdgePairs) DRCLayer::new(@engine, @engine._cmd(self.data, :#{f})) else diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index fd774ebd6..482f18db8 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -57,6 +57,14 @@ module DRC end end + # A wrapper for the edge mode value for Region#edges + class DRCEdgeMode + attr_accessor :value + def initialize(v) + self.value = v + end + end + # A wrapper for the join flag for extended class DRCJoinFlag attr_accessor :value diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 1362acee0..1c8180a8d 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1672,3 +1672,33 @@ TEST(92_issue1594_dual_top) CHECKPOINT (); compare_netlists (_this, output, au); } + +TEST(100_edge_interaction_with_count) +{ + run_test (_this, "100", false); +} + +TEST(100d_edge_interaction_with_count) +{ + run_test (_this, "100", true); +} + +TEST(101_edge_booleans_with_dots) +{ + run_test (_this, "101", false); +} + +TEST(101d_edge_booleans_with_dots) +{ + run_test (_this, "101", true); +} + +TEST(102_edge_modes) +{ + run_test (_this, "102", false); +} + +TEST(102d_edge_modes) +{ + run_test (_this, "102", true); +} diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 1f7ab2b93..d9fd91ddd 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -1080,6 +1080,12 @@ OASISReader::do_read (db::Layout &layout) LNameJoinOp2 op2; layer_names ().add (l1, l2 + 1, dt_map, op2); + // for singular layers, force a layer entry: + // this way we can have empty, but existing layers. + if (l1 == l2 && dt1 == dt2) { + open_dl (layout, db::LDPair (l1, dt1)); + } + reset_modal_variables (); // ignore properties attached to this name item diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc index f9e4bfefc..ae287e6f0 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc @@ -22,6 +22,7 @@ #include "dbOASISReader.h" +#include "dbOASISWriter.h" #include "dbTextWriter.h" #include "dbTestSupport.h" #include "tlLog.h" @@ -487,21 +488,18 @@ TEST(99) // XGEOMTRY tests (#773) TEST(100) { - const char *expected = + const char *expected = "begin_lib 0.0001\n" "begin_cell {mask}\n" - "boundary 7 1 {13237 5356} {13210 5490} {13192 5530} {13170 5563} {13130 5586} {13090 5583} {13070 5570} {13050 5551} {13037 5530} {13021 5490} {12988 5378} {12938 5390} {12963 5530} {12977 5570} {12998 5610} {13034 5650} {13051 5663} {13090 5678} {13130 5679} {13171 5667} {13210 5638} {13232 5611} {13253 5570} {13274 5490} {13291 5365} {13237 5356}\n" - "boundary 4 0 {10772 1658} {10772 1744} {14510 1744} {14510 1658} {10772 1658}\n" - "boundary 4 0 {14510 1658} {14510 1744} {15672 1744} {15672 1658} {14510 1658}\n" - "boundary 4 0 {18157 647} {18157 676} {21642 676} {21642 647} {18157 647}\n" - "boundary 6 0 {6743 2449} {6743 4230} {9061 4230} {9061 2449} {6743 2449}\n" - "boundary 2 3 {21642 3613} {21642 4005} {19409 4005} {19409 6980} {21812 6980} {21812 4958} {21942 4958} {21942 4005} {21812 4005} {21812 3613} {21642 3613}\n" - "boundary 2 4 {21642 4005} {21642 4958} {21812 4958} {21812 4005} {21642 4005}\n" - "boundary 8 0 {21680 4106} {21640 4107} {21600 4118} {21574 4130} {21560 4138} {21520 4163} {21509 4170} {21480 4194} {21458 4210} {21440 4227} {21411 4250} {21400 4262} {21366 4290} {21360 4298} {21324 4330} {21320 4335} {21282 4370} {21280 4373} {21241 4410} {21240 4411} {21200 4450} {21160 4490} {21159 4490} {21039 4610} {21000 4650} {20960 4690} {20960 4691} {20921 4730} {20920 4732} {20896 4770} {20886 4810} {20882 4850} {20880 4930} {20880 5330} {20920 5370} {20960 5370} {21000 5340} {21013 5330} {21040 5325} {21080 5309} {21120 5291} {21121 5290} {21160 5276} {21200 5258} {21210 5250} {21240 5240} {21280 5222} {21295 5210} {21320 5202} {21360 5181} {21374 5170} {21400 5160} {21440 5136} {21447 5130} {21480 5112} {21510 5090} {21520 5086} {21560 5058} {21568 5050} {21600 5027} {21617 5010} {21640 4993} {21662 4970} {21680 4955} {21701 4930} {21720 4910} {21735 4890} {21760 4856} {21764 4850} {21786 4810} {21800 4781} {21805 4770} {21818 4730} {21828 4690} {21836 4650} {21840 4616} {21841 4610} {21845 4530} {21845 4450} {21844 4410} {21841 4370} {21840 4358} {21836 4330} {21829 4290} {21818 4250} {21803 4210} {21800 4205} {21778 4170} {21760 4148} {21738 4130} {21720 4118} {21680 4106}\n" "boundary 1 0 {17922 6288} {17922 6510} {18150 6510} {18150 6288} {17922 6288}\n" "boundary 1 0 {18157 647} {18157 676} {21630 676} {21630 647} {18157 647}\n" "boundary 1 0 {21956 0} {21956 89} {22047 89} {22047 0} {21956 0}\n" + "boundary 2 3 {21642 3613} {21642 4005} {19409 4005} {19409 6980} {21812 6980} {21812 4958} {21942 4958} {21942 4005} {21812 4005} {21812 3613} {21642 3613}\n" + "boundary 2 4 {21642 4005} {21642 4958} {21812 4958} {21812 4005} {21642 4005}\n" "boundary 3 0 {15392 1744} {15392 1774} {15672 1774} {15672 1744} {15392 1744}\n" + "boundary 4 0 {10772 1658} {10772 1744} {14510 1744} {14510 1658} {10772 1658}\n" + "boundary 4 0 {14510 1658} {14510 1744} {15672 1744} {15672 1658} {14510 1658}\n" + "boundary 4 0 {18157 647} {18157 676} {21642 676} {21642 647} {18157 647}\n" "boundary 5 1 {15550 1658} {15550 1673} {15570 1673} {15570 1658} {15550 1658}\n" "boundary 5 1 {15661 1657} {15641 1659} {15642 1671} {15662 1669} {15661 1657}\n" "boundary 5 1 {18150 7440} {18150 7460} {18162 7460} {18162 7440} {18150 7440}\n" @@ -519,6 +517,9 @@ TEST(100) "boundary 5 1 {25710 1978} {25710 1998} {25722 1998} {25722 1978} {25710 1978}\n" "boundary 5 1 {25710 2800} {25710 2820} {25722 2820} {25722 2800} {25710 2800}\n" "boundary 5 2 {18074 6408} {17971 6486} {17983 6502} {18086 6424} {18074 6408}\n" + "boundary 6 0 {6743 2449} {6743 4230} {9061 4230} {9061 2449} {6743 2449}\n" + "boundary 7 1 {13237 5356} {13210 5490} {13192 5530} {13170 5563} {13130 5586} {13090 5583} {13070 5570} {13050 5551} {13037 5530} {13021 5490} {12988 5378} {12938 5390} {12963 5530} {12977 5570} {12998 5610} {13034 5650} {13051 5663} {13090 5678} {13130 5679} {13171 5667} {13210 5638} {13232 5611} {13253 5570} {13274 5490} {13291 5365} {13237 5356}\n" + "boundary 8 0 {21680 4106} {21640 4107} {21600 4118} {21574 4130} {21560 4138} {21520 4163} {21509 4170} {21480 4194} {21458 4210} {21440 4227} {21411 4250} {21400 4262} {21366 4290} {21360 4298} {21324 4330} {21320 4335} {21282 4370} {21280 4373} {21241 4410} {21240 4411} {21200 4450} {21160 4490} {21159 4490} {21039 4610} {21000 4650} {20960 4690} {20960 4691} {20921 4730} {20920 4732} {20896 4770} {20886 4810} {20882 4850} {20880 4930} {20880 5330} {20920 5370} {20960 5370} {21000 5340} {21013 5330} {21040 5325} {21080 5309} {21120 5291} {21121 5290} {21160 5276} {21200 5258} {21210 5250} {21240 5240} {21280 5222} {21295 5210} {21320 5202} {21360 5181} {21374 5170} {21400 5160} {21440 5136} {21447 5130} {21480 5112} {21510 5090} {21520 5086} {21560 5058} {21568 5050} {21600 5027} {21617 5010} {21640 4993} {21662 4970} {21680 4955} {21701 4930} {21720 4910} {21735 4890} {21760 4856} {21764 4850} {21786 4810} {21800 4781} {21805 4770} {21818 4730} {21828 4690} {21836 4650} {21840 4616} {21841 4610} {21845 4530} {21845 4450} {21844 4410} {21841 4370} {21840 4358} {21836 4330} {21829 4290} {21818 4250} {21803 4210} {21800 4205} {21778 4170} {21760 4148} {21738 4130} {21720 4118} {21680 4106}\n" "end_cell\n" "end_lib\n" ; @@ -543,6 +544,41 @@ TEST(100) EXPECT_EQ (std::string (os.string ()), std::string (expected)) } +// Empty layers through LAYERMAP +TEST(101) +{ + db::Layout ly; + ly.add_cell ("TOP"); + ly.insert_layer (db::LayerProperties (1, 0, "A")); + ly.insert_layer (db::LayerProperties (2, 0, "")); + ly.insert_layer (db::LayerProperties (3, 0, "C")); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_OASISReader101.oas"); + + { + tl::OutputStream stream (tmp_file); + db::OASISWriter writer; + db::SaveLayoutOptions options; + writer.write (ly, stream, options); + } + + db::Layout ly_new; + + { + tl::InputStream stream (tmp_file); + db::Reader reader (stream); + reader.read (ly_new); + } + + // NOTE: only named layers are written into layer table + EXPECT_EQ (ly_new.cell_by_name ("TOP").first, true); + EXPECT_EQ (int (ly_new.layers ()), 2); + if (int (ly_new.layers ()) == 2) { + EXPECT_EQ (ly_new.get_properties (0).to_string (), "A (1/0)"); + EXPECT_EQ (ly_new.get_properties (1).to_string (), "C (3/0)"); + } +} + TEST(Bug_121_1) { db::Manager m (false); diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc index 6df3ff111..c85a7f600 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc @@ -255,6 +255,13 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::Reader reader (stream); reader.read (layout); + // named layers create a mismatch between GDS and OASIS, so we unname them here + for (auto l = layout.begin_layers (); l != layout.end_layers (); ++l) { + db::LayerProperties lp = layout.get_properties ((*l).first); + lp.name.clear (); + layout.set_properties ((*l).first, lp); + } + db::SaveLayoutOptions options; db::OASISWriterOptions oasis_options; oasis_options.compression_level = compr; diff --git a/src/tl/tl/tlHttpStream.h b/src/tl/tl/tlHttpStream.h index a85dc3348..529e81d85 100644 --- a/src/tl/tl/tlHttpStream.h +++ b/src/tl/tl/tlHttpStream.h @@ -112,7 +112,6 @@ public: * @brief Polling: call this function regularly to explicitly establish polling * (in the Qt framework, this is done automatically within the event loop) * May throw a tl::CancelException to stop. - * Returns true if a message has arrived. */ void tick (); @@ -209,6 +208,21 @@ private: InputHttpStreamCallback *mp_callback; }; +/** + * @brief A HTTP stream with .gz support + */ +class TL_PUBLIC InflatingInputHttpStream + : public inflating_input_stream +{ +public: + /** + * @brief Open a stream with the given URL + */ + InflatingInputHttpStream (const std::string &url) + : inflating_input_stream (new InputHttpStream (url)) + { } +}; + } #endif diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index dc57b3a98..4ffc2d43e 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -140,6 +140,125 @@ public: gzFile zs; }; +// --------------------------------------------------------------- +// inflating_input_stream implementation + +/** + * @brief A wrapper that adds generic .gz support + */ +template +inflating_input_stream::inflating_input_stream (Base *delegate) + : m_inflating_stream (delegate), m_is_compressed (false), mp_delegate (delegate) +{ + enter_inflate (); +} + +template +size_t +inflating_input_stream::read (char *b, size_t n) +{ + // TODO: this is somewhat inefficient, but we only use it for pipe and HTTP streams + size_t i = 0; + while (i < n) { + + if (m_is_compressed || m_inflating_stream.blen () == 0) { + + const char *read = m_inflating_stream.get (1); + if (! read) { + break; + } + *b++ = *read; + i += 1; + + } else { + + size_t nn = std::min (n - i, m_inflating_stream.blen ()); + const char *read = m_inflating_stream.get (nn); + tl_assert (read != 0); + memcpy (b, read, nn); + b += nn; + i += nn; + + } + + } + return i; +} + +template +void +inflating_input_stream::enter_inflate () +{ + // identify and skip header for .gz file + if (auto_detect_gz ()) { + m_is_compressed = true; + m_inflating_stream.inflate (true /* stop after inflated block */); + } else { + m_inflating_stream.unget (m_inflating_stream.pos ()); + } +} + +template +bool +inflating_input_stream::auto_detect_gz () +{ + std::string header = m_inflating_stream.read_all (10); + if (header.size () < 10) { + return false; + } + + const unsigned char *header_data = (const unsigned char *) header.c_str (); + unsigned char flags = header_data[3]; + if (header_data[0] != 0x1f || header_data[1] != 0x8b || header_data[2] != 0x08 || (flags & 0xe0) != 0) { + return false; + } + + // .gz signature found + + bool has_fhcrc = (flags & 0x02) != 0; + bool has_extra = (flags & 0x04) != 0; + bool has_fname = (flags & 0x08) != 0; + bool has_comment = (flags & 0x10) != 0; + + if (has_extra) { + const unsigned char *xlen = (const unsigned char *) m_inflating_stream.get (2); + if (! xlen) { + throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing XLEN field"))); + } + const char *xdata = m_inflating_stream.get (size_t (xlen[0]) + (size_t (xlen[1]) << 8)); + if (! xdata) { + throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing EXTRA data"))); + } + } + + if (has_fname) { + const char *c; + while ((c = m_inflating_stream.get (1)) != 0 && *c) + ; + if (! c) { + throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing FNAME data trailing zero byte"))); + } + } + + if (has_comment) { + const char *c; + while ((c = m_inflating_stream.get (1)) != 0 && *c) + ; + if (! c) { + throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing COMMENT data trailing zero byte"))); + } + } + + if (has_fhcrc) { + const char *crc16 = m_inflating_stream.get (2); + if (! crc16) { + throw tl::Exception (tl::to_string (tr ("Corrupt .gz header - missing CRC16 data"))); + } + } + + return true; +} + // --------------------------------------------------------------- // InputStream implementation @@ -175,7 +294,7 @@ public: } InputStream::InputStream (InputStreamBase &delegate) - : m_pos (0), mp_bptr (0), mp_delegate (&delegate), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false) + : m_pos (0), mp_bptr (0), mp_delegate (&delegate), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false), m_stop_after_inflate (false) { m_bcap = 4096; // initial buffer capacity m_blen = 0; @@ -183,7 +302,7 @@ InputStream::InputStream (InputStreamBase &delegate) } InputStream::InputStream (InputStreamBase *delegate) - : m_pos (0), mp_bptr (0), mp_delegate (delegate), m_owns_delegate (true), mp_inflate (0), m_inflate_always (false) + : m_pos (0), mp_bptr (0), mp_delegate (delegate), m_owns_delegate (true), mp_inflate (0), m_inflate_always (false), m_stop_after_inflate (false) { m_bcap = 4096; // initial buffer capacity m_blen = 0; @@ -191,7 +310,7 @@ InputStream::InputStream (InputStreamBase *delegate) } InputStream::InputStream (const std::string &abstract_path) - : m_pos (0), mp_bptr (0), mp_delegate (0), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false) + : m_pos (0), mp_bptr (0), mp_delegate (0), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false), m_stop_after_inflate (false) { m_bcap = 4096; // initial buffer capacity m_blen = 0; @@ -252,7 +371,7 @@ InputStream::InputStream (const std::string &abstract_path) } else if (ex.test ("pipe:")) { - mp_delegate = new InputPipe (ex.get ()); + mp_delegate = new InflatingInputPipe (ex.get ()); } else { @@ -260,7 +379,7 @@ InputStream::InputStream (const std::string &abstract_path) if (uri.scheme () == "http" || uri.scheme () == "https") { #if defined(HAVE_CURL) || defined(HAVE_QT) - mp_delegate = new InputHttpStream (abstract_path); + mp_delegate = new InflatingInputHttpStream (abstract_path); #else throw tl::Exception (tl::to_string (tr ("HTTP support not enabled - HTTP/HTTPS paths are not available"))); #endif @@ -337,9 +456,16 @@ InputStream::get (size_t n, bool bypass_inflate) tl_assert (r != 0); // since deflate did not report at_end() return r; + } else if (m_stop_after_inflate) { + + // report EOF after the inflator has finished + return 0; + } else { + delete mp_inflate; mp_inflate = 0; + } } @@ -384,9 +510,16 @@ InputStream::get (size_t n, bool bypass_inflate) void InputStream::unget (size_t n) { + if (n == 0) { + return; + } + if (mp_inflate) { + // TODO: this will not work if mp_inflate just got destroyed + // (no unget into previous compressed block) mp_inflate->unget (n); } else { + tl_assert (mp_buffer + n <= mp_bptr); mp_bptr -= n; m_blen += n; m_pos -= n; @@ -476,10 +609,11 @@ void InputStream::copy_to (tl::OutputStream &os) } void -InputStream::inflate () +InputStream::inflate (bool stop_after) { tl_assert (mp_inflate == 0); mp_inflate = new tl::InflateFilter (*this); + m_stop_after_inflate = stop_after; } void @@ -1334,11 +1468,11 @@ OutputPipe::write (const char *b, size_t n) // --------------------------------------------------------------- // InputPipe delegate implementation -InputPipe::InputPipe (const std::string &path) +InputPipe::InputPipe (const std::string &source) : m_file (NULL) { - m_source = path; - m_file = popen (tl::string_to_system (path).c_str (), "r"); + m_source = source; + m_file = popen (tl::string_to_system (source).c_str (), "r"); if (m_file == NULL) { throw FilePOpenErrorException (m_source, errno); } diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h index 2412c4663..4e7cfdcf6 100644 --- a/src/tl/tl/tlStream.h +++ b/src/tl/tl/tlStream.h @@ -310,10 +310,9 @@ public: * an error occurs - commonly if the command cannot be executed. * This implementation is based on popen (). * - * @param cmd The command to execute - * @param read True, if the file should be read, false on write. + * @param source The command to execute */ - InputPipe (const std::string &path); + InputPipe (const std::string &source); /** * @brief Close the pipe @@ -348,8 +347,7 @@ public: */ virtual std::string source () const { - // No source (in the sense of a file name) is available .. - return std::string (); + return m_source; } virtual std::string absolute_path () const @@ -474,8 +472,11 @@ public: * the uncompressed data rather than the raw data, until the * compressed block is finished. * The stream must not be in inflate state yet. + * + * If "stop_after" is true, the stream will stop after the inflated + * block has finished. */ - void inflate (); + void inflate (bool stop_after = false); /** * @brief Enables "inflate" right from the beginning @@ -578,12 +579,87 @@ private: // inflate support InflateFilter *mp_inflate; bool m_inflate_always; + bool m_stop_after_inflate; // No copying currently InputStream (const InputStream &); InputStream &operator= (const InputStream &); }; +/** + * @brief A wrapper that adds generic .gz support + */ +template +class TL_PUBLIC_TEMPLATE inflating_input_stream + : public InputStreamBase +{ +public: + inflating_input_stream (Base *delegate); + + Base *delegate () + { + return mp_delegate; + } + + virtual size_t read (char *b, size_t n); + + virtual void reset () + { + m_inflating_stream.reset (); + enter_inflate (); + } + + virtual void close () + { + m_inflating_stream.close (); + } + + virtual std::string source () const + { + return m_inflating_stream.source (); + } + + virtual std::string absolute_path () const + { + return m_inflating_stream.absolute_path (); + } + + virtual std::string filename () const + { + return m_inflating_stream.filename (); + } + +private: + tl::InputStream m_inflating_stream; + bool m_is_compressed; + Base *mp_delegate; + + void enter_inflate (); + bool auto_detect_gz (); +}; + +/** + * @brief A pipe stream with .gz support + */ +class TL_PUBLIC InflatingInputPipe + : public inflating_input_stream +{ +public: + /** + * @brief Open a stream by connecting with the stdout of a given command + * + * Opening a pipe is a prerequisite for reading from the + * object. open() will throw a FilePOpenErrorException if + * an error occurs - commonly if the command cannot be executed. + * This implementation is based on popen (). + * + * @param source The command to execute + */ + InflatingInputPipe (const std::string &source) + : inflating_input_stream (new InputPipe (source)) + { } +}; + // --------------------------------------------------------------------------------- /** diff --git a/src/tl/unit_tests/tlHttpStreamTests.cc b/src/tl/unit_tests/tlHttpStreamTests.cc index e05c7e5fe..203ff8e1e 100644 --- a/src/tl/unit_tests/tlHttpStreamTests.cc +++ b/src/tl/unit_tests/tlHttpStreamTests.cc @@ -24,8 +24,10 @@ #include "tlHttpStream.h" #include "tlUnitTest.h" #include "tlTimer.h" +#include "tlStream.h" static std::string test_url1 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text"); +static std::string test_url1_gz ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata2/text.gz"); static std::string test_url2 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/dir1"); TEST(1) @@ -125,3 +127,29 @@ TEST(3) EXPECT_EQ (res, "hello, world.\n"); } +// tl::Stream embedding +TEST(4) +{ + if (! tl::InputHttpStream::is_available ()) { + throw tl::CancelException (); + } + + tl::InputStream stream (test_url1); + + std::string res = stream.read_all (); + EXPECT_EQ (res, "hello, world.\n"); +} + +// tl::Stream embedding with automatic unzip +TEST(5) +{ + if (! tl::InputHttpStream::is_available ()) { + throw tl::CancelException (); + } + + tl::InputStream stream (test_url1_gz); + + std::string res = stream.read_all (); + EXPECT_EQ (res, "hello, world.\n"); +} + diff --git a/src/tl/unit_tests/tlStreamTests.cc b/src/tl/unit_tests/tlStreamTests.cc index e51a7d4c4..517d71615 100644 --- a/src/tl/unit_tests/tlStreamTests.cc +++ b/src/tl/unit_tests/tlStreamTests.cc @@ -52,6 +52,13 @@ TEST(InputPipe2) EXPECT_NE (ret, 0); } +TEST(InputPipe3) +{ + tl::InputStream str ("pipe:echo HELLOWORLD"); + tl::TextInputStream tstr (str); + EXPECT_EQ (tstr.get_line (), "HELLOWORLD"); +} + TEST(OutputPipe1) { std::string tf = tmp_file ("pipe_out"); @@ -455,3 +462,5 @@ TEST(Backups) } } + + diff --git a/testdata/algo/deep_edges_au3.gds b/testdata/algo/deep_edges_au3.gds index 4bb15fdec..3742b8f31 100644 Binary files a/testdata/algo/deep_edges_au3.gds and b/testdata/algo/deep_edges_au3.gds differ diff --git a/testdata/algo/deep_edges_au3_flat.gds b/testdata/algo/deep_edges_au3_flat.gds new file mode 100644 index 000000000..ca8d210ec Binary files /dev/null and b/testdata/algo/deep_edges_au3_flat.gds differ diff --git a/testdata/algo/deep_edges_l1.gds b/testdata/algo/deep_edges_l1.gds new file mode 100644 index 000000000..7a9bada7e Binary files /dev/null and b/testdata/algo/deep_edges_l1.gds differ diff --git a/testdata/drc/drcSimpleTests_100.drc b/testdata/drc/drcSimpleTests_100.drc new file mode 100644 index 000000000..74d09c10f --- /dev/null +++ b/testdata/drc/drcSimpleTests_100.drc @@ -0,0 +1,89 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +e1 = l1.edges +e2 = l2.edges + +e1.output(10, 0) +e2.output(11, 0) + +e1.interacting(l2).output(100, 0) +e1.interacting(l2, 2).output(101, 0) +e1.interacting(l2, 2, 2).output(102, 0) +e1.interacting(l2, 3).output(103, 0) + +e1.split_interacting(l2)[0].output(110, 0) +e1.split_interacting(l2, 2)[0].output(111, 0) +e1.split_interacting(l2, 2, 2)[0].output(112, 0) +e1.split_interacting(l2, 3)[0].output(113, 0) + +e1.interacting(e2).output(200, 0) +e1.interacting(e2, 2).output(201, 0) +e1.interacting(e2, 2, 2).output(202, 0) +e1.interacting(e2, 3).output(203, 0) +e1.interacting(e2, 3..4).output(204, 0) + +e1.split_interacting(e2)[0].output(210, 0) +e1.split_interacting(e2, 2)[0].output(211, 0) +e1.split_interacting(e2, 2, 2)[0].output(212, 0) +e1.split_interacting(e2, 3)[0].output(213, 0) +e1.split_interacting(e2, 3..4)[0].output(214, 0) + +e1.not_interacting(l2).output(300, 0) +e1.not_interacting(l2, 2).output(301, 0) +e1.not_interacting(l2, 2, 2).output(302, 0) +e1.not_interacting(l2, 3).output(303, 0) + +e1.split_interacting(l2)[1].output(310, 0) +e1.split_interacting(l2, 2)[1].output(311, 0) +e1.split_interacting(l2, 2, 2)[1].output(312, 0) +e1.split_interacting(l2, 3)[1].output(313, 0) + +e1.not_interacting(e2).output(400, 0) +e1.not_interacting(e2, 2).output(401, 0) +e1.not_interacting(e2, 2, 2).output(402, 0) +e1.not_interacting(e2, 3).output(403, 0) +e1.not_interacting(e2, 3..4).output(404, 0) + +e1.split_interacting(e2)[1].output(410, 0) +e1.split_interacting(e2, 2)[1].output(411, 0) +e1.split_interacting(e2, 2, 2)[1].output(412, 0) +e1.split_interacting(e2, 3)[1].output(413, 0) +e1.split_interacting(e2, 3..4)[1].output(414, 0) + +# convex detection (the initial problem) + +c90 = l2.corners(as_dots, 90) +cm90 = l2.corners(as_dots, -90) +c90.output(1000, 0) +cm90.output(1001, 0) + +e2.interacting(c90, 2, 2).output(1100, 0) +e2.interacting(cm90, 2, 2).output(1101, 0) +e2.interacting(c90, 1, 1).output(1102, 0) +e2.interacting(cm90, 1, 1).output(1103, 0) + +c90.interacting(e1).output(1200, 0) +c90.not_interacting(e1).output(1201, 0) +c90.interacting(e2).output(1202, 0) +c90.not_interacting(e2).output(1203, 0) + +c90.interacting(l1).output(1300, 0) +c90.not_interacting(l1).output(1301, 0) +c90.interacting(l2).output(1302, 0) +c90.not_interacting(l2).output(1303, 0) + +c90.split_interacting(l1)[0].output(1310, 0) +c90.split_interacting(l1)[1].output(1311, 0) + diff --git a/testdata/drc/drcSimpleTests_100.gds b/testdata/drc/drcSimpleTests_100.gds new file mode 100644 index 000000000..e0c678c17 Binary files /dev/null and b/testdata/drc/drcSimpleTests_100.gds differ diff --git a/testdata/drc/drcSimpleTests_101.drc b/testdata/drc/drcSimpleTests_101.drc new file mode 100644 index 000000000..745d17f88 --- /dev/null +++ b/testdata/drc/drcSimpleTests_101.drc @@ -0,0 +1,52 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +e1 = l1.edges +e2 = l2.edges + +e1.output(10, 0) +e2.output(11, 0) + +c1 = l1.corners(as_dots) +c2 = l2.corners(as_dots) + +c1.output(20, 0) +c2.output(21, 0) + +(c2 & l1).output(100, 0) +(c2 - l1).output(101, 0) + +c2.andnot(l1)[0].output(110, 0) +c2.andnot(l1)[1].output(111, 0) + +c2.inside_part(l1).output(200, 0) +c2.outside_part(l1).output(201, 0) + +c2.inside_outside_part(l1)[0].output(210, 0) +c2.inside_outside_part(l1)[1].output(211, 0) + +c2.interacting(l1).output(300, 0) +c2.not_interacting(l1).output(301, 0) + +c2.split_interacting(l1)[0].output(310, 0) +c2.split_interacting(l1)[1].output(311, 0) + +(c2 & e1).output(400, 0) +(c2 - e1).output(401, 0) +(c2 ^ e1).output(402, 0) +(c2 | e1).output(403, 0) + +c2.andnot(e1)[0].output(410, 0) +c2.andnot(e1)[1].output(411, 0) + diff --git a/testdata/drc/drcSimpleTests_101.gds b/testdata/drc/drcSimpleTests_101.gds new file mode 100644 index 000000000..b3dc99ddb Binary files /dev/null and b/testdata/drc/drcSimpleTests_101.gds differ diff --git a/testdata/drc/drcSimpleTests_102.drc b/testdata/drc/drcSimpleTests_102.drc new file mode 100644 index 000000000..7187fe7aa --- /dev/null +++ b/testdata/drc/drcSimpleTests_102.drc @@ -0,0 +1,40 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +l2.edges.output(100, 0) +l2.edges(convex).output(101, 0) +l2.edges(concave).output(102, 0) +l2.edges(step).output(103, 0) +l2.edges(step_in).output(104, 0) +l2.edges(step_out).output(105, 0) + +l2.edges(not_convex).output(111, 0) +l2.edges(not_concave).output(112, 0) +l2.edges(not_step).output(113, 0) +l2.edges(not_step_in).output(114, 0) +l2.edges(not_step_out).output(115, 0) + +l2.drc(primary.edges).output(200, 0) +l2.drc(primary.edges(convex)).output(201, 0) +l2.drc(primary.edges(concave)).output(202, 0) +l2.drc(primary.edges(step)).output(203, 0) +l2.drc(primary.edges(step_in)).output(204, 0) +l2.drc(primary.edges(step_out)).output(205, 0) + +l2.drc(primary.edges(not_convex)).output(211, 0) +l2.drc(primary.edges(not_concave)).output(212, 0) +l2.drc(primary.edges(not_step)).output(213, 0) +l2.drc(primary.edges(not_step_in)).output(214, 0) +l2.drc(primary.edges(not_step_out)).output(215, 0) + diff --git a/testdata/drc/drcSimpleTests_102.gds b/testdata/drc/drcSimpleTests_102.gds new file mode 100644 index 000000000..b3dc99ddb Binary files /dev/null and b/testdata/drc/drcSimpleTests_102.gds differ diff --git a/testdata/drc/drcSimpleTests_au100.gds b/testdata/drc/drcSimpleTests_au100.gds new file mode 100644 index 000000000..dd25563fb Binary files /dev/null and b/testdata/drc/drcSimpleTests_au100.gds differ diff --git a/testdata/drc/drcSimpleTests_au100d.gds b/testdata/drc/drcSimpleTests_au100d.gds new file mode 100644 index 000000000..e977f6009 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au100d.gds differ diff --git a/testdata/drc/drcSimpleTests_au101.gds b/testdata/drc/drcSimpleTests_au101.gds new file mode 100644 index 000000000..f9d618e85 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au101.gds differ diff --git a/testdata/drc/drcSimpleTests_au101d.gds b/testdata/drc/drcSimpleTests_au101d.gds new file mode 100644 index 000000000..d61b0a5ae Binary files /dev/null and b/testdata/drc/drcSimpleTests_au101d.gds differ diff --git a/testdata/drc/drcSimpleTests_au102.gds b/testdata/drc/drcSimpleTests_au102.gds new file mode 100644 index 000000000..26068b9b3 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au102.gds differ diff --git a/testdata/drc/drcSimpleTests_au102d.gds b/testdata/drc/drcSimpleTests_au102d.gds new file mode 100644 index 000000000..e14a2cce2 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au102d.gds differ diff --git a/testdata/drc/drcSuiteTests_au1.oas b/testdata/drc/drcSuiteTests_au1.oas index 9e56a12cf..ef96649d3 100644 Binary files a/testdata/drc/drcSuiteTests_au1.oas and b/testdata/drc/drcSuiteTests_au1.oas differ diff --git a/testdata/drc/drcSuiteTests_au2.oas b/testdata/drc/drcSuiteTests_au2.oas index 067d6154c..81cb3d835 100644 Binary files a/testdata/drc/drcSuiteTests_au2.oas and b/testdata/drc/drcSuiteTests_au2.oas differ diff --git a/testdata/drc/drcSuiteTests_au3.oas b/testdata/drc/drcSuiteTests_au3.oas index 0960a1669..3d996bb5c 100644 Binary files a/testdata/drc/drcSuiteTests_au3.oas and b/testdata/drc/drcSuiteTests_au3.oas differ diff --git a/testdata/drc/drcSuiteTests_au4.oas b/testdata/drc/drcSuiteTests_au4.oas index d10bdf656..7c98f4486 100644 Binary files a/testdata/drc/drcSuiteTests_au4.oas and b/testdata/drc/drcSuiteTests_au4.oas differ diff --git a/testdata/drc/drcSuiteTests_au5.oas b/testdata/drc/drcSuiteTests_au5.oas index 990671ce6..2ad58c4ed 100644 Binary files a/testdata/drc/drcSuiteTests_au5.oas and b/testdata/drc/drcSuiteTests_au5.oas differ diff --git a/testdata/drc/drcSuiteTests_au6.oas b/testdata/drc/drcSuiteTests_au6.oas index bf7421344..d4e846c31 100644 Binary files a/testdata/drc/drcSuiteTests_au6.oas and b/testdata/drc/drcSuiteTests_au6.oas differ diff --git a/testdata/lefdef/blend_mode/au1.oas.gz b/testdata/lefdef/blend_mode/au1.oas.gz index e0e95c3a5..a1115551b 100644 Binary files a/testdata/lefdef/blend_mode/au1.oas.gz and b/testdata/lefdef/blend_mode/au1.oas.gz differ diff --git a/testdata/lefdef/blend_mode/au2.oas.gz b/testdata/lefdef/blend_mode/au2.oas.gz index b05825a58..091ede9a8 100644 Binary files a/testdata/lefdef/blend_mode/au2.oas.gz and b/testdata/lefdef/blend_mode/au2.oas.gz differ diff --git a/testdata/lefdef/blend_mode/au3.oas.gz b/testdata/lefdef/blend_mode/au3.oas.gz index b8ceb61e2..e52346650 100644 Binary files a/testdata/lefdef/blend_mode/au3.oas.gz and b/testdata/lefdef/blend_mode/au3.oas.gz differ diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index bdd290f01..cb1910017 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -821,6 +821,54 @@ class DBPolygon_TestClass < TestBase end + def test_breakPolygon + + pts = [] + pts << RBA::Point::new(0, 0) + pts << RBA::Point::new(0, 1000) + pts << RBA::Point::new(100, 1000) + pts << RBA::Point::new(100, 100) + pts << RBA::Point::new(1000, 100) + pts << RBA::Point::new(1000, 0) + + split = RBA::Polygon::new(pts).break(4, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::Polygon::new(pts).break(0, 2.0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::Polygon::new(pts).break(0, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)") + + split = RBA::SimplePolygon::new(pts).break(4, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::SimplePolygon::new(pts).break(0, 2.0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::SimplePolygon::new(pts).break(0, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)") + + pts = [] + pts << RBA::DPoint::new(0, 0) + pts << RBA::DPoint::new(0, 1000) + pts << RBA::DPoint::new(100, 1000) + pts << RBA::DPoint::new(100, 100) + pts << RBA::DPoint::new(1000, 100) + pts << RBA::DPoint::new(1000, 0) + + split = RBA::DPolygon::new(pts).break(4, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::DPolygon::new(pts).break(0, 2.0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::DPolygon::new(pts).break(0, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)") + + split = RBA::DSimplePolygon::new(pts).break(4, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::DSimplePolygon::new(pts).break(0, 2.0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,100;1000,100;1000,0);(0,100;0,1000;100,1000;100,100)") + split = RBA::DSimplePolygon::new(pts).break(0, 0) + assert_equal(split.collect { |p| p.to_s }.join(";"), "(0,0;0,1000;100,1000;100,100;1000,100;1000,0)") + + end + def test_voidMethodsReturnSelf hull = [ RBA::Point::new(0, 0), RBA::Point::new(6000, 0),