diff --git a/src/db/db/dbEdgePairRelations.cc b/src/db/db/dbEdgePairRelations.cc index cdb1b3ea9..8f9721633 100644 --- a/src/db/db/dbEdgePairRelations.cc +++ b/src/db/db/dbEdgePairRelations.cc @@ -48,6 +48,37 @@ db::Edge::distance_type edge_projection (const db::Edge &a, const db::Edge &b) return db::coord_traits::rounded (a.double_length () * fabs (l2 - l1)); } +/** + * @brief Gets a flag indicating whether zero distance is included in the checks + */ +static bool include_zero_flag (collinear_mode_type coll_mode, const db::Edge &a, const db::Edge &b) +{ + if (coll_mode == AlwaysIncludeCollinear) { + + return true; + + } else if (coll_mode == AlwaysIncludeCollinear) { + + return false; + + } else { + + int s1 = a.side_of (b.p1 ()); + int s2 = a.side_of (b.p2 ()); + + if (s1 == 0 && s2 == 0) { + if (coll_mode == IncludeCollinearWhenTouch) { + return a.intersect (b); + } else if (coll_mode == IncludeCollinearWhenOverlap) { + return a.coincident (b); + } + } + + return false; + + } +} + /** * @brief Returns the part of the "other" edge which is on the inside side of e and within distance d * @@ -67,12 +98,7 @@ bool euclidian_near_part_of_edge (collinear_mode_type coll_mode, db::coord_trait int s1 = e.side_of (g.p1 ()); int s2 = e.side_of (g.p2 ()); - // "kissing corner" issue: force include zero if the edges are collinear and overlap. - bool include_zero = (coll_mode == AlwaysIncludeCollinear); - if (coll_mode == IncludeCollinearWhenTouch && s1 == 0 && s2 == 0 && e.intersect (g)) { - include_zero = true; - } - + bool include_zero = include_zero_flag (coll_mode, e, g); int thr = include_zero ? 0 : -1; // keep only part of other which is on the "inside" side of e @@ -211,12 +237,7 @@ static bool var_near_part_of_edge (collinear_mode_type coll_mode, db::coord_trai int s1 = e.side_of (g.p1 ()); int s2 = e.side_of (g.p2 ()); - // "kissing corner" issue: force include zero if the edges are collinear and overlap - bool include_zero = (coll_mode == AlwaysIncludeCollinear); - if (coll_mode == IncludeCollinearWhenTouch && s1 == 0 && s2 == 0 && e.intersect (g)) { - include_zero = true; - } - + bool include_zero = include_zero_flag (coll_mode, e, g); int thr = include_zero ? 0 : -1; // keep only part of other which is on the "inside" side of e diff --git a/src/db/db/dbEdgePairRelations.h b/src/db/db/dbEdgePairRelations.h index 08d2da83d..a0113bb37 100644 --- a/src/db/db/dbEdgePairRelations.h +++ b/src/db/db/dbEdgePairRelations.h @@ -108,14 +108,19 @@ enum collinear_mode_type { NeverIncludeCollinear = 0, /** - * @brief include collinear edges when they touch (e.g. kissing corner case) + * @brief include collinear edges when they share at least one common point */ IncludeCollinearWhenTouch = 1, + /** + * @brief include collinear edges when they share more than a single common point + */ + IncludeCollinearWhenOverlap = 2, + /** * @brief always include collinear edges */ - AlwaysIncludeCollinear = 2 + AlwaysIncludeCollinear = 3 }; /** diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index bfd95774d..b098a28f4 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -130,8 +130,8 @@ struct DB_PUBLIC RegionCheckOptions RectFilter _rect_filter = NoRectFilter, bool _negative = false, PropertyConstraint _prop_constraint = IgnoreProperties, - collinear_mode_type _include_zero = IncludeCollinearWhenTouch) - : EdgesCheckOptions (_whole_edges, _metrics, _ignore_angle, _min_projection, _max_projection, _include_zero), + collinear_mode_type _coll_mode = IncludeCollinearWhenTouch) + : EdgesCheckOptions (_whole_edges, _metrics, _ignore_angle, _min_projection, _max_projection, _coll_mode), shielded (_shielded), opposite_filter (_opposite_filter), rect_filter (_rect_filter), diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index dd88ca6d2..ee259cba6 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -390,23 +390,27 @@ static db::CompoundRegionOperationNode *new_edge_pair_to_second_edges (db::Compo return new db::CompoundRegionEdgePairToEdgeProcessingOperationNode (new db::EdgePairToSecondEdgesProcessor (), input, true /*processor is owned*/); } -static db::CompoundRegionOperationNode *new_check_node (db::CompoundRegionOperationNode *other, db::edge_relation_type rel, bool different_polygons, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative) +static db::CompoundRegionOperationNode *new_check_node (db::CompoundRegionOperationNode *other, db::edge_relation_type rel, bool different_polygons, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, db::collinear_mode_type collinear_mode, bool negative) { check_non_null (other, "other"); - return new db::CompoundRegionCheckOperationNode (0, other, rel, different_polygons, d, - db::RegionCheckOptions (whole_edges, - metrics, - ignore_angle.is_nil () ? 90 : ignore_angle.to_double (), - min_projection.is_nil () ? db::Region::distance_type (0) : min_projection.to (), - max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), - shielded, - opposite_filter, - rect_filter, - negative) - ); + + db::RegionCheckOptions options; + + options.whole_edges = whole_edges; + options.metrics = metrics; + options.ignore_angle = ignore_angle.is_nil () ? 90 : ignore_angle.to_double (); + options.min_projection = min_projection.is_nil () ? db::Region::distance_type (0) : min_projection.to (); + options.max_projection = max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (); + options.shielded = shielded; + options.opposite_filter = opposite_filter; + options.rect_filter = rect_filter; + options.negative = negative; + options.collinear_mode = collinear_mode; + + return new db::CompoundRegionCheckOperationNode (0, other, rel, different_polygons, d, options); } -static db::CompoundRegionOperationNode *new_width_check (db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, bool negative) +static db::CompoundRegionOperationNode *new_width_check (db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::collinear_mode_type collinear_mode, bool negative) { db::RegionCheckOptions options (whole_edges, metrics, @@ -415,28 +419,29 @@ static db::CompoundRegionOperationNode *new_width_check (db::Coord d, bool whole max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), shielded); options.negative = negative; + options.collinear_mode = collinear_mode; return new db::CompoundRegionToEdgePairProcessingOperationNode (new db::SinglePolygonCheck (db::WidthRelation, d, options), new_primary (), true /*processor is owned*/); } -static db::CompoundRegionOperationNode *new_space_or_isolated_check (db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative, bool isolated) +static db::CompoundRegionOperationNode *new_space_or_isolated_check (db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, db::collinear_mode_type collinear_mode, bool negative, bool isolated) { // NOTE: we have to use the "foreign" scheme with a filter because only this scheme // guarantees that all subject shapes are visited and receive all intruders. Having all intruders is crucial for the // semantics of the "drc" feature - return new_check_node (new_foreign (), db::SpaceRelation, isolated, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative); + return new_check_node (new_foreign (), db::SpaceRelation, isolated, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, collinear_mode, negative); } -static db::CompoundRegionOperationNode *new_space_check (db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative) +static db::CompoundRegionOperationNode *new_space_check (db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, db::collinear_mode_type collinear_mode, bool negative) { - return new_space_or_isolated_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative, false); + return new_space_or_isolated_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, collinear_mode, negative, false); } -static db::CompoundRegionOperationNode *new_isolated_check (db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative) +static db::CompoundRegionOperationNode *new_isolated_check (db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, db::collinear_mode_type collinear_mode, bool negative) { - return new_space_or_isolated_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative, true); + return new_space_or_isolated_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, collinear_mode, negative, true); } -static db::CompoundRegionOperationNode *new_notch_check (db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, bool negative) +static db::CompoundRegionOperationNode *new_notch_check (db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::collinear_mode_type collinear_mode, bool negative) { db::RegionCheckOptions options (whole_edges, metrics, @@ -445,27 +450,28 @@ static db::CompoundRegionOperationNode *new_notch_check (db::Coord d, bool whole max_projection.is_nil () ? std::numeric_limits::max () : max_projection.to (), shielded); options.negative = negative; + options.collinear_mode = collinear_mode; return new db::CompoundRegionToEdgePairProcessingOperationNode (new db::SinglePolygonCheck (db::SpaceRelation, d, options), new_primary (), true /*processor is owned*/); } -static db::CompoundRegionOperationNode *new_separation_check (db::CompoundRegionOperationNode *other, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative) +static db::CompoundRegionOperationNode *new_separation_check (db::CompoundRegionOperationNode *other, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, db::collinear_mode_type collinear_mode, bool negative) { - return new_check_node (other, db::SpaceRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative); + return new_check_node (other, db::SpaceRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, collinear_mode, negative); } -static db::CompoundRegionOperationNode *new_overlap_check (db::CompoundRegionOperationNode *other, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative) +static db::CompoundRegionOperationNode *new_overlap_check (db::CompoundRegionOperationNode *other, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, db::collinear_mode_type collinear_mode, bool negative) { - return new_check_node (other, db::WidthRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative); + return new_check_node (other, db::WidthRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, collinear_mode, negative); } -static db::CompoundRegionOperationNode *new_enclosing_check (db::CompoundRegionOperationNode *other, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative) +static db::CompoundRegionOperationNode *new_enclosing_check (db::CompoundRegionOperationNode *other, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, db::collinear_mode_type collinear_mode, bool negative) { - return new_check_node (other, db::OverlapRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative); + return new_check_node (other, db::OverlapRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, collinear_mode, negative); } -static db::CompoundRegionOperationNode *new_enclosed_check (db::CompoundRegionOperationNode *other, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative) +static db::CompoundRegionOperationNode *new_enclosed_check (db::CompoundRegionOperationNode *other, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, db::collinear_mode_type collinear_mode, bool negative) { - return new_check_node (other, db::InsideRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative); + return new_check_node (other, db::InsideRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, collinear_mode, negative); } static db::CompoundRegionOperationNode *new_perimeter_filter (db::CompoundRegionOperationNode *input, bool inverse, db::coord_traits::perimeter_type pmin, db::coord_traits::perimeter_type pmax) @@ -660,31 +666,45 @@ Class decl_CompoundRegionOperationNode ("db", " gsi::constructor ("new_minkowski_sum|#new_minkowsky_sum", &new_minkowski_sum_node4, gsi::arg ("input"), gsi::arg ("p"), "@brief Creates a node providing a Minkowski sum with a point sequence forming a contour.\n" ) + - gsi::constructor ("new_width_check", &new_width_check, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("negative", false), + gsi::constructor ("new_width_check", &new_width_check, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("collinear_mode", db::IncludeCollinearWhenTouch), gsi::arg ("negative", false), "@brief Creates a node providing a width check.\n" + "\n" + "The collinear_mode argument has been inserted in version 0.29.\n" ) + - gsi::constructor ("new_space_check", &new_space_check, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), + gsi::constructor ("new_space_check", &new_space_check, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("collinear_mode", db::IncludeCollinearWhenTouch), gsi::arg ("negative", false), "@brief Creates a node providing a space check.\n" + "\n" + "The collinear_mode argument has been inserted in version 0.29.\n" ) + - gsi::constructor ("new_isolated_check", &new_isolated_check, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), + gsi::constructor ("new_isolated_check", &new_isolated_check, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("collinear_mode", db::IncludeCollinearWhenTouch), gsi::arg ("negative", false), "@brief Creates a node providing a isolated polygons (space between different polygons) check.\n" + "\n" + "The collinear_mode argument has been inserted in version 0.29.\n" ) + - gsi::constructor ("new_notch_check", &new_notch_check, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("negative", false), + gsi::constructor ("new_notch_check", &new_notch_check, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("collinear_mode", db::IncludeCollinearWhenTouch), gsi::arg ("negative", false), "@brief Creates a node providing a intra-polygon space check.\n" + "\n" + "The collinear_mode argument has been inserted in version 0.29.\n" ) + - gsi::constructor ("new_separation_check", &new_separation_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), + gsi::constructor ("new_separation_check", &new_separation_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("collinear_mode", db::IncludeCollinearWhenTouch), gsi::arg ("negative", false), "@brief Creates a node providing a separation check.\n" + "\n" + "The collinear_mode argument has been inserted in version 0.29.\n" ) + - gsi::constructor ("new_overlap_check", &new_overlap_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), + gsi::constructor ("new_overlap_check", &new_overlap_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("collinear_mode", db::IncludeCollinearWhenTouch), gsi::arg ("negative", false), "@brief Creates a node providing an overlap check.\n" + "\n" + "The collinear_mode argument has been inserted in version 0.29.\n" ) + - gsi::constructor ("new_enclosing_check", &new_enclosing_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), + gsi::constructor ("new_enclosing_check", &new_enclosing_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("collinear_mode", db::IncludeCollinearWhenTouch), gsi::arg ("negative", false), "@brief Creates a node providing an inside (enclosure) check.\n" + "\n" + "The collinear_mode argument has been inserted in version 0.29.\n" ) + - gsi::constructor ("new_enclosed_check", &new_enclosed_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false), + gsi::constructor ("new_enclosed_check", &new_enclosed_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("collinear_mode", db::IncludeCollinearWhenTouch), gsi::arg ("negative", false), "@brief Creates a node providing an enclosed (secondary enclosing primary) check.\n" "\n" - "This method has been added in version 0.27.5.\n" + "This method has been added in version 0.27.5. The collinear_mode argument has been inserted in version 0.29.\n" ) + gsi::constructor ("new_perimeter_filter", &new_perimeter_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("pmin", 0), gsi::arg ("pmax", std::numeric_limits::perimeter_type>::max (), "max"), "@brief Creates a node filtering the input by perimeter.\n" @@ -803,9 +823,7 @@ Class decl_CompoundRegionOperationNode ("db", " "\n" "The search distance for intruder shapes is determined by the operation and computed from the operation's requirements.\n" "\n" - "NOTE: this feature is experimental and not deployed into the the DRC framework yet.\n" - "\n" - "This class has been introduced in version 0.27." + "This class has been introduced in version 0.27. The API is considered internal and will change without notice." ); gsi::EnumIn decl_dbCompoundRegionLogicalBoolOperationNode_LogicalOp ("db", "LogicalOp", diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 21f03a7a8..2c33ddb34 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -3274,7 +3274,12 @@ gsi::Enum decl_CollinearMode ("db", "CollinearMode", ) + gsi::enum_const ("IncludeCollinearWhenTouch", db::IncludeCollinearWhenTouch, "@brief Specifies that check functions should include collinear edges when they touch\n" - "With this specification, the check functions will also check edges which are collinear, but only if they touch in at least one point. " + "With this specification, the check functions will also check edges which are collinear, but only if they share at least one point. " + "This is the mode that allows checking the 'kissing corner' cases." + ) + + gsi::enum_const ("IncludeCollinearWhenOverlap", db::IncludeCollinearWhenOverlap, + "@brief Specifies that check functions should include collinear edges when they overlap\n" + "With this specification, the check functions will also check edges which are collinear, but only if they share more than a single point. " "This is the mode that allows checking the 'kissing corner' cases." ), "@brief This class represents the collinear_mode type for \\Region#width and related checks.\n" diff --git a/src/db/unit_tests/dbEdgePairRelationsTests.cc b/src/db/unit_tests/dbEdgePairRelationsTests.cc index 305641e4f..9abc40b6c 100644 --- a/src/db/unit_tests/dbEdgePairRelationsTests.cc +++ b/src/db/unit_tests/dbEdgePairRelationsTests.cc @@ -382,6 +382,22 @@ TEST(8_KissingCornerProblem) res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, -1), db::Point (0, -100)), &output); EXPECT_EQ (res, false); + f.set_collinear_mode (db::IncludeCollinearWhenOverlap); + + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 201), db::Point (0, 101)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 0), db::Point (1, 100)), db::Edge (db::Point (0, 201), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 100)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 200), db::Point (0, 50)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,40;0,100):(0,110;0,50)"); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 0), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, -1), db::Point (0, -100)), &output); + EXPECT_EQ (res, false); + f.set_collinear_mode (db::NeverIncludeCollinear); res = f.check (db::Edge (db::Point (0, 0), db::Point (0, 100)), db::Edge (db::Point (0, 201), db::Point (0, 101)), &output); @@ -417,6 +433,22 @@ TEST(8_KissingCornerProblem) res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, -1)), &output); EXPECT_EQ (res, false); + f.set_collinear_mode (db::IncludeCollinearWhenOverlap); + + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 101), db::Point (0, 201)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (1, 100), db::Point (1, 0)), db::Edge (db::Point (0, 0), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 100), db::Point (0, 200)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, 50), db::Point (0, 200)), &output); + EXPECT_EQ (res, true); + EXPECT_EQ (output.first ().to_string () + ":" + output.second ().to_string (), "(0,100;0,40):(0,50;0,110)"); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, 0)), &output); + EXPECT_EQ (res, false); + res = f.check (db::Edge (db::Point (0, 100), db::Point (0, 0)), db::Edge (db::Point (0, -100), db::Point (0, -1)), &output); + EXPECT_EQ (res, false); + f.set_collinear_mode (db::NeverIncludeCollinear); f.set_metrics (db::Euclidian); 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 8eebae6fb..d9b9a0d10 100644 --- a/src/drc/drc/built-in-macros/_drc_cop_integration.rb +++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb @@ -1485,6 +1485,7 @@ CODE shielded = nil opposite_filter = RBA::Region::NoOppositeFilter rect_filter = RBA::Region::NoRectFilter + collinear_mode = RBA::Region::IncludeCollinearWhenTouch n = 1 args.each do |a| @@ -1492,6 +1493,10 @@ CODE metrics = a.value elsif a.is_a?(DRCWholeEdges) whole_edges = a.value + elsif a.is_a?(DRCPropertiesConstraint) + prop_constraint = a.value + elsif a.is_a?(DRCCollinearMode) + collinear_mode = a.value elsif a.is_a?(DRCOppositeErrorFilter) opposite_filter = a.value elsif a.is_a?(DRCRectangleErrorFilter) @@ -1523,6 +1528,8 @@ CODE elsif rect_filter != RBA::Region::NoRectFilter raise("A rectangle error filter cannot be used with this check") end + + args << collinear_mode if :#{f} == :width || :#{f} == :space || :#{f} == :notch || :#{f} == :isolated other && raise("No other layer must be specified for a single-layer check") diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 6cb79e63f..279e67c46 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -322,6 +322,12 @@ module DRC end end + def without_touching(f = true) + self._context("without_touching") do + DRCCollinearMode::new(f ? RBA::Region::IncludeCollinearWhenOverlap : RBA::Region::IncludeCollinearWhenTouch) + end + end + def euclidian DRCMetrics::new(RBA::Region::Euclidian) end diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index ee4ef6803..738f60904 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -3687,10 +3687,12 @@ CODE # 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". @/li - # @li @b transparent @/b: performs the check without shielding (polygon layers only) @/li - # @li @b shielded @/b: performs the check with shielding (polygon layers only) @/li + # @li @b without_touching @/b: With this option present, touching corners (aka "kissing + # corners") will not yield errors. The default is to produce errors in these cases. @/li + # @li @b transparent @/b: Performs the check without shielding (polygon layers only) @/li + # @li @b shielded @/b: Performs the check with shielding (polygon layers only) @/li # @li @b props_eq @/b, @b props_ne @/b, @b props_copy @/b: (only props_copy applies to width check) - - # see "Properties constraints" below. @/li + # See "Properties constraints" below. @/li # @/ul # # Note that without the angle_limit, acute corners will always be reported, since two @@ -3792,6 +3794,22 @@ CODE # space_connected = metal1_nets.space(0.4.um, props_eq + props_copy) # @/code # + # @h3 Touching shapes @/h3 + # + # The \without_touching option will turn off errors that arise due to + # the "kissing corner" configuration (or "checkerboard pattern"). Formally + # this is a width violation across the diagonal, but when considering this + # configuration as disconnected boxes, no error should be reported. + # + # The following images illustrate the effect of the \without_touching option: + # + # @table + # @tr + # @td @img(/images/drc_width5.png) @/td + # @td @img(/images/drc_width6.png) @/td + # @/tr + # @/table + # # %DRC% # @name space @@ -3912,7 +3930,25 @@ CODE # @/tr # @/table # - # @h3 opposite and rectangle error filtering @/h3 + # @h3 Touching shapes @/h3 + # + # Like \width and \space, the separation check also supports the \without_touching option. + # + # This option will turn off errors that arise due to + # collinear edges touching in one corner (the "kissing corners" configuration). + # By default, such edges will yield an error, as they + # form a zero-distance situation. With this option in place, no errors will be reported. + # + # The following images illustrate the effect of the \without_touching option: + # + # @table + # @tr + # @td @img(/images/drc_separation12.png) @/td + # @td @img(/images/drc_separation13.png) @/td + # @/tr + # @/table + # + # @h3 Opposite and rectangle error filtering @/h3 # # The options for the separation check are those available for the \width or \space # method plus opposite and rectangle error filtering. @@ -4121,6 +4157,7 @@ CODE opposite_filter = RBA::Region::NoOppositeFilter rect_filter = RBA::Region::NoRectFilter prop_constraint = RBA::Region::IgnoreProperties + collinear_mode = RBA::Region::IncludeCollinearWhenTouch n = 1 args.each do |a| @@ -4132,6 +4169,8 @@ CODE negative = true elsif a.is_a?(DRCPropertiesConstraint) prop_constraint = a.value + elsif a.is_a?(DRCCollinearMode) + collinear_mode = a.value elsif a.is_a?(DRCOppositeErrorFilter) opposite_filter = a.value elsif a.is_a?(DRCRectangleErrorFilter) @@ -4185,6 +4224,8 @@ CODE raise("A rectangle error filter can only be used for polygon layers") end + args << collinear_mode + border = (metrics == RBA::Region::Square ? value * 1.5 : value) if :#{f} == :width || :#{f} == :space || :#{f} == :notch || :#{f} == :isolated diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index 495f458d1..0acc4a7ad 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -95,6 +95,16 @@ module DRC end end + # A wrapper for the "collinear mode" for + # the DRC functions. The purpose of this class + # is to identify the value by the class. + class DRCCollinearMode + attr_accessor :value + def initialize(v) + self.value = v + end + end + # A wrapper for the "as_dots" or "as_boxes" flag for # some DRC functions. The purpose of this class # is to identify the value by the class.