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: