WIP: because the fixed scheme works nicely, add a new scale_and_snap function.

This commit is contained in:
Matthias Koefferlein 2019-11-06 00:56:33 +01:00
parent 895206dfa1
commit 1e2a8b264d
13 changed files with 398 additions and 49 deletions

View File

@ -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<db::Point> &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<FlatRegion> 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<FlatRegion> new_region (new FlatRegion (merged_semantics ()));
gx = std::max (db::Coord (1), gx);
gy = std::max (db::Coord (1), gy);
std::vector<db::Point> 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
{

View File

@ -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 <class Trans>
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<db::Point> &heap);
private:
AsIfFlatRegion &operator= (const AsIfFlatRegion &other);

View File

@ -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
*

View File

@ -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; }

View File

@ -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<db::ScaleAndGridReducer> vars (db::ScaleAndGridReducer (g, m, d));
vars.collect (layout, cell);
vars.separate_variants (layout, cell);
std::set<db::cell_index_type> called_cells;
cell.collect_called_cells (called_cells);
called_cells.insert (cell.cell_index ());
db::LayoutLocker layout_locker (&layout);
layout.update ();
std::vector<db::Point> 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<db::ICplxTrans, size_t> &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<db::CellInstArray> 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<db::CellInstArray>::const_iterator i = new_insts.begin (); i != new_insts.end (); ++i) {
c->insert (*i);
}
}
}
}

View File

@ -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

View File

@ -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
{

View File

@ -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
*

View File

@ -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;

View File

@ -350,5 +350,92 @@ region_to_edge_interaction_filter_base<OutputType>::fill_output ()
template class region_to_edge_interaction_filter_base<db::Polygon>;
template class region_to_edge_interaction_filter_base<db::Edge>;
// -------------------------------------------------------------------------------------
// Polygon snapping
db::Polygon
snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &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<db::Point> &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));
}
}

View File

@ -524,6 +524,37 @@ private:
OutputContainer *mp_output;
};
template <class C>
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<db::Point> &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<db::Point> &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

View File

@ -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<db::MetaInfo> 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<db::Layout> 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"

View File

@ -949,10 +949,28 @@ Class<db::Region> 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 "