Implemented edge pair filters, DRC: with(out)_angle, with(out)_length and with(out)_distance

This commit is contained in:
Matthias Koefferlein 2021-05-28 23:46:52 +02:00
parent 931b3ef1ce
commit c8548709bb
18 changed files with 695 additions and 44 deletions

View File

@ -24,6 +24,7 @@ SOURCES = \
dbCompoundOperation.cc \
dbEdge.cc \
dbEdgePair.cc \
dbEdgePairFilters.cc \
dbEdgePairRelations.cc \
dbEdgePairs.cc \
dbEdgeProcessor.cc \
@ -233,6 +234,7 @@ HEADERS = \
dbCompoundOperation.h \
dbEdge.h \
dbEdgePair.h \
dbEdgePairFilters.h \
dbEdgePairRelations.h \
dbEdgePairs.h \
dbEdgeProcessor.h \

View File

@ -346,16 +346,87 @@ EdgePairsDelegate *DeepEdgePairs::add (const EdgePairs &other) const
}
}
EdgePairsDelegate *DeepEdgePairs::filter_in_place (const EdgePairFilterBase &filter)
EdgePairsDelegate *
DeepEdgePairs::filter_in_place (const EdgePairFilterBase &filter)
{
// TODO: implement
return AsIfFlatEdgePairs::filter_in_place (filter);
// TODO: implement to be really in-place
*this = *apply_filter (filter);
return this;
}
EdgePairsDelegate *DeepEdgePairs::filtered (const EdgePairFilterBase &filter) const
EdgePairsDelegate *
DeepEdgePairs::filtered (const EdgePairFilterBase &filter) const
{
// TODO: implement
return AsIfFlatEdgePairs::filtered (filter);
return apply_filter (filter);
}
DeepEdgePairs *
DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const
{
const db::DeepLayer &edge_pairs = deep_layer ();
std::unique_ptr<VariantsCollectorBase> vars;
if (filter.vars ()) {
vars.reset (new db::VariantsCollectorBase (filter.vars ()));
vars->collect (edge_pairs.layout (), edge_pairs.initial_cell ());
if (filter.wants_variants ()) {
const_cast<db::DeepLayer &> (edge_pairs).separate_variants (*vars);
}
}
db::Layout &layout = const_cast<db::Layout &> (edge_pairs.layout ());
std::map<db::cell_index_type, std::map<db::ICplxTrans, db::Shapes> > to_commit;
std::unique_ptr<db::DeepEdgePairs> res (new db::DeepEdgePairs (edge_pairs.derived ()));
for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
const db::Shapes &s = c->shapes (edge_pairs.layer ());
if (vars.get ()) {
const std::map<db::ICplxTrans, size_t> &vv = vars->variants (c->cell_index ());
for (std::map<db::ICplxTrans, size_t>::const_iterator v = vv.begin (); v != vv.end (); ++v) {
db::Shapes *st;
if (vv.size () == 1) {
st = & c->shapes (res->deep_layer ().layer ());
} else {
st = & to_commit [c->cell_index ()] [v->first];
}
const db::ICplxTrans &tr = v->first;
for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::EdgePairs); ! si.at_end (); ++si) {
if (filter.selected (si->edge_pair ().transformed (tr))) {
st->insert (*si);
}
}
}
} else {
db::Shapes &st = c->shapes (res->deep_layer ().layer ());
for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::EdgePairs); ! si.at_end (); ++si) {
if (filter.selected (si->edge_pair ())) {
st.insert (*si);
}
}
}
}
if (! to_commit.empty () && vars.get ()) {
res->deep_layer ().commit_shapes (*vars, to_commit);
}
return res.release ();
}
RegionDelegate *

View File

@ -104,6 +104,7 @@ private:
void init ();
EdgesDelegate *generic_edges (bool first, bool second) const;
DeepEdgePairs *apply_filter (const EdgePairFilterBase &filter) const;
};
}

View File

