WIP: exclude area for fill functions

This commit is contained in:
Matthias Koefferlein 2025-09-06 17:58:44 +02:00
parent 6ada4cb6f9
commit ff6ab4267b
7 changed files with 404 additions and 267 deletions

View File

@ -42,10 +42,18 @@ public:
// .. nothing yet ..
}
GenericRasterizer (const db::Polygon &fp, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, const db::Vector &dim)
GenericRasterizer (const std::vector<db::Polygon> &fr, const db::Box &rasterized_area, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, const db::Vector &dim)
: m_row_step (row_step), m_column_step (column_step), m_row_steps (0), m_column_steps (0), m_origin (origin), m_dim (dim)
{
rasterize (fp);
rasterize (rasterized_area, fr);
}
GenericRasterizer (const db::Polygon &fp, const db::Box &rasterized_area, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, const db::Vector &dim)
: m_row_step (row_step), m_column_step (column_step), m_row_steps (0), m_column_steps (0), m_origin (origin), m_dim (dim)
{
std::vector<db::Polygon> fr;
fr.push_back (fp);
rasterize (rasterized_area, fr);
}
void move (const db::Vector &d)
@ -59,101 +67,6 @@ public:
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 (m_row_step.y ())) / std::abs (m_row_step.y ());
}
if (m_column_step.x () == 0) {
m_column_steps = 1;
} else {
m_column_steps = tl::lcm (dx, std::abs (m_column_step.x ())) / std::abs (m_column_step.x ());
}
// because the rasterizer can't handle overlapping cells we need to multiply the row and columns steps
// with an integer until the effective rasterizer pitch gets big enough.
m_row_steps *= (m_dim.x () - 1) / (m_row_steps * m_row_step.x ()) + 1;
m_column_steps *= (m_dim.y () - 1) / (m_column_steps * m_column_step.y ()) + 1;
db::Box fp_bbox = fp.box ();
// compensate for distortion by sheared kernel
db::Coord ex = std::max (std::abs (db::Coord (m_column_step.x () * m_column_steps)), std::abs (db::Coord (m_row_step.x () * m_row_steps)));
db::Coord ey = std::max (std::abs (db::Coord (m_column_step.y () * m_column_steps)), std::abs (db::Coord (m_row_step.y () * m_row_steps)));
fp_bbox.enlarge (db::Vector (ex, ey));
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) - 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 () - 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;
size_t ny = fp_bbox.height () / ddy;
tl_assert (fp.box ().inside (fp_bbox));
if (nx == 0 || ny == 0) {
// nothing to rasterize:
return;
}
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) {
db::Vector dr = m_row_step * long (ir);
db::Vector dc = m_column_step * long (ic);
am.reinitialize (db::Point (fp_left, fp_bottom) + dr + dc, db::Vector (ddx, ddy), m_dim, nx, ny);
if (db::rasterize (fp, am)) {
m_area_maps.push_back (db::AreaMap ());
m_area_maps.back ().swap (am);
}
}
}
// adds the "dead corner" piece
for (unsigned int ic = 0; ic < (unsigned int) std::abs (columns_per_rows); ++ic) {
for (unsigned int ir = 0; ir < (unsigned int) std::abs (rows_per_columns); ++ir) {
db::Vector dr = m_row_step * long ((rows_per_columns > 0 ? -int (ir + 1) : ir) + m_row_steps);
db::Vector dc = m_column_step * long ((columns_per_rows > 0 ? -int (ic + 1) : ic) + m_column_steps);
am.reinitialize (db::Point (fp_left, fp_bottom) + dr + dc, db::Vector (ddx, ddy), m_dim, nx, ny);
if (db::rasterize (fp, am)) {
m_area_maps.push_back (db::AreaMap ());
m_area_maps.back ().swap (am);
}
}
}
}
const db::Point &p0 () const { return m_origin; }
unsigned int row_steps () const { return m_row_steps; }
@ -180,12 +93,212 @@ private:
unsigned int m_row_steps, m_column_steps;
db::Point m_origin;
db::Vector m_dim;
void rasterize (const db::Box &rasterized_area, const std::vector<db::Polygon> &fr)
{
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 (m_row_step.y ())) / std::abs (m_row_step.y ());
}
if (m_column_step.x () == 0) {
m_column_steps = 1;
} else {
m_column_steps = tl::lcm (dx, std::abs (m_column_step.x ())) / std::abs (m_column_step.x ());
}
// because the rasterizer can't handle overlapping cells we need to multiply the row and columns steps
// with an integer until the effective rasterizer pitch gets big enough.
m_row_steps *= (m_dim.x () - 1) / (m_row_steps * m_row_step.x ()) + 1;
m_column_steps *= (m_dim.y () - 1) / (m_column_steps * m_column_step.y ()) + 1;
db::Box ra_org = rasterized_area;
// compensate for distortion by sheared kernel
db::Coord ex = std::max (std::abs (db::Coord (m_column_step.x () * m_column_steps)), std::abs (db::Coord (m_row_step.x () * m_row_steps)));
db::Coord ey = std::max (std::abs (db::Coord (m_column_step.y () * m_column_steps)), std::abs (db::Coord (m_row_step.y () * m_row_steps)));
ra_org.enlarge (db::Vector (ex, ey));
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) - 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 ra_left = db::Coord (tl::round_down (ra_org.left () - m_origin.x (), ddx)) + m_origin.x ();
db::Coord ra_bottom = db::Coord (tl::round_down (ra_org.bottom () - m_origin.y (), ddy)) + m_origin.y ();
db::Coord ra_right = db::Coord (tl::round_up (ra_org.right () - m_origin.x (), ddx)) + m_origin.x ();
db::Coord ra_top = db::Coord (tl::round_up (ra_org.top () - m_origin.y (), ddy)) + m_origin.y ();
db::Box ra = db::Box (ra_left, ra_bottom, ra_right, ra_top);
size_t nx = ra.width () / ddx;
size_t ny = ra.height () / ddy;
tl_assert (ra_org.inside (ra));
if (nx == 0 || ny == 0) {
// nothing to rasterize:
return;
}
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) {
db::Vector dr = m_row_step * long (ir);
db::Vector dc = m_column_step * long (ic);
am.reinitialize (db::Point (ra_left, ra_bottom) + dr + dc, db::Vector (ddx, ddy), m_dim, nx, ny);
bool any = false;
for (auto i = fr.begin (); i != fr.end (); ++i) {
if (db::rasterize (*i, am)) {
any = true;
}
}
if (any) {
m_area_maps.push_back (db::AreaMap ());
m_area_maps.back ().swap (am);
}
}
}
// adds the "dead corner" piece
for (unsigned int ic = 0; ic < (unsigned int) std::abs (columns_per_rows); ++ic) {
for (unsigned int ir = 0; ir < (unsigned int) std::abs (rows_per_columns); ++ir) {
db::Vector dr = m_row_step * long ((rows_per_columns > 0 ? -int (ir + 1) : ir) + m_row_steps);
db::Vector dc = m_column_step * long ((columns_per_rows > 0 ? -int (ic + 1) : ic) + m_column_steps);
am.reinitialize (db::Point (ra_left, ra_bottom) + dr + dc, db::Vector (ddx, ddy), m_dim, nx, ny);
bool any = false;
for (auto i = fr.begin (); i != fr.end (); ++i) {
if (db::rasterize (*i, am)) {
any = true;
}
}
if (any) {
m_area_maps.push_back (db::AreaMap ());
m_area_maps.back ().swap (am);
}
}
}
}
};
static size_t
create_instances (GenericRasterizer &am, db::Cell *cell, db::cell_index_type fill_cell_index, const db::Vector &kernel_origin, const db::Vector &fill_margin, const GenericRasterizer *exclude_rasterized, std::vector<db::Polygon> *filled_regions)
{
size_t ninsts = 0;
for (unsigned int i = 0; i < am.area_maps (); ++i) {
db::AreaMap &am1 = am.area_map (i);
const db::AreaMap *am1_excl = 0;
if (exclude_rasterized) {
tl_assert (i < exclude_rasterized->area_maps ());
am1_excl = &exclude_rasterized->area_map (i);
}
size_t nx = am1.nx ();
size_t ny = am1.ny ();
// Create the fill cell instances
for (size_t i = 0; i < nx; ++i) {
for (size_t j = 0; j < ny; ) {
size_t jj = j + 1;
if (am1.get (i, j) == am1.pixel_area () && (!am1_excl || am1_excl->get (i, j) == 0)) {
while (jj != ny && am1.get (i, jj) == am1.pixel_area () && (!am1_excl || am1_excl->get (i, jj) == 0)) {
++jj;
}
db::Vector p0 = (am1.p0 () - db::Point ()) - kernel_origin;
p0 += db::Vector (i * am1.d ().x (), j * am1.d ().y ());
db::CellInstArray array;
// try to expand the array in x direction
size_t ii = i + 1;
for ( ; ii < nx; ++ii) {
bool all = true;
for (size_t k = j; k < jj && all; ++k) {
all = (am1.get (ii, k) == am1.pixel_area () && (!am1_excl || am1_excl->get (ii, k) == 0));
}
if (all) {
for (size_t k = j; k < jj; ++k) {
// disable pixel, so we do not see it again in the following columns
am1.get (ii, k) = 0;
}
} else {
break;
}
}
ninsts += (jj - j) * (ii - i);
if (jj > j + 1 || ii > i + 1) {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0), db::Vector (0, am1.d ().y ()), db::Vector (am1.d ().x (), 0), (unsigned long) (jj - j), (unsigned long) (ii - i));
} else {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0));
}
{
// In case we run this from a tiling processor we need to lock against multithread races
tl_assert (cell->layout () != 0);
tl::MutexLocker locker (&cell->layout ()->lock ());
cell->insert (array);
}
if (filled_regions) {
if (am1.d ().y () == am1.p ().y () && am1.d ().x () == am1.p ().x ()) {
db::Box fill_box (db::Point (), db::Point (am1.p ().x () * db::Coord (ii - i), am1.p ().y () * db::Coord (jj - j)));
filled_regions->push_back (db::Polygon (fill_box.enlarged (fill_margin).moved (kernel_origin + p0)));
} else {
db::Box fill_box (db::Point (), db::Point () + am1.p ());
fill_box.enlarge (fill_margin);
for (size_t k = 0; k < jj - j; ++k) {
for (size_t l = 0; l < ii - i; ++l) {
filled_regions->push_back (db::Polygon (fill_box.moved (kernel_origin + p0 + db::Vector (am1.d ().x () * db::Coord (l), am1.d ().y () * db::Coord (k)))));
}
}
}
}
}
j = jj;
}
}
}
return ninsts;
}
static bool
fill_polygon_impl (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts, const db::Vector &fill_margin, const db::Box &glue_box)
std::vector <db::Polygon> *remaining_parts, const db::Vector &fill_margin, const db::Box &glue_box, const db::Region &exclude_area)
{
if (row_step.x () <= 0 || column_step.y () <= 0) {
throw tl::Exception (tl::to_string (tr ("Invalid row or column step vectors in fill_region: row step must have a positive x component while column step must have a positive y component")));
@ -197,153 +310,133 @@ fill_polygon_impl (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type f
db::Vector kernel_origin (fc_bbox.left (), fc_bbox.bottom ());
std::vector <db::Polygon> filled_regions;
db::EdgeProcessor ep;
// under- and oversize the polygon to remove slivers that cannot be filled.
db::Coord dx = fc_bbox.width () / 2 - 1, dy = fc_bbox.height () / 2 - 1;
std::vector <db::Polygon> fpa;
std::vector <db::Polygon> fpb;
fpa.push_back (fp0);
db::Region fr (fp0);
db::Box rasterized_area = fp0.box ();
ep.size (fpa, -dx, 0, fpb, 3 /*mode*/, false /*=don't resolve holes*/);
fpa.swap (fpb);
fpb.clear ();
std::unique_ptr<GenericRasterizer> exclude_rasterized;
ep.size (fpa, dx, 0, fpb, 3 /*mode*/, false /*=don't resolve holes*/);
fpa.swap (fpb);
fpb.clear ();
if (! exclude_area.empty ()) {
ep.size (fpa, 0, -dy, fpb, 3 /*mode*/, false /*=don't resolve holes*/);
fpa.swap (fpb);
fpb.clear ();
auto iter = exclude_area.iter ();
iter.confine_region (fp0.box ());
ep.size (fpa, 0, dy, fpb, 3 /*mode*/, false /*=don't resolve holes*/);
fpa.swap (fpb);
fpb.clear ();
// over- and undersize the polygons to fill gaps that cannot be filled.
db::Region excluded (iter);
excluded.size (dx, 0);
excluded.size (-dx, 0);
excluded.size (dy, 0);
excluded.size (-dy, 0);
excluded.merge ();
ep.simple_merge (fpa, fpb, false /*=don't resolve holes*/);
if (enhanced_fill || remaining_parts != 0) {
filled_regions.clear ();
tl::warn << "@@@ using booleans for exclude";
// In enhanced fill or if the remaining parts are requested, it is better to implement the
// exclude area by a boolean NOT
fr -= excluded;
} else {
tl::warn << "@@@ using exclude area rasterizer";
// Otherwise use a second rasterizer for the exclude polygons that must have a zero pixel coverage for the
// pixel to be filled.
std::vector<db::Polygon> excluded_poly;
excluded_poly.reserve (excluded.count ());
for (auto i = excluded.begin (); ! i.at_end (); ++i) {
excluded_poly.push_back (*i);
}
excluded.clear ();
exclude_rasterized.reset (new GenericRasterizer (excluded_poly, rasterized_area, row_step, column_step, origin, fc_bbox.p2 () - fc_bbox.p1 ()));
}
}
std::vector <db::Polygon> filled_poly;
// under- and oversize the polygon to remove slivers that cannot be filled.
fr.size (-dx, 0);
fr.size (dx, 0);
fr.size (0, -dy);
fr.size (0, dy);
fr.merge ();
filled_poly.reserve (fr.count ());
for (auto i = fr.begin (); ! i.at_end (); ++i) {
filled_poly.push_back (*i);
}
fr.clear ();
if (filled_poly.empty ()) {
return false;
}
std::vector <db::Polygon> filled_regions;
bool any_fill = false;
for (std::vector <db::Polygon>::const_iterator fp = fpb.begin (); fp != fpb.end (); ++fp) {
if (exclude_rasterized.get ()) {
if (fp->hull ().size () == 0) {
continue;
}
// disable enhanced mode an obey the origin if the polygon is not entirely inside and not at the boundary of the glue box
bool ef = enhanced_fill;
if (ef && ! glue_box.empty () && ! fp->box ().enlarged (db::Vector (1, 1)).inside (glue_box)) {
ef = false;
}
// pick a heuristic "good" starting point in enhanced mode
// TODO: this is a pretty weak optimization.
db::Point o = origin;
if (ef) {
o = fp->hull () [0];
}
size_t ninsts = 0;
GenericRasterizer am (*fp, row_step, column_step, o, fc_bbox.p2 () - fc_bbox.p1 ());
for (unsigned int i = 0; i < am.area_maps (); ++i) {
db::AreaMap &am1 = am.area_map (i);
size_t nx = am1.nx ();
size_t ny = am1.ny ();
// Create the fill cell instances
for (size_t i = 0; i < nx; ++i) {
for (size_t j = 0; j < ny; ) {
size_t jj = j + 1;
if (am1.get (i, j) == am1.pixel_area ()) {
while (jj != ny && am1.get (i, jj) == am1.pixel_area ()) {
++jj;
}
db::Vector p0 = (am1.p0 () - db::Point ()) - kernel_origin;
p0 += db::Vector (i * am1.d ().x (), j * am1.d ().y ());
db::CellInstArray array;
// try to expand the array in x direction
size_t ii = i + 1;
for ( ; ii < nx; ++ii) {
bool all = true;
for (size_t k = j; k < jj && all; ++k) {
all = am1.get (ii, k) == am1.pixel_area ();
}
if (all) {
for (size_t k = j; k < jj; ++k) {
// disable pixel, so we do not see it again in the following columns
am1.get (ii, k) = 0;
}
} else {
break;
}
}
ninsts += (jj - j) * (ii - i);
if (jj > j + 1 || ii > i + 1) {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0), db::Vector (0, am1.d ().y ()), db::Vector (am1.d ().x (), 0), (unsigned long) (jj - j), (unsigned long) (ii - i));
} else {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0));
}
{
// In case we run this from a tiling processor we need to lock against multithread races
tl_assert (cell->layout () != 0);
tl::MutexLocker locker (&cell->layout ()->lock ());
cell->insert (array);
}
if (remaining_parts) {
if (am1.d ().y () == am1.p ().y () && am1.d ().x () == am1.p ().x ()) {
db::Box fill_box (db::Point (), db::Point (am1.p ().x () * db::Coord (ii - i), am1.p ().y () * db::Coord (jj - j)));
filled_regions.push_back (db::Polygon (fill_box.enlarged (fill_margin).moved (kernel_origin + p0)));
} else {
db::Box fill_box (db::Point (), db::Point () + am1.p ());
fill_box.enlarge (fill_margin);
for (size_t k = 0; k < jj - j; ++k) {
for (size_t l = 0; l < ii - i; ++l) {
filled_regions.push_back (db::Polygon (fill_box.moved (kernel_origin + p0 + db::Vector (am1.d ().x () * db::Coord (l), am1.d ().y () * db::Coord (k)))));
}
}
}
}
any_fill = true;
}
j = jj;
}
}
tl_assert (remaining_parts == 0);
GenericRasterizer am (filled_poly, rasterized_area, row_step, column_step, origin, fc_bbox.p2 () - fc_bbox.p1 ());
size_t ninsts = create_instances (am, cell, fill_cell_index, kernel_origin, fill_margin, exclude_rasterized.get (), 0);
if (ninsts > 0) {
any_fill = true;
}
if (tl::verbosity () >= 30 && ninsts > 0) {
tl::info << "Part " << fp->to_string ();
tl::info << "Part " << fp0.to_string ();
tl::info << "Created " << ninsts << " instances";
}
} else {
for (auto fp = filled_poly.begin (); fp != filled_poly.end (); ++fp) {
if (fp->is_empty ()) {
continue;
}
// disable enhanced mode an obey the origin if the polygon is not entirely inside and not at the boundary of the glue box
bool ef = enhanced_fill;
if (ef && ! glue_box.empty () && ! fp->box ().enlarged (db::Vector (1, 1)).inside (glue_box)) {
ef = false;
}
// pick a heuristic "good" starting point in enhanced mode
// TODO: this is a pretty weak optimization.
db::Point o = origin;
if (ef) {
o = fp->hull () [0];
}
GenericRasterizer am (*fp, rasterized_area, row_step, column_step, o, fc_bbox.p2 () - fc_bbox.p1 ());
size_t ninsts = create_instances (am, cell, fill_cell_index, kernel_origin, fill_margin, 0, remaining_parts ? &filled_regions : 0);
if (ninsts > 0) {
any_fill = true;
}
if (tl::verbosity () >= 30 && ninsts > 0) {
tl::info << "Part " << fp->to_string ();
tl::info << "Created " << ninsts << " instances";
}
}
}
if (any_fill) {
if (remaining_parts) {
db::EdgeProcessor ep;
std::vector <db::Polygon> fp1;
fp1.push_back (fp0);
ep.boolean (fp1, filled_regions, *remaining_parts, db::BooleanOp::ANotB, false /*=don't resolve holes*/);
@ -358,25 +451,25 @@ fill_polygon_impl (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type f
DB_PUBLIC bool
fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_cell_index, const Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts, const db::Vector &fill_margin, const db::Box &glue_box)
std::vector <db::Polygon> *remaining_parts, const db::Vector &fill_margin, const db::Box &glue_box, const db::Region &exclude_area)
{
return fill_polygon_impl (cell, fp0, fill_cell_index, fc_box, row_step, column_step, origin, enhanced_fill, remaining_parts, fill_margin, glue_box);
return fill_polygon_impl (cell, fp0, fill_cell_index, fc_box, row_step, column_step, origin, enhanced_fill, remaining_parts, fill_margin, glue_box, exclude_area);
}
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, const db::Box &glue_box)
std::vector <db::Polygon> *remaining_parts, const db::Vector &fill_margin, const db::Box &glue_box, const db::Region &exclude_area)
{
if (fc_bbox.empty () || fc_bbox.width () == 0 || fc_bbox.height () == 0) {
throw tl::Exception (tl::to_string (tr ("Invalid fill cell footprint (empty or zero width/height)")));
}
return fill_polygon_impl (cell, fp0, fill_cell_index, fc_bbox, db::Vector (fc_bbox.width (), 0), db::Vector (0, fc_bbox.height ()), origin, enhanced_fill, remaining_parts, fill_margin, glue_box);
return fill_polygon_impl (cell, fp0, fill_cell_index, fc_bbox, db::Vector (fc_bbox.width (), 0), db::Vector (0, fc_bbox.height ()), origin, enhanced_fill, remaining_parts, fill_margin, glue_box, exclude_area);
}
static void
fill_region_impl (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, int iteration, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, int iteration, const db::Box &glue_box, const db::Region &exclude_area)
{
if (row_step.x () <= 0 || column_step.y () <= 0) {
throw tl::Exception (tl::to_string (tr ("Invalid row or column step vectors in fill_region: row step must have a positive x component while column step must have a positive y component")));
@ -403,7 +496,7 @@ fill_region_impl (db::Cell *cell, const db::Region &fr, db::cell_index_type fill
tl::RelativeProgress progress (progress_title, n);
for (db::Region::const_iterator p = fr.begin_merged (); !p.at_end (); ++p) {
if (! fill_polygon_impl (cell, *p, fill_cell_index, fc_bbox, row_step, column_step, origin, enhanced_fill, remaining_parts ? &rem_pp : 0, fill_margin, glue_box)) {
if (! fill_polygon_impl (cell, *p, fill_cell_index, fc_bbox, row_step, column_step, origin, enhanced_fill, remaining_parts ? &rem_pp : 0, fill_margin, glue_box, exclude_area)) {
if (remaining_polygons) {
rem_poly.push_back (*p);
}
@ -433,27 +526,27 @@ fill_region_impl (db::Cell *cell, const db::Region &fr, db::cell_index_type fill
DB_PUBLIC void
fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
fill_region_impl (cell, fr, fill_cell_index, fc_bbox, row_step, column_step, origin, enhanced_fill, remaining_parts, fill_margin, remaining_polygons, 0, glue_box);
fill_region_impl (cell, fr, fill_cell_index, fc_bbox, row_step, column_step, origin, enhanced_fill, remaining_parts, fill_margin, remaining_polygons, 0, glue_box, exclude_area);
}
DB_PUBLIC void
fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
if (fc_bbox.empty () || fc_bbox.width () == 0 || fc_bbox.height () == 0) {
throw tl::Exception (tl::to_string (tr ("Invalid fill cell footprint (empty or zero width/height)")));
}
fill_region_impl (cell, fr, fill_cell_index, fc_bbox, db::Vector (fc_bbox.width (), 0), db::Vector (0, fc_bbox.height ()),
origin, enhanced_fill, remaining_parts, fill_margin, remaining_polygons, 0, glue_box);
origin, enhanced_fill, remaining_parts, fill_margin, remaining_polygons, 0, glue_box, exclude_area);
}
DB_PUBLIC void
fill_region_repeat (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index,
const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step,
const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
const db::Region *fill_region = &fr;
@ -467,7 +560,7 @@ fill_region_repeat (db::Cell *cell, const db::Region &fr, db::cell_index_type fi
++iteration;
remaining.clear ();
fill_region_impl (cell, *fill_region, fill_cell_index, fc_box, row_step, column_step, db::Point (), true, &remaining, fill_margin, remaining_polygons, iteration, glue_box);
fill_region_impl (cell, *fill_region, fill_cell_index, fc_box, row_step, column_step, db::Point (), true, &remaining, fill_margin, remaining_polygons, iteration, glue_box, exclude_area);
new_fill_region.swap (remaining);
fill_region = &new_fill_region;

View File

@ -23,12 +23,12 @@
#include "dbTypes.h"
#include "dbPolygon.h"
#include "dbRegion.h"
namespace db
{
class Cell;
class Region;
/**
* @brief Creates a tiling pattern for a single polygon using a fill cell which is repeated periodically
@ -41,6 +41,7 @@ class Region;
* @param column_step (some_versions) The column advance vector of the fill cell. By default this is (0, fc_bbox.height())
* @param origin Specifies the origin of the fill raster if enhanced_fill is false
* @param enhanced_fill If set, the tiling offset will be optimized such that as much tiling cells fit into each polygon
* @param exclude_area The region which fill cells must not overlap
*
* Optional parameters:
*
@ -79,12 +80,16 @@ class Region;
*/
DB_PUBLIC bool
fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), const db::Box &glue_box = db::Box ());
fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box,
const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (),
const db::Box &glue_box = db::Box ());
DB_PUBLIC bool
fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), const db::Box &glue_box = db::Box ());
fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box,
const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (),
const db::Box &glue_box = db::Box (), const db::Region &exclude_area = db::Region ());
/**
@ -98,12 +103,16 @@ fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cel
*/
DB_PUBLIC void
fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), db::Region *remaining_polygons = 0, const db::Box &glue_box = db::Box ());
fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box,
const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), db::Region *remaining_polygons = 0,
const db::Box &glue_box = db::Box (), const db::Region &exclude_area = db::Region ());
DB_PUBLIC void
fill_region (db::Cell *cell, const db::Region &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), db::Region *remaining_polygons = 0, const db::Box &glue_box = db::Box ());
fill_region (db::Cell *cell, const db::Region &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box,
const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), db::Region *remaining_polygons = 0,
const db::Box &glue_box = db::Box (), const db::Region &exclude_area = db::Region ());
/**
* @brief An iterative version for enhanced fill
@ -118,6 +127,7 @@ fill_region (db::Cell *cell, const db::Region &fp, db::cell_index_type fill_cell
DB_PUBLIC void
fill_region_repeat (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index,
const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step,
const db::Vector &fill_margin, db::Region *remaining_polygons = 0, const db::Box &glue_box = db::Box ());
const db::Vector &fill_margin, db::Region *remaining_polygons = 0,
const db::Box &glue_box = db::Box (), const db::Region &exclude_area = db::Region ());
}

View File

@ -1510,23 +1510,23 @@ static void move_tree_shapes3 (db::Cell *cell, db::Cell &source_cell, const db::
static void
fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point *origin,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
db::fill_region (cell, fr, fill_cell_index, fc_box, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box);
db::fill_region (cell, fr, fill_cell_index, fc_box, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box, exclude_area);
}
static void
fill_region_skew (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point *origin,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
db::fill_region (cell, fr, fill_cell_index, fc_box, row_step, column_step, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box);
db::fill_region (cell, fr, fill_cell_index, fc_box, row_step, column_step, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box, exclude_area);
}
static void
fill_region_multi (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step,
const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
db::fill_region_repeat (cell, fr, fill_cell_index, fc_box, row_step, column_step, fill_margin, remaining_polygons, glue_box);
db::fill_region_repeat (cell, fr, fill_cell_index, fc_box, row_step, column_step, fill_margin, remaining_polygons, glue_box, exclude_area);
}
static db::Instance cell_inst_dtransform_simple (db::Cell *cell, const db::Instance &inst, const db::DTrans &t)
@ -2215,6 +2215,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
gsi::arg ("fill_margin", db::Vector ()),
gsi::arg ("remaining_polygons", (db::Region *)0, "nil"),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("exclude_area", db::Region (), "empty"),
"@brief Fills the given region with cells of the given type (extended version)\n"
"@param region The region to fill\n"
"@param fill_cell_index The fill cell to place\n"
@ -2224,6 +2225,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"@param fill_margin See explanation below\n"
"@param remaining_polygons See explanation below\n"
"@param glue_box Guarantees fill cell compatibility to neighbor regions in enhanced mode\n"
"@param exclude_area A region that defines the areas which are not be filled\n"
"\n"
"This method creates a regular pattern of fill cells to cover the interior of the given region as far as possible. "
"This process is also known as tiling. This implementation supports rectangular (not necessarily square) tile cells. "
@ -2236,6 +2238,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"The implementation will basically try to find a repetition pattern of the tile cell's footprint "
"and produce instances which fit entirely into the fill region.\n"
"If an exclude area is given, the fill cells also must not overlap that region.\n"
"\n"
"There is also a version available which offers skew step vectors as a generalization of the orthogonal ones.\n"
"\n"
@ -2275,7 +2278,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"at the raster implied by origin at the glue box border and beyond. To ensure fill cell compatibility inside the tiling processor, it is sufficient to use the tile "
"box as the glue box.\n"
"\n"
"This method has been introduced in version 0.23 and enhanced in version 0.27.\n"
"This method has been introduced in version 0.23 and enhanced in version 0.27. The 'exclude_area' argument has been added in version 0.30.4.\n"
) +
gsi::method_ext ("fill_region", &fill_region_skew, gsi::arg ("region"),
gsi::arg ("fill_cell_index"),
@ -2287,16 +2290,18 @@ Class<db::Cell> decl_Cell ("db", "Cell",
gsi::arg ("fill_margin", db::Vector ()),
gsi::arg ("remaining_polygons", (db::Region *)0, "nil"),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("exclude_area", db::Region (), "empty"),
"@brief Fills the given region with cells of the given type (skew step version)\n"
"@param region The region to fill\n"
"@param fill_cell_index The fill cell to place\n"
"@param fc_bbox The fill cell's box to place\n"
"@param fc_bbox The fill cell's box, defining the box that needs to be inside the fill region\n"
"@param row_step The 'rows' step vector\n"
"@param column_step The 'columns' step vector\n"
"@param origin The global origin of the fill pattern or nil to allow local (per-polygon) optimization\n"
"@param remaining_parts See explanation in other version\n"
"@param fill_margin See explanation in other version\n"
"@param remaining_polygons See explanation in other version\n"
"@param exclude_area A region that defines the areas which are not be filled\n"
"\n"
"This version is similar to the version providing an orthogonal fill, but it offers more generic stepping of the fill cell.\n"
"The step pattern is defined by an origin and two vectors (row_step and column_step) which span the axes of the fill cell pattern.\n"
@ -2305,7 +2310,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"be overlapping and there can be space between the fill box instances. Fill boxes are placed where they fit entirely into a polygon of the region. "
"The fill boxes lower left corner is the reference for the fill pattern and aligns with the origin if given.\n"
"\n"
"This variant has been introduced in version 0.27.\n"
"This variant has been introduced in version 0.27. The 'exclude_area' argument has been added in version 0.30.4.\n"
) +
gsi::method_ext ("fill_region_multi", &fill_region_multi, gsi::arg ("region"),
gsi::arg ("fill_cell_index"),
@ -2315,6 +2320,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
gsi::arg ("fill_margin", db::Vector ()),
gsi::arg ("remaining_polygons", (db::Region *)0, "nil"),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("exclude_area", db::Region (), "empty"),
"@brief Fills the given region with cells of the given type in enhanced mode with iterations\n"
"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. "
@ -2323,7 +2329,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"The origin is ignored unless a glue box is given. See \\fill_region for a description of this concept.\n"
"\n"
"This method has been introduced in version 0.27.\n"
"This method has been introduced in version 0.27. The 'exclude_area' argument has been added in version 0.30.4.\n"
) +
gsi::method_ext ("begin_shapes_rec", &begin_shapes_rec, gsi::arg ("layer"),
"@brief Delivers a recursive shape iterator for the shapes below the cell on the given layer\n"

View File

@ -1309,23 +1309,23 @@ tl::Variant complex_op (db::Region *region, db::CompoundRegionOperationNode *nod
static void
fill_region (const db::Region *fr, db::Cell *cell, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point *origin,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
db::fill_region (cell, *fr, fill_cell_index, fc_box, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box);
db::fill_region (cell, *fr, fill_cell_index, fc_box, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box, exclude_area);
}
static void
fill_region_skew (const db::Region *fr, db::Cell *cell, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point *origin,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
db::fill_region (cell, *fr, fill_cell_index, fc_box, row_step, column_step, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box);
db::fill_region (cell, *fr, fill_cell_index, fc_box, row_step, column_step, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box, exclude_area);
}
static void
fill_region_multi (const db::Region *fr, db::Cell *cell, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step,
const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
db::fill_region_repeat (cell, *fr, fill_cell_index, fc_box, row_step, column_step, fill_margin, remaining_polygons, glue_box);
db::fill_region_repeat (cell, *fr, fill_cell_index, fc_box, row_step, column_step, fill_margin, remaining_polygons, glue_box, exclude_area);
}
static db::Region
@ -4340,6 +4340,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
gsi::arg ("fill_margin", db::Vector ()),
gsi::arg ("remaining_polygons", (db::Region *)0, "nil"),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("exclude_area", db::Region (), "empty"),
"@brief A mapping of \\Cell#fill_region to the Region class\n"
"\n"
"This method is equivalent to \\Cell#fill_region, but is based on Region (with the cell being the first parameter).\n"
@ -4348,14 +4349,15 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
) +
gsi::method_ext ("fill", &fill_region_skew, gsi::arg ("in_cell"),
gsi::arg ("fill_cell_index"),
gsi::arg ("fc_origin"),
gsi::arg ("fc_bbox"),
gsi::arg ("row_step"),
gsi::arg ("column_step"),
gsi::arg ("origin", &default_origin, "(0, 0)"),
gsi::arg ("remaining_parts", (db::Region *)0, "nil"),
gsi::arg ("fill_margin", db::Vector ()),
gsi::arg ("remaining_polygons", (db::Region *)0, "nil"),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("exclude_area", db::Region (), "empty"),
"@brief A mapping of \\Cell#fill_region to the Region class\n"
"\n"
"This method is equivalent to \\Cell#fill_region, but is based on Region (with the cell being the first parameter).\n"
@ -4364,12 +4366,13 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
) +
gsi::method_ext ("fill_multi", &fill_region_multi, gsi::arg ("in_cell"),
gsi::arg ("fill_cell_index"),
gsi::arg ("fc_origin"),
gsi::arg ("fc_bbox"),
gsi::arg ("row_step"),
gsi::arg ("column_step"),
gsi::arg ("fill_margin", db::Vector ()),
gsi::arg ("remaining_polygons", (db::Region *)0, "nil"),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("exclude_area", db::Region (), "empty"),
"@brief A mapping of \\Cell#fill_region to the Region class\n"
"\n"
"This method is equivalent to \\Cell#fill_region, but is based on Region (with the cell being the first parameter).\n"

View File

@ -644,6 +644,10 @@ module DRC
DRCFillStep::new(false, x, y)
end
def fill_exclude(excl)
DRCFillExclude::new(excl)
end
def auto_origin
DRCFillOrigin::new
end

View File

@ -5755,6 +5755,8 @@ CODE
# @li @b multi_origin @/b: lets the algorithm choose the origin and repeats the fill with different origins
# until no further fill cell can be fitted. @/li
# @li @b fill_pattern(..) @/b: specifies the fill pattern. @/li
# @li @b fill_exclude(excl) @/b: specifies a fill exclude region ('excl' is a polygon layer). This is conceptually
# equivalent to subtracting that layer from the fill region, but usually more efficient. @/li
# @/ul
#
# "fill_pattern" generates a fill pattern object. This object is used for configuring the fill pattern
@ -5895,6 +5897,7 @@ CODE
pattern = nil
origin = RBA::DPoint::new
repeat = false
excl = RBA::Region::new
args.each_with_index do |a,ai|
if a.is_a?(DRCSource)
@ -5907,6 +5910,11 @@ CODE
raise("Duplicate fill pattern specification for '#{m}' at argument ##{ai+1}")
end
pattern = a
elsif a.is_a?(DRCFillExclude)
if excl
raise("Duplicate exclude region specification for '#{m}' at argument ##{ai+1}")
end
excl = a.excl
elsif a.is_a?(DRCFillStep)
if a.for_row
if row_step
@ -5988,6 +5996,7 @@ CODE
tp.var("fc_index", fc_index)
tp.var("repeat", repeat)
tp.var("with_left", with_left)
tp.var("excl", excl)
tp.queue(<<"END")
var tc_box = _frame.bbox;
@ -5997,8 +6006,8 @@ CODE
tile_box = tile_box & tc_box;
var left = with_left ? Region.new : nil;
repeat ?
(region & tile_box).fill_multi(top_cell, fc_index, fc_box, rs, cs, fill_margin, left, _tile.bbox) :
(region & tile_box).fill(top_cell, fc_index, fc_box, rs, cs, origin, left, fill_margin, left, _tile.bbox);
(region & tile_box).fill_multi(top_cell, fc_index, fc_box, rs, cs, fill_margin, left, _tile.bbox, excl) :
(region & tile_box).fill(top_cell, fc_index, fc_box, rs, cs, origin, left, fill_margin, left, _tile.bbox, excl);
with_left && _output(#{result_arg}, left)
)
END
@ -6020,9 +6029,9 @@ END
@engine.run_timed("\"#{m}\" in: #{@engine.src_line}", self.data) do
if repeat
self.data.fill_multi(top_cell, fc_index, fc_box, rs, cs, fill_margin, result)
self.data.fill_multi(top_cell, fc_index, fc_box, rs, cs, fill_margin, result, RBA::Box::new, excl)
else
self.data.fill(top_cell, fc_index, fc_box, rs, cs, origin, result, fill_margin, result)
self.data.fill(top_cell, fc_index, fc_box, rs, cs, origin, result, fill_margin, result, RBA::Box::new, excl)
end
end

View File

@ -475,6 +475,18 @@ module DRC
end
# A wrapper for the fill step definition
class DRCFillExclude
def initialize(excl)
excl.is_a?(DRCLayer) || raise("Exclude layer argument needs to be a DRC layer")
excl.requires_region("Exclude layer")
@excl = excl.data
end
def excl
@excl
end
end
# A wrapper for the fill step definition
class DRCFillStep
def initialize(for_row, x, y = nil)