WIP: first draft of angle-class selectors for edges and edge pairs for simplifying edge orientation checks

This commit is contained in:
Matthias Koefferlein 2022-11-11 23:46:45 +01:00
parent aadb8510d2
commit b02d3b24a8
9 changed files with 333 additions and 9 deletions

View File

@ -45,6 +45,26 @@ db::Trans OrientationReducer::reduce (const db::Trans &trans) const
// ------------------------------------------------------------------------------------------
db::ICplxTrans OrthogonalTransformationReducer::reduce (const db::ICplxTrans &trans) const
{
if (trans.is_ortho ()) {
return db::ICplxTrans ();
} else {
db::ICplxTrans res;
double a = trans.angle ();
double a90 = floor (a / 90.0 + 0.5) * 90.0;
res.angle (a - a90);
return res;
}
}
db::Trans OrthogonalTransformationReducer::reduce (const db::Trans &trans) const
{
return db::Trans ();
}
// ------------------------------------------------------------------------------------------
db::ICplxTrans MagnificationReducer::reduce (const db::ICplxTrans &trans) const
{
return db::ICplxTrans (trans.mag ());

View File

@ -69,6 +69,16 @@ struct DB_PUBLIC OrientationReducer
db::Trans reduce (const db::Trans &trans) const;
};
/**
* @brief A reducer for invariance against orthogonal transformations (rotations of multiple of 90 degree)
*/
struct DB_PUBLIC OrthogonalTransformationReducer
: public TransformationReducer
{
db::ICplxTrans reduce (const db::ICplxTrans &trans) const;
db::Trans reduce (const db::Trans &trans) const;
};
/**
* @brief A magnification reducer
*

View File

@ -274,6 +274,73 @@ EdgeOrientationFilter::selected (const db::Edge &edge) const
}
}
// -------------------------------------------------------------------------------------------------------------
// SpecialEdgeOrientationFilter implementation
SpecialEdgeOrientationFilter::SpecialEdgeOrientationFilter (FilterType type, bool inverse)
: m_type (type), m_inverse (inverse)
{
// .. nothing yet ..
}
static EdgeAngleChecker s_ortho_checkers [] = {
EdgeAngleChecker (0.0, true, 0.0, true),
EdgeAngleChecker (90.0, true, 90.0, true)
};
static EdgeAngleChecker s_diagonal_checkers [] = {
EdgeAngleChecker (-45.0, true, -45.0, true),
EdgeAngleChecker (45.0, true, 45.0, true)
};
static EdgeAngleChecker s_orthodiagonal_checkers [] = {
EdgeAngleChecker (-45.0, true, -45.0, true),
EdgeAngleChecker (0.0, true, 0.0, true),
EdgeAngleChecker (45.0, true, 45.0, true),
EdgeAngleChecker (90.0, true, 90.0, true)
};
bool
SpecialEdgeOrientationFilter::selected (const db::Edge &edge) const
{
const EdgeAngleChecker *eb, *ee;
switch (m_type) {
case Ortho:
eb = s_ortho_checkers;
ee = s_ortho_checkers + sizeof (s_ortho_checkers) / sizeof (s_ortho_checkers [0]);
break;
case Diagonal:
eb = s_diagonal_checkers;
ee = s_diagonal_checkers + sizeof (s_diagonal_checkers) / sizeof (s_diagonal_checkers [0]);
break;
case OrthoDiagonal:
default:
eb = s_orthodiagonal_checkers;
ee = s_orthodiagonal_checkers + sizeof (s_orthodiagonal_checkers) / sizeof (s_orthodiagonal_checkers [0]);
break;
}
db::Vector en, ev;
en = db::Vector (edge.ortho_length (), 0);
// NOTE: this edge normalization confines the angle to a range between (-90 .. 90] (-90 excluded).
// A horizontal edge has 0 degree, a vertical one has 90 degree.
if (edge.dx () < 0 || (edge.dx () == 0 && edge.dy () < 0)) {
ev = -edge.d ();
} else {
ev = edge.d ();
}
for (auto ec = eb; ec != ee; ++ec) {
if ((*ec) (en, ev)) {
return ! m_inverse;
}
}
return m_inverse;
}
// -------------------------------------------------------------------------------------------------------------
// Edge to Edge relation implementation

View File

@ -246,6 +246,76 @@ private:
EdgeAngleChecker m_checker;
};
/**
* @brief An edge orientation filter for use with Edges::filter or Edges::filtered
*
* This filter renders edges with special orientations.
*/
struct DB_PUBLIC SpecialEdgeOrientationFilter
: public EdgeFilterBase
{
enum FilterType {
Ortho = 0, // 0 and 90 degree
Diagonal = 1, // -45 and 45 degree
OrthoDiagonal = 2 // both Ortho and Diagonal
};
/**
* @brief Constructor
*
* @param type The type of filter
*/
SpecialEdgeOrientationFilter (FilterType type, bool inverse);
/**
* @brief Returns true if the edge orientation matches the criterion
*/
virtual bool selected (const db::Edge &edge) const;
/**
* @brief Returns true if all edge orientations match the criterion
*/
virtual bool selected (const std::unordered_set<db::Edge> &edges) const
{
for (std::unordered_set<db::Edge>::const_iterator e = edges.begin (); e != edges.end (); ++e) {
if (! selected (*e)) {
return false;
}
}
return true;
}
/**
* @brief This filter is not isotropic
*/
virtual const TransformationReducer *vars () const
{
return &m_vars;
}
/**
* @brief Requires merged input
*/
virtual bool requires_raw_input () const
{
return false;
}
/**
* @brief Wants to build variants
*/
virtual bool wants_variants () const
{
return true;
}
private:
FilterType m_type;
bool m_inverse;
db::OrthogonalTransformationReducer m_vars;
};
/**
* @brief A predicate defining edge a interacts with b
*/