@ -0,0 +1,100 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2021 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 "dbCommon.h"
#include "dbEdgePairFilters.h"
#include "dbEdges.h"
namespace db
{
// ---------------------------------------------------------------------------------------------------
// EdgeFilterBasedEdgePairFilter implementation
EdgeFilterBasedEdgePairFilter::EdgeFilterBasedEdgePairFilter (EdgeFilterBase *edge_filter, bool one_must_match)
: mp_edge_filter (edge_filter), m_one_must_match (one_must_match)
{
// .. nothing yet ..
}
EdgeFilterBasedEdgePairFilter::~EdgeFilterBasedEdgePairFilter ()
{
// .. nothing yet ..
}
bool EdgeFilterBasedEdgePairFilter::selected (const db::EdgePair &edge_pair) const
{
if (m_one_must_match) {
return mp_edge_filter->selected (edge_pair.first ()) || mp_edge_filter->selected (edge_pair.second ());
} else {
return mp_edge_filter->selected (edge_pair.first ()) && mp_edge_filter->selected (edge_pair.second ());
}
}
const TransformationReducer *EdgeFilterBasedEdgePairFilter::vars () const
{
return mp_edge_filter->vars ();
}
bool EdgeFilterBasedEdgePairFilter::wants_variants () const
{
return mp_edge_filter->wants_variants ();
}
// ---------------------------------------------------------------------------------------------------
// EdgePairFilterByDistance implementation
EdgePairFilterByDistance::EdgePairFilterByDistance (distance_type min_distance, distance_type max_distance, bool inverted)
: m_min_distance (min_distance), m_max_distance (max_distance), m_inverted (inverted)
{
// .. nothing yet ..
}
static db::coord_traits<db::Coord>::distance_type
distance_of_point_from_edge (const db::Edge &e, const db::Point &p)
{
if (db::sprod_sign (p - e.p1 (), e.d ()) < 0) {
return e.p1 ().distance (p);
} else if (db::sprod_sign (p - e.p2 (), e.d ()) > 0) {
return e.p2 ().distance (p);
} else {
return e.distance (p);
}
}
bool EdgePairFilterByDistance::selected (const db::EdgePair &edge_pair) const
{
distance_type dist = 0;
db::Edge e1 = edge_pair.first (), e2 = edge_pair.second ();
if (! e1.intersect (e2)) {
distance_type d12 = std::min (distance_of_point_from_edge (e2, e1.p1 ()), distance_of_point_from_edge (e2, e1.p2 ()));
distance_type d21 = std::min (distance_of_point_from_edge (e1, e2.p1 ()), distance_of_point_from_edge (e1, e2.p2 ()));
dist = std::min (d12, d21);
}
bool sel = (dist >= m_min_distance && dist < m_max_distance);
return m_inverted ? !sel : sel;
}
}

View File

@ -0,0 +1,82 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2021 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
*/
#ifndef HDR_dbEdgePairFilters
#define HDR_dbEdgePairFilters
#include "dbEdgePairs.h"
namespace db
{
class EdgeFilterBase;
/**
* @brief A base class for edge pair filters based on edge filters
*
* If "one_must_match" is true, it is sufficient for one edge to be selected.
* If it is false, both edges need to be selected to make the edge pair selected.
*
* NOTE: the edge filter is not owned by the edge pair filter object.
*/
class DB_PUBLIC EdgeFilterBasedEdgePairFilter
: public EdgePairFilterBase
{
public:
EdgeFilterBasedEdgePairFilter (EdgeFilterBase *edge_filter, bool one_must_match);
virtual ~EdgeFilterBasedEdgePairFilter ();
virtual bool selected (const db::EdgePair &edge_pair) const;
virtual const TransformationReducer *vars () const;
virtual bool wants_variants () const;
private:
EdgeFilterBase *mp_edge_filter;
bool m_one_must_match;
};
/**
* @brief Filters edge pairs based on the distance of the edges.
*
* The distance is measured as the smallest distance between each of the points of the two edges.
*/
class DB_PUBLIC EdgePairFilterByDistance
: public EdgePairFilterBase
{
public:
typedef db::coord_traits<db::Coord>::distance_type distance_type;
EdgePairFilterByDistance (distance_type min_distance, distance_type max_distance, bool inverted);
virtual bool selected (const db::EdgePair &edge_pair) const;
virtual const TransformationReducer *vars () const { return 0; }
virtual bool wants_variants () const { return false; }
private:
distance_type m_min_distance, m_max_distance;
bool m_inverted;
};
}
#endif

