Introducing edge modes for DRC 'edges' function - allows easy extraction of convex edges only

This commit is contained in:
Matthias Koefferlein 2024-03-07 21:51:52 +01:00
parent 156f0f4477
commit 853de5b0ae
23 changed files with 405 additions and 34 deletions

View File

@ -143,7 +143,7 @@ AsIfFlatRegion::to_string (size_t nmax) const
} }
EdgesDelegate * EdgesDelegate *
AsIfFlatRegion::edges (const EdgeFilterBase *filter) const AsIfFlatRegion::edges (const EdgeFilterBase *filter, const PolygonToEdgeProcessorBase *proc) const
{ {
std::unique_ptr<FlatEdges> result (new FlatEdges ()); std::unique_ptr<FlatEdges> result (new FlatEdges ());
db::PropertyMapper pm (result->properties_repository (), properties_repository ()); db::PropertyMapper pm (result->properties_repository (), properties_repository ());
@ -154,8 +154,29 @@ AsIfFlatRegion::edges (const EdgeFilterBase *filter) const
} }
result->reserve (n); result->reserve (n);
std::vector<db::Edge> heap;
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
db::properties_id_type prop_id = p.prop_id (); db::properties_id_type prop_id = p.prop_id ();
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) { for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) {
if (! filter || filter->selected (*e)) { if (! filter || filter->selected (*e)) {
if (prop_id != 0) { if (prop_id != 0) {
@ -165,6 +186,9 @@ AsIfFlatRegion::edges (const EdgeFilterBase *filter) const
} }
} }
} }
}
} }
return result.release (); return result.release ();

View File

@ -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 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) virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter)
{ {

View File

@ -1335,7 +1335,7 @@ DeepRegion::snapped (db::Coord gx, db::Coord gy)
} }
EdgesDelegate * EdgesDelegate *
DeepRegion::edges (const EdgeFilterBase *filter) const DeepRegion::edges (const EdgeFilterBase *filter, const PolygonToEdgeProcessorBase *proc) const
{ {
std::unique_ptr<db::DeepEdges> res (new db::DeepEdges (deep_layer ().derived ())); std::unique_ptr<db::DeepEdges> res (new db::DeepEdges (deep_layer ().derived ()));
@ -1343,7 +1343,7 @@ DeepRegion::edges (const EdgeFilterBase *filter) const
return res.release (); 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 // Hierarchical edge detector - no pre-merge required
@ -1388,11 +1388,26 @@ DeepRegion::edges (const EdgeFilterBase *filter) const
const db::Shapes &s = c->shapes (polygons.layer ()); const db::Shapes &s = c->shapes (polygons.layer ());
db::Shapes &st = c->shapes (res->deep_layer ().layer ()); db::Shapes &st = c->shapes (res->deep_layer ().layer ());
std::vector<db::Edge> heap;
for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) {
db::Polygon poly; db::Polygon poly;
si->polygon (poly); si->polygon (poly);
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) { for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
if (! filter || filter->selected ((*e).transformed (tr))) { if (! filter || filter->selected ((*e).transformed (tr))) {
st.insert (db::EdgeWithProperties (*e, pm (si->prop_id ()))); st.insert (db::EdgeWithProperties (*e, pm (si->prop_id ())));
@ -1403,6 +1418,8 @@ DeepRegion::edges (const EdgeFilterBase *filter) const
} }
}
res->set_is_merged (merged_semantics () || is_merged ()); res->set_is_merged (merged_semantics () || is_merged ());
} }

View File

@ -119,7 +119,7 @@ public:
virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy); 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 *process_in_place (const PolygonProcessorBase &filter);
virtual RegionDelegate *processed (const PolygonProcessorBase &filter) const; virtual RegionDelegate *processed (const PolygonProcessorBase &filter) const;

View File

@ -176,7 +176,7 @@ EmptyRegion::angle_check (double, double, bool) const
} }
EdgesDelegate * EdgesDelegate *
EmptyRegion::edges (const EdgeFilterBase *) const EmptyRegion::edges (const EdgeFilterBase *, const PolygonToEdgeProcessorBase *) const
{ {
return new EmptyEdges (); return new EmptyEdges ();
} }

View File

