mirror of https://github.com/KLayout/klayout.git
Implemented #808 (Feature suggestion: DRC to report edges attached to corners as edge pairs). Solution is available for DRC layers and universal DRC expressions.
This commit is contained in:
parent
fd1e206c56
commit
5ceeafc0ff
|
|
@ -52,7 +52,7 @@ void CornerDetectorCore::detect_corners (const db::Polygon &poly, const CornerPo
|
|||
db::Point pn = ctr [j];
|
||||
|
||||
if (m_checker (pt - pp, pn - pt)) {
|
||||
delivery.make_point (pt);
|
||||
delivery.make_point (pt, db::Edge (pp, pt), db::Edge (pt, pn));
|
||||
}
|
||||
|
||||
pp = pt;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace db
|
|||
class DB_PUBLIC CornerPointDelivery
|
||||
{
|
||||
public:
|
||||
virtual void make_point (const db::Point &pt) const = 0;
|
||||
virtual void make_point (const db::Point &pt, const db::Edge &e1, const db::Edge &e2) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -55,7 +55,7 @@ public:
|
|||
: m_d (dim, dim), mp_result (&result)
|
||||
{ }
|
||||
|
||||
virtual void make_point (const db::Point &pt) const
|
||||
virtual void make_point (const db::Point &pt, const db::Edge &, const db::Edge &) const
|
||||
{
|
||||
mp_result->push_back (db::Polygon (db::Box (pt - m_d, pt + m_d)));
|
||||
}
|
||||
|
|
@ -76,7 +76,7 @@ public:
|
|||
: mp_result (&result)
|
||||
{ }
|
||||
|
||||
virtual void make_point (const db::Point &pt) const
|
||||
virtual void make_point (const db::Point &pt, const db::Edge &, const db::Edge &) const
|
||||
{
|
||||
mp_result->push_back (db::Edge (pt, pt));
|
||||
}
|
||||
|
|
@ -86,6 +86,26 @@ private:
|
|||
std::vector<db::Edge> *mp_result;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An interface to accept corners and turns them into edge pairs
|
||||
*/
|
||||
class DB_PUBLIC CornerEdgePairDelivery
|
||||
: public CornerPointDelivery
|
||||
{
|
||||
public:
|
||||
CornerEdgePairDelivery (std::vector<db::EdgePair> &result)
|
||||
: mp_result (&result)
|
||||
{ }
|
||||
|
||||
virtual void make_point (const db::Point &, const db::Edge &e1, const db::Edge &e2) const
|
||||
{
|
||||
mp_result->push_back (db::EdgePair (e1, e2));
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<db::EdgePair> *mp_result;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class implementing the core corner detection algorithm
|
||||
*/
|
||||
|
|
@ -155,6 +175,31 @@ public:
|
|||
virtual bool wants_variants () const { return false; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A corner detector delivering edge pairs for the corners
|
||||
*/
|
||||
class DB_PUBLIC CornersAsEdgePairs
|
||||
: public db::PolygonToEdgePairProcessorBase, private CornerDetectorCore
|
||||
{
|
||||
public:
|
||||
CornersAsEdgePairs (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
|
||||
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void process (const db::Polygon &poly, std::vector<db::EdgePair> &result) const
|
||||
{
|
||||
detect_corners (poly, CornerEdgePairDelivery (result));
|
||||
}
|
||||
|
||||
virtual const TransformationReducer *vars () const { return 0; }
|
||||
virtual bool result_is_merged () const { return false; }
|
||||
virtual bool result_must_not_be_merged () const { return true; } // to preserve dots
|
||||
virtual bool requires_raw_input () const { return false; }
|
||||
virtual bool wants_variants () const { return false; }
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Extents
|
||||
|
||||
|
|
|
|||
|
|
@ -226,6 +226,12 @@ static db::CompoundRegionOperationNode *new_corners_as_dots (db::CompoundRegionO
|
|||
return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/);
|
||||
}
|
||||
|
||||
static db::CompoundRegionOperationNode *new_corners_as_edge_pairs (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
|
||||
{
|
||||
check_non_null (input, "input");
|
||||
return new db::CompoundRegionToEdgePairProcessingOperationNode (new db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/);
|
||||
}
|
||||
|
||||
static db::CompoundRegionOperationNode *new_extents (db::CompoundRegionOperationNode *input, db::Coord e)
|
||||
{
|
||||
check_non_null (input, "input");
|
||||
|
|
@ -603,6 +609,12 @@ Class<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode ("db", "
|
|||
gsi::constructor ("new_corners_as_dots", &new_corners_as_dots, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"),
|
||||
"@brief Creates a node turning corners into dots (single-point edges).\n"
|
||||
) +
|
||||
gsi::constructor ("new_corners_as_edge_pairs", &new_corners_as_edge_pairs, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"),
|
||||
"@brief Creates a node turning corners into edge pairs containing the two edges adjacent to the corner.\n"
|
||||
"The first edge will be the incoming edge and the second one the outgoing edge.\n"
|
||||
"\n"
|
||||
"This feature has been introduced in version 0.27.1.\n"
|
||||
) +
|
||||
gsi::constructor ("new_extents", &new_extents, gsi::arg ("input"), gsi::arg ("e", 0),
|
||||
"@brief Creates a node returning the extents of the objects.\n"
|
||||
"The 'e' parameter provides a generic enlargement which is applied to the boxes. This is helpful to cover dot-like edges or edge pairs in the input."
|
||||
|
|
|
|||
|
|
@ -123,6 +123,11 @@ static db::Region corners_to_boxes (const db::Region *r, double angle_start, dou
|
|||
return r->processed (db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, dim));
|
||||
}
|
||||
|
||||
static db::EdgePairs corners_to_edge_pairs (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end)
|
||||
{
|
||||
return r->processed (db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end));
|
||||
}
|
||||
|
||||
static db::Region *new_si (const db::RecursiveShapeIterator &si)
|
||||
{
|
||||
return new db::Region (si);
|
||||
|
|
@ -1373,14 +1378,22 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"A similar function that reports corners as point-like edges is \\corners_dots.\n"
|
||||
"\n"
|
||||
"This function has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
|
||||
"This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
|
||||
) +
|
||||
method_ext ("corners_dots", &corners_to_dots, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true),
|
||||
"@brief This method will select all corners whose attached edges satisfy the angle condition.\n"
|
||||
"\n"
|
||||
"This method is similar to \\corners, but delivers an \\Edges collection with dot-like edges for each corner.\n"
|
||||
"\n"
|
||||
"This function has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
|
||||
"This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
|
||||
) +
|
||||
method_ext ("corners_edge_pairs", &corners_to_edge_pairs, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true),
|
||||
"@brief This method will select all corners whose attached edges satisfy the angle condition.\n"
|
||||
"\n"
|
||||
"This method is similar to \\corners, but delivers an \\EdgePairs collection with an edge pairs for each corner.\n"
|
||||
"The first edge is the incoming edge of the corner, the second one the outgoing edge.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.1.\n"
|
||||
) +
|
||||
method ("merge", (db::Region &(db::Region::*) ()) &db::Region::merge,
|
||||
"@brief Merge the region\n"
|
||||
|
|
|
|||
|
|
@ -787,14 +787,14 @@ CODE
|
|||
# The "corners" method is available as a plain function or as a method on \DRC# expressions.
|
||||
# The plain function is equivalent to "primary.corners".
|
||||
|
||||
def corners(as_dots = DRCAsDots::new(false))
|
||||
def corners(output_mode = DRCOutputMode::new(:dots))
|
||||
@engine._context("corners") do
|
||||
if as_dots.is_a?(DRCAsDots)
|
||||
as_dots = as_dots.value
|
||||
if output_mode.is_a?(DRCOutputMode)
|
||||
output_mode = output_mode.value
|
||||
else
|
||||
raise("Invalid argument (#{as_dots.inspect}) for 'corners' method")
|
||||
end
|
||||
DRCOpNodeCornersFilter::new(@engine, as_dots, self)
|
||||
DRCOpNodeCornersFilter::new(@engine, output_mode, self)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -859,8 +859,8 @@ CODE
|
|||
args.each_with_index do |a,ia|
|
||||
if a.is_a?(1.0.class) && :#{f} != :middle
|
||||
f << a
|
||||
elsif a.is_a?(DRCAsDots)
|
||||
as_edges = a.value
|
||||
elsif a.is_a?(DRCOutputMode)
|
||||
as_edges = (a.value == :edges || a.value == :dots)
|
||||
elsif @@std_refs[a] && :#{f} != :middle
|
||||
f = @@std_refs[a]
|
||||
else
|
||||
|
|
@ -1996,11 +1996,11 @@ end
|
|||
class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
|
||||
|
||||
attr_accessor :input
|
||||
attr_accessor :as_dots
|
||||
attr_accessor :output_mode
|
||||
|
||||
def initialize(engine, as_dots, input)
|
||||
def initialize(engine, output_mode, input)
|
||||
super(engine)
|
||||
self.as_dots = as_dots
|
||||
self.output_mode = output_mode
|
||||
self.input = input
|
||||
self.description = "corners"
|
||||
end
|
||||
|
|
@ -2011,8 +2011,10 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
|
|||
args << (self.gt ? false : true)
|
||||
args << (self.lt ? self.lt : (self.le ? self.le : 180.0))
|
||||
args << (self.lt ? false : true)
|
||||
if self.as_dots
|
||||
if self.output_mode == :dots || self.output_mode == :edges
|
||||
RBA::CompoundRegionOperationNode::new_corners_as_dots(*args)
|
||||
elsif self.output_mode == :edge_pairs
|
||||
RBA::CompoundRegionOperationNode::new_corners_as_edge_pairs(*args)
|
||||
else
|
||||
args << 2 # dimension is 2x2 DBU
|
||||
RBA::CompoundRegionOperationNode::new_corners_as_rectangles(*args)
|
||||
|
|
|
|||
|
|
@ -781,7 +781,7 @@ CODE
|
|||
|
||||
# %DRC%
|
||||
# @name corners
|
||||
# @brief Selects all polygons which are rectilinear
|
||||
# @brief Selects corners of polygons
|
||||
# @synopsis corners([ options ]) (in condition)
|
||||
# @synopsis corners(layer [, options ])
|
||||
#
|
||||
|
|
@ -791,15 +791,16 @@ CODE
|
|||
# \DRC# expressions (see \Layer#drc and \DRC#corners for more details).
|
||||
#
|
||||
# Like the layer-based version, the "corners" operator accepts the
|
||||
# output type option: "as_dots" for dot-like edges and "as_boxes" for
|
||||
# small (2x2 DBU) box markers.
|
||||
# output type option: "as_dots" for dot-like edges, "as_boxes" for
|
||||
# small (2x2 DBU) box markers and "as_edge_pairs" for edge pairs.
|
||||
# The default output type is "as_boxes".
|
||||
#
|
||||
# The "corners" operator can be put into a condition which means it's
|
||||
# applied to corners meeting a particular angle constraint.
|
||||
|
||||
def _cop_corners(as_dots = DRCAsDots::new(false))
|
||||
def _cop_corners(output_mode = DRCOutputMode::new(:boxes))
|
||||
# NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here.
|
||||
return primary.corners(as_dots)
|
||||
return primary.corners(output_mode)
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
|
|||
|
|
@ -226,15 +226,19 @@ module DRC
|
|||
end
|
||||
|
||||
def as_dots
|
||||
DRCAsDots::new(true)
|
||||
DRCOutputMode::new(:dots)
|
||||
end
|
||||
|
||||
def as_edges
|
||||
DRCAsDots::new(true)
|
||||
DRCOutputMode::new(:edges)
|
||||
end
|
||||
|
||||
def as_boxes
|
||||
DRCAsDots::new(false)
|
||||
DRCOutputMode::new(:boxes)
|
||||
end
|
||||
|
||||
def as_edge_pairs
|
||||
DRCOutputMode::new(:edge_pairs)
|
||||
end
|
||||
|
||||
def area_only(r)
|
||||
|
|
|
|||
|
|
@ -1118,8 +1118,8 @@ CODE
|
|||
elsif a.is_a?(DRCPattern)
|
||||
as_pattern = a.as_pattern
|
||||
pattern = a.pattern
|
||||
elsif a.is_a?(DRCAsDots)
|
||||
as_dots = a.value
|
||||
elsif a.is_a?(DRCOutputMode)
|
||||
as_dots = (a.value == :edges || a.value == :dots)
|
||||
else
|
||||
raise("Invalid argument type #{a.inspect}")
|
||||
end
|
||||
|
|
@ -1167,6 +1167,8 @@ CODE
|
|||
# @ul
|
||||
# @li @b as_boxes @/b: with this option, small boxes will be produced as markers @/li
|
||||
# @li @b as_dots @/b: with this option, point-like edges will be produced instead of small boxes @/li
|
||||
# @li @b as_edge_pairs @/b: with this option, an edge pair is produced for each corner selected. The first edge
|
||||
# is the incoming edge to the corner, the second edge the outgoing edge. @/li
|
||||
# @/ul
|
||||
#
|
||||
# The following images show the effect of this method:
|
||||
|
|
@ -1185,7 +1187,7 @@ CODE
|
|||
|
||||
requires_region
|
||||
|
||||
as_dots = false
|
||||
output_mode = :boxes
|
||||
amin = -180.0
|
||||
amax = 180.0
|
||||
|
||||
|
|
@ -1199,14 +1201,23 @@ CODE
|
|||
elsif a.is_a?(1.0.class) || a.is_a?(1.class)
|
||||
amin = a.to_f
|
||||
amax = a.to_f
|
||||
elsif a.is_a?(DRCAsDots)
|
||||
as_dots = a.value
|
||||
elsif a.is_a?(DRCOutputMode)
|
||||
output_mode = a.value
|
||||
else
|
||||
raise("Invalid argument #{a.inspect}")
|
||||
end
|
||||
end
|
||||
|
||||
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, as_dots ? :corners_dots : :corners, amin, amax))
|
||||
f = :corners
|
||||
cls = RBA::Region
|
||||
if output_mode == :edges || output_mode == :dots
|
||||
f = :corners_dots
|
||||
cls = RBA::Edges
|
||||
elsif output_mode == :edge_pairs
|
||||
f = :corners_edge_pairs
|
||||
cls = RBA::EdgePairs
|
||||
end
|
||||
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, cls, f, amin, amax))
|
||||
|
||||
end
|
||||
|
||||
|
|
@ -1375,8 +1386,8 @@ CODE
|
|||
args.each do |a|
|
||||
if a.is_a?(1.0.class) && :#{f} != :middle
|
||||
f << a
|
||||
elsif a.is_a?(DRCAsDots)
|
||||
as_edges = a.value
|
||||
elsif a.is_a?(DRCOutputMode)
|
||||
as_edges = (a.value == :edges || a.value == :dots)
|
||||
elsif @@std_refs[a] && :#{f} != :middle
|
||||
f = @@std_refs[a]
|
||||
else
|
||||
|
|
@ -4736,7 +4747,7 @@ END
|
|||
end
|
||||
|
||||
def requires_edge_pairs
|
||||
self.data.is_a?(RBA::EdgePairs) || raise("Requires a edge pair layer")
|
||||
self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge pair layer")
|
||||
end
|
||||
|
||||
def requires_edges
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ module DRC
|
|||
# A wrapper for the "as_dots" or "as_boxes" flag for
|
||||
# some DRC functions. The purpose of this class
|
||||
# is to identify the value by the class.
|
||||
class DRCAsDots
|
||||
class DRCOutputMode
|
||||
attr_accessor :value
|
||||
def initialize(v)
|
||||
self.value = v
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@ See <a href="/about/drc_ref_netter.xml#connect_global">Netter#connect_global</a>
|
|||
<p>
|
||||
See <a href="/about/drc_ref_netter.xml#connect_implicit">Netter#connect_implicit</a> for a description of that function.
|
||||
</p>
|
||||
<a name="corners"/><h2>"corners" - Selects all polygons which are rectilinear</h2>
|
||||
<a name="corners"/><h2>"corners" - Selects corners of polygons</h2>
|
||||
<keyword name="corners"/>
|
||||
<p>Usage:</p>
|
||||
<ul>
|
||||
|
|
@ -356,8 +356,9 @@ argument, "corners" represents the corner generator/filter in primary shapes for
|
|||
<a href="/about/drc_ref_drc.xml">DRC</a> expressions (see <a href="/about/drc_ref_layer.xml#drc">Layer#drc</a> and <a href="/about/drc_ref_drc.xml#corners">DRC#corners</a> for more details).
|
||||
</p><p>
|
||||
Like the layer-based version, the "corners" operator accepts the
|
||||
output type option: "as_dots" for dot-like edges and "as_boxes" for
|
||||
small (2x2 DBU) box markers.
|
||||
output type option: "as_dots" for dot-like edges, "as_boxes" for
|
||||
small (2x2 DBU) box markers and "as_edge_pairs" for edge pairs.
|
||||
The default output type is "as_boxes".
|
||||
</p><p>
|
||||
The "corners" operator can be put into a condition which means it's
|
||||
applied to corners meeting a particular angle constraint.
|
||||
|
|
|
|||
|
|
@ -263,6 +263,8 @@ The options available are:
|
|||
<ul>
|
||||
<li><b>as_boxes </b>: with this option, small boxes will be produced as markers </li>
|
||||
<li><b>as_dots </b>: with this option, point-like edges will be produced instead of small boxes </li>
|
||||
<li><b>as_edge_pairs </b>: with this option, an edge pair is produced for each corner selected. The first edge
|
||||
is the incoming edge to the corner, the second edge the outgoing edge. </li>
|
||||
</ul>
|
||||
</p><p>
|
||||
The following images show the effect of this method:
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ l1.drc(-90 < corners(as_dots) <= 90.0).output(101, 0)
|
|||
l1.drc(corners(as_boxes) == -90).output(102, 0) # outer corners
|
||||
l1.drc(corners(as_boxes) == 90).output(103, 0) # inner corners
|
||||
l1.drc(corners(as_boxes) <= -90).output(104, 0)
|
||||
l1.drc(corners(as_edge_pairs) == 90).output(105, 0)
|
||||
|
||||
l1.drc(middle).output(110, 0)
|
||||
l1.drc(middle(as_dots)).output(111, 0)
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -66,6 +66,9 @@ a1.extent_refs(0.25, 0.5, 0.5, 0.75).output(1053, 0)
|
|||
a1.corners.sized(0.05).output(1060, 0)
|
||||
a1.corners(-90.0, as_boxes).sized(0.05).output(1061, 0)
|
||||
a1.corners(-90.0, as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1062, 0)
|
||||
a1.corners(-90.0, as_edge_pairs).polygons(0).output(1063, 0)
|
||||
a1.corners(-90.0, as_edge_pairs).first_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1064, 0)
|
||||
a1.corners(-90.0, as_edge_pairs).second_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1065, 0)
|
||||
|
||||
a1.select { |p| p.bbox.width < 0.8 }.output(1100, 0)
|
||||
a1.collect { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1101, 0)
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue