Reworked the fill scheme for better support of skewed fill repetitions.

This commit is contained in:
Matthias Koefferlein 2021-03-11 21:27:49 +01:00
parent 7d53f6d454
commit f2d106651b
8 changed files with 99 additions and 447 deletions

View File

@ -32,243 +32,6 @@
namespace db
{
namespace
{
struct AddJoinOperator
{
void operator() (unsigned int &a, unsigned int b)
{
a += b;
}
};
}
static db::Vector
optimize_offset (const db::Polygon &fp, const db::AreaMap &am)
{
db::Coord xshift = 0, yshift = 0;
{
tl::interval_map <db::Coord, unsigned int> voting;
AddJoinOperator op;
db::AreaMap::area_type amax = db::AreaMap::area_type (am.d ().x ()) * db::AreaMap::area_type (am.d ().y ());
db::Coord dx = am.d ().x ();
db::Coord dy = am.d ().y ();
size_t nx = am.nx ();
size_t ny = am.ny ();
// Derive a optimal new x offset from the mapping
for (size_t j = 0; j < size_t (ny); ++j) {
bool x1set = false;
bool x2set = false;
db::Coord x1 = 0;
db::Coord x2 = 0;
for (size_t i = 0; i < size_t (nx); ++i) {
if (am.get (i, j) >= amax) {
if (! x1set) {
x1 = 0;
x1set = true;
} else if (x2set) {
x1 = x2;
x1set = true;
x2set = false;
}
} else if (am.get (i, j) > 0) {
if (! x1set || x2set) {
x1 = db::Coord (am.get (i, j) / dy);
x1set = true;
x2set = false;
} else if (! x2set) {
x2 = db::Coord (am.get (i, j) / dy);
x2set = true;
}
} else if (am.get (i, j) == 0) {
if (x1set) {
if (! x2set) {
x2 = 0;
}
if (x1 + x2 < dx) {
voting.add (-x1, x2 + 1, (unsigned int) 1, op);
} else {
voting.add (-x1, x2 - dx + 1, (unsigned int) 1, op);
voting.add (dx - x1, x2 + 1, (unsigned int) 1, op);
}
x1set = false;
x2set = false;
}
}
}
if (x1set) {
if (! x2set) {
x2 = 0;
}
if (x1 + x2 < dx) {
voting.add (-x1, x2 + 1, (unsigned int) 1, op);
} else {
voting.add (-x1, x2 - dx + 1, (unsigned int) 1, op);
voting.add (dx - x1, x2 + 1, (unsigned int) 1, op);
}
}
}
std::set <db::Coord> xshifts;
for (db::Polygon::polygon_edge_iterator e = fp.begin_edge (); ! e.at_end (); ++e) {
xshifts.insert (((*e).p1 ().x () - am.p0 ().x ()) % dx);
}
unsigned int max_votes = 0;
for (std::set <db::Coord>::const_iterator xs = xshifts.begin (); xs != xshifts.end (); ++xs) {
const unsigned int *z = voting.mapped (*xs);
if (z && *z > max_votes) {
xshift = *xs;
max_votes = *z;
}
}
}
{
tl::interval_map <db::Coord, unsigned int> voting;
AddJoinOperator op;
db::AreaMap::area_type amax = db::AreaMap::area_type (am.d ().x ()) * db::AreaMap::area_type (am.d ().y ());
db::Coord dx = am.d ().x ();
db::Coord dy = am.d ().y ();
size_t nx = am.nx ();
size_t ny = am.ny ();
// Derive a optimal new y offset from the mapping
for (size_t i = 0; i < size_t (nx); ++i) {
bool y1set = false;
bool y2set = false;
db::Coord y1 = 0;
db::Coord y2 = 0;
for (size_t j = 0; j < size_t (ny); ++j) {
if (am.get (i, j) >= amax) {
if (! y1set) {
y1 = 0;
y1set = true;
} else if (y2set) {
y1 = y2;
y1set = true;
y2set = false;
}
} else if (am.get (i, j) > 0) {
if (! y1set || y2set) {
y1 = db::Coord (am.get (i, j) / dx);
y1set = true;
y2set = false;
} else if (! y2set) {
y2 = db::Coord (am.get (i, j) / dx);
y2set = true;
}
} else if (am.get (i, j) == 0) {
if (y1set) {
if (! y2set) {
y2 = 0;
}
if (y1 + y2 < dy) {
voting.add (-y1, y2 + 1, (unsigned int) 1, op);
} else {
voting.add (-y1, y2 - dy + 1, (unsigned int) 1, op);
voting.add (dy - y1, y2 + 1, (unsigned int) 1, op);
}
y1set = false;
y2set = false;
}
}
}
if (y1set) {
if (! y2set) {
y2 = 0;
}
if (y1 + y2 < dy) {
voting.add (-y1, y2 + 1, (unsigned int) 1, op);
} else {
voting.add (-y1, y2 - dy + 1, (unsigned int) 1, op);
voting.add (dy - y1, y2 + 1, (unsigned int) 1, op);
}
}
}
std::set <db::Coord> yshifts;
for (db::Polygon::polygon_edge_iterator e = fp.begin_edge (); ! e.at_end (); ++e) {
yshifts.insert (((*e).p1 ().y () - am.p0 ().y ()) % dy);
}
unsigned int max_votes = 0;
for (std::set <db::Coord>::const_iterator ys = yshifts.begin (); ys != yshifts.end (); ++ys) {
const unsigned int *z = voting.mapped (*ys);
if (z && *z > max_votes) {
yshift = *ys;
max_votes = *z;
}
}
}
return db::Vector (xshift, yshift);
}
class GenericRasterizer
{
public:
@ -281,37 +44,53 @@ public:
GenericRasterizer (const db::Polygon &fp, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin)
: m_row_step (row_step), m_column_step (column_step), m_row_steps (0), m_column_steps (0), m_origin (origin)
{
db::Coord dx = row_step.x ();
db::Coord dy = column_step.y ();
rasterize (fp);
}
if (row_step.y () == 0) {
void move (const db::Vector &d)
{
m_origin += d;
clear ();
}
void clear ()
{
m_area_maps.clear ();
}
void rasterize (const db::Polygon &fp)
{
db::Coord dx = m_row_step.x ();
db::Coord dy = m_column_step.y ();
if (m_row_step.y () == 0) {
m_row_steps = 1;
} else {
m_row_steps = tl::lcm (dy, std::abs (row_step.y ())) / std::abs (row_step.y ());
m_row_steps = tl::lcm (dy, std::abs (m_row_step.y ())) / std::abs (m_row_step.y ());
}
if (column_step.x () == 0) {
if (m_column_step.x () == 0) {
m_column_steps = 1;
} else {
m_column_steps = tl::lcm (dx, std::abs (column_step.x ())) / std::abs (column_step.x ());
m_column_steps = tl::lcm (dx, std::abs (m_column_step.x ())) / std::abs (m_column_step.x ());
}
db::Box fp_bbox = fp.box ();
// compensate for distortion by sheared kernel
fp_bbox.enlarge (db::Vector (db::coord_traits<db::Coord>::rounded (double (fp_bbox.height ()) * std::abs (column_step.x ()) / dy), db::coord_traits<db::Coord>::rounded (double (fp_bbox.width ()) * std::abs (row_step.y ()) / dx)));
fp_bbox.enlarge (db::Vector (db::coord_traits<db::Coord>::rounded (double (fp_bbox.height ()) * std::abs (m_column_step.x ()) / dy), db::coord_traits<db::Coord>::rounded (double (fp_bbox.width ()) * std::abs (m_row_step.y ()) / dx)));
int columns_per_rows = (int (m_row_steps) * row_step.y ()) / dy;
int rows_per_columns = (int (m_column_steps) * column_step.x ()) / dx;
int columns_per_rows = (int (m_row_steps) * m_row_step.y ()) / dy;
int rows_per_columns = (int (m_column_steps) * m_column_step.x ()) / dx;
db::Coord ddx = dx * db::Coord (m_row_steps) - column_step.x () * columns_per_rows;
db::Coord ddy = dy * db::Coord (m_column_steps) - row_step.y () * rows_per_columns;
db::Coord ddx = dx * db::Coord (m_row_steps) - m_column_step.x () * columns_per_rows;
db::Coord ddy = dy * db::Coord (m_column_steps) - m_row_step.y () * rows_per_columns;
// round polygon bbox
db::Coord fp_left = db::Coord (tl::round_down (fp_bbox.left () - origin.x (), ddx)) + origin.x ();
db::Coord fp_bottom = db::Coord (tl::round_down (fp_bbox.bottom () - origin.y (), ddy)) + origin.y ();
db::Coord fp_right = db::Coord (tl::round_up (fp_bbox.right () - origin.x (), ddx)) + origin.x ();
db::Coord fp_top = db::Coord (tl::round_up (fp_bbox.top () - origin.y (), ddy)) + origin.y ();
db::Coord fp_left = db::Coord (tl::round_down (fp_bbox.left () - m_origin.x (), ddx)) + m_origin.x ();
db::Coord fp_bottom = db::Coord (tl::round_down (fp_bbox.bottom () - m_origin.y (), ddy)) + m_origin.y ();
db::Coord fp_right = db::Coord (tl::round_up (fp_bbox.right () - m_origin.x (), ddx)) + m_origin.x ();
db::Coord fp_top = db::Coord (tl::round_up (fp_bbox.top () - m_origin.y (), ddy)) + m_origin.y ();
fp_bbox = db::Box (fp_left, fp_bottom, fp_right, fp_top);
size_t nx = fp_bbox.width () / ddx;
@ -326,6 +105,8 @@ public:
m_area_maps.reserve (m_row_steps * m_column_steps + std::abs (columns_per_rows) * std::abs (rows_per_columns));
db::AreaMap am;
for (unsigned int ic = 0; ic < m_column_steps; ++ic) {
for (unsigned int ir = 0; ir < m_row_steps; ++ir) {
@ -333,10 +114,12 @@ public:
db::Vector dr = m_row_step * long (ir);
db::Vector dc = m_column_step * long (ic);
m_area_maps.push_back (db::AreaMap ());
m_area_maps.back ().reinitialize (db::Point (fp_left, fp_bottom) + dr + dc, db::Vector (ddx, ddy), db::Vector (dx, dy), nx, ny);
am.reinitialize (db::Point (fp_left, fp_bottom) + dr + dc, db::Vector (ddx, ddy), db::Vector (dx, dy), nx, ny);
db::rasterize (fp, m_area_maps.back ());
if (db::rasterize (fp, am)) {
m_area_maps.push_back (db::AreaMap ());
m_area_maps.back ().swap (am);
}
}
@ -351,16 +134,42 @@ public:
db::Vector dr = m_row_step * long ((rows_per_columns > 0 ? -(ir + 1) : ir) + m_row_steps);
db::Vector dc = m_column_step * long ((columns_per_rows > 0 ? -(ic + 1) : ic) + m_column_steps);
m_area_maps.push_back (db::AreaMap ());
m_area_maps.back ().reinitialize (db::Point (fp_left, fp_bottom) + dr + dc, db::Vector (ddx, ddy), db::Vector (dx, dy), nx, ny);
am.reinitialize (db::Point (fp_left, fp_bottom) + dr + dc, db::Vector (ddx, ddy), db::Vector (dx, dy), nx, ny);
db::rasterize (fp, m_area_maps.back ());
if (db::rasterize (fp, am)) {
m_area_maps.push_back (db::AreaMap ());
m_area_maps.back ().swap (am);
}
}
}
}
size_t filled_pixels () const
{
size_t n = 0;
for (std::vector<db::AreaMap>::const_iterator a = m_area_maps.begin (); a != m_area_maps.end (); ++a) {
db::Coord nx = db::Coord (a->nx ());
db::Coord ny = db::Coord (a->ny ());
db::AreaMap::area_type amax = a->pixel_area ();
double n = 0;
for (size_t i = 0; i < size_t (nx); ++i) {
for (size_t j = 0; j < size_t (ny); ++j) {
if (a->get (i, j) >= amax) {
n += 1;
}
}
}
}
return n;
}
const db::Point &p0 () const { return m_origin; }
unsigned int row_steps () const { return m_row_steps; }
@ -384,178 +193,6 @@ private:
};
static bool
rasterize_simple (const db::Polygon &fp, const db::Box &fc_bbox, const db::Point &p0, db::AreaMap &am)
{
db::Coord dx = fc_bbox.width ();
db::Coord dy = fc_bbox.height ();
if (tl::verbosity () >= 50) {
tl::info << "Simple rasterize polygon: " << fp.to_string () << " with box " << fc_bbox.to_string ();
}
db::Box fp_bbox = fp.box ();
// round polygon bbox
db::Coord fp_left = dx * ((fp_bbox.left () - p0.x ()) / dx) + p0.x ();
db::Coord fp_bottom = dy * ((fp_bbox.bottom () - p0.y ()) / dy) + p0.y ();
db::Coord fp_right = dx * ((fp_bbox.right () + dx - 1 - p0.x ()) / dx) + p0.x ();
db::Coord fp_top = dy * ((fp_bbox.top () + dy - 1 - p0.y ()) / dy) + p0.y ();
fp_bbox = db::Box (fp_left, fp_bottom, fp_right, fp_top);
db::Coord nx = fp_bbox.width () / dx;
db::Coord ny = fp_bbox.height () / dy;
if (nx <= 0 || ny <= 0) {
// nothing to rasterize:
return false;
}
am.reinitialize (fp_bbox.p1 (), db::Vector (dx, dy), size_t (nx), size_t (ny));
// Rasterize to determine fill regions
db::rasterize (fp, am);
return true;
}
static bool
rasterize_extended (const db::Polygon &fp, const db::Box &fc_bbox, db::AreaMap &am)
{
db::Coord dx = fc_bbox.width ();
db::Coord dy = fc_bbox.height ();
if (tl::verbosity () >= 50) {
tl::info << "Optimized rasterize polygon: " << fp.to_string () << " with box " << fc_bbox.to_string ();
}
db::Box fp_bbox = fp.box ();
db::Coord nx = (fp_bbox.width () + dx - 1) / dx;
db::Coord ny = (fp_bbox.height () + dy - 1) / dy;
if (nx <= 0 || ny <= 0) {
// nothing to rasterize:
return false;
}
// @@@
// try to create a point for which the fill box is inside the polygon
size_t nhull = fp.hull ().size ();
if (nhull < 3) {
return false;
}
db::Point p0 = fp.hull ()[0];
db::Point p1 = fp.hull ()[1];
db::Point pm1 = fp.hull ()[nhull - 1];
db::Coord hx = (dx + 1) / 2;
db::Coord hy = (dy + 1) / 2;
db::Edge e1 (p0, p1);
if (e1.dx () < 0) {
e1.move (db::Vector (hx, hy));
} else {
e1.move (db::Vector (hx, -hy));
}
db::Edge em1 (p0, pm1);
if (em1.dy () < 0) {
em1.move (db::Vector (hx, hy));
} else {
em1.move (db::Vector (-hx, hy));
}
std::pair<bool, db::Point> cp = e1.cut_point (em1);
db::Point o = fp_bbox.p1 ();
if (cp.first) {
db::Point po = cp.second - db::Vector (hx, hy);
o = po - db::Vector (dx * ((po.x () - fp_bbox.p1 ().x ()) / dx), dy * ((po.y () - fp_bbox.p1 ().y ()) / dy));
}
printf("@@@ fp=%s\n", fp.to_string().c_str()); fflush(stdout); // @@@
printf("@@@ -> o=%s\n", o.to_string().c_str()); fflush(stdout); // @@@
// @@@
am.reinitialize (o, db::Vector (dx, dy), size_t (nx), size_t (ny));
// Rasterize to determine fill regions
db::rasterize (fp, am);
// @@@
{
db::Coord nx = db::Coord (am.nx ());
db::Coord ny = db::Coord (am.ny ());
db::AreaMap::area_type amax = am.pixel_area ();
double n = 0;
for (size_t i = 0; i < size_t (nx); ++i) {
for (size_t j = 0; j < size_t (ny); ++j) {
if (am.get (i, j) >= amax) {
n += 1;
}
}
}
printf("@@@ -> n=%d\n", int(n)); fflush(stdout); // @@@
}
// @@@
return true; // @@@
if (tl::verbosity () >= 50) {
db::Coord nx = db::Coord (am.nx ());
db::Coord ny = db::Coord (am.ny ());
db::AreaMap::area_type amax = am.pixel_area ();
double n = 0;
for (size_t i = 0; i < size_t (nx); ++i) {
for (size_t j = 0; j < size_t (ny); ++j) {
if (am.get (i, j) >= amax) {
n += 1;
}
}
}
tl::info << "Number of fill regions before optimization: " << n;
}
db::Vector d = optimize_offset (fp, am);
if (tl::verbosity () >= 50) {
tl::info << "Shift vector: " << d.to_string ();
}
if (d.x () != 0 || d.y () != 0) {
am.move (d);
am.clear ();
db::rasterize (fp, am);
if (tl::verbosity () >= 50) {
db::Coord nx = db::Coord (am.nx ());
db::Coord ny = db::Coord (am.ny ());
db::AreaMap::area_type amax = am.pixel_area ();
double n = 0;
for (size_t i = 0; i < size_t (nx); ++i) {
for (size_t j = 0; j < size_t (ny); ++j) {
if (am.get (i, j) >= amax) {
n += 1;
}
}
}
tl::info << "Number of fill regions after optimization: " << n;
}
}
return true;
}
DB_PUBLIC bool
fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts, const db::Vector &fill_margin)
@ -612,11 +249,18 @@ fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_ce
for (std::vector <db::Polygon>::const_iterator fp = fpb.begin (); fp != fpb.end (); ++fp) {
if (fp->hull ().size () == 0) {
continue;
}
size_t ninsts = 0;
GenericRasterizer am (*fp, row_step, column_step, origin);
db::Point o = origin;
if (enhanced_fill) {
o = fp->hull () [0];
}
// @@@ optimize fill offset ...
GenericRasterizer am (*fp, row_step, column_step, o);
for (unsigned int i = 0; i < am.area_maps (); ++i) {
@ -804,8 +448,6 @@ fill_region_repeat (db::Cell *cell, const db::Region &fr, db::cell_index_type fi
new_fill_region.swap (remaining);
fill_region = &new_fill_region;
break; // @@@
}
}

View File

@ -1617,15 +1617,20 @@ AreaMap::reinitialize (const db::Point &p0, const db::Vector &d, const db::Vecto
m_p0 = p0;
m_d = d;
m_p = db::Vector (std::min (d.x (), p.x ()), std::min (d.y (), p.y ()));
m_nx = nx;
m_ny = ny;
if (mp_av) {
delete mp_av;
if (nx != m_nx || ny != m_ny) {
m_nx = nx;
m_ny = ny;
if (mp_av) {
delete mp_av;
}
mp_av = new area_type [nx * ny];
}
mp_av = new area_type [nx * ny];
clear ();
}
@ -1645,6 +1650,7 @@ AreaMap::swap (AreaMap &other)
{
std::swap (m_p0, other.m_p0);
std::swap (m_d, other.m_d);
std::swap (m_p, other.m_p);
std::swap (m_nx, other.m_nx);
std::swap (m_ny, other.m_ny);
std::swap (mp_av, other.mp_av);
@ -1676,7 +1682,7 @@ AreaMap::bbox () const
// -------------------------------------------------------------------------
// Implementation of rasterize
void
bool
rasterize (const db::Polygon &polygon, db::AreaMap &am)
{
typedef db::AreaMap::area_type area_type;
@ -1685,7 +1691,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
// check if the polygon overlaps the rasterization area. Otherwise, we simply do nothing.
if (! pbox.overlaps (box)) {
return;
return false;
}
db::Coord ymin = box.bottom (), ymax = box.top ();
@ -1702,7 +1708,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
// no scanning required (i.e. degenerated polygon) -> do nothing
if (iy0 == iy1 || ix0 == ix1) {
return;
return false;
}
// collect edges
@ -1733,7 +1739,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
}
if (c == edges.end ()) {
return;
return false;
}
std::vector <db::Edge>::iterator f = c;
@ -1876,6 +1882,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
}
return true;
}
// -------------------------------------------------------------------------

View File

@ -640,8 +640,10 @@ private:
*
* This will decompose the polygon and produce per-pixel area values for the given
* polygon. The area contributions will be added to the given area map.
*
* Returns a value indicating whether the map will be non-empty.
*/
void DB_PUBLIC rasterize (const db::Polygon &polygon, db::AreaMap &am);
bool DB_PUBLIC rasterize (const db::Polygon &polygon, db::AreaMap &am);
/**
* @brief Minkowsky sum of an edge and a polygon

View File

@ -1886,9 +1886,10 @@ Class<db::Cell> decl_Cell ("db", "Cell",
) +
gsi::method_ext ("fill_region_multi", &fill_region_repeat, gsi::arg ("region"), gsi::arg ("fill_cell_index"), gsi::arg ("kernel_origin"), gsi::arg ("row_step"), gsi::arg ("column_step"), gsi::arg ("fill_margin"), gsi::arg ("remaining_polygons"),
"@brief Fills the given region with cells of the given type in enhanced mode with iterations\n"
"This version operates like \\fill_region, but repeates the fill generation until no further fill cells can be placed. "
"This version operates like \\fill_region, but repeats the fill generation until no further fill cells can be placed. "
"As the fill pattern origin changes between the iterations, narrow regions can be filled which cannot with a fixed fill pattern origin. "
"The \\fill_margin parameter is important as it controls the distance between fill cells with a different origin and therefore pitch-incompatible arrays.\n"
"The \\fill_margin parameter is important as it controls the distance between fill cells with a different origin and therefore "
"introduces a safety distance between pitch-incompatible arrays.\n"
"\n"
"This method has been introduced in version 0.27.\n"
) +

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.