Merge pull request #847 from KLayout/more-edge-pair-filters

More edge pair filters
This commit is contained in:
Matthias Köfferlein 2021-07-06 23:38:02 +02:00 committed by GitHub
commit fb9c0077eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 435 additions and 47 deletions

View File

@ -78,4 +78,51 @@ bool EdgePairFilterByDistance::selected (const db::EdgePair &edge_pair) const
return m_inverted ? !sel : sel;
}
// ---------------------------------------------------------------------------------------------------
// EdgePairFilterByArea implementation
EdgePairFilterByArea::EdgePairFilterByArea (area_type min_area, area_type max_area, bool inverted)
: m_min_area (min_area), m_max_area (max_area), m_inverted (inverted)
{
// .. nothing yet ..
}
bool EdgePairFilterByArea::selected (const db::EdgePair &edge_pair) const
{
area_type dist = edge_pair.to_simple_polygon (0).area ();
bool sel = (dist >= m_min_area && dist < m_max_area);
return m_inverted ? !sel : sel;
}
// ---------------------------------------------------------------------------------------------------
// EdgePairFilterByArea implementation
InternalAngleEdgePairFilter::InternalAngleEdgePairFilter (double a, bool inverted)
: m_inverted (inverted), m_checker (a, true, a, true)
{
// .. nothing yet ..
}
InternalAngleEdgePairFilter::InternalAngleEdgePairFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverted)
: m_inverted (inverted), m_checker (amin, include_amin, amax, include_amax)
{
// .. nothing yet ..
}
bool
InternalAngleEdgePairFilter::selected (const db::EdgePair &edge_pair) const
{
db::Vector d1 = edge_pair.first ().d ();
db::Vector d2 = edge_pair.second ().d ();
if (db::sprod_sign (d1, d2) < 0) {
d1 = -d1;
}
if (db::vprod_sign (d1, d2) < 0) {
std::swap (d1, d2);
}
return m_checker (d1, d2) != m_inverted;
}
}

View File

