diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 203a89e9f..209c303ae 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -533,7 +533,7 @@ EdgesDelegate * AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const { std::auto_ptr output (new FlatEdges (true)); - EdgeBooleanClusterCollector cluster_collector (&output->raw_edges (), op); + EdgeBooleanClusterCollectorToShapes cluster_collector (&output->raw_edges (), op); db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (size () + (other ? other->size () : 0)); diff --git a/src/db/db/dbAsIfFlatEdges.h b/src/db/db/dbAsIfFlatEdges.h index c47cf8409..6596036b4 100644 --- a/src/db/db/dbAsIfFlatEdges.h +++ b/src/db/db/dbAsIfFlatEdges.h @@ -27,6 +27,7 @@ #include "dbCommon.h" #include "dbBoxScanner.h" #include "dbEdgesDelegate.h" +#include "dbEdgeBoolean.h" #include "dbBoxScanner.h" #include "dbPolygonTools.h" @@ -138,6 +139,11 @@ public: return boolean (&other, EdgeOr); } + virtual EdgesDelegate *intersections (const Edges &other) const + { + return boolean (&other, EdgeIntersections); + } + virtual EdgesDelegate *add_in_place (const Edges &other) { return add (other); diff --git a/src/db/db/dbBoxScanner.h b/src/db/db/dbBoxScanner.h index 868ecc709..eefd7df5a 100644 --- a/src/db/db/dbBoxScanner.h +++ b/src/db/db/dbBoxScanner.h @@ -137,6 +137,21 @@ struct box_scanner_receiver * terminate the scan process early if the outcome is known. */ bool stop () const { return false; } + + /** + * @brief Pre-scanning operations + * + * This method is called before the scanning starts. + */ + void initialize () { } + + /** + * @brief Post-scanning operations + * + * This method is called after the scan has finished (without exception). The argument is the + * return value (false if "stop" stopped the process). + */ + void finalize (bool) { } }; /** @@ -265,6 +280,22 @@ public: */ template bool process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ()) + { + rec.initialize (); + bool ret = do_process (rec, enl, bc); + rec.finalize (ret); + return ret; + } + +private: + container_type m_pp; + double m_fill_factor; + size_t m_scanner_thr; + bool m_report_progress; + std::string m_progress_desc; + + template + bool do_process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ()) { typedef typename BoxConvert::box_type box_type; typedef typename box_type::coord_type coord_type; @@ -420,13 +451,6 @@ public: return true; } - -private: - container_type m_pp; - double m_fill_factor; - size_t m_scanner_thr; - bool m_report_progress; - std::string m_progress_desc; }; /** @@ -469,6 +493,21 @@ struct box_scanner_receiver2 * terminate the scan process early if the outcome is known. */ bool stop () const { return false; } + + /** + * @brief Pre-scanning operations + * + * This method is called before the scanning starts. + */ + void initialize () { } + + /** + * @brief Post-scanning operations + * + * This method is called after the scan has finished (without exception). The argument is the + * return value (false if "stop" stopped the process). + */ + void finalize (bool) { } }; /** @@ -613,6 +652,23 @@ public: */ template bool process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ()) + { + rec.initialize (); + bool ret = do_process (rec, enl, bc1, bc2); + rec.finalize (ret); + return ret; + } + +private: + container_type1 m_pp1; + container_type2 m_pp2; + double m_fill_factor; + size_t m_scanner_thr; + bool m_report_progress; + std::string m_progress_desc; + + template + bool do_process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ()) { typedef typename BoxConvert1::box_type box_type; // must be same as BoxConvert2::box_type typedef typename box_type::coord_type coord_type; @@ -845,14 +901,6 @@ public: return true; } - -private: - container_type1 m_pp1; - container_type2 m_pp2; - double m_fill_factor; - size_t m_scanner_thr; - bool m_report_progress; - std::string m_progress_desc; }; /** diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index 220b2c212..e8929c7bf 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -349,7 +349,7 @@ public: // .. and run the merge operation s->second.clear (); - EdgeBooleanClusterCollector cluster_collector (&s->second, EdgeOr); + EdgeBooleanClusterCollectorToShapes cluster_collector (&s->second, EdgeOr); m_scanner.process (cluster_collector, 1, db::box_convert ()); return s->second; @@ -751,11 +751,11 @@ EdgesDelegate *DeepEdges::merged () const } DeepLayer -DeepEdges::and_or_not_with (const DeepEdges *other, bool and_op) const +DeepEdges::and_or_not_with (const DeepEdges *other, EdgeBoolOp op) const { DeepLayer dl_out (m_deep_layer.derived ()); - db::EdgeBoolAndOrNotLocalOperation op (and_op); + db::EdgeBoolAndOrNotLocalOperation local_op (op); db::local_processor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell ()); proc.set_base_verbosity (base_verbosity ()); @@ -763,7 +763,7 @@ DeepEdges::and_or_not_with (const DeepEdges *other, bool and_op) const proc.set_area_ratio (m_deep_layer.store ()->max_area_ratio ()); proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ()); - proc.run (&op, m_deep_layer.layer (), other->deep_layer ().layer (), dl_out.layer ()); + proc.run (&local_op, m_deep_layer.layer (), other->deep_layer ().layer (), dl_out.layer ()); return dl_out; } @@ -786,6 +786,26 @@ DeepEdges::edge_region_op (const DeepRegion *other, bool outside, bool include_b return dl_out; } +EdgesDelegate *DeepEdges::intersections (const Edges &other) const +{ + const DeepEdges *other_deep = dynamic_cast (other.delegate ()); + + if (empty () || other.empty ()) { + + // Nothing to do + return new EmptyEdges (); + + } else if (! other_deep) { + + return AsIfFlatEdges::intersections (other); + + } else { + + return new DeepEdges (and_or_not_with (other_deep, EdgeIntersections)); + + } +} + EdgesDelegate *DeepEdges::and_with (const Edges &other) const { const DeepEdges *other_deep = dynamic_cast (other.delegate ()); @@ -801,7 +821,7 @@ EdgesDelegate *DeepEdges::and_with (const Edges &other) const } else { - return new DeepEdges (and_or_not_with (other_deep, true)); + return new DeepEdges (and_or_not_with (other_deep, EdgeAnd)); } } @@ -851,7 +871,7 @@ EdgesDelegate *DeepEdges::not_with (const Edges &other) const } else { - return new DeepEdges (and_or_not_with (other_deep, false)); + return new DeepEdges (and_or_not_with (other_deep, EdgeNot)); } } @@ -903,8 +923,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, false)); - DeepLayer n2 (other_deep->and_or_not_with (this, false)); + DeepLayer n1 (and_or_not_with (other_deep, EdgeNot)); + DeepLayer n2 (other_deep->and_or_not_with (this, EdgeNot)); n1.add_from (n2); return new DeepEdges (n1); diff --git a/src/db/db/dbDeepEdges.h b/src/db/db/dbDeepEdges.h index e60bab2b1..af4f13e4f 100644 --- a/src/db/db/dbDeepEdges.h +++ b/src/db/db/dbDeepEdges.h @@ -28,6 +28,7 @@ #include "dbAsIfFlatEdges.h" #include "dbDeepShapeStore.h" +#include "dbEdgeBoolean.h" #include "dbEdgePairs.h" namespace db { @@ -132,6 +133,8 @@ public: virtual EdgesDelegate *add_in_place (const Edges &other); virtual EdgesDelegate *add (const Edges &other) const; + virtual EdgesDelegate *intersections (const Edges &other) const; + virtual EdgesDelegate *inside_part (const Region &other) const; virtual EdgesDelegate *outside_part (const Region &other) const; @@ -168,7 +171,7 @@ private: void init (); void ensure_merged_edges_valid () const; const DeepLayer &merged_deep_layer () const; - DeepLayer and_or_not_with(const DeepEdges *other, bool and_op) const; + DeepLayer and_or_not_with(const DeepEdges *other, EdgeBoolOp op) const; DeepLayer edge_region_op (const DeepRegion *other, bool outside, bool include_borders) const; EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; virtual EdgesDelegate *pull_generic (const Edges &edges) const; diff --git a/src/db/db/dbEdgeBoolean.h b/src/db/db/dbEdgeBoolean.h index 126960546..a85fb6e40 100644 --- a/src/db/db/dbEdgeBoolean.h +++ b/src/db/db/dbEdgeBoolean.h @@ -24,14 +24,20 @@ #define HDR_dbEdgeBoolean #include "dbEdge.h" -#include "dbEdgesDelegate.h" +#include "dbHash.h" #include "dbBoxScanner.h" +#include "dbShapes.h" #include "tlIntervalMap.h" namespace db { +/** + * @brief A common definition for the boolean operations available on edges + */ +enum EdgeBoolOp { EdgeOr, EdgeNot, EdgeXor, EdgeAnd, EdgeIntersections }; + struct OrJoinOp { void operator() (int &v, int n) @@ -216,7 +222,15 @@ struct EdgeBooleanClusterCollector : public db::cluster_collector > { EdgeBooleanClusterCollector (OutputContainer *output, EdgeBoolOp op) - : db::cluster_collector > (EdgeBooleanCluster (output, op), op != EdgeAnd /*report single*/) + : db::cluster_collector > (EdgeBooleanCluster (output, 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) { // .. nothing yet .. } @@ -227,12 +241,180 @@ struct EdgeBooleanClusterCollector // 1.) not degenerate // 2.) parallel with some tolerance of roughly 1 dbu // 3.) connected + // In intersection-detection mode, identify intersection points otherwise + // and insert into the intersections container as degenerated edges. + if (! o1->is_degenerate () && ! o2->is_degenerate () && fabs ((double) db::vprod (*o1, *o2)) < db::coord_traits::prec_distance () * std::min (o1->double_length (), o2->double_length ()) && (o1->p1 () == o2->p1 () || o1->p1 () == o2->p2 () || o1->p2 () == o2->p1 () || o1->p2 () == o2->p2 () || o1->coincident (*o2))) { + db::cluster_collector >::add (o1, p1, o2, p2); + + } else if (mp_intersections && p1 != p2) { + + std::pair ip = o1->intersect_point (*o2); + if (ip.first) { + m_intersections.insert (ip.second); + } + } } + + /** + * @brief A receiver for the reducer which removes points that are on the edges + */ + struct RemovePointsOnEdges + : public db::box_scanner_receiver2 + { + public: + RemovePointsOnEdges (std::set &points_to_remove) + : mp_points_to_remove (&points_to_remove) + { } + + void add (const db::Edge *e, const size_t &, const db::Point *pt, const size_t &) + { + if (e->contains (*pt)) { + mp_points_to_remove->insert (*pt); + } + } + + private: + std::set *mp_points_to_remove; + }; + + /** + * @brief An inserter to produce degenerated edges from points + */ + struct PointInserter + : public std::iterator + { + typedef db::Point value_type; + + PointInserter (OutputContainer *output) + : mp_output (output) + { } + + PointInserter &operator= (const db::Point &pt) + { + mp_output->insert (db::Edge (pt, pt)); + return *this; + } + + PointInserter &operator* () { return *this; } + PointInserter &operator++ () { return *this; } + PointInserter &operator++ (int) { return *this; } + + private: + OutputContainer *mp_output; + }; + + /** + * @brief Finalizes the implementation for "EdgeIntersections" + * 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 ()) { + 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); + } + for (std::set::const_iterator p = m_intersections.begin (); p != m_intersections.end (); ++p) { + intersections_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 ()); + + 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; +}; + +/** + * @brief A helper class to use db::Shapes as container for EdgeBooleanClusterCollector + */ +struct DB_PUBLIC ShapesToOutputContainerAdaptor +{ +public: + struct Iterator + : public db::Shapes::shape_iterator + { + Iterator (const db::Shapes::shape_iterator &iter) + : db::Shapes::shape_iterator (iter) + { } + + Iterator () + : db::Shapes::shape_iterator () + { } + + const db::Edge *operator-> () const + { + return (db::Shapes::shape_iterator::operator* ()).basic_ptr (db::Edge::tag ()); + } + + bool operator!= (const Iterator &other) const + { + // only for testing whether at end: + return at_end () != other.at_end (); + } + + bool operator== (const Iterator &other) const + { + // only for testing whether at end: + return at_end () == other.at_end (); + } + }; + + typedef Iterator const_iterator; + + ShapesToOutputContainerAdaptor (db::Shapes &shapes) + : mp_shapes (&shapes) + { + // .. nothing yet .. + } + + const_iterator begin () + { + return Iterator (mp_shapes->begin (db::ShapeIterator::Edges)); + } + + const_iterator end () + { + return Iterator (); + } + + void insert (const db::Edge &edge) + { + mp_shapes->insert (edge); + } + +private: + db::Shapes *mp_shapes; +}; + +/** + * @brief A specialization of the EdgeBooleanClusterCollector for a Shapes output container + */ +struct DB_PUBLIC EdgeBooleanClusterCollectorToShapes + : EdgeBooleanClusterCollector +{ + EdgeBooleanClusterCollectorToShapes (db::Shapes *output, EdgeBoolOp op) + : EdgeBooleanClusterCollector (&m_adaptor, op), m_adaptor (*output) + { + } + +private: + ShapesToOutputContainerAdaptor m_adaptor; }; } diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index a2c236d54..b4dfa2dc2 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -950,6 +950,16 @@ public: return *this; } + /** + * @brief Intersections with other edges + * Intersections are similar to "AND", but will also report + * non-parallel intersections between crossing edges. + */ + Edges intersections (const Edges &other) const + { + return Edges (mp_delegate->intersections (other)); + } + /** * @brief returns the extended edges * diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index 42564daca..6247faebe 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -155,11 +155,6 @@ class DB_PUBLIC EdgeToEdgePairProcessorBase // .. nothing yet .. }; -/** - * @brief A common definition for the boolean operations available on edges - */ -enum EdgeBoolOp { EdgeOr, EdgeNot, EdgeXor, EdgeAnd }; - class RecursiveShapeIterator; class EdgeFilterBase; class EdgePairsDelegate; @@ -267,6 +262,7 @@ public: virtual EdgesDelegate *or_with (const Edges &other) const = 0; virtual EdgesDelegate *add_in_place (const Edges &other) = 0; virtual EdgesDelegate *add (const Edges &other) const = 0; + virtual EdgesDelegate *intersections (const Edges &other) const = 0; virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const = 0; diff --git a/src/db/db/dbEmptyEdges.h b/src/db/db/dbEmptyEdges.h index 243bd56a3..368aabaff 100644 --- a/src/db/db/dbEmptyEdges.h +++ b/src/db/db/dbEmptyEdges.h @@ -81,6 +81,7 @@ public: virtual EdgesDelegate *or_with (const Edges &other) const; virtual EdgesDelegate *add_in_place (const Edges &other); virtual EdgesDelegate *add (const Edges &other) const; + virtual EdgesDelegate *intersections (const Edges &) const { return new EmptyEdges (); } virtual RegionDelegate *extended (coord_type, coord_type, coord_type, coord_type, bool) const; diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index 75c977bee..75d80f539 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -112,7 +112,7 @@ FlatEdges::ensure_merged_edges_valid () const m_merged_edges.clear (); db::Shapes tmp (false); - EdgeBooleanClusterCollector cluster_collector (&tmp, EdgeOr); + EdgeBooleanClusterCollectorToShapes cluster_collector (&tmp, EdgeOr); db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (m_edges.size ()); diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 3c85dc9be..1728f32d6 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1228,11 +1228,6 @@ public: add_pair (*c1, *i2, p, t); } - bool stop () const - { - return false; - } - /** * @brief Finally join the clusters in the join set * @@ -1275,6 +1270,11 @@ public: } } + // needs explicit implementation because we have two base classes: + bool stop () const { return false; } + void initialize () { } + void finalize (bool) { } + private: struct InteractionKeyForClustersType : public InstanceToInstanceInteraction diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index 0789aa2d5..e4eacdc9b 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -177,8 +177,8 @@ std::string SelfOverlapMergeLocalOperation::description () const // --------------------------------------------------------------------------------------------- // EdgeBoolAndOrNotLocalOperation implementation -EdgeBoolAndOrNotLocalOperation::EdgeBoolAndOrNotLocalOperation (bool is_and) - : m_is_and (is_and) +EdgeBoolAndOrNotLocalOperation::EdgeBoolAndOrNotLocalOperation (EdgeBoolOp op) + : m_op (op) { // .. nothing yet .. } @@ -186,19 +186,27 @@ EdgeBoolAndOrNotLocalOperation::EdgeBoolAndOrNotLocalOperation (bool is_and) local_operation::on_empty_intruder_mode EdgeBoolAndOrNotLocalOperation::on_empty_intruder_hint () const { - return m_is_and ? Drop : Copy; + return (m_op == EdgeAnd || m_op == EdgeIntersections) ? Drop : Copy; } std::string EdgeBoolAndOrNotLocalOperation::description () const { - return m_is_and ? tl::to_string (tr ("Edge AND operation")) : tl::to_string (tr ("Edge NOT operation")); + if (m_op == EdgeIntersections) { + return tl::to_string (tr ("Edge INTERSECTION operation")); + } else if (m_op == EdgeAnd) { + return tl::to_string (tr ("Edge AND operation")); + } else if (m_op == EdgeNot) { + return tl::to_string (tr ("Edge NOT operation")); + } else { + return std::string (); + } } void EdgeBoolAndOrNotLocalOperation::compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::unordered_set &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const { - EdgeBooleanClusterCollector > cluster_collector (&result, m_is_and ? EdgeAnd : EdgeNot); + EdgeBooleanClusterCollector > cluster_collector (&result, m_op); db::box_scanner scanner; @@ -210,17 +218,18 @@ EdgeBoolAndOrNotLocalOperation::compute_local (db::Layout * /*layout*/, const sh } bool any_subject = false; + bool is_and = (m_op == EdgeAnd || m_op == EdgeIntersections); for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { const db::Edge &subject = interactions.subject_shape (i->first); if (others.find (subject) != others.end ()) { - if (m_is_and) { + if (is_and) { result.insert (subject); } } else if (i->second.empty ()) { // shortcut (not: keep, and: drop) - if (! m_is_and) { + if (! is_and) { result.insert (subject); } } else { diff --git a/src/db/db/dbLocalOperation.h b/src/db/db/dbLocalOperation.h index 499704b29..fa388793a 100644 --- a/src/db/db/dbLocalOperation.h +++ b/src/db/db/dbLocalOperation.h @@ -28,6 +28,7 @@ #include "dbCommon.h" #include "dbLayout.h" +#include "dbEdgeBoolean.h" #include #include @@ -151,7 +152,7 @@ class DB_PUBLIC EdgeBoolAndOrNotLocalOperation : public local_operation { public: - EdgeBoolAndOrNotLocalOperation (bool is_and); + EdgeBoolAndOrNotLocalOperation (EdgeBoolOp op); virtual void compute_local (db::Layout *layout, const shape_interactions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const; virtual on_empty_intruder_mode on_empty_intruder_hint () const; @@ -161,7 +162,7 @@ public: virtual db::Coord dist () const { return 1; } private: - bool m_is_and; + EdgeBoolOp m_op; }; /** diff --git a/src/db/db/dbOriginalLayerEdges.cc b/src/db/db/dbOriginalLayerEdges.cc index ee47f5771..631652f8b 100644 --- a/src/db/db/dbOriginalLayerEdges.cc +++ b/src/db/db/dbOriginalLayerEdges.cc @@ -253,7 +253,7 @@ OriginalLayerEdges::ensure_merged_edges_valid () const m_merged_edges.clear (); db::Shapes tmp (false); - EdgeBooleanClusterCollector cluster_collector (&tmp, EdgeOr); + EdgeBooleanClusterCollectorToShapes cluster_collector (&tmp, EdgeOr); db::box_scanner scanner (report_progress (), progress_desc ()); scanner.reserve (size ()); diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 2f6d5c35c..d48b1f064 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -943,6 +943,13 @@ Class dec_Edges ("db", "Edges", "\n" "This method has been introduced in version 0.26.1\n" ) + + method ("intersections", &db::Edges::intersections, gsi::arg ("other"), + "@brief Computes the intersections between this edges and other edges\n" + "This computation is like an AND operation, but also including crossing points between non-coincident edges as " + "degenerated (point-like) edges.\n" + "\n" + "This method has been introduced in version 0.26.2\n" + ) + method ("inside_part", &db::Edges::inside_part, gsi::arg ("other"), "@brief Returns the parts of the edges of this edge collection which are inside the polygons of the region\n" "\n" diff --git a/src/db/unit_tests/dbBoxScanner.cc b/src/db/unit_tests/dbBoxScanner.cc index 8d396e3b7..48369cb64 100644 --- a/src/db/unit_tests/dbBoxScanner.cc +++ b/src/db/unit_tests/dbBoxScanner.cc @@ -36,6 +36,8 @@ struct BoxScannerTestRecorder } bool stop () const { return false; } + void initialize () { str += "[i]"; } + void finalize (bool) { str += "[f]"; } void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) { @@ -54,6 +56,8 @@ struct BoxScannerTestRecorderStopping } bool stop () const { return do_stop; } + void initialize () { str += "[i]"; } + void finalize (bool s) { str += s ? "[f+]" : "[f-]"; } void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) { @@ -70,6 +74,8 @@ struct BoxScannerTestRecorder2 void finish (const db::Box *, size_t) { } bool stop () const { return false; } + void initialize () { } + void finalize (bool) { } void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) { @@ -91,6 +97,8 @@ struct BoxScannerTestRecorderTwo } bool stop () const { return false; } + void initialize () { str += "[i]"; } + void finalize (bool) { str += "[f]"; } void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) { @@ -113,6 +121,8 @@ struct BoxScannerTestRecorderTwoStopping } bool stop () const { return do_stop; } + void initialize () { str += "[i]"; } + void finalize (bool s) { str += s ? "[f+]" : "[f-]"; } void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) { @@ -130,6 +140,8 @@ struct BoxScannerTestRecorder2Two void finish2 (const db::SimplePolygon *, int) { } bool stop () const { return false; } + void initialize () { } + void finalize (bool) { } void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) { @@ -159,11 +171,11 @@ TEST(1) db::box_convert bc; bs.set_scanner_threshold (0); EXPECT_EQ (bs.process (tr, 1, bc), true); - EXPECT_EQ (tr.str, "(4-2)(5-2)(5-4)(3-2)(3-4)(5-3)<2><5><4><3>(1-0)<0><1>"); + EXPECT_EQ (tr.str, "[i](4-2)(5-2)(5-4)(3-2)(3-4)(5-3)<2><5><4><3>(1-0)<0><1>[f]"); BoxScannerTestRecorderStopping trstop; EXPECT_EQ (bs.process (trstop, 1, bc), false); - EXPECT_EQ (trstop.str, "(4-2)"); + EXPECT_EQ (trstop.str, "[i](4-2)[f-]"); } TEST(1a) @@ -182,7 +194,7 @@ TEST(1a) db::box_convert bc; bs.set_scanner_threshold (0); bs.process (tr, 1, bc); - EXPECT_EQ (tr.str, "(1-0)<0><1>"); + EXPECT_EQ (tr.str, "[i](1-0)<0><1>[f]"); } TEST(1b) @@ -204,7 +216,7 @@ TEST(1b) db::box_convert bc; bs.set_scanner_threshold (0); bs.process (tr, 1, bc); - EXPECT_EQ (tr.str, "(3-0)(1-3)(4-1)(2-4)<0><3><1><4><2>"); + EXPECT_EQ (tr.str, "[i](3-0)(1-3)(4-1)(2-4)<0><3><1><4><2>[f]"); } TEST(1c) @@ -226,7 +238,7 @@ TEST(1c) db::box_convert bc; bs.set_scanner_threshold (0); bs.process (tr, 1, bc); - EXPECT_EQ (tr.str, "(3-0)(1-3)<0>(4-1)<3>(2-4)<1><4><2>"); + EXPECT_EQ (tr.str, "[i](3-0)(1-3)<0>(4-1)<3>(2-4)<1><4><2>[f]"); } TEST(1d) @@ -248,7 +260,7 @@ TEST(1d) db::box_convert bc; bs.set_scanner_threshold (0); bs.process (tr, 0, bc); - EXPECT_EQ (tr.str, "(3-0)<0><3><1><4><2>"); + EXPECT_EQ (tr.str, "[i](3-0)<0><3><1><4><2>[f]"); } TEST(1e) @@ -269,7 +281,7 @@ TEST(1e) bs.set_fill_factor (0.0); db::box_convert bc; bs.process (tr, 0, bc); - EXPECT_EQ (tr.str, "(0-3)<0><1><2><3><4>"); + EXPECT_EQ (tr.str, "[i](0-3)<0><1><2><3><4>[f]"); } TEST(1f) @@ -280,7 +292,7 @@ TEST(1f) bs.set_fill_factor (0.0); db::box_convert bc; bs.process (tr, 0, bc); - EXPECT_EQ (tr.str, ""); + EXPECT_EQ (tr.str, "[i][f]"); } TEST(1g) @@ -302,7 +314,7 @@ TEST(1g) bs.set_fill_factor (0.0); db::box_convert bc; bs.process (tr, 0, bc); - EXPECT_EQ (tr.str, "<2><4>(0-3)<0><1><3>"); + EXPECT_EQ (tr.str, "[i]<2><4>(0-3)<0><1><3>[f]"); } void run_test2 (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true) @@ -927,7 +939,7 @@ TEST(two_1) db::box_convert bc2; bs.set_scanner_threshold (0); bs.process (tr, 1, bc1, bc2); - EXPECT_EQ (tr.str, "(2-12)(2-14)(4-12)(4-14)(2-15)(4-15)(5-12)(5-14)(5-15)(2-13)(4-13)(3-12)(3-14)(3-13)(3-15)(5-13)(0-10)<2><5><4><3><12><15><14><13>(0-11)(1-10)(1-11)<0><1><10><11>"); + EXPECT_EQ (tr.str, "[i](2-12)(2-14)(4-12)(4-14)(2-15)(4-15)(5-12)(5-14)(5-15)(2-13)(4-13)(3-12)(3-14)(3-13)(3-15)(5-13)(0-10)<2><5><4><3><12><15><14><13>(0-11)(1-10)(1-11)<0><1><10><11>[f]"); } TEST(two_1a) @@ -963,7 +975,7 @@ TEST(two_1a) db::box_convert bc2; bs.set_scanner_threshold (0); bs.process (tr, 1, bc1, bc2); - EXPECT_EQ (tr.str, "(2-11)(2-12)(1-11)(1-12)<1><2><11><12>(0-10)<0><10>"); + EXPECT_EQ (tr.str, "[i](2-11)(2-12)(1-11)(1-12)<1><2><11><12>(0-10)<0><10>[f]"); } TEST(two_1b) @@ -999,12 +1011,12 @@ TEST(two_1b) db::box_convert bc2; bs.set_scanner_threshold (0); EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true); - EXPECT_EQ (tr.str, "(1-12)(2-12)(1-11)(2-11)<1><2><11><12>(0-10)<0><10>"); + EXPECT_EQ (tr.str, "[i](1-12)(2-12)(1-11)(2-11)<1><2><11><12>(0-10)<0><10>[f]"); BoxScannerTestRecorderTwoStopping trstop; EXPECT_EQ (bs.process (trstop, 1, bc1, bc2), false); - EXPECT_EQ (trstop.str, "(1-12)"); + EXPECT_EQ (trstop.str, "[i](1-12)[f-]"); } TEST(two_1c) @@ -1035,7 +1047,7 @@ TEST(two_1c) db::box_convert bc2; bs.set_scanner_threshold (0); EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true); - EXPECT_EQ (tr.str, "<0><10>(1-12)(2-12)(1-11)(2-11)<1><2><12><11>"); + EXPECT_EQ (tr.str, "[i]<0><10>(1-12)(2-12)(1-11)(2-11)<1><2><12><11>[f]"); } void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true) diff --git a/src/db/unit_tests/dbDeepEdgesTests.cc b/src/db/unit_tests/dbDeepEdgesTests.cc index 2fa7f90d2..33ff2c563 100644 --- a/src/db/unit_tests/dbDeepEdgesTests.cc +++ b/src/db/unit_tests/dbDeepEdgesTests.cc @@ -131,6 +131,7 @@ TEST(3_Edge2EdgeBooleans) db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); db::Region r2and3 = r2 & r3; + db::Edges e2 = r2.edges (); db::Edges e3 = r3.edges (); db::Edges e2and3 = r2and3.edges (); @@ -144,6 +145,8 @@ TEST(3_Edge2EdgeBooleans) 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)); CHECKPOINT(); db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au3.gds"); diff --git a/src/db/unit_tests/dbEdges.cc b/src/db/unit_tests/dbEdges.cc index 86d00817e..eff26ff66 100644 --- a/src/db/unit_tests/dbEdges.cc +++ b/src/db/unit_tests/dbEdges.cc @@ -852,6 +852,37 @@ TEST(22) ee.insert (db::Edge (4000,-2000,-2000,-2000)); EXPECT_EQ ((e & ee).to_string (), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"); + EXPECT_EQ (e.intersections (ee).to_string (), "(400,0;-2000,0);(500,-174;400,0);(1000,0;900,-173);(4000,0;1000,0)"); + + // Edge/edge intersections + ee.clear (); + e.clear (); + e.insert (db::Edge (0, -100, 0, 150)); + ee.insert (db::Edge (-50, 50, 50, 50)); + ee.insert (db::Edge (-50, 100, 50, 100)); + EXPECT_EQ ((e & ee).to_string (), ""); // AND does not report intersection points + EXPECT_EQ (e.intersections (ee).to_string (), "(0,50;0,50);(0,100;0,100)"); + + // Edge is intersected by pair with connection point on this line + ee.clear (); + e.clear (); + e.insert (db::Edge (0, -100, 0, 150)); + ee.insert (db::Edge (-50, 50, 0, 50)); + ee.insert (db::Edge (0, 60, 50, 60)); + ee.insert (db::Edge (-50, 100, 0, 100)); + ee.insert (db::Edge (0, 100, 50, 100)); + EXPECT_EQ ((e & ee).to_string (), ""); // AND does not report intersection points + EXPECT_EQ (e.intersections (ee).to_string (), "(0,50;0,50);(0,60;0,60);(0,100;0,100)"); + + // Coincident edges are crossed by another one + ee.clear (); + e.clear (); + e.insert (db::Edge (0, -100, 0, 250)); + ee.insert (db::Edge (0, 0, 0, 150)); + ee.insert (db::Edge (-50, 100, 50, 100)); + ee.insert (db::Edge (-50, 200, 50, 200)); + EXPECT_EQ ((e & ee).to_string (), "(0,0;0,150)"); + EXPECT_EQ (e.intersections (ee).to_string (), "(0,0;0,150);(0,200;0,200)"); } TEST(23) diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 8d4507afd..7b42e7a02 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -1601,6 +1601,16 @@ CODE # This method is available for polygon and edge layers. Edges can be selected # with respect to other edges or polygons. + # %DRC% + # @name intersections + # @brief Returns the intersection points of intersecting edge segments for two edge collections + # @synopsis layer.intersections(edges) + # This operation is similar to the "&" operator, but it does also report intersection points + # between non-colinear, but intersection edges. Such points are reported as point-like, + # degenerated edge objects. + # + # This method is available for edge layers. The argument must be an edge layer. + # %DRC% # @name inside_part # @brief Returns the parts of the edges inside the given region @@ -1731,7 +1741,6 @@ CODE end %w(inside_part outside_part).each do |f| - # In tiled mode, there are no modifying versions. Emulate using the non-modifying one. eval <<"CODE" def #{f}(other) other.requires_region("#{f}") @@ -1746,6 +1755,21 @@ CODE CODE end + %w(intersections).each do |f| + eval <<"CODE" + def #{f}(other) + other.requires_edges("#{f}") + requires_edges("#{f}") + if @engine.is_tiled? + @data = @engine._tcmd(@data, 0, @data.class, :#{f}, other.data) + DRCLayer::new(@engine, @data) + else + DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :#{f}, other.data)) + end + end +CODE + end + # %DRC% # @name rectangles # @brief Selects all rectangle polygons from the input diff --git a/testdata/algo/deep_edges_au3.gds b/testdata/algo/deep_edges_au3.gds index ae59b5807..62914f828 100644 Binary files a/testdata/algo/deep_edges_au3.gds and b/testdata/algo/deep_edges_au3.gds differ diff --git a/testdata/drc/drcSuiteTests.drc b/testdata/drc/drcSuiteTests.drc index b3164b5cd..be4c43ed6 100644 --- a/testdata/drc/drcSuiteTests.drc +++ b/testdata/drc/drcSuiteTests.drc @@ -589,6 +589,22 @@ def run_testsuite(dm, ic, tiled = false, hier = false) p = c.edges.pull_interacting(b.edges) p.output(lb + 5, dm) + lb += 10 #440 + message "--- edge booleans, intersections #{lb}" + + p = c.edges - b.edges + p.output(lb, dm) + p = b.edges - c.edges + p.output(lb + 1, dm) + p = c.edges & b.edges + p.output(lb + 2, dm) + p = c.edges ^ b.edges + p.output(lb + 3, dm) + p = c.edges.intersections(b.edges) + p.output(lb + 4, dm) + p = b.edges.intersections(c.edges) + p.output(lb + 5, dm) + end if $drc_test_mode == 1 diff --git a/testdata/drc/drcSuiteTests_au1.oas b/testdata/drc/drcSuiteTests_au1.oas index 0774c72d2..636910e6b 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 77a154493..d9a80d47f 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 6a46f4798..8717c39e9 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 3bf1fc990..6a45d5648 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 939fa3b93..c596d626f 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 2fa6f7253..bf3a28492 100644 Binary files a/testdata/drc/drcSuiteTests_au6.oas and b/testdata/drc/drcSuiteTests_au6.oas differ