mirror of https://github.com/KLayout/klayout.git
2108 lines
63 KiB
C++
2108 lines
63 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2026 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 "dbFlatEdgePairs.h"
|
|
#include "dbFlatEdges.h"
|
|
#include "dbFlatTexts.h"
|
|
#include "dbEmptyRegion.h"
|
|
#include "dbEmptyEdgePairs.h"
|
|
#include "dbEmptyEdges.h"
|
|
#include "dbRegion.h"
|
|
#include "dbRegionUtils.h"
|
|
#include "dbShapeProcessor.h"
|
|
#include "dbBoxConvert.h"
|
|
#include "dbBoxScanner.h"
|
|
#include "dbClip.h"
|
|
#include "dbPolygonTools.h"
|
|
#include "dbHash.h"
|
|
#include "dbRegionLocalOperations.h"
|
|
#include "dbHierProcessor.h"
|
|
#include "dbCompoundOperation.h"
|
|
#include "dbLayoutToNetlist.h"
|
|
|
|
#include <sstream>
|
|
|
|
namespace db
|
|
{
|
|
|
|
namespace {
|
|
|
|
struct ResultCountingInserter
|
|
{
|
|
typedef db::Polygon value_type;
|
|
|
|
ResultCountingInserter (std::unordered_map<const db::Polygon *, size_t, tl::ptr_hash_from_value<db::Polygon> > &result)
|
|
: mp_result (&result)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void insert (const db::Polygon &p)
|
|
{
|
|
(*mp_result)[&p] += 1;
|
|
}
|
|
|
|
void init (const db::Polygon *p)
|
|
{
|
|
(*mp_result)[p] = 0;
|
|
}
|
|
|
|
private:
|
|
std::unordered_map<const db::Polygon *, size_t, tl::ptr_hash_from_value<db::Polygon> > *mp_result;
|
|
};
|
|
|
|
}
|
|
|
|
static
|
|
RegionDelegate *region_from_box (const db::Box &b, db::properties_id_type prop_id)
|
|
{
|
|
if (! b.empty () && b.width () > 0 && b.height () > 0) {
|
|
FlatRegion *new_region = new FlatRegion ();
|
|
if (prop_id != 0) {
|
|
new_region->insert (db::BoxWithProperties (b, prop_id));
|
|
} else {
|
|
new_region->insert (b);
|
|
}
|
|
return new_region;
|
|
} else {
|
|
return new EmptyRegion ();
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------------------------------
|
|
// AsIfFlagRegion implementation
|
|
|
|
AsIfFlatRegion::AsIfFlatRegion ()
|
|
: RegionDelegate (), m_bbox_valid (false)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
AsIfFlatRegion::~AsIfFlatRegion ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
AsIfFlatRegion::AsIfFlatRegion (const AsIfFlatRegion &other)
|
|
: RegionDelegate (other), m_bbox_valid (false)
|
|
{
|
|
operator= (other);
|
|
}
|
|
|
|
AsIfFlatRegion &
|
|
AsIfFlatRegion::operator= (const AsIfFlatRegion &other)
|
|
{
|
|
if (this != &other) {
|
|
m_bbox_valid = other.m_bbox_valid;
|
|
m_bbox = other.m_bbox;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
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.prop_id () != 0) {
|
|
os << db::properties (p.prop_id ()).to_dict_var ().to_string ();
|
|
}
|
|
}
|
|
if (! p.at_end ()) {
|
|
os << "...";
|
|
}
|
|
return os.str ();
|
|
}
|
|
|
|
EdgesDelegate *
|
|
AsIfFlatRegion::edges (const EdgeFilterBase *filter, const PolygonToEdgeProcessorBase *proc) const
|
|
{
|
|
std::unique_ptr<FlatEdges> result (new FlatEdges ());
|
|
|
|
size_t n = 0;
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
n += p->vertices ();
|
|
}
|
|
result->reserve (n);
|
|
|
|
std::vector<db::EdgeWithProperties> heap;
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
|
|
if (proc) {
|
|
|
|
heap.clear ();
|
|
proc->process (p.wp (), heap);
|
|
|
|
for (auto e = heap.begin (); e != heap.end (); ++e) {
|
|
if (! filter || filter->selected (*e, e->properties_id ())) {
|
|
if (e->properties_id () != 0) {
|
|
result->insert (*e);
|
|
} else {
|
|
result->insert (e->base ());
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
auto prop_id = p.prop_id ();
|
|
|
|
for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) {
|
|
if (! filter || filter->selected (*e, prop_id)) {
|
|
if (prop_id != 0) {
|
|
result->insert (db::EdgeWithProperties (*e, prop_id));
|
|
} else {
|
|
result->insert (*e);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result.release ();
|
|
}
|
|
|
|
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 if (p->is_box ()) {
|
|
a += (p->box () & box).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;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
struct ComparePolygonsWithProperties
|
|
{
|
|
bool operator() (const std::pair<db::properties_id_type, const db::Polygon *> &a, const std::pair<db::properties_id_type, const db::Polygon *> &b) const
|
|
{
|
|
if (a.first != b.first) {
|
|
return db::properties_id_less (a.first, b.first);
|
|
}
|
|
return *a.second < *b.second;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
void AsIfFlatRegion::merge_polygons_to (db::Shapes &output, bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const
|
|
{
|
|
db::EdgeProcessor ep (report_progress (), progress_desc ());
|
|
ep.set_base_verbosity (base_verbosity ());
|
|
|
|
// count edges and reserve memory
|
|
size_t n = 0;
|
|
db::properties_id_type prop_id = 0;
|
|
bool multiple_properties = false;
|
|
for (RegionIterator s (begin ()); ! s.at_end (); ++s, ++n) {
|
|
if (n == 0) {
|
|
prop_id = s.prop_id ();
|
|
} else if (! multiple_properties && prop_id != s.prop_id ()) {
|
|
multiple_properties = true;
|
|
}
|
|
}
|
|
|
|
if (multiple_properties && join_properties_on_merge) {
|
|
|
|
// this merge variant requires a two-step approach: we first and then join original properties IDs
|
|
// in a separate interaction step
|
|
|
|
std::vector<db::properties_id_type> org_prop_ids;
|
|
org_prop_ids.reserve (n);
|
|
|
|
n = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
n += p->vertices ();
|
|
}
|
|
ep.reserve (n);
|
|
|
|
size_t org_poly_id = 0;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++org_poly_id) {
|
|
org_prop_ids.push_back (p.prop_id ());
|
|
ep.insert (*p, org_poly_id);
|
|
}
|
|
|
|
// and run the merge step
|
|
db::MergeOp op (min_wc);
|
|
db::PolygonContainer pc;
|
|
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence);
|
|
ep.process (pg, op);
|
|
|
|
// reserve space for new (merged) polygons
|
|
for (auto p = pc.polygons ().begin (); p != pc.polygons ().end (); ++p) {
|
|
n += p->vertices ();
|
|
}
|
|
ep.reserve (n);
|
|
|
|
size_t merged_poly_id = org_poly_id;
|
|
for (auto p = pc.polygons ().begin (); p != pc.polygons ().end (); ++p, ++merged_poly_id) {
|
|
ep.insert (*p, merged_poly_id);
|
|
}
|
|
|
|
// compute interactions between merged and original polygons
|
|
|
|
db::InteractionDetector id;
|
|
id.set_include_touching (false);
|
|
db::EdgeSink es;
|
|
ep.process (es, id);
|
|
id.finish ();
|
|
|
|
// collect original property IDs per merged polygon
|
|
|
|
std::vector<std::set<db::properties_id_type> > prop_ids_per_merged_polygon;
|
|
prop_ids_per_merged_polygon.resize (merged_poly_id - org_poly_id);
|
|
|
|
for (auto ii = id.begin (); ii != id.end (); ++ii) {
|
|
auto first = std::min (ii->first, ii->second);
|
|
auto second = std::max (ii->first, ii->second);
|
|
if (first < org_poly_id && second >= org_poly_id) {
|
|
prop_ids_per_merged_polygon [second - org_poly_id].insert (org_prop_ids [first]);
|
|
}
|
|
}
|
|
|
|
// Form new polygons with joined properties
|
|
|
|
for (auto p = pc.polygons ().begin (); p != pc.polygons ().end (); ++p) {
|
|
|
|
const std::set<db::properties_id_type> &prop_ids = prop_ids_per_merged_polygon [p - pc.polygons ().begin ()];
|
|
|
|
db::properties_id_type prop_id = 0;
|
|
if (prop_ids.size () == 1) {
|
|
prop_id = *prop_ids.begin ();
|
|
} else if (prop_ids.size () > 1) {
|
|
db::PropertiesSet ps;
|
|
for (auto p = prop_ids.begin (); p != prop_ids.end (); ++p) {
|
|
// merge in "larger one wins" mode - the advantage of this mode is that
|
|
// it is independent on the order of the attribute sets (which in fact are pointers)
|
|
ps.join_max (db::properties (*p));
|
|
}
|
|
prop_id = db::properties_id (ps);
|
|
}
|
|
|
|
if (prop_id != 0) {
|
|
output.insert (db::PolygonWithProperties (*p, prop_id));
|
|
} else {
|
|
output.insert (*p);
|
|
}
|
|
|
|
}
|
|
|
|
} else if (multiple_properties) {
|
|
|
|
db::Shapes result (output.is_editable ());
|
|
|
|
std::vector<std::pair<db::properties_id_type, const db::Polygon *> > polygons_by_prop_id;
|
|
polygons_by_prop_id.reserve (n);
|
|
|
|
db::AddressablePolygonDelivery addressable_polygons (begin ());
|
|
while (! addressable_polygons.at_end ()) {
|
|
polygons_by_prop_id.push_back (std::make_pair (addressable_polygons.prop_id (), addressable_polygons.operator-> ()));
|
|
addressable_polygons.inc ();
|
|
}
|
|
|
|
std::sort (polygons_by_prop_id.begin (), polygons_by_prop_id.end (), ComparePolygonsWithProperties ());
|
|
|
|
for (auto p = polygons_by_prop_id.begin (); p != polygons_by_prop_id.end (); ) {
|
|
|
|
auto pp = p;
|
|
while (pp != polygons_by_prop_id.end () && pp->first == p->first) {
|
|
++pp;
|
|
}
|
|
|
|
ep.clear ();
|
|
|
|
n = 0;
|
|
for (auto i = p; i != pp; ++i) {
|
|
n += i->second->vertices ();
|
|
}
|
|
ep.reserve (n);
|
|
|
|
n = 0;
|
|
for (auto i = p; i != pp; ++i, ++n) {
|
|
ep.insert (*i->second, n);
|
|
}
|
|
|
|
// and run the merge step
|
|
db::MergeOp op (min_wc);
|
|
db::ShapeGenerator pc (result, false /*don't clear*/, p->first);
|
|
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence);
|
|
ep.process (pg, op);
|
|
|
|
p = pp;
|
|
|
|
}
|
|
|
|
output.swap (result);
|
|
|
|
} else {
|
|
|
|
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);
|
|
}
|
|
|
|
output.clear ();
|
|
|
|
// and run the merge step
|
|
db::MergeOp op (min_wc);
|
|
db::ShapeGenerator pc (output, false /*don't clear*/, prop_id);
|
|
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence);
|
|
ep.process (pg, op);
|
|
|
|
}
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::filtered (const PolygonFilterBase &filter) const
|
|
{
|
|
std::unique_ptr<FlatRegion> new_region (new FlatRegion ());
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
if (filter.selected (*p, p.prop_id ())) {
|
|
if (p.prop_id () != 0) {
|
|
new_region->insert (db::PolygonWithProperties (*p, p.prop_id ()));
|
|
} else {
|
|
new_region->insert (*p);
|
|
}
|
|
}
|
|
}
|
|
|
|
new_region->set_is_merged (true);
|
|
return new_region.release ();
|
|
}
|
|
|
|
std::pair<RegionDelegate *, RegionDelegate *>
|
|
AsIfFlatRegion::filtered_pair (const PolygonFilterBase &filter) const
|
|
{
|
|
std::unique_ptr<FlatRegion> new_region_true (new FlatRegion ());
|
|
std::unique_ptr<FlatRegion> new_region_false (new FlatRegion ());
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
if (filter.selected (*p, p.prop_id ())) {
|
|
if (p.prop_id () != 0) {
|
|
new_region_true->insert (db::PolygonWithProperties (*p, p.prop_id ()));
|
|
} else {
|
|
new_region_true->insert (*p);
|
|
}
|
|
} else {
|
|
if (p.prop_id () != 0) {
|
|
new_region_false->insert (db::PolygonWithProperties (*p, p.prop_id ()));
|
|
} else {
|
|
new_region_false->insert (*p);
|
|
}
|
|
}
|
|
}
|
|
|
|
new_region_true->set_is_merged (true);
|
|
new_region_false->set_is_merged (true);
|
|
return std::make_pair (new_region_true.release (), new_region_false.release ());
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::processed (const PolygonProcessorBase &filter) const
|
|
{
|
|
std::unique_ptr<FlatRegion> new_region (new FlatRegion ());
|
|
if (filter.result_must_not_be_merged ()) {
|
|
new_region->set_merged_semantics (false);
|
|
}
|
|
|
|
std::vector<db::PolygonWithProperties> poly_res;
|
|
|
|
for (RegionIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) {
|
|
|
|
poly_res.clear ();
|
|
filter.process (p.wp (), poly_res);
|
|
for (auto pr = poly_res.begin (); pr != poly_res.end (); ++pr) {
|
|
if (pr->properties_id () != 0) {
|
|
new_region->insert (*pr);
|
|
} else {
|
|
new_region->insert (pr->base ());
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return new_region.release ();
|
|
}
|
|
|
|
EdgesDelegate *
|
|
AsIfFlatRegion::processed_to_edges (const PolygonToEdgeProcessorBase &filter) const
|
|
{
|
|
std::unique_ptr<FlatEdges> new_edges (new FlatEdges ());
|
|
if (filter.result_must_not_be_merged ()) {
|
|
new_edges->set_merged_semantics (false);
|
|
}
|
|
|
|
std::vector<db::EdgeWithProperties> edge_res;
|
|
|
|
for (RegionIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) {
|
|
|
|
edge_res.clear ();
|
|
filter.process (p.wp (), edge_res);
|
|
for (auto er = edge_res.begin (); er != edge_res.end (); ++er) {
|
|
if (er->properties_id () != 0) {
|
|
new_edges->insert (*er);
|
|
} else {
|
|
new_edges->insert (er->base ());
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return new_edges.release ();
|
|
}
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &filter) const
|
|
{
|
|
std::unique_ptr<FlatEdgePairs> new_edge_pairs (new FlatEdgePairs ());
|
|
if (filter.result_must_not_be_merged ()) {
|
|
new_edge_pairs->set_merged_semantics (false);
|
|
}
|
|
|
|
std::vector<db::EdgePairWithProperties> edge_pair_res;
|
|
|
|
for (RegionIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) {
|
|
|
|
edge_pair_res.clear ();
|
|
filter.process (p.wp (), edge_pair_res);
|
|
for (auto epr = edge_pair_res.begin (); epr != edge_pair_res.end (); ++epr) {
|
|
if (epr->properties_id () != 0) {
|
|
new_edge_pairs->insert (*epr);
|
|
} else {
|
|
new_edge_pairs->insert (epr->base ());
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return new_edge_pairs.release ();
|
|
}
|
|
|
|
namespace {
|
|
|
|
class OutputPairHolder
|
|
{
|
|
public:
|
|
OutputPairHolder (InteractingOutputMode output_mode, bool merged_semantics)
|
|
{
|
|
if (output_mode == None) {
|
|
return;
|
|
}
|
|
|
|
if (output_mode == Positive || output_mode == Negative || output_mode == PositiveAndNegative) {
|
|
m_positive.reset (new FlatRegion (merged_semantics));
|
|
m_results.push_back (& m_positive->raw_polygons ());
|
|
} else {
|
|
m_results.push_back ((db::Shapes *) 0);
|
|
}
|
|
|
|
if (output_mode == PositiveAndNegative) {
|
|
m_negative.reset (new FlatRegion (merged_semantics));
|
|
m_results.push_back (& m_negative->raw_polygons ());
|
|
}
|
|
}
|
|
|
|
std::pair<RegionDelegate *, RegionDelegate *> region_pair ()
|
|
{
|
|
return std::make_pair (m_positive.release (), m_negative.release ());
|
|
}
|
|
|
|
const std::vector<db::Shapes *> &results () { return m_results; }
|
|
|
|
private:
|
|
std::unique_ptr<FlatRegion> m_positive, m_negative;
|
|
std::vector<db::Shapes *> m_results;
|
|
};
|
|
|
|
}
|
|
|
|
std::pair<RegionDelegate *, RegionDelegate *>
|
|
AsIfFlatRegion::in_and_out_generic (const Region &other, InteractingOutputMode output_mode) const
|
|
{
|
|
OutputPairHolder oph (output_mode, merged_semantics ());
|
|
|
|
if (output_mode == None) {
|
|
return oph.region_pair ();
|
|
}
|
|
|
|
// shortcut
|
|
if (empty ()) {
|
|
if (output_mode == Positive || output_mode == Negative) {
|
|
return std::make_pair (clone (), (RegionDelegate *) 0);
|
|
} else {
|
|
return std::make_pair (clone (), clone ());
|
|
}
|
|
} else if (other.empty ()) {
|
|
if (output_mode == Positive) {
|
|
return std::make_pair (new EmptyRegion (), (RegionDelegate *) 0);
|
|
} else if (output_mode == Negative) {
|
|
return std::make_pair (clone (), (RegionDelegate *) 0);
|
|
} else {
|
|
return std::make_pair (new EmptyRegion (), clone ());
|
|
}
|
|
}
|
|
|
|
std::set <db::Polygon> op;
|
|
for (RegionIterator o (other.begin_merged ()); ! o.at_end (); ++o) {
|
|
op.insert (*o);
|
|
}
|
|
|
|
std::unique_ptr<FlatRegion> new_region (new FlatRegion (false));
|
|
|
|
for (RegionIterator o (begin_merged ()); ! o.at_end (); ++o) {
|
|
if (op.find (*o) != op.end ()) {
|
|
if (output_mode == Positive || output_mode == PositiveAndNegative) {
|
|
oph.results () [0]->insert (*o);
|
|
}
|
|
} else {
|
|
if (output_mode == Negative) {
|
|
oph.results () [0]->insert (*o);
|
|
} else if (output_mode == PositiveAndNegative) {
|
|
oph.results () [1]->insert (*o);
|
|
}
|
|
}
|
|
}
|
|
|
|
return oph.region_pair ();
|
|
}
|
|
|
|
std::pair<RegionDelegate *, RegionDelegate *>
|
|
AsIfFlatRegion::selected_interacting_generic (const Edges &other, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const
|
|
{
|
|
OutputPairHolder oph (output_mode, merged_semantics () || is_merged ());
|
|
|
|
if (output_mode == None) {
|
|
return oph.region_pair ();
|
|
}
|
|
|
|
min_count = std::max (size_t (1), min_count);
|
|
|
|
// shortcut
|
|
if (empty ()) {
|
|
if (output_mode == Positive || output_mode == Negative) {
|
|
return std::make_pair (clone (), (RegionDelegate *) 0);
|
|
} else {
|
|
return std::make_pair (clone (), clone ());
|
|
}
|
|
} else if (max_count < min_count || other.empty ()) {
|
|
if (output_mode == Positive) {
|
|
return std::make_pair (new EmptyRegion (), (RegionDelegate *) 0);
|
|
} else if (output_mode == Negative) {
|
|
return std::make_pair (clone (), (RegionDelegate *) 0);
|
|
} else {
|
|
return std::make_pair (new EmptyRegion (), clone ());
|
|
}
|
|
}
|
|
|
|
bool counting = !(min_count == 1 && max_count == std::numeric_limits<size_t>::max ());
|
|
|
|
db::RegionIterator polygons (begin_merged ());
|
|
|
|
db::interacting_with_edge_local_operation<db::Polygon, db::Edge, db::Polygon> op (output_mode, min_count, max_count, true);
|
|
|
|
db::local_processor<db::Polygon, db::Edge, db::Polygon> proc;
|
|
proc.set_base_verbosity (base_verbosity ());
|
|
proc.set_description (progress_desc ());
|
|
proc.set_report_progress (report_progress ());
|
|
|
|
std::vector<generic_shape_iterator<db::Edge> > others;
|
|
others.push_back (counting ? other.begin_merged () : other.begin ());
|
|
|
|
std::unique_ptr<FlatRegion> output (new FlatRegion (merged_semantics ()));
|
|
std::vector<db::Shapes *> results;
|
|
results.push_back (&output->raw_polygons ());
|
|
|
|
proc.run_flat (polygons, others, std::vector<bool> (), &op, oph.results ());
|
|
|
|
return oph.region_pair ();
|
|
}
|
|
|
|
std::pair<RegionDelegate *, RegionDelegate *>
|
|
AsIfFlatRegion::selected_interacting_generic (const Texts &other, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const
|
|
{
|
|
OutputPairHolder oph (output_mode, merged_semantics () || is_merged ());
|
|
|
|
if (output_mode == None) {
|
|
return oph.region_pair ();
|
|
}
|
|
|
|
min_count = std::max (size_t (1), min_count);
|
|
|
|
// shortcut
|
|
if (empty ()) {
|
|
if (output_mode == Positive || output_mode == Negative) {
|
|
return std::make_pair (clone (), (RegionDelegate *) 0);
|
|
} else {
|
|
return std::make_pair (clone (), clone ());
|
|
}
|
|
} else if (max_count < min_count || other.empty ()) {
|
|
if (output_mode == Positive) {
|
|
return std::make_pair (new EmptyRegion (), (RegionDelegate *) 0);
|
|
} else if (output_mode == Negative) {
|
|
return std::make_pair (clone (), (RegionDelegate *) 0);
|
|
} else {
|
|
return std::make_pair (new EmptyRegion (), clone ());
|
|
}
|
|
}
|
|
|
|
db::RegionIterator polygons (begin_merged ());
|
|
|
|
db::interacting_with_text_local_operation<db::Polygon, db::Text, db::Polygon> op (output_mode, min_count, max_count);
|
|
|
|
db::local_processor<db::Polygon, db::Text, db::Polygon> proc;
|
|
proc.set_base_verbosity (base_verbosity ());
|
|
proc.set_description (progress_desc ());
|
|
proc.set_report_progress (report_progress ());
|
|
|
|
std::vector<generic_shape_iterator<db::Text> > others;
|
|
others.push_back (other.begin ());
|
|
|
|
proc.run_flat (polygons, others, std::vector<bool> (), &op, oph.results ());
|
|
|
|
return oph.region_pair ();
|
|
}
|
|
|
|
|
|
std::pair<RegionDelegate *, RegionDelegate *>
|
|
AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, bool touching, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const
|
|
{
|
|
OutputPairHolder oph (output_mode, merged_semantics () || is_merged ());
|
|
|
|
if (output_mode == None) {
|
|
return oph.region_pair ();
|
|
}
|
|
|
|
min_count = std::max (size_t (1), min_count);
|
|
|
|
// shortcut
|
|
if (empty ()) {
|
|
if (output_mode == Positive || output_mode == Negative) {
|
|
return std::make_pair (clone (), (RegionDelegate *) 0);
|
|
} else {
|
|
return std::make_pair (clone (), clone ());
|
|
}
|
|
} else if (max_count < min_count || other.empty ()) {
|
|
// clear, if b is empty and
|
|
// * mode is inside, enclosing or interacting and inverse is false ("inside" or "interacting")
|
|
// * mode is outside and inverse is true ("not outside")
|
|
if ((mode <= 0)) {
|
|
if (output_mode == Positive) {
|
|
return std::make_pair (new EmptyRegion (), (RegionDelegate *) 0);
|
|
} else if (output_mode == Negative) {
|
|
return std::make_pair (clone (), (RegionDelegate *) 0);
|
|
} else {
|
|
return std::make_pair (new EmptyRegion (), clone ());
|
|
}
|
|
} else {
|
|
if (output_mode == Positive) {
|
|
return std::make_pair (clone(), (RegionDelegate *) 0);
|
|
} else if (output_mode == Negative) {
|
|
return std::make_pair (new EmptyRegion (), (RegionDelegate *) 0);
|
|
} else {
|
|
return std::make_pair (clone (), new EmptyRegion ());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool counting = !(min_count == 1 && max_count == std::numeric_limits<size_t>::max ());
|
|
|
|
db::RegionIterator polygons (begin_merged ());
|
|
|
|
db::interacting_local_operation<db::Polygon, db::Polygon, db::Polygon> op (mode, touching, output_mode, min_count, max_count, true);
|
|
|
|
db::local_processor<db::Polygon, db::Polygon, db::Polygon> proc;
|
|
proc.set_base_verbosity (base_verbosity ());
|
|
proc.set_description (progress_desc ());
|
|
proc.set_report_progress (report_progress ());
|
|
|
|
std::vector<generic_shape_iterator<db::Polygon> > others;
|
|
// NOTE: with counting the other region needs to be merged
|
|
others.push_back (counting ? other.begin_merged () : other.begin ());
|
|
|
|
proc.run_flat (polygons, others, std::vector<bool> (), &op, oph.results ());
|
|
|
|
return oph.region_pair ();
|
|
}
|
|
|
|
EdgesDelegate *
|
|
AsIfFlatRegion::pull_generic (const Edges &other) const
|
|
{
|
|
if (other.empty ()) {
|
|
return other.delegate ()->clone ();
|
|
} else if (empty ()) {
|
|
return new EmptyEdges ();
|
|
}
|
|
|
|
db::RegionIterator polygons (begin ());
|
|
|
|
db::pull_with_edge_local_operation <db::Polygon, db::Edge, db::Edge> op;
|
|
|
|
db::local_processor<db::Polygon, db::Edge, db::Edge> proc;
|
|
proc.set_base_verbosity (base_verbosity ());
|
|
proc.set_description (progress_desc ());
|
|
proc.set_report_progress (report_progress ());
|
|
|
|
std::vector<generic_shape_iterator<db::Edge> > others;
|
|
others.push_back (other.begin_merged ());
|
|
|
|
std::unique_ptr<FlatEdges> output (new FlatEdges (other.merged_semantics () || other.is_merged ()));
|
|
std::vector<db::Shapes *> results;
|
|
results.push_back (&output->raw_edges ());
|
|
|
|
proc.run_flat (polygons, others, std::vector<bool> (), &op, results);
|
|
|
|
return output.release ();
|
|
}
|
|
|
|
TextsDelegate *
|
|
AsIfFlatRegion::pull_generic (const Texts &other) const
|
|
{
|
|
db::RegionIterator polygons (begin ());
|
|
|
|
db::pull_with_text_local_operation <db::Polygon, db::Text, db::Text> op;
|
|
|
|
db::local_processor<db::Polygon, db::Text, db::Text> proc;
|
|
proc.set_base_verbosity (base_verbosity ());
|
|
proc.set_description (progress_desc ());
|
|
proc.set_report_progress (report_progress ());
|
|
|
|
std::vector<generic_shape_iterator<db::Text> > others;
|
|
others.push_back (other.begin ());
|
|
|
|
std::unique_ptr<FlatTexts> output (new FlatTexts ());
|
|
std::vector<db::Shapes *> results;
|
|
results.push_back (&output->raw_texts ());
|
|
|
|
proc.run_flat (polygons, others, std::vector<bool> (), &op, results);
|
|
|
|
return output.release ();
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::pull_generic (const Region &other, int mode, bool touching) const
|
|
{
|
|
db::RegionIterator polygons (begin ());
|
|
|
|
db::pull_local_operation <db::Polygon, db::Polygon, db::Polygon> op (mode, touching);
|
|
|
|
db::local_processor<db::Polygon, db::Polygon, db::Polygon> proc;
|
|
proc.set_base_verbosity (base_verbosity ());
|
|
proc.set_description (progress_desc ());
|
|
proc.set_report_progress (report_progress ());
|
|
|
|
std::vector<generic_shape_iterator<db::Polygon> > others;
|
|
others.push_back (other.begin_merged ());
|
|
|
|
std::unique_ptr<FlatRegion> output (new FlatRegion (other.merged_semantics () || other.is_merged ()));
|
|
std::vector<db::Shapes *> results;
|
|
results.push_back (&output->raw_polygons ());
|
|
|
|
proc.run_flat (polygons, others, std::vector<bool> (), &op, results);
|
|
|
|
return output.release ();
|
|
}
|
|
|
|
template <class Trans>
|
|
void
|
|
AsIfFlatRegion::produce_markers_for_grid_check (const db::Polygon &poly, const Trans &tr, db::Coord gx, db::Coord gy, db::Shapes &shapes)
|
|
{
|
|
Trans tr_inv = tr.inverted ();
|
|
|
|
gx = std::max (db::Coord (1), gx);
|
|
gy = std::max (db::Coord (1), gy);
|
|
|
|
for (size_t i = 0; i < poly.holes () + 1; ++i) {
|
|
|
|
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) {
|
|
db::Point p = tr * *pt;
|
|
if ((p.x () % gx) != 0 || (p.y () % gy) != 0) {
|
|
shapes.insert (EdgePair (db::Edge (p, p), db::Edge (p, p)).transformed (tr_inv));
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
template void AsIfFlatRegion::produce_markers_for_grid_check<db::ICplxTrans> (const db::Polygon &poly, const db::ICplxTrans &tr, db::Coord gx, db::Coord gy, db::Shapes &shapes);
|
|
template void AsIfFlatRegion::produce_markers_for_grid_check<db::UnitTrans> (const db::Polygon &poly, const db::UnitTrans &tr, db::Coord gx, db::Coord gy, db::Shapes &shapes);
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::grid_check (db::Coord gx, db::Coord gy) const
|
|
{
|
|
if (gx < 0 || gy < 0) {
|
|
throw tl::Exception (tl::to_string (tr ("Grid check requires a positive grid value")));
|
|
}
|
|
|
|
if (gx == 0 && gy == 0) {
|
|
return new EmptyEdgePairs ();
|
|
}
|
|
|
|
std::unique_ptr<db::FlatEdgePairs> res (new db::FlatEdgePairs ());
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
produce_markers_for_grid_check (*p, db::UnitTrans (), gx, gy, res->raw_edge_pairs ());
|
|
}
|
|
|
|
return res.release ();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class Trans>
|
|
void
|
|
AsIfFlatRegion::produce_markers_for_angle_check (const db::Polygon &poly, const Trans &tr, double min, double max, bool inverse, db::Shapes &shapes)
|
|
{
|
|
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 (size_t i = 0; i < poly.holes () + 1; ++i) {
|
|
|
|
const db::Polygon::contour_type *h = 0;
|
|
if (i == 0) {
|
|
h = &poly.hull ();
|
|
} else {
|
|
h = &poly.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]);
|
|
e.transform (tr);
|
|
db::Edge ee (e.p2 (), (*h) [(j + 2) % np]);
|
|
ee.transform (tr);
|
|
|
|
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) {
|
|
shapes.insert (EdgePair (e, ee));
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
template void AsIfFlatRegion::produce_markers_for_angle_check<db::ICplxTrans> (const db::Polygon &poly, const db::ICplxTrans &tr, double min, double max, bool inverse, db::Shapes &shapes);
|
|
template void AsIfFlatRegion::produce_markers_for_angle_check<db::UnitTrans> (const db::Polygon &poly, const db::UnitTrans &tr, double min, double max, bool inverse, db::Shapes &shapes);
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::angle_check (double min, double max, bool inverse) const
|
|
{
|
|
std::unique_ptr<db::FlatEdgePairs> res (new db::FlatEdgePairs ());
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
produce_markers_for_angle_check (*p, db::UnitTrans (), min, max, inverse, res->raw_edge_pairs ());
|
|
}
|
|
|
|
return res.release ();
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::snapped (db::Coord gx, db::Coord gy)
|
|
{
|
|
if (gx < 0 || gy < 0) {
|
|
throw tl::Exception (tl::to_string (tr ("Grid snap requires a positive grid value")));
|
|
}
|
|
|
|
std::unique_ptr<FlatRegion> new_region (new FlatRegion ());
|
|
|
|
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 ();
|
|
}
|
|
|
|
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::unique_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 ();
|
|
}
|
|
|
|
template <class TR>
|
|
static
|
|
void region_cop_with_properties_impl (AsIfFlatRegion *region, db::Shapes *output_to, db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint)
|
|
{
|
|
db::local_processor<db::PolygonWithProperties, db::PolygonWithProperties, db::object_with_properties<TR> > proc;
|
|
proc.set_base_verbosity (region->base_verbosity ());
|
|
proc.set_description (region->progress_desc ());
|
|
proc.set_report_progress (region->report_progress ());
|
|
|
|
db::generic_shape_iterator<db::PolygonWithProperties> polygons (db::make_wp_iter (region->begin_merged ()));
|
|
|
|
std::vector<generic_shape_iterator<db::PolygonWithProperties> > others;
|
|
std::vector<bool> foreign;
|
|
std::vector<db::Region *> inputs = node.inputs ();
|
|
for (std::vector<db::Region *>::const_iterator i = inputs.begin (); i != inputs.end (); ++i) {
|
|
if (*i == subject_regionptr () || *i == foreign_regionptr ()) {
|
|
others.push_back (db::make_wp_iter (region->begin_merged ()));
|
|
foreign.push_back (*i == foreign_regionptr ());
|
|
} else {
|
|
others.push_back (db::make_wp_iter ((*i)->begin ()));
|
|
foreign.push_back (false);
|
|
}
|
|
}
|
|
|
|
std::vector<db::Shapes *> results;
|
|
results.push_back (output_to);
|
|
|
|
compound_local_operation_with_properties<db::Polygon, db::Polygon, TR> op (&node, prop_constraint);
|
|
proc.run_flat (polygons, others, foreign, &op, results);
|
|
}
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::cop_to_edge_pairs (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint)
|
|
{
|
|
std::unique_ptr<FlatEdgePairs> output (new FlatEdgePairs ());
|
|
region_cop_with_properties_impl<db::EdgePair> (this, &output->raw_edge_pairs (), node, prop_constraint);
|
|
return output.release ();
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::cop_to_region (db::CompoundRegionOperationNode &node, db::PropertyConstraint prop_constraint)
|
|
{
|
|
std::unique_ptr<FlatRegion> output (new FlatRegion ());
|
|
region_cop_with_properties_impl<db::Polygon> (this, &output->raw_polygons (), node, prop_constraint);
|
|
return output.release ();
|
|
}
|
|
|
|
EdgesDelegate *
|
|
AsIfFlatRegion::cop_to_edges (db::CompoundRegionOperationNode &node, PropertyConstraint prop_constraint)
|
|
{
|
|
std::unique_ptr<FlatEdges> output (new FlatEdges ());
|
|
region_cop_with_properties_impl<db::Edge> (this, &output->raw_edges (), node, prop_constraint);
|
|
return output.release ();
|
|
}
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::width_check (db::Coord d, const RegionCheckOptions &options) const
|
|
{
|
|
return run_single_polygon_check (db::WidthRelation, d, options);
|
|
}
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::space_or_isolated_check (db::Coord d, const RegionCheckOptions &options, bool isolated) const
|
|
{
|
|
// NOTE: we have to use "foreign" to make sure every subject sees neighboring subjects
|
|
// @@@ use null ptr instead of foreign_regionptr
|
|
return run_check (db::SpaceRelation, isolated, foreign_regionptr (), d, options);
|
|
}
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::space_check (db::Coord d, const RegionCheckOptions &options) const
|
|
{
|
|
return space_or_isolated_check (d, options, false);
|
|
}
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::isolated_check (db::Coord d, const RegionCheckOptions &options) const
|
|
{
|
|
return space_or_isolated_check (d, options, true);
|
|
}
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::notch_check (db::Coord d, const RegionCheckOptions &options) const
|
|
{
|
|
return run_single_polygon_check (db::SpaceRelation, d, options);
|
|
}
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::enclosing_check (const Region &other, db::Coord d, const RegionCheckOptions &options) const
|
|
{
|
|
return run_check (db::OverlapRelation, true, &other, d, options);
|
|
}
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::overlap_check (const Region &other, db::Coord d, const RegionCheckOptions &options) const
|
|
{
|
|
return run_check (db::WidthRelation, true, &other, d, options);
|
|
}
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::separation_check (const Region &other, db::Coord d, const RegionCheckOptions &options) const
|
|
{
|
|
return run_check (db::SpaceRelation, true, &other, d, options);
|
|
}
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::inside_check (const Region &other, db::Coord d, const RegionCheckOptions &options) const
|
|
{
|
|
return run_check (db::InsideRelation, true, &other, d, options);
|
|
}
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, const RegionCheckOptions &options) const
|
|
{
|
|
bool has_other = other && other != subject_regionptr () && other != foreign_regionptr ();
|
|
|
|
// force different polygons in the different properties case to skip intra-polygon checks
|
|
if (! has_other && pc_always_different (options.prop_constraint)) {
|
|
different_polygons = true;
|
|
}
|
|
|
|
bool needs_merged_primary = (! has_other && different_polygons) || options.needs_merged ();
|
|
bool primary_is_merged = is_merged ();
|
|
db::RegionIterator polygons;
|
|
|
|
if (! merged_semantics ()) {
|
|
primary_is_merged = true; // means: don't merge again
|
|
needs_merged_primary = false;
|
|
polygons = begin ();
|
|
} else if (! needs_merged_primary) {
|
|
// The implementation may run faster if the primary is not merged
|
|
primary_is_merged = false;
|
|
polygons = begin_unmerged ();
|
|
} else {
|
|
primary_is_merged = true;
|
|
polygons = begin_merged ();
|
|
}
|
|
|
|
EdgeRelationFilter check (rel, d, options);
|
|
|
|
std::vector<db::RegionIterator> others;
|
|
std::vector<bool> foreign;
|
|
bool other_is_merged = true;
|
|
|
|
if (! has_other) {
|
|
|
|
foreign.push_back (other == foreign_regionptr ());
|
|
others.push_back (polygons);
|
|
other_is_merged = primary_is_merged;
|
|
|
|
} else {
|
|
|
|
foreign.push_back (false);
|
|
|
|
if (! other->merged_semantics ()) {
|
|
others.push_back (other->begin ());
|
|
other_is_merged = true;
|
|
} else if (options.whole_edges) {
|
|
// NOTE: whole edges needs both inputs merged
|
|
others.push_back (other->begin_merged ());
|
|
other_is_merged = true;
|
|
} else {
|
|
others.push_back (other->begin ());
|
|
other_is_merged = other->is_merged ();
|
|
}
|
|
|
|
// adds another intruder section to implement subject merging ("primary_intruders")
|
|
if (! primary_is_merged) {
|
|
foreign.push_back (true);
|
|
others.push_back (polygons);
|
|
}
|
|
|
|
}
|
|
|
|
std::unique_ptr<FlatEdgePairs> output (new FlatEdgePairs ());
|
|
|
|
std::vector<db::Shapes *> results;
|
|
results.push_back (&output->raw_edge_pairs ());
|
|
|
|
if (pc_skip (options.prop_constraint)) {
|
|
|
|
db::check_local_operation<db::Polygon, db::Polygon> op (check, different_polygons, primary_is_merged, has_other, other_is_merged, options);
|
|
|
|
db::local_processor<db::Polygon, db::Polygon, db::EdgePair> proc;
|
|
proc.set_base_verbosity (base_verbosity ());
|
|
proc.set_description (progress_desc ());
|
|
proc.set_report_progress (report_progress ());
|
|
|
|
proc.run_flat (polygons, others, foreign, &op, results);
|
|
|
|
} else {
|
|
|
|
db::check_local_operation_with_properties<db::Polygon, db::Polygon> op (check, different_polygons, primary_is_merged, has_other, other_is_merged, options);
|
|
|
|
db::local_processor<db::PolygonWithProperties, db::PolygonWithProperties, db::EdgePairWithProperties> proc;
|
|
proc.set_base_verbosity (base_verbosity ());
|
|
proc.set_description (progress_desc ());
|
|
proc.set_report_progress (report_progress ());
|
|
|
|
std::vector<db::generic_shape_iterator<db::PolygonWithProperties> > others_wp;
|
|
for (auto o = others.begin (); o != others.end (); ++o) {
|
|
others_wp.push_back (db::make_wp_iter (std::move (*o)));
|
|
}
|
|
|
|
proc.run_flat (db::make_wp_iter (std::move (polygons)), others_wp, foreign, &op, results);
|
|
|
|
}
|
|
|
|
return output.release ();
|
|
}
|
|
|
|
EdgePairsDelegate *
|
|
AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, const RegionCheckOptions &options) const
|
|
{
|
|
std::unique_ptr<FlatEdgePairs> result (new FlatEdgePairs ());
|
|
|
|
EdgeRelationFilter check (rel, d, options);
|
|
|
|
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
|
|
|
edge2edge_check_negative_or_positive<db::Shapes> edge_check (check, result->raw_edge_pairs (), options.negative, false /*=same polygons*/, false /*=same layers*/, options.shielded, true /*symmetric edge pairs*/, pc_remove (options.prop_constraint) ? 0 : p.prop_id ());
|
|
poly2poly_check<db::Polygon> poly_check (edge_check);
|
|
|
|
do {
|
|
poly_check.single (*p, 0);
|
|
} while (edge_check.prepare_next_pass ());
|
|
|
|
}
|
|
|
|
return result.release ();
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) 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 {
|
|
|
|
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true));
|
|
merge_polygons_to (new_region->raw_polygons (), min_coherence, min_wc, join_properties_on_merge);
|
|
|
|
return new_region.release ();
|
|
|
|
}
|
|
}
|
|
|
|
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, db::RegionIterator (begin ()).prop_id ());
|
|
|
|
} else if (! merged_semantics () || is_merged ()) {
|
|
|
|
// Generic case
|
|
std::unique_ptr<FlatRegion> new_region (new FlatRegion ());
|
|
|
|
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) {
|
|
pc.set_prop_id (p.prop_id ());
|
|
sf.put (*p);
|
|
}
|
|
|
|
// in case of negative sizing the output polygons will still be merged (on positive sizing they might
|
|
// overlap after size and are not necessarily merged)
|
|
if (dx < 0 && dy < 0 && is_merged ()) {
|
|
new_region->set_is_merged (true);
|
|
}
|
|
|
|
return new_region.release ();
|
|
|
|
} else {
|
|
|
|
std::unique_ptr<FlatRegion> new_region (new FlatRegion ());
|
|
|
|
// old implementation without property support
|
|
#if 0
|
|
// 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);
|
|
}
|
|
|
|
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*/, min_coherence () /*min. coherence*/);
|
|
db::BooleanOp op (db::BooleanOp::Or);
|
|
ep.process (pg, op);
|
|
#else
|
|
|
|
// Generic case
|
|
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_merged ()); ! p.at_end (); ++p) {
|
|
pc.set_prop_id (p.prop_id ());
|
|
sf.put (*p);
|
|
}
|
|
|
|
#endif
|
|
|
|
// in case of negative sizing the output polygons will still be merged (on positive sizing they might
|
|
// overlap after size and are not necessarily merged)
|
|
if (dx < 0 && dy < 0 && merged_semantics ()) {
|
|
new_region->set_is_merged (true);
|
|
}
|
|
|
|
return new_region.release ();
|
|
|
|
}
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const
|
|
{
|
|
return sized_inside (inside, outside, d, d, steps, mode);
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const
|
|
{
|
|
// empirical value
|
|
const int max_steps = 25;
|
|
|
|
if (steps <= 0 || empty ()) {
|
|
// Nothing to do - NOTE: don't return EmptyRegion because we want to
|
|
// maintain "deepness"
|
|
return clone ();
|
|
}
|
|
|
|
if (dx < 0 || dy < 0) {
|
|
throw tl::Exception (tl::to_string (tr ("'sized_inside' operation does not make sense with negative sizing")));
|
|
}
|
|
|
|
if (dx == 0 && dy == 0) {
|
|
steps = 1;
|
|
}
|
|
|
|
// NOTE: it does not provide benefits to merge the outside region, so just don't
|
|
auto inside_polygons = outside ? inside.begin () : inside.begin_merged ();
|
|
bool inside_polygons_is_merged = outside ? inside.is_merged () : true;
|
|
|
|
auto polygons = begin_merged ();
|
|
|
|
std::unique_ptr<RegionDelegate> res (new FlatRegion ());
|
|
std::unique_ptr<RegionDelegate> prev;
|
|
|
|
int steps_from = 0;
|
|
|
|
while (steps > 0) {
|
|
|
|
db::Coord dx_chunk = dx, dy_chunk = dy;
|
|
int steps_chunk = steps;
|
|
|
|
// We perform at most max_steps in one chunk.
|
|
// This is supposed to limit the search range and merge shapes instead of creating
|
|
// heavily overlapping ones.
|
|
if (steps > max_steps) {
|
|
steps_chunk = max_steps;
|
|
dx_chunk = db::coord_traits<db::Coord>::rounded (dx * max_steps / double (steps));
|
|
dy_chunk = db::coord_traits<db::Coord>::rounded (dy * max_steps / double (steps));
|
|
}
|
|
|
|
steps -= steps_chunk;
|
|
dx -= dx_chunk;
|
|
dy -= dy_chunk;
|
|
|
|
// NOTE: as we merge the inside region in the inside case, we can use distance 0
|
|
db::Coord dist = outside ? std::max (dx_chunk, dy_chunk) : 0;
|
|
db::sized_inside_local_operation<db::Polygon, db::Polygon, db::Polygon> op (dx_chunk, dy_chunk, steps_chunk, mode, dist, outside, inside_polygons_is_merged);
|
|
|
|
db::local_processor<db::Polygon, db::Polygon, db::Polygon> proc;
|
|
proc.set_base_verbosity (base_verbosity ());
|
|
proc.set_description (progress_desc ());
|
|
proc.set_report_progress (report_progress ());
|
|
|
|
// indicate chunk in the progress description
|
|
proc.set_description (proc.description (&op) + tl::sprintf (tl::to_string (tr (" (steps %d..%d)")), steps_from + 1, steps_from + steps_chunk + 1));
|
|
steps_from += steps_chunk;
|
|
|
|
std::vector<db::generic_shape_iterator<db::Polygon> > others;
|
|
others.push_back (inside_polygons);
|
|
|
|
std::vector<db::Shapes *> results;
|
|
db::FlatRegion *res_flat = dynamic_cast<db::FlatRegion *> (res.get ());
|
|
tl_assert (res_flat != 0);
|
|
results.push_back (&res_flat->raw_polygons ());
|
|
proc.run_flat (prev.get () ? prev->begin () : polygons, others, std::vector<bool> (), &op, results);
|
|
|
|
// NOTE: in the last step we apply a polygon breaker in addition to "merge" so the
|
|
// result is granular for better deep mode performance
|
|
if (steps > 0) {
|
|
prev.reset (res->merged ());
|
|
res.reset (new db::FlatRegion ());
|
|
} else {
|
|
res.reset (res->processed (db::PolygonBreaker (proc.max_vertex_count (), proc.area_ratio ())));
|
|
}
|
|
|
|
}
|
|
|
|
return res.release ();
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::and_with (const Region &other, PropertyConstraint property_constraint) const
|
|
{
|
|
if (empty () || other.empty ()) {
|
|
|
|
// Nothing to do
|
|
return new EmptyRegion ();
|
|
|
|
} else if (is_box () && other.is_box ()) {
|
|
|
|
if (pc_skip (property_constraint) || pc_match (property_constraint, db::RegionIterator (begin ()).prop_id (), other.begin ().prop_id ())) {
|
|
|
|
// Simplified handling for boxes
|
|
db::Box b = bbox ();
|
|
b &= other.bbox ();
|
|
|
|
db::properties_id_type prop_id_out = pc_norm (property_constraint, db::RegionIterator (begin ()).prop_id ());
|
|
|
|
return region_from_box (b, prop_id_out);
|
|
|
|
} else {
|
|
return new EmptyRegion ();
|
|
}
|
|
|
|
} else if (is_box () && ! other.strict_handling ()) {
|
|
|
|
db::properties_id_type self_prop_id = pc_skip (property_constraint) ? 0 : db::RegionIterator (begin ()).prop_id ();
|
|
|
|
// map AND with box to clip ..
|
|
db::Box b = bbox ();
|
|
std::unique_ptr<FlatRegion> new_region (new FlatRegion (false));
|
|
|
|
db::properties_id_type prop_id_out = pc_norm (property_constraint, self_prop_id);
|
|
|
|
std::vector<db::Polygon> clipped;
|
|
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) {
|
|
|
|
db::properties_id_type prop_id = p.prop_id ();
|
|
if (pc_match (property_constraint, self_prop_id, prop_id)) {
|
|
|
|
clipped.clear ();
|
|
clip_poly (*p, b, clipped);
|
|
|
|
if (prop_id_out == 0) {
|
|
new_region->raw_polygons ().insert (clipped.begin (), clipped.end ());
|
|
} else {
|
|
for (auto i = clipped.begin (); i != clipped.end (); ++i) {
|
|
new_region->raw_polygons ().insert (db::PolygonWithProperties (*i, prop_id_out));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new_region.release ();
|
|
|
|
} else if (other.is_box () && ! strict_handling ()) {
|
|
|
|
db::properties_id_type other_prop_id = pc_skip (property_constraint) ? 0 : other.begin ().prop_id ();
|
|
|
|
// map AND with box to clip ..
|
|
db::Box b = other.bbox ();
|
|
std::unique_ptr<FlatRegion> new_region (new FlatRegion (false));
|
|
|
|
std::vector<db::Polygon> clipped;
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
|
|
db::properties_id_type prop_id = p.prop_id ();
|
|
if (pc_match (property_constraint, prop_id, other_prop_id)) {
|
|
|
|
clipped.clear ();
|
|
clip_poly (*p, b, clipped);
|
|
|
|
db::properties_id_type prop_id_out = pc_norm (property_constraint, prop_id);
|
|
if (prop_id_out == 0) {
|
|
new_region->raw_polygons ().insert (clipped.begin (), clipped.end ());
|
|
} else {
|
|
for (auto i = clipped.begin (); i != clipped.end (); ++i) {
|
|
new_region->raw_polygons ().insert (db::PolygonWithProperties (*i, prop_id_out));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new_region.release ();
|
|
|
|
} else if (! bbox ().overlaps (other.bbox ())) {
|
|
|
|
// Result will be nothing
|
|
return new EmptyRegion ();
|
|
|
|
} else {
|
|
return and_or_not_with (true, other, property_constraint);
|
|
}
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::not_with (const Region &other, PropertyConstraint property_constraint) const
|
|
{
|
|
if (empty ()) {
|
|
|
|
// Nothing to do
|
|
return new EmptyRegion ();
|
|
|
|
} else if (other.empty () && ! strict_handling ()) {
|
|
|
|
// Nothing to do
|
|
return clone ()->remove_properties (pc_remove (property_constraint));
|
|
|
|
} else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling ()) {
|
|
|
|
// Nothing to do
|
|
return clone ()->remove_properties (pc_remove (property_constraint));
|
|
|
|
} else {
|
|
return and_or_not_with (false, other, property_constraint);
|
|
}
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::and_or_not_with (bool is_and, const Region &other, PropertyConstraint property_constraint) const
|
|
{
|
|
if (pc_skip (property_constraint)) {
|
|
|
|
// 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::unique_ptr<FlatRegion> new_region (new FlatRegion (true));
|
|
db::BooleanOp op (is_and ? db::BooleanOp::And : 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 ();
|
|
|
|
} else {
|
|
|
|
db::generic_shape_iterator<db::PolygonWithProperties> polygons (db::make_wp_iter (begin ()));
|
|
|
|
std::unique_ptr<FlatRegion> output (new FlatRegion ());
|
|
std::vector<db::Shapes *> results;
|
|
results.push_back (&output->raw_polygons ());
|
|
|
|
db::bool_and_or_not_local_operation_with_properties<db::Polygon, db::Polygon, db::Polygon> op (is_and, property_constraint);
|
|
|
|
db::local_processor<db::PolygonWithProperties, db::PolygonWithProperties, db::PolygonWithProperties> proc;
|
|
proc.set_base_verbosity (base_verbosity ());
|
|
proc.set_description (progress_desc ());
|
|
proc.set_report_progress (report_progress ());
|
|
|
|
std::vector<db::generic_shape_iterator<db::PolygonWithProperties> > others;
|
|
others.push_back (db::make_wp_iter (other.begin ()));
|
|
|
|
proc.run_flat (polygons, others, std::vector<bool> (), &op, results);
|
|
|
|
return output.release ();
|
|
|
|
}
|
|
}
|
|
|
|
std::pair<RegionDelegate *, RegionDelegate *>
|
|
AsIfFlatRegion::andnot_with (const Region &other, PropertyConstraint property_constraint) const
|
|
{
|
|
if (empty ()) {
|
|
|
|
// Nothing to do
|
|
return std::make_pair (new EmptyRegion (), new EmptyRegion ());
|
|
|
|
} else if (other.empty () && ! strict_handling ()) {
|
|
|
|
// Nothing to do
|
|
return std::make_pair (new EmptyRegion (), clone ()->remove_properties (pc_remove (property_constraint)));
|
|
|
|
} else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling ()) {
|
|
|
|
// Nothing to do
|
|
return std::make_pair (new EmptyRegion (), clone ()->remove_properties (pc_remove (property_constraint)));
|
|
|
|
} else if (pc_skip (property_constraint)) {
|
|
|
|
// 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::unique_ptr<FlatRegion> new_region1 (new FlatRegion (true));
|
|
db::BooleanOp op1 (db::BooleanOp::And);
|
|
db::ShapeGenerator pc1 (new_region1->raw_polygons (), true /*clear*/);
|
|
db::PolygonGenerator pg1 (pc1, false /*don't resolve holes*/, min_coherence ());
|
|
|
|
std::unique_ptr<FlatRegion> new_region2 (new FlatRegion (true));
|
|
db::BooleanOp op2 (db::BooleanOp::ANotB);
|
|
db::ShapeGenerator pc2 (new_region2->raw_polygons (), true /*clear*/);
|
|
db::PolygonGenerator pg2 (pc2, false /*don't resolve holes*/, min_coherence ());
|
|
|
|
std::vector<std::pair<db::EdgeSink *, db::EdgeEvaluatorBase *> > procs;
|
|
procs.push_back (std::make_pair (&pg1, &op1));
|
|
procs.push_back (std::make_pair (&pg2, &op2));
|
|
ep.process (procs);
|
|
|
|
return std::make_pair (new_region1.release (), new_region2.release ());
|
|
|
|
} else {
|
|
|
|
db::generic_shape_iterator<db::PolygonWithProperties> polygons (db::make_wp_iter (begin ()));
|
|
|
|
std::unique_ptr<FlatRegion> output1 (new FlatRegion ());
|
|
std::unique_ptr<FlatRegion> output2 (new FlatRegion ());
|
|
std::vector<db::Shapes *> results;
|
|
results.push_back (&output1->raw_polygons ());
|
|
results.push_back (&output2->raw_polygons ());
|
|
|
|
db::two_bool_and_not_local_operation_with_properties<db::Polygon, db::Polygon, db::Polygon> op (property_constraint);
|
|
|
|
db::local_processor<db::PolygonWithProperties, db::PolygonWithProperties, db::PolygonWithProperties> proc;
|
|
proc.set_base_verbosity (base_verbosity ());
|
|
proc.set_description (progress_desc ());
|
|
proc.set_report_progress (report_progress ());
|
|
|
|
std::vector<db::generic_shape_iterator<db::PolygonWithProperties> > others;
|
|
others.push_back (db::make_wp_iter (other.begin ()));
|
|
|
|
proc.run_flat (polygons, others, std::vector<bool> (), &op, results);
|
|
|
|
return std::make_pair (output1.release (), output2.release ());
|
|
|
|
}
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::xor_with (const Region &other, PropertyConstraint prop_constraint) 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, prop_constraint);
|
|
|
|
} else {
|
|
|
|
// TODO: implement property constraint
|
|
|
|
// 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::unique_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, PropertyConstraint /*prop_constraint*/) 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 {
|
|
|
|
// TODO: implement property constraint
|
|
|
|
// 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::unique_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
|
|
{
|
|
const FlatRegion *other_flat = dynamic_cast<const FlatRegion *> (other.delegate ());
|
|
if (other_flat) {
|
|
|
|
std::unique_ptr<FlatRegion> new_region (new FlatRegion (*other_flat));
|
|
new_region->set_is_merged (false);
|
|
new_region->invalidate_cache ();
|
|
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
if (p.prop_id () == 0) {
|
|
new_region->raw_polygons ().insert (*p);
|
|
} else {
|
|
new_region->raw_polygons ().insert (db::PolygonWithProperties (*p, p.prop_id ()));
|
|
}
|
|
}
|
|
|
|
return new_region.release ();
|
|
|
|
} else {
|
|
|
|
std::unique_ptr<FlatRegion> new_region (new FlatRegion (false /*not merged*/));
|
|
|
|
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
|
if (p.prop_id () == 0) {
|
|
new_region->raw_polygons ().insert (*p);
|
|
} else {
|
|
new_region->raw_polygons ().insert (db::PolygonWithProperties (*p, p.prop_id ()));
|
|
}
|
|
}
|
|
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) {
|
|
if (p.prop_id () == 0) {
|
|
new_region->raw_polygons ().insert (*p);
|
|
} else {
|
|
new_region->raw_polygons ().insert (db::PolygonWithProperties (*p, p.prop_id ()));
|
|
}
|
|
}
|
|
|
|
return new_region.release ();
|
|
|
|
}
|
|
}
|
|
|
|
static void
|
|
deliver_shapes_of_nets_recursive (db::Shapes &out, const db::Circuit *circuit, const LayoutToNetlist *l2n, unsigned int lid, NetPropertyMode prop_mode, const tl::Variant &net_prop_name, const db::ICplxTrans &tr, const std::set<const db::Net *> *net_filter)
|
|
{
|
|
db::CplxTrans dbu_trans (l2n->internal_layout ()->dbu ());
|
|
auto dbu_trans_inv = dbu_trans.inverted ();
|
|
|
|
for (auto n = circuit->begin_nets (); n != circuit->end_nets (); ++n) {
|
|
|
|
if (! net_filter || net_filter->find (n.operator-> ()) != net_filter->end ()) {
|
|
db::properties_id_type prop_id = db::NetBuilder::make_netname_propid (prop_mode, net_prop_name, *n);
|
|
l2n->shapes_of_net (*n, lid, true, out, prop_id, tr);
|
|
}
|
|
|
|
// dive into subcircuits
|
|
for (auto sc = circuit->begin_subcircuits (); sc != circuit->end_subcircuits (); ++sc) {
|
|
const db::Circuit *circuit_ref = sc->circuit_ref ();
|
|
db::ICplxTrans tr_ref = tr * (dbu_trans_inv * sc->trans () * dbu_trans);
|
|
deliver_shapes_of_nets_recursive (out, circuit_ref, l2n, lid, prop_mode, net_prop_name, tr_ref, net_filter);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
RegionDelegate *
|
|
AsIfFlatRegion::nets (LayoutToNetlist *l2n, NetPropertyMode prop_mode, const tl::Variant &net_prop_name, const std::vector<const db::Net *> *net_filter) const
|
|
{
|
|
if (! l2n->is_netlist_extracted ()) {
|
|
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
|
|
}
|
|
|
|
std::unique_ptr<db::FlatRegion> result (new db::FlatRegion ());
|
|
tl::optional<unsigned int> li = l2n->layer_by_original (this);
|
|
|
|
if (! li.has_value ()) {
|
|
throw tl::Exception (tl::to_string (tr ("The given layer is not an original layer used in netlist extraction")));
|
|
}
|
|
|
|
if (l2n->netlist ()->top_circuit_count () == 0) {
|
|
throw tl::Exception (tl::to_string (tr ("No top circuit found in netlist")));
|
|
} else if (l2n->netlist ()->top_circuit_count () > 1) {
|
|
throw tl::Exception (tl::to_string (tr ("More than one top circuit found in netlist")));
|
|
}
|
|
const db::Circuit *top_circuit = l2n->netlist ()->begin_top_down ().operator-> ();
|
|
|
|
std::set<const db::Net *> net_filter_set;
|
|
if (net_filter) {
|
|
net_filter_set.insert (net_filter->begin (), net_filter->end ());
|
|
}
|
|
|
|
deliver_shapes_of_nets_recursive (result->raw_polygons (), top_circuit, l2n, li.value (), prop_mode, net_prop_name, db::ICplxTrans (), net_filter ? &net_filter_set : 0);
|
|
|
|
return result.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) {
|
|
db::properties_id_type prop_id = p.prop_id ();
|
|
if (prop_id != 0) {
|
|
shapes.insert (db::PolygonWithProperties (*p, prop_id));
|
|
} else {
|
|
shapes.insert (*p);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
AsIfFlatRegion::equals (const Region &other) const
|
|
{
|
|
if (empty () != other.empty ()) {
|
|
return false;
|
|
}
|
|
if (count () != other.count ()) {
|
|
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 (count () != other.count ()) {
|
|
return (count () < other.count ());
|
|
}
|
|
RegionIterator o1 (begin ());
|
|
RegionIterator o2 (other.begin ());
|
|
while (! o1.at_end () && ! o2.at_end ()) {
|
|
if (*o1 != *o2) {
|
|
return *o1 < *o2;
|
|
}
|
|
++o1;
|
|
++o2;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|