diff --git a/src/db/db/dbCellVariants.cc b/src/db/db/dbCellVariants.cc index 5f36cc7b7..97f3c8c0d 100644 --- a/src/db/db/dbCellVariants.cc +++ b/src/db/db/dbCellVariants.cc @@ -45,6 +45,26 @@ db::Trans OrientationReducer::reduce (const db::Trans &trans) const // ------------------------------------------------------------------------------------------ +db::ICplxTrans OrthogonalTransformationReducer::reduce (const db::ICplxTrans &trans) const +{ + if (trans.is_ortho ()) { + return db::ICplxTrans (); + } else { + db::ICplxTrans res; + double a = trans.angle (); + double a90 = floor (a / 90.0 + 0.5) * 90.0; + res.angle (a - a90); + return res; + } +} + +db::Trans OrthogonalTransformationReducer::reduce (const db::Trans &trans) const +{ + return db::Trans (); +} + +// ------------------------------------------------------------------------------------------ + db::ICplxTrans MagnificationReducer::reduce (const db::ICplxTrans &trans) const { return db::ICplxTrans (trans.mag ()); diff --git a/src/db/db/dbCellVariants.h b/src/db/db/dbCellVariants.h index fb945586b..8bbc3f1e9 100644 --- a/src/db/db/dbCellVariants.h +++ b/src/db/db/dbCellVariants.h @@ -69,6 +69,16 @@ struct DB_PUBLIC OrientationReducer db::Trans reduce (const db::Trans &trans) const; }; +/** + * @brief A reducer for invariance against orthogonal transformations (rotations of multiple of 90 degree) + */ +struct DB_PUBLIC OrthogonalTransformationReducer + : public TransformationReducer +{ + db::ICplxTrans reduce (const db::ICplxTrans &trans) const; + db::Trans reduce (const db::Trans &trans) const; +}; + /** * @brief A magnification reducer * diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index df0ca4dfd..cf7bcb709 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -274,6 +274,73 @@ EdgeOrientationFilter::selected (const db::Edge &edge) const } } +// ------------------------------------------------------------------------------------------------------------- +// SpecialEdgeOrientationFilter implementation + +SpecialEdgeOrientationFilter::SpecialEdgeOrientationFilter (FilterType type, bool inverse) + : m_type (type), m_inverse (inverse) +{ + // .. nothing yet .. +} + +static EdgeAngleChecker s_ortho_checkers [] = { + EdgeAngleChecker (0.0, true, 0.0, true), + EdgeAngleChecker (90.0, true, 90.0, true) +}; + +static EdgeAngleChecker s_diagonal_checkers [] = { + EdgeAngleChecker (-45.0, true, -45.0, true), + EdgeAngleChecker (45.0, true, 45.0, true) +}; + +static EdgeAngleChecker s_orthodiagonal_checkers [] = { + EdgeAngleChecker (-45.0, true, -45.0, true), + EdgeAngleChecker (0.0, true, 0.0, true), + EdgeAngleChecker (45.0, true, 45.0, true), + EdgeAngleChecker (90.0, true, 90.0, true) +}; + +bool +SpecialEdgeOrientationFilter::selected (const db::Edge &edge) const +{ + const EdgeAngleChecker *eb, *ee; + + switch (m_type) { + case Ortho: + eb = s_ortho_checkers; + ee = s_ortho_checkers + sizeof (s_ortho_checkers) / sizeof (s_ortho_checkers [0]); + break; + case Diagonal: + eb = s_diagonal_checkers; + ee = s_diagonal_checkers + sizeof (s_diagonal_checkers) / sizeof (s_diagonal_checkers [0]); + break; + case OrthoDiagonal: + default: + eb = s_orthodiagonal_checkers; + ee = s_orthodiagonal_checkers + sizeof (s_orthodiagonal_checkers) / sizeof (s_orthodiagonal_checkers [0]); + break; + } + + db::Vector en, ev; + en = db::Vector (edge.ortho_length (), 0); + + // NOTE: this edge normalization confines the angle to a range between (-90 .. 90] (-90 excluded). + // A horizontal edge has 0 degree, a vertical one has 90 degree. + if (edge.dx () < 0 || (edge.dx () == 0 && edge.dy () < 0)) { + ev = -edge.d (); + } else { + ev = edge.d (); + } + + for (auto ec = eb; ec != ee; ++ec) { + if ((*ec) (en, ev)) { + return ! m_inverse; + } + } + + return m_inverse; +} + // ------------------------------------------------------------------------------------------------------------- // Edge to Edge relation implementation diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h index 055d78eae..4577397a4 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -246,6 +246,76 @@ private: EdgeAngleChecker m_checker; }; +/** + * @brief An edge orientation filter for use with Edges::filter or Edges::filtered + * + * This filter renders edges with special orientations. + */ + +struct DB_PUBLIC SpecialEdgeOrientationFilter + : public EdgeFilterBase +{ + enum FilterType { + Ortho = 0, // 0 and 90 degree + Diagonal = 1, // -45 and 45 degree + OrthoDiagonal = 2 // both Ortho and Diagonal + }; + + /** + * @brief Constructor + * + * @param type The type of filter + */ + SpecialEdgeOrientationFilter (FilterType type, bool inverse); + + /** + * @brief Returns true if the edge orientation matches the criterion + */ + virtual bool selected (const db::Edge &edge) const; + + /** + * @brief Returns true if all edge orientations match the criterion + */ + virtual bool selected (const std::unordered_set &edges) const + { + for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { + if (! selected (*e)) { + return false; + } + } + return true; + } + + /** + * @brief This filter is not isotropic + */ + virtual const TransformationReducer *vars () const + { + return &m_vars; + } + + /** + * @brief Requires merged input + */ + virtual bool requires_raw_input () const + { + return false; + } + + /** + * @brief Wants to build variants + */ + virtual bool wants_variants () const + { + return true; + } + +private: + FilterType m_type; + bool m_inverse; + db::OrthogonalTransformationReducer m_vars; +}; + /** * @brief A predicate defining edge a interacts with b */ diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 89516e8fa..c60b21c43 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -22,6 +22,7 @@ #include "gsiDecl.h" +#include "gsiEnums.h" #include "dbEdgePairs.h" #include "dbEdges.h" @@ -232,6 +233,13 @@ static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double am return r->filtered (ef); } +static db::EdgePairs with_angle3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse) +{ + db::SpecialEdgeOrientationFilter f (type, inverse); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return r->filtered (ef); +} + static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool inverse) { db::EdgeOrientationFilter f (a, inverse); @@ -246,6 +254,13 @@ static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, doub return r->filtered (ef); } +static db::EdgePairs with_angle_both3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse) +{ + db::SpecialEdgeOrientationFilter f (type, inverse); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return r->filtered (ef); +} + static db::EdgePairs with_internal_angle1 (const db::EdgePairs *r, double a, bool inverse) { db::InternalAngleEdgePairFilter f (a, inverse); @@ -677,6 +692,17 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.1.\n" ) + + method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"), + "@brief Filter the edge pairs by orientation of their edges\n" + "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " + "edge pairs with at least one edge having an angle of the given type are returned. If \"inverse\" is true, " + "edge pairs not fulfilling this criterion are returned.\n" + "\n" + "This version allows specifying an edge type instead of an angle. Edge types include multiple distinct orientations " + "and are specified using one of the \\OrthoEdges, \\DiagonalEgdes or \\OrthoDiagonalEdges types.\n" + "\n" + "This method has been added in version 0.28.\n" + ) + method_ext ("with_angle_both", with_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Filter the edge pairs by orientation of both of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " @@ -702,6 +728,17 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.1.\n" ) + + method_ext ("with_angle_both", with_angle_both3, gsi::arg ("type"), gsi::arg ("inverse"), + "@brief Filter the edge pairs by orientation of their edges\n" + "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " + "edge pairs with both edges having an angle of the given type are returned. If \"inverse\" is true, " + "edge pairs not fulfilling this criterion are returned.\n" + "\n" + "This version allows specifying an edge type instead of an angle. Edge types include multiple distinct orientations " + "and are specified using one of the \\OrthoEdges, \\DiagonalEgdes or \\OrthoDiagonalEdges types.\n" + "\n" + "This method has been added in version 0.28.\n" + ) + method_ext ("with_area", with_area1, gsi::arg ("area"), gsi::arg ("inverse"), "@brief Filters the edge pairs by the enclosed area\n" "Filters the edge pairs in the edge pair collection by enclosed area. If \"inverse\" is false, only " @@ -838,4 +875,22 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "This class has been introduced in version 0.23.\n" ); +gsi::EnumIn decl_EdgePairsEdgeFilterType ("db", "EdgeType", + gsi::enum_const ("OrthoEdges", db::SpecialEdgeOrientationFilter::Ortho, + "@brief Horizontal and vertical edges are selected\n" + ) + + gsi::enum_const ("DiagonalEdges", db::SpecialEdgeOrientationFilter::Diagonal, + "@brief Diagonal edges are selected (-45 and 45 degree)\n" + ) + + gsi::enum_const ("OrthoDiagonalEdges", db::SpecialEdgeOrientationFilter::OrthoDiagonal, + "@brief Diagonal or orthogonal edges are selected (0, 90, -45 and 45 degree)\n" + ), + "@brief This enum specifies the the edge type for edge angle filters.\n" + "\n" + "This enum was introduced in version 0.28.\n" +); + +// Inject the db::SpecialEdgeOrientationFilter::FilterType declarations into EdgePairs: +gsi::ClassExt decl_EdgePairsEdgeFilterType_into_parent (decl_EdgePairsEdgeFilterType.defs ()); + } diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 911bf9ac6..f2ded4b5c 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -212,6 +212,12 @@ static db::Edges with_angle2 (const db::Edges *r, double amin, double amax, bool return r->filtered (f); } +static db::Edges with_angle3 (const db::Edges *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse) +{ + db::SpecialEdgeOrientationFilter f (type, inverse); + return r->filtered (f); +} + static db::EdgePairs width2 (const db::Edges *r, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection) { return r->width_check (d, db::EdgesCheckOptions (whole_edges, @@ -609,13 +615,13 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "This method has been introduced in version 0.26." ) + method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"), - "@brief Filter the edges by length\n" + "@brief Filters the edges by length\n" "Filters the edges in the edge collection by length. If \"inverse\" is false, only " "edges which have the given length are returned. If \"inverse\" is true, " "edges not having the given length are returned.\n" ) + method_ext ("with_length", with_length2, gsi::arg ("min_length"), gsi::arg ("max_length"), gsi::arg ("inverse"), - "@brief Filter the edges by length\n" + "@brief Filters the edges by length\n" "Filters the edges in the edge collection by length. If \"inverse\" is false, only " "edges which have a length larger or equal to \"min_length\" and less than \"max_length\" are " "returned. If \"inverse\" is true, " @@ -625,7 +631,7 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "If you don't want to specify a lower or upper limit, pass nil to that parameter.\n" ) + method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), - "@brief Filter the edges by orientation\n" + "@brief Filters the edges by orientation\n" "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " "edges which have the given angle to the x-axis are returned. If \"inverse\" is true, " "edges not having the given angle are returned.\n" @@ -637,7 +643,7 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "@/code\n" ) + method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), - "@brief Filter the edges by orientation\n" + "@brief Filters the edges by orientation\n" "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " "edges which have an angle to the x-axis larger or equal to \"min_angle\" (depending on \"include_min_angle\") and equal or less than \"max_angle\" (depending on \"include_max_angle\") are " "returned. If \"inverse\" is true, " @@ -648,6 +654,17 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "The two \"include..\" arguments have been added in version 0.27." ) + + method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"), + "@brief Filters the edges by orientation type\n" + "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " + "edges which have an angle of the given type are returned. If \"inverse\" is true, " + "edges which do not conform to this criterion are returned.\n" + "\n" + "This version allows specifying an edge type instead of an angle. Edge types include multiple distinct orientations " + "and are specified using one of the \\OrthoEdges, \\DiagonalEgdes or \\OrthoDiagonalEdges types.\n" + "\n" + "This method has been added in version 0.28.\n" + ) + method ("insert", (void (db::Edges::*)(const db::Edge &)) &db::Edges::insert, gsi::arg ("edge"), "@brief Inserts an edge\n" "\n" @@ -1749,5 +1766,23 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "This class has been introduced in version 0.23.\n" ); +gsi::EnumIn decl_EdgesEdgeFilterType ("db", "EdgeType", + gsi::enum_const ("OrthoEdges", db::SpecialEdgeOrientationFilter::Ortho, + "@brief Horizontal and vertical edges are selected\n" + ) + + gsi::enum_const ("DiagonalEdges", db::SpecialEdgeOrientationFilter::Diagonal, + "@brief Diagonal edges are selected (-45 and 45 degree)\n" + ) + + gsi::enum_const ("OrthoDiagonalEdges", db::SpecialEdgeOrientationFilter::OrthoDiagonal, + "@brief Diagonal or orthogonal edges are selected (0, 90, -45 and 45 degree)\n" + ), + "@brief This enum specifies the the edge type for edge angle filters.\n" + "\n" + "This enum was introduced in version 0.28.\n" +); + +// Inject the db::SpecialEdgeOrientationFilter::FilterType declarations into Edges: +gsi::ClassExt decl_EdgesEdgeFilterType_into_parent (decl_EdgesEdgeFilterType.defs ()); + } diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 7dce0a451..fc024aa13 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -77,6 +77,18 @@ module DRC DRCBothEdges::new end + def ortho + DRCOrthoEdges::new + end + + def diagonal_only + DRCDiagonalOnlyEdges::new + end + + def diagonal + DRCDiagonalEdges::new + end + def shift(x, y) self._context("shift") do RBA::DCplxTrans::new(RBA::DVector::new(_make_value(x) * self.dbu, _make_value(y) * self.dbu)) diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index c8e2731a5..8d420316c 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -864,7 +864,10 @@ CODE # @synopsis layer.with_angle(min .. max) # @synopsis layer.with_angle(value) # @synopsis layer.with_angle(min, max) - # @synopsis edge_pair_layer.with_angle(min, max [, both]) + # @synopsis layer.with_angle(ortho) + # @synopsis layer.with_angle(diagonal) + # @synopsis layer.with_angle(diagonal_only) + # @synopsis edge_pair_layer.with_angle(... [, both]) # # When called on an edge layer, the method selects edges by their angle, # measured against the horizontal axis in the mathematical sense. @@ -876,7 +879,7 @@ CODE # The first version of this method selects # edges with a angle larger or equal to min and less than max (but not equal). # The second version selects edges with exactly the given angle. The third - # version is identical to the first one. + # version is identical to the first one. # # When called on an edge pair layer, this method selects edge pairs with one or both edges # meeting the angle criterion. In this case an additional argument is accepted which can be @@ -907,6 +910,17 @@ CODE # @/tr # @/table # + # Specifying "ortho", "diagonal" or "diagonal_only" instead of the angle values will select + # 0 and 90 degree edges (ortho), -45 and 45 degree edges (diagonal_only) and both types (diagonal). + # This simplifies the implementation of selectors for manhattan or half-manhattan features: + # + # @code + # ortho_edges = edges.with_angle(ortho) + # + # # which is equivalent to, but more efficient as: + # ortho_edges = edges.with_angle(0) + edges.with_angle(90) + # @/code + # # Note that in former versions, with_angle could be used on polygon layers selecting corners with specific angles. # This feature has been deprecated. Use \corners instead. @@ -916,11 +930,16 @@ CODE # @synopsis layer.without_angle(min .. max) # @synopsis layer.without_angle(value) # @synopsis layer.without_angle(min, max) - # @synopsis edge_pair_layer.without_angle(min, max [, both]) + # @synopsis layer.without_angle(ortho) + # @synopsis layer.without_angle(diagonal) + # @synopsis layer.without_angle(diagonal_only) + # @synopsis edge_pair_layer.without_angle(... [, both]) # # The method basically is the inverse of \with_angle. It selects all edges # of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle - # is not inside the given interval (first and third form). When called on edge pairs, it selects + # is not inside the given interval (first and third form) or of the given type (other forms). + # + # When called on edge pairs, it selects # edge pairs by the angles of their edges. # # A note on the "both" modifier (without_angle called on edge pairs): "both" means that @@ -998,6 +1017,12 @@ CODE a = args[0] if a.is_a?(Range) DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.begin, a.end, #{inv.inspect})) + elsif a.is_a?(DRCOrthoEdges) || a.is_a?(DRCDiagonalOnlyEdges) || a.is_a?(DRCDiagonalEdges) + if self.data.is_a?(RBA::Edges) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.e_value, #{inv.inspect})) + else + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.ep_value, #{inv.inspect})) + end else DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a, #{inv.inspect})) end diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index 86fd67e8d..522148a97 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -16,10 +16,40 @@ module DRC end end - # A wrapper for the "both edges" flag for EdgePair#with_length or EdgePair#with_angle + # A wrapper for the "both edges" flag for EdgePairs#with_length or EdgePairs#with_angle class DRCBothEdges end + # A wrapper for the "ortho edges" flag for Edges#with_angle or EdgePairs#with_angle + class DRCOrthoEdges + def ep_value + RBA::EdgePairs::OrthoEdges + end + def e_value + RBA::Edges::OrthoEdges + end + end + + # A wrapper for the "diagonal only edges" flag for Edges#with_angle or EdgePairs#with_angle + class DRCDiagonalOnlyEdges + def ep_value + RBA::EdgePairs::DiagonalEdges + end + def e_value + RBA::Edges::DiagonalEdges + end + end + + # A wrapper for the "diagonal edges" flag for Edges#with_angle or EdgePairs#with_angle + class DRCDiagonalEdges + def ep_value + RBA::EdgePairs::OrthoDiagonalEdges + end + def e_value + RBA::Edges::OrthoDiagonalEdges + end + end + # A wrapper for the padding modes of with_density class DRCDensityPadding attr_accessor :value