mirror of https://github.com/KLayout/klayout.git
481 lines
16 KiB
C++
481 lines
16 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2024 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 "dbShapeProcessor.h"
|
|
#include "dbLayout.h"
|
|
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <memory>
|
|
|
|
namespace db
|
|
{
|
|
|
|
// -------------------------------------------------------------------------------
|
|
// ShapeProcessor implementation
|
|
|
|
ShapeProcessor::ShapeProcessor (bool report_progress, const std::string &progress_desc)
|
|
: m_processor (report_progress, progress_desc)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
ShapeProcessor::clear ()
|
|
{
|
|
m_processor.clear ();
|
|
}
|
|
|
|
void
|
|
ShapeProcessor::reserve (size_t n)
|
|
{
|
|
m_processor.reserve (n);
|
|
}
|
|
|
|
size_t
|
|
ShapeProcessor::count () const
|
|
{
|
|
return m_processor.count ();
|
|
}
|
|
|
|
void
|
|
ShapeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op)
|
|
{
|
|
m_processor.process (es, op);
|
|
}
|
|
|
|
size_t
|
|
ShapeProcessor::count_edges (const db::Shape &shape) const
|
|
{
|
|
size_t n = 0;
|
|
|
|
if (shape.is_polygon ()) {
|
|
|
|
// KLUDGE: Shape should have a point_count property (or similar)
|
|
for (db::Shape::polygon_edge_iterator e = shape.begin_edge (); ! e.at_end (); ++e) {
|
|
++n;
|
|
}
|
|
|
|
} else if (shape.is_path ()) {
|
|
|
|
// KLUDGE: This is rather inefficient ..
|
|
db::Polygon poly;
|
|
shape.polygon (poly);
|
|
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
|
|
++n;
|
|
}
|
|
|
|
} else if (shape.is_box ()) {
|
|
n += 4;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
void
|
|
ShapeProcessor::merge (const std::vector<db::Shape> &in, const std::vector<db::CplxTrans> &trans, std::vector <db::Edge> &out_edges, unsigned int min_wc)
|
|
{
|
|
clear ();
|
|
|
|
size_t e = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in.begin (); i != in.end (); ++i) {
|
|
e += count_edges (*i);
|
|
}
|
|
reserve (e + e / 4); // heuristic reserve for crossing points
|
|
|
|
size_t n = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in.begin (); i != in.end (); ++i, ++n) {
|
|
if (n < trans.size ()) {
|
|
insert (*i, db::ICplxTrans (trans [n]), n);
|
|
} else {
|
|
insert (*i, n);
|
|
}
|
|
}
|
|
|
|
db::MergeOp op (min_wc);
|
|
db::EdgeContainer out (out_edges);
|
|
process (out, op);
|
|
}
|
|
|
|
void
|
|
ShapeProcessor::merge (const std::vector<db::Shape> &in, const std::vector<db::CplxTrans> &trans, std::vector <db::Polygon> &out_polygons, unsigned int min_wc, bool resolve_holes, bool min_coherence)
|
|
{
|
|
clear ();
|
|
|
|
size_t e = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in.begin (); i != in.end (); ++i) {
|
|
e += count_edges (*i);
|
|
}
|
|
reserve (e + e / 4); // heuristic reserve for crossing points
|
|
|
|
size_t n = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in.begin (); i != in.end (); ++i, ++n) {
|
|
if (n < trans.size ()) {
|
|
insert (*i, db::ICplxTrans (trans [n]), n);
|
|
} else {
|
|
insert (*i, n);
|
|
}
|
|
}
|
|
|
|
db::MergeOp op (min_wc);
|
|
db::PolygonContainer pc (out_polygons);
|
|
db::PolygonGenerator out (pc, resolve_holes, min_coherence);
|
|
process (out, op);
|
|
}
|
|
|
|
void
|
|
ShapeProcessor::boolean (const std::vector<db::Shape> &in_a, const std::vector<db::CplxTrans> &trans_a,
|
|
const std::vector<db::Shape> &in_b, const std::vector<db::CplxTrans> &trans_b,
|
|
int mode, std::vector <db::Edge> &out_edges)
|
|
{
|
|
clear ();
|
|
|
|
size_t e = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in_a.begin (); i != in_a.end (); ++i) {
|
|
e += count_edges (*i);
|
|
}
|
|
for (std::vector<db::Shape>::const_iterator i = in_b.begin (); i != in_b.end (); ++i) {
|
|
e += count_edges (*i);
|
|
}
|
|
reserve (e + e / 4); // heuristic reserve for crossing points
|
|
|
|
size_t n;
|
|
n = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in_a.begin (); i != in_a.end (); ++i, ++n) {
|
|
if (n < trans_a.size ()) {
|
|
insert (*i, db::ICplxTrans (trans_a [n]), n * 2);
|
|
} else {
|
|
insert (*i, n * 2);
|
|
}
|
|
}
|
|
n = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in_b.begin (); i != in_b.end (); ++i, ++n) {
|
|
if (n < trans_b.size ()) {
|
|
insert (*i, db::ICplxTrans (trans_b [n]), n * 2 + 1);
|
|
} else {
|
|
insert (*i, n * 2 + 1);
|
|
}
|
|
}
|
|
|
|
db::BooleanOp op ((db::BooleanOp::BoolOp) mode);
|
|
db::EdgeContainer out (out_edges);
|
|
process (out, op);
|
|
}
|
|
|
|
void
|
|
ShapeProcessor::boolean (const std::vector<db::Shape> &in_a, const std::vector<db::CplxTrans> &trans_a,
|
|
const std::vector<db::Shape> &in_b, const std::vector<db::CplxTrans> &trans_b,
|
|
int mode, std::vector <db::Polygon> &out_polygons, bool resolve_holes, bool min_coherence)
|
|
{
|
|
clear ();
|
|
|
|
size_t e = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in_a.begin (); i != in_a.end (); ++i) {
|
|
e += count_edges (*i);
|
|
}
|
|
for (std::vector<db::Shape>::const_iterator i = in_b.begin (); i != in_b.end (); ++i) {
|
|
e += count_edges (*i);
|
|
}
|
|
reserve (e + e / 4); // heuristic reserve for crossing points
|
|
|
|
size_t n;
|
|
n = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in_a.begin (); i != in_a.end (); ++i, ++n) {
|
|
if (n < trans_a.size ()) {
|
|
insert (*i, db::ICplxTrans (trans_a [n]), n * 2);
|
|
} else {
|
|
insert (*i, n * 2);
|
|
}
|
|
}
|
|
n = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in_b.begin (); i != in_b.end (); ++i, ++n) {
|
|
if (n < trans_b.size ()) {
|
|
insert (*i, db::ICplxTrans (trans_b [n]), n * 2 + 1);
|
|
} else {
|
|
insert (*i, n * 2 + 1);
|
|
}
|
|
}
|
|
|
|
db::BooleanOp op ((db::BooleanOp::BoolOp) mode);
|
|
db::PolygonContainer pc (out_polygons);
|
|
db::PolygonGenerator out (pc, resolve_holes, min_coherence);
|
|
process (out, op);
|
|
}
|
|
|
|
void
|
|
ShapeProcessor::size (const std::vector<db::Shape> &in, const std::vector<db::CplxTrans> &trans,
|
|
db::Coord dx, db::Coord dy, std::vector <db::Polygon> &out, unsigned int mode, bool resolve_holes, bool min_coherence)
|
|
{
|
|
// 1st step: merge input
|
|
clear ();
|
|
|
|
size_t e = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in.begin (); i != in.end (); ++i) {
|
|
e += count_edges (*i);
|
|
}
|
|
reserve (e + e / 4); // heuristic reserve for crossing points
|
|
|
|
size_t n;
|
|
n = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in.begin (); i != in.end (); ++i, ++n) {
|
|
if (n < trans.size ()) {
|
|
insert (*i, db::ICplxTrans (trans [n]), n * 2);
|
|
} else {
|
|
insert (*i, n * 2);
|
|
}
|
|
}
|
|
|
|
// Merge the polygons and feed them into the sizing filter
|
|
#if ! defined(DEBUG_SIZE_INTERMEDIATE)
|
|
db::PolygonContainer pc (out);
|
|
db::PolygonGenerator pg2 (pc, resolve_holes, min_coherence);
|
|
db::SizingPolygonFilter siz (pg2, dx, dy, mode);
|
|
db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/);
|
|
db::BooleanOp op (db::BooleanOp::Or);
|
|
process (pg, op);
|
|
#else
|
|
// Intermediate output for debugging
|
|
db::PolygonContainer pc (out);
|
|
db::PolygonGenerator pg2 (pc, false, false);
|
|
db::BooleanOp op (db::BooleanOp::Or);
|
|
process (pg2, op);
|
|
for (std::vector <db::Polygon>::iterator p = out.begin (); p != out.end (); ++p) {
|
|
*p = p->sized (dx, dy, mode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
ShapeProcessor::size (const std::vector<db::Shape> &in, const std::vector<db::CplxTrans> &trans,
|
|
db::Coord dx, db::Coord dy, std::vector <db::Edge> &out, unsigned int mode)
|
|
{
|
|
// 1st step: merge input
|
|
clear ();
|
|
|
|
size_t e = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in.begin (); i != in.end (); ++i) {
|
|
e += count_edges (*i);
|
|
}
|
|
reserve (e + e / 4); // heuristic reserve for crossing points
|
|
|
|
size_t n;
|
|
n = 0;
|
|
for (std::vector<db::Shape>::const_iterator i = in.begin (); i != in.end (); ++i, ++n) {
|
|
if (n < trans.size ()) {
|
|
insert (*i, db::ICplxTrans (trans [n]), n * 2);
|
|
} else {
|
|
insert (*i, n * 2);
|
|
}
|
|
}
|
|
|
|
// Merge the polygons and feed them into the sizing filter
|
|
db::EdgeContainer ec (out);
|
|
db::SizingPolygonFilter siz (ec, dx, dy, mode);
|
|
db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/);
|
|
db::BooleanOp op (db::BooleanOp::Or);
|
|
process (pg, op);
|
|
}
|
|
|
|
void
|
|
ShapeProcessor::collect_shapes_hier (const db::CplxTrans &tr, const db::Layout &layout, const db::Cell &cell, unsigned int layer, int hier_levels, size_t &pn, size_t pdelta)
|
|
{
|
|
db::ICplxTrans tri (tr);
|
|
for (db::Shapes::shape_iterator s = cell.shapes (layer).begin (db::ShapeIterator::All); ! s.at_end (); ++s) {
|
|
insert (*s, tri, pn);
|
|
pn += pdelta;
|
|
}
|
|
|
|
if (hier_levels != 0) {
|
|
|
|
for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) {
|
|
for (db::CellInstArray::iterator a = i->begin (); ! a.at_end (); ++a) {
|
|
collect_shapes_hier (tr * i->complex_trans (*a), layout, layout.cell (i->cell_index ()), layer, hier_levels > 0 ? hier_levels - 1 : hier_levels, pn, pdelta);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
size_t
|
|
ShapeProcessor::count_edges_hier (const db::Layout &layout, const db::Cell &cell, unsigned int layer, std::map<std::pair<db::cell_index_type, int>, size_t> &cache, int hier_levels) const
|
|
{
|
|
std::map<std::pair<db::cell_index_type, int>, size_t>::iterator c = cache.find (std::make_pair (cell.cell_index (), hier_levels));
|
|
|
|
if (c != cache.end ()) {
|
|
return c->second;
|
|
} else {
|
|
|
|
size_t n = 0;
|
|
for (db::Shapes::shape_iterator s = cell.shapes (layer).begin (db::ShapeIterator::All); ! s.at_end (); ++s) {
|
|
n += count_edges (*s);
|
|
}
|
|
|
|
if (hier_levels != 0) {
|
|
for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) {
|
|
n += count_edges_hier (layout, layout.cell (i->cell_index ()), layer, cache, hier_levels > 0 ? hier_levels - 1 : hier_levels) * i->size ();
|
|
}
|
|
}
|
|
|
|
cache.insert (std::make_pair (std::make_pair (cell.cell_index (), hier_levels), n));
|
|
return n;
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
ShapeProcessor::boolean (const db::Layout &layout_in_a, const db::Cell &cell_in_a, const std::vector<unsigned int> &layers_in_a,
|
|
const db::Layout &layout_in_b, const db::Cell &cell_in_b, const std::vector<unsigned int> &layers_in_b,
|
|
db::Shapes &shapes, int mode, bool with_sub_hierarchy, bool resolve_holes, bool min_coherence)
|
|
{
|
|
double fa = 1.0, fb = 1.0;
|
|
if (shapes.cell () && shapes.cell ()->layout ()) {
|
|
double target_dbu = shapes.cell ()->layout ()->dbu ();
|
|
fa = layout_in_a.dbu () / target_dbu;
|
|
fb = layout_in_b.dbu () / target_dbu;
|
|
}
|
|
|
|
// count the edges so we know how much memory to reserve
|
|
size_t ne = 0;
|
|
std::map<std::pair<db::cell_index_type, int>, size_t> cache;
|
|
for (std::vector<unsigned int>::const_iterator l = layers_in_a.begin (); l != layers_in_a.end (); ++l) {
|
|
ne += count_edges_hier (layout_in_a, cell_in_a, *l, cache, with_sub_hierarchy ? -1 : 0);
|
|
cache.clear ();
|
|
}
|
|
for (std::vector<unsigned int>::const_iterator l = layers_in_b.begin (); l != layers_in_b.end (); ++l) {
|
|
ne += count_edges_hier (layout_in_b, cell_in_b, *l, cache, with_sub_hierarchy ? -1 : 0);
|
|
cache.clear ();
|
|
}
|
|
|
|
clear ();
|
|
reserve (ne + ne / 4); // heuristic reserve for crossing points
|
|
|
|
size_t pn;
|
|
|
|
// collect all shapes of layout a into property ID's 0, 2, 4, 6, ...
|
|
pn = 0;
|
|
for (std::vector<unsigned int>::const_iterator l = layers_in_a.begin (); l != layers_in_a.end (); ++l) {
|
|
collect_shapes_hier (db::CplxTrans (fa), layout_in_a, cell_in_a, *l, with_sub_hierarchy ? -1 : 0, pn, 2);
|
|
}
|
|
|
|
// collect all shapes of layout a into property ID's 1, 3, 5, 7, ...
|
|
pn = 1;
|
|
for (std::vector<unsigned int>::const_iterator l = layers_in_b.begin (); l != layers_in_b.end (); ++l) {
|
|
collect_shapes_hier (db::CplxTrans (fb), layout_in_b, cell_in_b, *l, with_sub_hierarchy ? -1 : 0, pn, 2);
|
|
}
|
|
|
|
db::BooleanOp op ((db::BooleanOp::BoolOp) mode);
|
|
db::ShapeGenerator sg (shapes, true /*clear shapes*/);
|
|
db::PolygonGenerator out (sg, resolve_holes, min_coherence);
|
|
process (out, op);
|
|
}
|
|
|
|
void
|
|
ShapeProcessor::size (const db::Layout &layout_in, const db::Cell &cell_in, const std::vector<unsigned int> &layers_in,
|
|
db::Shapes &out, db::Coord dx, db::Coord dy, unsigned int mode, bool with_sub_hierarchy, bool resolve_holes, bool min_coherence)
|
|
{
|
|
double f = 1.0;
|
|
if (out.cell () && out.cell ()->layout ()) {
|
|
double target_dbu = out.cell ()->layout ()->dbu ();
|
|
f = layout_in.dbu () / target_dbu;
|
|
}
|
|
|
|
// count the edges so we know how much memory to reserve
|
|
size_t ne = 0;
|
|
std::map<std::pair<db::cell_index_type, int>, size_t> cache;
|
|
for (std::vector<unsigned int>::const_iterator l = layers_in.begin (); l != layers_in.end (); ++l) {
|
|
ne += count_edges_hier (layout_in, cell_in, *l, cache, with_sub_hierarchy ? -1 : 0);
|
|
cache.clear ();
|
|
}
|
|
|
|
clear ();
|
|
reserve (ne + ne / 4); // heuristic reserve for crossing points
|
|
|
|
// collect all shapes of layout a into property ID's 0, 2, 4, 6, ... so we can use boolean OR
|
|
size_t pn = 0;
|
|
for (std::vector<unsigned int>::const_iterator l = layers_in.begin (); l != layers_in.end (); ++l) {
|
|
collect_shapes_hier (db::CplxTrans (f), layout_in, cell_in, *l, with_sub_hierarchy ? -1 : 0, pn, 2);
|
|
}
|
|
|
|
// 1st step: merge input
|
|
out.clear ();
|
|
|
|
// Merge the polygons and feed them into the sizing filter
|
|
#if ! defined(DEBUG_SIZE_INTERMEDIATE)
|
|
db::ShapeGenerator sg (out, true /*clear shapes*/);
|
|
db::PolygonGenerator pg2 (sg, resolve_holes, min_coherence);
|
|
db::SizingPolygonFilter siz (pg2, dx, dy, mode);
|
|
db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/);
|
|
db::BooleanOp op (db::BooleanOp::Or);
|
|
process (pg, op);
|
|
#else
|
|
// Intermediate output for debugging
|
|
db::ShapeGenerator sg (out, true /*clear shapes*/);
|
|
std::vector <db::Polygon> out2;
|
|
db::PolygonContainer pc (out2);
|
|
db::PolygonGenerator pg2 (pc, false, false);
|
|
db::BooleanOp op (db::BooleanOp::Or);
|
|
process (pg2, op);
|
|
sg.start ();
|
|
for (std::vector <db::Polygon>::iterator p = out2.begin (); p != out2.end (); ++p) {
|
|
sg.put (p->sized (dx, dy, mode));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
ShapeProcessor::merge (const db::Layout &layout_in, const db::Cell &cell_in, const std::vector<unsigned int> &layers_in,
|
|
db::Shapes &shapes, bool with_sub_hierarchy, unsigned int min_wc, bool resolve_holes, bool min_coherence)
|
|
{
|
|
double f = 1.0;
|
|
if (shapes.cell () && shapes.cell ()->layout ()) {
|
|
double target_dbu = shapes.cell ()->layout ()->dbu ();
|
|
f = layout_in.dbu () / target_dbu;
|
|
}
|
|
|
|
// count the edges so we know how much memory to reserve
|
|
size_t ne = 0;
|
|
std::map<std::pair<db::cell_index_type, int>, size_t> cache;
|
|
for (std::vector<unsigned int>::const_iterator l = layers_in.begin (); l != layers_in.end (); ++l) {
|
|
ne += count_edges_hier (layout_in, cell_in, *l, cache, with_sub_hierarchy ? -1 : 0);
|
|
cache.clear ();
|
|
}
|
|
|
|
clear ();
|
|
reserve (ne + ne / 4); // heuristic reserve for crossing points
|
|
|
|
// collect all shapes of layout a into property ID's 0, 1, 2, 3, ...
|
|
size_t pn = 0;
|
|
for (std::vector<unsigned int>::const_iterator l = layers_in.begin (); l != layers_in.end (); ++l) {
|
|
collect_shapes_hier (db::CplxTrans (f), layout_in, cell_in, *l, with_sub_hierarchy ? -1 : 0, pn, 1);
|
|
}
|
|
|
|
db::MergeOp op (min_wc);
|
|
db::ShapeGenerator sg (shapes, true /*clear shapes*/);
|
|
db::PolygonGenerator out (sg, resolve_holes, min_coherence);
|
|
process (out, op);
|
|
}
|
|
|
|
} // namespace db
|
|
|