diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc index 9134dc12f..89c90d84c 100644 --- a/src/db/db/dbRegionProcessors.cc +++ b/src/db/db/dbRegionProcessors.cc @@ -52,7 +52,7 @@ void CornerDetectorCore::detect_corners (const db::Polygon &poly, const CornerPo db::Point pn = ctr [j]; if (m_checker (pt - pp, pn - pt)) { - delivery.make_point (pt); + delivery.make_point (pt, db::Edge (pp, pt), db::Edge (pt, pn)); } pp = pt; diff --git a/src/db/db/dbRegionProcessors.h b/src/db/db/dbRegionProcessors.h index d91613148..516529144 100644 --- a/src/db/db/dbRegionProcessors.h +++ b/src/db/db/dbRegionProcessors.h @@ -41,7 +41,7 @@ namespace db class DB_PUBLIC CornerPointDelivery { public: - virtual void make_point (const db::Point &pt) const = 0; + virtual void make_point (const db::Point &pt, const db::Edge &e1, const db::Edge &e2) const = 0; }; /** @@ -55,7 +55,7 @@ public: : m_d (dim, dim), mp_result (&result) { } - virtual void make_point (const db::Point &pt) const + virtual void make_point (const db::Point &pt, const db::Edge &, const db::Edge &) const { mp_result->push_back (db::Polygon (db::Box (pt - m_d, pt + m_d))); } @@ -76,7 +76,7 @@ public: : mp_result (&result) { } - virtual void make_point (const db::Point &pt) const + virtual void make_point (const db::Point &pt, const db::Edge &, const db::Edge &) const { mp_result->push_back (db::Edge (pt, pt)); } @@ -86,6 +86,26 @@ private: std::vector *mp_result; }; +/** + * @brief An interface to accept corners and turns them into edge pairs + */ +class DB_PUBLIC CornerEdgePairDelivery + : public CornerPointDelivery +{ +public: + CornerEdgePairDelivery (std::vector &result) + : mp_result (&result) + { } + + virtual void make_point (const db::Point &, const db::Edge &e1, const db::Edge &e2) const + { + mp_result->push_back (db::EdgePair (e1, e2)); + } + +private: + std::vector *mp_result; +}; + /** * @brief A helper class implementing the core corner detection algorithm */ @@ -155,6 +175,31 @@ public: virtual bool wants_variants () const { return false; } }; +/** + * @brief A corner detector delivering edge pairs for the corners + */ +class DB_PUBLIC CornersAsEdgePairs + : public db::PolygonToEdgePairProcessorBase, private CornerDetectorCore +{ +public: + CornersAsEdgePairs (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end) + : CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end) + { + // .. nothing yet .. + } + + void process (const db::Polygon &poly, std::vector &result) const + { + detect_corners (poly, CornerEdgePairDelivery (result)); + } + + virtual const TransformationReducer *vars () const { return 0; } + virtual bool result_is_merged () const { return false; } + virtual bool result_must_not_be_merged () const { return true; } // to preserve dots + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return false; } +}; + // ----------------------------------------------------------------------------------- // Extents diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index e9699eed7..bea6a2de9 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -226,6 +226,12 @@ static db::CompoundRegionOperationNode *new_corners_as_dots (db::CompoundRegionO return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/); } +static db::CompoundRegionOperationNode *new_corners_as_edge_pairs (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end) +{ + check_non_null (input, "input"); + return new db::CompoundRegionToEdgePairProcessingOperationNode (new db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/); +} + static db::CompoundRegionOperationNode *new_extents (db::CompoundRegionOperationNode *input, db::Coord e) { check_non_null (input, "input"); @@ -603,6 +609,12 @@ Class decl_CompoundRegionOperationNode ("db", " gsi::constructor ("new_corners_as_dots", &new_corners_as_dots, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), "@brief Creates a node turning corners into dots (single-point edges).\n" ) + + gsi::constructor ("new_corners_as_edge_pairs", &new_corners_as_edge_pairs, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), + "@brief Creates a node turning corners into edge pairs containing the two edges adjacent to the corner.\n" + "The first edge will be the incoming edge and the second one the outgoing edge.\n" + "\n" + "This feature has been introduced in version 0.27.1.\n" + ) + gsi::constructor ("new_extents", &new_extents, gsi::arg ("input"), gsi::arg ("e", 0), "@brief Creates a node returning the extents of the objects.\n" "The 'e' parameter provides a generic enlargement which is applied to the boxes. This is helpful to cover dot-like edges or edge pairs in the input." diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 86a6dd4e5..87b7f6e31 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -123,6 +123,11 @@ static db::Region corners_to_boxes (const db::Region *r, double angle_start, dou return r->processed (db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, dim)); } +static db::EdgePairs corners_to_edge_pairs (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end) +{ + return r->processed (db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end)); +} + static db::Region *new_si (const db::RecursiveShapeIterator &si) { return new db::Region (si); @@ -1373,14 +1378,22 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "A similar function that reports corners as point-like edges is \\corners_dots.\n" "\n" - "This function has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n" + "This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n" ) + method_ext ("corners_dots", &corners_to_dots, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), "@brief This method will select all corners whose attached edges satisfy the angle condition.\n" "\n" "This method is similar to \\corners, but delivers an \\Edges collection with dot-like edges for each corner.\n" "\n" - "This function has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n" + "This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n" + ) + + method_ext ("corners_edge_pairs", &corners_to_edge_pairs, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), + "@brief This method will select all corners whose attached edges satisfy the angle condition.\n" + "\n" + "This method is similar to \\corners, but delivers an \\EdgePairs collection with an edge pairs for each corner.\n" + "The first edge is the incoming edge of the corner, the second one the outgoing edge.\n" + "\n" + "This method has been introduced in version 0.27.1.\n" ) + method ("merge", (db::Region &(db::Region::*) ()) &db::Region::merge, "@brief Merge the region\n" 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 6e24ce748..e0d2ea017 100644 --- a/src/drc/drc/built-in-macros/_drc_complex_ops.rb +++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb @@ -787,14 +787,14 @@ CODE # The "corners" method is available as a plain function or as a method on \DRC# expressions. # The plain function is equivalent to "primary.corners". - def corners(as_dots = DRCAsDots::new(false)) + def corners(output_mode = DRCOutputMode::new(:dots)) @engine._context("corners") do - if as_dots.is_a?(DRCAsDots) - as_dots = as_dots.value + if output_mode.is_a?(DRCOutputMode) + output_mode = output_mode.value else raise("Invalid argument (#{as_dots.inspect}) for 'corners' method") end - DRCOpNodeCornersFilter::new(@engine, as_dots, self) + DRCOpNodeCornersFilter::new(@engine, output_mode, self) end end @@ -859,8 +859,8 @@ CODE args.each_with_index do |a,ia| if a.is_a?(1.0.class) && :#{f} != :middle f << a - elsif a.is_a?(DRCAsDots) - as_edges = a.value + elsif a.is_a?(DRCOutputMode) + as_edges = (a.value == :edges || a.value == :dots) elsif @@std_refs[a] && :#{f} != :middle f = @@std_refs[a] else @@ -1996,11 +1996,11 @@ end class DRCOpNodeCornersFilter < DRCOpNodeWithCompare attr_accessor :input - attr_accessor :as_dots + attr_accessor :output_mode - def initialize(engine, as_dots, input) + def initialize(engine, output_mode, input) super(engine) - self.as_dots = as_dots + self.output_mode = output_mode self.input = input self.description = "corners" end @@ -2011,8 +2011,10 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare args << (self.gt ? false : true) args << (self.lt ? self.lt : (self.le ? self.le : 180.0)) args << (self.lt ? false : true) - if self.as_dots + if self.output_mode == :dots || self.output_mode == :edges RBA::CompoundRegionOperationNode::new_corners_as_dots(*args) + elsif self.output_mode == :edge_pairs + RBA::CompoundRegionOperationNode::new_corners_as_edge_pairs(*args) else args << 2 # dimension is 2x2 DBU RBA::CompoundRegionOperationNode::new_corners_as_rectangles(*args) diff --git a/src/drc/drc/built-in-macros/_drc_cop_integration.rb b/src/drc/drc/built-in-macros/_drc_cop_integration.rb index a53504a73..58b5518a4 100644 --- a/src/drc/drc/built-in-macros/_drc_cop_integration.rb +++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb @@ -781,7 +781,7 @@ CODE # %DRC% # @name corners - # @brief Selects all polygons which are rectilinear + # @brief Selects corners of polygons # @synopsis corners([ options ]) (in condition) # @synopsis corners(layer [, options ]) # @@ -791,15 +791,16 @@ CODE # \DRC# expressions (see \Layer#drc and \DRC#corners for more details). # # Like the layer-based version, the "corners" operator accepts the - # output type option: "as_dots" for dot-like edges and "as_boxes" for - # small (2x2 DBU) box markers. + # output type option: "as_dots" for dot-like edges, "as_boxes" for + # small (2x2 DBU) box markers and "as_edge_pairs" for edge pairs. + # The default output type is "as_boxes". # # The "corners" operator can be put into a condition which means it's # applied to corners meeting a particular angle constraint. - def _cop_corners(as_dots = DRCAsDots::new(false)) + def _cop_corners(output_mode = DRCOutputMode::new(:boxes)) # NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here. - return primary.corners(as_dots) + return primary.corners(output_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 bc979d8e7..51f44c266 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -226,15 +226,19 @@ module DRC end def as_dots - DRCAsDots::new(true) + DRCOutputMode::new(:dots) end def as_edges - DRCAsDots::new(true) + DRCOutputMode::new(:edges) end def as_boxes - DRCAsDots::new(false) + DRCOutputMode::new(:boxes) + end + + def as_edge_pairs + DRCOutputMode::new(:edge_pairs) end def area_only(r) diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 4a354267a..041ad60c5 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -1118,8 +1118,8 @@ CODE elsif a.is_a?(DRCPattern) as_pattern = a.as_pattern pattern = a.pattern - elsif a.is_a?(DRCAsDots) - as_dots = a.value + elsif a.is_a?(DRCOutputMode) + as_dots = (a.value == :edges || a.value == :dots) else raise("Invalid argument type #{a.inspect}") end @@ -1167,6 +1167,8 @@ CODE # @ul # @li @b as_boxes @/b: with this option, small boxes will be produced as markers @/li # @li @b as_dots @/b: with this option, point-like edges will be produced instead of small boxes @/li + # @li @b as_edge_pairs @/b: with this option, an edge pair is produced for each corner selected. The first edge + # is the incoming edge to the corner, the second edge the outgoing edge. @/li # @/ul # # The following images show the effect of this method: @@ -1185,7 +1187,7 @@ CODE requires_region - as_dots = false + output_mode = :boxes amin = -180.0 amax = 180.0 @@ -1199,14 +1201,23 @@ CODE elsif a.is_a?(1.0.class) || a.is_a?(1.class) amin = a.to_f amax = a.to_f - elsif a.is_a?(DRCAsDots) - as_dots = a.value + elsif a.is_a?(DRCOutputMode) + output_mode = a.value else raise("Invalid argument #{a.inspect}") end end - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, as_dots ? :corners_dots : :corners, amin, amax)) + f = :corners + cls = RBA::Region + if output_mode == :edges || output_mode == :dots + f = :corners_dots + cls = RBA::Edges + elsif output_mode == :edge_pairs + f = :corners_edge_pairs + cls = RBA::EdgePairs + end + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, cls, f, amin, amax)) end @@ -1375,8 +1386,8 @@ CODE args.each do |a| if a.is_a?(1.0.class) && :#{f} != :middle f << a - elsif a.is_a?(DRCAsDots) - as_edges = a.value + elsif a.is_a?(DRCOutputMode) + as_edges = (a.value == :edges || a.value == :dots) elsif @@std_refs[a] && :#{f} != :middle f = @@std_refs[a] else @@ -4736,7 +4747,7 @@ END end def requires_edge_pairs - self.data.is_a?(RBA::EdgePairs) || raise("Requires a edge pair layer") + self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge pair layer") end def requires_edges diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index 611cb414a..dedd797ef 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -77,7 +77,7 @@ module DRC # A wrapper for the "as_dots" or "as_boxes" flag for # some DRC functions. The purpose of this class # is to identify the value by the class. - class DRCAsDots + class DRCOutputMode attr_accessor :value def initialize(v) self.value = v diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml index cf4e00cce..ef4aa3e2c 100644 --- a/src/lay/lay/doc/about/drc_ref_global.xml +++ b/src/lay/lay/doc/about/drc_ref_global.xml @@ -342,7 +342,7 @@ See Netter#connect_global

See Netter#connect_implicit for a description of that function.

-

"corners" - Selects all polygons which are rectilinear

+

"corners" - Selects corners of polygons

Usage:

    @@ -356,8 +356,9 @@ argument, "corners" represents the corner generator/filter in primary shapes for DRC expressions (see Layer#drc and DRC#corners for more details).

    Like the layer-based version, the "corners" operator accepts the -output type option: "as_dots" for dot-like edges and "as_boxes" for -small (2x2 DBU) box markers. +output type option: "as_dots" for dot-like edges, "as_boxes" for +small (2x2 DBU) box markers and "as_edge_pairs" for edge pairs. +The default output type is "as_boxes".

    The "corners" operator can be put into a condition which means it's applied to corners meeting a particular angle constraint. diff --git a/src/lay/lay/doc/about/drc_ref_layer.xml b/src/lay/lay/doc/about/drc_ref_layer.xml index 916647af0..24b008f5d 100644 --- a/src/lay/lay/doc/about/drc_ref_layer.xml +++ b/src/lay/lay/doc/about/drc_ref_layer.xml @@ -263,6 +263,8 @@ The options available are:

    • as_boxes : with this option, small boxes will be produced as markers
    • as_dots : with this option, point-like edges will be produced instead of small boxes
    • +
    • as_edge_pairs : with this option, an edge pair is produced for each corner selected. The first edge +is the incoming edge to the corner, the second edge the outgoing edge.

    The following images show the effect of this method: diff --git a/testdata/drc/drcGenericTests_6.drc b/testdata/drc/drcGenericTests_6.drc index 7e345876f..34eeb7d0c 100644 --- a/testdata/drc/drcGenericTests_6.drc +++ b/testdata/drc/drcGenericTests_6.drc @@ -22,6 +22,7 @@ l1.drc(-90 < corners(as_dots) <= 90.0).output(101, 0) l1.drc(corners(as_boxes) == -90).output(102, 0) # outer corners l1.drc(corners(as_boxes) == 90).output(103, 0) # inner corners l1.drc(corners(as_boxes) <= -90).output(104, 0) +l1.drc(corners(as_edge_pairs) == 90).output(105, 0) l1.drc(middle).output(110, 0) l1.drc(middle(as_dots)).output(111, 0) diff --git a/testdata/drc/drcGenericTests_au6.gds b/testdata/drc/drcGenericTests_au6.gds index 1cdcbf50a..6674e7552 100644 Binary files a/testdata/drc/drcGenericTests_au6.gds and b/testdata/drc/drcGenericTests_au6.gds differ diff --git a/testdata/drc/drcGenericTests_au6d.gds b/testdata/drc/drcGenericTests_au6d.gds index 0eedf1c58..8bf276f1d 100644 Binary files a/testdata/drc/drcGenericTests_au6d.gds and b/testdata/drc/drcGenericTests_au6d.gds differ diff --git a/testdata/drc/drcSimpleTests_2.drc b/testdata/drc/drcSimpleTests_2.drc index d2055c458..89de3aa2e 100644 --- a/testdata/drc/drcSimpleTests_2.drc +++ b/testdata/drc/drcSimpleTests_2.drc @@ -66,6 +66,9 @@ a1.extent_refs(0.25, 0.5, 0.5, 0.75).output(1053, 0) a1.corners.sized(0.05).output(1060, 0) a1.corners(-90.0, as_boxes).sized(0.05).output(1061, 0) a1.corners(-90.0, as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1062, 0) +a1.corners(-90.0, as_edge_pairs).polygons(0).output(1063, 0) +a1.corners(-90.0, as_edge_pairs).first_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1064, 0) +a1.corners(-90.0, as_edge_pairs).second_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1065, 0) a1.select { |p| p.bbox.width < 0.8 }.output(1100, 0) a1.collect { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1101, 0) diff --git a/testdata/drc/drcSimpleTests_au2.gds b/testdata/drc/drcSimpleTests_au2.gds index 76c8b8afe..b916b5cae 100644 Binary files a/testdata/drc/drcSimpleTests_au2.gds and b/testdata/drc/drcSimpleTests_au2.gds differ