@ -25,6 +25,7 @@
#define HDR_dbEdgePairFilters
#include "dbEdgePairs.h"
#include "dbEdgesUtils.h"
namespace db
{
@ -69,12 +70,57 @@ public:
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; }
virtual const TransformationReducer *vars () const { return &m_vars; }
virtual bool wants_variants () const { return true; }
private:
distance_type m_min_distance, m_max_distance;
bool m_inverted;
db::MagnificationReducer m_vars;
};
/**
* @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 EdgePairFilterByArea
: public EdgePairFilterBase
{
public:
typedef db::coord_traits<db::Coord>::area_type area_type;
EdgePairFilterByArea (area_type min_area, area_type max_area, bool inverted);
virtual bool selected (const db::EdgePair &edge_pair) const;
virtual const TransformationReducer *vars () const { return &m_vars; }
virtual bool wants_variants () const { return true; }
private:
area_type m_min_area, m_max_area;
bool m_inverted;
db::MagnificationReducer m_vars;
};
/**
* @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 InternalAngleEdgePairFilter
: public EdgePairFilterBase
{
public:
InternalAngleEdgePairFilter (double a, bool inverted);
InternalAngleEdgePairFilter (double amin, bool include_amin, double amax, bool include_amax, 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:
bool m_inverted;
db::EdgeAngleChecker m_checker;
};
}

View File

@ -236,7 +236,6 @@ struct DB_PUBLIC EdgeOrientationFilter
}
private:
db::DVector m_emin, m_emax;
bool m_inverse;
db::MagnificationAndOrientationReducer m_vars;
EdgeAngleChecker m_checker;

View File

@ -246,6 +246,30 @@ static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, doub
return r->filtered (ef);
}
static db::EdgePairs with_internal_angle1 (const db::EdgePairs *r, double a, bool inverse)
{
db::InternalAngleEdgePairFilter f (a, inverse);
return r->filtered (f);
}
static db::EdgePairs with_internal_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax)
{
db::InternalAngleEdgePairFilter f (amin, include_amin, amax, include_amax, inverse);
return r->filtered (f);
}
static db::EdgePairs with_area1 (const db::EdgePairs *r, db::EdgePair::area_type a, bool inverse)
{
db::EdgePairFilterByArea f (a, a + 1, inverse);
return r->filtered (f);
}
static db::EdgePairs with_area2 (const db::EdgePairs *r, db::EdgePair::area_type amin, db::EdgePair::area_type amax, bool inverse)
{
db::EdgePairFilterByArea f (amin, amax, inverse);
return r->filtered (f);
}
extern Class<db::ShapeCollection> decl_dbShapeCollection;
Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
@ -621,7 +645,7 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
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 where both edges have a distance between min_distance and max_distance (max_distance itself is excluded) 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"
@ -678,6 +702,45 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_area", with_area1, gsi::arg ("area"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by the enclosed area\n"
"Filters the edge pairs in the edge pair collection by enclosed area. If \"inverse\" is false, only "
"edge pairs with the given area are returned. If \"inverse\" is true, "
"edge pairs not with the given area are returned.\n"
"\n"
"This method has been added in version 0.27.2.\n"
) +
method_ext ("with_area", with_area2, gsi::arg ("min_area"), gsi::arg ("max_area"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by the enclosed area\n"
"Filters the edge pairs in the edge pair collection by enclosed area. If \"inverse\" is false, only "
"edge pairs with an area between min_area and max_area (max_area itself is excluded) 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.2.\n"
) +
method_ext ("with_internal_angle", with_internal_angle1, gsi::arg ("angle"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by the angle between their edges\n"
"Filters the edge pairs in the edge pair collection by the angle between their edges. If \"inverse\" is false, only "
"edge pairs with the given angle are returned. If \"inverse\" is true, "
"edge pairs not with the given angle are returned.\n"
"\n"
"The angle is measured between the two edges. It is between 0 (parallel or anti-parallel edges) and 90 degree (perpendicular edges).\n"
"\n"
"This method has been added in version 0.27.2.\n"
) +
method_ext ("with_internal_angle", with_internal_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 Filters the edge pairs by the angle between their edges\n"
"Filters the edge pairs in the edge pair collection by the angle between their edges. If \"inverse\" is false, only "
"edge pairs with an angle between min_angle and max_angle (max_angle itself is excluded) are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"The angle is measured between the two edges. It is between 0 (parallel or anti-parallel edges) and 90 degree (perpendicular edges).\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.2.\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

@ -24,6 +24,7 @@
#include "tlUnitTest.h"
#include "dbEdgePairs.h"
#include "dbEdgePairFilters.h"
#include "dbEdges.h"
#include "dbRegion.h"
#include "dbTestSupport.h"
@ -160,3 +161,42 @@ TEST(4)
db::Region r (db::RecursiveShapeIterator (ly, ly.cell (top_cell), l1));
EXPECT_EQ (db::compare (r, "(-10,-21;9,20;50,51;91,80);(-10,-21;9,20;110,121;91,80)"), true);
}
TEST(5_InternalAngleFilter)
{
db::EdgePair ep0 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 0), db::Point (0, 0)));
db::EdgePair ep45 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (100, 100)));
db::EdgePair ep180 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (100, 0)));
db::EdgePair ep90 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (0, 100)));
db::EdgePair epm90 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 100), db::Point (0, 0)));
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (ep0), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (ep180), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (ep90), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (epm90), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (ep45), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (ep0), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (ep180), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (ep90), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (epm90), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (ep45), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep0), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep180), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep90), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (epm90), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep45), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep0), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep180), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep90), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (epm90), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep45), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep0), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep180), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep90), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (epm90), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep45), true);
}

View File

@ -330,28 +330,30 @@ module DRC
# %DRC%
# @name with_area
# @brief Selects polygons by area
# @brief Selects polygons or edge pairs by area
# @synopsis layer.with_area(min .. max)
# @synopsis layer.with_area(value)
# @synopsis layer.with_area(min, max)
# The first form will select all polygons with an area larger or
# The first form will select all polygons or edge pairs with an area larger or
# equal to min and less (but not equal to) max. The second form
# will select the polygons with exactly the given area.
# will select the polygons or edge pairs with exactly the given area.
# The third form basically is equivalent to the first form, but
# allows specification of nil for min or max indicating no lower or
# upper limit.
#
# This method is available for polygon or edge pair layers.
# %DRC%
# @name without_area
# @brief Selects polygons by area
# @brief Selects polygons or edge pairs by area
# @synopsis layer.without_area(min .. max)
# @synopsis layer.without_area(value)
# @synopsis layer.without_area(min, max)
# This method is the inverse of "with_area". It will select
# polygons without an area equal to the given one or outside
# polygons or edge pairs without an area equal to the given one or outside
# the given interval.
#
# This method is available for polygon layers only.
# This method is available for polygon or edge pair layers.
%w(area).each do |f|
[true, false].each do |inv|
@ -361,7 +363,8 @@ module DRC
@engine._context("#{mn}") do
requires_region
self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge pair or polygon layer")
if args.size == 1
a = args[0]
if a.is_a?(Range)
@ -857,7 +860,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])
# @synopsis edge_pair_layer.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.
@ -871,10 +874,6 @@ CODE
# The second version selects edges with exactly the given angle. The third
# version is identical to the first one.
#
# 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.
#
# 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.
@ -903,6 +902,9 @@ CODE
# @td @img(/images/drc_with_angle4.png) @/td
# @/tr
# @/table
#
# Note that in former versions, with_angle could be used on polygon layers selecting corners with specific angles.
# This feature has been deprecated. Use \corners instead.
# %DRC%
# @name without_angle
@ -910,11 +912,12 @@ 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])
# @synopsis edge_pair_layer.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).
# is not inside the given interval (first and third form). When called on edge pairs, it selects
# edge pairs by the angles of their edges.
#
# A note on the "both" modifier (without_angle called on edge pairs): "both" means that
# both edges need to be "without_angle". For example
@ -925,8 +928,41 @@ CODE
# ep = edge_pairs.without_angle(0, both)
# @/code
#
# See \with_internal_angle and \without_internal_angle to select edge pairs by the
# angle between the edges.
%w(angle).each do |f|
# %DRC%
# @name with_internal_angle
# @brief Selects edge pairs by their internal angle
# @synopsis edge_pair_layer.with_internal_angle(min .. max)
# @synopsis edge_pair_layer.with_internal_angle(value)
# @synopsis edge_pair_layer.with_internal_angle(min, max)
#
# This method selects edge pairs by the angle enclosed by their edges.
# The angle is between 0 (parallel or anti-parallel edges) and 90 degree (perpendicular edges).
# If an interval or two values are given, the angle is checked to be within the given
# range.
#
# Here are examples for "with_internal_angle" on edge pair layers:
#
# @code
# # selects edge pairs with parallel edges
# ep1 = edge_pairs.with_internal_angle(0)
# # selects edge pairs with perpendicular edges
# ep2 = edge_pairs.with_internal_angle(90)
# @/code
# %DRC%
# @name without_internal_angle
# @brief Selects edge pairs by their internal angle
# @synopsis edge_pair_layer.without_internal_angle(min .. max)
# @synopsis edge_pair_layer.without_internal_angle(value)
# @synopsis edge_pair_layer.without_internal_angle(min, max)
#
# The method basically is the inverse of \with_internal_angle. It selects all
# edge pairs by the angle enclosed by their edges, applying the opposite criterion than \with_internal_angle.
%w(angle internal_angle).each do |f|
[true, false].each do |inv|
mn = (inv ? "without" : "with") + "_" + f
eval <<"CODE"
@ -935,16 +971,22 @@ CODE
@engine._context("#{mn}") do
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")
if "#{f}" == "angle"
self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge, edge pair or polygon layer")
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
f = :with_#{f}_both
false
else
true
end
else
requires_edge_pairs
end
result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs

View File

@ -1283,12 +1283,12 @@ TEST(48d_drcWithFragments)
run_test (_this, "48", true);
}
TEST(49_drcWithFragments)
TEST(49_epAngle)
{
run_test (_this, "49", false);
}
TEST(49d_drcWithFragments)
TEST(49d_epAngle)
{
run_test (_this, "49", true);
}
@ -1298,3 +1298,8 @@ TEST(50_issue826)
run_test (_this, "50", false);
}
TEST(51_epInternalAngle)
{
run_test (_this, "51", false);
}

View File

@ -3157,7 +3157,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>
<li><tt>edge_pair_layer.with_angle(min, max [, both])</tt></li>
</ul>
<p>
When called on an edge layer, the method selects edges by their angle,
@ -3172,10 +3172,6 @@ edges with a angle larger or equal to min and less than max (but not equal).
The second version selects edges with exactly the given angle. The third
version is identical to the first one.
</p><p>
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.
@ -3204,8 +3200,11 @@ The following images demonstrate some use cases of <a href="#with_angle">with_an
<td><img src="/images/drc_with_angle4.png"/></td>
</tr>
</table>
</p><p>
Note that in former versions, with_angle could be used on polygon layers selecting corners with specific angles.
This feature has been deprecated. Use <a href="#corners">corners</a> instead.
</p>
<a name="with_area"/><h2>"with_area" - Selects polygons by area</h2>
<a name="with_area"/><h2>"with_area" - Selects polygons or edge pairs by area</h2>
<keyword name="with_area"/>
<p>Usage:</p>
<ul>
@ -3214,12 +3213,14 @@ The following images demonstrate some use cases of <a href="#with_angle">with_an
<li><tt>layer.with_area(min, max)</tt></li>
</ul>
<p>
The first form will select all polygons with an area larger or
The first form will select all polygons or edge pairs with an area larger or
equal to min and less (but not equal to) max. The second form
will select the polygons with exactly the given area.
will select the polygons or edge pairs with exactly the given area.
The third form basically is equivalent to the first form, but
allows specification of nil for min or max indicating no lower or
upper limit.
</p><p>
This method is available for polygon or edge pair layers.
</p>
<a name="with_area_ratio"/><h2>"with_area_ratio" - Selects polygons by the ratio of the bounding box area vs. polygon area</h2>
<keyword name="with_area_ratio"/>
@ -3334,7 +3335,7 @@ The tile size must be specified with the "tile_size" option:
</p><p>
<pre>
# reports areas where layer 1/0 density is below 10% on 20x20 um tiles
low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um))
low_density = input(1, 0).with_density(0.0 .. 0.1, tile_size(20.um))
</pre>
</p><p>
Anisotropic tiles can be specified by giving two values, like "tile_size(10.um, 20.um)".
@ -3348,7 +3349,7 @@ in increments of the tile step:
<pre>
# reports areas where layer 1/0 density is below 10% on 30x30 um tiles
# with a tile step of 20x20 um:
low_density = input(1, 0).density(0.0 .. 0.1, tile_size(30.um), tile_step(20.um))
low_density = input(1, 0).with_density(0.0 .. 0.1, tile_size(30.um), tile_step(20.um))
</pre>
</p><p>
For "tile_step", anisotropic values can be given as well by using two values: the first for the
@ -3366,7 +3367,7 @@ drawn boundary layer. To specify a separate, additional layer included in the bo
# reports density of layer 1/0 below 10% on 20x20 um tiles. The layout's boundary is taken from
# layer 0/0:
cell_frame = input(0, 0)
low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), tile_boundary(cell_frame))
low_density = input(1, 0).with_density(0.0 .. 0.1, tile_size(20.um), tile_boundary(cell_frame))
</pre>
</p><p>
Note that the layer given in "tile_boundary" adds to the input layer for computing the bounding box.
@ -3378,7 +3379,7 @@ direction. With the "tile_origin" option this allows full control over the area
<pre>
# reports density of layer 1/0 below 10% on 20x20 um tiles in the region 0,0 .. 2000,3000
# (100 and 150 tiles of 20 um each are used in horizontal and vertical direction):
low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), tile_origin(0.0, 0.0), tile_count(100, 150))
low_density = input(1, 0).with_density(0.0 .. 0.1, tile_size(20.um), tile_origin(0.0, 0.0), tile_count(100, 150))
</pre>
</p><p>
The "padding mode" indicates how the area outside the layout's bounding box is considered.
@ -3392,7 +3393,7 @@ There are two modes:
Example:
</p><p>
<pre>
low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), padding_ignore)
low_density = input(1, 0).with_density(0.0 .. 0.1, tile_size(20.um), padding_ignore)
</pre>
</p><p>
The complementary version of "with_density" is <a href="#without_density">without_density</a>.
@ -3430,6 +3431,29 @@ This method is available for edge pair layers only.
This method is available for polygon layers. It will select all polygons from the input layer
which have the specified number of holes.
</p>
<a name="with_internal_angle"/><h2>"with_internal_angle" - Selects edge pairs by their internal angle</h2>
<keyword name="with_internal_angle"/>
<p>Usage:</p>
<ul>
<li><tt>edge_pair_layer.with_internal_angle(min .. max)</tt></li>
<li><tt>edge_pair_layer.with_internal_angle(value)</tt></li>
<li><tt>edge_pair_layer.with_internal_angle(min, max)</tt></li>
</ul>
<p>
This method selects edge pairs by the angle enclosed by their edges.
The angle is between 0 (parallel or anti-parallel edges) and 90 degree (perpendicular edges).
If an interval or two values are given, the angle is checked to be within the given
range.
</p><p>
Here are examples for "with_internal_angle" on edge pair layers:
</p><p>
<pre>
# selects edge pairs with parallel edges
ep1 = edge_pairs.with_internal_angle(0)
# selects edge pairs with perpendicular edges
ep2 = edge_pairs.with_internal_angle(90)
</pre>
</p>
<a name="with_length"/><h2>"with_length" - Selects edges by their length</h2>
<keyword name="with_length"/>
<p>Usage:</p>
@ -3505,12 +3529,13 @@ 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>
<li><tt>edge_pair_layer.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
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).
is not inside the given interval (first and third form). When called on edge pairs, it selects
edge pairs by the angles of their edges.
</p><p>
A note on the "both" modifier (without_angle called on edge pairs): "both" means that
both edges need to be "without_angle". For example
@ -3520,8 +3545,11 @@ both edges need to be "without_angle". For example
# the edge pair is skipped if one edge is horizontal
ep = edge_pairs.without_angle(0, both)
</pre>
</p><p>
See <a href="#with_internal_angle">with_internal_angle</a> and <a href="#without_internal_angle">without_internal_angle</a> to select edge pairs by the
angle between the edges.
</p>
<a name="without_area"/><h2>"without_area" - Selects polygons by area</h2>
<a name="without_area"/><h2>"without_area" - Selects polygons or edge pairs by area</h2>
<keyword name="without_area"/>
<p>Usage:</p>
<ul>
@ -3531,10 +3559,10 @@ ep = edge_pairs.without_angle(0, both)
</ul>
<p>
This method is the inverse of "with_area". It will select
polygons without an area equal to the given one or outside
polygons or edge pairs without an area equal to the given one or outside
the given interval.
</p><p>
This method is available for polygon layers only.
This method is available for polygon or edge pair layers.
</p>
<a name="without_area_ratio"/><h2>"without_area_ratio" - Selects polygons by the aspect ratio of their bounding box</h2>
<keyword name="without_area_ratio"/>
@ -3647,6 +3675,18 @@ This method is available for edge pair layers only.
This method is available for polygon layers. It will select all polygons from the input layer
which do not have the specified number of holes.
</p>
<a name="without_internal_angle"/><h2>"without_internal_angle" - Selects edge pairs by their internal angle</h2>
<keyword name="without_internal_angle"/>
<p>Usage:</p>
<ul>
<li><tt>edge_pair_layer.without_internal_angle(min .. max)</tt></li>
<li><tt>edge_pair_layer.without_internal_angle(value)</tt></li>
<li><tt>edge_pair_layer.without_internal_angle(min, max)</tt></li>
</ul>
<p>
The method basically is the inverse of <a href="#with_internal_angle">with_internal_angle</a>. It selects all
edge pairs by the angle enclosed by their edges, applying the opposite criterion than <a href="#with_internal_angle">with_internal_angle</a>.
</p>
<a name="without_length"/><h2>"without_length" - Selects edges by the their length</h2>
<keyword name="without_length"/>
<p>Usage:</p>

26
testdata/drc/drcSimpleTests_51.drc vendored Normal file
View File

@ -0,0 +1,26 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
ep = input(1, 0).drc(sep(input(2, 0), angle_limit(91)) < 0.5)
input(1, 0).output(1, 0)
input(2, 0).output(2, 0)
ep.polygons(0).output(100, 0)
ep.with_internal_angle(45.0).polygons(0).output(200, 0)
ep.with_internal_angle(0.0).polygons(0).output(201, 0)
ep.with_internal_angle(45.0..91.0).polygons(0).output(202, 0)
ep.without_internal_angle(45.0).polygons(0).output(220, 0)
ep.without_internal_angle(0.0).polygons(0).output(221, 0)
ep.without_internal_angle(45.0..91.0).polygons(0).output(222, 0)
ep.with_area(0 .. 0.1).polygons(0).output(300, 0)
ep.without_area(0 .. 0.1).polygons(0).output(301, 0)

BIN
testdata/drc/drcSimpleTests_51.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au51.gds vendored Normal file

Binary file not shown.

View File

@ -225,6 +225,86 @@ class DBEdgePairs_TestClass < TestBase
end
def test_5
# filters
ep1 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 20, 10, 0))
ep2 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 0, 10, 20))
ep3 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 20), RBA::Edge::new(10, 20, 10, 0))
ep4 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 0, 10, 10))
r1 = RBA::EdgePairs::new([ ep1, ep2, ep3, ep4 ])
assert_equal(r1.with_distance(10, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_distance(5, 20, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_distance(15, 20, false).to_s, "")
assert_equal(r1.with_distance(15, 20, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_length(10, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_length(10, 20, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_length(10, 21, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_length(10, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0)")
assert_equal(r1.with_length_both(10, false).to_s, "(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_length_both(10, 20, false).to_s, "(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_length_both(10, 21, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_length_both(10, true).to_s, "(0,0;0,20)/(10,20;10,0)")
assert_equal(r1.with_angle(0, false).to_s, "")
assert_equal(r1.with_angle(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle(0, 90, false).to_s, "")
assert_equal(r1.with_angle(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle_both(0, false).to_s, "")
assert_equal(r1.with_angle_both(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle_both(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle_both(0, 90, false).to_s, "")
assert_equal(r1.with_angle_both(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_area(0, false).to_s, "(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_area(150, false).to_s, "(0,0;0,10)/(10,20;10,0)")
assert_equal(r1.with_area(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0)")
assert_equal(r1.with_area(150, 151, false).to_s, "(0,0;0,10)/(10,20;10,0)")
assert_equal(r1.with_area(150, 150, false).to_s, "")
assert_equal(r1.with_area(150, 151, true).to_s, "(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_internal_angle(0, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_internal_angle(0, 0, false).to_s, "")
assert_equal(r1.with_internal_angle(0, true).to_s, "")
ep1 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 20, 10, 0))
ep2 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(20, 0, 30, 0))
ep3 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(-20, 0, -30, 0))
r1 = RBA::EdgePairs::new([ ep1, ep2, ep3 ])
assert_equal(r1.with_distance(20, false).to_s, "(0,0;0,10)/(20,0;30,0);(0,0;0,10)/(-20,0;-30,0)")
assert_equal(r1.with_distance(20, true).to_s, "(0,0;0,10)/(10,20;10,0)")
assert_equal(r1.with_internal_angle(0, false).to_s, "(0,0;0,10)/(10,20;10,0)")
assert_equal(r1.with_internal_angle(90, false).to_s, "(0,0;0,10)/(20,0;30,0);(0,0;0,10)/(-20,0;-30,0)")
assert_equal(r1.with_internal_angle(-90, false).to_s, "")
assert_equal(r1.with_angle(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(20,0;30,0);(0,0;0,10)/(-20,0;-30,0)")
assert_equal(r1.with_angle(0, false).to_s, "(0,0;0,10)/(20,0;30,0);(0,0;0,10)/(-20,0;-30,0)")
assert_equal(r1.with_angle(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(20,0;30,0);(0,0;0,10)/(-20,0;-30,0)")
assert_equal(r1.with_angle_both(90, false).to_s, "(0,0;0,10)/(10,20;10,0)")
assert_equal(r1.with_angle_both(0, false).to_s, "")
ep1 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 20, 10, 0))
ep2 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(20, 10, 30, 0))
r1 = RBA::EdgePairs::new([ ep1, ep2 ])
assert_equal(r1.with_internal_angle(0, false).to_s, "(0,0;0,10)/(10,20;10,0)")
assert_equal(r1.with_internal_angle(90, false).to_s, "")
assert_equal(r1.with_internal_angle(90, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(20,10;30,0)")
assert_equal(r1.with_internal_angle(45, false).to_s, "(0,0;0,10)/(20,10;30,0)")
assert_equal(r1.with_internal_angle(0, 45, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(20,10;30,0)")
assert_equal(r1.with_internal_angle(0, 45, true, true, true).to_s, "")
end
end