diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index 099961f30..f5971dafd 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -207,4 +207,71 @@ EdgeSegmentSelector::process (const db::Edge &edge, std::vector &res) } } +// ------------------------------------------------------------------------------------------------------------- +// EdgeAngleChecker implementation + +EdgeAngleChecker::EdgeAngleChecker (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end) +{ + m_t_start = db::CplxTrans(1.0, angle_start, false, db::DVector ()); + m_t_end = db::CplxTrans(1.0, angle_end, false, db::DVector ()); + + m_include_start = include_angle_start; + m_include_end = include_angle_end; + + m_big_angle = (angle_end - angle_start + db::epsilon) > 180.0; + m_all = (angle_end - angle_start - db::epsilon) > 360.0; +} + +bool +EdgeAngleChecker::check (const db::Vector &a, const db::Vector &b) const +{ + db::DVector vout (b); + + db::DVector v1 = m_t_start * a; + db::DVector v2 = m_t_end * a; + + int vps1 = db::vprod_sign (v1, vout); + int vps2 = db::vprod_sign (v2, vout); + bool opp1 = vps1 == 0 && (db::sprod_sign (v1, vout) < 0); + bool opp2 = vps2 == 0 && (db::sprod_sign (v2, vout) < 0); + + bool vp1 = !opp1 && (m_include_start ? (db::vprod_sign (v1, vout) >= 0) : (db::vprod_sign (v1, vout) > 0)); + bool vp2 = !opp2 && (m_include_end ? (db::vprod_sign (v2, vout) <= 0) : (db::vprod_sign (v2, vout) < 0)); + + if (m_big_angle && (vp1 || vp2)) { + return true; + } else if (! m_big_angle && vp1 && vp2) { + return true; + } else { + return false; + } +} + +// ------------------------------------------------------------------------------------------------------------- +// EdgeOrientationFilter implementation + +EdgeOrientationFilter::EdgeOrientationFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverse) + : m_inverse (inverse), m_checker (amin, include_amin, amax, include_amax) +{ + // .. nothing yet .. +} + +EdgeOrientationFilter::EdgeOrientationFilter (double a, bool inverse) + : m_inverse (inverse), m_checker (a, true, a, true) +{ + // .. nothing yet .. +} + +bool +EdgeOrientationFilter::selected (const db::Edge &edge) const +{ + // 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)) { + return m_checker (db::Vector (edge.ortho_length (), 0), -edge.d ()) != m_inverse; + } else { + return m_checker (db::Vector (edge.ortho_length (), 0), edge.d ()) != m_inverse; + } +} + } diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h index 9a08173c4..bfb0650b4 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -103,6 +103,41 @@ private: db::MagnificationReducer m_vars; }; +/** + * @brief An angle detector + * + * This detector can check whether the angle between two edges is within a certain angle interval. + * It takes two edges: a and b. If b "turns left" (b following a), the angle will be positive, if it "turns" right, + * the angle will be negative. The angle can be between -180 and 180 degree. The case of reflection + * (exactly 180 degree) is not considered. + * + * The constraint can be given in terms of a minimum and maximum angle. "include" specifies whether the + * angle value itself is included. The operator() will return true, if the angle between the given + * edges a and b in matching the constraint. + */ +class DB_PUBLIC EdgeAngleChecker +{ +public: + EdgeAngleChecker (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end); + + bool operator() (const db::Edge &a, const db::Edge &b) const + { + return m_all || check (a.d (), b.d ()); + } + + bool operator() (const db::Vector &a, const db::Vector &b) const + { + return m_all || check (a, b); + } + +private: + db::CplxTrans m_t_start, m_t_end; + bool m_include_start, m_include_end; + bool m_big_angle, m_all; + + bool check (const db::Vector &a, const db::Vector &b) const; +}; + /** * @brief An edge orientation filter for use with Edges::filter or Edges::filtered * @@ -126,12 +161,7 @@ struct DB_PUBLIC EdgeOrientationFilter * This filter will filter out all edges whose angle against x axis * is larger or equal to amin and less than amax. */ - EdgeOrientationFilter (double amin, double amax, bool inverse) - : m_inverse (inverse), m_exact (false) - { - m_emin = db::DVector (cos (amin * M_PI / 180.0), sin (amin * M_PI / 180.0)); - m_emax = db::DVector (cos (amax * M_PI / 180.0), sin (amax * M_PI / 180.0)); - } + EdgeOrientationFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverse); /** * @brief Constructor @@ -142,33 +172,12 @@ struct DB_PUBLIC EdgeOrientationFilter * This filter will filter out all edges whose angle against x axis * is equal to a. */ - EdgeOrientationFilter (double a, bool inverse) - : m_inverse (inverse), m_exact (true) - { - m_emin = db::DVector (cos (a * M_PI / 180.0), sin (a * M_PI / 180.0)); - } + EdgeOrientationFilter (double a, bool inverse); /** * @brief Returns true if the edge orientation matches the criterion */ - virtual bool selected (const db::Edge &edge) const - { - int smin = db::vprod_sign (m_emin, db::DVector (edge.d ())); - if (m_exact) { - if (! m_inverse) { - return smin == 0; - } else { - return smin != 0; - } - } else { - int smax = db::vprod_sign (m_emax, db::DVector (edge.d ())); - if (! m_inverse) { - return (smin >= 0 && smax < 0) || (smax > 0 && smin <= 0); - } else { - return ! ((smin >= 0 && smax < 0) || (smax > 0 && smin <= 0)); - } - } - } + virtual bool selected (const db::Edge &edge) const; /** * @brief This filter is not isotropic @@ -197,8 +206,8 @@ struct DB_PUBLIC EdgeOrientationFilter private: db::DVector m_emin, m_emax; bool m_inverse; - bool m_exact; db::MagnificationAndOrientationReducer m_vars; + EdgeAngleChecker m_checker; }; /** diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc index c2cf6c8a7..9134dc12f 100644 --- a/src/db/db/dbRegionProcessors.cc +++ b/src/db/db/dbRegionProcessors.cc @@ -31,15 +31,9 @@ namespace db // CornerDetectorCore implementation CornerDetectorCore::CornerDetectorCore (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end) + : m_checker (angle_start, include_angle_start, angle_end, include_angle_end) { - m_t_start = db::CplxTrans(1.0, angle_start, false, db::DVector ()); - m_t_end = db::CplxTrans(1.0, angle_end, false, db::DVector ()); - - m_include_start = include_angle_start; - m_include_end = include_angle_end; - - m_big_angle = (angle_end - angle_start + db::epsilon) > 180.0; - m_all = (angle_end - angle_start - db::epsilon) > 360.0; + // .. nothing yet .. } void CornerDetectorCore::detect_corners (const db::Polygon &poly, const CornerPointDelivery &delivery) const @@ -57,30 +51,8 @@ void CornerDetectorCore::detect_corners (const db::Polygon &poly, const CornerPo db::Point pn = ctr [j]; - if (m_all) { + if (m_checker (pt - pp, pn - pt)) { delivery.make_point (pt); - } else { - - db::Vector vin (pt - pp); - db::DVector vout (pn - pt); - - db::DVector v1 = m_t_start * vin; - db::DVector v2 = m_t_end * vin; - - int vps1 = db::vprod_sign (v1, vout); - int vps2 = db::vprod_sign (v2, vout); - bool opp1 = vps1 == 0 && (db::sprod_sign (v1, vout) < 0); - bool opp2 = vps2 == 0 && (db::sprod_sign (v2, vout) < 0); - - bool vp1 = !opp1 && (m_include_start ? (db::vprod_sign (v1, vout) >= 0) : (db::vprod_sign (v1, vout) > 0)); - bool vp2 = !opp2 && (m_include_end ? (db::vprod_sign (v2, vout) <= 0) : (db::vprod_sign (v2, vout) < 0)); - - if (m_big_angle && (vp1 || vp2)) { - delivery.make_point (pt); - } else if (! m_big_angle && vp1 && vp2) { - delivery.make_point (pt); - } - } pp = pt; diff --git a/src/db/db/dbRegionProcessors.h b/src/db/db/dbRegionProcessors.h index 4f9a4f206..d91613148 100644 --- a/src/db/db/dbRegionProcessors.h +++ b/src/db/db/dbRegionProcessors.h @@ -27,6 +27,7 @@ #include "dbCommon.h" #include "dbRegionDelegate.h" #include "dbPolygonTools.h" +#include "dbEdgesUtils.h" namespace db { @@ -97,9 +98,7 @@ public: void detect_corners (const db::Polygon &poly, const CornerPointDelivery &delivery) const; private: - db::CplxTrans m_t_start, m_t_end; - bool m_include_start, m_include_end; - bool m_big_angle, m_all; + db::EdgeAngleChecker m_checker; }; /** diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index 9ade51e65..9ccc49e7f 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -326,10 +326,10 @@ static db::CompoundRegionOperationNode *new_edge_length_filter (db::CompoundRegi return new db::CompoundRegionEdgeFilterOperationNode (new db::EdgeLengthFilter (lmin, lmax, inverse), input, true /*processor is owned*/); } -static db::CompoundRegionOperationNode *new_edge_orientation_filter (db::CompoundRegionOperationNode *input, bool inverse, double amin, double amax) +static db::CompoundRegionOperationNode *new_edge_orientation_filter (db::CompoundRegionOperationNode *input, bool inverse, double amin, bool include_amin, double amax, bool include_amax) { check_non_null (input, "input"); - return new db::CompoundRegionEdgeFilterOperationNode (new db::EdgeOrientationFilter (amin, amax, inverse), input, true /*processor is owned*/); + return new db::CompoundRegionEdgeFilterOperationNode (new db::EdgeOrientationFilter (amin, include_amin, amax, include_amax, inverse), input, true /*processor is owned*/); } static db::CompoundRegionOperationNode *new_polygons (db::CompoundRegionOperationNode *input, db::Coord e) @@ -675,7 +675,7 @@ Class decl_CompoundRegionOperationNode ("db", " 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" ) + - gsi::constructor ("new_edge_orientation_filter", &new_edge_orientation_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("amin"), gsi::arg ("amax"), + gsi::constructor ("new_edge_orientation_filter", &new_edge_orientation_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("amin"), gsi::arg ("include_amin"), gsi::arg ("amax"), gsi::arg ("include_amax"), "@brief Creates a node filtering edges by their orientation.\n" ) + gsi::constructor ("new_polygons", &new_polygons, gsi::arg ("input"), gsi::arg ("e", 0), diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index b48d3b76c..0f4398e0f 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -206,9 +206,9 @@ static db::Edges with_angle1 (const db::Edges *r, double a, bool inverse) return r->filtered (f); } -static db::Edges with_angle2 (const db::Edges *r, double amin, double amax, bool inverse) +static db::Edges with_angle2 (const db::Edges *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { - db::EdgeOrientationFilter f (amin, amax, inverse); + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse); return r->filtered (f); } @@ -581,12 +581,17 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "horizontal = edges.with_orientation(0, true)\n" "@/code\n" ) + - method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), + method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_amin", true), gsi::arg ("include_amax", true), "@brief Filter 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\" and less than \"max_angle\" are " "returned. If \"inverse\" is true, " - "edges which do not conform to this criterion are returned." + "edges which do not conform to this criterion are returned.\n" + "\n" + "With \"include_min\" set to true (the default), the minimum angle is included in the criterion while with false, the " + "minimum angle itself is not included. Same for \"include_max\".\n" + "\n" + "The two \"include..\" arguments have been added in version 0.27." ) + method ("insert", (void (db::Edges::*)(const db::Edge &)) &db::Edges::insert, gsi::arg ("edge"), "@brief Inserts an edge\n" diff --git a/src/db/unit_tests/dbDeepEdgesTests.cc b/src/db/unit_tests/dbDeepEdgesTests.cc index 08cce0a34..06125dbc8 100644 --- a/src/db/unit_tests/dbDeepEdgesTests.cc +++ b/src/db/unit_tests/dbDeepEdgesTests.cc @@ -259,8 +259,8 @@ TEST(5_Filters) target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); - db::EdgeOrientationFilter eof1 (0, 1, false); - db::EdgeOrientationFilter eof2 (0, 1, true); + db::EdgeOrientationFilter eof1 (0, true, 1, true, false); + db::EdgeOrientationFilter eof2 (0, true, 1, true, true); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e2.filtered (eof1)); target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2.filtered (eof2)); diff --git a/src/db/unit_tests/dbEdgesTests.cc b/src/db/unit_tests/dbEdgesTests.cc index 5083e65b1..c9e17596a 100644 --- a/src/db/unit_tests/dbEdgesTests.cc +++ b/src/db/unit_tests/dbEdgesTests.cc @@ -200,23 +200,23 @@ TEST(4) EXPECT_EQ (r.filtered (f1).to_string (), "(0,200;100,200);(100,0;0,0);(300,0;200,0)"); } { - db::EdgeOrientationFilter f1 (50.0, 80.0, false); + db::EdgeOrientationFilter f1 (50.0, true, 80.0, true, false); EXPECT_EQ (r.filtered (f1).to_string (), "(200,0;250,200);(250,-200;300,0)"); } { - db::EdgeOrientationFilter f1 (50.0, 80.0, true); + db::EdgeOrientationFilter f1 (50.0, true, 80.0, true, true); EXPECT_EQ (r.filtered (f1).to_string (), "(0,0;0,200);(0,200;100,200);(100,200;100,0);(100,0;0,0);(250,200;300,0);(300,0;200,0);(200,0;250,-200)"); } { - db::EdgeOrientationFilter f1 (0.0, 1.0, false); + db::EdgeOrientationFilter f1 (0.0, true, 1.0, true, false); EXPECT_EQ (r.filtered (f1).to_string (), "(0,200;100,200);(100,0;0,0);(300,0;200,0)"); } { - db::EdgeOrientationFilter f1 (-1.0, 1.0, false); + db::EdgeOrientationFilter f1 (-1.0, true, 1.0, true, false); EXPECT_EQ (r.filtered (f1).to_string (), "(0,200;100,200);(100,0;0,0);(300,0;200,0)"); } { - db::EdgeOrientationFilter f1 (-1.0, 0.0, false); + db::EdgeOrientationFilter f1 (-1.0, true, 0.0, true, false); EXPECT_EQ (r.filtered (f1).to_string (), ""); } { @@ -224,15 +224,15 @@ TEST(4) EXPECT_EQ (r.filtered (f1).to_string (), "(0,0;0,200);(100,200;100,0)"); } { - db::EdgeOrientationFilter f1 (90.0, 91.0, false); + db::EdgeOrientationFilter f1 (90.0, true, 91.0, true, false); EXPECT_EQ (r.filtered (f1).to_string (), "(0,0;0,200);(100,200;100,0)"); } { - db::EdgeOrientationFilter f1 (89.0, 91.0, false); + db::EdgeOrientationFilter f1 (89.0, true, 91.0, true, false); EXPECT_EQ (r.filtered (f1).to_string (), "(0,0;0,200);(100,200;100,0)"); } { - db::EdgeOrientationFilter f1 (89.0, 90.0, false); + db::EdgeOrientationFilter f1 (89.0, true, 90.0, true, false); EXPECT_EQ (r.filtered (f1).to_string (), ""); } } 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 fcb737383..9296d2a98 100644 --- a/src/drc/drc/built-in-macros/_drc_complex_ops.rb +++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb @@ -1324,7 +1324,9 @@ class DRCOpNodeEdgeOrientationFilter < DRCOpNodeWithCompare args = [ n, self.inverse ] angle_delta = 1e-6 args << (self.gt ? self.gt + angle_delta : (self.ge ? self.ge : -180.0)) + args << (self.gt ? false : true) args << (self.lt ? self.lt : (self.le ? self.le + angle_delta : 180.0)) + args << (self.lt ? false : true) RBA::CompoundRegionOperationNode::new_edge_orientation_filter(*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 949e04e57..d6cfaffa7 100644 --- a/src/drc/drc/built-in-macros/_drc_cop_integration.rb +++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb @@ -334,13 +334,15 @@ CODE # %DRC% # @name area - # @brief Selects the primary shape if the area is meeting the condition + # @brief Computes the total area or in universal DRC context: selects the primary shape if the area is meeting the condition # @synopsis area (in condition) # @synopsis area(layer) # # This function can be used with a layer argument in which case it - # is equivalent to "layer.area" (see \Layer#area). Without a layer - # argument, "area" represents an area filter for primary shapes in + # is equivalent to "layer.area" (see \Layer#area) and returns the total area of the + # polygons in the layer. + # + # Without a layer argument, "area" represents an area filter for primary shapes in # \DRC# expressions (see \Layer#drc and \DRC#area for more details). # %DRC% @@ -378,13 +380,15 @@ CODE # %DRC% # @name perimeter - # @brief Selects the primary shape if the perimeter is meeting the condition + # @brief Computes the total perimeter or in universal DRC context: selects the primary shape if the perimeter is meeting the condition # @synopsis perimeter (in condition) # @synopsis perimeter(layer) # # This function can be used with a layer argument in which case it - # is equivalent to "layer.perimeter" (see \Layer#perimeter). Without a layer - # argument, "perimeter" represents a perimeter filter for primary shapes in + # is equivalent to "layer.perimeter" (see \Layer#perimeter) and returns the + # total perimeter of all polygons in the layer. + # + # Without a layer argument, "perimeter" represents a perimeter filter for primary shapes in # \DRC# expressions (see \Layer#drc and \DRC#perimeter for more details). # %DRC% @@ -409,6 +413,27 @@ CODE # argument, "rectilinear" represents the rectilinear polygons filter for primary shapes in # \DRC# expressions (see \Layer#drc and \DRC#rectilinear for more details). + # %DRC% + # @name length (in condition) + # @brief Computes the total edge length of an edge layer or in universal DRC context: selects edges based on a length condition + # @synopsis length (in condition) + # @synopsis length(layer) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.length" (see \Layer#length). Without a layer + # argument, "length" represents the edge length filter on the primary shape edges in + # \DRC# expressions (see \Layer#drc and \DRC#length for more details). In this context, + # the operation acts similar to \Layer#with_length. + + # %DRC% + # @name angle (in condition) + # @brief In universal DRC context: selects edges based on their orientation + # @synopsis angle (in condition) + # + # "angle" represents the edge orientation filter on the primary shape edges in + # \DRC# expressions (see \Layer#drc and \DRC#angle for more details). In this context, + # the operation acts similar to \Layer#with_angle. + %w( area holes @@ -417,6 +442,8 @@ CODE perimeter rectangles rectilinear + length + angle ).each do |f| # NOTE: these methods are fallback for the respective global ones which route to DRCLayer or here. eval <<"CODE" diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 78146057d..b03b9c4a0 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -1574,6 +1574,7 @@ CODE %w( and andnot + angle area bbox centers diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index ea74b4abe..7ecec71c6 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -2780,7 +2780,7 @@ CODE @engine._cmd(self.data, :length) * @engine.dbu.to_f end end - + # %DRC% # @name flatten # @brief Flattens the layer diff --git a/src/drc/unit_tests/drcGenericTests.cc b/src/drc/unit_tests/drcGenericTests.cc index ceb96eb0d..3ff538d89 100644 --- a/src/drc/unit_tests/drcGenericTests.cc +++ b/src/drc/unit_tests/drcGenericTests.cc @@ -178,3 +178,13 @@ TEST(11d) { run_test (_this, "11", true); } + +TEST(12) +{ + run_test (_this, "12", false); +} + +TEST(12d) +{ + run_test (_this, "12", true); +}