@ -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_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 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 *filter_in_place (const PolygonFilterBase &) { return this; }
virtual RegionDelegate *filtered (const PolygonFilterBase &) const { return new EmptyRegion (); } virtual RegionDelegate *filtered (const PolygonFilterBase &) const { return new EmptyRegion (); }
virtual RegionDelegate *process_in_place (const PolygonProcessorBase &) { return this; } virtual RegionDelegate *process_in_place (const PolygonProcessorBase &) { return this; }

View File

@ -770,7 +770,7 @@ public:
*/ */
Edges edges () const 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 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);
} }
/** /**

View File

@ -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_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 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 *filter_in_place (const PolygonFilterBase &filter) = 0;
virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const = 0; virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const = 0;
virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter) = 0; virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter) = 0;

View File

@ -136,11 +136,72 @@ bool RelativeExtentsAsEdges::result_must_not_be_merged () const
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// PolygonToEdgeProcessor implementation // 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<db::Edge> &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<db::Edge> &result) const void PolygonToEdgeProcessor::process (const db::Polygon &poly, std::vector<db::Edge> &result) const
{ {
if (m_mode == All) {
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
result.push_back (*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);
}
}
} }
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------

View File

@ -293,12 +293,14 @@ class DB_PUBLIC PolygonToEdgeProcessor
: public db::PolygonToEdgeProcessorBase : public db::PolygonToEdgeProcessorBase
{ {
public: public:
PolygonToEdgeProcessor () enum EdgeMode { All = 0, Convex, Concave, StepIn, StepOut, Step };
{
// .. nothing yet .. PolygonToEdgeProcessor (EdgeMode mode = All);
}
void process (const db::Polygon &poly, std::vector<db::Edge> &result) const; void process (const db::Polygon &poly, std::vector<db::Edge> &result) const;
private:
EdgeMode m_mode;
}; };
/** /**

View File

@ -316,13 +316,13 @@ static db::CompoundRegionOperationNode *new_minkowski_sum_node4 (db::CompoundReg
return new db::CompoundRegionProcessingOperationNode (new db::minkowski_sum_computation<std::vector<db::Point> > (p), input, true /*processor is owned*/); return new db::CompoundRegionProcessingOperationNode (new db::minkowski_sum_computation<std::vector<db::Point> > (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"); check_non_null (input, "input");
if (input->result_type () == db::CompoundRegionOperationNode::EdgePairs) { if (input->result_type () == db::CompoundRegionOperationNode::EdgePairs) {
return new db::CompoundRegionEdgePairToEdgeProcessingOperationNode (new db::EdgePairToEdgesProcessor (), input, true /*processor is owned*/); return new db::CompoundRegionEdgePairToEdgeProcessingOperationNode (new db::EdgePairToEdgesProcessor (), input, true /*processor is owned*/);
} else if (input->result_type () == db::CompoundRegionOperationNode::Region) { } 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 { } else {
input->keep (); input->keep ();
return input; return input;
@ -726,8 +726,12 @@ Class<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode ("db", "
"@brief Creates a node filtering the input for rectangular or square shapes.\n" "@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" "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" "@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<db::Edge::distance_type>::max (), "max"), 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<db::Edge::distance_type>::max (), "max"),
"@brief Creates a node filtering edges by their length.\n" "@brief Creates a node filtering edges by their length.\n"

View File

@ -780,6 +780,13 @@ size_dvm (db::Region *region, const db::Vector &dv, unsigned int mode)
return *region; 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; static db::Point default_origin;
// provided by gsiDeclDbPolygon.cc: // provided by gsiDeclDbPolygon.cc:
@ -2237,15 +2244,20 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"If the region is not merged, this method may return false even\n" "If the region is not merged, this method may return false even\n"
"if the merged region would be a box.\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" "@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 " "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" "of the polygons are oriented clockwise while edges making up the holes are oriented counterclockwise.\n"
"\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 " "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" "\n"
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\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<db::Shapes>, gsi::arg ("preferred_orientation", po_any (), "\\Polygon#PO_any"), factory_ext ("decompose_convex", &decompose_convex<db::Shapes>, gsi::arg ("preferred_orientation", po_any (), "\\Polygon#PO_any"),
"@brief Decomposes the region into convex pieces.\n" "@brief Decomposes the region into convex pieces.\n"
@ -3239,6 +3251,33 @@ gsi::Enum<db::metrics_type> decl_Metrics ("db", "Metrics",
gsi::ClassExt<db::Region> inject_Metrics_in_Region (decl_Metrics.defs ()); gsi::ClassExt<db::Region> inject_Metrics_in_Region (decl_Metrics.defs ());
gsi::ClassExt<db::Edges> inject_Metrics_in_Edges (decl_Metrics.defs ()); gsi::ClassExt<db::Edges> inject_Metrics_in_Edges (decl_Metrics.defs ());
gsi::Enum<db::PolygonToEdgeProcessor::EdgeMode> 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<db::Region> inject_EdgeMode_in_Region (decl_EdgeMode.defs ());
gsi::Enum<db::PropertyConstraint> decl_PropertyConstraint ("db", "PropertyConstraint", gsi::Enum<db::PropertyConstraint> decl_PropertyConstraint ("db", "PropertyConstraint",
gsi::enum_const ("IgnoreProperties", db::IgnoreProperties, gsi::enum_const ("IgnoreProperties", db::IgnoreProperties,
"@brief Specifies to ignore properties\n" "@brief Specifies to ignore properties\n"

View File

@ -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<db::Edge> 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)");
}

View File

@ -87,6 +87,7 @@ SOURCES = \
dbDeepTextsTests.cc \ dbDeepTextsTests.cc \
dbNetShapeTests.cc \ dbNetShapeTests.cc \
dbHierNetsProcessorTests.cc \ dbHierNetsProcessorTests.cc \
dbRegionProcessorTests.cc \
dbAsIfFlatRegionTests.cc dbAsIfFlatRegionTests.cc
INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC

View File

@ -989,6 +989,7 @@ CODE
# @name edges # @name edges
# @brief Converts the input shapes into edges # @brief Converts the input shapes into edges
# @synopsis expression.edges # @synopsis expression.edges
# @synopsis expression.edges(mode)
# #
# Polygons will be separated into edges forming their contours. Edge pairs will be # Polygons will be separated into edges forming their contours. Edge pairs will be
# decomposed into individual edges. # decomposed into individual edges.
@ -1001,9 +1002,31 @@ CODE
# @code # @code
# out = in.drc(primary.edges) # out = in.drc(primary.edges)
# @/code # @/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 def edges(mode = nil)
return DRCOpNodeFilter::new(@engine, self, :new_edges, "edges") 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 end
# %DRC% # %DRC%

View File

@ -255,6 +255,26 @@ module DRC
DRCJoinFlag::new(true) DRCJoinFlag::new(true)
end 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 def padding_zero
DRCDensityPadding::new(:zero) DRCDensityPadding::new(:zero)
end end

View File

@ -3387,6 +3387,8 @@ CODE
# %DRC% # %DRC%
# @name edges # @name edges
# @brief Decomposes the layer into single 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 # 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 # 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 # Merged semantics applies, i.e. the result reflects merged polygons rather than
# individual ones unless raw mode is chosen. # 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| %w(edges).each do |f|
eval <<"CODE" 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 @engine._context("#{f}") do
if self.data.is_a?(RBA::Region) 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) elsif self.data.is_a?(RBA::EdgePairs)
DRCLayer::new(@engine, @engine._cmd(self.data, :#{f})) DRCLayer::new(@engine, @engine._cmd(self.data, :#{f}))
else else

View File

@ -57,6 +57,14 @@ module DRC
end end
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 # A wrapper for the join flag for extended
class DRCJoinFlag class DRCJoinFlag
attr_accessor :value attr_accessor :value

View File

@ -1636,3 +1636,13 @@ TEST(91d_edge_booleans_with_dots)
{ {
run_test (_this, "91", true); run_test (_this, "91", true);
} }
TEST(92_edge_modes)
{
run_test (_this, "92", false);
}
TEST(92d_edge_modes)
{
run_test (_this, "92", true);
}

29
testdata/drc/drcSimpleTests_92.drc vendored Normal file
View File

@ -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)

BIN
testdata/drc/drcSimpleTests_92.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au92.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au92d.gds vendored Normal file

Binary file not shown.