mirror of https://github.com/KLayout/klayout.git
1142 lines
28 KiB
C++
1142 lines
28 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2019 Matthias Koefferlein
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
#include "dbAsIfFlatRegion.h"
|
|
#include "dbFlatRegion.h"
|
|
#include "dbEmptyRegion.h"
|
|
#include "dbRegion.h"
|
|
#include "dbShapeProcessor.h"
|
|
#include "dbBoxConvert.h"
|
|
#include "dbBoxScanner.h"
|
|
#include "dbClip.h"
|
|
#include "dbPolygonTools.h"
|
|
|
|
#include <sstream>
|
|
|
|
namespace db
|
|
{
|
|
|
|
// -------------------------------------------------------------------------------------------------------------
|
|
// AsIfFlagRegion implementation
|
|
|
|
AsIfFlatRegion::AsIfFlatRegion ()
|
|
: RegionDelegate (), m_bbox_valid (false)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
AsIfFlatRegion::~AsIfFlatRegion ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
std::string
|
|
AsIfFlatRegion::to_string (size_t nmax) const
|
|
{
|
|
std::ostringstream os;
|
|
RegionIterator p (begin ());
|
|
bool first = true;
|
|
for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) {
|
|
if (! first) {
|
|
os << ";";
|
|
}
|
|
first = false;
|
|
os << p->to_string ();
|
|
}
|
|
if (! p.at_end ()) {
|
|
os << "...";
|
|
}
|
|
return os.str ();
|
|
}
|
|
|
|
Edges
|
|
AsIfFlatRegion::edges (const EdgeFilterBase *filter) const
|
|
{
|
|
Edges edges;
|
|
|
|
size_t n = 0;
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
n += p->vertices ();
|
|
}
|
|
edges.reserve (n);
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) {
|
|
if (! filter || filter->selected (*e)) {
|
|
edges.insert (*e);
|
|
}
|
|
}
|
|
}
|
|
|
|
return edges;
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::hulls () const
|
|
{
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (false));
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
db::Polygon h;
|
|
h.assign_hull (p->begin_hull (), p->end_hull ());
|
|
new_region->insert (h);
|
|
}
|
|
|
|
return new_region.release ();
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::holes () const
|
|
{
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (false));
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
for (size_t i = 0; i < p->holes (); ++i) {
|
|
db::Polygon h;
|
|
h.assign_hull (p->begin_hole ((unsigned int) i), p->end_hole ((unsigned int) i));
|
|
new_region->insert (h);
|
|
}
|
|
}
|
|
|
|
return new_region.release ();
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::rounded_corners (double rinner, double router, unsigned int n) const
|
|
{
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (false));
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
new_region->insert (db::compute_rounded (*p, rinner, router, n));
|
|
}
|
|
|
|
return new_region.release ();
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::smoothed (coord_type d) const
|
|
{
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (false));
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
new_region->insert (db::smooth (*p, d));
|
|
}
|
|
|
|
return new_region.release ();
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::in (const Region &other, bool invert) const
|
|
{
|
|
std::set <db::Polygon> op;
|
|
for (RegionIterator o (other.begin_merged ()); ! o.at_end (); ++o) {
|
|
op.insert (*o);
|
|
}
|
|
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (false));
|
|
|
|
for (RegionIterator o (begin_merged ()); ! o.at_end (); ++o) {
|
|
if ((op.find (*o) == op.end ()) == invert) {
|
|
new_region->insert (*o);
|
|
}
|
|
}
|
|
|
|
return new_region.release ();
|
|
}
|
|
|
|
size_t
|
|
AsIfFlatRegion::size () const
|
|
{
|
|
size_t n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
++n;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
bool
|
|
AsIfFlatRegion::is_box () const
|
|
{
|
|
RegionIterator p (begin ());
|
|
if (p.at_end ()) {
|
|
return false;
|
|
} else {
|
|
const db::Polygon &poly = *p;
|
|
++p;
|
|
if (! p.at_end ()) {
|
|
return false;
|
|
} else {
|
|
return poly.is_box ();
|
|
}
|
|
}
|
|
}
|
|
|
|
AsIfFlatRegion::area_type
|
|
AsIfFlatRegion::area (const db::Box &box) const
|
|
{
|
|
area_type a = 0;
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
if (box.empty () || p->box ().inside (box)) {
|
|
a += p->area ();
|
|
} else {
|
|
std::vector<db::Polygon> clipped;
|
|
clip_poly (*p, box, clipped);
|
|
for (std::vector<db::Polygon>::const_iterator c = clipped.begin (); c != clipped.end (); ++c) {
|
|
a += c->area ();
|
|
}
|
|
}
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
AsIfFlatRegion::perimeter_type
|
|
AsIfFlatRegion::perimeter (const db::Box &box) const
|
|
{
|
|
perimeter_type d = 0;
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
|
|
if (box.empty () || p->box ().inside (box)) {
|
|
d += p->perimeter ();
|
|
} else {
|
|
|
|
for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) {
|
|
|
|
if (box.empty ()) {
|
|
d += (*e).length ();
|
|
} else {
|
|
|
|
std::pair<bool, db::Edge> ce = (*e).clipped (box);
|
|
if (ce.first) {
|
|
|
|
db::Coord dx = ce.second.dx ();
|
|
db::Coord dy = ce.second.dy ();
|
|
db::Coord x = ce.second.p1 ().x ();
|
|
db::Coord y = ce.second.p1 ().y ();
|
|
if ((dx == 0 && x == box.left () && dy < 0) ||
|
|
(dx == 0 && x == box.right () && dy > 0) ||
|
|
(dy == 0 && y == box.top () && dx < 0) ||
|
|
(dy == 0 && y == box.bottom () && dx > 0)) {
|
|
// not counted -> box is at outside side of the edge
|
|
} else {
|
|
d += ce.second.length ();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
Box AsIfFlatRegion::bbox () const
|
|
{
|
|
if (! m_bbox_valid) {
|
|
m_bbox = compute_bbox ();
|
|
m_bbox_valid = true;
|
|
}
|
|
return m_bbox;
|
|
}
|
|
|
|
Box AsIfFlatRegion::compute_bbox () const
|
|
{
|
|
db::Box b;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
b += p->box ();
|
|
}
|
|
return b;
|
|
}
|
|
|
|
void AsIfFlatRegion::update_bbox (const db::Box &b)
|
|
{
|
|
m_bbox = b;
|
|
m_bbox_valid = true;
|
|
}
|
|
|
|
void AsIfFlatRegion::invalidate_bbox ()
|
|
{
|
|
m_bbox_valid = false;
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::filtered (const PolygonFilterBase &filter) const
|
|
{
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion ());
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
if (filter.selected (*p)) {
|
|
new_region->insert (*p);
|
|
}
|
|
}
|
|
|
|
return new_region.release ();
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::selected_interacting_generic (const Edges &other, bool inverse) const
|
|
{
|
|
if (other.empty ()) {
|
|
if (! inverse) {
|
|
return new EmptyRegion ();
|
|
} else {
|
|
return clone ();
|
|
}
|
|
} else if (empty ()) {
|
|
return clone ();
|
|
}
|
|
|
|
db::box_scanner2<db::Polygon, size_t, db::Edge, size_t> scanner (report_progress (), progress_desc ());
|
|
scanner.reserve1 (size ());
|
|
scanner.reserve2 (other.size ());
|
|
|
|
std::auto_ptr<FlatRegion> output (new FlatRegion (false));
|
|
region_to_edge_interaction_filter<Shapes> filter (output->raw_polygons (), inverse);
|
|
|
|
AddressablePolygonDelivery p (begin_merged (), has_valid_merged_polygons ());
|
|
|
|
for ( ; ! p.at_end (); ++p) {
|
|
scanner.insert1 (p.operator-> (), 0);
|
|
if (inverse) {
|
|
filter.preset (p.operator-> ());
|
|
}
|
|
}
|
|
|
|
AddressableEdgeDelivery e (other.addressable_edges ());
|
|
|
|
for ( ; ! e.at_end (); ++e) {
|
|
scanner.insert2 (e.operator-> (), 0);
|
|
}
|
|
|
|
scanner.process (filter, 1, db::box_convert<db::Polygon> (), db::box_convert<db::Edge> ());
|
|
|
|
if (inverse) {
|
|
filter.fill_output ();
|
|
}
|
|
|
|
return output.release ();
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const
|
|
{
|
|
db::EdgeProcessor ep (report_progress (), progress_desc ());
|
|
ep.set_base_verbosity (base_verbosity ());
|
|
|
|
// shortcut
|
|
if (empty ()) {
|
|
return clone ();
|
|
} else if (other.empty ()) {
|
|
// clear, if b is empty and
|
|
// * mode is inside or interacting and inverse is false ("inside" or "interacting")
|
|
// * mode is outside and inverse is true ("not outside")
|
|
if ((mode <= 0) != inverse) {
|
|
return new EmptyRegion ();
|
|
} else {
|
|
return clone ();
|
|
}
|
|
}
|
|
|
|
for (RegionIterator p = other.begin (); ! p.at_end (); ++p) {
|
|
if (p->box ().touches (bbox ())) {
|
|
ep.insert (*p, 0);
|
|
}
|
|
}
|
|
|
|
size_t n = 1;
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p, ++n) {
|
|
if (mode > 0 || p->box ().touches (other.bbox ())) {
|
|
ep.insert (*p, n);
|
|
}
|
|
}
|
|
|
|
db::InteractionDetector id (mode, 0);
|
|
id.set_include_touching (touching);
|
|
db::EdgeSink es;
|
|
ep.process (es, id);
|
|
id.finish ();
|
|
|
|
std::auto_ptr<FlatRegion> output (new FlatRegion (false));
|
|
|
|
n = 0;
|
|
std::set <size_t> selected;
|
|
for (db::InteractionDetector::iterator i = id.begin (); i != id.end () && i->first == 0; ++i) {
|
|
++n;
|
|
selected.insert (i->second);
|
|
}
|
|
|
|
output->reserve (n);
|
|
|
|
n = 1;
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p, ++n) {
|
|
if ((selected.find (n) == selected.end ()) == inverse) {
|
|
output->raw_polygons ().insert (*p);
|
|
}
|
|
}
|
|
|
|
return output.release ();
|
|
}
|
|
|
|
EdgePairs
|
|
AsIfFlatRegion::grid_check (db::Coord gx, db::Coord gy) const
|
|
{
|
|
EdgePairs out;
|
|
|
|
gx = std::max (db::Coord (1), gx);
|
|
gy = std::max (db::Coord (1), gy);
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
|
|
for (size_t i = 0; i < p->holes () + 1; ++i) {
|
|
|
|
db::Polygon::polygon_contour_iterator b, e;
|
|
|
|
if (i == 0) {
|
|
b = p->begin_hull ();
|
|
e = p->end_hull ();
|
|
} else {
|
|
b = p->begin_hole ((unsigned int) (i - 1));
|
|
e = p->end_hole ((unsigned int) (i - 1));
|
|
}
|
|
|
|
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
|
|
if (((*pt).x () % gx) != 0 || ((*pt).y () % gy) != 0) {
|
|
out.insert (EdgePair (db::Edge (*pt, *pt), db::Edge (*pt, *pt)));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
static bool ac_less (double cos_a, bool gt180_a, double cos_b, bool gt180_b)
|
|
{
|
|
if (gt180_a != gt180_b) {
|
|
return gt180_a < gt180_b;
|
|
} else {
|
|
if (gt180_a) {
|
|
return cos_a < cos_b - 1e-10;
|
|
} else {
|
|
return cos_a > cos_b + 1e-10;
|
|
}
|
|
}
|
|
}
|
|
|
|
EdgePairs
|
|
AsIfFlatRegion::angle_check (double min, double max, bool inverse) const
|
|
{
|
|
EdgePairs out;
|
|
|
|
double cos_min = cos (std::max (0.0, std::min (360.0, min)) / 180.0 * M_PI);
|
|
double cos_max = cos (std::max (0.0, std::min (360.0, max)) / 180.0 * M_PI);
|
|
bool gt180_min = min > 180.0;
|
|
bool gt180_max = max > 180.0;
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
|
|
for (size_t i = 0; i < p->holes () + 1; ++i) {
|
|
|
|
const db::Polygon::contour_type *h = 0;
|
|
if (i == 0) {
|
|
h = &p->hull ();
|
|
} else {
|
|
h = &p->hole ((unsigned int) (i - 1));
|
|
}
|
|
|
|
size_t np = h->size ();
|
|
|
|
for (size_t j = 0; j < np; ++j) {
|
|
|
|
db::Edge e ((*h) [j], (*h) [(j + 1) % np]);
|
|
db::Edge ee (e.p2 (), (*h) [(j + 2) % np]);
|
|
double le = e.double_length ();
|
|
double lee = ee.double_length ();
|
|
|
|
double cos_a = -db::sprod (e, ee) / (le * lee);
|
|
bool gt180_a = db::vprod_sign (e, ee) > 0;
|
|
|
|
if ((ac_less (cos_a, gt180_a, cos_max, gt180_max) && !ac_less (cos_a, gt180_a, cos_min, gt180_min)) == !inverse) {
|
|
out.insert (EdgePair (e, ee));
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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 (snapped_polygon (*p, gx, gy, heap));
|
|
}
|
|
|
|
return new_region.release ();
|
|
}
|
|
|
|
/**
|
|
* @brief A helper class to implement the strange polygon detector
|
|
*/
|
|
struct DB_PUBLIC StrangePolygonInsideFunc
|
|
{
|
|
inline bool operator() (int wc) const
|
|
{
|
|
return wc < 0 || wc > 1;
|
|
}
|
|
};
|
|
|
|
void
|
|
AsIfFlatRegion::produce_shape_for_strange_polygon (const db::Polygon &poly, db::Shapes &shapes)
|
|
{
|
|
EdgeProcessor ep;
|
|
ep.insert (poly);
|
|
|
|
StrangePolygonInsideFunc inside;
|
|
db::GenericMerge<StrangePolygonInsideFunc> op (inside);
|
|
db::ShapeGenerator pc (shapes, false);
|
|
db::PolygonGenerator pg (pc, false, false);
|
|
ep.process (pg, op);
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::strange_polygon_check () const
|
|
{
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (merged_semantics ()));
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
produce_shape_for_strange_polygon (*p, new_region->raw_polygons ());
|
|
}
|
|
|
|
return new_region.release ();
|
|
}
|
|
|
|
EdgePairs
|
|
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
|
|
{
|
|
EdgePairs result;
|
|
|
|
db::box_scanner<db::Polygon, size_t> scanner (report_progress (), progress_desc ());
|
|
scanner.reserve (size () + (other ? other->size () : 0));
|
|
|
|
AddressablePolygonDelivery p (begin_merged (), has_valid_merged_polygons ());
|
|
|
|
size_t n = 0;
|
|
for ( ; ! p.at_end (); ++p) {
|
|
scanner.insert (p.operator-> (), n);
|
|
n += 2;
|
|
}
|
|
|
|
AddressablePolygonDelivery po;
|
|
|
|
if (other) {
|
|
|
|
po = other->addressable_merged_polygons ();
|
|
|
|
n = 1;
|
|
for ( ; ! po.at_end (); ++po) {
|
|
scanner.insert (po.operator-> (), n);
|
|
n += 2;
|
|
}
|
|
|
|
}
|
|
|
|
EdgeRelationFilter check (rel, d, metrics);
|
|
check.set_include_zero (other != 0);
|
|
check.set_whole_edges (whole_edges);
|
|
check.set_ignore_angle (ignore_angle);
|
|
check.set_min_projection (min_projection);
|
|
check.set_max_projection (max_projection);
|
|
|
|
edge2edge_check<db::EdgePairs> edge_check (check, result, different_polygons, other != 0);
|
|
poly2poly_check<db::EdgePairs> poly_check (edge_check);
|
|
|
|
do {
|
|
scanner.process (poly_check, d, db::box_convert<db::Polygon> ());
|
|
} while (edge_check.prepare_next_pass ());
|
|
|
|
return result;
|
|
}
|
|
|
|
EdgePairs
|
|
AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
|
{
|
|
EdgePairs result;
|
|
|
|
EdgeRelationFilter check (rel, d, metrics);
|
|
check.set_whole_edges (whole_edges);
|
|
check.set_ignore_angle (ignore_angle);
|
|
check.set_min_projection (min_projection);
|
|
check.set_max_projection (max_projection);
|
|
|
|
edge2edge_check<db::EdgePairs> edge_check (check, result, false, false);
|
|
poly2poly_check<db::EdgePairs> poly_check (edge_check);
|
|
|
|
do {
|
|
|
|
size_t n = 0;
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
poly_check.enter (*p, n);
|
|
n += 2;
|
|
}
|
|
|
|
} while (edge_check.prepare_next_pass ());
|
|
|
|
return result;
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc) const
|
|
{
|
|
if (empty ()) {
|
|
|
|
return new EmptyRegion ();
|
|
|
|
} else if (is_box ()) {
|
|
|
|
// take box only if min_wc == 0, otherwise clear
|
|
if (min_wc > 0) {
|
|
return new EmptyRegion ();
|
|
} else {
|
|
return clone ();
|
|
}
|
|
|
|
} else {
|
|
|
|
db::EdgeProcessor ep (report_progress (), progress_desc ());
|
|
ep.set_base_verbosity (base_verbosity ());
|
|
|
|
// count edges and reserve memory
|
|
size_t n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) {
|
|
n += p->vertices ();
|
|
}
|
|
ep.reserve (n);
|
|
|
|
// insert the polygons into the processor
|
|
n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) {
|
|
ep.insert (*p, n);
|
|
}
|
|
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (true));
|
|
|
|
// and run the merge step
|
|
db::MergeOp op (min_wc);
|
|
db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
|
|
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence);
|
|
ep.process (pg, op);
|
|
|
|
return new_region.release ();
|
|
|
|
}
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::region_from_box (const db::Box &b)
|
|
{
|
|
if (! b.empty () && b.width () > 0 && b.height () > 0) {
|
|
FlatRegion *new_region = new FlatRegion ();
|
|
new_region->insert (b);
|
|
return new_region;
|
|
} else {
|
|
return new EmptyRegion ();
|
|
}
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::sized (coord_type d, unsigned int mode) const
|
|
{
|
|
return sized (d, d, mode);
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const
|
|
{
|
|
if (empty ()) {
|
|
|
|
// ignore empty
|
|
return new EmptyRegion ();
|
|
|
|
} else if (is_box () && mode >= 2) {
|
|
|
|
// simplified handling for a box
|
|
db::Box b = bbox ().enlarged (db::Vector (dx, dy));
|
|
return region_from_box (b);
|
|
|
|
} else if (! merged_semantics ()) {
|
|
|
|
// Generic case
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (false /*output isn't merged*/));
|
|
|
|
db::ShapeGenerator pc (new_region->raw_polygons (), false);
|
|
db::PolygonGenerator pg (pc, false, true);
|
|
db::SizingPolygonFilter sf (pg, dx, dy, mode);
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
sf.put (*p);
|
|
}
|
|
|
|
return new_region.release ();
|
|
|
|
} else {
|
|
|
|
// Generic case - the size operation will merge first
|
|
db::EdgeProcessor ep (report_progress (), progress_desc ());
|
|
ep.set_base_verbosity (base_verbosity ());
|
|
|
|
// count edges and reserve memory
|
|
size_t n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
n += p->vertices ();
|
|
}
|
|
ep.reserve (n);
|
|
|
|
// insert the polygons into the processor
|
|
n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) {
|
|
ep.insert (*p, n);
|
|
}
|
|
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (false /*output isn't merged*/));
|
|
db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
|
|
db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/);
|
|
db::SizingPolygonFilter siz (pg2, dx, dy, mode);
|
|
db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/);
|
|
db::BooleanOp op (db::BooleanOp::Or);
|
|
ep.process (pg, op);
|
|
|
|
return new_region.release ();
|
|
|
|
}
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::and_with (const Region &other) const
|
|
{
|
|
if (empty () || other.empty ()) {
|
|
|
|
// Nothing to do
|
|
return new EmptyRegion ();
|
|
|
|
} else if (is_box () && other.is_box ()) {
|
|
|
|
// Simplified handling for boxes
|
|
db::Box b = bbox ();
|
|
b &= other.bbox ();
|
|
return region_from_box (b);
|
|
|
|
} else if (is_box () && ! other.strict_handling ()) {
|
|
|
|
// map AND with box to clip ..
|
|
db::Box b = bbox ();
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (false));
|
|
|
|
std::vector<db::Polygon> clipped;
|
|
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) {
|
|
clipped.clear ();
|
|
clip_poly (*p, b, clipped);
|
|
new_region->raw_polygons ().insert (clipped.begin (), clipped.end ());
|
|
}
|
|
|
|
return new_region.release ();
|
|
|
|
} else if (other.is_box () && ! strict_handling ()) {
|
|
|
|
// map AND with box to clip ..
|
|
db::Box b = other.bbox ();
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (false));
|
|
|
|
std::vector<db::Polygon> clipped;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
clipped.clear ();
|
|
clip_poly (*p, b, clipped);
|
|
new_region->raw_polygons ().insert (clipped.begin (), clipped.end ());
|
|
}
|
|
|
|
return new_region.release ();
|
|
|
|
} else if (! bbox ().overlaps (other.bbox ())) {
|
|
|
|
// Result will be nothing
|
|
return new EmptyRegion ();
|
|
|
|
} else {
|
|
|
|
// Generic case
|
|
db::EdgeProcessor ep (report_progress (), progress_desc ());
|
|
ep.set_base_verbosity (base_verbosity ());
|
|
|
|
// count edges and reserve memory
|
|
size_t n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
n += p->vertices ();
|
|
}
|
|
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) {
|
|
n += p->vertices ();
|
|
}
|
|
ep.reserve (n);
|
|
|
|
// insert the polygons into the processor
|
|
n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) {
|
|
ep.insert (*p, n);
|
|
}
|
|
n = 1;
|
|
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) {
|
|
ep.insert (*p, n);
|
|
}
|
|
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (true));
|
|
db::BooleanOp op (db::BooleanOp::And);
|
|
db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
|
|
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ());
|
|
ep.process (pg, op);
|
|
|
|
return new_region.release ();
|
|
|
|
}
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::not_with (const Region &other) const
|
|
{
|
|
if (empty ()) {
|
|
|
|
// Nothing to do
|
|
return new EmptyRegion ();
|
|
|
|
} else if (other.empty () && ! strict_handling ()) {
|
|
|
|
// Nothing to do
|
|
return clone ();
|
|
|
|
} else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling ()) {
|
|
|
|
// Nothing to do
|
|
return clone ();
|
|
|
|
} else {
|
|
|
|
// Generic case
|
|
db::EdgeProcessor ep (report_progress (), progress_desc ());
|
|
ep.set_base_verbosity (base_verbosity ());
|
|
|
|
// count edges and reserve memory
|
|
size_t n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
n += p->vertices ();
|
|
}
|
|
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) {
|
|
n += p->vertices ();
|
|
}
|
|
ep.reserve (n);
|
|
|
|
// insert the polygons into the processor
|
|
n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) {
|
|
ep.insert (*p, n);
|
|
}
|
|
n = 1;
|
|
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) {
|
|
ep.insert (*p, n);
|
|
}
|
|
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (true));
|
|
db::BooleanOp op (db::BooleanOp::ANotB);
|
|
db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
|
|
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ());
|
|
ep.process (pg, op);
|
|
|
|
return new_region.release ();
|
|
|
|
}
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::xor_with (const Region &other) const
|
|
{
|
|
if (empty () && ! other.strict_handling ()) {
|
|
|
|
return other.delegate ()->clone ();
|
|
|
|
} else if (other.empty () && ! strict_handling ()) {
|
|
|
|
return clone ();
|
|
|
|
} else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling () && ! other.strict_handling ()) {
|
|
|
|
// Simplified handling for disjunct case
|
|
return or_with (other);
|
|
|
|
} else {
|
|
|
|
// Generic case
|
|
db::EdgeProcessor ep (report_progress (), progress_desc ());
|
|
ep.set_base_verbosity (base_verbosity ());
|
|
|
|
// count edges and reserve memory
|
|
size_t n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
n += p->vertices ();
|
|
}
|
|
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) {
|
|
n += p->vertices ();
|
|
}
|
|
ep.reserve (n);
|
|
|
|
// insert the polygons into the processor
|
|
n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) {
|
|
ep.insert (*p, n);
|
|
}
|
|
n = 1;
|
|
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) {
|
|
ep.insert (*p, n);
|
|
}
|
|
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (true));
|
|
db::BooleanOp op (db::BooleanOp::Xor);
|
|
db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
|
|
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ());
|
|
ep.process (pg, op);
|
|
|
|
return new_region.release ();
|
|
|
|
}
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::or_with (const Region &other) const
|
|
{
|
|
if (empty () && ! other.strict_handling ()) {
|
|
|
|
return other.delegate ()->clone ();
|
|
|
|
} else if (other.empty () && ! strict_handling ()) {
|
|
|
|
// Nothing to do
|
|
return clone ();
|
|
|
|
} else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling () && ! other.strict_handling ()) {
|
|
|
|
// Simplified handling for disjunct case
|
|
return add (other);
|
|
|
|
} else {
|
|
|
|
// Generic case
|
|
db::EdgeProcessor ep (report_progress (), progress_desc ());
|
|
ep.set_base_verbosity (base_verbosity ());
|
|
|
|
// count edges and reserve memory
|
|
size_t n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
n += p->vertices ();
|
|
}
|
|
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) {
|
|
n += p->vertices ();
|
|
}
|
|
ep.reserve (n);
|
|
|
|
// insert the polygons into the processor
|
|
n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) {
|
|
ep.insert (*p, n);
|
|
}
|
|
n = 1;
|
|
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) {
|
|
ep.insert (*p, n);
|
|
}
|
|
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (true));
|
|
db::BooleanOp op (db::BooleanOp::Or);
|
|
db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
|
|
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ());
|
|
ep.process (pg, op);
|
|
|
|
return new_region.release ();
|
|
|
|
}
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::add (const Region &other) const
|
|
{
|
|
FlatRegion *other_flat = dynamic_cast<FlatRegion *> (other.delegate ());
|
|
if (other_flat) {
|
|
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (*other_flat));
|
|
new_region->set_is_merged (false);
|
|
new_region->invalidate_cache ();
|
|
|
|
size_t n = new_region->raw_polygons ().size () + size ();
|
|
|
|
new_region->reserve (n);
|
|
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
new_region->raw_polygons ().insert (*p);
|
|
}
|
|
|
|
return new_region.release ();
|
|
|
|
} else {
|
|
|
|
std::auto_ptr<FlatRegion> new_region (new FlatRegion (false /*not merged*/));
|
|
|
|
size_t n = size () + other.size ();
|
|
|
|
new_region->reserve (n);
|
|
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
new_region->raw_polygons ().insert (*p);
|
|
}
|
|
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) {
|
|
new_region->raw_polygons ().insert (*p);
|
|
}
|
|
|
|
return new_region.release ();
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
AsIfFlatRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const
|
|
{
|
|
// improves performance when inserting an original layout into the same layout
|
|
db::LayoutLocker locker (layout);
|
|
|
|
db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer);
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
shapes.insert (*p);
|
|
}
|
|
}
|
|
|
|
bool
|
|
AsIfFlatRegion::equals (const Region &other) const
|
|
{
|
|
if (empty () != other.empty ()) {
|
|
return false;
|
|
}
|
|
if (size () != other.size ()) {
|
|
return false;
|
|
}
|
|
RegionIterator o1 (begin ());
|
|
RegionIterator o2 (other.begin ());
|
|
while (! o1.at_end () && ! o2.at_end ()) {
|
|
if (*o1 != *o2) {
|
|
return false;
|
|
}
|
|
++o1;
|
|
++o2;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
AsIfFlatRegion::less (const Region &other) const
|
|
{
|
|
if (empty () != other.empty ()) {
|
|
return empty () < other.empty ();
|
|
}
|
|
if (size () != other.size ()) {
|
|
return (size () < other.size ());
|
|
}
|
|
RegionIterator o1 (begin ());
|
|
RegionIterator o2 (other.begin ());
|
|
while (! o1.at_end () && ! o2.at_end ()) {
|
|
if (*o1 != *o2) {
|
|
return *o1 < *o2;
|
|
}
|
|
++o1;
|
|
++o2;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|