diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 0b8682b67..8e49a58b2 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -632,56 +632,11 @@ AsIfFlatRegion::angle_check (double min, double max, bool inverse) const return res.release (); } -static inline db::Coord snap_to_grid (db::Coord c, db::Coord g) -{ - // This form of snapping always snaps g/2 to right/top. - if (c < 0) { - c = -g * ((-c + (g - 1) / 2) / g); - } else { - c = g * ((c + g / 2) / g); - } - return c; -} - -db::Polygon -AsIfFlatRegion::snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector &heap) -{ - db::Polygon pnew; - - for (size_t i = 0; i < poly.holes () + 1; ++i) { - - heap.clear (); - - db::Polygon::polygon_contour_iterator b, e; - - if (i == 0) { - b = poly.begin_hull (); - e = poly.end_hull (); - } else { - b = poly.begin_hole ((unsigned int) (i - 1)); - e = poly.end_hole ((unsigned int) (i - 1)); - } - - for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { - heap.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy))); - } - - if (i == 0) { - pnew.assign_hull (heap.begin (), heap.end ()); - } else { - pnew.insert_hole (heap.begin (), heap.end ()); - } - - } - - return pnew; -} - RegionDelegate * AsIfFlatRegion::snapped (db::Coord gx, db::Coord gy) { if (gx < 0 || gy < 0) { - throw tl::Exception (tl::to_string (tr ("Grid check requires a positive grid value"))); + throw tl::Exception (tl::to_string (tr ("Grid snap requires a positive grid value"))); } std::auto_ptr new_region (new FlatRegion (merged_semantics ())); @@ -698,6 +653,31 @@ AsIfFlatRegion::snapped (db::Coord gx, db::Coord gy) return new_region.release (); } +RegionDelegate * +AsIfFlatRegion::scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) +{ + if (gx < 0 || gy < 0) { + throw tl::Exception (tl::to_string (tr ("Grid snap requires a positive grid value"))); + } + + if (mx <= 0 || dx <= 0 || my <= 0 || dy <= 0) { + throw tl::Exception (tl::to_string (tr ("Scale and snap requires positive and non-null magnification or divisor values"))); + } + + std::auto_ptr new_region (new FlatRegion (merged_semantics ())); + + gx = std::max (db::Coord (1), gx); + gy = std::max (db::Coord (1), gy); + + std::vector heap; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (scaled_and_snapped_polygon (*p, gx, mx, dx, 0, gy, my, dy, 0, heap)); + } + + return new_region.release (); +} + EdgePairsDelegate * AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const { diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index 712ebb851..07ccc9a7a 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -105,6 +105,13 @@ public: virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy); + virtual RegionDelegate *scaled_and_snapped_in_place (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) + { + return scaled_and_snapped (gx, mx, dx, gy, my, dy); + } + + virtual RegionDelegate *scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy); + virtual EdgesDelegate *edges (const EdgeFilterBase *) const; virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter) @@ -247,7 +254,6 @@ protected: static void produce_markers_for_grid_check (const db::Polygon &poly, const Trans &tr, db::Coord gx, db::Coord gy, db::Shapes &shapes); template static void produce_markers_for_angle_check (const db::Polygon &poly, const Trans &tr, double min, double max, bool inverse, db::Shapes &shapes); - static db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector &heap); private: AsIfFlatRegion &operator= (const AsIfFlatRegion &other); diff --git a/src/db/db/dbCellVariants.h b/src/db/db/dbCellVariants.h index f8aa9f588..34775bc4a 100644 --- a/src/db/db/dbCellVariants.h +++ b/src/db/db/dbCellVariants.h @@ -168,6 +168,61 @@ private: } }; +/** + * @brief A scale+grid reducer + * + * This reducer incarnation reduces the transformation to it's displacement modulo a grid + * after a specified scaling has been applied. + * The scaling is given by a divider and multiplier and is mult / div. + */ +struct DB_PUBLIC ScaleAndGridReducer + : public TransformationReducer +{ + ScaleAndGridReducer (db::Coord grid, db::Coord mult, db::Coord div) + : m_mult (mult), m_grid (int64_t (grid) * int64_t (div)) + { + // .. nothing yet .. + } + + db::ICplxTrans reduce (const db::ICplxTrans &trans) const + { + // NOTE: we need to keep magnification, angle and mirror so when combining the + // reduced transformations, the result will be equivalent to reducing the combined + // transformation. + db::ICplxTrans res (trans); + res.disp (db::Vector (mod (trans.disp ().x ()), mod (trans.disp ().y ()))); + return res; + } + + db::Trans reduce (const db::Trans &trans) const + { + db::Trans res (trans); + res.disp (db::Vector (mod (trans.disp ().x ()), mod (trans.disp ().y ()))); + return res; + } + + bool is_translation_invariant () const { return false; } + +private: + int64_t m_mult; + int64_t m_grid; + + inline db::Coord mod (db::Coord c) const + { + int64_t cc = int64_t (c) * m_mult; + if (cc < 0) { + cc = m_grid - (-cc) % m_grid; + if (cc == m_grid) { + return 0; + } else { + return db::Coord (cc); + } + } else { + return db::Coord (cc % m_grid); + } + } +}; + /** * @brief A class computing variants for cells according to a given criterion * diff --git a/src/db/db/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h index 0ea403ebe..1e98b04ca 100644 --- a/src/db/db/dbEmptyRegion.h +++ b/src/db/db/dbEmptyRegion.h @@ -72,7 +72,9 @@ public: virtual EdgePairsDelegate *angle_check (double, double, bool) const; virtual RegionDelegate *snapped_in_place (db::Coord, db::Coord) { return this; } - virtual RegionDelegate *snapped (db::Coord, db::Coord) { return new EmptyRegion (); } + virtual RegionDelegate *snapped (db::Coord, db::Coord) { return new EmptyRegion (); } + virtual RegionDelegate *scaled_and_snapped_in_place (db::Coord, db::Coord, db::Coord, db::Coord, db::Coord, db::Coord) { return this; } + virtual RegionDelegate *scaled_and_snapped (db::Coord, db::Coord, db::Coord, db::Coord, db::Coord, db::Coord) { return new EmptyRegion (); } virtual EdgesDelegate *edges (const EdgeFilterBase *) const; virtual RegionDelegate *filter_in_place (const PolygonFilterBase &) { return this; } diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index e0452ea43..0b8bc636c 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -22,6 +22,8 @@ #include "dbLayoutUtils.h" +#include "dbCellVariants.h" +#include "dbRegionUtils.h" #include "tlProgress.h" namespace db @@ -431,5 +433,108 @@ ContextCache::find_layout_context (db::cell_index_type from, db::cell_index_type return c->second; } +// ------------------------------------------------------------ +// Scale and snap a layout + +void +scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db::Coord d) +{ + if (g < 0) { + throw tl::Exception (tl::to_string (tr ("Snapping requires a positive grid value"))); + } + + if (m <= 0 || d <= 0) { + throw tl::Exception (tl::to_string (tr ("Scale and snap requires positive and non-null magnification or divisor values"))); + } + + if (! g && m == d) { + return; + } + + db::cell_variants_collector vars (db::ScaleAndGridReducer (g, m, d)); + + vars.collect (layout, cell); + vars.separate_variants (layout, cell); + + std::set called_cells; + cell.collect_called_cells (called_cells); + called_cells.insert (cell.cell_index ()); + + db::LayoutLocker layout_locker (&layout); + layout.update (); + + std::vector heap; + + unsigned int work_layer = layout.insert_layer (); + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + if (called_cells.find (c->cell_index ()) == called_cells.end ()) { + continue; + } + + const std::map &v = vars.variants (c->cell_index ()); + tl_assert (v.size () == size_t (1)); + db::ICplxTrans tr = v.begin ()->first; + + // NOTE: tr_disp is already multiplied with mag, so it can be an integer + db::Vector tr_disp = tr.disp (); + + tr.disp (db::Vector ()); + db::ICplxTrans trinv = tr.inverted (); + + for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { + + db::Shapes &s = c->shapes ((*l).first); + db::Shapes &out = c->shapes (work_layer); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); ! si.at_end (); ++si) { + + db::Polygon poly; + si->polygon (poly); + poly.transform (tr); + out.insert (scaled_and_snapped_polygon (poly, g, m, d, tr_disp.x (), g, m, d, tr_disp.y (), heap).transformed (trinv)); + + } + + s.swap (out); + out.clear (); + + } + + // Snap instance placements to grid and magnify + // NOTE: we can modify the instances because the ScaleAndGridReducer marked every cell with children + // as a variant cell (an effect of ScaleAndGridReducer::want_variants(cell) == true where cells have children). + // Variant cells are not copied blindly back to the original layout. + + std::list new_insts; + + for (db::Cell::const_iterator inst = c->begin (); ! inst.at_end (); ++inst) { + + const db::CellInstArray &ia = inst->cell_inst (); + for (db::CellInstArray::iterator i = ia.begin (); ! i.at_end (); ++i) { + + db::Trans ti (*i); + ti.disp (scaled_and_snapped_vector (ti.disp (), g, m, d, g, m, d)); + + if (ia.is_complex ()) { + new_insts.push_back (db::CellInstArray (ia.object (), ia.complex_trans (ti))); + } else { + new_insts.push_back (db::CellInstArray (ia.object (), ti)); + } + + } + + } + + c->clear_insts (); + + for (std::list::const_iterator i = new_insts.begin (); i != new_insts.end (); ++i) { + c->insert (*i); + } + + } +} + } diff --git a/src/db/db/dbLayoutUtils.h b/src/db/db/dbLayoutUtils.h index 8e89f61d1..44e176ad6 100644 --- a/src/db/db/dbLayoutUtils.h +++ b/src/db/db/dbLayoutUtils.h @@ -223,6 +223,14 @@ private: const db::Layout *mp_layout; }; +/** + * @brief Scales and snaps the layout below the given cell + * + * This method will scale and snap all layers from the given cell and below to the + * specified grid. Scaling happens by the rational factor m / d. + */ +void scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db::Coord d); + } // namespace db #endif diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 6a5ace5e7..749ddc704 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -389,6 +389,18 @@ Region::snapped (db::Coord gx, db::Coord gy) const return Region (mp_delegate->snapped (gx, gy)); } +void +Region::scale_and_snap (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) +{ + set_delegate (mp_delegate->scaled_and_snapped_in_place (gx, mx, dx, gy, my, dy)); +} + +Region +Region::scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) const +{ + return Region (mp_delegate->scaled_and_snapped (gx, mx, dx, gy, my, dy)); +} + Region Region::strange_polygon_check () const { diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index b8f96c109..3ae069688 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -903,6 +903,19 @@ public: */ Region snapped (db::Coord gx, db::Coord gy) const; + /** + * @brief Scales and grid-snaps the region + * + * This method will scale the region by mx/dx in horizontal and by my/dy in vertical + * direction and then snape to gx and gy respectively. + */ + void scale_and_snap (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy); + + /** + * @brief Returns the scaled and snapped region + */ + Region scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) const; + /** * @brief Performs a check for "strange" polygons * diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index 5bfea5e07..ffbc8d631 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -260,6 +260,8 @@ public: virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy) = 0; virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy) = 0; + virtual RegionDelegate *scaled_and_snapped_in_place (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) = 0; + virtual RegionDelegate *scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) = 0; virtual EdgesDelegate *edges (const EdgeFilterBase *filter) const = 0; virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) = 0; diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc index bfdda2990..98475d9a0 100644 --- a/src/db/db/dbRegionUtils.cc +++ b/src/db/db/dbRegionUtils.cc @@ -350,5 +350,92 @@ region_to_edge_interaction_filter_base::fill_output () template class region_to_edge_interaction_filter_base; template class region_to_edge_interaction_filter_base; +// ------------------------------------------------------------------------------------- +// Polygon snapping + +db::Polygon +snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector &heap) +{ + db::Polygon pnew; + + for (size_t i = 0; i < poly.holes () + 1; ++i) { + + heap.clear (); + + db::Polygon::polygon_contour_iterator b, e; + + if (i == 0) { + b = poly.begin_hull (); + e = poly.end_hull (); + } else { + b = poly.begin_hole ((unsigned int) (i - 1)); + e = poly.end_hole ((unsigned int) (i - 1)); + } + + for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { + heap.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy))); + } + + if (i == 0) { + pnew.assign_hull (heap.begin (), heap.end ()); + } else { + pnew.insert_hole (heap.begin (), heap.end ()); + } + + } + + return pnew; } +db::Polygon +scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector &heap) +{ + db::Polygon pnew; + + int64_t dgx = int64_t (gx) * int64_t (dx); + int64_t dgy = int64_t (gy) * int64_t (dy); + + for (size_t i = 0; i < poly.holes () + 1; ++i) { + + heap.clear (); + + db::Polygon::polygon_contour_iterator b, e; + + if (i == 0) { + b = poly.begin_hull (); + e = poly.end_hull (); + } else { + b = poly.begin_hole ((unsigned int) (i - 1)); + e = poly.end_hole ((unsigned int) (i - 1)); + } + + for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { + int64_t x = snap_to_grid (int64_t ((*pt).x ()) * mx + int64_t (ox), dgx) / int64_t (dx); + int64_t y = snap_to_grid (int64_t ((*pt).y ()) * my + int64_t (oy), dgy) / int64_t (dy); + heap.push_back (db::Point (db::Coord (x), db::Coord (y))); + } + + if (i == 0) { + pnew.assign_hull (heap.begin (), heap.end ()); + } else { + pnew.insert_hole (heap.begin (), heap.end ()); + } + + } + + return pnew; +} + +db::Vector +scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy) +{ + int64_t dgx = int64_t (gx) * int64_t (dx); + int64_t dgy = int64_t (gy) * int64_t (dy); + + int64_t x = snap_to_grid (int64_t (v.x ()) * mx, dgx) / int64_t (dx); + int64_t y = snap_to_grid (int64_t (v.y ()) * my, dgy) / int64_t (dy); + + return db::Vector (db::Coord (x), db::Coord (y)); +} + +} diff --git a/src/db/db/dbRegionUtils.h b/src/db/db/dbRegionUtils.h index 9e082f582..95fd8e5ea 100644 --- a/src/db/db/dbRegionUtils.h +++ b/src/db/db/dbRegionUtils.h @@ -524,6 +524,37 @@ private: OutputContainer *mp_output; }; +template +static inline C snap_to_grid (C c, C g) +{ + // This form of snapping always snaps g/2 to right/top. + if (c < 0) { + c = -g * ((-c + (g - 1) / 2) / g); + } else { + c = g * ((c + g / 2) / g); + } + return c; +} + +/** + * @brief Snaps a polygon to the given grid + * Heap is a vector of points reused for the point list + */ +DB_PUBLIC db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector &heap); + +/** + * @brief Scales and snaps a polygon to the given grid + * Heap is a vector of points reused for the point list + * The coordinate transformation is q = ((p * m + o) % (g * d)) / d. + */ +DB_PUBLIC db::Polygon scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector &heap); + +/** + * @brief Scales and snaps a vector to the given grid + * The coordinate transformation is q = ((p * m) % (g * d)) / d. + */ +DB_PUBLIC db::Vector scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord gy, db::Coord my, db::Coord dy); + } // namespace db #endif diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 4bbd4a941..2d6df3c87 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -33,6 +33,7 @@ #include "dbPCellDeclaration.h" #include "dbHash.h" #include "dbRegion.h" +#include "dbLayoutUtils.h" #include "tlStream.h" namespace gsi @@ -812,6 +813,16 @@ static const std::string &layout_meta_get_description (const db::MetaInfo *mi) return mi->description; } +static void scale_and_snap1 (db::Layout *layout, db::Cell &cell, db::Coord g, db::Coord m, db::Coord d) +{ + scale_and_snap (*layout, cell, g, m, d); +} + +static void scale_and_snap2 (db::Layout *layout, db::cell_index_type ci, db::Coord g, db::Coord m, db::Coord d) +{ + scale_and_snap (*layout, layout->cell (ci), g, m, d); +} + Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", gsi::constructor ("new", &layout_meta_info_ctor, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("description", std::string ()), "@brief Creates a layout meta info object\n" @@ -1542,6 +1553,25 @@ Class decl_Layout ("db", "Layout", "an index in the range of 0 to layers-1 needs to be a valid layer. These layers can be either valid, " "special or unused. Use \\is_valid_layer? and \\is_special_layer? to test for the first two states.\n" ) + + gsi::method_ext ("scale_and_snap", &scale_and_snap1, gsi::arg ("cell"), gsi::arg ("grid"), gsi::arg ("mult"), gsi::arg ("div"), + "@brief Scales and snaps the layout below a given cell by the given rational factor and snaps to the given grid\n" + "\n" + "This method is useful to scale a layout by a non-integer factor. The " + "scale factor is given by the rational number mult / div. After scaling, the " + "layout will be snapped to the given grid.\n" + "\n" + "Snapping happens 'as-if-flat' - that is, touching edges will stay touching, regardless of their " + "hierarchy path. To achieve this, this method usually needs to produce cell variants.\n" + "\n" + "This method has been introduced in version 0.26.1.\n" + ) + + gsi::method_ext ("scale_and_snap", &scale_and_snap2, gsi::arg ("cell_index"), gsi::arg ("grid"), gsi::arg ("mult"), gsi::arg ("div"), + "@brief Scales and snaps the layout below a given cell by the given rational factor and snaps to the given grid\n" + "\n" + "Like the other version of \\scale_and_snap, but taking a cell index for the argument.\n" + "\n" + "This method has been introduced in version 0.26.1.\n" + ) + gsi::method ("transform", (void (db::Layout::*) (const db::Trans &t)) &db::Layout::transform, "@brief Transforms the layout with the given transformation\n" "@args trans\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 3fcc353da..9f0939bc8 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -949,10 +949,28 @@ Class decl_Region ("db", "Region", "This method will snap the region to the given grid - each x or y coordinate is brought on the gx or gy grid by rounding " "to the nearest value which is a multiple of gx or gy.\n" "\n" - "If gx or gy is 0 or less, no snapping happens in that direction.\n" + "If gx or gy is 0, no snapping happens in that direction.\n" "\n" "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" ) + + method ("scaled_and_snapped", &db::Region::scaled_and_snapped, gsi::arg ("gx"), gsi::arg ("mx"), gsi::arg ("dx"), gsi::arg ("gy"),gsi::arg ("my"), gsi::arg ("dy"), + "@brief Returns the scaled and snapped region\n" + "This method will scale and snap the region to the given grid and return the scaled and snapped region (see \\scale_and_snap). The original region is not modified.\n" + "\n" + "This method has been introduced in version 0.26.1." + ) + + method ("scale_and_snap", &db::Region::scale_and_snap, gsi::arg ("gx"), gsi::arg ("mx"), gsi::arg ("dx"), gsi::arg ("gy"),gsi::arg ("my"), gsi::arg ("dy"), + "@brief Scales and snaps the region to the given grid\n" + "This method will first scale the region by a rational factor of mx/dx horizontally and my/dy vertically and then " + "snap the region to the given grid - each x or y coordinate is brought on the gx or gy grid by rounding " + "to the nearest value which is a multiple of gx or gy.\n" + "\n" + "If gx or gy is 0, the result is brought on a grid of 1.\n" + "\n" + "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" + "\n" + "This method has been introduced in version 0.26.1." + ) + method ("grid_check", &db::Region::grid_check, gsi::arg ("gx"), gsi::arg ("gy"), "@brief Returns a marker for all vertices not being on the given grid\n" "This method will return an edge pair object for every vertex whose x coordinate is not a multiple of gx or whose "