View File

@ -38,6 +38,9 @@
namespace db
{
// ---------------------------------------------------------------------------------------------------
// EdgePairs implementation
EdgePairs::EdgePairs ()
: mp_delegate (new EmptyEdgePairs ())
{

View File

@ -42,17 +42,13 @@ class Edges;
class Region;
class DeepShapeStore;
class TransformationReducer;
class EdgeFilterBase;
/**
* @brief An edge pair set iterator
*
* The iterator delivers the edge pairs of the edge pair set
*/
/**
* @brief An edge set iterator
*
* The iterator delivers the edges of the edge set
*/
class DB_PUBLIC EdgePairsIterator
: public generic_shape_iterator<db::EdgePair>
{
@ -117,7 +113,7 @@ public:
EdgePairFilterBase () { }
virtual ~EdgePairFilterBase () { }
virtual bool selected (const db::EdgePair &edge) const = 0;
virtual bool selected (const db::EdgePair &edge_pair) const = 0;
virtual const TransformationReducer *vars () const = 0;
virtual bool wants_variants () const = 0;
};

View File

@ -27,6 +27,8 @@
#include "dbEdges.h"
#include "dbRegion.h"
#include "dbDeepEdgePairs.h"
#include "dbEdgesUtils.h"
#include "dbEdgePairFilters.h"
namespace gsi
{
@ -176,6 +178,74 @@ static size_t id (const db::EdgePairs *ep)
return tl::id_of (ep->delegate ());
}
static db::EdgePairs with_distance1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse)
{
db::EdgePairFilterByDistance ef (length, length + 1, inverse);
return r->filtered (ef);
}
static db::EdgePairs with_distance2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse)
{
db::EdgePairFilterByDistance ef (min.is_nil () ? db::Edges::distance_type (0) : min.to<db::Edges::distance_type> (), max.is_nil () ? std::numeric_limits <db::Edges::distance_type>::max () : max.to<db::Edges::distance_type> (), inverse);
return r->filtered (ef);
}
static db::EdgePairs with_length1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse)
{
db::EdgeLengthFilter f (length, length + 1, inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_length2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse)
{
db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to<db::Edges::distance_type> (), max.is_nil () ? std::numeric_limits <db::Edges::distance_type>::max () : max.to<db::Edges::distance_type> (), inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_length_both1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse)
{
db::EdgeLengthFilter f (length, length + 1, inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_length_both2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse)
{
db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to<db::Edges::distance_type> (), max.is_nil () ? std::numeric_limits <db::Edges::distance_type>::max () : max.to<db::Edges::distance_type> (), inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_angle1 (const db::EdgePairs *r, double a, bool inverse)
{
db::EdgeOrientationFilter f (a, inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax)
{
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool inverse)
{
db::EdgeOrientationFilter f (a, inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax)
{
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/);
return r->filtered (ef);
}
extern Class<db::ShapeCollection> decl_dbShapeCollection;
Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
@ -502,6 +572,112 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"The boxes will not be merged, so it is possible to determine overlaps "
"of these boxes for example.\n"
) +
method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by length of one of their edges\n"
"Filters the edge pairs in the edge pair collection by length of at least one of their edges. If \"inverse\" is false, only "
"edge pairs with at least one edge having the given length are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_length", with_length2, gsi::arg ("min_length"), gsi::arg ("max_length"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by length of one of their edges\n"
"Filters the edge pairs in the edge pair collection by length of at least one of their edges. If \"inverse\" is false, only "
"edge pairs with at least one edge having a length between min_length and max_length (excluding max_length itself) are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"If you don't want to specify a lower or upper limit, pass nil to that parameter.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_length_both", with_length_both1, gsi::arg ("length"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by length of both of their edges\n"
"Filters the edge pairs in the edge pair collection by length of both of their edges. If \"inverse\" is false, only "
"edge pairs where both edges have the given length are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_length_both", with_length_both2, gsi::arg ("min_length"), gsi::arg ("max_length"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by length of both of their edges\n"
"Filters the edge pairs in the edge pair collection by length of both of their edges. If \"inverse\" is false, only "
"edge pairs with both edges having a length between min_length and max_length (excluding max_length itself) are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"If you don't want to specify a lower or upper limit, pass nil to that parameter.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_distance", with_distance1, gsi::arg ("distance"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by the distance of the edges\n"
"Filters the edge pairs in the edge pair collection by distance of the edges. If \"inverse\" is false, only "
"edge pairs where both edges have the given distance are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"Distance is measured as the shortest distance between any of the points on the edges.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_distance", with_distance2, gsi::arg ("min_distance"), gsi::arg ("max_distance"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by the distance of the edges\n"
"Filters the edge pairs in the edge pair collection by distance of the edges. If \"inverse\" is false, only "
"edge pairs where both edges have a distance between min_distance and max_distance (max_distance itself is excluded). If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"Distance is measured as the shortest distance between any of the points on the edges.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"),
"@brief Filter the edge pairs by orientation of their edges\n"
"Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only "
"edge pairs with at least one edge having the given angle to the x-axis are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"This will filter edge pairs with at least one horizontal edge:\n"
"\n"
"@code\n"
"horizontal = edge_pairs.with_orientation(0, false)\n"
"@/code\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false),
"@brief Filter the edge pairs by orientation of their edges\n"
"Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only "
"edge pairs with at least one edge having an angle between min_angle and max_angle are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"With \"include_min_angle\" 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_angle\" where the default is false, meaning the maximum angle is not included in the range.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_angle_both", with_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"),
"@brief Filter the edge pairs by orientation of both of their edges\n"
"Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only "
"edge pairs with both edges having the given angle to the x-axis are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"This will filter edge pairs with at least one horizontal edge:\n"
"\n"
"@code\n"
"horizontal = edge_pairs.with_orientation(0, false)\n"
"@/code\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_angle_both", with_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false),
"@brief Filter the edge pairs by orientation of both of their edges\n"
"Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only "
"edge pairs with both edges having an angle between min_angle and max_angle are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"With \"include_min_angle\" 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_angle\" where the default is false, meaning the maximum angle is not included in the range.\n"
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("polygons", &polygons1,
"@brief Converts the edge pairs to polygons\n"
"This method creates polygons from the edge pairs. Each polygon will be a triangle or quadrangle "

View File

@ -575,10 +575,10 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"edges which have the given angle to the x-axis are returned. If \"inverse\" is true, "
"edges not having the given angle are returned.\n"
"\n"
"This will filter horizontal edges:\n"
"This will select horizontal edges:\n"
"\n"
"@code\n"
"horizontal = edges.with_orientation(0, true)\n"
"horizontal = edges.with_orientation(0, false)\n"
"@/code\n"
) +
method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false),

View File

@ -64,6 +64,10 @@ module DRC
end
def both
DRCBothEdges::new
end
def shift(x, y)
self._context("shift") do
RBA::DCplxTrans::new(RBA::DVector::new(_make_value(x) * self.dbu, _make_value(y) * self.dbu))
@ -1793,6 +1797,7 @@ CODE
with_bbox_min
with_bbox_width
with_length
with_distance
without_angle
without_area
without_area_ratio

View File

@ -699,13 +699,19 @@ CODE
# @synopsis layer.with_length(min .. max)
# @synopsis layer.with_length(value)
# @synopsis layer.with_length(min, max)
# The method selects edges by their length. The first version selected
# @synopsis edge_pairlayer.with_length(min, max [, both])
# The method selects edges by their length. The first version selects
# edges with a length larger or equal to min and less than max (but not equal).
# The second version selects edges with exactly the given length. The third
# version is similar to the first one, but allows specification of nil for min or
# max indicating that there is no lower or upper limit.
#
# This method is available for edge layers only.
# When called on an edge pair layer, this method will select edge pairs if one
# or both of the edges meet the length criterion. Use the additional argument
# and pass "both" (plain word) to specify that both edges need to be within the given interval.
# By default, it's sufficient for one edge to meet the criterion.
#
# This method is available for edge and edge pair layers.
# %DRC%
# @name without_length
@ -713,11 +719,12 @@ CODE
# @synopsis layer.without_length(min .. max)
# @synopsis layer.without_length(value)
# @synopsis layer.without_length(min, max)
# @synopsis edge_pairlayer.with_length(min, max [, both])
# The method basically is the inverse of \with_length. It selects all edges
# of the edge layer which do not have the given length (second form) or are
# not inside the given interval (first and third form).
#
# This method is available for edge layers only.
# This method is available for edge and edge pair layers.
%w(length).each do |f|
[true, false].each do |inv|
@ -727,18 +734,96 @@ CODE
@engine._context("#{mn}") do
requires_edges
requires_edges_or_edge_pairs
result_class = self.data.class
f = :with_#{f}
args = args.select do |a|
if a.is_a?(DRCBothEdges)
if !self.data.is_a?(RBA::EdgePairs)
raise("'both' keyword only available for edge pair layers")
end
f = :with_#{f}_both
false
else
true
end
end
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._make_value_with_nil(a.begin), @engine._make_value_with_nil(a.end), #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_value_with_nil(a.begin), @engine._make_value_with_nil(a.end), #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._make_value(a), #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_value(a), #{inv.inspect}))
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._make_value_with_nil(args[0]), @engine._make_value_with_nil(args[1]), #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_value_with_nil(args[0]), @engine._make_value_with_nil(args[1]), #{inv.inspect}))
else
raise("Invalid number of arguments (1 or 2 expected)")
raise("Invalid number of range arguments (1 or 2 expected)")
end
end
end
CODE
end
end
# %DRC%
# @name with_distance
# @brief Selects edge pairs by the distance of the edges
# @synopsis layer.with_distance(min .. max)
# @synopsis layer.with_distance(value)
# @synopsis layer.with_distance(min, max)
# The method selects edge pairs by the distance of their edges. The first version selects
# edge pairs with a distance larger or equal to min and less than max (but not equal).
# The second version selects edge pairs with exactly the given distance. The third
# version is similar to the first one, but allows specification of nil for min or
# max indicating that there is no lower or upper limit.
#
# The distance of the edges is defined by the minimum distance of all points from the
# edges involved. For edge pairs generated in geometrical checks this is equivalent
# to the actual distance of the original edges.
#
# This method is available for edge pair layers only.
# %DRC%
# @name without_distance
# @brief Selects edge pairs by the distance of the edges
# @synopsis layer.without_distance(min .. max)
# @synopsis layer.without_distance(value)
# @synopsis layer.without_distance(min, max)
# The method basically is the inverse of \with_distance. It selects all edge pairs
# of the edge pair layer which do not have the given distance (second form) or are
# not inside the given interval (first and third form).
#
# This method is available for edge pair layers only.
%w(distance).each do |f|
[true, false].each do |inv|
mn = (inv ? "without" : "with") + "_" + f
eval <<"CODE"
def #{mn}(*args)
@engine._context("#{mn}") do
requires_edge_pairs
result_class = RBA::EdgePairs
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_#{f}, @engine._make_value_with_nil(a.begin), @engine._make_value_with_nil(a.end), #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_#{f}, @engine._make_value(a), #{inv.inspect}))
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_#{f}, @engine._make_value_with_nil(args[0]), @engine._make_value_with_nil(args[1]), #{inv.inspect}))
else
raise("Invalid number of range arguments (1 or 2 expected)")
end
end
@ -754,6 +839,7 @@ CODE
# @synopsis layer.with_angle(min .. max)
# @synopsis layer.with_angle(value)
# @synopsis layer.with_angle(min, max)
# @synopsis edge_pairlayer.with_angle(min, max [, both])
#
# When called on an edge layer, the method selects edges by their angle,
# measured against the horizontal axis in the mathematical sense.
@ -771,6 +857,11 @@ CODE
# given angle or is within the given angle interval. The angle is measured between the edges forming the corner.
# For each corner, an edge pair containing the edges forming in the angle is returned.
#
# When called on an edge pair layer, this method selects edge pairs with one or both edges
# meeting the angle criterion. In this case an additional argument is accepted which can be
# either "both" (plain word) to indicate that both edges have to be within the given interval.
# Without this argument, it is sufficient for one edge to meet the criterion.
#
# A method delivering all objects not matching the angle criterion is \without_angle.
#
# The following images demonstrate some use cases of \with_angle and \without_angle:
@ -792,37 +883,52 @@ CODE
# @synopsis layer.without_angle(min .. max)
# @synopsis layer.without_angle(value)
# @synopsis layer.without_angle(min, max)
# @synopsis edge_pairlayer.without_angle(min, max [, both])
#
# The method basically is the inverse of \with_angle. It selects all edges
# of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle
# is not inside the given interval (first and third form).
[true, false].each do |inv|
mn = (inv ? "without" : "with") + "_angle"
eval <<"CODE"
def #{mn}(*args)
%w(angle).each do |f|
[true, false].each do |inv|
mn = (inv ? "without" : "with") + "_" + f
eval <<"CODE"
def #{mn}(*args)
@engine._context("#{mn}") do
@engine._context("#{mn}") do
requires_edges_or_region
result_class = self.data.is_a?(RBA::Region) ? RBA::EdgePairs : RBA::Edges
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a.begin, a.end, #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a, #{inv.inspect}))
f = :with_#{f}
args = args.select do |a|
if a.is_a?(DRCBothEdges)
if !self.data.is_a?(RBA::EdgePairs)
raise("'both' keyword only available for edge pair layers")
end
f = :with_#{f}_both
false
else
true
end
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, args[0], args[1], #{inv.inspect}))
else
raise("Invalid number of arguments (1 or 2 expected)")
result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.begin, a.end, #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a, #{inv.inspect}))
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, args[0], args[1], #{inv.inspect}))
else
raise("Invalid number of range arguments (1 or 2 expected)")
end
end
end
end
CODE
end
end
# %DRC%
@ -4567,6 +4673,10 @@ END
self.data.is_a?(RBA::Edges) || raise("Requires an edge layer")
end
def requires_edges_or_edge_pairs
self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge or edge pair layer")
end
def requires_edges_or_region
self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || raise("Requires an edge or polygon layer")
end

View File

@ -16,6 +16,10 @@ module DRC
end
end
# A wrapper for the "both edges" flag for EdgePair#with_length or EdgePair#with_angle
class DRCBothEdges
end
# A wrapper for the sizing mode value
class DRCSizingMode
attr_accessor :value

View File

@ -1282,3 +1282,13 @@ TEST(48d_drcWithFragments)
{
run_test (_this, "48", true);
}
TEST(49_drcWithFragments)
{
run_test (_this, "49", false);
}
TEST(49d_drcWithFragments)
{
run_test (_this, "49", true);
}

View File

@ -3158,6 +3158,7 @@ Shielding is enabled by default, but can be switched off with the "transparent"
<li><tt>layer.with_angle(min .. max)</tt></li>
<li><tt>layer.with_angle(value)</tt></li>
<li><tt>layer.with_angle(min, max)</tt></li>
<li><tt>edge_pairlayer.with_angle(min, max [, both])</tt></li>
</ul>
<p>
When called on an edge layer, the method selects edges by their angle,
@ -3176,6 +3177,11 @@ When called on a polygon layer, this method selects corners which match the
given angle or is within the given angle interval. The angle is measured between the edges forming the corner.
For each corner, an edge pair containing the edges forming in the angle is returned.
</p><p>
When called on an edge pair layer, this method selects edge pairs with one or both edges
meeting the angle criterion. In this case an additional argument is accepted which can be
either "both" (plain word) to indicate that both edges have to be within the given interval.
Without this argument, it is sufficient for one edge to meet the criterion.
</p><p>
A method delivering all objects not matching the angle criterion is <a href="#without_angle">without_angle</a>.
</p><p>
The following images demonstrate some use cases of <a href="#with_angle">with_angle</a> and <a href="#without_angle:">without_angle:</a>
@ -3369,6 +3375,27 @@ low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), tile_origin(0.0,
</p><p>
The complementary version of "with_density" is <a href="#without_density">without_density</a>.
</p>
<a name="with_distance"/><h2>"with_distance" - Selects edge pairs by the distance of the edges</h2>
<keyword name="with_distance"/>
<p>Usage:</p>
<ul>
<li><tt>layer.with_distance(min .. max)</tt></li>
<li><tt>layer.with_distance(value)</tt></li>
<li><tt>layer.with_distance(min, max)</tt></li>
</ul>
<p>
The method selects edge pairs by the distance of their edges. The first version selects
edge pairs with a distance larger or equal to min and less than max (but not equal).
The second version selects edge pairs with exactly the given distance. The third
version is similar to the first one, but allows specification of nil for min or
max indicating that there is no lower or upper limit.
</p><p>
The distance of the edges is defined by the minimum distance of all points from the
edges involved. For edge pairs generated in geometrical checks this is equivalent
to the actual distance of the original edges.
</p><p>
This method is available for edge pair layers only.
</p>
<a name="with_holes"/><h2>"with_holes" - Selects all polygons with the specified number of holes</h2>
<keyword name="with_holes"/>
<p>Usage:</p>
@ -3388,15 +3415,21 @@ which have the specified number of holes.
<li><tt>layer.with_length(min .. max)</tt></li>
<li><tt>layer.with_length(value)</tt></li>
<li><tt>layer.with_length(min, max)</tt></li>
<li><tt>edge_pairlayer.with_length(min, max [, both])</tt></li>
</ul>
<p>
The method selects edges by their length. The first version selected
The method selects edges by their length. The first version selects
edges with a length larger or equal to min and less than max (but not equal).
The second version selects edges with exactly the given length. The third
version is similar to the first one, but allows specification of nil for min or
max indicating that there is no lower or upper limit.
</p><p>
This method is available for edge layers only.
When called on an edge pair layer, this method will select edge pairs if one
or both of the edges meet the length criterion. Use the additional argument
and pass "both" (plain word) to specify that both edges need to be within the given interval.
By default, it's sufficient for one edge to meet the criterion.
</p><p>
This method is available for edge and edge pair layers.
</p>
<a name="with_perimeter"/><h2>"with_perimeter" - Selects polygons by perimeter</h2>
<keyword name="with_perimeter"/>
@ -3441,6 +3474,7 @@ This method is available for polygon layers only.
<li><tt>layer.without_angle(min .. max)</tt></li>
<li><tt>layer.without_angle(value)</tt></li>
<li><tt>layer.without_angle(min, max)</tt></li>
<li><tt>edge_pairlayer.without_angle(min, max [, both])</tt></li>
</ul>
<p>
The method basically is the inverse of <a href="#with_angle">with_angle</a>. It selects all edges
@ -3546,6 +3580,21 @@ This method is available for polygon layers only.
For details about the operations and the operation see <a href="#with_density">with_density</a>. This version will return the
tiles where the density is not within the given range.
</p>
<a name="without_distance"/><h2>"without_distance" - Selects edge pairs by the distance of the edges</h2>
<keyword name="without_distance"/>
<p>Usage:</p>
<ul>
<li><tt>layer.without_distance(min .. max)</tt></li>
<li><tt>layer.without_distance(value)</tt></li>
<li><tt>layer.without_distance(min, max)</tt></li>
</ul>
<p>
The method basically is the inverse of <a href="#with_distance">with_distance</a>. It selects all edge pairs
of the edge pair layer which do not have the given distance (second form) or are
not inside the given interval (first and third form).
</p><p>
This method is available for edge pair layers only.
</p>
<a name="without_holes"/><h2>"without_holes" - Selects all polygons with the specified number of holes</h2>
<keyword name="without_holes"/>
<p>Usage:</p>
@ -3565,13 +3614,14 @@ which do not have the specified number of holes.
<li><tt>layer.without_length(min .. max)</tt></li>
<li><tt>layer.without_length(value)</tt></li>
<li><tt>layer.without_length(min, max)</tt></li>
<li><tt>edge_pairlayer.with_length(min, max [, both])</tt></li>
</ul>
<p>
The method basically is the inverse of <a href="#with_length">with_length</a>. It selects all edges
of the edge layer which do not have the given length (second form) or are
not inside the given interval (first and third form).
</p><p>
This method is available for edge layers only.
This method is available for edge and edge pair layers.
</p>
<a name="without_perimeter"/><h2>"without_perimeter" - Selects polygons by perimeter</h2>
<keyword name="without_perimeter"/>

41
testdata/drc/drcSimpleTests_49.drc vendored Normal file
View File

@ -0,0 +1,41 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
ep = input(1, 0).drc(space(projection) < 0.5)
ep.polygons(0).output(100, 0)
ep.with_distance(0..0.1).polygons(0).output(110, 0)
ep.with_distance(0.25, nil).polygons(0).output(111, 0)
ep.without_distance(0..0.1).polygons(0).output(120, 0)
ep.without_distance(0.25, nil).polygons(0).output(121, 0)
ep.with_angle(45.0).polygons(0).output(200, 0)
ep.with_angle(0.0).polygons(0).output(201, 0)
ep.with_angle(45.0..91.0).polygons(0).output(202, 0)
ep.with_angle(45.0, both).polygons(0).output(210, 0)
ep.with_angle(0.0, both).polygons(0).output(211, 0)
ep.with_angle(45.0..91.0, both).polygons(0).output(212, 0)
ep.without_angle(45.0).polygons(0).output(220, 0)
ep.without_angle(0.0).polygons(0).output(221, 0)
ep.without_angle(45.0..91.0).polygons(0).output(222, 0)
ep.without_angle(45.0, both).polygons(0).output(230, 0)
ep.without_angle(0.0, both).polygons(0).output(231, 0)
ep.without_angle(45.0..91.0, both).polygons(0).output(232, 0)
ep.with_length(0.5).polygons(0).output(300, 0)
ep.with_length(0.4..0.51).polygons(0).output(301, 0)
ep.with_length(0.5, both).polygons(0).output(310, 0)
ep.with_length(0.4..0.51, both).polygons(0).output(311, 0)
ep.without_length(0.5).polygons(0).output(320, 0)
ep.without_length(0.4..0.51).polygons(0).output(321, 0)
ep.without_length(0.5, both).polygons(0).output(330, 0)
ep.without_length(0.4..0.51, both).polygons(0).output(331, 0)

BIN
testdata/drc/drcSimpleTests_49.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au49.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au49d.gds vendored Normal file

Binary file not shown.