diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 54f0b4f3b..aa70225ad 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -143,7 +143,7 @@ AsIfFlatRegion::to_string (size_t nmax) const } EdgesDelegate * -AsIfFlatRegion::edges (const EdgeFilterBase *filter) const +AsIfFlatRegion::edges (const EdgeFilterBase *filter, const PolygonToEdgeProcessorBase *proc) const { std::unique_ptr result (new FlatEdges ()); db::PropertyMapper pm (result->properties_repository (), properties_repository ()); @@ -154,17 +154,41 @@ AsIfFlatRegion::edges (const EdgeFilterBase *filter) const } result->reserve (n); + std::vector heap; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + db::properties_id_type prop_id = p.prop_id (); - for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { - if (! filter || filter->selected (*e)) { - if (prop_id != 0) { - result->insert (db::EdgeWithProperties (*e, pm (prop_id))); - } else { - result->insert (*e); + + if (proc) { + + heap.clear (); + proc->process (*p, heap); + + for (auto e = heap.begin (); e != heap.end (); ++e) { + if (! filter || filter->selected (*e)) { + if (prop_id != 0) { + result->insert (db::EdgeWithProperties (*e, pm (prop_id))); + } else { + result->insert (*e); + } } } + + } else { + + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + if (! filter || filter->selected (*e)) { + if (prop_id != 0) { + result->insert (db::EdgeWithProperties (*e, pm (prop_id))); + } else { + result->insert (*e); + } + } + } + } + } return result.release (); diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index 3013ca7e4..bc59155a1 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -84,7 +84,7 @@ public: virtual RegionDelegate *scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy); - virtual EdgesDelegate *edges (const EdgeFilterBase *) const; + virtual EdgesDelegate *edges (const EdgeFilterBase *filter, const db::PolygonToEdgeProcessorBase *proc) const; virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter) { diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index ceb7cf0a9..eb986ea15 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1335,7 +1335,7 @@ DeepRegion::snapped (db::Coord gx, db::Coord gy) } EdgesDelegate * -DeepRegion::edges (const EdgeFilterBase *filter) const +DeepRegion::edges (const EdgeFilterBase *filter, const PolygonToEdgeProcessorBase *proc) const { std::unique_ptr res (new db::DeepEdges (deep_layer ().derived ())); @@ -1343,7 +1343,7 @@ DeepRegion::edges (const EdgeFilterBase *filter) const return res.release (); } - if (! filter && merged_semantics () && ! merged_polygons_available ()) { + if (! proc && ! filter && merged_semantics () && ! merged_polygons_available ()) { // Hierarchical edge detector - no pre-merge required @@ -1388,15 +1388,32 @@ DeepRegion::edges (const EdgeFilterBase *filter) const const db::Shapes &s = c->shapes (polygons.layer ()); db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + std::vector heap; + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { db::Polygon poly; si->polygon (poly); - for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { - if (! filter || filter->selected ((*e).transformed (tr))) { - st.insert (db::EdgeWithProperties (*e, pm (si->prop_id ()))); + if (proc) { + + heap.clear (); + proc->process (poly, heap); + + for (auto e = heap.begin (); e != heap.end (); ++e) { + if (! filter || filter->selected ((*e).transformed (tr))) { + st.insert (db::EdgeWithProperties (*e, pm (si->prop_id ()))); + } } + + } else { + + for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { + if (! filter || filter->selected ((*e).transformed (tr))) { + st.insert (db::EdgeWithProperties (*e, pm (si->prop_id ()))); + } + } + } } diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 1bef5efb1..4804cf288 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -119,7 +119,7 @@ public: virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy); - virtual EdgesDelegate *edges (const EdgeFilterBase *) const; + virtual EdgesDelegate *edges (const EdgeFilterBase *filter, const db::PolygonToEdgeProcessorBase *proc) const; virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter); virtual RegionDelegate *processed (const PolygonProcessorBase &filter) const; diff --git a/src/db/db/dbEmptyRegion.cc b/src/db/db/dbEmptyRegion.cc index 49f87313b..a9b5e36e0 100644 --- a/src/db/db/dbEmptyRegion.cc +++ b/src/db/db/dbEmptyRegion.cc @@ -176,7 +176,7 @@ EmptyRegion::angle_check (double, double, bool) const } EdgesDelegate * -EmptyRegion::edges (const EdgeFilterBase *) const +EmptyRegion::edges (const EdgeFilterBase *, const PolygonToEdgeProcessorBase *) const { return new EmptyEdges (); } diff --git a/src/db/db/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h index 874945a7b..086ea9837 100644 --- a/src/db/db/dbEmptyRegion.h +++ b/src/db/db/dbEmptyRegion.h @@ -82,7 +82,7 @@ public: virtual RegionDelegate *scaled_and_snapped_in_place (db::Coord, db::Coord, db::Coord, db::Coord, db::Coord, db::Coord) { return this; } virtual RegionDelegate *scaled_and_snapped (db::Coord, db::Coord, db::Coord, db::Coord, db::Coord, db::Coord) { return new EmptyRegion (); } - virtual EdgesDelegate *edges (const EdgeFilterBase *) const; + virtual EdgesDelegate *edges (const EdgeFilterBase *, const PolygonToEdgeProcessorBase *) const; virtual RegionDelegate *filter_in_place (const PolygonFilterBase &) { return this; } virtual RegionDelegate *filtered (const PolygonFilterBase &) const { return new EmptyRegion (); } virtual RegionDelegate *process_in_place (const PolygonProcessorBase &) { return this; } diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index ee62f4390..39880b2ca 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -770,7 +770,7 @@ public: */ Edges edges () const { - return Edges (mp_delegate->edges (0)); + return Edges (mp_delegate->edges (0, 0)); } /** @@ -783,7 +783,34 @@ public: */ Edges edges (const EdgeFilterBase &filter) const { - return mp_delegate->edges (&filter); + return mp_delegate->edges (&filter, 0); + } + + /** + * @brief Returns an edge set containing all edges of the polygons in this region + * + * Merged semantics applies. In merged semantics, only full, outer edges are delivered. + * This version allows specifying a polygon to edge processor with additional features + * like extraction of convex edges only. + */ + Edges edges (const db::PolygonToEdgeProcessorBase &proc) const + { + return Edges (mp_delegate->edges (0, &proc)); + } + + /** + * @brief Returns an edge set containing all edges of the polygons in this region + * + * This version allows one to specify a filter by which the edges are filtered before they are + * returned. + * + * Merged semantics applies. In merged semantics, only full, outer edges are delivered. + * This version allows specifying a polygon to edge processor with additional features + * like extraction of convex edges only. + */ + Edges edges (const EdgeFilterBase &filter, const db::PolygonToEdgeProcessorBase &proc) const + { + return mp_delegate->edges (&filter, &proc); } /** diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index ab8597646..0e5686ed6 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -267,7 +267,7 @@ public: virtual RegionDelegate *scaled_and_snapped_in_place (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) = 0; virtual RegionDelegate *scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) = 0; - virtual EdgesDelegate *edges (const EdgeFilterBase *filter) const = 0; + virtual EdgesDelegate *edges (const EdgeFilterBase *filter, const db::PolygonToEdgeProcessorBase *proc) const = 0; virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) = 0; virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const = 0; virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter) = 0; diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc index ce809ac93..9ba85b006 100644 --- a/src/db/db/dbRegionProcessors.cc +++ b/src/db/db/dbRegionProcessors.cc @@ -136,10 +136,71 @@ bool RelativeExtentsAsEdges::result_must_not_be_merged () const // ----------------------------------------------------------------------------------- // PolygonToEdgeProcessor implementation +PolygonToEdgeProcessor::PolygonToEdgeProcessor (PolygonToEdgeProcessor::EdgeMode mode) + : m_mode (mode) +{ + // .. nothing yet .. +} + +inline void +next (db::Polygon::contour_type::simple_iterator &iter, const db::Polygon::contour_type &contour) +{ + if (++iter == contour.end ()) { + iter = contour.begin (); + } +} + +static void +contour_to_edges (const db::Polygon::contour_type &contour, PolygonToEdgeProcessor::EdgeMode mode, std::vector &result) +{ + if (contour.size () < 3) { + return; + } + + db::Polygon::contour_type::simple_iterator pm1 = contour.begin (); + db::Polygon::contour_type::simple_iterator p0 = pm1; + next (p0, contour); + db::Polygon::contour_type::simple_iterator p1 = p0; + next (p1, contour); + db::Polygon::contour_type::simple_iterator p2 = p1; + next (p2, contour); + + while (pm1 != contour.end ()) { + + int s1 = db::vprod_sign (*p0 - *pm1, *p1 - *p0); + int s2 = db::vprod_sign (*p1 - *p0, *p2 - *p1); + + if (mode == PolygonToEdgeProcessor::All || + (mode == PolygonToEdgeProcessor::Convex && s1 < 0 && s2 < 0) || + (mode == PolygonToEdgeProcessor::Concave && s1 > 0 && s2 > 0) || + (mode == PolygonToEdgeProcessor::StepOut && s1 > 0 && s2 < 0) || + (mode == PolygonToEdgeProcessor::StepIn && s1 < 0 && s2 > 0) || + (mode == PolygonToEdgeProcessor::Step && s1 * s2 < 0)) { + result.push_back (db::Edge (*p0, *p1)); + } + + ++pm1; + next (p0, contour); + next (p1, contour); + next (p2, contour); + + } +} + void PolygonToEdgeProcessor::process (const db::Polygon &poly, std::vector &result) const { - for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { - result.push_back (*e); + if (m_mode == All) { + + for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { + result.push_back (*e); + } + + } else { + + for (unsigned int i = 0; i < poly.holes () + 1; ++i) { + contour_to_edges (poly.contour (i), m_mode, result); + } + } } diff --git a/src/db/db/dbRegionProcessors.h b/src/db/db/dbRegionProcessors.h index 74f1a5100..9cfc96fc5 100644 --- a/src/db/db/dbRegionProcessors.h +++ b/src/db/db/dbRegionProcessors.h @@ -293,12 +293,14 @@ class DB_PUBLIC PolygonToEdgeProcessor : public db::PolygonToEdgeProcessorBase { public: - PolygonToEdgeProcessor () - { - // .. nothing yet .. - } + enum EdgeMode { All = 0, Convex, Concave, StepIn, StepOut, Step }; + + PolygonToEdgeProcessor (EdgeMode mode = All); void process (const db::Polygon &poly, std::vector &result) const; + +private: + EdgeMode m_mode; }; /** diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index 1fe59dae5..06e30f87d 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -316,13 +316,13 @@ static db::CompoundRegionOperationNode *new_minkowski_sum_node4 (db::CompoundReg return new db::CompoundRegionProcessingOperationNode (new db::minkowski_sum_computation > (p), input, true /*processor is owned*/); } -static db::CompoundRegionOperationNode *new_edges (db::CompoundRegionOperationNode *input) +static db::CompoundRegionOperationNode *new_edges (db::CompoundRegionOperationNode *input, db::PolygonToEdgeProcessor::EdgeMode edge_mode) { check_non_null (input, "input"); if (input->result_type () == db::CompoundRegionOperationNode::EdgePairs) { return new db::CompoundRegionEdgePairToEdgeProcessingOperationNode (new db::EdgePairToEdgesProcessor (), input, true /*processor is owned*/); } else if (input->result_type () == db::CompoundRegionOperationNode::Region) { - return new db::CompoundRegionToEdgeProcessingOperationNode (new db::PolygonToEdgeProcessor (), input, true /*processor is owned*/); + return new db::CompoundRegionToEdgeProcessingOperationNode (new db::PolygonToEdgeProcessor (edge_mode), input, true /*processor is owned*/); } else { input->keep (); return input; @@ -726,8 +726,12 @@ Class decl_CompoundRegionOperationNode ("db", " "@brief Creates a node filtering the input for rectangular or square shapes.\n" "If 'is_square' is true, only squares will be selected. If 'inverse' is true, the non-rectangle/non-square shapes are returned.\n" ) + - gsi::constructor ("new_edges", &new_edges, gsi::arg ("input"), + gsi::constructor ("new_edges", &new_edges, gsi::arg ("input"), gsi::arg ("mode", db::PolygonToEdgeProcessor::All, "All"), "@brief Creates a node converting polygons into its edges.\n" + "The 'mode' argument allows selecting specific edges when generating edges from a polygon. " + "See \\EdgeMode for the various options. By default, all edges are generated from polygons.\n" + "\n" + "The 'mode' argument has been added in version 0.29." ) + gsi::constructor ("new_edge_length_filter", &new_edge_length_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("lmin", 0), gsi::arg ("lmax", std::numeric_limits::max (), "max"), "@brief Creates a node filtering edges by their length.\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 43b809553..b51b1f4e9 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -780,6 +780,13 @@ size_dvm (db::Region *region, const db::Vector &dv, unsigned int mode) return *region; } +static db::Edges +edges (const db::Region *region, db::PolygonToEdgeProcessor::EdgeMode mode) +{ + db::PolygonToEdgeProcessor proc (mode); + return region->edges (proc); +} + static db::Point default_origin; // provided by gsiDeclDbPolygon.cc: @@ -2237,15 +2244,20 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "If the region is not merged, this method may return false even\n" "if the merged region would be a box.\n" ) + - method ("edges", (db::Edges (db::Region::*) () const) &db::Region::edges, + method_ext ("edges", &edges, gsi::arg ("mode", db::PolygonToEdgeProcessor::All, "All"), "@brief Returns an edge collection representing all edges of the polygons in this region\n" "This method will decompose the polygons into the individual edges. Edges making up the hulls " "of the polygons are oriented clockwise while edges making up the holes are oriented counterclockwise.\n" "\n" + "The 'mode' parameter allows selecting specific edges, such as convex or concave ones. By default, " + "all edges are selected.\n" + "\n" "The edge collection returned can be manipulated in various ways. See \\Edges for a description of the " - "possibilities of the edge collection.\n" + "features of the edge collection.\n" "\n" "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" + "\n" + "The mode argument has been added in version 0.29." ) + factory_ext ("decompose_convex", &decompose_convex, gsi::arg ("preferred_orientation", po_any (), "\\Polygon#PO_any"), "@brief Decomposes the region into convex pieces.\n" @@ -3239,6 +3251,33 @@ gsi::Enum decl_Metrics ("db", "Metrics", gsi::ClassExt inject_Metrics_in_Region (decl_Metrics.defs ()); gsi::ClassExt inject_Metrics_in_Edges (decl_Metrics.defs ()); +gsi::Enum decl_EdgeMode ("db", "EdgeMode", + gsi::enum_const ("All", db::PolygonToEdgeProcessor::All, + "@brief Selects all edges\n" + ) + + gsi::enum_const ("Concave", db::PolygonToEdgeProcessor::Concave, + "@brief Selects only concave edges\n" + ) + + gsi::enum_const ("Convex", db::PolygonToEdgeProcessor::Convex, + "@brief Selects only convex edges\n" + ) + + gsi::enum_const ("Step", db::PolygonToEdgeProcessor::Step, + "@brief Selects only step edges leading inside or outside\n" + ) + + gsi::enum_const ("StepIn", db::PolygonToEdgeProcessor::StepIn, + "@brief Selects only step edges leading inside\n" + ) + + gsi::enum_const ("StepOut", db::PolygonToEdgeProcessor::StepOut, + "@brief Selects only step edges leading outside\n" + ), + "@brief This class represents the edge mode type for \\Region#edges.\n" + "\n" + "This enum has been introduced in version 0.29." +); + +// Inject the Region::EdgeMode declarations into Region: +gsi::ClassExt inject_EdgeMode_in_Region (decl_EdgeMode.defs ()); + gsi::Enum decl_PropertyConstraint ("db", "PropertyConstraint", gsi::enum_const ("IgnoreProperties", db::IgnoreProperties, "@brief Specifies to ignore properties\n" diff --git a/src/db/unit_tests/dbRegionProcessorTests.cc b/src/db/unit_tests/dbRegionProcessorTests.cc new file mode 100644 index 000000000..da534f5a9 --- /dev/null +++ b/src/db/unit_tests/dbRegionProcessorTests.cc @@ -0,0 +1,80 @@ + + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlUnitTest.h" +#include "tlString.h" + +#include "dbRegionProcessors.h" + + +TEST(1_RegionToEdgesProcessor) +{ + db::Point hull[] = { + db::Point (0, 0), + db::Point (0, 1000), + db::Point (1000, 1000), + db::Point (1000, 2000), + db::Point (2000, 2000), + db::Point (2000, 1000), + db::Point (3000, 1000), + db::Point (3000, 0) + }; + + db::Point hole[] = { + db::Point (100, 100), + db::Point (2900, 100), + db::Point (2900, 900), + db::Point (100, 900) + }; + + db::Polygon poly; + poly.assign_hull (hull + 0, hull + sizeof (hull) / sizeof (hull[0])); + poly.insert_hole (hole + 0, hole + sizeof (hole) / sizeof (hole[0])); + + std::vector result; + + result.clear (); + db::PolygonToEdgeProcessor ().process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(0,0;0,1000);(0,1000;1000,1000);(1000,1000;1000,2000);(1000,2000;2000,2000);(2000,2000;2000,1000);(2000,1000;3000,1000);(3000,1000;3000,0);(3000,0;0,0);(100,100;2900,100);(2900,100;2900,900);(2900,900;100,900);(100,900;100,100)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::Concave).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(2900,100;2900,900);(2900,900;100,900);(100,900;100,100);(100,100;2900,100)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::Convex).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(1000,2000;2000,2000);(3000,1000;3000,0);(3000,0;0,0);(0,0;0,1000)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::Step).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(0,1000;1000,1000);(1000,1000;1000,2000);(2000,2000;2000,1000);(2000,1000;3000,1000)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::StepOut).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(1000,1000;1000,2000);(2000,1000;3000,1000)"); + + result.clear (); + db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::StepIn).process (poly, result); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(0,1000;1000,1000);(2000,2000;2000,1000)"); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 2884fc920..a5ef9690e 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -87,6 +87,7 @@ SOURCES = \ dbDeepTextsTests.cc \ dbNetShapeTests.cc \ dbHierNetsProcessorTests.cc \ + dbRegionProcessorTests.cc \ dbAsIfFlatRegionTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/drc/drc/built-in-macros/_drc_complex_ops.rb b/src/drc/drc/built-in-macros/_drc_complex_ops.rb index c3674e3d2..23b5b13b5 100644 --- a/src/drc/drc/built-in-macros/_drc_complex_ops.rb +++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb @@ -989,6 +989,7 @@ CODE # @name edges # @brief Converts the input shapes into edges # @synopsis expression.edges + # @synopsis expression.edges(mode) # # Polygons will be separated into edges forming their contours. Edge pairs will be # decomposed into individual edges. @@ -1001,9 +1002,31 @@ CODE # @code # out = in.drc(primary.edges) # @/code + # + # The "mode" argument allows selecting specific edges from polygons. + # Allows values are: "convex", "concave", "step", "step_in" and "step_out". + # "step" generates edges only that provide a step between two other + # edges. "step_in" creates edges that make a step towards the inside of + # the polygon and "step_out" creates edges that make a step towards the + # outside (hull contours in clockwise orientation, holes counterclockwise): + # + # @code + # out = in.drc(primary.edges(convex)) + # @/code + # + # The mode argument is ignored when translating other objects than + # polygons. - def edges - return DRCOpNodeFilter::new(@engine, self, :new_edges, "edges") + def edges(mode = nil) + if mode + if ! mode.is_a?(DRC::DRCEdgeMode) + raise "The mode argument needs to be a mode type (convex, concave, step, step_in or step_out)" + end + mode = mode.value + else + mode = RBA::EdgeMode::All + end + return DRCOpNodeFilter::new(@engine, self, :new_edges, "edges", mode) end # %DRC% diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 6cb79e63f..3714a325b 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -255,6 +255,26 @@ module DRC DRCJoinFlag::new(true) end + def convex + DRCEdgeMode::new(RBA::EdgeMode::Convex) + end + + def concave + DRCEdgeMode::new(RBA::EdgeMode::Concave) + end + + def step_in + DRCEdgeMode::new(RBA::EdgeMode::StepIn) + end + + def step_out + DRCEdgeMode::new(RBA::EdgeMode::StepOut) + end + + def step + DRCEdgeMode::new(RBA::EdgeMode::Step) + end + def padding_zero DRCDensityPadding::new(:zero) end diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index ee4ef6803..fe89bb036 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -3387,6 +3387,8 @@ CODE # %DRC% # @name edges # @brief Decomposes the layer into single edges + # @synopsis layer.edges + # @synopsis layer.edges(mode) # # Edge pair collections are decomposed into the individual edges that make up # the edge pairs. Polygon layers are decomposed into the edges making up the @@ -3395,13 +3397,37 @@ CODE # # Merged semantics applies, i.e. the result reflects merged polygons rather than # individual ones unless raw mode is chosen. + # + # The "mode" argument allows selecting specific edges from polygons. + # Allows values are: "convex", "concave", "step", "step_in" and "step_out". + # "step" generates edges only that provide a step between two other + # edges. "step_in" creates edges that make a step towards the inside of + # the polygon and "step_out" creates edges that make a step towards the + # outside (hull contours in clockwise orientation, holes counterclockwise): + # + # @code + # out = in.edges(convex) + # @/code + # + # This feature is only available for polygon layers. %w(edges).each do |f| eval <<"CODE" - def #{f} + def #{f}(mode = nil) + if mode + if ! mode.is_a?(DRC::DRCEdgeMode) + raise "The mode argument needs to be a mode type (convex, concave, step, step_in or step_out)" + end + if ! self.data.is_a?(RBA::Region) + raise "The mode argument is only available for polygon layers" + end + mode = mode.value + else + mode = RBA::EdgeMode::All + end @engine._context("#{f}") do if self.data.is_a?(RBA::Region) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :#{f})) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :#{f}, mode)) elsif self.data.is_a?(RBA::EdgePairs) DRCLayer::new(@engine, @engine._cmd(self.data, :#{f})) else diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index 495f458d1..e0ad17d31 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -57,6 +57,14 @@ module DRC end end + # A wrapper for the edge mode value for Region#edges + class DRCEdgeMode + attr_accessor :value + def initialize(v) + self.value = v + end + end + # A wrapper for the join flag for extended class DRCJoinFlag attr_accessor :value diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 3c3d7444b..64e8b84ec 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1636,3 +1636,13 @@ TEST(91d_edge_booleans_with_dots) { run_test (_this, "91", true); } + +TEST(92_edge_modes) +{ + run_test (_this, "92", false); +} + +TEST(92d_edge_modes) +{ + run_test (_this, "92", true); +} diff --git a/testdata/drc/drcSimpleTests_92.drc b/testdata/drc/drcSimpleTests_92.drc new file mode 100644 index 000000000..5894b4c93 --- /dev/null +++ b/testdata/drc/drcSimpleTests_92.drc @@ -0,0 +1,29 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +l2.edges.output(100, 0) +l2.edges(convex).output(101, 0) +l2.edges(concave).output(102, 0) +l2.edges(step).output(103, 0) +l2.edges(step_in).output(104, 0) +l2.edges(step_out).output(105, 0) + +l2.drc(primary.edges).output(200, 0) +l2.drc(primary.edges(convex)).output(201, 0) +l2.drc(primary.edges(concave)).output(202, 0) +l2.drc(primary.edges(step)).output(203, 0) +l2.drc(primary.edges(step_in)).output(204, 0) +l2.drc(primary.edges(step_out)).output(205, 0) + + diff --git a/testdata/drc/drcSimpleTests_92.gds b/testdata/drc/drcSimpleTests_92.gds new file mode 100644 index 000000000..b3dc99ddb Binary files /dev/null and b/testdata/drc/drcSimpleTests_92.gds differ diff --git a/testdata/drc/drcSimpleTests_au92.gds b/testdata/drc/drcSimpleTests_au92.gds new file mode 100644 index 000000000..2ce998226 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au92.gds differ diff --git a/testdata/drc/drcSimpleTests_au92d.gds b/testdata/drc/drcSimpleTests_au92d.gds new file mode 100644 index 000000000..77d0297d8 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au92d.gds differ