mirror of https://github.com/KLayout/klayout.git
WIP (code reduction and performance enhancement of basic DRC checks)
This commit is contained in:
parent
a0367c1530
commit
9fa82f01d8
|
|
@ -72,6 +72,7 @@ SOURCES = \
|
|||
dbRecursiveInstanceIterator.cc \
|
||||
dbRecursiveShapeIterator.cc \
|
||||
dbRegion.cc \
|
||||
dbRegionCheckUtils.cc \
|
||||
dbRegionLocalOperations.cc \
|
||||
dbSaveLayoutOptions.cc \
|
||||
dbShape.cc \
|
||||
|
|
@ -290,6 +291,7 @@ HEADERS = \
|
|||
dbRecursiveInstanceIterator.h \
|
||||
dbRecursiveShapeIterator.h \
|
||||
dbRegion.h \
|
||||
dbRegionCheckUtils.h \
|
||||
dbRegionLocalOperations.h \
|
||||
dbSaveLayoutOptions.h \
|
||||
dbShape.h \
|
||||
|
|
|
|||
|
|
@ -1150,7 +1150,7 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord
|
|||
|
||||
size_t n = 0;
|
||||
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
||||
poly_check.enter (*p, n);
|
||||
poly_check.single (*p, n);
|
||||
n += 2;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
|
||||
#include "dbCellVariants.h"
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "tlUtils.h"
|
||||
|
||||
namespace db
|
||||
|
|
|
|||
|
|
@ -1725,7 +1725,7 @@ DeepRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, c
|
|||
s->polygon (poly);
|
||||
|
||||
do {
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (edge_check.prepare_next_pass ());
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "dbLayoutUtils.h"
|
||||
#include "dbCellVariants.h"
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "tlProgress.h"
|
||||
|
||||
namespace db
|
||||
|
|
|
|||
|
|
@ -2827,5 +2827,93 @@ decompose_trapezoids (const db::SimplePolygon &sp, TrapezoidDecompositionMode mo
|
|||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Polygon snapping
|
||||
|
||||
db::Polygon
|
||||
snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap)
|
||||
{
|
||||
db::Polygon pnew;
|
||||
|
||||
for (size_t i = 0; i < poly.holes () + 1; ++i) {
|
||||
|
||||
heap.clear ();
|
||||
|
||||
db::Polygon::polygon_contour_iterator b, e;
|
||||
|
||||
if (i == 0) {
|
||||
b = poly.begin_hull ();
|
||||
e = poly.end_hull ();
|
||||
} else {
|
||||
b = poly.begin_hole ((unsigned int) (i - 1));
|
||||
e = poly.end_hole ((unsigned int) (i - 1));
|
||||
}
|
||||
|
||||
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
|
||||
heap.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy)));
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
pnew.assign_hull (heap.begin (), heap.end ());
|
||||
} else {
|
||||
pnew.insert_hole (heap.begin (), heap.end ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return pnew;
|
||||
}
|
||||
|
||||
db::Polygon
|
||||
scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap)
|
||||
{
|
||||
db::Polygon pnew;
|
||||
|
||||
int64_t dgx = int64_t (gx) * int64_t (dx);
|
||||
int64_t dgy = int64_t (gy) * int64_t (dy);
|
||||
|
||||
for (size_t i = 0; i < poly.holes () + 1; ++i) {
|
||||
|
||||
heap.clear ();
|
||||
|
||||
db::Polygon::polygon_contour_iterator b, e;
|
||||
|
||||
if (i == 0) {
|
||||
b = poly.begin_hull ();
|
||||
e = poly.end_hull ();
|
||||
} else {
|
||||
b = poly.begin_hole ((unsigned int) (i - 1));
|
||||
e = poly.end_hole ((unsigned int) (i - 1));
|
||||
}
|
||||
|
||||
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
|
||||
int64_t x = snap_to_grid (int64_t ((*pt).x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
|
||||
int64_t y = snap_to_grid (int64_t ((*pt).y ()) * my + int64_t (oy), dgy) / int64_t (dy);
|
||||
heap.push_back (db::Point (db::Coord (x), db::Coord (y)));
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
pnew.assign_hull (heap.begin (), heap.end ());
|
||||
} else {
|
||||
pnew.insert_hole (heap.begin (), heap.end ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return pnew;
|
||||
}
|
||||
|
||||
db::Vector
|
||||
scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy)
|
||||
{
|
||||
int64_t dgx = int64_t (gx) * int64_t (dx);
|
||||
int64_t dgy = int64_t (gy) * int64_t (dy);
|
||||
|
||||
int64_t x = snap_to_grid (int64_t (v.x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
|
||||
int64_t y = snap_to_grid (int64_t (v.y ()) * my + int64_t (oy), dgy) / int64_t (dy);
|
||||
|
||||
return db::Vector (db::Coord (x), db::Coord (y));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -760,6 +760,37 @@ void DB_PUBLIC decompose_trapezoids (const db::Polygon &p, TrapezoidDecompositio
|
|||
*/
|
||||
void DB_PUBLIC decompose_trapezoids (const db::SimplePolygon &p, TrapezoidDecompositionMode mode, SimplePolygonSink &sink);
|
||||
|
||||
template <class C>
|
||||
static inline C snap_to_grid (C c, C g)
|
||||
{
|
||||
// This form of snapping always snaps g/2 to right/top.
|
||||
if (c < 0) {
|
||||
c = -g * ((-c + (g - 1) / 2) / g);
|
||||
} else {
|
||||
c = g * ((c + g / 2) / g);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Snaps a polygon to the given grid
|
||||
* Heap is a vector of points reused for the point list
|
||||
*/
|
||||
DB_PUBLIC db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap);
|
||||
|
||||
/**
|
||||
* @brief Scales and snaps a polygon to the given grid
|
||||
* Heap is a vector of points reused for the point list
|
||||
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
|
||||
*/
|
||||
DB_PUBLIC db::Polygon scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap);
|
||||
|
||||
/**
|
||||
* @brief Scales and snaps a vector to the given grid
|
||||
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
|
||||
*/
|
||||
DB_PUBLIC db::Vector scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,601 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2021 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 "dbRegionCheckUtils.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "dbEdgeBoolean.h"
|
||||
#include "tlSelect.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Edge2EdgeCheckBase implementation
|
||||
|
||||
Edge2EdgeCheckBase::Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: mp_check (&check), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons),
|
||||
m_first_pseudo (std::numeric_limits<size_t>::max ()),
|
||||
m_with_shielding (with_shielding),
|
||||
m_symmetric_edges (symmetric_edges),
|
||||
m_has_edge_pair_output (true),
|
||||
m_has_negative_edge_output (false),
|
||||
m_pass (0)
|
||||
{
|
||||
m_distance = check.distance ();
|
||||
}
|
||||
|
||||
bool
|
||||
Edge2EdgeCheckBase::prepare_next_pass ()
|
||||
{
|
||||
++m_pass;
|
||||
|
||||
if (m_pass == 1) {
|
||||
|
||||
m_first_pseudo = m_ep.size ();
|
||||
|
||||
if (m_with_shielding && ! m_ep.empty ()) {
|
||||
|
||||
m_ep_discarded.resize (m_ep.size (), false);
|
||||
|
||||
// second pass:
|
||||
return true;
|
||||
|
||||
} else if (m_has_negative_edge_output) {
|
||||
|
||||
// second pass:
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! m_ep.empty () && m_has_edge_pair_output) {
|
||||
|
||||
std::vector<bool>::const_iterator d = m_ep_discarded.begin ();
|
||||
std::vector<bool>::const_iterator i = m_ep_intra_polygon.begin ();
|
||||
std::vector<db::EdgePair>::const_iterator ep = m_ep.begin ();
|
||||
while (ep != m_ep.end () && size_t (ep - m_ep.begin ()) < m_first_pseudo) {
|
||||
bool use_result = true;
|
||||
if (d != m_ep_discarded.end ()) {
|
||||
use_result = ! *d;
|
||||
++d;
|
||||
}
|
||||
if (use_result) {
|
||||
put (*ep, *i);
|
||||
}
|
||||
++ep;
|
||||
++i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool shields (const db::EdgePair &ep, const db::Edge &q)
|
||||
{
|
||||
db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ());
|
||||
db::Edge pe2 (ep.second ().p1 (), ep.first ().p2 ());
|
||||
|
||||
std::pair<bool, db::Point> ip1 = pe1.intersect_point (q);
|
||||
std::pair<bool, db::Point> ip2 = pe2.intersect_point (q);
|
||||
|
||||
if (ip1.first && ip2.first) {
|
||||
return ip1.second != ip2.second || (pe1.side_of (q.p1 ()) != 0 && pe2.side_of (q.p2 ()) != 0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Edge2EdgeCheckBase::finish (const Edge *o, size_t p)
|
||||
{
|
||||
if (m_has_negative_edge_output && m_pass == 1 && m_pseudo_edges.find (std::make_pair (*o, p)) == m_pseudo_edges.end ()) {
|
||||
|
||||
std::pair<db::Edge, size_t> k (*o, p);
|
||||
std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i0 = m_e2ep.find (k);
|
||||
|
||||
bool fully_removed = false;
|
||||
bool any = false;
|
||||
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; ! fully_removed && i != m_e2ep.end () && i->first == k; ++i) {
|
||||
size_t n = i->second / 2;
|
||||
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
|
||||
any = true;
|
||||
fully_removed = (((i->second & 1) == 0 ? m_ep [n].first () : m_ep [n].second ()) == *o);
|
||||
}
|
||||
}
|
||||
|
||||
if (! any) {
|
||||
|
||||
put_negative (*o, (int) p);
|
||||
|
||||
} else if (! fully_removed) {
|
||||
|
||||
std::set<db::Edge> partial_edges;
|
||||
|
||||
db::EdgeBooleanCluster<std::set<db::Edge> > ec (&partial_edges, db::EdgeNot);
|
||||
ec.add (o, 0);
|
||||
|
||||
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) {
|
||||
size_t n = i->second / 2;
|
||||
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
|
||||
ec.add (((i->second & 1) == 0 ? &m_ep [n].first () : &m_ep [n].second ()), 1);
|
||||
}
|
||||
}
|
||||
|
||||
ec.finish ();
|
||||
|
||||
for (std::set<db::Edge>::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) {
|
||||
put_negative (*e, (int) p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner)
|
||||
{
|
||||
if (m_pass == 1) {
|
||||
for (std::set<std::pair<db::Edge, size_t> >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) {
|
||||
scanner.insert (&e->first, e->second);
|
||||
}
|
||||
return ! m_pseudo_edges.empty ();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool edges_considered (bool requires_different_polygons, bool requires_different_layers, size_t p1, size_t p2)
|
||||
{
|
||||
if (p1 == p2) {
|
||||
if (requires_different_polygons) {
|
||||
return false;
|
||||
} else if ((p1 & size_t (1)) != 0) {
|
||||
// edges from the same polygon are only considered on first layer.
|
||||
// Reasoning: this case happens when "intruder" polygons are put on layer 1
|
||||
// while "subject" polygons are put on layer 0. We don't want "intruders"
|
||||
// to generate intra-polygon markers.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (((p1 ^ p2) & size_t (1)) == 0) {
|
||||
if (requires_different_layers) {
|
||||
return false;
|
||||
} else if ((p1 & size_t (1)) != 0) {
|
||||
// edges on the same layer are only considered on first layer.
|
||||
// Reasoning: this case happens when "intruder" polygons are put on layer 1
|
||||
// while "subject" polygons are put on layer 0. We don't want "intruders"
|
||||
// to generate inter-polygon markers between them.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
|
||||
{
|
||||
if (m_pass == 0) {
|
||||
|
||||
// Overlap or inside checks require input from different layers
|
||||
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
|
||||
|
||||
// ensure that the first check argument is of layer 1 and the second of
|
||||
// layer 2 (unless both are of the same layer)
|
||||
int l1 = int (p1 & size_t (1));
|
||||
int l2 = int (p2 & size_t (1));
|
||||
|
||||
if (l1 > l2) {
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
}
|
||||
|
||||
db::EdgePair ep;
|
||||
if (mp_check->check (*o1, *o2, &ep)) {
|
||||
|
||||
ep.set_symmetric (m_symmetric_edges);
|
||||
|
||||
// found a violation: store inside the local buffer for now. In the second
|
||||
// pass we will eliminate those which are shielded completely (with shielding)
|
||||
// and/or compute the negative edges.
|
||||
size_t n = m_ep.size ();
|
||||
|
||||
m_ep.push_back (ep);
|
||||
m_ep_intra_polygon.push_back (p1 == p2);
|
||||
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
|
||||
|
||||
if (m_has_negative_edge_output) {
|
||||
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p1));
|
||||
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p1));
|
||||
if (p1 != p2) {
|
||||
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p2));
|
||||
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p2));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// set the discarded flags for shielded output
|
||||
if (m_with_shielding) {
|
||||
|
||||
// a simple (complete) shielding implementation which is based on the
|
||||
// assumption that shielding is relevant as soon as a foreign edge cuts through
|
||||
// both of the edge pair's connecting edges.
|
||||
|
||||
// TODO: this implementation does not take into account the nature of the
|
||||
// EdgePair - because of "whole_edge" it may not reflect the part actually
|
||||
// violating the distance.
|
||||
|
||||
std::vector<size_t> n1, n2;
|
||||
|
||||
for (unsigned int p = 0; p < 2; ++p) {
|
||||
|
||||
std::pair<db::Edge, size_t> k (*o1, p1);
|
||||
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) {
|
||||
size_t n = i->second / 2;
|
||||
if (n < m_first_pseudo && ! m_ep_discarded [n]) {
|
||||
n1.push_back (n);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort (n1.begin (), n1.end ());
|
||||
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
n1.swap (n2);
|
||||
|
||||
}
|
||||
|
||||
for (unsigned int p = 0; p < 2; ++p) {
|
||||
|
||||
std::vector<size_t> nn;
|
||||
std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn));
|
||||
|
||||
for (std::vector<size_t>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
|
||||
db::EdgePair ep = m_ep [*i].normalized ();
|
||||
if (shields (ep, *o2)) {
|
||||
m_ep_discarded [*i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
n1.swap (n2);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// for negative output edges are cancelled by short interactions perpendicular to them
|
||||
// For this we have generated "pseudo edges" running along the sides of the original violation. We now check a real
|
||||
// edge vs. a pseudo edge with the same conditions as the normal interaction and add them to the results. In the
|
||||
// negative case this means we cancel a real edge.
|
||||
|
||||
if (m_has_negative_edge_output &&
|
||||
(m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end ()) != (m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ())) {
|
||||
|
||||
// Overlap or inside checks require input from different layers
|
||||
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
|
||||
|
||||
// ensure that the first check argument is of layer 1 and the second of
|
||||
// layer 2 (unless both are of the same layer)
|
||||
int l1 = int (p1 & size_t (1));
|
||||
int l2 = int (p2 & size_t (1));
|
||||
|
||||
if (l1 > l2) {
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
}
|
||||
|
||||
db::EdgePair ep;
|
||||
if (mp_check->check (*o1, *o2, &ep)) {
|
||||
|
||||
size_t n = m_ep.size ();
|
||||
|
||||
m_ep.push_back (ep);
|
||||
m_ep_intra_polygon.push_back (p1 == p2); // not really required, but there for consistency
|
||||
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool
|
||||
Edge2EdgeCheckBase::requires_different_layers () const
|
||||
{
|
||||
return m_requires_different_layers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void
|
||||
Edge2EdgeCheckBase::set_requires_different_layers (bool f)
|
||||
{
|
||||
m_requires_different_layers = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool
|
||||
Edge2EdgeCheckBase::different_polygons () const
|
||||
{
|
||||
return m_different_polygons;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void
|
||||
Edge2EdgeCheckBase::set_different_polygons (bool f)
|
||||
{
|
||||
m_different_polygons = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the distance value
|
||||
*/
|
||||
EdgeRelationFilter::distance_type
|
||||
Edge2EdgeCheckBase::distance () const
|
||||
{
|
||||
return m_distance;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Poly2PolyCheckBase implementation
|
||||
|
||||
template <class PolygonType>
|
||||
poly2poly_check<PolygonType>::poly2poly_check (Edge2EdgeCheckBase &output)
|
||||
: mp_output (& output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
poly2poly_check<PolygonType>::poly2poly_check ()
|
||||
: mp_output (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
static size_t vertices (const db::Polygon &p)
|
||||
{
|
||||
return p.vertices ();
|
||||
}
|
||||
|
||||
static size_t vertices (const db::PolygonRef &p)
|
||||
{
|
||||
return p.obj ().vertices ();
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::single (const PolygonType &o, size_t p)
|
||||
{
|
||||
tl_assert (! mp_output->requires_different_layers () && ! mp_output->different_polygons ());
|
||||
|
||||
// finally we check the polygons vs. itself for checks involving intra-polygon interactions
|
||||
|
||||
m_scanner.clear ();
|
||||
m_scanner.reserve (vertices (o));
|
||||
|
||||
m_edge_heap.clear ();
|
||||
|
||||
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
|
||||
m_edge_heap.push_back (*e);
|
||||
m_scanner.insert (& m_edge_heap.back (), p);
|
||||
}
|
||||
|
||||
mp_output->feed_pseudo_edges (m_scanner);
|
||||
|
||||
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::connect (Edge2EdgeCheckBase &output)
|
||||
{
|
||||
mp_output = &output;
|
||||
clear ();
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::clear ()
|
||||
{
|
||||
m_scanner.clear ();
|
||||
m_edge_heap.clear ();
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::enter (const PolygonType &o, size_t p)
|
||||
{
|
||||
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
|
||||
m_edge_heap.push_back (*e);
|
||||
m_scanner.insert (& m_edge_heap.back (), p);
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::process ()
|
||||
{
|
||||
mp_output->feed_pseudo_edges (m_scanner);
|
||||
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template class poly2poly_check<db::Polygon>;
|
||||
template class poly2poly_check<db::PolygonRef>;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// RegionToEdgeInteractionFilterBase implementation
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::region_to_edge_interaction_filter_base (bool inverse, bool get_all)
|
||||
: m_inverse (inverse), m_get_all (get_all)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
void
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::preset (const OutputType *s)
|
||||
{
|
||||
m_seen.insert (s);
|
||||
}
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
void
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::add (const PolygonType *p, size_t, const EdgeType *e, size_t)
|
||||
{
|
||||
const OutputType *o = 0;
|
||||
tl::select (o, p, e);
|
||||
|
||||
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
|
||||
|
||||
// A polygon and an edge interact if the edge is either inside completely
|
||||
// of at least one edge of the polygon intersects with the edge
|
||||
bool interacts = false;
|
||||
if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) {
|
||||
interacts = true;
|
||||
} else {
|
||||
for (typename PolygonType::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) {
|
||||
if ((*pe).intersect (*e)) {
|
||||
interacts = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (interacts) {
|
||||
if (m_inverse) {
|
||||
m_seen.erase (o);
|
||||
} else {
|
||||
if (! m_get_all) {
|
||||
m_seen.insert (o);
|
||||
}
|
||||
put (*o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
void
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::fill_output ()
|
||||
{
|
||||
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
|
||||
put (**s);
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Polygon>;
|
||||
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::PolygonRef>;
|
||||
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Edge>;
|
||||
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::Edge>;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// RegionToTextInteractionFilterBase implementation
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::region_to_text_interaction_filter_base (bool inverse, bool get_all)
|
||||
: m_inverse (inverse), m_get_all (get_all)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
void
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::preset (const OutputType *s)
|
||||
{
|
||||
m_seen.insert (s);
|
||||
}
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
void
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::add (const PolygonType *p, size_t, const TextType *t, size_t)
|
||||
{
|
||||
const OutputType *o = 0;
|
||||
tl::select (o, p, t);
|
||||
|
||||
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
|
||||
|
||||
// A polygon and an text interact if the text is either inside completely
|
||||
// of at least one text of the polygon intersects with the text
|
||||
db::Point pt = db::box_convert<TextType> () (*t).p1 ();
|
||||
if (p->box ().contains (pt) && db::inside_poly (p->begin_edge (), pt) >= 0) {
|
||||
if (m_inverse) {
|
||||
m_seen.erase (o);
|
||||
} else {
|
||||
if (! m_get_all) {
|
||||
m_seen.insert (o);
|
||||
}
|
||||
put (*o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
void
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::fill_output ()
|
||||
{
|
||||
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
|
||||
put (**s);
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::PolygonRef>;
|
||||
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Polygon>;
|
||||
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Text>;
|
||||
template class region_to_text_interaction_filter_base<db::Polygon, db::TextRef, db::TextRef>;
|
||||
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::TextRef>;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,424 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2021 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
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbRegionCheckUtils
|
||||
#define HDR_dbRegionCheckUtils
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbCellVariants.h"
|
||||
#include "dbBoxScanner.h"
|
||||
#include "dbEdgePairRelations.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
|
||||
*/
|
||||
class DB_PUBLIC Edge2EdgeCheckBase
|
||||
: public db::box_scanner_receiver<db::Edge, size_t>
|
||||
{
|
||||
public:
|
||||
Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges);
|
||||
|
||||
/**
|
||||
* @brief Call this to initiate a new pass until the return value is false
|
||||
*/
|
||||
bool prepare_next_pass ();
|
||||
|
||||
/**
|
||||
* @brief Before the scanner is run, this method must be called to feed additional edges into the scanner
|
||||
* (required for negative edge output - cancellation of perpendicular edges)
|
||||
*/
|
||||
bool feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner);
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the box_scanner_receiver interface
|
||||
*/
|
||||
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2);
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the box_scanner_receiver interface
|
||||
*/
|
||||
void finish (const Edge *o, size_t);
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool requires_different_layers () const;
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void set_requires_different_layers (bool f);
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool different_polygons () const;
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void set_different_polygons (bool f);
|
||||
|
||||
/**
|
||||
* @brief Sets a flag indicating that this class wants negative edge output
|
||||
*/
|
||||
void set_has_negative_edge_output (bool f)
|
||||
{
|
||||
m_has_negative_edge_output = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a flag indicating that this class wants negative edge output
|
||||
*/
|
||||
bool has_negative_edge_output () const
|
||||
{
|
||||
return m_has_negative_edge_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a flag indicating that this class wants normal edge pair output
|
||||
*/
|
||||
void set_has_edge_pair_output (bool f)
|
||||
{
|
||||
m_has_edge_pair_output = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a flag indicating that this class wants normal edge pair output
|
||||
*/
|
||||
bool has_edge_pair_output () const
|
||||
{
|
||||
return m_has_edge_pair_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the distance value
|
||||
*/
|
||||
EdgeRelationFilter::distance_type distance () const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Normal edge pair output (violations)
|
||||
*/
|
||||
virtual void put (const db::EdgePair & /*edge*/, bool /*intra-polygon*/) const { }
|
||||
|
||||
/**
|
||||
* @brief Negative edge output
|
||||
*/
|
||||
virtual void put_negative (const db::Edge & /*edge*/, int /*layer*/) const { }
|
||||
|
||||
private:
|
||||
const EdgeRelationFilter *mp_check;
|
||||
bool m_requires_different_layers;
|
||||
bool m_different_polygons;
|
||||
EdgeRelationFilter::distance_type m_distance;
|
||||
std::vector<db::EdgePair> m_ep;
|
||||
std::multimap<std::pair<db::Edge, size_t>, size_t> m_e2ep;
|
||||
std::set<std::pair<db::Edge, size_t> > m_pseudo_edges;
|
||||
size_t m_first_pseudo;
|
||||
std::vector<bool> m_ep_discarded, m_ep_intra_polygon;
|
||||
bool m_with_shielding;
|
||||
bool m_symmetric_edges;
|
||||
bool m_has_edge_pair_output;
|
||||
bool m_has_negative_edge_output;
|
||||
unsigned int m_pass;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
*/
|
||||
template <class Output>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check
|
||||
: public Edge2EdgeCheckBase
|
||||
{
|
||||
public:
|
||||
edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output), mp_output_intra (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
edge2edge_check (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output_inter), mp_output_intra (&output_intra)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
void put (const db::EdgePair &edge, bool inter_polygon) const
|
||||
{
|
||||
if (! inter_polygon || ! mp_output_intra) {
|
||||
mp_output_inter->insert (edge);
|
||||
} else {
|
||||
mp_output_intra->insert (edge);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Output *mp_output_inter;
|
||||
Output *mp_output_intra;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
* This version allows delivery of the negative edges.
|
||||
*/
|
||||
template <class Output, class NegativeEdgeOutput>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output
|
||||
: public edge2edge_check<Output>
|
||||
{
|
||||
public:
|
||||
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
|
||||
mp_l1_negative_output (&l1_negative_output),
|
||||
mp_l2_negative_output (&l2_negative_output)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (true);
|
||||
}
|
||||
|
||||
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
|
||||
mp_l1_negative_output (&l1_negative_output),
|
||||
mp_l2_negative_output (&l2_negative_output)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (true);
|
||||
}
|
||||
|
||||
protected:
|
||||
void put_negative (const db::Edge &edge, int layer) const
|
||||
{
|
||||
if (layer == 0) {
|
||||
mp_l1_negative_output->insert (edge);
|
||||
}
|
||||
if (layer == 1) {
|
||||
mp_l2_negative_output->insert (edge);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
* This version has only negative edge output.
|
||||
*/
|
||||
template <class NegativeEdgeOutput>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check_negative
|
||||
: public Edge2EdgeCheckBase
|
||||
{
|
||||
public:
|
||||
edge2edge_check_negative (const EdgeRelationFilter &check, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding)
|
||||
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, false),
|
||||
mp_l1_negative_output (&l1_negative_output),
|
||||
mp_l2_negative_output (&l2_negative_output)
|
||||
{
|
||||
set_has_negative_edge_output (true);
|
||||
set_has_edge_pair_output (false);
|
||||
}
|
||||
|
||||
protected:
|
||||
void put_negative (const db::Edge &edge, int layer) const
|
||||
{
|
||||
if (layer == 0) {
|
||||
mp_l1_negative_output->insert (edge);
|
||||
}
|
||||
if (layer == 1) {
|
||||
mp_l2_negative_output->insert (edge);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
* This version has positive or negative output. Negative output is mapped to edge pairs
|
||||
* as well.
|
||||
*/
|
||||
template <class Output>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive
|
||||
: public edge2edge_check<Output>
|
||||
{
|
||||
public:
|
||||
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
|
||||
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
|
||||
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
|
||||
}
|
||||
|
||||
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
|
||||
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
|
||||
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
|
||||
}
|
||||
|
||||
protected:
|
||||
void put_negative (const db::Edge &edge, int layer) const
|
||||
{
|
||||
if (layer == 0) {
|
||||
edge2edge_check<Output>::put (db::EdgePair (edge, edge.swapped_points ()), false);
|
||||
}
|
||||
#if 0
|
||||
// NOTE: second-input negative edge output isn't worth a lot as the second input often is not merged, hence
|
||||
// the outer edges to not represent the actual contour.
|
||||
if (layer == 1) {
|
||||
edge2edge_check<Output>::put (db::EdgePair (edge.swapped_points (), edge), false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
|
||||
*/
|
||||
template <class PolygonType>
|
||||
class DB_PUBLIC poly2poly_check
|
||||
{
|
||||
public:
|
||||
poly2poly_check (Edge2EdgeCheckBase &output);
|
||||
poly2poly_check ();
|
||||
|
||||
void clear ();
|
||||
|
||||
void single (const PolygonType&o, size_t p);
|
||||
|
||||
void connect (Edge2EdgeCheckBase &output);
|
||||
void enter (const PolygonType &o, size_t p);
|
||||
void process ();
|
||||
|
||||
private:
|
||||
db::Edge2EdgeCheckBase *mp_output;
|
||||
db::box_scanner<db::Edge, size_t> m_scanner;
|
||||
std::list<db::Edge> m_edge_heap;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to edge interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
class DB_PUBLIC region_to_edge_interaction_filter_base
|
||||
: public db::box_scanner_receiver2<PolygonType, size_t, db::Edge, size_t>
|
||||
{
|
||||
public:
|
||||
region_to_edge_interaction_filter_base (bool inverse, bool get_all);
|
||||
|
||||
void preset (const OutputType *s);
|
||||
void add (const PolygonType *p, size_t, const EdgeType *e, size_t);
|
||||
void fill_output ();
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &s) const = 0;
|
||||
|
||||
private:
|
||||
std::set<const OutputType *> m_seen;
|
||||
bool m_inverse, m_get_all;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to edge interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class EdgeType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
|
||||
class DB_PUBLIC_TEMPLATE region_to_edge_interaction_filter
|
||||
: public region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>
|
||||
{
|
||||
public:
|
||||
region_to_edge_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
|
||||
: region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType> (inverse, get_all), mp_output (&output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &res) const
|
||||
{
|
||||
mp_output->insert (res);
|
||||
}
|
||||
|
||||
private:
|
||||
OutputContainer *mp_output;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to text interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
class DB_PUBLIC region_to_text_interaction_filter_base
|
||||
: public db::box_scanner_receiver2<PolygonType, size_t, TextType, size_t>
|
||||
{
|
||||
public:
|
||||
region_to_text_interaction_filter_base (bool inverse, bool get_all);
|
||||
|
||||
void preset (const OutputType *s);
|
||||
void add (const PolygonType *p, size_t, const TextType *e, size_t);
|
||||
void fill_output ();
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &s) const = 0;
|
||||
|
||||
private:
|
||||
std::set<const OutputType *> m_seen;
|
||||
bool m_inverse, m_get_all;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to text interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class TextType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
|
||||
class DB_PUBLIC_TEMPLATE region_to_text_interaction_filter
|
||||
: public region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>
|
||||
{
|
||||
public:
|
||||
region_to_text_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
|
||||
: region_to_text_interaction_filter_base<PolygonType, TextType, OutputType> (inverse, get_all), mp_output (&output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &poly) const
|
||||
{
|
||||
mp_output->insert (poly);
|
||||
}
|
||||
|
||||
private:
|
||||
OutputContainer *mp_output;
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -21,8 +21,8 @@
|
|||
*/
|
||||
|
||||
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbRegionLocalOperations.h"
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbLocalOperationUtils.h"
|
||||
#include "dbHierProcessor.h"
|
||||
|
||||
|
|
@ -123,7 +123,7 @@ static bool shields_interaction (const db::EdgePair &ep, const P &poly)
|
|||
|
||||
template <class TS, class TI>
|
||||
check_local_operation<TS, TI>::check_local_operation (const EdgeRelationFilter &check, bool different_polygons, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options)
|
||||
: m_check (check), m_different_polygons (different_polygons), m_has_other (has_other), m_other_is_merged (other_is_merged), m_options (options)
|
||||
: m_check (check), m_different_polygons (different_polygons), m_has_other (has_other), m_other_is_merged (other_is_merged), m_options (options), m_poly_check ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -206,10 +206,9 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
bool symmetric_edge_pairs = ! m_has_other && m_options.opposite_filter == db::NoOppositeFilter && m_options.rect_filter == RectFilter::NoRectFilter;
|
||||
|
||||
edge2edge_check_negative_or_positive<std::unordered_set<db::EdgePair> > edge_check (m_check, result, intra_polygon_result, m_options.negative, m_different_polygons, m_has_other, m_options.shielded, symmetric_edge_pairs);
|
||||
poly2poly_check<TS> poly_check (edge_check);
|
||||
m_poly_check.connect (edge_check);
|
||||
|
||||
std::list<TS> heap;
|
||||
db::box_scanner<TS, size_t> scanner;
|
||||
std::unordered_set<TI> polygons;
|
||||
|
||||
std::set<unsigned int> ids;
|
||||
|
|
@ -224,7 +223,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
size_t n = 0;
|
||||
for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
const TS &subject = interactions.subject_shape (i->first);
|
||||
scanner.insert (push_polygon_to_heap (layout, subject, heap), n);
|
||||
m_poly_check.enter (subject, n);
|
||||
n += 2;
|
||||
}
|
||||
|
||||
|
|
@ -262,7 +261,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
|
||||
n = 1;
|
||||
for (typename std::unordered_set<TI>::const_iterator o = polygons.begin (); o != polygons.end (); ++o) {
|
||||
scanner.insert (push_polygon_to_heap (layout, *o, heap), n);
|
||||
m_poly_check.enter (*o, n);
|
||||
n += 2;
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +269,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
|
||||
n = 1;
|
||||
for (std::set<unsigned int>::const_iterator id = ids.begin (); id != ids.end (); ++id) {
|
||||
scanner.insert (push_polygon_to_heap (layout, interactions.intruder_shape (*id).second, heap), n);
|
||||
m_poly_check.enter (interactions.intruder_shape (*id).second, n);
|
||||
n += 2;
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +285,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
// we can't directly insert because TS may be != TI
|
||||
const TS &ts = interactions.subject_shape (i->first);
|
||||
insert_into_hash (polygons, ts);
|
||||
scanner.insert (push_polygon_to_heap (layout, ts, heap), n);
|
||||
m_poly_check.enter (ts, n);
|
||||
n += 2;
|
||||
}
|
||||
|
||||
|
|
@ -295,7 +294,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
for (std::set<unsigned int>::const_iterator id = ids.begin (); id != ids.end (); ++id) {
|
||||
const TI &ti = interactions.intruder_shape (*id).second;
|
||||
if (polygons.find (ti) == polygons.end ()) {
|
||||
scanner.insert (push_polygon_to_heap (layout, ti, heap), n);
|
||||
m_poly_check.enter (ti, n);
|
||||
n += 2;
|
||||
}
|
||||
}
|
||||
|
|
@ -303,7 +302,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
|
|||
}
|
||||
|
||||
do {
|
||||
scanner.process (poly_check, m_check.distance (), db::box_convert<TS> ());
|
||||
m_poly_check.process ();
|
||||
} while (edge_check.prepare_next_pass ());
|
||||
|
||||
// detect and remove parts of the result which have or do not have results "opposite"
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "dbEdgePairRelations.h"
|
||||
#include "dbLocalOperation.h"
|
||||
#include "dbEdgeProcessor.h"
|
||||
#include "dbRegionCheckUtils.h"
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
|
@ -218,6 +219,7 @@ private:
|
|||
bool m_has_other;
|
||||
bool m_other_is_merged;
|
||||
db::RegionCheckOptions m_options;
|
||||
mutable poly2poly_check<TS> m_poly_check;
|
||||
};
|
||||
|
||||
typedef check_local_operation<db::PolygonRef, db::PolygonRef> CheckLocalOperation;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbRegionCheckUtils.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "dbEdgeBoolean.h"
|
||||
#include "tlSelect.h"
|
||||
|
|
@ -29,496 +30,6 @@
|
|||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Edge2EdgeCheckBase implementation
|
||||
|
||||
Edge2EdgeCheckBase::Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: mp_check (&check), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons),
|
||||
m_first_pseudo (std::numeric_limits<size_t>::max ()),
|
||||
m_with_shielding (with_shielding),
|
||||
m_symmetric_edges (symmetric_edges),
|
||||
m_has_edge_pair_output (true),
|
||||
m_has_negative_edge_output (false),
|
||||
m_pass (0)
|
||||
{
|
||||
m_distance = check.distance ();
|
||||
}
|
||||
|
||||
bool
|
||||
Edge2EdgeCheckBase::prepare_next_pass ()
|
||||
{
|
||||
++m_pass;
|
||||
|
||||
if (m_pass == 1) {
|
||||
|
||||
m_first_pseudo = m_ep.size ();
|
||||
|
||||
if (m_with_shielding && ! m_ep.empty ()) {
|
||||
|
||||
m_ep_discarded.resize (m_ep.size (), false);
|
||||
|
||||
// second pass:
|
||||
return true;
|
||||
|
||||
} else if (m_has_negative_edge_output) {
|
||||
|
||||
// second pass:
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! m_ep.empty () && m_has_edge_pair_output) {
|
||||
|
||||
std::vector<bool>::const_iterator d = m_ep_discarded.begin ();
|
||||
std::vector<bool>::const_iterator i = m_ep_intra_polygon.begin ();
|
||||
std::vector<db::EdgePair>::const_iterator ep = m_ep.begin ();
|
||||
while (ep != m_ep.end () && size_t (ep - m_ep.begin ()) < m_first_pseudo) {
|
||||
bool use_result = true;
|
||||
if (d != m_ep_discarded.end ()) {
|
||||
use_result = ! *d;
|
||||
++d;
|
||||
}
|
||||
if (use_result) {
|
||||
put (*ep, *i);
|
||||
}
|
||||
++ep;
|
||||
++i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool shields (const db::EdgePair &ep, const db::Edge &q)
|
||||
{
|
||||
db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ());
|
||||
db::Edge pe2 (ep.second ().p1 (), ep.first ().p2 ());
|
||||
|
||||
std::pair<bool, db::Point> ip1 = pe1.intersect_point (q);
|
||||
std::pair<bool, db::Point> ip2 = pe2.intersect_point (q);
|
||||
|
||||
if (ip1.first && ip2.first) {
|
||||
return ip1.second != ip2.second || (pe1.side_of (q.p1 ()) != 0 && pe2.side_of (q.p2 ()) != 0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Edge2EdgeCheckBase::finish (const Edge *o, size_t p)
|
||||
{
|
||||
if (m_has_negative_edge_output && m_pass == 1 && m_pseudo_edges.find (std::make_pair (*o, p)) == m_pseudo_edges.end ()) {
|
||||
|
||||
std::pair<db::Edge, size_t> k (*o, p);
|
||||
std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i0 = m_e2ep.find (k);
|
||||
|
||||
bool fully_removed = false;
|
||||
bool any = false;
|
||||
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; ! fully_removed && i != m_e2ep.end () && i->first == k; ++i) {
|
||||
size_t n = i->second / 2;
|
||||
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
|
||||
any = true;
|
||||
fully_removed = (((i->second & 1) == 0 ? m_ep [n].first () : m_ep [n].second ()) == *o);
|
||||
}
|
||||
}
|
||||
|
||||
if (! any) {
|
||||
|
||||
put_negative (*o, (int) p);
|
||||
|
||||
} else if (! fully_removed) {
|
||||
|
||||
std::set<db::Edge> partial_edges;
|
||||
|
||||
db::EdgeBooleanCluster<std::set<db::Edge> > ec (&partial_edges, db::EdgeNot);
|
||||
ec.add (o, 0);
|
||||
|
||||
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) {
|
||||
size_t n = i->second / 2;
|
||||
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
|
||||
ec.add (((i->second & 1) == 0 ? &m_ep [n].first () : &m_ep [n].second ()), 1);
|
||||
}
|
||||
}
|
||||
|
||||
ec.finish ();
|
||||
|
||||
for (std::set<db::Edge>::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) {
|
||||
put_negative (*e, (int) p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner)
|
||||
{
|
||||
if (m_pass == 1) {
|
||||
for (std::set<std::pair<db::Edge, size_t> >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) {
|
||||
scanner.insert (&e->first, e->second);
|
||||
}
|
||||
return ! m_pseudo_edges.empty ();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool edges_considered (bool requires_different_polygons, bool requires_different_layers, size_t p1, size_t p2)
|
||||
{
|
||||
if (p1 == p2) {
|
||||
if (requires_different_polygons) {
|
||||
return false;
|
||||
} else if ((p1 & size_t (1)) != 0) {
|
||||
// edges from the same polygon are only considered on first layer.
|
||||
// Reasoning: this case happens when "intruder" polygons are put on layer 1
|
||||
// while "subject" polygons are put on layer 0. We don't want "intruders"
|
||||
// to generate intra-polygon markers.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (((p1 ^ p2) & size_t (1)) == 0) {
|
||||
if (requires_different_layers) {
|
||||
return false;
|
||||
} else if ((p1 & size_t (1)) != 0) {
|
||||
// edges on the same layer are only considered on first layer.
|
||||
// Reasoning: this case happens when "intruder" polygons are put on layer 1
|
||||
// while "subject" polygons are put on layer 0. We don't want "intruders"
|
||||
// to generate inter-polygon markers between them.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
|
||||
{
|
||||
if (m_pass == 0) {
|
||||
|
||||
// Overlap or inside checks require input from different layers
|
||||
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
|
||||
|
||||
// ensure that the first check argument is of layer 1 and the second of
|
||||
// layer 2 (unless both are of the same layer)
|
||||
int l1 = int (p1 & size_t (1));
|
||||
int l2 = int (p2 & size_t (1));
|
||||
|
||||
if (l1 > l2) {
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
}
|
||||
|
||||
db::EdgePair ep;
|
||||
if (mp_check->check (*o1, *o2, &ep)) {
|
||||
|
||||
ep.set_symmetric (m_symmetric_edges);
|
||||
|
||||
// found a violation: store inside the local buffer for now. In the second
|
||||
// pass we will eliminate those which are shielded completely (with shielding)
|
||||
// and/or compute the negative edges.
|
||||
size_t n = m_ep.size ();
|
||||
|
||||
m_ep.push_back (ep);
|
||||
m_ep_intra_polygon.push_back (p1 == p2);
|
||||
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
|
||||
|
||||
if (m_has_negative_edge_output) {
|
||||
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p1));
|
||||
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p1));
|
||||
if (p1 != p2) {
|
||||
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p2));
|
||||
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p2));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// set the discarded flags for shielded output
|
||||
if (m_with_shielding) {
|
||||
|
||||
// a simple (complete) shielding implementation which is based on the
|
||||
// assumption that shielding is relevant as soon as a foreign edge cuts through
|
||||
// both of the edge pair's connecting edges.
|
||||
|
||||
// TODO: this implementation does not take into account the nature of the
|
||||
// EdgePair - because of "whole_edge" it may not reflect the part actually
|
||||
// violating the distance.
|
||||
|
||||
std::vector<size_t> n1, n2;
|
||||
|
||||
for (unsigned int p = 0; p < 2; ++p) {
|
||||
|
||||
std::pair<db::Edge, size_t> k (*o1, p1);
|
||||
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) {
|
||||
size_t n = i->second / 2;
|
||||
if (n < m_first_pseudo && ! m_ep_discarded [n]) {
|
||||
n1.push_back (n);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort (n1.begin (), n1.end ());
|
||||
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
n1.swap (n2);
|
||||
|
||||
}
|
||||
|
||||
for (unsigned int p = 0; p < 2; ++p) {
|
||||
|
||||
std::vector<size_t> nn;
|
||||
std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn));
|
||||
|
||||
for (std::vector<size_t>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
|
||||
db::EdgePair ep = m_ep [*i].normalized ();
|
||||
if (shields (ep, *o2)) {
|
||||
m_ep_discarded [*i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
n1.swap (n2);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// for negative output edges are cancelled by short interactions perpendicular to them
|
||||
// For this we have generated "pseudo edges" running along the sides of the original violation. We now check a real
|
||||
// edge vs. a pseudo edge with the same conditions as the normal interaction and add them to the results. In the
|
||||
// negative case this means we cancel a real edge.
|
||||
|
||||
if (m_has_negative_edge_output &&
|
||||
(m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end ()) != (m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ())) {
|
||||
|
||||
// Overlap or inside checks require input from different layers
|
||||
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
|
||||
|
||||
// ensure that the first check argument is of layer 1 and the second of
|
||||
// layer 2 (unless both are of the same layer)
|
||||
int l1 = int (p1 & size_t (1));
|
||||
int l2 = int (p2 & size_t (1));
|
||||
|
||||
if (l1 > l2) {
|
||||
std::swap (o1, o2);
|
||||
std::swap (p1, p2);
|
||||
}
|
||||
|
||||
db::EdgePair ep;
|
||||
if (mp_check->check (*o1, *o2, &ep)) {
|
||||
|
||||
size_t n = m_ep.size ();
|
||||
|
||||
m_ep.push_back (ep);
|
||||
m_ep_intra_polygon.push_back (p1 == p2); // not really required, but there for consistency
|
||||
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
|
||||
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool
|
||||
Edge2EdgeCheckBase::requires_different_layers () const
|
||||
{
|
||||
return m_requires_different_layers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void
|
||||
Edge2EdgeCheckBase::set_requires_different_layers (bool f)
|
||||
{
|
||||
m_requires_different_layers = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool
|
||||
Edge2EdgeCheckBase::different_polygons () const
|
||||
{
|
||||
return m_different_polygons;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void
|
||||
Edge2EdgeCheckBase::set_different_polygons (bool f)
|
||||
{
|
||||
m_different_polygons = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the distance value
|
||||
*/
|
||||
EdgeRelationFilter::distance_type
|
||||
Edge2EdgeCheckBase::distance () const
|
||||
{
|
||||
return m_distance;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Poly2PolyCheckBase implementation
|
||||
|
||||
template <class PolygonType>
|
||||
poly2poly_check<PolygonType>::poly2poly_check (Edge2EdgeCheckBase &output)
|
||||
: mp_output (& output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::finish (const PolygonType *o, size_t p)
|
||||
{
|
||||
enter (*o, p);
|
||||
}
|
||||
|
||||
static size_t vertices (const db::Polygon &p)
|
||||
{
|
||||
return p.vertices ();
|
||||
}
|
||||
|
||||
static size_t vertices (const db::PolygonRef &p)
|
||||
{
|
||||
return p.obj ().vertices ();
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::enter (const PolygonType &o, size_t p)
|
||||
{
|
||||
if (! mp_output->requires_different_layers () && ! mp_output->different_polygons ()) {
|
||||
|
||||
// finally we check the polygons vs. itself for checks involving intra-polygon interactions
|
||||
|
||||
m_scanner.clear ();
|
||||
m_scanner.reserve (vertices (o));
|
||||
|
||||
m_edges.clear ();
|
||||
m_edges.reserve (vertices (o));
|
||||
|
||||
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
|
||||
m_edges.push_back (*e);
|
||||
m_scanner.insert (& m_edges.back (), p);
|
||||
}
|
||||
|
||||
mp_output->feed_pseudo_edges (m_scanner);
|
||||
|
||||
tl_assert (m_edges.size () == vertices (o));
|
||||
|
||||
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::add (const PolygonType *o1, size_t p1, const PolygonType *o2, size_t p2)
|
||||
{
|
||||
enter (*o1, p1, *o2, p2);
|
||||
}
|
||||
|
||||
static bool interact (const db::Box &box, const db::Edge &e)
|
||||
{
|
||||
if (! e.bbox ().touches (box)) {
|
||||
return false;
|
||||
} else if (e.is_ortho ()) {
|
||||
return true;
|
||||
} else {
|
||||
return e.clipped (box).first;
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType>
|
||||
void
|
||||
poly2poly_check<PolygonType>::enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2)
|
||||
{
|
||||
if (p1 != p2 && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) {
|
||||
|
||||
bool take_all = mp_output->has_negative_edge_output ();
|
||||
|
||||
db::Box common_box;
|
||||
if (! take_all) {
|
||||
db::Vector e (mp_output->distance (), mp_output->distance ());
|
||||
common_box = o1.box ().enlarged (e) & o2.box ().enlarged (e);
|
||||
if (common_box.empty ()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_scanner.clear ();
|
||||
m_scanner.reserve (vertices (o1) + vertices (o2));
|
||||
|
||||
m_edges.clear ();
|
||||
m_edges.reserve (vertices (o1) + vertices (o2));
|
||||
|
||||
bool any_o1 = false, any_o2 = false;
|
||||
|
||||
for (typename PolygonType::polygon_edge_iterator e = o1.begin_edge (); ! e.at_end (); ++e) {
|
||||
if (take_all || interact (common_box, *e)) {
|
||||
m_edges.push_back (*e);
|
||||
m_scanner.insert (& m_edges.back (), p1);
|
||||
any_o1 = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (typename PolygonType::polygon_edge_iterator e = o2.begin_edge (); ! e.at_end (); ++e) {
|
||||
if (take_all || interact (common_box, *e)) {
|
||||
m_edges.push_back (*e);
|
||||
m_scanner.insert (& m_edges.back (), p2);
|
||||
any_o2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! take_all && (! any_o1 || ! any_o2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mp_output->feed_pseudo_edges (m_scanner);
|
||||
|
||||
// temporarily disable intra-polygon check in that step .. we do that later in finish()
|
||||
// if required (#650).
|
||||
bool no_intra = mp_output->different_polygons ();
|
||||
mp_output->set_different_polygons (true);
|
||||
|
||||
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
|
||||
|
||||
mp_output->set_different_polygons (no_intra);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template class poly2poly_check<db::Polygon>;
|
||||
template class poly2poly_check<db::PolygonRef>;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// RegionPerimeterFilter implementation
|
||||
|
||||
|
|
@ -859,7 +370,7 @@ SinglePolygonCheck::process (const db::Polygon &polygon, std::vector<db::EdgePai
|
|||
poly2poly_check<db::Polygon> poly_check (edge_check);
|
||||
|
||||
do {
|
||||
poly_check.enter (polygon, 0);
|
||||
poly_check.single (polygon, 0);
|
||||
} while (edge_check.prepare_next_pass ());
|
||||
|
||||
res.insert (res.end (), result.begin (), result.end ());
|
||||
|
|
@ -967,219 +478,4 @@ HullExtractionProcessor::process (const db::Polygon &poly, std::vector<db::Polyg
|
|||
res.back ().assign_hull (poly.begin_hull (), poly.end_hull ());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// RegionToEdgeInteractionFilterBase implementation
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::region_to_edge_interaction_filter_base (bool inverse, bool get_all)
|
||||
: m_inverse (inverse), m_get_all (get_all)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
void
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::preset (const OutputType *s)
|
||||
{
|
||||
m_seen.insert (s);
|
||||
}
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
void
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::add (const PolygonType *p, size_t, const EdgeType *e, size_t)
|
||||
{
|
||||
const OutputType *o = 0;
|
||||
tl::select (o, p, e);
|
||||
|
||||
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
|
||||
|
||||
// A polygon and an edge interact if the edge is either inside completely
|
||||
// of at least one edge of the polygon intersects with the edge
|
||||
bool interacts = false;
|
||||
if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) {
|
||||
interacts = true;
|
||||
} else {
|
||||
for (typename PolygonType::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) {
|
||||
if ((*pe).intersect (*e)) {
|
||||
interacts = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (interacts) {
|
||||
if (m_inverse) {
|
||||
m_seen.erase (o);
|
||||
} else {
|
||||
if (! m_get_all) {
|
||||
m_seen.insert (o);
|
||||
}
|
||||
put (*o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
void
|
||||
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::fill_output ()
|
||||
{
|
||||
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
|
||||
put (**s);
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Polygon>;
|
||||
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::PolygonRef>;
|
||||
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Edge>;
|
||||
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::Edge>;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// RegionToTextInteractionFilterBase implementation
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::region_to_text_interaction_filter_base (bool inverse, bool get_all)
|
||||
: m_inverse (inverse), m_get_all (get_all)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
void
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::preset (const OutputType *s)
|
||||
{
|
||||
m_seen.insert (s);
|
||||
}
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
void
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::add (const PolygonType *p, size_t, const TextType *t, size_t)
|
||||
{
|
||||
const OutputType *o = 0;
|
||||
tl::select (o, p, t);
|
||||
|
||||
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
|
||||
|
||||
// A polygon and an text interact if the text is either inside completely
|
||||
// of at least one text of the polygon intersects with the text
|
||||
db::Point pt = db::box_convert<TextType> () (*t).p1 ();
|
||||
if (p->box ().contains (pt) && db::inside_poly (p->begin_edge (), pt) >= 0) {
|
||||
if (m_inverse) {
|
||||
m_seen.erase (o);
|
||||
} else {
|
||||
if (! m_get_all) {
|
||||
m_seen.insert (o);
|
||||
}
|
||||
put (*o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
void
|
||||
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::fill_output ()
|
||||
{
|
||||
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
|
||||
put (**s);
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::PolygonRef>;
|
||||
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Polygon>;
|
||||
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Text>;
|
||||
template class region_to_text_interaction_filter_base<db::Polygon, db::TextRef, db::TextRef>;
|
||||
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::TextRef>;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Polygon snapping
|
||||
|
||||
db::Polygon
|
||||
snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap)
|
||||
{
|
||||
db::Polygon pnew;
|
||||
|
||||
for (size_t i = 0; i < poly.holes () + 1; ++i) {
|
||||
|
||||
heap.clear ();
|
||||
|
||||
db::Polygon::polygon_contour_iterator b, e;
|
||||
|
||||
if (i == 0) {
|
||||
b = poly.begin_hull ();
|
||||
e = poly.end_hull ();
|
||||
} else {
|
||||
b = poly.begin_hole ((unsigned int) (i - 1));
|
||||
e = poly.end_hole ((unsigned int) (i - 1));
|
||||
}
|
||||
|
||||
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
|
||||
heap.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy)));
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
pnew.assign_hull (heap.begin (), heap.end ());
|
||||
} else {
|
||||
pnew.insert_hole (heap.begin (), heap.end ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return pnew;
|
||||
}
|
||||
|
||||
db::Polygon
|
||||
scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap)
|
||||
{
|
||||
db::Polygon pnew;
|
||||
|
||||
int64_t dgx = int64_t (gx) * int64_t (dx);
|
||||
int64_t dgy = int64_t (gy) * int64_t (dy);
|
||||
|
||||
for (size_t i = 0; i < poly.holes () + 1; ++i) {
|
||||
|
||||
heap.clear ();
|
||||
|
||||
db::Polygon::polygon_contour_iterator b, e;
|
||||
|
||||
if (i == 0) {
|
||||
b = poly.begin_hull ();
|
||||
e = poly.end_hull ();
|
||||
} else {
|
||||
b = poly.begin_hole ((unsigned int) (i - 1));
|
||||
e = poly.end_hole ((unsigned int) (i - 1));
|
||||
}
|
||||
|
||||
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
|
||||
int64_t x = snap_to_grid (int64_t ((*pt).x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
|
||||
int64_t y = snap_to_grid (int64_t ((*pt).y ()) * my + int64_t (oy), dgy) / int64_t (dy);
|
||||
heap.push_back (db::Point (db::Coord (x), db::Coord (y)));
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
pnew.assign_hull (heap.begin (), heap.end ());
|
||||
} else {
|
||||
pnew.insert_hole (heap.begin (), heap.end ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return pnew;
|
||||
}
|
||||
|
||||
db::Vector
|
||||
scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy)
|
||||
{
|
||||
int64_t dgx = int64_t (gx) * int64_t (dx);
|
||||
int64_t dgy = int64_t (gy) * int64_t (dy);
|
||||
|
||||
int64_t x = snap_to_grid (int64_t (v.x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
|
||||
int64_t y = snap_to_grid (int64_t (v.y ()) * my + int64_t (oy), dgy) / int64_t (dy);
|
||||
|
||||
return db::Vector (db::Coord (x), db::Coord (y));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -583,298 +583,6 @@ public:
|
|||
virtual bool result_must_not_be_merged () const { return false; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
|
||||
*/
|
||||
class DB_PUBLIC Edge2EdgeCheckBase
|
||||
: public db::box_scanner_receiver<db::Edge, size_t>
|
||||
{
|
||||
public:
|
||||
Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges);
|
||||
|
||||
/**
|
||||
* @brief Call this to initiate a new pass until the return value is false
|
||||
*/
|
||||
bool prepare_next_pass ();
|
||||
|
||||
/**
|
||||
* @brief Before the scanner is run, this method must be called to feed additional edges into the scanner
|
||||
* (required for negative edge output - cancellation of perpendicular edges)
|
||||
*/
|
||||
bool feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner);
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the box_scanner_receiver interface
|
||||
*/
|
||||
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2);
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the box_scanner_receiver interface
|
||||
*/
|
||||
void finish (const Edge *o, size_t);
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool requires_different_layers () const;
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void set_requires_different_layers (bool f);
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the check requires different layers
|
||||
*/
|
||||
bool different_polygons () const;
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the check requires different layers
|
||||
*/
|
||||
void set_different_polygons (bool f);
|
||||
|
||||
/**
|
||||
* @brief Sets a flag indicating that this class wants negative edge output
|
||||
*/
|
||||
void set_has_negative_edge_output (bool f)
|
||||
{
|
||||
m_has_negative_edge_output = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a flag indicating that this class wants negative edge output
|
||||
*/
|
||||
bool has_negative_edge_output () const
|
||||
{
|
||||
return m_has_negative_edge_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a flag indicating that this class wants normal edge pair output
|
||||
*/
|
||||
void set_has_edge_pair_output (bool f)
|
||||
{
|
||||
m_has_edge_pair_output = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a flag indicating that this class wants normal edge pair output
|
||||
*/
|
||||
bool has_edge_pair_output () const
|
||||
{
|
||||
return m_has_edge_pair_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the distance value
|
||||
*/
|
||||
EdgeRelationFilter::distance_type distance () const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Normal edge pair output (violations)
|
||||
*/
|
||||
virtual void put (const db::EdgePair & /*edge*/, bool /*intra-polygon*/) const { }
|
||||
|
||||
/**
|
||||
* @brief Negative edge output
|
||||
*/
|
||||
virtual void put_negative (const db::Edge & /*edge*/, int /*layer*/) const { }
|
||||
|
||||
private:
|
||||
const EdgeRelationFilter *mp_check;
|
||||
bool m_requires_different_layers;
|
||||
bool m_different_polygons;
|
||||
EdgeRelationFilter::distance_type m_distance;
|
||||
std::vector<db::EdgePair> m_ep;
|
||||
std::multimap<std::pair<db::Edge, size_t>, size_t> m_e2ep;
|
||||
std::set<std::pair<db::Edge, size_t> > m_pseudo_edges;
|
||||
size_t m_first_pseudo;
|
||||
std::vector<bool> m_ep_discarded, m_ep_intra_polygon;
|
||||
bool m_with_shielding;
|
||||
bool m_symmetric_edges;
|
||||
bool m_has_edge_pair_output;
|
||||
bool m_has_negative_edge_output;
|
||||
unsigned int m_pass;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
*/
|
||||
template <class Output>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check
|
||||
: public Edge2EdgeCheckBase
|
||||
{
|
||||
public:
|
||||
edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output), mp_output_intra (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
edge2edge_check (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output_inter), mp_output_intra (&output_intra)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
void put (const db::EdgePair &edge, bool inter_polygon) const
|
||||
{
|
||||
if (! inter_polygon || ! mp_output_intra) {
|
||||
mp_output_inter->insert (edge);
|
||||
} else {
|
||||
mp_output_intra->insert (edge);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Output *mp_output_inter;
|
||||
Output *mp_output_intra;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
* This version allows delivery of the negative edges.
|
||||
*/
|
||||
template <class Output, class NegativeEdgeOutput>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output
|
||||
: public edge2edge_check<Output>
|
||||
{
|
||||
public:
|
||||
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
|
||||
mp_l1_negative_output (&l1_negative_output),
|
||||
mp_l2_negative_output (&l2_negative_output)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (true);
|
||||
}
|
||||
|
||||
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
|
||||
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
|
||||
mp_l1_negative_output (&l1_negative_output),
|
||||
mp_l2_negative_output (&l2_negative_output)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (true);
|
||||
}
|
||||
|
||||
protected:
|
||||
void put_negative (const db::Edge &edge, int layer) const
|
||||
{
|
||||
if (layer == 0) {
|
||||
mp_l1_negative_output->insert (edge);
|
||||
}
|
||||
if (layer == 1) {
|
||||
mp_l2_negative_output->insert (edge);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
* This version has only negative edge output.
|
||||
*/
|
||||
template <class NegativeEdgeOutput>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check_negative
|
||||
: public Edge2EdgeCheckBase
|
||||
{
|
||||
public:
|
||||
edge2edge_check_negative (const EdgeRelationFilter &check, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding)
|
||||
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, false),
|
||||
mp_l1_negative_output (&l1_negative_output),
|
||||
mp_l2_negative_output (&l2_negative_output)
|
||||
{
|
||||
set_has_negative_edge_output (true);
|
||||
set_has_edge_pair_output (false);
|
||||
}
|
||||
|
||||
protected:
|
||||
void put_negative (const db::Edge &edge, int layer) const
|
||||
{
|
||||
if (layer == 0) {
|
||||
mp_l1_negative_output->insert (edge);
|
||||
}
|
||||
if (layer == 1) {
|
||||
mp_l2_negative_output->insert (edge);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality
|
||||
*
|
||||
* This class implements the edge-to-edge part of the polygon DRC.
|
||||
* This version has positive or negative output. Negative output is mapped to edge pairs
|
||||
* as well.
|
||||
*/
|
||||
template <class Output>
|
||||
class DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive
|
||||
: public edge2edge_check<Output>
|
||||
{
|
||||
public:
|
||||
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
|
||||
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
|
||||
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
|
||||
}
|
||||
|
||||
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
|
||||
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric)
|
||||
{
|
||||
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
|
||||
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
|
||||
}
|
||||
|
||||
protected:
|
||||
void put_negative (const db::Edge &edge, int layer) const
|
||||
{
|
||||
if (layer == 0) {
|
||||
edge2edge_check<Output>::put (db::EdgePair (edge, edge.swapped_points ()), false);
|
||||
}
|
||||
#if 0
|
||||
// NOTE: second-input negative edge output isn't worth a lot as the second input often is not merged, hence
|
||||
// the outer edges to not represent the actual contour.
|
||||
if (layer == 1) {
|
||||
edge2edge_check<Output>::put (db::EdgePair (edge.swapped_points (), edge), false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
|
||||
*/
|
||||
template <class PolygonType>
|
||||
class DB_PUBLIC poly2poly_check
|
||||
: public db::box_scanner_receiver<PolygonType, size_t>
|
||||
{
|
||||
public:
|
||||
poly2poly_check (Edge2EdgeCheckBase &output);
|
||||
|
||||
void finish (const PolygonType *o, size_t p);
|
||||
void enter (const PolygonType&o, size_t p);
|
||||
void add (const PolygonType *o1, size_t p1, const PolygonType *o2, size_t p2);
|
||||
void enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2);
|
||||
|
||||
private:
|
||||
db::Edge2EdgeCheckBase *mp_output;
|
||||
db::box_scanner<db::Edge, size_t> m_scanner;
|
||||
std::vector<db::Edge> m_edges;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A class wrapping the single-polygon checks into a polygon-to-edge pair processor
|
||||
*/
|
||||
|
|
@ -892,129 +600,6 @@ private:
|
|||
db::RegionCheckOptions m_options;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to edge interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class EdgeType, class OutputType>
|
||||
class DB_PUBLIC region_to_edge_interaction_filter_base
|
||||
: public db::box_scanner_receiver2<PolygonType, size_t, db::Edge, size_t>
|
||||
{
|
||||
public:
|
||||
region_to_edge_interaction_filter_base (bool inverse, bool get_all);
|
||||
|
||||
void preset (const OutputType *s);
|
||||
void add (const PolygonType *p, size_t, const EdgeType *e, size_t);
|
||||
void fill_output ();
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &s) const = 0;
|
||||
|
||||
private:
|
||||
std::set<const OutputType *> m_seen;
|
||||
bool m_inverse, m_get_all;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to edge interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class EdgeType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
|
||||
class DB_PUBLIC_TEMPLATE region_to_edge_interaction_filter
|
||||
: public region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>
|
||||
{
|
||||
public:
|
||||
region_to_edge_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
|
||||
: region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType> (inverse, get_all), mp_output (&output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &res) const
|
||||
{
|
||||
mp_output->insert (res);
|
||||
}
|
||||
|
||||
private:
|
||||
OutputContainer *mp_output;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to text interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class TextType, class OutputType>
|
||||
class DB_PUBLIC region_to_text_interaction_filter_base
|
||||
: public db::box_scanner_receiver2<PolygonType, size_t, TextType, size_t>
|
||||
{
|
||||
public:
|
||||
region_to_text_interaction_filter_base (bool inverse, bool get_all);
|
||||
|
||||
void preset (const OutputType *s);
|
||||
void add (const PolygonType *p, size_t, const TextType *e, size_t);
|
||||
void fill_output ();
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &s) const = 0;
|
||||
|
||||
private:
|
||||
std::set<const OutputType *> m_seen;
|
||||
bool m_inverse, m_get_all;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the region to text interaction functionality
|
||||
*/
|
||||
template <class PolygonType, class TextType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
|
||||
class DB_PUBLIC_TEMPLATE region_to_text_interaction_filter
|
||||
: public region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>
|
||||
{
|
||||
public:
|
||||
region_to_text_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
|
||||
: region_to_text_interaction_filter_base<PolygonType, TextType, OutputType> (inverse, get_all), mp_output (&output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void put (const OutputType &poly) const
|
||||
{
|
||||
mp_output->insert (poly);
|
||||
}
|
||||
|
||||
private:
|
||||
OutputContainer *mp_output;
|
||||
};
|
||||
|
||||
template <class C>
|
||||
static inline C snap_to_grid (C c, C g)
|
||||
{
|
||||
// This form of snapping always snaps g/2 to right/top.
|
||||
if (c < 0) {
|
||||
c = -g * ((-c + (g - 1) / 2) / g);
|
||||
} else {
|
||||
c = g * ((c + g / 2) / g);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Snaps a polygon to the given grid
|
||||
* Heap is a vector of points reused for the point list
|
||||
*/
|
||||
DB_PUBLIC db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap);
|
||||
|
||||
/**
|
||||
* @brief Scales and snaps a polygon to the given grid
|
||||
* Heap is a vector of points reused for the point list
|
||||
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
|
||||
*/
|
||||
DB_PUBLIC db::Polygon scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap);
|
||||
|
||||
/**
|
||||
* @brief Scales and snaps a vector to the given grid
|
||||
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
|
||||
*/
|
||||
DB_PUBLIC db::Vector scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy);
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
#include "tlUnitTest.h"
|
||||
#include "tlStringEx.h"
|
||||
|
||||
#include "dbRegionUtils.h"
|
||||
#include "dbRegionCheckUtils.h"
|
||||
|
||||
TEST(1_SimpleLShape)
|
||||
{
|
||||
|
|
@ -52,7 +52,7 @@ TEST(1_SimpleLShape)
|
|||
|
||||
do {
|
||||
// single polygon check
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0),(2000,1000;1000,1000)|(1000,2000;2000,2000)");
|
||||
|
|
@ -85,7 +85,7 @@ TEST(1s_SimpleLShape)
|
|||
|
||||
do {
|
||||
// single polygon check
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)/(1000,1000;1000,0),(1000,2000;2000,2000)/(2000,1000;1000,1000)");
|
||||
|
|
@ -118,7 +118,7 @@ TEST(2_SimpleLWithBigPart)
|
|||
|
||||
do {
|
||||
// single polygon check
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0)");
|
||||
|
|
@ -153,7 +153,7 @@ TEST(3_SimpleTWithBigPart)
|
|||
|
||||
do {
|
||||
// single polygon check
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0),(0,2500;0,3500)|(1000,3500;1000,2500)");
|
||||
|
|
@ -188,7 +188,7 @@ TEST(4_SimpleNotch)
|
|||
|
||||
do {
|
||||
// single polygon check
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(1000,1000;2000,1000)|(2000,2000;1000,2000)");
|
||||
|
|
@ -225,7 +225,7 @@ TEST(5_LShapeNotch)
|
|||
|
||||
do {
|
||||
// single polygon check
|
||||
poly_check.enter (poly, 0);
|
||||
poly_check.single (poly, 0);
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(1500,500;2000,500)|(2000,1500;1500,1500),(1500,1500;1500,2500)|(500,2500;500,1500)");
|
||||
|
|
@ -264,14 +264,12 @@ TEST(6_SeparationLvsBox)
|
|||
db::Polygon poly2;
|
||||
poly2.assign_hull (pts2, pts2 + sizeof (pts2) / sizeof (pts2[0]));
|
||||
|
||||
db::box_scanner<db::Polygon, size_t> scanner;
|
||||
scanner.insert (&poly1, 0); // layer 0
|
||||
scanner.insert (&poly2, 1); // layer 1
|
||||
|
||||
db::poly2poly_check<db::Polygon> poly_check (e2e);
|
||||
poly_check.enter (poly1, 0); // layer 0
|
||||
poly_check.enter (poly2, 1); // layer 1
|
||||
|
||||
do {
|
||||
scanner.process (poly_check, er.distance (), db::box_convert<db::Polygon> ());
|
||||
poly_check.process ();
|
||||
} while (e2e.prepare_next_pass ());
|
||||
|
||||
EXPECT_EQ (tl::to_string (ep), "(1000,1000;1000,0)/(2000,0;2000,1000),(3000,2000;2000,2000)/(2000,1000;3000,1000)");
|
||||
|
|
@ -10,7 +10,7 @@ SOURCES = \
|
|||
dbCompoundOperationTests.cc \
|
||||
dbFillToolTests.cc \
|
||||
dbRecursiveInstanceIteratorTests.cc \
|
||||
dbRegionUtilsTests.cc \
|
||||
dbRegionCheckUtilsTests.cc \
|
||||
dbUtilsTests.cc \
|
||||
dbWriterTools.cc \
|
||||
dbLoadLayoutOptionsTests.cc \
|
||||
|
|
|
|||
Loading…
Reference in New Issue