View File

@ -22,6 +22,7 @@
#include "gsiDecl.h"
#include "gsiEnums.h"
#include "dbEdgePairs.h"
#include "dbEdges.h"
@ -232,6 +233,13 @@ static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double am
return r->filtered (ef);
}
static db::EdgePairs with_angle3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse)
{
db::SpecialEdgeOrientationFilter f (type, 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);
@ -246,6 +254,13 @@ static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, doub
return r->filtered (ef);
}
static db::EdgePairs with_angle_both3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse)
{
db::SpecialEdgeOrientationFilter f (type, inverse);
db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_internal_angle1 (const db::EdgePairs *r, double a, bool inverse)
{
db::InternalAngleEdgePairFilter f (a, inverse);
@ -677,6 +692,17 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_angle", with_angle3, gsi::arg ("type"), 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 an angle of the given type are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"This version allows specifying an edge type instead of an angle. Edge types include multiple distinct orientations "
"and are specified using one of the \\OrthoEdges, \\DiagonalEgdes or \\OrthoDiagonalEdges types.\n"
"\n"
"This method has been added in version 0.28.\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 "
@ -702,6 +728,17 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_angle_both", with_angle_both3, gsi::arg ("type"), 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 both edges having an angle of the given type are returned. If \"inverse\" is true, "
"edge pairs not fulfilling this criterion are returned.\n"
"\n"
"This version allows specifying an edge type instead of an angle. Edge types include multiple distinct orientations "
"and are specified using one of the \\OrthoEdges, \\DiagonalEgdes or \\OrthoDiagonalEdges types.\n"
"\n"
"This method has been added in version 0.28.\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 "
@ -838,4 +875,22 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"This class has been introduced in version 0.23.\n"
);
gsi::EnumIn<db::EdgePairs, db::SpecialEdgeOrientationFilter::FilterType> decl_EdgePairsEdgeFilterType ("db", "EdgeType",
gsi::enum_const ("OrthoEdges", db::SpecialEdgeOrientationFilter::Ortho,
"@brief Horizontal and vertical edges are selected\n"
) +
gsi::enum_const ("DiagonalEdges", db::SpecialEdgeOrientationFilter::Diagonal,
"@brief Diagonal edges are selected (-45 and 45 degree)\n"
) +
gsi::enum_const ("OrthoDiagonalEdges", db::SpecialEdgeOrientationFilter::OrthoDiagonal,
"@brief Diagonal or orthogonal edges are selected (0, 90, -45 and 45 degree)\n"
),
"@brief This enum specifies the the edge type for edge angle filters.\n"
"\n"
"This enum was introduced in version 0.28.\n"
);
// Inject the db::SpecialEdgeOrientationFilter::FilterType declarations into EdgePairs:
gsi::ClassExt<db::EdgePairs> decl_EdgePairsEdgeFilterType_into_parent (decl_EdgePairsEdgeFilterType.defs ());
}

