From 80996a77fbad9395d25f66070ae45794f6268df7 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Thu, 14 Jan 2021 00:21:21 +0100
Subject: [PATCH] New *_sum quantifiers, doc enhancements, bug fixing
---
scripts/extract_doc.rb | 19 +-
src/db/db/dbAsIfFlatRegion.cc | 5 +-
src/db/db/dbCompoundOperation.cc | 43 +-
src/db/db/dbCompoundOperation.h | 53 +-
src/db/db/dbDeepEdges.cc | 2 +
src/db/db/dbDeepRegion.cc | 3 +-
src/db/db/dbEdgesDelegate.h | 9 +-
src/db/db/dbEdgesUtils.h | 42 +-
src/db/db/dbPolygon.h | 10 +-
src/db/db/dbRegionDelegate.h | 18 +
src/db/db/dbRegionLocalOperations.h | 3 +
src/db/db/dbRegionUtils.cc | 132 ++++-
src/db/db/dbRegionUtils.h | 104 +++-
src/db/db/gsiDeclDbCompoundOperation.cc | 29 ++
.../drc/built-in-macros/_drc_complex_ops.rb | 453 ++++++++++--------
.../built-in-macros/_drc_cop_integration.rb | 149 ++++--
src/drc/drc/built-in-macros/_drc_tags.rb | 151 ------
src/drc/unit_tests/drcGenericTests.cc | 10 +
src/lay/lay/doc/about/drc_ref_drc.xml | 169 +++++--
src/lay/lay/doc/about/drc_ref_global.xml | 76 ++-
src/lay/lay/doc/about/drc_ref_layer.xml | 306 +++++++++++-
src/rba/rba/rba.cc | 124 +++--
src/rba/rba/rba.h | 23 +
src/rba/rba/rbaUtils.cc | 9 +-
src/rba/unit_tests/{rba.cc => rbaTests.cc} | 0
src/rba/unit_tests/unit_tests.pro | 2 +-
testdata/drc/drcGenericTests_15.drc | 30 ++
testdata/drc/drcGenericTests_15.gds | Bin 0 -> 1264 bytes
testdata/drc/drcGenericTests_au15.gds | Bin 0 -> 7522 bytes
testdata/drc/drcGenericTests_au15d.gds | Bin 0 -> 5640 bytes
30 files changed, 1375 insertions(+), 599 deletions(-)
rename src/rba/unit_tests/{rba.cc => rbaTests.cc} (100%)
create mode 100644 testdata/drc/drcGenericTests_15.drc
create mode 100644 testdata/drc/drcGenericTests_15.gds
create mode 100644 testdata/drc/drcGenericTests_au15.gds
create mode 100644 testdata/drc/drcGenericTests_au15d.gds
diff --git a/scripts/extract_doc.rb b/scripts/extract_doc.rb
index 1d659f503..aaf279ee8 100755
--- a/scripts/extract_doc.rb
+++ b/scripts/extract_doc.rb
@@ -14,7 +14,7 @@ def create_ref(mod, s)
"#{s} "
elsif s =~ /(.*)#(.*)/
if $2 != ""
- "#{s} "
+ "" + ($1 == "global" ? $2 : s) + " "
else
"#{$1} "
end
@@ -23,6 +23,22 @@ def create_ref(mod, s)
end
end
+def create_link(mod, s)
+ if s =~ /(.*)::(.*)#(.*)/
+ " "
+ elsif s =~ /(.*)::(.*)/
+ " "
+ elsif s =~ /(.*)#(.*)/
+ if $2 != ""
+ " "
+ else
+ " "
+ end
+ else
+ " "
+ end
+end
+
def create_class_doc_ref(s)
"#{s} "
end
@@ -32,6 +48,7 @@ def escape(mod, s)
gsub("<", "<").
gsub(">", ">").
gsub(/\\([\w:#]+)/) { create_ref(mod, $1) }.
+ gsub(/\\\\([\w:#]+)/) { create_link(mod, $1) }.
gsub(/RBA::([\w#]+)/) { create_class_doc_ref($1) }
end
diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc
index 0242a4a52..4a2b6a2ff 100644
--- a/src/db/db/dbAsIfFlatRegion.cc
+++ b/src/db/db/dbAsIfFlatRegion.cc
@@ -1046,15 +1046,14 @@ void region_cop_impl (AsIfFlatRegion *region, db::Shapes *output_to, db::Compoun
db::local_processor proc;
proc.set_base_verbosity (region->base_verbosity ());
- bool needs_merged = node.wants_merged ();
- db::RegionIterator polygons (needs_merged ? region->begin_merged () : region->begin ());
+ db::RegionIterator polygons (region->begin_merged ());
std::vector > others;
std::vector foreign;
std::vector inputs = node.inputs ();
for (std::vector::const_iterator i = inputs.begin (); i != inputs.end (); ++i) {
if (*i == subject_regionptr () || *i == foreign_regionptr ()) {
- others.push_back (needs_merged ? region->begin_merged () : region->begin ());
+ others.push_back (region->begin_merged ());
foreign.push_back (*i == foreign_regionptr ());
} else {
others.push_back ((*i)->begin ());
diff --git a/src/db/db/dbCompoundOperation.cc b/src/db/db/dbCompoundOperation.cc
index 143e9eb96..c26b82cd6 100644
--- a/src/db/db/dbCompoundOperation.cc
+++ b/src/db/db/dbCompoundOperation.cc
@@ -428,17 +428,6 @@ CompoundRegionMultiInputOperationNode::wants_variants () const
return false;
}
-bool
-CompoundRegionMultiInputOperationNode::wants_merged () const
-{
- for (tl::shared_collection::const_iterator i = m_children.begin (); i != m_children.end (); ++i) {
- if (i->wants_merged ()) {
- return true;
- }
- }
- return false;
-}
-
// ---------------------------------------------------------------------------------------------
CompoundRegionLogicalBoolOperationNode::CompoundRegionLogicalBoolOperationNode (LogicalOp op, bool invert, const std::vector &inputs)
@@ -1188,8 +1177,8 @@ template void CompoundRegionJoinOperationNode::implement_compute_localselected (p);
-}
-
-bool
-CompoundRegionFilterOperationNode::is_selected (const db::PolygonRef &p) const
-{
- return mp_filter->selected (p.obj ().transformed (p.trans ()));
-}
-
// ---------------------------------------------------------------------------------------------
-CompoundRegionEdgeFilterOperationNode::CompoundRegionEdgeFilterOperationNode (EdgeFilterBase *filter, CompoundRegionOperationNode *input, bool owns_filter)
- : CompoundRegionMultiInputOperationNode (input), mp_filter (filter), m_owns_filter (owns_filter)
+CompoundRegionEdgeFilterOperationNode::CompoundRegionEdgeFilterOperationNode (EdgeFilterBase *filter, CompoundRegionOperationNode *input, bool owns_filter, bool sum_of)
+ : CompoundRegionMultiInputOperationNode (input), mp_filter (filter), m_owns_filter (owns_filter), m_sum_of (sum_of)
{
set_description ("filter");
}
@@ -1254,12 +1231,6 @@ CompoundRegionEdgeFilterOperationNode::do_compute_local (db::Layout *layout, con
implement_compute_local (layout, interactions, results, max_vertex_count, area_ratio);
}
-bool
-CompoundRegionEdgeFilterOperationNode::is_selected (const db::Edge &p) const
-{
- return mp_filter->selected (p);
-}
-
// ---------------------------------------------------------------------------------------------
CompoundRegionEdgePairFilterOperationNode::CompoundRegionEdgePairFilterOperationNode (EdgePairFilterBase *filter, CompoundRegionOperationNode *input, bool owns_filter)
@@ -1622,12 +1593,6 @@ CompoundRegionCheckOperationNode::computed_dist () const
return m_check.distance ();
}
-bool
-CompoundRegionCheckOperationNode::wants_merged () const
-{
- return true;
-}
-
void
CompoundRegionCheckOperationNode::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const
{
diff --git a/src/db/db/dbCompoundOperation.h b/src/db/db/dbCompoundOperation.h
index 31af2a94e..fcb34e85d 100644
--- a/src/db/db/dbCompoundOperation.h
+++ b/src/db/db/dbCompoundOperation.h
@@ -147,11 +147,6 @@ public:
*/
virtual bool wants_variants () const { return false; }
- /**
- * @brief Returns true, if the processor wants to have merged primary inputs
- */
- virtual bool wants_merged () const { return false; }
-
void compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const
{
implement_compute_local (layout, interactions, results, max_vertex_count, area_ratio);
@@ -413,7 +408,6 @@ public:
virtual const TransformationReducer *vars () const;
virtual bool wants_variants () const;
- virtual bool wants_merged () const;
virtual void invalidate_cache () const;
@@ -641,7 +635,6 @@ public:
virtual const db::TransformationReducer *vars () const { return mp_vars; }
virtual bool wants_variants () const { return m_wants_variants; }
virtual db::Coord computed_dist () const { return m_op->dist (); }
- virtual bool wants_merged () const { return true; }
virtual std::vector inputs () const
{
@@ -892,7 +885,7 @@ class DB_PUBLIC CompoundRegionFilterOperationNode
: public CompoundRegionMultiInputOperationNode
{
public:
- CompoundRegionFilterOperationNode (PolygonFilterBase *filter, CompoundRegionOperationNode *input, bool owns_filter = false);
+ CompoundRegionFilterOperationNode (PolygonFilterBase *filter, CompoundRegionOperationNode *input, bool owns_filter = false, bool sum_of_set = false);
~CompoundRegionFilterOperationNode ();
// specifies the result type
@@ -903,14 +896,11 @@ public:
virtual const TransformationReducer *vars () const { return mp_filter->vars (); }
virtual bool wants_variants () const { return mp_filter->wants_variants (); }
- virtual bool wants_merged () const { return true; }
private:
PolygonFilterBase *mp_filter;
bool m_owns_filter;
-
- bool is_selected (const db::Polygon &p) const;
- bool is_selected (const db::PolygonRef &p) const;
+ bool m_sum_of_set;
template
void implement_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const
@@ -920,9 +910,15 @@ private:
child (0)->compute_local (layout, interactions, one, max_vertex_count, area_ratio);
- for (typename std::unordered_set::const_iterator p = one.front ().begin (); p != one.front ().end (); ++p) {
- if (is_selected (*p)) {
- results.front ().insert (*p);
+ if (m_sum_of_set) {
+ if (mp_filter->selected_set (one.front ())) {
+ results.front ().insert (one.front ().begin (), one.front ().end ());
+ }
+ } else {
+ for (typename std::unordered_set::const_iterator p = one.front ().begin (); p != one.front ().end (); ++p) {
+ if (mp_filter->selected (*p)) {
+ results.front ().insert (*p);
+ }
}
}
}
@@ -932,7 +928,7 @@ class DB_PUBLIC CompoundRegionEdgeFilterOperationNode
: public CompoundRegionMultiInputOperationNode
{
public:
- CompoundRegionEdgeFilterOperationNode (EdgeFilterBase *filter, CompoundRegionOperationNode *input, bool owns_filter = false);
+ CompoundRegionEdgeFilterOperationNode (EdgeFilterBase *filter, CompoundRegionOperationNode *input, bool owns_filter = false, bool sum_of = false);
~CompoundRegionEdgeFilterOperationNode ();
// specifies the result type
@@ -943,13 +939,11 @@ public:
virtual const TransformationReducer *vars () const { return mp_filter->vars (); }
virtual bool wants_variants () const { return mp_filter->wants_variants (); }
- virtual bool wants_merged () const { return true; }
private:
EdgeFilterBase *mp_filter;
bool m_owns_filter;
-
- bool is_selected (const db::Edge &p) const;
+ bool m_sum_of;
template
void implement_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const
@@ -959,9 +953,15 @@ private:
child (0)->compute_local (layout, interactions, one, max_vertex_count, area_ratio);
- for (typename std::unordered_set::const_iterator p = one.front ().begin (); p != one.front ().end (); ++p) {
- if (is_selected (*p)) {
- results.front ().insert (*p);
+ if (m_sum_of) {
+ if (mp_filter->selected (one.front ())) {
+ results.front ().insert (one.front ().begin (), one.front ().end ());
+ }
+ } else {
+ for (typename std::unordered_set::const_iterator p = one.front ().begin (); p != one.front ().end (); ++p) {
+ if (mp_filter->selected (*p)) {
+ results.front ().insert (*p);
+ }
}
}
}
@@ -982,7 +982,6 @@ public:
virtual const TransformationReducer *vars () const { return mp_filter->vars (); }
virtual bool wants_variants () const { return mp_filter->wants_variants (); }
- virtual bool wants_merged () const { return true; }
private:
EdgePairFilterBase *mp_filter;
@@ -1020,7 +1019,6 @@ public:
virtual const TransformationReducer *vars () const { return mp_proc->vars (); }
virtual bool wants_variants () const { return mp_proc->wants_variants (); }
- virtual bool wants_merged () const { return ! mp_proc->requires_raw_input (); }
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
@@ -1147,7 +1145,6 @@ public:
virtual const TransformationReducer *vars () const { return mp_proc->vars (); }
virtual bool wants_variants () const { return mp_proc->wants_variants (); }
- virtual bool wants_merged () const { return true; }
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
@@ -1188,7 +1185,6 @@ public:
virtual const TransformationReducer *vars () const { return mp_proc->vars (); }
virtual bool wants_variants () const { return mp_proc->wants_variants (); }
- virtual bool wants_merged () const { return true; }
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
@@ -1228,7 +1224,6 @@ public:
virtual const TransformationReducer *vars () const { return mp_proc->vars (); }
virtual bool wants_variants () const { return mp_proc->wants_variants (); }
- virtual bool wants_merged () const { return true; }
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
@@ -1269,7 +1264,6 @@ public:
virtual const TransformationReducer *vars () const { return mp_proc->vars (); }
virtual bool wants_variants () const { return mp_proc->wants_variants (); }
- virtual bool wants_merged () const { return true; }
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
@@ -1307,7 +1301,6 @@ public:
virtual const TransformationReducer *vars () const { return mp_proc->vars (); }
virtual bool wants_variants () const { return mp_proc->wants_variants (); }
- virtual bool wants_merged () const { return true; }
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
@@ -1358,7 +1351,6 @@ public:
virtual const TransformationReducer *vars () const { return mp_proc->vars (); }
virtual bool wants_variants () const { return mp_proc->wants_variants (); }
- virtual bool wants_merged () const { return true; }
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
@@ -1417,7 +1409,6 @@ public:
virtual OnEmptyIntruderHint on_empty_intruder_hint () const;
virtual db::Coord computed_dist () const;
- virtual bool wants_merged () const;
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const;
diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc
index a19407a65..b996e9bb2 100644
--- a/src/db/db/dbDeepEdges.cc
+++ b/src/db/db/dbDeepEdges.cc
@@ -37,6 +37,8 @@
#include "dbHierProcessor.h"
#include "dbEmptyEdges.h"
+#include
+
namespace db
{
diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc
index 1db2bada4..c2864fcff 100644
--- a/src/db/db/dbDeepRegion.cc
+++ b/src/db/db/dbDeepRegion.cc
@@ -1362,8 +1362,7 @@ Output *region_cop_impl (DeepRegion *region, db::CompoundRegionOperationNode &no
proc.set_base_verbosity (region->base_verbosity ());
proc.set_threads (region->deep_layer ().store ()->threads ());
- bool needs_merged = node.wants_merged ();
- const db::DeepLayer &polygons (needs_merged ? region->merged_deep_layer () : region->deep_layer ());
+ const db::DeepLayer &polygons (region->merged_deep_layer ());
std::vector other_layers;
for (std::vector::const_iterator i = inputs.begin (); i != inputs.end (); ++i) {
diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h
index 55dae894e..2954f81e9 100644
--- a/src/db/db/dbEdgesDelegate.h
+++ b/src/db/db/dbEdgesDelegate.h
@@ -34,6 +34,7 @@
#include "dbGenericShapeIterator.h"
#include
+#include
namespace db {
@@ -115,10 +116,16 @@ public:
/**
* @brief Filters the edge
- * If this method returns true, the polygon is kept. Otherwise it's discarded.
+ * If this method returns true, the edge is kept. Otherwise it's discarded.
*/
virtual bool selected (const db::Edge &edge) const = 0;
+ /**
+ * @brief Filters the edge set
+ * If this method returns true, the edges are kept. Otherwise they are discarded.
+ */
+ virtual bool selected (const std::unordered_set &edge) const = 0;
+
/**
* @brief Returns the transformation reducer for building cell variants
* This method may return 0. In this case, not cell variants are built.
diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h
index bfb0650b4..0fd02770a 100644
--- a/src/db/db/dbEdgesUtils.h
+++ b/src/db/db/dbEdgesUtils.h
@@ -24,11 +24,14 @@
#define HDR_dbEdgesUtils
#include "dbCommon.h"
+#include "dbHash.h"
#include "dbEdges.h"
#include "dbBoxScanner.h"
#include "dbPolygonTools.h"
#include "tlSelect.h"
+#include
+
namespace db {
class PolygonSink;
@@ -65,12 +68,19 @@ struct DB_PUBLIC EdgeLengthFilter
*/
virtual bool selected (const db::Edge &edge) const
{
- length_type l = edge.length ();
- if (! m_inverse) {
- return l >= m_lmin && l < m_lmax;
- } else {
- return ! (l >= m_lmin && l < m_lmax);
+ return check (edge.length ());
+ }
+
+ /**
+ * @brief Returns true if the total edge length matches the criterion
+ */
+ bool selected (const std::unordered_set &edges) const
+ {
+ length_type l = 0;
+ for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) {
+ l += e->length ();
}
+ return check (l);
}
/**
@@ -101,6 +111,15 @@ private:
length_type m_lmin, m_lmax;
bool m_inverse;
db::MagnificationReducer m_vars;
+
+ virtual bool check (length_type l) const
+ {
+ if (! m_inverse) {
+ return l >= m_lmin && l < m_lmax;
+ } else {
+ return ! (l >= m_lmin && l < m_lmax);
+ }
+ }
};
/**
@@ -179,6 +198,19 @@ struct DB_PUBLIC EdgeOrientationFilter
*/
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 &edges) const
+ {
+ for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) {
+ if (! selected (*e)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* @brief This filter is not isotropic
*/
diff --git a/src/db/db/dbPolygon.h b/src/db/db/dbPolygon.h
index ee4a1dd3c..7c5b4aa3c 100644
--- a/src/db/db/dbPolygon.h
+++ b/src/db/db/dbPolygon.h
@@ -3192,7 +3192,15 @@ public:
}
}
- /**
+ /**
+ * @brief The area ratio of the polygon
+ */
+ double area_ratio () const
+ {
+ return this->obj ().area_ratio ();
+ }
+
+ /**
* @brief The area of the polygon
*/
area_type area () const
diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h
index f6296cded..e6d4d568b 100644
--- a/src/db/db/dbRegionDelegate.h
+++ b/src/db/db/dbRegionDelegate.h
@@ -64,6 +64,24 @@ public:
*/
virtual bool selected (const db::Polygon &polygon) const = 0;
+ /**
+ * @brief Filters the polygon reference
+ * If this method returns true, the polygon is kept. Otherwise it's discarded.
+ */
+ virtual bool selected (const db::PolygonRef &polygon) const = 0;
+
+ /**
+ * @brief Filters the set of polygons (taking the overall properties)
+ * If this method returns true, the polygon is kept. Otherwise it's discarded.
+ */
+ virtual bool selected_set (const std::unordered_set &polygons) const = 0;
+
+ /**
+ * @brief Filters the set of polygon references (taking the overall properties)
+ * If this method returns true, the polygon is kept. Otherwise it's discarded.
+ */
+ virtual bool selected_set (const std::unordered_set &polygons) const = 0;
+
/**
* @brief Returns the transformation reducer for building cell variants
* This method may return 0. In this case, not cell variants are built.
diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h
index ca827eecc..73da44aad 100644
--- a/src/db/db/dbRegionLocalOperations.h
+++ b/src/db/db/dbRegionLocalOperations.h
@@ -29,6 +29,9 @@
#include "dbLocalOperation.h"
#include "dbEdgeProcessor.h"
+#include
+#include
+
namespace db
{
diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc
index 088dc3971..086bbd21c 100644
--- a/src/db/db/dbRegionUtils.cc
+++ b/src/db/db/dbRegionUtils.cc
@@ -446,13 +446,8 @@ RegionPerimeterFilter::RegionPerimeterFilter (perimeter_type pmin, perimeter_typ
// .. nothing yet ..
}
-bool RegionPerimeterFilter::selected (const db::Polygon &poly) const
+bool RegionPerimeterFilter::check (perimeter_type p) const
{
- perimeter_type p = 0;
- for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end () && p < m_pmax; ++e) {
- p += (*e).length ();
- }
-
if (! m_inverse) {
return p >= m_pmin && p < m_pmax;
} else {
@@ -460,6 +455,34 @@ bool RegionPerimeterFilter::selected (const db::Polygon &poly) const
}
}
+bool RegionPerimeterFilter::selected (const db::Polygon &poly) const
+{
+ return check (poly.perimeter ());
+}
+
+bool RegionPerimeterFilter::selected (const db::PolygonRef &poly) const
+{
+ return check (poly.perimeter ());
+}
+
+bool RegionPerimeterFilter::selected_set (const std::unordered_set &poly) const
+{
+ perimeter_type ps = 0;
+ for (std::unordered_set::const_iterator p = poly.begin (); p != poly.end (); ++p) {
+ ps += p->perimeter ();
+ }
+ return check (ps);
+}
+
+bool RegionPerimeterFilter::selected_set (const std::unordered_set &poly) const
+{
+ perimeter_type ps = 0;
+ for (std::unordered_set::const_iterator p = poly.begin (); p != poly.end (); ++p) {
+ ps += p->perimeter ();
+ }
+ return check (ps);
+}
+
const TransformationReducer *RegionPerimeterFilter::vars () const
{
return &m_vars;
@@ -474,10 +497,8 @@ RegionAreaFilter::RegionAreaFilter (area_type amin, area_type amax, bool inverse
// .. nothing yet ..
}
-bool
-RegionAreaFilter::selected (const db::Polygon &poly) const
+bool RegionAreaFilter::check (area_type a) const
{
- area_type a = poly.area ();
if (! m_inverse) {
return a >= m_amin && a < m_amax;
} else {
@@ -485,6 +506,34 @@ RegionAreaFilter::selected (const db::Polygon &poly) const
}
}
+bool RegionAreaFilter::selected (const db::Polygon &poly) const
+{
+ return check (poly.area ());
+}
+
+bool RegionAreaFilter::selected (const db::PolygonRef &poly) const
+{
+ return check (poly.area ());
+}
+
+bool RegionAreaFilter::selected_set (const std::unordered_set &poly) const
+{
+ area_type as = 0;
+ for (std::unordered_set::const_iterator p = poly.begin (); p != poly.end (); ++p) {
+ as += p->area ();
+ }
+ return check (as);
+}
+
+bool RegionAreaFilter::selected_set (const std::unordered_set &poly) const
+{
+ area_type as = 0;
+ for (std::unordered_set::const_iterator p = poly.begin (); p != poly.end (); ++p) {
+ as += p->area ();
+ }
+ return check (as);
+}
+
const TransformationReducer *
RegionAreaFilter::vars () const
{
@@ -506,6 +555,12 @@ RectilinearFilter::selected (const db::Polygon &poly) const
return poly.is_rectilinear () != m_inverse;
}
+bool
+RectilinearFilter::selected (const db::PolygonRef &poly) const
+{
+ return poly.is_rectilinear () != m_inverse;
+}
+
const TransformationReducer *
RectilinearFilter::vars () const
{
@@ -532,6 +587,17 @@ RectangleFilter::selected (const db::Polygon &poly) const
return ok != m_inverse;
}
+bool
+RectangleFilter::selected (const db::PolygonRef &poly) const
+{
+ bool ok = poly.is_box ();
+ if (ok && m_is_square) {
+ db::Box box = poly.box ();
+ ok = box.width () == box.height ();
+ }
+ return ok != m_inverse;
+}
+
const TransformationReducer *RectangleFilter::vars () const
{
return 0;
@@ -547,10 +613,9 @@ RegionBBoxFilter::RegionBBoxFilter (value_type vmin, value_type vmax, bool inver
}
bool
-RegionBBoxFilter::selected (const db::Polygon &poly) const
+RegionBBoxFilter::check (const db::Box &box) const
{
value_type v = 0;
- db::Box box = poly.box ();
if (m_parameter == BoxWidth) {
v = box.width ();
} else if (m_parameter == BoxHeight) {
@@ -569,6 +634,18 @@ RegionBBoxFilter::selected (const db::Polygon &poly) const
}
}
+bool
+RegionBBoxFilter::selected (const db::Polygon &poly) const
+{
+ return check (poly.box ());
+}
+
+bool
+RegionBBoxFilter::selected (const db::PolygonRef &poly) const
+{
+ return check (poly.box ());
+}
+
const TransformationReducer *
RegionBBoxFilter::vars () const
{
@@ -588,29 +665,54 @@ RegionRatioFilter::RegionRatioFilter (double vmin, bool min_included, double vma
// .. nothing yet ..
}
-bool RegionRatioFilter::selected (const db::Polygon &poly) const
+template
+static double compute_ratio_parameter (const P &poly, RegionRatioFilter::parameter_type parameter)
{
double v = 0.0;
- if (m_parameter == AreaRatio) {
+
+ if (parameter == RegionRatioFilter::AreaRatio) {
+
v = poly.area_ratio ();
- } else if (m_parameter == AspectRatio) {
+
+ } else if (parameter == RegionRatioFilter::AspectRatio) {
+
db::Box box = poly.box ();
double f = std::max (box.height (), box.width ());
double d = std::min (box.height (), box.width ());
if (d < 1) {
return false;
}
+
v = f / d;
- } else if (m_parameter == RelativeHeight) {
+
+ } else if (parameter == RegionRatioFilter::RelativeHeight) {
+
db::Box box = poly.box ();
double f = box.height ();
double d = box.width ();
if (d < 1) {
return false;
}
+
v = f / d;
+
}
+ return v;
+}
+
+bool RegionRatioFilter::selected (const db::Polygon &poly) const
+{
+ double v = compute_ratio_parameter (poly, m_parameter);
+
+ bool ok = (v - (m_vmin_included ? -db::epsilon : db::epsilon) > m_vmin && v - (m_vmax_included ? db::epsilon : -db::epsilon) < m_vmax);
+ return ok != m_inverse;
+}
+
+bool RegionRatioFilter::selected (const db::PolygonRef &poly) const
+{
+ double v = compute_ratio_parameter (poly, m_parameter);
+
bool ok = (v - (m_vmin_included ? -db::epsilon : db::epsilon) > m_vmin && v - (m_vmax_included ? db::epsilon : -db::epsilon) < m_vmax);
return ok != m_inverse;
}
diff --git a/src/db/db/dbRegionUtils.h b/src/db/db/dbRegionUtils.h
index 5e6e54ed7..bcaa65645 100644
--- a/src/db/db/dbRegionUtils.h
+++ b/src/db/db/dbRegionUtils.h
@@ -59,6 +59,21 @@ struct DB_PUBLIC RegionPerimeterFilter
*/
virtual bool selected (const db::Polygon &poly) const;
+ /**
+ * @brief Returns true if the polygon's perimeter matches the criterion
+ */
+ virtual bool selected (const db::PolygonRef &poly) const;
+
+ /**
+ * @brief Returns true if the polygon's perimeter sum matches the criterion
+ */
+ virtual bool selected_set (const std::unordered_set &polygons) const;
+
+ /**
+ * @brief Returns true if the polygon's perimeter sum matches the criterion
+ */
+ virtual bool selected_set (const std::unordered_set &polygons) const;
+
/**
* @brief This filter is isotropic
*/
@@ -78,6 +93,8 @@ private:
perimeter_type m_pmin, m_pmax;
bool m_inverse;
db::MagnificationReducer m_vars;
+
+ bool check (perimeter_type p) const;
};
/**
@@ -108,6 +125,21 @@ struct DB_PUBLIC RegionAreaFilter
*/
virtual bool selected (const db::Polygon &poly) const;
+ /**
+ * @brief Returns true if the polygon's area matches the criterion
+ */
+ virtual bool selected (const db::PolygonRef &poly) const;
+
+ /**
+ * @brief Returns true if the polygon's area sum matches the criterion
+ */
+ virtual bool selected_set (const std::unordered_set &polygons) const;
+
+ /**
+ * @brief Returns true if the polygon's area sum matches the criterion
+ */
+ virtual bool selected_set (const std::unordered_set &polygons) const;
+
/**
* @brief This filter is isotropic
*/
@@ -127,6 +159,42 @@ private:
area_type m_amin, m_amax;
bool m_inverse;
db::MagnificationReducer m_vars;
+
+ bool check (area_type a) const;
+};
+
+/**
+ * @brief A filter implementation which implements the set filters through "all must match"
+ */
+
+struct DB_PUBLIC AllMustMatchFilter
+ : public PolygonFilterBase
+{
+ /**
+ * @brief Constructor
+ */
+ AllMustMatchFilter () { }
+
+ virtual bool selected_set (const std::unordered_set &polygons) const
+ {
+ for (std::unordered_set::const_iterator p = polygons.begin (); p != polygons.end (); ++p) {
+ if (! selected (*p)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual bool selected_set (const std::unordered_set &polygons) const
+ {
+ for (std::unordered_set::const_iterator p = polygons.begin (); p != polygons.end (); ++p) {
+ if (! selected (*p)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
};
/**
@@ -136,7 +204,7 @@ private:
*/
struct DB_PUBLIC RectilinearFilter
- : public PolygonFilterBase
+ : public AllMustMatchFilter
{
/**
* @brief Constructor
@@ -145,10 +213,15 @@ struct DB_PUBLIC RectilinearFilter
RectilinearFilter (bool inverse);
/**
- * @brief Returns true if the polygon's area matches the criterion
+ * @brief Returns true if the polygon is rectilinear
*/
virtual bool selected (const db::Polygon &poly) const;
+ /**
+ * @brief Returns true if the polygon is rectilinear
+ */
+ virtual bool selected (const db::PolygonRef &poly) const;
+
/**
* @brief This filter does not need variants
*/
@@ -175,7 +248,7 @@ private:
*/
struct DB_PUBLIC RectangleFilter
- : public PolygonFilterBase
+ : public AllMustMatchFilter
{
/**
* @brief Constructor
@@ -184,10 +257,15 @@ struct DB_PUBLIC RectangleFilter
RectangleFilter (bool is_square, bool inverse);
/**
- * @brief Returns true if the polygon's area matches the criterion
+ * @brief Returns true if the polygon is a rectangle
*/
virtual bool selected (const db::Polygon &poly) const;
+ /**
+ * @brief Returns true if the polygon is a rectangle
+ */
+ virtual bool selected (const db::PolygonRef &poly) const;
+
/**
* @brief This filter does not need variants
*/
@@ -225,7 +303,7 @@ private:
*/
struct DB_PUBLIC RegionBBoxFilter
- : public PolygonFilterBase
+ : public AllMustMatchFilter
{
typedef db::Box::distance_type value_type;
@@ -250,10 +328,15 @@ struct DB_PUBLIC RegionBBoxFilter
RegionBBoxFilter (value_type vmin, value_type vmax, bool inverse, parameter_type parameter);
/**
- * @brief Returns true if the polygon's area matches the criterion
+ * @brief Returns true if the polygon's bounding box matches the criterion
*/
virtual bool selected (const db::Polygon &poly) const;
+ /**
+ * @brief Returns true if the polygon's bounding box matches the criterion
+ */
+ virtual bool selected (const db::PolygonRef &poly) const;
+
/**
* @brief This filter is isotropic unless the parameter is width or height
*/
@@ -275,6 +358,8 @@ private:
parameter_type m_parameter;
db::MagnificationReducer m_isotropic_vars;
db::MagnificationAndOrientationReducer m_anisotropic_vars;
+
+ bool check (const db::Box &box) const;
};
/**
@@ -291,7 +376,7 @@ private:
*/
struct DB_PUBLIC RegionRatioFilter
- : public PolygonFilterBase
+ : public AllMustMatchFilter
{
/**
* @brief The parameters available
@@ -316,6 +401,11 @@ struct DB_PUBLIC RegionRatioFilter
*/
virtual bool selected (const db::Polygon &poly) const;
+ /**
+ * @brief Returns true if the polygon's area matches the criterion
+ */
+ virtual bool selected (const db::PolygonRef &poly) const;
+
/**
* @brief This filter is isotropic unless the parameter is width or height
*/
diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc
index dde1c2a51..5fb0fa3b6 100644
--- a/src/db/db/gsiDeclDbCompoundOperation.cc
+++ b/src/db/db/gsiDeclDbCompoundOperation.cc
@@ -326,6 +326,12 @@ static db::CompoundRegionOperationNode *new_edge_length_filter (db::CompoundRegi
return new db::CompoundRegionEdgeFilterOperationNode (new db::EdgeLengthFilter (lmin, lmax, inverse), input, true /*processor is owned*/);
}
+static db::CompoundRegionOperationNode *new_edge_length_sum_filter (db::CompoundRegionOperationNode *input, bool inverse, db::Edge::distance_type lmin, db::Edge::distance_type lmax)
+{
+ check_non_null (input, "input");
+ return new db::CompoundRegionEdgeFilterOperationNode (new db::EdgeLengthFilter (lmin, lmax, inverse), input, true /*processor is owned*/, true /*sum*/);
+}
+
static db::CompoundRegionOperationNode *new_edge_orientation_filter (db::CompoundRegionOperationNode *input, bool inverse, double amin, bool include_amin, double amax, bool include_amax)
{
check_non_null (input, "input");
@@ -472,12 +478,24 @@ static db::CompoundRegionOperationNode *new_perimeter_filter (db::CompoundRegion
return new db::CompoundRegionFilterOperationNode (new db::RegionPerimeterFilter (pmin, pmax, inverse), input, true);
}
+static db::CompoundRegionOperationNode *new_perimeter_sum_filter (db::CompoundRegionOperationNode *input, bool inverse, db::coord_traits::perimeter_type pmin, db::coord_traits::perimeter_type pmax)
+{
+ check_non_null (input, "input");
+ return new db::CompoundRegionFilterOperationNode (new db::RegionPerimeterFilter (pmin, pmax, inverse), input, true, true /*sum of set*/);
+}
+
static db::CompoundRegionOperationNode *new_area_filter (db::CompoundRegionOperationNode *input, bool inverse, db::coord_traits::area_type amin, db::coord_traits::area_type amax)
{
check_non_null (input, "input");
return new db::CompoundRegionFilterOperationNode (new db::RegionAreaFilter (amin, amax, inverse), input, true);
}
+static db::CompoundRegionOperationNode *new_area_sum_filter (db::CompoundRegionOperationNode *input, bool inverse, db::coord_traits::area_type amin, db::coord_traits::area_type amax)
+{
+ check_non_null (input, "input");
+ return new db::CompoundRegionFilterOperationNode (new db::RegionAreaFilter (amin, amax, inverse), input, true, true /*sum of set*/);
+}
+
static db::CompoundRegionOperationNode *new_rectilinear_filter (db::CompoundRegionOperationNode *input, bool inverse)
{
check_non_null (input, "input");
@@ -659,11 +677,19 @@ Class decl_CompoundRegionOperationNode ("db", "
"This node renders the input if the perimeter is between pmin and pmax (exclusively). If 'inverse' is set to true, the "
"input shape is returned if the perimeter is less than pmin (exclusively) or larger than pmax (inclusively)."
) +
+ gsi::constructor ("new_perimeter_sum_filter", &new_perimeter_sum_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("amin", 0), gsi::arg ("amax", std::numeric_limits::area_type>::max (), "max"),
+ "@brief Creates a node filtering the input by area sum.\n"
+ "Like \\new_perimeter_filter, but applies to the sum of all shapes in the current set.\n"
+ ) +
gsi::constructor ("new_area_filter", &new_area_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("amin", 0), gsi::arg ("amax", std::numeric_limits::area_type>::max (), "max"),
"@brief Creates a node filtering the input by area.\n"
"This node renders the input if the area is between amin and amax (exclusively). If 'inverse' is set to true, the "
"input shape is returned if the area is less than amin (exclusively) or larger than amax (inclusively)."
) +
+ gsi::constructor ("new_area_sum_filter", &new_area_sum_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("amin", 0), gsi::arg ("amax", std::numeric_limits::area_type>::max (), "max"),
+ "@brief Creates a node filtering the input by area sum.\n"
+ "Like \\new_area_filter, but applies to the sum of all shapes in the current set.\n"
+ ) +
gsi::constructor ("new_bbox_filter", &new_bbox_filter, gsi::arg ("input"), gsi::arg ("parameter"), gsi::arg ("inverse", false), gsi::arg ("pmin", 0), gsi::arg ("pmax", std::numeric_limits::area_type>::max (), "max"),
"@brief Creates a node filtering the input by bounding box parameters.\n"
"This node renders the input if the specified bounding box parameter of the input shape is between pmin and pmax (exclusively). If 'inverse' is set to true, the "
@@ -687,6 +713,9 @@ Class decl_CompoundRegionOperationNode ("db", "
gsi::constructor ("new_edge_length_filter", &new_edge_length_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("lmin", 0), gsi::arg ("lmax", std::numeric_limits::max (), "max"),
"@brief Creates a node filtering edges by their length.\n"
) +
+ gsi::constructor ("new_edge_length_sum_filter", &new_edge_length_sum_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("lmin", 0), gsi::arg ("lmax", std::numeric_limits::max (), "max"),
+ "@brief Creates a node filtering edges by their length sum (over the local set).\n"
+ ) +
gsi::constructor ("new_edge_orientation_filter", &new_edge_orientation_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("amin"), gsi::arg ("include_amin"), gsi::arg ("amax"), gsi::arg ("include_amax"),
"@brief Creates a node filtering edges by their orientation.\n"
) +
diff --git a/src/drc/drc/built-in-macros/_drc_complex_ops.rb b/src/drc/drc/built-in-macros/_drc_complex_ops.rb
index 3ae166c66..131c8ff06 100644
--- a/src/drc/drc/built-in-macros/_drc_complex_ops.rb
+++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb
@@ -27,55 +27,206 @@ module DRC
# The following global functions are relevant for the DRC expressions:
#
# @ul
-# @li \global#angle @/li
-# @li \global#area @/li
-# @li \global#area_ratio @/li
-# @li \global#bbox_area_ratio @/li
-# @li \global#bbox_height @/li
-# @li \global#bbox_max @/li
-# @li \global#bbox_min @/li
-# @li \global#bbox_width @/li
-# @li \global#case @/li
-# @li \global#corners @/li
-# @li \global#covering @/li
-# @li \global#enc @/li
-# @li \global#enclosing @/li
-# @li \global#extent_refs @/li
-# @li \global#extents @/li
-# @li \global#foreign @/li
-# @li \global#holes @/li
-# @li \global#hulls @/li
-# @li \global#if_all @/li
-# @li \global#if_any @/li
-# @li \global#if_none @/li
-# @li \global#inside @/li
-# @li \global#interacting @/li
-# @li \global#iso @/li
-# @li \global#length @/li
-# @li \global#middle @/li
-# @li \global#notch @/li
-# @li \global#odd_polygons @/li
-# @li \global#outside @/li
-# @li \global#overlap @/li
-# @li \global#overlapping @/li
-# @li \global#perimeter @/li
-# @li \global#primary @/li
-# @li \global#rectangles @/li
-# @li \global#rectilinear @/li
-# @li \global#relative_height @/li
-# @li \global#rounded_corners @/li
-# @li \global#secondary @/li
-# @li \global#separation @/li
-# @li \global#sep @/li
-# @li \global#sized @/li
-# @li \global#smoothed @/li
-# @li \global#space @/li
-# @li \global#squares @/li
-# @li \global#width @/li
+# @li \\global#angle @/li
+# @li \\global#area @/li
+# @li \\global#area_ratio @/li
+# @li \\global#bbox_area_ratio @/li
+# @li \\global#bbox_height @/li
+# @li \\global#bbox_max @/li
+# @li \\global#bbox_min @/li
+# @li \\global#bbox_width @/li
+# @li \\global#case @/li
+# @li \\global#corners @/li
+# @li \\global#covering @/li
+# @li \\global#enc @/li
+# @li \\global#enclosing @/li
+# @li \\global#extent_refs @/li
+# @li \\global#extents @/li
+# @li \\global#foreign @/li
+# @li \\global#holes @/li
+# @li \\global#hulls @/li
+# @li \\global#if_all @/li
+# @li \\global#if_any @/li
+# @li \\global#if_none @/li
+# @li \\global#inside @/li
+# @li \\global#interacting @/li
+# @li \\global#iso @/li
+# @li \\global#length @/li
+# @li \\global#middle @/li
+# @li \\global#notch @/li
+# @li \\global#odd_polygons @/li
+# @li \\global#outside @/li
+# @li \\global#overlap @/li
+# @li \\global#overlapping @/li
+# @li \\global#perimeter @/li
+# @li \\global#primary @/li
+# @li \\global#rectangles @/li
+# @li \\global#rectilinear @/li
+# @li \\global#relative_height @/li
+# @li \\global#rounded_corners @/li
+# @li \\global#secondary @/li
+# @li \\global#separation @/li
+# @li \\global#sep @/li
+# @li \\global#sized @/li
+# @li \\global#smoothed @/li
+# @li \\global#space @/li
+# @li \\global#squares @/li
+# @li \\global#width @/li
# @/ul
#
# The following documentation will list the methods available for DRC expression objects.
+# A base class for implementing ranges that can be put into a condition
+module DRCComparable
+
+ attr_accessor :reverse
+ attr_accessor :original
+ attr_accessor :lt, :le, :gt, :ge
+ attr_accessor :description
+ attr_accessor :mode_or_supported
+ attr_accessor :mode_or
+
+ def _init_comparable
+ self.reverse = false
+ self.original = nil
+ self.le = nil
+ self.ge = nil
+ self.lt = nil
+ self.gt = nil
+ self.gt = nil
+ self.description = ""
+ self.mode_or_supported = false
+ self.mode_or = false
+ end
+
+ def _check_bounds
+ if ! self.mode_or && (self.lt || self.le) && (self.gt || self.ge)
+ epsilon = 1e-10
+ lower = self.ge ? self.ge - epsilon : self.gt + epsilon
+ upper = self.le ? self.le + epsilon : self.lt - epsilon
+ if lower > upper - epsilon
+ raise("'" + self.description + "': lower bound is larger than upper bound")
+ end
+ end
+ end
+
+ def set_lt(value)
+ (self.lt || self.le) && raise("'" + self.description + "' already has an upper bound of " + ("%.12g" % (self.lt || self.le)))
+ self.lt = value
+ self._check_bounds
+ end
+
+ def set_le(value)
+ (self.lt || self.le) && raise("'" + self.description + "' already has an upper bound of " + ("%.12g" % (self.lt || self.le)))
+ self.le = value
+ self._check_bounds
+ end
+
+ def set_gt(value)
+ (self.gt || self.ge) && raise("'" + self.description + "' already has an lower bound of " + ("%.12g" % (self.gt || self.ge)))
+ self.gt = value
+ self._check_bounds
+ end
+
+ def set_ge(value)
+ (self.gt || self.ge) && raise("'" + self.description + "' already has an lower bound of " + ("%.12g" % (self.gt || self.ge)))
+ self.ge = value
+ self._check_bounds
+ end
+
+ def coerce(something)
+ reversed = self.dup
+ reversed.reverse = true
+ reversed.original = self
+ [ reversed, something ]
+ end
+
+ def _self_or_original
+ return (self.original || self).dup
+ end
+
+ def !=(other)
+ if self.respond_to?(:inverted)
+ res = self.==(other).inverted
+ else
+ if !self.mode_or_supported
+ raise("!= operator is not allowed for '" + self.description + "'")
+ end
+ if !(other.is_a?(Float) || other.is_a?(Integer))
+ raise("!= operator needs a numerical argument for '" + self.description + "' argument")
+ end
+ res = self._self_or_original
+ res.mode_or = true
+ res.set_lt(other)
+ res.set_gt(other)
+ end
+ res
+ end
+
+ def ==(other)
+ if !(other.is_a?(Float) || other.is_a?(Integer))
+ raise("== operator needs a numerical argument for '" + self.description + "' argument")
+ end
+ res = self._self_or_original
+ res.set_le(other)
+ res.set_ge(other)
+ return res
+ end
+
+ def <(other)
+ if !(other.is_a?(Float) || other.is_a?(Integer))
+ raise("< operator needs a numerical argument for '" + self.description + "' argument")
+ end
+ res = self._self_or_original
+ if reverse
+ res.set_gt(other)
+ else
+ res.set_lt(other)
+ end
+ return res
+ end
+
+ def <=(other)
+ if !(other.is_a?(Float) || other.is_a?(Integer))
+ raise("<= operator needs a numerical argument for '" + self.description + "' argument")
+ end
+ res = self._self_or_original
+ if reverse
+ res.set_ge(other)
+ else
+ res.set_le(other)
+ end
+ return res
+ end
+
+ def >(other)
+ if !(other.is_a?(Float) || other.is_a?(Integer))
+ raise("> operator needs a numerical argument for '" + self.description + "' argument")
+ end
+ res = self._self_or_original
+ if reverse
+ res.set_lt(other)
+ else
+ res.set_gt(other)
+ end
+ return res
+ end
+
+ def >=(other)
+ if !(other.is_a?(Float) || other.is_a?(Integer))
+ raise(">= operator needs a numerical argument for '" + self.description + "' argument")
+ end
+ res = self._self_or_original
+ if reverse
+ res.set_le(other)
+ else
+ res.set_ge(other)
+ end
+ return res
+ end
+
+end
+
class DRCOpNode
attr_accessor :description
@@ -239,7 +390,23 @@ CODE
# The plain function is equivalent to "primary.area".
def area
- DRCOpNodeAreaFilter::new(@engine, self)
+ DRCOpNodeAreaFilter::new(@engine, self, false)
+ end
+
+ # %DRC%
+ # @name area_sum
+ # @brief Selects the input polygons if the sum of all areas meets the condition
+ # @synopsis expression.area_sum (in condition)
+ #
+ # Returns the input polygons if the sum of their areas meets the specified
+ # condition. This condition is evaluated on the total of all shapes generated in one step of the
+ # "drc" loop. As there is a single primary in each loop iteration, "primary.area_sum" is
+ # equivalent to "primary.area".
+ #
+ # See \Layer#drc for more details about comparison specs.
+
+ def area_sum
+ DRCOpNodeAreaFilter::new(@engine, self, true)
end
# %DRC%
@@ -293,7 +460,23 @@ CODE
# The plain function is equivalent to "primary.perimeter".
def perimeter
- DRCOpNodePerimeterFilter::new(@engine, self)
+ DRCOpNodePerimeterFilter::new(@engine, self, false)
+ end
+
+ # %DRC%
+ # @name perimeter_sum
+ # @brief Selects the input polygons if the sum of all perimeters meets the condition
+ # @synopsis expression.perimeter_sum (in condition)
+ #
+ # Returns the input polygons if the sum of their perimeters meets the specified
+ # condition. This condition is evaluated on the total of all shapes generated in one step of the
+ # "drc" loop. As there is a single primary in each loop iteration, "primary.perimeter_sum" is
+ # equivalent to "primary.perimeter".
+ #
+ # See \Layer#drc for more details about comparison specs.
+
+ def perimeter_sum
+ DRCOpNodePerimeterFilter::new(@engine, self, true)
end
# %DRC%
@@ -482,7 +665,22 @@ CODE
# The plain function is equivalent to "primary.length".
def length
- DRCOpNodeEdgeLengthFilter::new(@engine, self)
+ DRCOpNodeEdgeLengthFilter::new(@engine, self, false)
+ end
+
+ # %DRC%
+ # @name length_sum
+ # @brief Selects the input edges if the sum of their lengths meets the condition
+ # @synopsis expression.length_sum (in condition)
+ #
+ # Returns the input edges if the sum of their lengths meets the specified
+ # condition. This condition is evaluated on the total of all edges generated in one step of the
+ # "drc" loop.
+ #
+ # See \Layer#drc for more details about comparison specs.
+
+ def length_sum
+ DRCOpNodeEdgeLengthFilter::new(@engine, self, true)
end
# %DRC%
@@ -1236,18 +1434,14 @@ class DRCOpNodeCase < DRCOpNode
end
class DRCOpNodeWithCompare < DRCOpNode
+
+ include DRCComparable
attr_accessor :reverse
- attr_accessor :original
- attr_accessor :lt, :le, :gt, :ge, :arg
- attr_accessor :mode_or_supported
- attr_accessor :mode_or
- def initialize(engine, original = nil, reverse = false)
+ def initialize(engine)
super(engine)
- self.reverse = reverse
- self.original = original
- self.description = original ? original.description : "BasicWithCompare"
+ self.description = "BasicWithCompare"
self.mode_or = false
self.mode_or_supported = false
end
@@ -1269,129 +1463,6 @@ class DRCOpNodeWithCompare < DRCOpNode
end
end
- def _check_bounds
- if ! self.mode_or && (self.lt || self.le) && (self.gt || self.ge)
- epsilon = 1e-10
- lower = self.ge ? self.ge - epsilon : self.gt + epsilon
- upper = self.le ? self.le + epsilon : self.lt - epsilon
- if lower > upper - epsilon
- raise("Lower bound is larger than upper bound")
- end
- end
- end
-
- def set_lt(value)
- (self.lt || self.le) && raise("'" + self.description + "' already has an upper bound of " + ("%.12g" % (self.lt || self.le)))
- self.lt = value
- self._check_bounds
- end
-
- def set_le(value)
- (self.lt || self.le) && raise("'" + self.description + "' already has an upper bound of " + ("%.12g" % (self.lt || self.le)))
- self.le = value
- self._check_bounds
- end
-
- def set_gt(value)
- (self.gt || self.ge) && raise("'" + self.description + "' already has an lower bound of " + ("%.12g" % (self.gt || self.ge)))
- self.gt = value
- self._check_bounds
- end
-
- def set_ge(value)
- (self.gt || self.ge) && raise("'" + self.description + "' already has an lower bound of " + ("%.12g" % (self.gt || self.ge)))
- self.ge = value
- self._check_bounds
- end
-
- def coerce(something)
- [ DRCOpNodeWithCompare::new(@engine, self, true), something ]
- end
-
- def _self_or_original
- return (self.original || self).dup
- end
-
- def !=(other)
- if self.respond_to?(:inverted)
- res = self.==(other).inverted
- else
- if !self.mode_or_supported
- raise("!= operator is not allowed for '" + self.description + "'")
- end
- if !(other.is_a?(Float) || other.is_a?(Integer))
- raise("!= operator needs a numerical argument for '" + self.description + "' argument")
- end
- res = self._self_or_original
- res.mode_or = true
- res.set_lt(other)
- res.set_gt(other)
- end
- res
- end
-
- def ==(other)
- if !(other.is_a?(Float) || other.is_a?(Integer))
- raise("== operator needs a numerical argument for '" + self.description + "' argument")
- end
- res = self._self_or_original
- res.set_le(other)
- res.set_ge(other)
- return res
- end
-
- def <(other)
- if !(other.is_a?(Float) || other.is_a?(Integer))
- raise("< operator needs a numerical argument for '" + self.description + "' argument")
- end
- res = self._self_or_original
- if reverse
- res.set_gt(other)
- else
- res.set_lt(other)
- end
- return res
- end
-
- def <=(other)
- if !(other.is_a?(Float) || other.is_a?(Integer))
- raise("<= operator needs a numerical argument for '" + self.description + "' argument")
- end
- res = self._self_or_original
- if reverse
- res.set_ge(other)
- else
- res.set_le(other)
- end
- return res
- end
-
- def >(other)
- if !(other.is_a?(Float) || other.is_a?(Integer))
- raise("> operator needs a numerical argument for '" + self.description + "' argument")
- end
- res = self._self_or_original
- if reverse
- res.set_lt(other)
- else
- res.set_gt(other)
- end
- return res
- end
-
- def >=(other)
- if !(other.is_a?(Float) || other.is_a?(Integer))
- raise(">= operator needs a numerical argument for '" + self.description + "' argument")
- end
- res = self._self_or_original
- if reverse
- res.set_le(other)
- else
- res.set_ge(other)
- end
- return res
- end
-
end
class DRCOpNodeCountFilter < DRCOpNodeWithCompare
@@ -1431,16 +1502,18 @@ class DRCOpNodeAreaFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :inverse
+ attr_accessor :sum
- def initialize(engine, input)
+ def initialize(engine, input, sum)
super(engine)
self.input = input
self.inverse = false
self.description = "area"
+ self.sum = sum
end
def _description_for_dump
- self.inverse ? "area" : "not_area"
+ (self.inverse ? "area" : "not_area") + (self.sum ? "_sum" : "")
end
def do_create_node(cache)
@@ -1449,7 +1522,7 @@ class DRCOpNodeAreaFilter < DRCOpNodeWithCompare
if self.lt || self.le
args << (self.lt ? @engine._make_area_value(self.lt) : @engine._make_area_value(self.le) + 1)
end
- RBA::CompoundRegionOperationNode::new_area_filter(*args)
+ RBA::CompoundRegionOperationNode::send(self.sum ? :new_area_sum_filter : :new_area_filter, *args)
end
def inverted
@@ -1464,16 +1537,18 @@ class DRCOpNodeEdgeLengthFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :inverse
+ attr_accessor :sum
- def initialize(engine, input)
+ def initialize(engine, input, sum)
super(engine)
self.input = input
self.inverse = false
self.description = "length"
+ self.sum = sum
end
def _description_for_dump
- self.inverse ? "length" : "not_length"
+ (self.inverse ? "length" : "not_length") + (self.sum ? "_sum" : "")
end
def do_create_node(cache)
@@ -1491,7 +1566,7 @@ class DRCOpNodeEdgeLengthFilter < DRCOpNodeWithCompare
args << (self.lt ? @engine._make_value(self.lt) : @engine._make_value(self.le) + 1)
end
- RBA::CompoundRegionOperationNode::new_edge_length_filter(*args)
+ RBA::CompoundRegionOperationNode::send(self.sum ? :new_edge_length_sum_filter : :new_edge_length_filter, *args)
end
@@ -1551,16 +1626,18 @@ class DRCOpNodePerimeterFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :inverse
+ attr_accessor :sum
- def initialize(engine, input)
+ def initialize(engine, input, sum)
super(engine)
self.input = input
self.inverse = false
self.description = "perimeter"
+ self.sum = sum
end
def _description_for_dump
- self.inverse ? "perimeter" : "not_perimeter"
+ (self.inverse ? "perimeter" : "not_perimeter") + (self.sum ? "_sum" : "")
end
def do_create_node(cache)
@@ -1569,7 +1646,7 @@ class DRCOpNodePerimeterFilter < DRCOpNodeWithCompare
if self.lt || self.le
args << (self.lt ? @engine._make_value(self.lt) : @engine._make_value(self.le) + 1)
end
- RBA::CompoundRegionOperationNode::new_perimeter_filter(*args)
+ RBA::CompoundRegionOperationNode::send(self.sum ? :new_perimeter_sum_filter : :new_perimeter_filter, *args)
end
def inverted
diff --git a/src/drc/drc/built-in-macros/_drc_cop_integration.rb b/src/drc/drc/built-in-macros/_drc_cop_integration.rb
index 5ce9df8c7..9c7d95395 100644
--- a/src/drc/drc/built-in-macros/_drc_cop_integration.rb
+++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb
@@ -51,6 +51,8 @@ module DRC
# merged, so the secondary polygons may be partial. This is important when
# using measurement operations like "area" on secondary polygons.
#
+ # @h3 Checks @/h3
+ #
# Here is an example for a generic DRC operation which performs a width
# check for less than 0.5.um on the primary shapes. It uses the \global#width operator:
#
@@ -88,13 +90,90 @@ module DRC
# out = in.drc(width(projection) < 0.5.um)
# @/code
#
- # The "drc" function supports filter operators. These select input or derived polygons
- # based on their properties. These filters are:
+ #
+ # @h3 Edges and edge pairs @/h3
+ #
+ # Although the "drc" function operates on polygon layers, internally it is
+ # able to handle edge and edge pair types too. Some operations generate edge pairs,
+ # some other generate edges. As results from one operation can be processed further
+ # in the DRC expressions, methods are available to filter, process and convert
+ # these types.
+ #
+ # For example, the check produces edge pairs which can be converted into polygons
+ # using the "polygons" method:
+ #
+ # @code
+ # out = in.drc((width(projection) < 0.5.um).polygons)
+ # @/code
+ #
+ # Note the subtle difference: when putting the "polygons" method inside the "drc"
+ # brackets, it is executed locally on every checked primary polygon. The result
+ # may be identical to the global conversion:
+ #
+ # @code
+ # # same, but with "global" conversion:
+ # out = in.drc(width(projection) < 0.5.um).polygons
+ # @/code
+ #
+ # but having the check polygons inside the loop opens new opportunities and
+ # is more efficient in general.
+ #
+ # Conversion methods are:
#
# @ul
- # @li "\global#area": select polygons based on their area @/li
- # @li "\global#perimeter": select polygons based on their perimeter @/li
- # @li "\global#bbox_min", "\global#bbox_max", "\global#bbox_width", "\global#bbox_height": select polygons based on their bounding box properties @/li
+ # @li \DRC#polygons: converts edge pairs to polygons @/li
+ # @li \DRC#extended, \DRC#extended_in, \DRC#extended_out: converts edges to polygons @/li
+ # @li \DRC#first_edges, \DRC#second_edges: extracts edges from edge pairs @/li
+ # @li \DRC#edges: decomposes edge pairs and polygons into edges @/li
+ # @li \DRC#corners: can extract corners from polygons @/li
+ # @/ul
+ #
+ # This example decomposes the primary polygons into edges:
+ #
+ # @code
+ # out = in.drc(primary.edges)
+ # @/code
+ #
+ # (for backward compatibility you cannot abbreviate "primary.edges" simply as "edges" like
+ # other functions).
+ #
+ # The previous isn't quite exciting as it is equivalent to
+ #
+ # @code
+ # # Same as above
+ # out = in.edges
+ # @/code
+ #
+ # But it gets more interesting as within the loop, "edges" delivers the edge set for
+ # each individual polygon. So this will give you the edges of polygons with more than four corners:
+ #
+ # @code
+ # out = in.drc(primary.edges.count > 4)
+ # @/code
+ #
+ # The same result can be achieved with classic DRC with "interact" and a figure count, but
+ # at a much higher computation cost.
+ #
+ # @h3 Edge and edge/polygon operations @/h3
+ #
+ # The "drc" framework supports the following edge and edge/polygon operations:
+ #
+ # @ul
+ # @li Edge vs. edge and edge vs. polygon booleans @/li
+ # @li Edge vs. polygon interactions (\DRC#interacting, \DRC#overlapping) @/li
+ # @li Edge sampling (\DRC#start_segments, \DRC#centers, \DRC#end_segments) @/li
+ # @/ul
+ #
+ # @h3 Filters @/h3
+ #
+ # Filter operators select input polygons or edges based on their properties. These filters are:
+ #
+ # @ul
+ # @li "\DRC#area": selects polygons based on their area @/li
+ # @li "\DRC#perimeter": selects polygons based on their perimeter @/li
+ # @li "\DRC#bbox_min", "\global#bbox_max", "\global#bbox_width", "\global#bbox_height": selects polygons based on their bounding box properties @/li
+ # @li "\DRC#length": selects edges based on their length @/li
+ # @li "\DRC#angle": selects edges based on their orientation @/li
# @/ul
#
# For example, to select polygons with an area larger than one square micrometer, use:
@@ -125,6 +204,8 @@ module DRC
# It's recommended therefore to use the measurement functions on primary polygons
# only.
#
+ # @h3 Filter predicates @/h3
+ #
# The "drc" feature also supports some predicates. "predicates" are boolean values
# indicating a certain condition. A predicate filter works in a way that it only
# passes the polygons
@@ -132,6 +213,7 @@ module DRC
#
# @ul
# @li "\global#rectangles": Filters rectangles @/li
+ # @li "\global#squares": Filters squares @/li
# @li "\global#rectilinear": Filters rectilinear ("Manhattan") polygons @/li
# @/ul
#
@@ -139,6 +221,8 @@ module DRC
# standalone, so they act on primary shapes. It's possible to use the predicates
# on computed shapes or secondary input, but that may not render the desired results.
#
+ # @h3 Logical NOT operator @/h3
+ #
# The "!" operator will evaluate the expression behind it and return the
# current primary shape if the input is empty and return an empty polygon set
# if not. Hence the following filter will deliver all polygons which are
@@ -148,6 +232,8 @@ module DRC
# out = in.drc(! rectangles)
# @/code
#
+ # @h3 Logical combination operators @/h3
+ #
# The logical "if_any" or "if_all" statements allow connecting multiple
# conditions and evaluate to "true" (means: a non-empty shape set) if either
# on input is a non-empty shape set ("if_any") or if all inputs are non-empty
@@ -175,6 +261,7 @@ module DRC
# out = in.drc(if_any(rectangles.sized(100.nm), primary))
# @/code
#
+ # @h3 Polygon manipulations @/h3
#
# The "drc" operations feature polygon manipulations where the input is
# either the primary polygon or derived shapes.
@@ -209,6 +296,32 @@ module DRC
# out = l1.drc((secondary(l2) & primary).area > 1.0)
# @/code
#
+ # @h3 Quantifiers @/h3
+ #
+ # Some filters operate on properties of the full, local shape set.
+ # While the loop is executed, the DRC expressions will collect shapes, either
+ # from the primary, it's neighborhood (secondary) or by deriving shape sets.
+ #
+ # Obviously the primary is a simple one: it consists of a single shape, because
+ # this is how the loop operates. Derived shape sets however can be more complex.
+ # "Quantifiers" allow to assess properties of the complete, per-primary shape
+ # set. A simple one is "count" which checks if the number of shapes within
+ # a shape set is within a given range.
+ #
+ # Obviously, "primary.count == 1" is always true. The following condition will
+ # select all primary shapes which have more than 13 corners:
+ #
+ # @code
+ # out = in.drc(if_any(primary.corners.count > 13))
+ # @/code
+ #
+ # Note an important detail here: the "if_any" function will render primary
+ # @b polygons @/b, if the expression inside gives a non-empty result. Without
+ # "if_any", the result would be the output of "count" which is the set of all
+ # corners where the corner count is larger than 13.
+ #
+ # @h3 Expressions as objects @/h3
+ #
# The expression inside the "drc" function is a Ruby object and can be
# stored in variables. If you need the same expression multiple times, it can be
# more efficient to use the same Ruby object. In this example, the same expression
@@ -229,28 +342,7 @@ module DRC
# or the boolean operation. But when the "drc" function executes the operation it will
# only compute the area once as it is represented by the same Ruby object.
#
- #
- # The "drc" functionality also offers support for edge pairs and edges. Edge pairs
- # are the results of check operations and can be turned into polygons using the
- # "polygons" method:
- #
- # @code
- # drc = in.drc((width < 0.5.um).polygons)
- # @/code
- #
- # Similarly, polygons can be converted into edges:
- #
- # @code
- # drc = in.drc(primary.edges)
- # @/code
- #
- # The "drc" framework supports edge vs. edge and edge vs. polygon booleans, edge
- # filters (\global#length, \global#angle), edge vs. polygon interactions (\global#interacting, \global#overlapping),
- # edge sampling (\global#start_segments, \global#centers, \global#end_segments) and edge to polygon
- # conversions (\global#extended, \global#extended_in, \global#extended_out). Edge pairs
- # can be converted into polygons and edges and separated into first and second edges (\global#first_edges,
- # \global#second_edges).
- #
+ # @h3 Summary @/h3
#
# The bottom line is: DRC expressions are quite rich and there is a lot more to be said and written.
# More formal details about the bits and pieces can be found in the \DRC# class documentation.
@@ -1014,8 +1106,7 @@ CODE
elsif a.is_a?(DRCLayer)
other = self._make_node(a)
elsif a.is_a?(DRCProjectionLimits)
- minp = self._make_value(a.min)
- maxp = self._make_value(a.max)
+ (minp, maxp) = a.get_limits(self)
elsif a.is_a?(DRCShielded)
shielded = a.value
else
diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb
index 4efcde2cc..fb9c051fc 100644
--- a/src/drc/drc/built-in-macros/_drc_tags.rb
+++ b/src/drc/drc/built-in-macros/_drc_tags.rb
@@ -2,157 +2,6 @@
module DRC
- # A base class for implementing ranges that can be put into a condition
- module DRCComparable
-
- attr_accessor :reverse
- attr_accessor :original
- attr_accessor :lt, :le, :gt, :ge
- attr_accessor :description
- attr_accessor :mode_or_supported
- attr_accessor :mode_or
-
- def _init_comparable
- self.reverse = false
- self.original = nil
- self.le = nil
- self.ge = nil
- self.lt = nil
- self.gt = nil
- self.gt = nil
- self.description = ""
- self.mode_or_supported = false
- self.mode_or = false
- end
-
- def _check_bounds
- if ! self.mode_or && (self.lt || self.le) && (self.gt || self.ge)
- epsilon = 1e-10
- lower = self.ge ? self.ge - epsilon : self.gt + epsilon
- upper = self.le ? self.le + epsilon : self.lt - epsilon
- if lower > upper - epsilon
- raise("'" + self.description + "': lower bound is larger than upper bound")
- end
- end
- end
-
- def set_lt(value)
- (self.lt || self.le) && raise("'" + self.description + "' already has an upper bound of " + ("%.12g" % (self.lt || self.le)))
- self.lt = value
- self._check_bounds
- end
-
- def set_le(value)
- (self.lt || self.le) && raise("'" + self.description + "' already has an upper bound of " + ("%.12g" % (self.lt || self.le)))
- self.le = value
- self._check_bounds
- end
-
- def set_gt(value)
- (self.gt || self.ge) && raise("'" + self.description + "' already has an lower bound of " + ("%.12g" % (self.gt || self.ge)))
- self.gt = value
- self._check_bounds
- end
-
- def set_ge(value)
- (self.gt || self.ge) && raise("'" + self.description + "' already has an lower bound of " + ("%.12g" % (self.gt || self.ge)))
- self.ge = value
- self._check_bounds
- end
-
- def coerce(something)
- reversed = self.dup
- reversed.reverse = true
- reversed.original = self
- [ reversed, something ]
- end
-
- def _self_or_original
- return (self.original || self).dup
- end
-
- def !=(other)
- if self.respond_to?(:inverted)
- res = self.==(other).inverted
- else
- if !self.mode_or_supported
- raise("!= operator is not allowed for '" + self.description + "'")
- end
- if !(other.is_a?(Float) || other.is_a?(Integer))
- raise("!= operator needs a numerical argument for '" + self.description + "' argument")
- end
- res = self._self_or_original
- res.mode_or = true
- res.set_lt(other)
- res.set_gt(other)
- end
- res
- end
-
- def ==(other)
- if !(other.is_a?(Float) || other.is_a?(Integer))
- raise("== operator needs a numerical argument for '" + self.description + "' argument")
- end
- res = self._self_or_original
- res.set_le(other)
- res.set_ge(other)
- return res
- end
-
- def <(other)
- if !(other.is_a?(Float) || other.is_a?(Integer))
- raise("< operator needs a numerical argument for '" + self.description + "' argument")
- end
- res = self._self_or_original
- if reverse
- res.set_gt(other)
- else
- res.set_lt(other)
- end
- return res
- end
-
- def <=(other)
- if !(other.is_a?(Float) || other.is_a?(Integer))
- raise("<= operator needs a numerical argument for '" + self.description + "' argument")
- end
- res = self._self_or_original
- if reverse
- res.set_ge(other)
- else
- res.set_le(other)
- end
- return res
- end
-
- def >(other)
- if !(other.is_a?(Float) || other.is_a?(Integer))
- raise("> operator needs a numerical argument for '" + self.description + "' argument")
- end
- res = self._self_or_original
- if reverse
- res.set_lt(other)
- else
- res.set_gt(other)
- end
- return res
- end
-
- def >=(other)
- if !(other.is_a?(Float) || other.is_a?(Integer))
- raise(">= operator needs a numerical argument for '" + self.description + "' argument")
- end
- res = self._self_or_original
- if reverse
- res.set_le(other)
- else
- res.set_ge(other)
- end
- return res
- end
-
- end
-
# A wrapper for a named value which is stored in
# a variable for delayed execution
class DRCVar
diff --git a/src/drc/unit_tests/drcGenericTests.cc b/src/drc/unit_tests/drcGenericTests.cc
index 538925c36..f9521822b 100644
--- a/src/drc/unit_tests/drcGenericTests.cc
+++ b/src/drc/unit_tests/drcGenericTests.cc
@@ -208,3 +208,13 @@ TEST(14d)
{
run_test (_this, "14", true);
}
+
+TEST(15)
+{
+ run_test (_this, "15", false);
+}
+
+TEST(15d)
+{
+ run_test (_this, "15", true);
+}
diff --git a/src/lay/lay/doc/about/drc_ref_drc.xml b/src/lay/lay/doc/about/drc_ref_drc.xml
index cc85d54a3..446c3f7ec 100644
--- a/src/lay/lay/doc/about/drc_ref_drc.xml
+++ b/src/lay/lay/doc/about/drc_ref_drc.xml
@@ -28,47 +28,51 @@ out = in.drc((width < 2.0).polygons)
The following global functions are relevant for the DRC expressions:
The following documentation will list the methods available for DRC expression objects.
@@ -206,7 +210,61 @@ out = in.drc(primary.area < 2.0) # equivalent
The area method is available as a plain function or as a method on DRC expressions.
The plain function is equivalent to "primary.area".
-"bbox_height" - Selects the primary shape if its bounding box height is meeting the condition
+"area_ratio" - Selects the input polygon according to its area ratio (bounding box area by polygon area)
+
+Usage:
+
+expression.area_ratio (in condition)
+
+
+This operation is used in conditions to select shapes based on their area ratio.
+The area ratio is the ratio of bounding box vs. polygon area. It's a measure how
+"sparse" the polygons are and how good an approximation the bounding box is.
+The value is always larger or equal than 1. Boxes have a value of 1.
+
+This filter is applicable on polygon expressions. The result will be the input
+polygon if the condition is met.
+
+See Layer#drc for more details about comparison specs.
+
+The following example will select all polygons whose area ratio is larger than 3:
+
+
+out = in.drc(area_ratio > 3)
+out = in.drc(primary.area_ratio > 3) # equivalent
+
+
+The "area_ratio" method is available as a plain function or as a method on DRC# expressions.
+The plain function is equivalent to "primary.area_ratio".
+
+"bbox_aspect_ratio" - Selects the input polygon according to the aspect ratio of the bounding box
+
+Usage:
+
+expression.bbox_aspect_ratio (in condition)
+
+
+This operation is used in conditions to select shapes based on aspect ratios of their bounding boxes.
+The aspect ratio is computed by dividing the larger of width and height by the smaller of both.
+The aspect ratio is always larger or equal to 1. Square or square-boxed shapes have a
+bounding box aspect ratio of 1.
+
+This filter is applicable on polygon expressions. The result will be the input
+polygon if the bounding box condition is met.
+
+See Layer#drc for more details about comparison specs.
+
+The following example will select all polygons whose bounding box aspect ratio is larger than 3:
+
+
+out = in.drc(bbox_aspect_ratio > 3)
+out = in.drc(primary.bbox_aspect_ratio > 3) # equivalent
+
+
+The "bbox_aspect_ratio" method is available as a plain function or as a method on DRC# expressions.
+The plain function is equivalent to "primary.bbox_aspect_ratio".
+
+"bbox_height" - Selects the input polygon if its bounding box height is meeting the condition
Usage:
@@ -222,7 +280,7 @@ ones not rotated. This usually results in a larger computation effort and larger
The "bbox_height" method is available as a plain function or as a method on DRC expressions.
The plain function is equivalent to "primary.bbox_height".
-"bbox_max" - Selects the primary shape if its bounding box larger dimension is meeting the condition
+"bbox_max" - Selects the input polygon if its bounding box larger dimension is meeting the condition
Usage:
@@ -235,7 +293,7 @@ bounding box.
The "bbox_max" method is available as a plain function or as a method on DRC expressions.
The plain function is equivalent to "primary.bbox_max".
-"bbox_min" - Selects the primary shape if its bounding box smaller dimension is meeting the condition
+"bbox_min" - Selects the input polygon if its bounding box smaller dimension is meeting the condition
Usage:
@@ -259,7 +317,7 @@ out = in.drc(primary.bbox_min > 200.nm) # equivalent
The "bbox_min" method is available as a plain function or as a method on DRC# expressions.
The plain function is equivalent to "primary.bbox_min".
-"bbox_width" - Selects the primary shape if its bounding box width is meeting the condition
+"bbox_width" - Selects the input polygon if its bounding box width is meeting the condition
Usage:
@@ -554,7 +612,7 @@ wrap count rule in place.
This operation can be used as a plain function in which case it acts on primary
shapes or can be used as method on another DRC expression.
-"perimeter" - Selects the primary shape if the perimeter is meeting the condition
+"perimeter" - Selects the input polygon if the perimeter is meeting the condition
Usage:
@@ -635,6 +693,35 @@ out = in.drc(rectilinear)
out = in.drc(primary.rectilinear) # equivalent
+"relative_height" - Selects the input polygon according to the height vs. width of the bounding box
+
+Usage:
+
+expression.relative_height (in condition)
+
+
+This operation is used in conditions to select shapes based on the ratio of bounding box
+height vs. width. The taller the shape, the larger the value. Wide polygons have a value
+below 1. A square has a relative height of 1.
+
+This filter is applicable on polygon expressions. The result will be the input
+polygon if the condition is met.
+
+Don't use this method if you can use bbox_aspect_ratio , because the latter is
+isotropic and can be used hierarchically without generating rotation variants.
+
+See Layer#drc for more details about comparison specs.
+
+The following example will select all polygons whose relative height is larger than 3:
+
+
+out = in.drc(relative_height > 3)
+out = in.drc(primary.relative_height > 3) # equivalent
+
+
+The "relative_height" method is available as a plain function or as a method on DRC# expressions.
+The plain function is equivalent to "primary.bbox_aspect_ratio".
+
"rounded_corners" - Applies corner rounding
Usage:
@@ -684,6 +771,22 @@ This operation acts on polygons and applies polygon smoothing with the tolerance
The "smoothed" method is available as a plain function or as a method on DRC expressions.
The plain function is equivalent to "primary.smoothed".
+"squares" - Selects all polygons which are squares
+
+Usage:
+
+
+This operation can be used as a plain function in which case it acts on primary
+shapes or can be used as method on another DRC expression.
+The following example selects all squares:
+
+
+out = in.drc(squares)
+out = in.drc(primary.squares) # equivalent
+
+
"start_segments" - Returns the part at the beginning of each edge of the input
Usage:
diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml
index 72866dcdd..08a667dc8 100644
--- a/src/lay/lay/doc/about/drc_ref_global.xml
+++ b/src/lay/lay/doc/about/drc_ref_global.xml
@@ -43,6 +43,24 @@ polygons in the layer.
Without a layer argument, "area" represents an area filter for primary shapes in
DRC expressions (see Layer#drc and DRC#area for more details).
+"area_ratio" - Selects primary shapes based on the ratio of bounding box and polygon area
+
+Usage:
+
+area_ratio (in condition)
+
+
+See Layer#drc , area_ratio and DRC#area_ratio for more details.
+
+"bbox_aspect_ratio" - Selects primary shapes based on the aspect ratio of their bounding boxes
+
+Usage:
+
+bbox_aspect_ratio (in condition)
+
+
+See Layer#drc , bbox_aspect_ratio and DRC#bbox_aspect_ratio for more details.
+
"bbox_height" - Selects primary shapes based on their bounding box height
Usage:
@@ -364,13 +382,9 @@ applied to coners meeting a particular angle constraint.
Usage:
covering(other) (in conditions)
-covering(layer, other [, options ])
-This function can be used with a layer argument in which case it
-is equivalent to "layer.covering" (see Layer#covering ).
-
-Without a layer argument, this method represents the selector of primary shapes
+This method represents the selector of primary shapes
which entirely cover shapes from the other layer. This version can be put into
a condition indicating how many shapes of the other layer need to be covered.
Use this variant within DRC expressions (also see Layer#drc ).
@@ -567,6 +581,7 @@ out = in.drc(enclosing(other) <= 0.2.um)
out = in.drc(enclosing(other) > 0.2.um)
out = in.drc(enclosing(other) >= 0.2.um)
out = in.drc(enclosing(other) == 0.2.um)
+out = in.drc(enclosing(other) != 0.2.um)
out = in.drc(0.1.um <= enclosing(other) < 0.2.um)
@@ -759,13 +774,9 @@ this method.
Usage:
inside(other)
-inside(layer, other)
-This function can be used with a layer argument in which case it
-is equivalent to "layer.inside" (see Layer#inside ).
-
-Without a layer argument, this method represents the selector of primary shapes
+This method represents the selector of primary shapes
which are entirely inside shapes from the other layer.
Use this variant within DRC expressions (also see Layer#drc ).
@@ -774,12 +785,18 @@ Use this variant within DRC expressions (al
Usage:
interacting(other) (in conditions)
-interacting(layer, other [, options ])
See covering for a description of the use cases for this function.
When using "interacting", shapes are selected when the interact (overlap, touch)
shapes from the other layer.
+
+When using this method with a count, the operation may not render
+the correct results if the other input is not merged. By nature of the
+generic DRC feature, only those shapes that interact with the primary shape
+will be selected. If the other input is split into multiple polygons,
+not all components may be captured and the computed interaction count
+may be incorrect.
"is_deep?" - Returns true, if in deep mode
@@ -1066,13 +1083,9 @@ layout if is report database.
Usage:
outside(other)
-outside(layer, other)
-This function can be used with a layer argument in which case it
-is equivalent to "layer.outside" (see Layer#outside ).
-
-Without a layer argument, this method represents the selector of primary shapes
+This method represents the selector of primary shapes
which are entirely outside shapes from the other layer.
Use this variant within DRC expressions (also see Layer#drc ).
@@ -1092,12 +1105,18 @@ function provides a two-layer check. See there for details how to use this funct
Usage:
overlapping(other) (in conditions)
-overlapping(layer, other [, options ])
See covering for a description of the use cases for this function.
When using "overlapping", shapes are selected when the overlap
shapes from the other layer.
+
+When using this method with a count, the operation may not render
+the correct results if the other input is not merged. By nature of the
+generic DRC feature, only those shapes that interact with the primary shape
+will be selected. If the other input is split into multiple polygons,
+not all components may be captured and the computed interaction count
+may be incorrect.
"p" - Creates a point object
@@ -1206,6 +1225,15 @@ is equivalent to "layer.rectilinear" (see DRC expressions (see Layer#drc and DRC#rectilinear for more details).
+"relative_height" - Selects primary shapes based on the ratio of height and width of their bounding boxes
+
+Usage:
+
+relative_height (in condition)
+
+
+See Layer#drc , relative_height and DRC#relative_height for more details.
+
"report" - Specifies a report database for output
Usage:
@@ -1419,6 +1447,19 @@ For further methods on the source object see Source .
Provides a space check on the primary layer. Like width this
function provides a single-layer check. See there for details how to use this function.
+"squares" - Selects all polygons which are squares
+
+Usage:
+
+squares
+squares(layer)
+
+
+This function can be used with a layer argument in which case it
+is equivalent to "layer.squares" (see Layer#squares ). Without a layer
+argument, "squares" represents the rectangles filter for primary shapes in
+DRC expressions (see Layer#drc and DRC#squares for more details).
+
"target" - Specify the target layout
Usage:
@@ -1568,6 +1609,7 @@ out = in.drc(width <= 0.2.um)
out = in.drc(width > 0.2.um)
out = in.drc(width >= 0.2.um)
out = in.drc(width == 0.2.um)
+out = in.drc(width != 0.2.um)
out = in.drc(0.1.um <= width < 0.2.um)
diff --git a/src/lay/lay/doc/about/drc_ref_layer.xml b/src/lay/lay/doc/about/drc_ref_layer.xml
index d230f7ddb..e8e1f2ce9 100644
--- a/src/lay/lay/doc/about/drc_ref_layer.xml
+++ b/src/lay/lay/doc/about/drc_ref_layer.xml
@@ -366,18 +366,142 @@ merged, so the secondary polygons may be partial. This is important when
using measurement operations like "area" on secondary polygons.
Here is an example for a generic DRC operation which performs a width
-check for less than 0.5.um on the primary shapes.
+check for less than 0.5.um on the primary shapes. It uses the global#width operator:
out = in.drc(width < 0.5.um)
-Another example computes a boolean AND between two layers before selecting
-the result polygons with an area larger than 1 square micrometer:
+Other single or double-bounded conditions are available too, for example:
-other = ... another layer ..
-out = in.drc((primary & other).area > 1.0)
+out = in.drc(width <= 0.5.um)
+out = in.drc(width > 0.5.um)
+out = in.drc(width == 0.5.um)
+out = in.drc(width != 0.5.um)
+out = in.drc(0.2.um < width < 0.5.um)
+
+
+To specify the second input for a two-layer check, specify the second input
+with the check function. Here a two-layer separation check is used (global#separation ):
+
+
+l1 = input(1, 0)
+l2 = input(2, 0)
+out = l1.drc(separation(l2) < 0.5.um)
+
+
+The second input of this check function can be a computed expression. In this
+case the local loop will first evaluate the expression for the second input and
+then use it inside the check.
+
+Options for the checks are also specified inside the brackets. For example,
+to specify a projection metrics for width use:
+
+
+out = in.drc(width(projection) < 0.5.um)
+
+
+The "drc" function supports filter operators. These select input or derived polygons
+based on their properties. These filters are:
+
+
+
+For example, to select polygons with an area larger than one square micrometer, use:
+
+
+out = in.drc(area > 1.0)
+
+
+For the condition, use the usual numerical bounds like:
+
+
+out = in.drc(area == 1.0)
+out = in.drc(area <= 1.0)
+out = in.drc(0.2 < area < 1.0)
+
+
+The result of the area operation is the input polygon if the area condition is met.
+
+In the same fashion, "perimeter" applies to the perimeter of the polygon.
+"bbox_min" etc. will evaluate a particular dimensions of the polygon's bounding box and
+use the respective dimension for filtering the polygon.
+
+Note that it's basically possible to use the polygon filters on any kind of input.
+In fact, plain "area" for example is a shortcut for "global#primary .area" indicating that
+the area of primary shapes are supposed to be computed.
+However, any input other than the primary is not necessarily complete or it may
+consist of multiple polygons. Hence the computed values may be too big or too small.
+It's recommended therefore to use the measurement functions on primary polygons
+only.
+
+The "drc" feature also supports some predicates. "predicates" are boolean values
+indicating a certain condition. A predicate filter works in a way that it only
+passes the polygons
+The predicates available currently are:
+
+
+
+For the same reason as explained above, it's recommended to use these predicates
+standalone, so they act on primary shapes. It's possible to use the predicates
+on computed shapes or secondary input, but that may not render the desired results.
+
+The "!" operator will evaluate the expression behind it and return the
+current primary shape if the input is empty and return an empty polygon set
+if not. Hence the following filter will deliver all polygons which are
+not rectangles:
+
+
+out = in.drc(! rectangles)
+
+
+The logical "if_any" or "if_all" statements allow connecting multiple
+conditions and evaluate to "true" (means: a non-empty shape set) if either
+on input is a non-empty shape set ("if_any") or if all inputs are non-empty
+("if_all"). For example, this will select all polygons which are rectangles
+and whose area is larger than 20 quare micrometers:
+
+
+out = in.drc(if_all(rectangles, area > 20.0))
+
+
+In fact, "if_all" renders the result of the last expression, provided all
+previous ones are non-empty. So this operation will render rectangles
+sized by 100 nm and skip all other types of polygons:
+
+
+out = in.drc(if_all(rectangles, sized(100.nm)))
+
+
+Contrary to this, the "if_any" operation will render the first non-empty
+expression result and skip the following ones. So this example will
+size all rectangles by 100 nm and leave all other types of polygons
+untouched:
+
+
+out = in.drc(if_any(rectangles.sized(100.nm), primary))
+
+
+The "drc" operations feature polygon manipulations where the input is
+either the primary polygon or derived shapes.
+Manipulations include sizing ("global#sized "), corner rounding ("global#rounded_corners "), smoothing ("global#smoothed ")
+and boolean operations.
+
+This example computes a boolean AND between two layers before selecting
+the result polygons with an area larger than 1 square micrometer. Note that
+"primary" is a placeholder for the primary shape:
+
+
+l1 = input(1, 0)
+l2 = input(2, 0)
+out = l1.drc((primary & l2).area > 1.0)
This example demonstrates how the "drc" operation can improve performance: as the
@@ -385,7 +509,62 @@ boolean operation is computed locally and the result is discarded when no longer
less shapes need to be stored hence reducing the memory overhead and CPU time required
to manage these shapes.
-For more details about the expression see the DRC class documentation.
+Note that the precise form of the example above is
+
+
+out = l1.drc((primary & secondary(l2)).area > 1.0)
+
+
+The "global#secondar " operator indicates that "l2" is to be used as secondary input to the "drc" function. Only
+in this form, the operators of the boolean AND can be reversed:
+
+
+out = l1.drc((secondary(l2) & primary).area > 1.0)
+
+
+The expression inside the "drc" function is a Ruby object and can be
+stored in variables. If you need the same expression multiple times, it can be
+more efficient to use the same Ruby object. In this example, the same expression
+is used two times. Hence it's computed two times:
+
+
+out = l1.drc(((primary & l2).area == 1.0) + ((primary & l2).area == 2.0))
+
+
+A more efficient version is:
+
+
+overlap_area = (primary & l2).area
+out = l1.drc((overlap_area == 1.0) + (overlap_area == 2.0))
+
+
+Note that the first line prepares the operation, but does not execute the area computation
+or the boolean operation. But when the "drc" function executes the operation it will
+only compute the area once as it is represented by the same Ruby object.
+
+The "drc" functionality also offers support for edge pairs and edges. Edge pairs
+are the results of check operations and can be turned into polygons using the
+"polygons" method:
+
+
+drc = in.drc((width < 0.5.um).polygons)
+
+
+Similarly, polygons can be converted into edges:
+
+
+drc = in.drc(primary.edges)
+
+
+The "drc" framework supports edge vs. edge and edge vs. polygon booleans, edge
+filters (global#length , global#angle ), edge vs. polygon interactions (global#interacting , global#overlapping ),
+edge sampling (global#start_segments , global#centers , global#end_segments ) and edge to polygon
+conversions (global#extended , global#extended_in , global#extended_out ). Edge pairs
+can be converted into polygons and edges and separated into first and second edges (global#first_edges ,
+global#second_edges ).
+
+The bottom line is: DRC expressions are quite rich and there is a lot more to be said and written.
+More formal details about the bits and pieces can be found in the DRC class documentation.
"dup" - Duplicates a layer
@@ -1191,6 +1370,16 @@ i.e. all polygons are merged before non-rectangles are selected (see clean and raw ).
+"non_squares" - Selects all polygons from the input which are not squares
+
+Usage:
+
+
+This method is available for polygon layers. By default "merged" semantics applies,
+i.e. all polygons are merged before non-squares are selected (see clean and raw ).
+
"non_strict" - Marks a layer for non-strict handling
Usage:
@@ -1725,7 +1914,7 @@ l.raw
To avoid that, use the dup method to create a real (deep) copy.
-"rectangles" - Selects all rectangle polygons from the input
+"rectangles" - Selects all rectangles from the input
Usage:
@@ -2306,6 +2495,17 @@ The following image shows the effect of the space check:
+"squares" - Selects all squares from the input
+
+Usage:
+
+
+This method is available for polygon layers. By default "merged" semantics applies,
+i.e. all polygons are merged before squares are selected (see clean and raw ).
+non_squares will select all non-rectangles.
+
"start_segments" - Returns the part at the beginning of each edge
Usage:
@@ -2486,6 +2686,10 @@ make the check only consider edges enclosing angles of less than 45 degree. projection_limits(min, max) or projection_limits(min .. max) :
this option makes the check only consider edge pairs whose projected length on
each other is more or equal than min and less than max
+projecting (in condition) : This specification is equivalent to "projection_limits"
+but is more intuitive, as "projecting" is written with a condition, like
+"projecting < 2.um". Available operators are: "==", "<", "<=", ">" and ">=".
+Double-bounded ranges are also available, like: "0.5 <= projecting < 2.0".
transparent : performs the check without shielding (polygon layers only)
shielded : performs the check with shielding (polygon layers only)
@@ -2594,6 +2798,38 @@ 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.
+"with_area_ratio" - Selects polygons by the ratio of the bounding box area vs. polygon area
+
+Usage:
+
+layer.with_area_ratio(min .. max)
+layer.with_area_ratio(value)
+layer.with_area_ratio(min, max)
+
+
+The area ratio is a measure how far a polygon is approximated by it's
+bounding box. The value is always larger or equal to 1. Boxes have a
+area ratio of 1. Larger values mean more empty area inside the bounding box.
+
+This method is available for polygon layers only.
+
+"with_bbox_aspect_ratio" - Selects polygons by the aspect ratio of their bounding box
+
+Usage:
+
+layer.with_bbox_aspect_ratio(min .. max)
+layer.with_bbox_aspect_ratio(value)
+layer.with_bbox_aspect_ratio(min, max)
+
+
+The method selects polygons similar to with_area or with_perimeter .
+However, the measured value is the aspect ratio of the bounding
+box. It is the larger dimensions divided by the smaller one.
+The "thinner" the polygon, the larger the aspect ratio. A square
+bounding box gives an aspect ratio of 1.
+
+This method is available for polygon layers only.
+
"with_bbox_height" - Selects polygons by the height of the bounding box
Usage:
@@ -2691,6 +2927,24 @@ upper limit.
This method is available for polygon layers only.
+"with_relative_height" - Selects polygons by the ratio of the height vs. width of it's bounding box
+
+Usage:
+
+layer.with_relative_height(min .. max)
+layer.with_relative_height(value)
+layer.with_relative_height(min, max)
+
+
+The relative height is a measure how tall a polygon is. Tall polygons
+have values larger than 1, wide polygons have a value smaller than 1.
+Squares have a value of 1.
+
+Don't use this method when you can use with_area_ratio , which provides a
+similar measure but is isotropic.
+
+This method is available for polygon layers only.
+
"without_angle" - Selects edges by the their angle
Usage:
@@ -2719,18 +2973,29 @@ the given interval.
This method is available for polygon layers only.
-"without_bbox_height" - Selects polygons by the height of the bounding box
+"without_area_ratio" - Selects polygons by the aspect ratio of their bounding box
+
+Usage:
+
+layer.without_area_ratio(min .. max)
+layer.without_area_ratio(value)
+layer.without_area_ratio(min, max)
+
+
+The method provides the opposite filter for with_area_ratio .
+
+This method is available for polygon layers only.
+
+"without_bbox_height" - Selects polygons by the aspect ratio of their bounding box
Usage:
-layer.without_bbox_height(min .. max)
-layer.without_bbox_height(value)
-layer.without_bbox_height(min, max)
+layer.without_bbox_aspect_ratio(min .. max)
+layer.without_bbox_aspect_ratio(value)
+layer.without_bbox_aspect_ratio(min, max)
-The method selects polygons similar to without_area or without_perimeter .
-However, the measured dimension is the width of the
-bounding box.
+The method provides the opposite filter for with_bbox_aspect_ratio .
This method is available for polygon layers only.
@@ -2811,6 +3076,19 @@ the given interval.
This method is available for polygon layers only.
+"without_relative_height" - Selects polygons by the ratio of the height vs. width
+
+Usage:
+
+layer.without_relative_height(min .. max)
+layer.without_relative_height(value)
+layer.without_relative_height(min, max)
+
+
+The method provides the opposite filter for with_relative_height .
+
+This method is available for polygon layers only.
+
"xor" - Boolean XOR operation
Usage:
diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc
index 4d649ceb3..ba80f8b8c 100644
--- a/src/rba/rba/rba.cc
+++ b/src/rba/rba/rba.cc
@@ -35,6 +35,7 @@
#include "tlExpression.h"
#include "tlFileUtils.h"
#include "tlStream.h"
+#include "tlEnv.h"
#include "rba.h"
#include "rbaInspector.h"
@@ -77,74 +78,91 @@ namespace rba
// -------------------------------------------------------------------
// RubyStackTraceProvider definition and implementation
-class RBA_PUBLIC RubyStackTraceProvider
- : public gsi::StackTraceProvider
+RubyStackTraceProvider::RubyStackTraceProvider (const std::string &scope)
+ : m_scope (scope)
+{ }
+
+std::vector
+RubyStackTraceProvider::stack_trace () const
{
-public:
- RubyStackTraceProvider (const std::string &scope)
- : m_scope (scope)
- { }
+ std::vector bt;
+ bt.push_back (tl::BacktraceElement (rb_sourcefile (), rb_sourceline ()));
+ static ID id_caller = rb_intern ("caller");
+ rba_get_backtrace_from_array (rb_funcall (rb_mKernel, id_caller, 0), bt, 0);
+ return bt;
+}
- virtual std::vector stack_trace () const
- {
- std::vector bt;
- bt.push_back (tl::BacktraceElement (rb_sourcefile (), rb_sourceline ()));
- static ID id_caller = rb_intern ("caller");
- rba_get_backtrace_from_array (rb_funcall (rb_mKernel, id_caller, 0), bt, 0);
- return bt;
+size_t
+RubyStackTraceProvider::scope_index () const
+{
+ if (! m_scope.empty ()) {
+ return RubyStackTraceProvider::scope_index (stack_trace (), m_scope);
+ } else {
+ return 0;
}
+}
- virtual size_t scope_index () const
- {
- if (! m_scope.empty ()) {
- std::vector bt = stack_trace ();
- for (size_t i = 0; i < bt.size (); ++i) {
- if (bt[i].file == m_scope) {
- return i;
- }
+size_t
+RubyStackTraceProvider::scope_index (const std::vector &bt, const std::string &scope)
+{
+ if (! scope.empty ()) {
+
+ static int consider_scope = -1;
+
+ // disable scoped debugging (e.g. DRC script lines) if $KLAYOUT_RBA_DEBUG_SCOPE is set.
+ if (consider_scope < 0) {
+ consider_scope = tl::has_env ("KLAYOUT_RBA_DEBUG_SCOPE") ? 0 : 1;
+ }
+ if (! consider_scope) {
+ return 0;
+ }
+
+ for (size_t i = 0; i < bt.size (); ++i) {
+ if (bt[i].file == scope) {
+ return i;
}
}
- return 0;
}
+ return 0;
+}
- virtual int stack_depth () const
- {
- // NOTE: this implementation will provide an "internal stack depth".
- // It's not exactly the same than the length of the stack_trace vector length.
- // But the purpose is a relative compare, so efficiency is not sacrificed here
- // for unnecessary consistency.
- int d = 1;
- static ID id_caller = rb_intern ("caller");
- VALUE backtrace = rb_funcall (rb_mKernel, id_caller, 0);
- if (TYPE (backtrace) == T_ARRAY) {
- d += RARRAY_LEN(backtrace);
- }
- return d;
+int
+RubyStackTraceProvider::stack_depth () const
+{
+ // NOTE: this implementation will provide an "internal stack depth".
+ // It's not exactly the same than the length of the stack_trace vector length.
+ // But the purpose is a relative compare, so efficiency is not sacrificed here
+ // for unnecessary consistency.
+ int d = 1;
+ static ID id_caller = rb_intern ("caller");
+ VALUE backtrace = rb_funcall (rb_mKernel, id_caller, 0);
+ if (TYPE (backtrace) == T_ARRAY) {
+ d += RARRAY_LEN(backtrace);
}
+ return d;
+}
- // we could use this for ruby >= 1.9.3
+// we could use this for ruby >= 1.9.3
#if 0
- static int
- count_stack_levels(void *arg, VALUE file, int line, VALUE method)
- {
- *(int *)arg += 1;
- return 0;
- }
+static int
+RubyStackTraceProvider::count_stack_levels(void *arg, VALUE file, int line, VALUE method)
+{
+ *(int *)arg += 1;
+ return 0;
+}
- extern "C" int rb_backtrace_each (int (*iter)(void *arg, VALUE file, int line, VALUE method), void *arg);
+extern "C" int
+RubyStackTraceProvider::rb_backtrace_each (int (*iter)(void *arg, VALUE file, int line, VALUE method), void *arg);
- virutal int stack_depth ()
- {
- int l = 0;
- rb_backtrace_each(count_stack_levels, &l);
- return l;
- }
+virtual int
+RubyStackTraceProvider::stack_depth ()
+{
+ int l = 0;
+ rb_backtrace_each(count_stack_levels, &l);
+ return l;
+}
#endif
-private:
- const std::string &m_scope;
-};
-
// -------------------------------------------------------------------
// The lookup table for the method overload resolution
diff --git a/src/rba/rba/rba.h b/src/rba/rba/rba.h
index 8ed73ae00..41312de6f 100644
--- a/src/rba/rba/rba.h
+++ b/src/rba/rba/rba.h
@@ -216,6 +216,29 @@ public:
RubyInterpreterPrivateData *d;
};
+class RBA_PUBLIC RubyStackTraceProvider
+ : public gsi::StackTraceProvider
+{
+public:
+ RubyStackTraceProvider (const std::string &scope);
+
+ virtual std::vector stack_trace () const;
+ virtual size_t scope_index () const;
+ virtual int stack_depth () const;
+
+ static size_t scope_index (const std::vector &bt, const std::string &scope);
+
+ // we could use this for ruby >= 1.9.3
+#if 0
+ static int count_stack_levels(void *arg, VALUE file, int line, VALUE method);
+ extern "C" int rb_backtrace_each (int (*iter)(void *arg, VALUE file, int line, VALUE method), void *arg);
+ virtual int stack_depth ();
+#endif
+
+private:
+ const std::string &m_scope;
+};
+
}
#endif
diff --git a/src/rba/rba/rbaUtils.cc b/src/rba/rba/rbaUtils.cc
index db8a2611e..88705a08c 100644
--- a/src/rba/rba/rbaUtils.cc
+++ b/src/rba/rba/rbaUtils.cc
@@ -150,14 +150,7 @@ rba_check_error ()
rba_get_backtrace_from_array (rb_funcall (lasterr, rb_intern ("backtrace"), 0), bt, 0);
const std::string &ds = RubyInterpreter::instance ()->debugger_scope ();
- if (! ds.empty ()) {
- for (size_t i = 0; i < bt.size (); ++i) {
- if (bt [i].file == ds) {
- bt.erase (bt.begin (), bt.begin () + i);
- break;
- }
- }
- }
+ bt.erase (bt.begin (), bt.begin () + RubyStackTraceProvider::scope_index (bt, ds));
// parse the backtrace to get the line number
tl::BacktraceElement info;
diff --git a/src/rba/unit_tests/rba.cc b/src/rba/unit_tests/rbaTests.cc
similarity index 100%
rename from src/rba/unit_tests/rba.cc
rename to src/rba/unit_tests/rbaTests.cc
diff --git a/src/rba/unit_tests/unit_tests.pro b/src/rba/unit_tests/unit_tests.pro
index 42079651f..54ab31c3d 100644
--- a/src/rba/unit_tests/unit_tests.pro
+++ b/src/rba/unit_tests/unit_tests.pro
@@ -7,7 +7,7 @@ TARGET = rba_tests
include($$PWD/../../lib_ut.pri)
SOURCES = \
- rba.cc
+ rbaTests.cc
INCLUDEPATH += $$RBA_INC $$TL_INC $$DB_INC $$GSI_INC
DEPENDPATH += $$RBA_INC $$TL_INC $$DB_INC $$GSI_INC
diff --git a/testdata/drc/drcGenericTests_15.drc b/testdata/drc/drcGenericTests_15.drc
new file mode 100644
index 000000000..87bec31af
--- /dev/null
+++ b/testdata/drc/drcGenericTests_15.drc
@@ -0,0 +1,30 @@
+
+source $drc_test_source
+target $drc_test_target
+
+if $drc_test_deep
+ deep
+end
+
+l1 = input(1, 0)
+l2 = input(2, 0)
+l3 = input(3, 0)
+
+l1.output(1, 0)
+l2.output(2, 0)
+l3.output(3, 0)
+
+l1.drc((primary & secondary(l2)).edges.length_sum == 6.0).output(100, 0)
+l1.drc((primary & secondary(l2)).edges.length_sum <= 6.0).output(101, 0)
+
+l1.drc((primary & secondary(l2)).area_sum == 1.0).output(110, 0)
+l1.drc((primary & secondary(l2)).area_sum != 1.0).output(111, 0)
+l1.drc((primary & secondary(l2)).area_sum <= 2.0).output(112, 0)
+l1.drc((primary & secondary(l2)).area_sum >= 2.0).output(113, 0)
+
+l1.drc((primary & secondary(l2)).count >= 2.0).output(120, 0)
+l1.drc((primary & secondary(l2)).count != 2.0).output(121, 0)
+
+l1.drc((primary & secondary(l2)).perimeter_sum == 6.0).output(130, 0)
+l1.drc((primary & secondary(l2)).perimeter_sum <= 6.0).output(131, 0)
+
diff --git a/testdata/drc/drcGenericTests_15.gds b/testdata/drc/drcGenericTests_15.gds
new file mode 100644
index 0000000000000000000000000000000000000000..a916776f3f13efcbfba7fd5cb9e33834a44ab2e8
GIT binary patch
literal 1264
zcmaizKTpCy7{(vkauB2nDE`GbsL>dsMiY&RF@g*dNhF4WIQs$o01S@0yXnB@!rsoiQ9~39
zIbg^PZ)eXp_V2FWHuH_;+T~FhnQ#}M5T)#48CDb!wxPZ*`V9b!2~*uYSL6{en*h@@
zWF_(IH-h}3ckO5T{SkD{853f6uTadZN--ds(y8N~luio&SYN}b;~x{_A*IJN;oMO>
zhw~-sO$Tzs;Z~D>a9~pJts^lKWCgb?<#tBLCV$&VAnM#
zi&B1#0!-HJx=#If7d!pQ3%!HP-hN=92GBYZ2hV_!6PK(j2ISX1>X7`j!sKVNt{CW_
zo~Ky=k^aEBgiF>H1N{pvngtNazx3>qb;Usc>OIW@h~!_taml)(*WYmpCMq-wAkz9x
z?YLxJG0^Yt-(SBy9^jYq>4B)q;;yM2zF{Imwt2a$E({(JhD@?#4~
WS2f3urTmTqOxDv?-M>0@mW3ZZvZvqx
literal 0
HcmV?d00001
diff --git a/testdata/drc/drcGenericTests_au15.gds b/testdata/drc/drcGenericTests_au15.gds
new file mode 100644
index 0000000000000000000000000000000000000000..5414ddb8f530b5fbcb4f8d04967bc5d610f62327
GIT binary patch
literal 7522
zcmbtZzmH8p6h3?3?%NgiR}cgdAt54&f=EOV1+fXm78~{Igw>I3g@Rs3q0=ao{sK`b
z`~e~wA_@`Nh={o6yXSuI&dhyt-`R1C`@PJa@18SfzB6a$KIuzQEXl61V)B!W$&851
z$tnN0OcniwOXo$l^d`=ndvx;h>*wE3U3hi$*^_IFvTLG)*Y7W0xhgU~CDNZR+}ryz
zg?sNZJ`>saN^$c;k!>p?
zM7BFj*jJt#OZ6Vu2A{}whe`gvgXkp!?0peAFxMd49VYpQexa8L5d0$_8)Un~B>(su
z^b!Grf8u3>YfC7~=whhxs6~-C>fCKD>O}c!zf-_+|O?Gg3A`9lnAG{Sv{f$B^VtyszFbz+w8WpC|aC
ze#h2SKSab|^_?0&^{EIR^h*SPh<-i&P@jsJ|Fw%c|3L74{ch0pf4SCge{Ug$OwIPH
z2y8($U>4|G)a%i#JX?qiBjskSituP#SRdlJj%H~TI6j-LDk_7U-NSGOthS&%?=nTB
zl`TdV*JfYaqhhGOA+zAAH5yt=m{;x{CJ&i4igsI34jXMjQPiQ;L#fsa#H6tMk{zk<}W+~HxgpE25sc0Wc|t-p1~@W@AuN_SiiOt#{6*~aQrZ$}JK
zgK+t^XLRJ1qxVSjNn2dshpuc%)!S!ZkHrX0PVc2TZF|KGg
zZM4;~9(LHO*TKp|=da0DT%WBh>3EGlVl>^Dy3z*;s?juT}tSrfBREdz<9A^u&nh)!H}3a8MO^?lrOEU8*e_+{2P>
zW3^7gCJ$TZ=!!L^``(-Wxh+0-MKw04n#1@}owS_$1N+fRWjXhI{CJ1Nyc@2ew*PV6
zU{)x`=Y}@l%USmmt9!H>vO_ZdQSE0Xsu1M*xzE4V%wMdjI8XUpwO0G@@mm?n>Qo`@
z`?Y%*AAQ_+vFgJ2E{vbZ_UOBrXJ5a!;e3qmZ#XaGyBm@1(Ybl08UG;L*Yn*d;AiO9
z<1@a@X;b_Iez=z~>DS|H+<7M!7D=r7=B#Lwer&40kpnE$&?
z{Ik}tfS<8`dHk&P%j389PnQ1;0)9sR-)rKRrQd)bei1X(e~+J~|A3#N|NG7Ov-A`2
pGxX!}v-A`2GwNT!&zL`tpEZ90|9|H%qkabV*ONb!?sPsb{{VmcN6`QP
literal 0
HcmV?d00001
diff --git a/testdata/drc/drcGenericTests_au15d.gds b/testdata/drc/drcGenericTests_au15d.gds
new file mode 100644
index 0000000000000000000000000000000000000000..c614bd16f61b4964d8f1b288c9a0986456d59b6c
GIT binary patch
literal 5640
zcmbuCziSjh6vyA~-R+$*KQ1xBUl9=nMI?%a5)d&lg~5P9QiRm83mQ9x6rvW|+i8)`
zCWVDX{sOVE@DC74qXY}F2ogjb=Q}emH#522ot@nx-^=WL?#+8|=Dj&eDM=P-yq2`S
zQH=(Ps6&(XXKE+u-0U>bXuWxT>gCnjAKw0&oY}m%{(5PF#+x}@n$Az%U7$vrC>==T
zx2J=N{Qe7_y8LTBtX39Pb}*PA{2tsBeOA@=>!7=y)s}f6`M4CDb0$Q5l8Z<`>OE
zWn@ZFRvZHWIH2wur@QH0nxC8}T<_V;DI@I&)UR1scsEmB&P&xU^
zp^QK3oQ3;k_C&9I<=D!}R{>>3-_BPKWf6P%GWF(uY`y5kdnaZ_}+IRRe_6zz=J53kv%MD;*&EzkC
z#4I8x_$%+a%HvXP-nr!*U&JgTDB^eWMpt=UY9{ad@7p);Gk77seu5RP^=%=(zrp&o
z@_3$o(B__`){hv#4=9g&`M3NR;`cS8u{EtnmxcJ(PsGaOV{6)fbF0ef6|jhV1a?z+
z4>YqC#l9BX0)@wTsUloVdHD*Yyj@*_!V2v@P_#lJey)HK;>UAnXym>9AGAVkqqd`M
zhUSY`#&qZkt=(8+Z+P_~$S++D5Ihr=Kx$%3pBt!rf+&t1=PiuVbqYpI{R*qW=~jwf@Jp{fPKEe8m2B_=x@M@Dcmh;>+z{`D?Y&p#LDx
Bl*s@9
literal 0
HcmV?d00001