View File

@ -212,6 +212,12 @@ static db::Edges with_angle2 (const db::Edges *r, double amin, double amax, bool
return r->filtered (f);
}
static db::Edges with_angle3 (const db::Edges *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse)
{
db::SpecialEdgeOrientationFilter f (type, inverse);
return r->filtered (f);
}
static db::EdgePairs width2 (const db::Edges *r, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection)
{
return r->width_check (d, db::EdgesCheckOptions (whole_edges,
@ -609,13 +615,13 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"This method has been introduced in version 0.26."
) +
method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"),
"@brief Filter the edges by length\n"
"@brief Filters the edges by length\n"
"Filters the edges in the edge collection by length. If \"inverse\" is false, only "
"edges which have the given length are returned. If \"inverse\" is true, "
"edges not having the given length are returned.\n"
) +
method_ext ("with_length", with_length2, gsi::arg ("min_length"), gsi::arg ("max_length"), gsi::arg ("inverse"),
"@brief Filter the edges by length\n"
"@brief Filters the edges by length\n"
"Filters the edges in the edge collection by length. If \"inverse\" is false, only "
"edges which have a length larger or equal to \"min_length\" and less than \"max_length\" are "
"returned. If \"inverse\" is true, "
@ -625,7 +631,7 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"If you don't want to specify a lower or upper limit, pass nil to that parameter.\n"
) +
method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"),
"@brief Filter the edges by orientation\n"
"@brief Filters the edges by orientation\n"
"Filters the edges in the edge collection by orientation. If \"inverse\" is false, only "
"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"
@ -637,7 +643,7 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"@/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),
"@brief Filter the edges by orientation\n"
"@brief Filters the edges by orientation\n"
"Filters the edges in the edge collection by orientation. If \"inverse\" is false, only "
"edges which have an angle to the x-axis larger or equal to \"min_angle\" (depending on \"include_min_angle\") and equal or less than \"max_angle\" (depending on \"include_max_angle\") are "
"returned. If \"inverse\" is true, "
@ -648,6 +654,17 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"\n"
"The two \"include..\" arguments have been added in version 0.27."
) +
method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"),
"@brief Filters the edges by orientation type\n"
"Filters the edges in the edge collection by orientation. If \"inverse\" is false, only "
"edges which have an angle of the given type are returned. If \"inverse\" is true, "
"edges which do not conform to this criterion are returned.\n"
"\n"
"This version allows specifying an edge type instead of an angle. Edge types include multiple distinct orientations "
"and are specified using one of the \\OrthoEdges, \\DiagonalEgdes or \\OrthoDiagonalEdges types.\n"
"\n"
"This method has been added in version 0.28.\n"
) +
method ("insert", (void (db::Edges::*)(const db::Edge &)) &db::Edges::insert, gsi::arg ("edge"),
"@brief Inserts an edge\n"
"\n"
@ -1749,5 +1766,23 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"This class has been introduced in version 0.23.\n"
);
gsi::EnumIn<db::Edges, db::SpecialEdgeOrientationFilter::FilterType> decl_EdgesEdgeFilterType ("db", "EdgeType",
gsi::enum_const ("OrthoEdges", db::SpecialEdgeOrientationFilter::Ortho,
"@brief Horizontal and vertical edges are selected\n"
) +
gsi::enum_const ("DiagonalEdges", db::SpecialEdgeOrientationFilter::Diagonal,
"@brief Diagonal edges are selected (-45 and 45 degree)\n"
) +
gsi::enum_const ("OrthoDiagonalEdges", db::SpecialEdgeOrientationFilter::OrthoDiagonal,
"@brief Diagonal or orthogonal edges are selected (0, 90, -45 and 45 degree)\n"
),
"@brief This enum specifies the the edge type for edge angle filters.\n"
"\n"
"This enum was introduced in version 0.28.\n"
);
// Inject the db::SpecialEdgeOrientationFilter::FilterType declarations into Edges:
gsi::ClassExt<db::Edges> decl_EdgesEdgeFilterType_into_parent (decl_EdgesEdgeFilterType.defs ());
}

View File

@ -77,6 +77,18 @@ module DRC
DRCBothEdges::new
end
def ortho
DRCOrthoEdges::new
end
def diagonal_only
DRCDiagonalOnlyEdges::new
end
def diagonal
DRCDiagonalEdges::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))

View File

@ -864,7 +864,10 @@ CODE
# @synopsis layer.with_angle(min .. max)
# @synopsis layer.with_angle(value)
# @synopsis layer.with_angle(min, max)
# @synopsis edge_pair_layer.with_angle(min, max [, both])
# @synopsis layer.with_angle(ortho)
# @synopsis layer.with_angle(diagonal)
# @synopsis layer.with_angle(diagonal_only)
# @synopsis edge_pair_layer.with_angle(... [, both])
#
# When called on an edge layer, the method selects edges by their angle,
# measured against the horizontal axis in the mathematical sense.
@ -876,7 +879,7 @@ CODE
# The first version of this method selects
# 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.
# version is identical to the first one.
#
# 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
@ -907,6 +910,17 @@ CODE
# @/tr
# @/table
#
# Specifying "ortho", "diagonal" or "diagonal_only" instead of the angle values will select
# 0 and 90 degree edges (ortho), -45 and 45 degree edges (diagonal_only) and both types (diagonal).
# This simplifies the implementation of selectors for manhattan or half-manhattan features:
#
# @code
# ortho_edges = edges.with_angle(ortho)
#
# # which is equivalent to, but more efficient as:
# ortho_edges = edges.with_angle(0) + edges.with_angle(90)
# @/code
#
# 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.
@ -916,11 +930,16 @@ CODE
# @synopsis layer.without_angle(min .. max)
# @synopsis layer.without_angle(value)
# @synopsis layer.without_angle(min, max)
# @synopsis edge_pair_layer.without_angle(min, max [, both])
# @synopsis layer.without_angle(ortho)
# @synopsis layer.without_angle(diagonal)
# @synopsis layer.without_angle(diagonal_only)
# @synopsis edge_pair_layer.without_angle(... [, 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). When called on edge pairs, it selects
# is not inside the given interval (first and third form) or of the given type (other forms).
#
# 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
@ -998,6 +1017,12 @@ CODE
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}))
elsif a.is_a?(DRCOrthoEdges) || a.is_a?(DRCDiagonalOnlyEdges) || a.is_a?(DRCDiagonalEdges)
if self.data.is_a?(RBA::Edges)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.e_value, #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.ep_value, #{inv.inspect}))
end
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a, #{inv.inspect}))
end

View File

@ -16,10 +16,40 @@ module DRC
end
end
# A wrapper for the "both edges" flag for EdgePair#with_length or EdgePair#with_angle
# A wrapper for the "both edges" flag for EdgePairs#with_length or EdgePairs#with_angle
class DRCBothEdges
end
# A wrapper for the "ortho edges" flag for Edges#with_angle or EdgePairs#with_angle
class DRCOrthoEdges
def ep_value
RBA::EdgePairs::OrthoEdges
end
def e_value
RBA::Edges::OrthoEdges
end
end
# A wrapper for the "diagonal only edges" flag for Edges#with_angle or EdgePairs#with_angle
class DRCDiagonalOnlyEdges
def ep_value
RBA::EdgePairs::DiagonalEdges
end
def e_value
RBA::Edges::DiagonalEdges
end
end
# A wrapper for the "diagonal edges" flag for Edges#with_angle or EdgePairs#with_angle
class DRCDiagonalEdges
def ep_value
RBA::EdgePairs::OrthoDiagonalEdges
end
def e_value
RBA::Edges::OrthoDiagonalEdges
end
end
# A wrapper for the padding modes of with_density
class DRCDensityPadding
attr_accessor :value