diff --git a/scripts/extract_doc.rb b/scripts/extract_doc.rb index e57c82c88..8d8789958 100755 --- a/scripts/extract_doc.rb +++ b/scripts/extract_doc.rb @@ -72,6 +72,8 @@ class DocItem end end + self.name || raise("Doc block without name") + para && @paragraphs.push(para) end @@ -247,16 +249,25 @@ collector = Collector::new File.open($infile, "r") do |file| block = nil + line = 0 file.each_line do |l| - l = unescape(l) - if l =~ /^\s*#\s*#{$key}/ - block = [] - elsif l =~ /^\s*#\s*(.*)\s*$/ - block && block.push($1) - elsif l =~ /^\s*$/ - block && collector.add_block(block) - block = nil + + line += 1 + + begin + l = unescape(l) + if l =~ /^\s*#\s*#{$key}/ + block = [] + elsif l =~ /^\s*#\s*(.*)\s*$/ + block && block.push($1) + elsif l =~ /^\s*$/ + block && collector.add_block(block) + block = nil + end + rescue => ex + puts "ERROR in line #{line}:\n" + ex.to_s + exit 1 end end diff --git a/src/buddies/src/buddy_app.pri b/src/buddies/src/buddy_app.pri index d5bafd153..5168eb801 100644 --- a/src/buddies/src/buddy_app.pri +++ b/src/buddies/src/buddy_app.pri @@ -10,7 +10,7 @@ include($$PWD/../../app.pri) # On Mac OSX, these buddy tools have to be built as ordinary command line tools; # not as bundles (*.app) mac { - CONFIG -= app_bundle + CONFIG -= app_bundle } # Since the main function is entirely unspecific, we can put it into a common @@ -42,3 +42,8 @@ equals(HAVE_PYTHON, "1") { DEFINES += BD_TARGET=$$TARGET LIBS += $$RUBYLIBFILE + +if(mac|linux*) { + LIBS += -ldl +} + diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 90b03bf16..fd68bd081 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -119,7 +119,60 @@ SOURCES = \ gsiDeclDbVector.cc \ gsiDeclDbLayoutDiff.cc \ gsiDeclDbGlyphs.cc \ - dbConverters.cc + dbConverters.cc \ + dbAsIfFlatRegion.cc \ + dbEmptyRegion.cc \ + dbFlatRegion.cc \ + dbOriginalLayerRegion.cc \ + dbRegionDelegate.cc \ + dbEdgesDelegate.cc \ + dbEmptyEdges.cc \ + dbAsIfFlatEdges.cc \ + dbFlatEdges.cc \ + dbEdgeBoolean.cc \ + dbOriginalLayerEdges.cc \ + dbAsIfFlatEdgePairs.cc \ + dbEmptyEdgePairs.cc \ + dbFlatEdgePairs.cc \ + dbOriginalLayerEdgePairs.cc \ + dbEdgePairsDelegate.cc \ + dbDeepShapeStore.cc \ + dbHierarchyBuilder.cc \ + dbLocalOperation.cc \ + dbHierProcessor.cc \ + dbDeepRegion.cc \ + dbHierNetworkProcessor.cc \ + dbNetlist.cc \ + gsiDeclDbNetlist.cc \ + dbNetlistDeviceClasses.cc \ + dbNetlistDeviceExtractor.cc \ + dbNetlistExtractor.cc \ + gsiDeclDbNetlistDeviceClasses.cc \ + gsiDeclDbNetlistDeviceExtractor.cc \ + gsiDeclDbHierNetworkProcessor.cc \ + dbNetlistDeviceExtractorClasses.cc \ + dbLayoutToNetlist.cc \ + gsiDeclDbLayoutToNetlist.cc \ + dbCircuit.cc \ + dbDevice.cc \ + dbDeviceClass.cc \ + dbNet.cc \ + dbSubCircuit.cc \ + dbPin.cc \ + dbLayoutToNetlistReader.cc \ + dbLayoutToNetlistWriter.cc \ + dbLayoutToNetlistFormatDefs.cc \ + dbDeviceAbstract.cc \ + dbLocalOperationUtils.cc \ + gsiDeclDbDeepShapeStore.cc \ + dbNetlistSpiceWriter.cc \ + dbNetlistWriter.cc \ + dbCellVariants.cc \ + dbDeepEdges.cc \ + dbDeepEdgePairs.cc \ + dbRegionUtils.cc \ + dbEdgesUtils.cc \ + dbRegionProcessors.cc HEADERS = \ dbArray.h \ @@ -209,6 +262,54 @@ HEADERS = \ dbPlugin.h \ dbInit.h \ dbConverters.h \ + dbAsIfFlatRegion.h \ + dbEmptyRegion.h \ + dbFlatRegion.h \ + dbOriginalLayerRegion.h \ + dbRegionDelegate.h \ + dbEdgesDelegate.h \ + dbEmptyEdges.h \ + dbAsIfFlatEdges.h \ + dbFlatEdges.h \ + dbEdgeBoolean.h \ + dbOriginalLayerEdges.h \ + dbAsIfFlatEdgePairs.h \ + dbEmptyEdgePairs.h \ + dbFlatEdgePairs.h \ + dbOriginalLayerEdgePairs.h \ + dbEdgePairsDelegate.h \ + dbDeepShapeStore.h \ + dbHierarchyBuilder.h \ + dbLocalOperation.h \ + dbHierProcessor.h \ + dbNetlist.h \ + dbNetlistDeviceClasses.h \ + dbNetlistDeviceExtractor.h \ + dbNetlistExtractor.h \ + dbNetlistDeviceExtractorClasses.h \ + dbLayoutToNetlist.h \ + dbHierNetworkProcessor.h \ + dbNetlistUtils.h \ + dbNet.h \ + dbCircuit.h \ + dbDevice.h \ + dbDeviceClass.h \ + dbPin.h \ + dbSubCircuit.h \ + dbLayoutToNetlistReader.h \ + dbLayoutToNetlistWriter.h \ + dbLayoutToNetlistFormatDefs.h \ + dbDeviceAbstract.h \ + dbLocalOperationUtils.h \ + dbDeepRegion.h \ + dbNetlistSpiceWriter.h \ + dbNetlistWriter.h \ + dbCellVariants.h \ + dbDeepEdges.h \ + dbDeepEdgePairs.h \ + dbRegionUtils.h \ + dbEdgesUtils.h \ + dbRegionProcessors.h \ gsiDeclDbHelpers.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index 8d283e613..8e9f1b6e0 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -87,6 +87,17 @@ struct ArrayBase // .. nothing yet .. } + ArrayBase (const ArrayBase &) + : in_repository (false) + { + // .. nothing yet .. + } + + ArrayBase &operator= (const ArrayBase &) + { + return *this; + } + virtual ~ArrayBase () { // .. nothing yet .. diff --git a/src/db/db/dbAsIfFlatEdgePairs.cc b/src/db/db/dbAsIfFlatEdgePairs.cc new file mode 100644 index 000000000..0876cdc0d --- /dev/null +++ b/src/db/db/dbAsIfFlatEdgePairs.cc @@ -0,0 +1,300 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbAsIfFlatEdgePairs.h" +#include "dbFlatEdgePairs.h" +#include "dbFlatRegion.h" +#include "dbFlatEdges.h" +#include "dbEmptyEdgePairs.h" +#include "dbEdgePairs.h" +#include "dbBoxConvert.h" + +#include + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// AsIfFlagEdgePairs implementation + +AsIfFlatEdgePairs::AsIfFlatEdgePairs () + : EdgePairsDelegate (), m_bbox_valid (false) +{ + // .. nothing yet .. +} + +AsIfFlatEdgePairs::~AsIfFlatEdgePairs () +{ + // .. nothing yet .. +} + +std::string +AsIfFlatEdgePairs::to_string (size_t nmax) const +{ + std::ostringstream os; + EdgePairsIterator p (begin ()); + bool first = true; + for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) { + if (! first) { + os << ";"; + } + first = false; + os << p->to_string (); + } + if (! p.at_end ()) { + os << "..."; + } + return os.str (); +} + +EdgePairsDelegate * +AsIfFlatEdgePairs::in (const EdgePairs &other, bool invert) const +{ + std::set op; + for (EdgePairsIterator o (other.begin ()); ! o.at_end (); ++o) { + op.insert (*o); + } + + std::auto_ptr new_edge_pairs (new FlatEdgePairs (false)); + + for (EdgePairsIterator o (begin ()); ! o.at_end (); ++o) { + if ((op.find (*o) == op.end ()) == invert) { + new_edge_pairs->insert (*o); + } + } + + return new_edge_pairs.release (); +} + +size_t +AsIfFlatEdgePairs::size () const +{ + size_t n = 0; + for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { + ++n; + } + return n; +} + +Box AsIfFlatEdgePairs::bbox () const +{ + if (! m_bbox_valid) { + m_bbox = compute_bbox (); + m_bbox_valid = true; + } + return m_bbox; +} + +Box AsIfFlatEdgePairs::compute_bbox () const +{ + db::Box b; + for (EdgePairsIterator e (begin ()); ! e.at_end (); ++e) { + b += e->bbox (); + } + return b; +} + +void AsIfFlatEdgePairs::update_bbox (const db::Box &b) +{ + m_bbox = b; + m_bbox_valid = true; +} + +void AsIfFlatEdgePairs::invalidate_bbox () +{ + m_bbox_valid = false; +} + +EdgePairsDelegate * +AsIfFlatEdgePairs::filtered (const EdgePairFilterBase &filter) const +{ + std::auto_ptr new_edge_pairs (new FlatEdgePairs ()); + + for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + new_edge_pairs->insert (*p); + } + } + + return new_edge_pairs.release (); +} + +RegionDelegate * +AsIfFlatEdgePairs::polygons (db::Coord e) const +{ + std::auto_ptr output (new FlatRegion ()); + + for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) { + db::Polygon poly = ep->normalized ().to_polygon (e); + if (poly.vertices () >= 3) { + output->insert (poly); + } + } + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdgePairs::edges () const +{ + std::auto_ptr output (new FlatEdges ()); + + for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) { + output->insert (ep->first ()); + output->insert (ep->second ()); + } + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdgePairs::first_edges () const +{ + std::auto_ptr output (new FlatEdges ()); + + for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) { + output->insert (ep->first ()); + } + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdgePairs::second_edges () const +{ + std::auto_ptr output (new FlatEdges ()); + + for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) { + output->insert (ep->second ()); + } + + return output.release (); +} + +EdgePairsDelegate * +AsIfFlatEdgePairs::add (const EdgePairs &other) const +{ + FlatEdgePairs *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + std::auto_ptr new_edge_pairs (new FlatEdgePairs (*other_flat)); + new_edge_pairs->invalidate_cache (); + + size_t n = new_edge_pairs->raw_edge_pairs ().size () + size (); + + new_edge_pairs->reserve (n); + + for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { + new_edge_pairs->raw_edge_pairs ().insert (*p); + } + + return new_edge_pairs.release (); + + } else { + + std::auto_ptr new_edge_pairs (new FlatEdgePairs ()); + + size_t n = size () + other.size (); + + new_edge_pairs->reserve (n); + + for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { + new_edge_pairs->raw_edge_pairs ().insert (*p); + } + for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { + new_edge_pairs->raw_edge_pairs ().insert (*p); + } + + return new_edge_pairs.release (); + + } +} + +bool +AsIfFlatEdgePairs::equals (const EdgePairs &other) const +{ + if (empty () != other.empty ()) { + return false; + } + if (size () != other.size ()) { + return false; + } + EdgePairsIterator o1 (begin ()); + EdgePairsIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return false; + } + ++o1; + ++o2; + } + return true; +} + +bool +AsIfFlatEdgePairs::less (const EdgePairs &other) const +{ + if (empty () != other.empty ()) { + return empty () < other.empty (); + } + if (size () != other.size ()) { + return (size () < other.size ()); + } + EdgePairsIterator o1 (begin ()); + EdgePairsIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return *o1 < *o2; + } + ++o1; + ++o2; + } + return false; +} + +void +AsIfFlatEdgePairs::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + // improves performance when inserting an original layout into the same layout + db::LayoutLocker locker (layout); + + db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer); + for (EdgePairsIterator e (begin ()); ! e.at_end (); ++e) { + shapes.insert (*e); + } +} + +void +AsIfFlatEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const +{ + // improves performance when inserting an original layout into the same layout + db::LayoutLocker locker (layout); + + db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer); + for (EdgePairsIterator e (begin ()); ! e.at_end (); ++e) { + shapes.insert (e->normalized ().to_simple_polygon (enl)); + } +} + +} + diff --git a/src/db/db/dbAsIfFlatEdgePairs.h b/src/db/db/dbAsIfFlatEdgePairs.h new file mode 100644 index 000000000..b8a292f44 --- /dev/null +++ b/src/db/db/dbAsIfFlatEdgePairs.h @@ -0,0 +1,90 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbAsIfFlatEdgePairs +#define HDR_dbAsIfFlatEdgePairs + +#include "dbCommon.h" + +#include "dbEdgePairsDelegate.h" + +namespace db { + +/** + * @brief Provides default flat implementations + */ +class DB_PUBLIC AsIfFlatEdgePairs + : public EdgePairsDelegate +{ +public: + AsIfFlatEdgePairs (); + virtual ~AsIfFlatEdgePairs (); + + virtual size_t size () const; + virtual std::string to_string (size_t) const; + virtual Box bbox () const; + + virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter) + { + return filtered (filter); + } + + virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const; + + virtual EdgePairsDelegate *add_in_place (const EdgePairs &other) + { + return add (other); + } + + virtual EdgePairsDelegate *add (const EdgePairs &other) const; + + virtual RegionDelegate *polygons (db::Coord e) const; + virtual EdgesDelegate *edges () const; + virtual EdgesDelegate *first_edges () const; + virtual EdgesDelegate *second_edges () const; + + virtual EdgePairsDelegate *in (const EdgePairs &, bool) const; + + virtual bool equals (const EdgePairs &other) const; + virtual bool less (const EdgePairs &other) const; + + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + virtual void insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const; + +protected: + void update_bbox (const db::Box &box); + void invalidate_bbox (); + +private: + AsIfFlatEdgePairs &operator= (const AsIfFlatEdgePairs &other); + + mutable bool m_bbox_valid; + mutable db::Box m_bbox; + + virtual db::Box compute_bbox () const; +}; + +} + +#endif + diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc new file mode 100644 index 000000000..4e5e03581 --- /dev/null +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -0,0 +1,627 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbAsIfFlatEdges.h" +#include "dbFlatEdges.h" +#include "dbFlatEdgePairs.h" +#include "dbFlatRegion.h" +#include "dbEmptyEdges.h" +#include "dbEdges.h" +#include "dbEdgesUtils.h" +#include "dbEdgeBoolean.h" +#include "dbBoxConvert.h" +#include "dbRegion.h" +#include "dbFlatRegion.h" +#include "dbPolygonTools.h" +#include "dbShapeProcessor.h" +#include "dbEdgeProcessor.h" +#include "dbPolygonGenerators.h" +#include "dbPolygon.h" +#include "dbPath.h" + +#include + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// AsIfFlagEdges implementation + +AsIfFlatEdges::AsIfFlatEdges () + : EdgesDelegate (), m_bbox_valid (false) +{ + // .. nothing yet .. +} + +AsIfFlatEdges::~AsIfFlatEdges () +{ + // .. nothing yet .. +} + +std::string +AsIfFlatEdges::to_string (size_t nmax) const +{ + std::ostringstream os; + EdgesIterator p (begin ()); + bool first = true; + for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) { + if (! first) { + os << ";"; + } + first = false; + os << p->to_string (); + } + if (! p.at_end ()) { + os << "..."; + } + return os.str (); +} + +EdgesDelegate * +AsIfFlatEdges::selected_interacting_generic (const Region &other, bool inverse) const +{ + // shortcuts + if (other.empty () || empty ()) { + return new EmptyEdges (); + } + + db::box_scanner2 scanner (report_progress (), progress_desc ()); + + AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ()); + + for ( ; ! e.at_end (); ++e) { + scanner.insert1 (e.operator-> (), 0); + } + + AddressablePolygonDelivery p = other.addressable_polygons (); + + for ( ; ! p.at_end (); ++p) { + scanner.insert2 (p.operator-> (), 1); + } + + std::auto_ptr output (new FlatEdges (true)); + + if (! inverse) { + + edge_to_region_interaction_filter filter (*output); + scanner.process (filter, 1, db::box_convert (), db::box_convert ()); + + } else { + + std::set interacting; + edge_to_region_interaction_filter > filter (interacting); + scanner.process (filter, 1, db::box_convert (), db::box_convert ()); + + for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { + if (interacting.find (*o) == interacting.end ()) { + output->insert (*o); + } + } + + } + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdges::selected_interacting (const Region &other) const +{ + return selected_interacting_generic (other, false); +} + +EdgesDelegate * +AsIfFlatEdges::selected_not_interacting (const Region &other) const +{ + return selected_interacting_generic (other, true); +} + +namespace +{ + +struct JoinEdgesClusterCollector + : public db::cluster_collector +{ + typedef db::Edge::coord_type coord_type; + + JoinEdgesClusterCollector (db::PolygonSink *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i) + : db::cluster_collector (JoinEdgesCluster (output, ext_b, ext_e, ext_o, ext_i), true) + { + // .. nothing yet .. + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + if (o1->p2 () == o2->p1 () || o1->p1 () == o2->p2 ()) { + db::cluster_collector::add (o1, p1, o2, p2); + } + } +}; + +} + +RegionDelegate * +AsIfFlatEdges::extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const +{ + if (join) { + + std::auto_ptr output (new FlatRegion ()); + db::ShapeGenerator sg (output->raw_polygons (), false); + JoinEdgesClusterCollector cluster_collector (&sg, ext_b, ext_e, ext_o, ext_i); + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size ()); + + AddressableEdgeDelivery e (begin (), has_valid_edges ()); + + size_t n = 0; + for ( ; ! e.at_end (); ++e) { + scanner.insert (e.operator-> (), n); + ++n; + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + return output.release (); + + } else { + + std::auto_ptr output (new FlatRegion ()); + for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) { + output->insert (extended_edge (*e, ext_b, ext_e, ext_o, ext_i)); + } + + return output.release (); + + } +} + +EdgesDelegate * +AsIfFlatEdges::selected_interacting_generic (const Edges &edges, bool inverse) const +{ + db::box_scanner scanner (report_progress (), progress_desc ()); + + AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ()); + + for ( ; ! e.at_end (); ++e) { + scanner.insert (e.operator-> (), 0); + } + + AddressableEdgeDelivery ee = edges.addressable_edges (); + + for ( ; ! ee.at_end (); ++ee) { + scanner.insert (ee.operator-> (), 1); + } + + std::auto_ptr output (new FlatEdges (true)); + + if (! inverse) { + + edge_interaction_filter filter (*output); + scanner.process (filter, 1, db::box_convert ()); + + } else { + + std::set interacting; + edge_interaction_filter > filter (interacting); + scanner.process (filter, 1, db::box_convert ()); + + for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { + if (interacting.find (*o) == interacting.end ()) { + output->insert (*o); + } + } + + } + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdges::selected_interacting (const Edges &other) const +{ + return selected_interacting_generic (other, false); +} + +EdgesDelegate * +AsIfFlatEdges::selected_not_interacting (const Edges &other) const +{ + return selected_interacting_generic (other, true); +} + +EdgesDelegate * +AsIfFlatEdges::in (const Edges &other, bool invert) const +{ + std::set op; + for (EdgesIterator o (other.begin_merged ()); ! o.at_end (); ++o) { + op.insert (*o); + } + + std::auto_ptr new_region (new FlatEdges (false)); + + for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) { + if ((op.find (*o) == op.end ()) == invert) { + new_region->insert (*o); + } + } + + return new_region.release (); +} + +size_t +AsIfFlatEdges::size () const +{ + size_t n = 0; + for (EdgesIterator p (begin ()); ! p.at_end (); ++p) { + ++n; + } + return n; +} + +AsIfFlatEdges::length_type +AsIfFlatEdges::length (const db::Box &box) const +{ + distance_type l = 0; + + for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) { + + if (box.empty () || (box.contains (e->p1 ()) && box.contains (e->p2 ()))) { + l += e->length (); + } else { + + std::pair ce = e->clipped (box); + if (ce.first) { + + db::Coord dx = ce.second.dx (); + db::Coord dy = ce.second.dy (); + db::Coord x = ce.second.p1 ().x (); + db::Coord y = ce.second.p1 ().y (); + if ((dx == 0 && x == box.left () && dy < 0) || + (dx == 0 && x == box.right () && dy > 0) || + (dy == 0 && y == box.top () && dx < 0) || + (dy == 0 && y == box.bottom () && dx > 0)) { + // not counted -> box is at outside side of the edge + } else { + l += ce.second.length (); + } + + } + + } + + } + + return l; +} + +Box AsIfFlatEdges::bbox () const +{ + if (! m_bbox_valid) { + m_bbox = compute_bbox (); + m_bbox_valid = true; + } + return m_bbox; +} + +Box AsIfFlatEdges::compute_bbox () const +{ + db::Box b; + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + b += e->bbox (); + } + return b; +} + +void AsIfFlatEdges::update_bbox (const db::Box &b) +{ + m_bbox = b; + m_bbox_valid = true; +} + +void AsIfFlatEdges::invalidate_bbox () +{ + m_bbox_valid = false; +} + +EdgesDelegate * +AsIfFlatEdges::processed (const EdgeProcessorBase &filter) const +{ + std::auto_ptr edges (new FlatEdges ()); + + if (filter.result_must_not_be_merged ()) { + edges->set_merged_semantics (false); + } + + std::vector res_edges; + + for (EdgesIterator e (filter.requires_raw_input () ? begin () : begin_merged ()); ! e.at_end (); ++e) { + res_edges.clear (); + filter.process (*e, res_edges); + for (std::vector::const_iterator er = res_edges.begin (); er != res_edges.end (); ++er) { + edges->insert (*er); + } + } + + return edges.release (); +} + +EdgePairsDelegate * +AsIfFlatEdges::processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &filter) const +{ + std::auto_ptr edge_pairs (new FlatEdgePairs ()); + + if (filter.result_must_not_be_merged ()) { + edge_pairs->set_merged_semantics (false); + } + + std::vector res_edge_pairs; + + for (EdgesIterator e (filter.requires_raw_input () ? begin () : begin_merged ()); ! e.at_end (); ++e) { + res_edge_pairs.clear (); + filter.process (*e, res_edge_pairs); + for (std::vector::const_iterator epr = res_edge_pairs.begin (); epr != res_edge_pairs.end (); ++epr) { + edge_pairs->insert (*epr); + } + } + + return edge_pairs.release (); +} + +RegionDelegate * +AsIfFlatEdges::processed_to_polygons (const EdgeToPolygonProcessorBase &filter) const +{ + std::auto_ptr region (new FlatRegion ()); + + if (filter.result_must_not_be_merged ()) { + region->set_merged_semantics (false); + } + + std::vector res_polygons; + + for (EdgesIterator e (filter.requires_raw_input () ? begin () : begin_merged ()); ! e.at_end (); ++e) { + res_polygons.clear (); + filter.process (*e, res_polygons); + for (std::vector::const_iterator pr = res_polygons.begin (); pr != res_polygons.end (); ++pr) { + region->insert (*pr); + } + } + + return region.release (); +} + +EdgesDelegate * +AsIfFlatEdges::filtered (const EdgeFilterBase &filter) const +{ + std::auto_ptr new_region (new FlatEdges ()); + + for (EdgesIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + new_region->insert (*p); + } + } + + return new_region.release (); +} + +EdgePairsDelegate * +AsIfFlatEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + std::auto_ptr result (new FlatEdgePairs ()); + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + (other ? other->size () : 0)); + + AddressableEdgeDelivery e (begin_merged (), has_valid_edges ()); + + size_t n = 0; + for ( ; ! e.at_end (); ++e) { + scanner.insert (e.operator-> (), n); + n += 2; + } + + AddressableEdgeDelivery ee; + + if (other) { + ee = other->addressable_merged_edges (); + n = 1; + for ( ; ! ee.at_end (); ++ee) { + scanner.insert (ee.operator-> (), n); + n += 2; + } + } + + EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (other != 0); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + edge2edge_check_for_edges edge_check (check, *result, other != 0); + scanner.process (edge_check, d, db::box_convert ()); + + return result.release (); +} + +EdgesDelegate * +AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const +{ + std::auto_ptr output (new FlatEdges (true)); + EdgeBooleanClusterCollector cluster_collector (&output->raw_edges (), op); + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + (other ? other->size () : 0)); + + AddressableEdgeDelivery e (begin (), has_valid_edges ()); + + for ( ; ! e.at_end (); ++e) { + if (! e->is_degenerate ()) { + scanner.insert (e.operator-> (), 0); + } + } + + AddressableEdgeDelivery ee; + + if (other) { + ee = other->addressable_edges (); + for ( ; ! ee.at_end (); ++ee) { + if (! ee->is_degenerate ()) { + scanner.insert (ee.operator-> (), 1); + } + } + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdges::edge_region_op (const Region &other, bool outside, bool include_borders) const +{ + // shortcuts + if (other.empty ()) { + if (! outside) { + return new EmptyEdges (); + } else { + return clone (); + } + } else if (empty ()) { + return new EmptyEdges (); + } + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + for (db::Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { + if (p->box ().touches (bbox ())) { + ep.insert (*p, 0); + } + } + + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + ep.insert (*e, 1); + } + + std::auto_ptr output (new FlatEdges (false)); + db::EdgeShapeGenerator cc (output->raw_edges (), true /*clear*/); + db::EdgePolygonOp op (outside, include_borders); + ep.process (cc, op); + + return output.release (); +} + +EdgesDelegate * +AsIfFlatEdges::add (const Edges &other) const +{ + FlatEdges *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + std::auto_ptr new_edges (new FlatEdges (*other_flat)); + new_edges->set_is_merged (false); + new_edges->invalidate_cache (); + + size_t n = new_edges->raw_edges ().size () + size (); + + new_edges->reserve (n); + + for (EdgesIterator p (begin ()); ! p.at_end (); ++p) { + new_edges->raw_edges ().insert (*p); + } + + return new_edges.release (); + + } else { + + std::auto_ptr new_edges (new FlatEdges (false /*not merged*/)); + + size_t n = size () + other.size (); + + new_edges->reserve (n); + + for (EdgesIterator p (begin ()); ! p.at_end (); ++p) { + new_edges->raw_edges ().insert (*p); + } + for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) { + new_edges->raw_edges ().insert (*p); + } + + return new_edges.release (); + + } +} + +bool +AsIfFlatEdges::equals (const Edges &other) const +{ + if (empty () != other.empty ()) { + return false; + } + if (size () != other.size ()) { + return false; + } + EdgesIterator o1 (begin ()); + EdgesIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return false; + } + ++o1; + ++o2; + } + return true; +} + +bool +AsIfFlatEdges::less (const Edges &other) const +{ + if (empty () != other.empty ()) { + return empty () < other.empty (); + } + if (size () != other.size ()) { + return (size () < other.size ()); + } + EdgesIterator o1 (begin ()); + EdgesIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return *o1 < *o2; + } + ++o1; + ++o2; + } + return false; +} + +void +AsIfFlatEdges::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + // improves performance when inserting an original layout into the same layout + db::LayoutLocker locker (layout); + + db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer); + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + shapes.insert (*e); + } +} + +} + diff --git a/src/db/db/dbAsIfFlatEdges.h b/src/db/db/dbAsIfFlatEdges.h new file mode 100644 index 000000000..ffc1150b7 --- /dev/null +++ b/src/db/db/dbAsIfFlatEdges.h @@ -0,0 +1,193 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbAsIfFlatEdges +#define HDR_dbAsIfFlatEdges + +#include "dbCommon.h" +#include "dbBoxScanner.h" +#include "dbEdgesDelegate.h" +#include "dbBoxScanner.h" +#include "dbPolygonTools.h" + +#include +#include + +namespace db { + +class PolygonSink; + +/** + * @brief Provides default flat implementations + */ +class DB_PUBLIC AsIfFlatEdges + : public EdgesDelegate +{ +public: + AsIfFlatEdges (); + virtual ~AsIfFlatEdges (); + + virtual size_t size () const; + virtual std::string to_string (size_t) const; + virtual distance_type length (const db::Box &) const; + virtual Box bbox () const; + + virtual EdgePairsDelegate *width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::WidthRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairsDelegate *space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairsDelegate *enclosing_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::OverlapRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairsDelegate *overlap_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::WidthRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairsDelegate *separation_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairsDelegate *inside_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::InsideRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgesDelegate *process_in_place (const EdgeProcessorBase &filter) + { + return processed (filter); + } + + virtual EdgesDelegate *processed (const EdgeProcessorBase &filter) const; + virtual EdgePairsDelegate *processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &) const; + virtual RegionDelegate *processed_to_polygons (const EdgeToPolygonProcessorBase &) const; + + virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter) + { + return filtered (filter); + } + + virtual EdgesDelegate *filtered (const EdgeFilterBase &) const; + + virtual EdgesDelegate *merged_in_place () + { + return merged (); + } + + virtual EdgesDelegate *merged () const + { + return boolean (0, EdgeOr); + } + + virtual EdgesDelegate *and_with (const Edges &other) const + { + return boolean (&other, EdgeAnd); + } + + virtual EdgesDelegate *and_with (const Region &other) const + { + return edge_region_op (other, false /*inside*/, true /*include borders*/); + } + + virtual EdgesDelegate *not_with (const Edges &other) const + { + return boolean (&other, EdgeNot); + } + + virtual EdgesDelegate *not_with (const Region &other) const + { + return edge_region_op (other, true /*outside*/, true /*include borders*/); + } + + virtual EdgesDelegate *xor_with (const Edges &other) const + { + return boolean (&other, EdgeXor); + } + + virtual EdgesDelegate *or_with (const Edges &other) const + { + return boolean (&other, EdgeOr); + } + + virtual EdgesDelegate *add_in_place (const Edges &other) + { + return add (other); + } + + virtual EdgesDelegate *add (const Edges &other) const; + + virtual EdgesDelegate *inside_part (const Region &other) const + { + return edge_region_op (other, false /*inside*/, false /*don't include borders*/); + } + + virtual EdgesDelegate *outside_part (const Region &other) const + { + return edge_region_op (other, true /*outside*/, false /*don't include borders*/); + } + + virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const; + + virtual EdgesDelegate *selected_interacting (const Edges &) const; + virtual EdgesDelegate *selected_not_interacting (const Edges &) const; + virtual EdgesDelegate *selected_interacting (const Region &) const; + virtual EdgesDelegate *selected_not_interacting (const Region &) const; + + virtual EdgesDelegate *in (const Edges &, bool) const; + + virtual bool equals (const Edges &other) const; + virtual bool less (const Edges &other) const; + + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + +protected: + void update_bbox (const db::Box &box); + void invalidate_bbox (); + EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, bool inverse) const; + virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, bool inverse) const; + +private: + AsIfFlatEdges &operator= (const AsIfFlatEdges &other); + + mutable bool m_bbox_valid; + mutable db::Box m_bbox; + + virtual db::Box compute_bbox () const; + EdgesDelegate *boolean (const Edges *other, EdgeBoolOp op) const; + EdgesDelegate *edge_region_op (const Region &other, bool outside, bool include_borders) const; +}; + +} + +#endif + diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc new file mode 100644 index 000000000..a13e49823 --- /dev/null +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -0,0 +1,1157 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbAsIfFlatRegion.h" +#include "dbFlatRegion.h" +#include "dbFlatEdgePairs.h" +#include "dbFlatEdges.h" +#include "dbEmptyRegion.h" +#include "dbEmptyEdgePairs.h" +#include "dbRegion.h" +#include "dbRegionUtils.h" +#include "dbShapeProcessor.h" +#include "dbBoxConvert.h" +#include "dbBoxScanner.h" +#include "dbClip.h" +#include "dbPolygonTools.h" + +#include + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// AsIfFlagRegion implementation + +AsIfFlatRegion::AsIfFlatRegion () + : RegionDelegate (), m_bbox_valid (false) +{ + // .. nothing yet .. +} + +AsIfFlatRegion::~AsIfFlatRegion () +{ + // .. nothing yet .. +} + +std::string +AsIfFlatRegion::to_string (size_t nmax) const +{ + std::ostringstream os; + RegionIterator p (begin ()); + bool first = true; + for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) { + if (! first) { + os << ";"; + } + first = false; + os << p->to_string (); + } + if (! p.at_end ()) { + os << "..."; + } + return os.str (); +} + +EdgesDelegate * +AsIfFlatRegion::edges (const EdgeFilterBase *filter) const +{ + std::auto_ptr result (new FlatEdges ()); + + size_t n = 0; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + result->reserve (n); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + if (! filter || filter->selected (*e)) { + result->insert (*e); + } + } + } + + return result.release (); +} + +RegionDelegate * +AsIfFlatRegion::in (const Region &other, bool invert) const +{ + std::set op; + for (RegionIterator o (other.begin_merged ()); ! o.at_end (); ++o) { + op.insert (*o); + } + + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator o (begin_merged ()); ! o.at_end (); ++o) { + if ((op.find (*o) == op.end ()) == invert) { + new_region->insert (*o); + } + } + + return new_region.release (); +} + +size_t +AsIfFlatRegion::size () const +{ + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + ++n; + } + return n; +} + +bool +AsIfFlatRegion::is_box () const +{ + RegionIterator p (begin ()); + if (p.at_end ()) { + return false; + } else { + const db::Polygon &poly = *p; + ++p; + if (! p.at_end ()) { + return false; + } else { + return poly.is_box (); + } + } +} + +AsIfFlatRegion::area_type +AsIfFlatRegion::area (const db::Box &box) const +{ + area_type a = 0; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (box.empty () || p->box ().inside (box)) { + a += p->area (); + } else { + std::vector clipped; + clip_poly (*p, box, clipped); + for (std::vector::const_iterator c = clipped.begin (); c != clipped.end (); ++c) { + a += c->area (); + } + } + } + + return a; +} + +AsIfFlatRegion::perimeter_type +AsIfFlatRegion::perimeter (const db::Box &box) const +{ + perimeter_type d = 0; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + + if (box.empty () || p->box ().inside (box)) { + d += p->perimeter (); + } else { + + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + + if (box.empty ()) { + d += (*e).length (); + } else { + + std::pair ce = (*e).clipped (box); + if (ce.first) { + + db::Coord dx = ce.second.dx (); + db::Coord dy = ce.second.dy (); + db::Coord x = ce.second.p1 ().x (); + db::Coord y = ce.second.p1 ().y (); + if ((dx == 0 && x == box.left () && dy < 0) || + (dx == 0 && x == box.right () && dy > 0) || + (dy == 0 && y == box.top () && dx < 0) || + (dy == 0 && y == box.bottom () && dx > 0)) { + // not counted -> box is at outside side of the edge + } else { + d += ce.second.length (); + } + + } + + } + + } + + } + + } + + return d; +} + +Box AsIfFlatRegion::bbox () const +{ + if (! m_bbox_valid) { + m_bbox = compute_bbox (); + m_bbox_valid = true; + } + return m_bbox; +} + +Box AsIfFlatRegion::compute_bbox () const +{ + db::Box b; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + b += p->box (); + } + return b; +} + +void AsIfFlatRegion::update_bbox (const db::Box &b) +{ + m_bbox = b; + m_bbox_valid = true; +} + +void AsIfFlatRegion::invalidate_bbox () +{ + m_bbox_valid = false; +} + +RegionDelegate * +AsIfFlatRegion::filtered (const PolygonFilterBase &filter) const +{ + std::auto_ptr new_region (new FlatRegion ()); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + new_region->insert (*p); + } + } + + return new_region.release (); +} + +RegionDelegate * +AsIfFlatRegion::processed (const PolygonProcessorBase &filter) const +{ + std::auto_ptr new_region (new FlatRegion ()); + if (filter.result_must_not_be_merged ()) { + new_region->set_merged_semantics (false); + } + + std::vector poly_res; + + for (RegionIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) { + + poly_res.clear (); + filter.process (*p, poly_res); + for (std::vector::const_iterator pr = poly_res.begin (); pr != poly_res.end (); ++pr) { + new_region->insert (*pr); + } + + } + + return new_region.release (); +} + +EdgesDelegate * +AsIfFlatRegion::processed_to_edges (const PolygonToEdgeProcessorBase &filter) const +{ + std::auto_ptr new_edges (new FlatEdges ()); + if (filter.result_must_not_be_merged ()) { + new_edges->set_merged_semantics (false); + } + + std::vector edge_res; + + for (RegionIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) { + + edge_res.clear (); + filter.process (*p, edge_res); + for (std::vector::const_iterator er = edge_res.begin (); er != edge_res.end (); ++er) { + new_edges->insert (*er); + } + + } + + return new_edges.release (); +} + +EdgePairsDelegate * +AsIfFlatRegion::processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &filter) const +{ + std::auto_ptr new_edge_pairs (new FlatEdgePairs ()); + if (filter.result_must_not_be_merged ()) { + new_edge_pairs->set_merged_semantics (false); + } + + std::vector edge_pair_res; + + for (RegionIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) { + + edge_pair_res.clear (); + filter.process (*p, edge_pair_res); + for (std::vector::const_iterator epr = edge_pair_res.begin (); epr != edge_pair_res.end (); ++epr) { + new_edge_pairs->insert (*epr); + } + + } + + return new_edge_pairs.release (); +} + +RegionDelegate * +AsIfFlatRegion::selected_interacting_generic (const Edges &other, bool inverse) const +{ + if (other.empty ()) { + if (! inverse) { + return new EmptyRegion (); + } else { + return clone (); + } + } else if (empty ()) { + return clone (); + } + + db::box_scanner2 scanner (report_progress (), progress_desc ()); + scanner.reserve1 (size ()); + scanner.reserve2 (other.size ()); + + std::auto_ptr output (new FlatRegion (false)); + region_to_edge_interaction_filter filter (output->raw_polygons (), inverse); + + AddressablePolygonDelivery p (begin_merged (), has_valid_merged_polygons ()); + + for ( ; ! p.at_end (); ++p) { + scanner.insert1 (p.operator-> (), 0); + if (inverse) { + filter.preset (p.operator-> ()); + } + } + + AddressableEdgeDelivery e (other.addressable_edges ()); + + for ( ; ! e.at_end (); ++e) { + scanner.insert2 (e.operator-> (), 0); + } + + scanner.process (filter, 1, db::box_convert (), db::box_convert ()); + + if (inverse) { + filter.fill_output (); + } + + return output.release (); +} + +RegionDelegate * +AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const +{ + db::EdgeProcessor ep (report_progress (), progress_desc ()); + ep.set_base_verbosity (base_verbosity ()); + + // shortcut + if (empty ()) { + return clone (); + } else if (other.empty ()) { + // clear, if b is empty and + // * mode is inside or interacting and inverse is false ("inside" or "interacting") + // * mode is outside and inverse is true ("not outside") + if ((mode <= 0) != inverse) { + return new EmptyRegion (); + } else { + return clone (); + } + } + + for (RegionIterator p = other.begin (); ! p.at_end (); ++p) { + if (p->box ().touches (bbox ())) { + ep.insert (*p, 0); + } + } + + size_t n = 1; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p, ++n) { + if (mode > 0 || p->box ().touches (other.bbox ())) { + ep.insert (*p, n); + } + } + + db::InteractionDetector id (mode, 0); + id.set_include_touching (touching); + db::EdgeSink es; + ep.process (es, id); + id.finish (); + + std::auto_ptr output (new FlatRegion (false)); + + n = 0; + std::set selected; + for (db::InteractionDetector::iterator i = id.begin (); i != id.end () && i->first == 0; ++i) { + ++n; + selected.insert (i->second); + } + + output->reserve (n); + + n = 1; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p, ++n) { + if ((selected.find (n) == selected.end ()) == inverse) { + output->raw_polygons ().insert (*p); + } + } + + return output.release (); +} + +template +void +AsIfFlatRegion::produce_markers_for_grid_check (const db::Polygon &poly, const Trans &tr, db::Coord gx, db::Coord gy, db::Shapes &shapes) +{ + gx = std::max (db::Coord (1), gx); + gy = std::max (db::Coord (1), gy); + + for (size_t i = 0; i < poly.holes () + 1; ++i) { + + db::Polygon::polygon_contour_iterator b, e; + + if (i == 0) { + b = poly.begin_hull (); + e = poly.end_hull (); + } else { + b = poly.begin_hole ((unsigned int) (i - 1)); + e = poly.end_hole ((unsigned int) (i - 1)); + } + + for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { + db::Point p = tr * *pt; + if ((p.x () % gx) != 0 || (p.y () % gy) != 0) { + shapes.insert (EdgePair (db::Edge (p, p), db::Edge (p, p))); + } + } + + } +} + +template void AsIfFlatRegion::produce_markers_for_grid_check (const db::Polygon &poly, const db::ICplxTrans &tr, db::Coord gx, db::Coord gy, db::Shapes &shapes); +template void AsIfFlatRegion::produce_markers_for_grid_check (const db::Polygon &poly, const db::UnitTrans &tr, db::Coord gx, db::Coord gy, db::Shapes &shapes); + +EdgePairsDelegate * +AsIfFlatRegion::grid_check (db::Coord gx, db::Coord gy) const +{ + if (gx < 0 || gy < 0) { + throw tl::Exception (tl::to_string (tr ("Grid check requires a positive grid value"))); + } + + if (gx == 0 && gy == 0) { + return new EmptyEdgePairs (); + } + + std::auto_ptr res (new db::FlatEdgePairs ()); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + produce_markers_for_grid_check (*p, db::UnitTrans (), gx, gy, res->raw_edge_pairs ()); + } + + return res.release (); +} + +static bool ac_less (double cos_a, bool gt180_a, double cos_b, bool gt180_b) +{ + if (gt180_a != gt180_b) { + return gt180_a < gt180_b; + } else { + if (gt180_a) { + return cos_a < cos_b - 1e-10; + } else { + return cos_a > cos_b + 1e-10; + } + } +} + +template +void +AsIfFlatRegion::produce_markers_for_angle_check (const db::Polygon &poly, const Trans &tr, double min, double max, bool inverse, db::Shapes &shapes) +{ + double cos_min = cos (std::max (0.0, std::min (360.0, min)) / 180.0 * M_PI); + double cos_max = cos (std::max (0.0, std::min (360.0, max)) / 180.0 * M_PI); + bool gt180_min = min > 180.0; + bool gt180_max = max > 180.0; + + for (size_t i = 0; i < poly.holes () + 1; ++i) { + + const db::Polygon::contour_type *h = 0; + if (i == 0) { + h = &poly.hull (); + } else { + h = &poly.hole ((unsigned int) (i - 1)); + } + + size_t np = h->size (); + + for (size_t j = 0; j < np; ++j) { + + db::Edge e ((*h) [j], (*h) [(j + 1) % np]); + e.transform (tr); + db::Edge ee (e.p2 (), (*h) [(j + 2) % np]); + ee.transform (tr); + + double le = e.double_length (); + double lee = ee.double_length (); + + double cos_a = -db::sprod (e, ee) / (le * lee); + bool gt180_a = db::vprod_sign (e, ee) > 0; + + if ((ac_less (cos_a, gt180_a, cos_max, gt180_max) && !ac_less (cos_a, gt180_a, cos_min, gt180_min)) == !inverse) { + shapes.insert (EdgePair (e, ee)); + } + + } + + } +} + +template void AsIfFlatRegion::produce_markers_for_angle_check (const db::Polygon &poly, const db::ICplxTrans &tr, double min, double max, bool inverse, db::Shapes &shapes); +template void AsIfFlatRegion::produce_markers_for_angle_check (const db::Polygon &poly, const db::UnitTrans &tr, double min, double max, bool inverse, db::Shapes &shapes); + +EdgePairsDelegate * +AsIfFlatRegion::angle_check (double min, double max, bool inverse) const +{ + std::auto_ptr res (new db::FlatEdgePairs ()); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + produce_markers_for_angle_check (*p, db::UnitTrans (), min, max, inverse, res->raw_edge_pairs ()); + } + + return res.release (); +} + +static inline db::Coord snap_to_grid (db::Coord c, db::Coord g) +{ + // This form of snapping always snaps g/2 to right/top. + if (c < 0) { + c = -g * ((-c + (g - 1) / 2) / g); + } else { + c = g * ((c + g / 2) / g); + } + return c; +} + +db::Polygon +AsIfFlatRegion::snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector &heap) +{ + db::Polygon pnew; + + for (size_t i = 0; i < poly.holes () + 1; ++i) { + + heap.clear (); + + db::Polygon::polygon_contour_iterator b, e; + + if (i == 0) { + b = poly.begin_hull (); + e = poly.end_hull (); + } else { + b = poly.begin_hole ((unsigned int) (i - 1)); + e = poly.end_hole ((unsigned int) (i - 1)); + } + + for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { + heap.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy))); + } + + if (i == 0) { + pnew.assign_hull (heap.begin (), heap.end ()); + } else { + pnew.insert_hole (heap.begin (), heap.end ()); + } + + } + + return pnew; +} + +RegionDelegate * +AsIfFlatRegion::snapped (db::Coord gx, db::Coord gy) +{ + if (gx < 0 || gy < 0) { + throw tl::Exception (tl::to_string (tr ("Grid check requires a positive grid value"))); + } + + std::auto_ptr new_region (new FlatRegion (merged_semantics ())); + + gx = std::max (db::Coord (1), gx); + gy = std::max (db::Coord (1), gy); + + std::vector heap; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (snapped_polygon (*p, gx, gy, heap)); + } + + return new_region.release (); +} + +EdgePairsDelegate * +AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + std::auto_ptr result (new FlatEdgePairs ()); + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + (other ? other->size () : 0)); + + AddressablePolygonDelivery p (begin_merged (), has_valid_merged_polygons ()); + + size_t n = 0; + for ( ; ! p.at_end (); ++p) { + scanner.insert (p.operator-> (), n); + n += 2; + } + + AddressablePolygonDelivery po; + + if (other) { + + po = other->addressable_merged_polygons (); + + n = 1; + for ( ; ! po.at_end (); ++po) { + scanner.insert (po.operator-> (), n); + n += 2; + } + + } + + EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (other != 0); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + edge2edge_check edge_check (check, *result, different_polygons, other != 0); + poly2poly_check poly_check (edge_check); + + do { + scanner.process (poly_check, d, db::box_convert ()); + } while (edge_check.prepare_next_pass ()); + + return result.release (); +} + +EdgePairsDelegate * +AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + std::auto_ptr result (new FlatEdgePairs ()); + + EdgeRelationFilter check (rel, d, metrics); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + edge2edge_check edge_check (check, *result, false, false); + poly2poly_check poly_check (edge_check); + + do { + + size_t n = 0; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + poly_check.enter (*p, n); + n += 2; + } + + } while (edge_check.prepare_next_pass ()); + + return result.release (); +} + +RegionDelegate * +AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc) const +{ + if (empty ()) { + + return new EmptyRegion (); + + } else if (is_box ()) { + + // take box only if min_wc == 0, otherwise clear + if (min_wc > 0) { + return new EmptyRegion (); + } else { + return clone (); + } + + } else { + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + ep.set_base_verbosity (base_verbosity ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + + // and run the merge step + db::MergeOp op (min_wc); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::region_from_box (const db::Box &b) +{ + if (! b.empty () && b.width () > 0 && b.height () > 0) { + FlatRegion *new_region = new FlatRegion (); + new_region->insert (b); + return new_region; + } else { + return new EmptyRegion (); + } +} + +RegionDelegate * +AsIfFlatRegion::sized (coord_type d, unsigned int mode) const +{ + return sized (d, d, mode); +} + +RegionDelegate * +AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const +{ + if (empty ()) { + + // ignore empty + return new EmptyRegion (); + + } else if (is_box () && mode >= 2) { + + // simplified handling for a box + db::Box b = bbox ().enlarged (db::Vector (dx, dy)); + return region_from_box (b); + + } else if (! merged_semantics ()) { + + // Generic case + std::auto_ptr new_region (new FlatRegion (false /*output isn't merged*/)); + + db::ShapeGenerator pc (new_region->raw_polygons (), false); + db::PolygonGenerator pg (pc, false, true); + db::SizingPolygonFilter sf (pg, dx, dy, mode); + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + sf.put (*p); + } + + return new_region.release (); + + } else { + + // Generic case - the size operation will merge first + db::EdgeProcessor ep (report_progress (), progress_desc ()); + ep.set_base_verbosity (base_verbosity ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (false /*output isn't merged*/)); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/); + db::SizingPolygonFilter siz (pg2, dx, dy, mode); + db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/); + db::BooleanOp op (db::BooleanOp::Or); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::and_with (const Region &other) const +{ + if (empty () || other.empty ()) { + + // Nothing to do + return new EmptyRegion (); + + } else if (is_box () && other.is_box ()) { + + // Simplified handling for boxes + db::Box b = bbox (); + b &= other.bbox (); + return region_from_box (b); + + } else if (is_box () && ! other.strict_handling ()) { + + // map AND with box to clip .. + db::Box b = bbox (); + std::auto_ptr new_region (new FlatRegion (false)); + + std::vector clipped; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + clipped.clear (); + clip_poly (*p, b, clipped); + new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); + } + + return new_region.release (); + + } else if (other.is_box () && ! strict_handling ()) { + + // map AND with box to clip .. + db::Box b = other.bbox (); + std::auto_ptr new_region (new FlatRegion (false)); + + std::vector clipped; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + clipped.clear (); + clip_poly (*p, b, clipped); + new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); + } + + return new_region.release (); + + } else if (! bbox ().overlaps (other.bbox ())) { + + // Result will be nothing + return new EmptyRegion (); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + ep.set_base_verbosity (base_verbosity ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::And); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::not_with (const Region &other) const +{ + if (empty ()) { + + // Nothing to do + return new EmptyRegion (); + + } else if (other.empty () && ! strict_handling ()) { + + // Nothing to do + return clone (); + + } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling ()) { + + // Nothing to do + return clone (); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + ep.set_base_verbosity (base_verbosity ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::ANotB); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::xor_with (const Region &other) const +{ + if (empty () && ! other.strict_handling ()) { + + return other.delegate ()->clone (); + + } else if (other.empty () && ! strict_handling ()) { + + return clone (); + + } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling () && ! other.strict_handling ()) { + + // Simplified handling for disjunct case + return or_with (other); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + ep.set_base_verbosity (base_verbosity ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::Xor); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::or_with (const Region &other) const +{ + if (empty () && ! other.strict_handling ()) { + + return other.delegate ()->clone (); + + } else if (other.empty () && ! strict_handling ()) { + + // Nothing to do + return clone (); + + } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling () && ! other.strict_handling ()) { + + // Simplified handling for disjunct case + return add (other); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + ep.set_base_verbosity (base_verbosity ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::Or); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::add (const Region &other) const +{ + FlatRegion *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + std::auto_ptr new_region (new FlatRegion (*other_flat)); + new_region->set_is_merged (false); + new_region->invalidate_cache (); + + size_t n = new_region->raw_polygons ().size () + size (); + + new_region->reserve (n); + + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + + return new_region.release (); + + } else { + + std::auto_ptr new_region (new FlatRegion (false /*not merged*/)); + + size_t n = size () + other.size (); + + new_region->reserve (n); + + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + + return new_region.release (); + + } +} + +void +AsIfFlatRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + // improves performance when inserting an original layout into the same layout + db::LayoutLocker locker (layout); + + db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer); + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + shapes.insert (*p); + } +} + +bool +AsIfFlatRegion::equals (const Region &other) const +{ + if (empty () != other.empty ()) { + return false; + } + if (size () != other.size ()) { + return false; + } + RegionIterator o1 (begin ()); + RegionIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return false; + } + ++o1; + ++o2; + } + return true; +} + +bool +AsIfFlatRegion::less (const Region &other) const +{ + if (empty () != other.empty ()) { + return empty () < other.empty (); + } + if (size () != other.size ()) { + return (size () < other.size ()); + } + RegionIterator o1 (begin ()); + RegionIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return *o1 < *o2; + } + ++o1; + ++o2; + } + return false; +} + +} + diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h new file mode 100644 index 000000000..b6c1b3e93 --- /dev/null +++ b/src/db/db/dbAsIfFlatRegion.h @@ -0,0 +1,243 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbAsIfFlatRegion +#define HDR_dbAsIfFlatRegion + +#include "dbCommon.h" + +#include "dbRegionDelegate.h" +#include "dbPolygon.h" +#include "dbEdge.h" +#include "dbBoxScanner.h" +#include "dbEdgePairRelations.h" + +#include + +namespace db { + +/** + * @brief Provides default flat implementations + */ +class DB_PUBLIC AsIfFlatRegion + : public RegionDelegate +{ +public: + AsIfFlatRegion (); + virtual ~AsIfFlatRegion (); + + virtual bool is_box () const; + virtual size_t size () const; + + virtual area_type area (const db::Box &box) const; + virtual perimeter_type perimeter (const db::Box &box) const; + virtual Box bbox () const; + + virtual std::string to_string (size_t nmax) const; + + EdgePairsDelegate *width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_single_polygon_check (db::WidthRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, false, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, true, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_single_polygon_check (db::SpaceRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::OverlapRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::WidthRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::InsideRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairsDelegate *grid_check (db::Coord gx, db::Coord gy) const; + virtual EdgePairsDelegate *angle_check (double min, double max, bool inverse) const; + + virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy) + { + return snapped (gx, gy); + } + + virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy); + + virtual EdgesDelegate *edges (const EdgeFilterBase *) const; + + virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter) + { + return processed (filter); + } + + virtual RegionDelegate *processed (const PolygonProcessorBase &filter) const; + virtual EdgesDelegate *processed_to_edges (const PolygonToEdgeProcessorBase &filter) const; + virtual EdgePairsDelegate *processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &filter) const; + + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) + { + return filtered (filter); + } + + virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const; + + virtual RegionDelegate *merged_in_place () + { + return merged (); + } + + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc) + { + return merged (min_coherence, min_wc); + } + + virtual RegionDelegate *merged () const + { + return merged (min_coherence (), 0); + } + + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const; + + virtual RegionDelegate *sized (coord_type d, unsigned int mode) const; + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const; + + virtual RegionDelegate *and_with (const Region &other) const; + virtual RegionDelegate *not_with (const Region &other) const; + virtual RegionDelegate *xor_with (const Region &other) const; + virtual RegionDelegate *or_with (const Region &other) const; + + virtual RegionDelegate *add_in_place (const Region &other) + { + return add (other); + } + + virtual RegionDelegate *add (const Region &other) const; + + virtual RegionDelegate *selected_outside (const Region &other) const + { + return selected_interacting_generic (other, 1, false, false); + } + + virtual RegionDelegate *selected_not_outside (const Region &other) const + { + return selected_interacting_generic (other, 1, false, true); + } + + virtual RegionDelegate *selected_inside (const Region &other) const + { + return selected_interacting_generic (other, -1, true, false); + } + + virtual RegionDelegate *selected_not_inside (const Region &other) const + { + return selected_interacting_generic (other, -1, true, true); + } + + virtual RegionDelegate *selected_interacting (const Region &other) const + { + return selected_interacting_generic (other, 0, true, false); + } + + virtual RegionDelegate *selected_not_interacting (const Region &other) const + { + return selected_interacting_generic (other, 0, true, true); + } + + virtual RegionDelegate *selected_interacting (const Edges &other) const + { + return selected_interacting_generic (other, false); + } + + virtual RegionDelegate *selected_not_interacting (const Edges &other) const + { + return selected_interacting_generic (other, true); + } + + virtual RegionDelegate *selected_overlapping (const Region &other) const + { + return selected_interacting_generic (other, 0, false, false); + } + + virtual RegionDelegate *selected_not_overlapping (const Region &other) const + { + return selected_interacting_generic (other, 0, false, true); + } + + virtual RegionDelegate *in (const Region &other, bool invert) const; + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; + + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + +protected: + void update_bbox (const db::Box &box); + void invalidate_bbox (); + + EdgePairsDelegate *run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + EdgePairsDelegate *run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + RegionDelegate *selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const; + RegionDelegate *selected_interacting_generic (const Edges &other, bool inverse) const; + + template + static void produce_markers_for_grid_check (const db::Polygon &poly, const Trans &tr, db::Coord gx, db::Coord gy, db::Shapes &shapes); + template + static void produce_markers_for_angle_check (const db::Polygon &poly, const Trans &tr, double min, double max, bool inverse, db::Shapes &shapes); + static db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector &heap); + +private: + AsIfFlatRegion &operator= (const AsIfFlatRegion &other); + + mutable bool m_bbox_valid; + mutable db::Box m_bbox; + + virtual db::Box compute_bbox () const; + static RegionDelegate *region_from_box (const db::Box &b); +}; + +} + +#endif + diff --git a/src/db/db/dbBoxScanner.h b/src/db/db/dbBoxScanner.h index bb7bdc749..c350469ba 100644 --- a/src/db/db/dbBoxScanner.h +++ b/src/db/db/dbBoxScanner.h @@ -122,13 +122,21 @@ struct box_scanner_receiver */ void finish (const Obj * /*obj*/, const Prop & /*prop*/) { } - /* + /** * @brief Callback for an interaction of o1 with o2. * * This method is called when the object o1 interacts with o2 within the current * definition. */ void add (const Obj * /*o1*/, const Prop & /*p1*/, const Obj * /*o2*/, const Prop & /*p2*/) { } + + /** + * @brief Indicates whether the scanner may stop + * + * The scanner will stop if this method returns true. This feature can be used to + * terminate the scan process early if the outcome is known. + */ + bool stop () const { return false; } }; /** @@ -250,9 +258,13 @@ public: * * The box converter must be capable of converting the Obj object into a box. * It must provide a box_type typedef. + * + * The scanner process can be terminated early by making the receiver's + * stop() method return true. In this case, this method will return false. + * Otherwise it will return true. */ template - void process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ()) + bool process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ()) { typedef typename BoxConvert::box_type box_type; typedef typename box_type::coord_type coord_type; @@ -261,14 +273,37 @@ public: typedef bs_side_compare_vs_const_func > below_func; typedef bs_side_compare_vs_const_func > left_func; + // sort out the entries with an empty bbox (we must not put that into sort) + + typename container_type::iterator wi = m_pp.begin (); + for (typename container_type::iterator ri = m_pp.begin (); ri != m_pp.end (); ++ri) { + if (! bc (*ri->first).empty ()) { + if (wi != ri) { + *wi = *ri; + } + ++wi; + } else { + // we call finish on empty elements though + rec.finish (ri->first, ri->second); + } + } + + if (wi != m_pp.end ()) { + m_pp.erase (wi, m_pp.end ()); + } + if (m_pp.size () <= m_scanner_thr) { // below m_scanner_thr elements use the brute force approach which is faster in that case for (iterator_type i = m_pp.begin (); i != m_pp.end (); ++i) { + box_type b1 = bc (*i->first); for (iterator_type j = i + 1; j != m_pp.end (); ++j) { - if (bs_boxes_overlap (bc (*i->first), bc (*j->first), enl)) { + if (bs_boxes_overlap (b1, bc (*j->first), enl)) { rec.add (i->first, i->second, j->first, j->second); + if (rec.stop ()) { + return false; + } } } } @@ -355,6 +390,9 @@ public: if (seen.insert (std::make_pair (i->first, j->first)).second) { seen.insert (std::make_pair (j->first, i->first)); rec.add (i->first, i->second, j->first, j->second); + if (rec.stop ()) { + return false; + } } } } @@ -379,6 +417,8 @@ public: } + return true; + } private: @@ -389,6 +429,432 @@ private: std::string m_progress_desc; }; +/** + * @brief A template for the twofold box scanner output receiver + * + * This template specifies the methods or provides a default implementation for them + * for use as the output receiver of the twofold box scanner. + */ +template +struct box_scanner_receiver2 +{ + /** + * @brief Indicates that the given object of first type is no longer used + * + * The finish1 method is called when an object of the first type is no longer in the queue and can be + * discarded. + */ + void finish1 (const Obj1 * /*obj*/, const Prop1 & /*prop*/) { } + + /** + * @brief Indicates that the given object of second type is no longer used + * + * The finish method is called when an object of the second type is no longer in the queue and can be + * discarded. + */ + void finish2 (const Obj2 * /*obj*/, const Prop2 & /*prop*/) { } + + /** + * @brief Callback for an interaction of o1 with o2. + * + * This method is called when the object o1 interacts with o2 within the current + * definition. + */ + void add (const Obj1 * /*o1*/, const Prop1 & /*p1*/, const Obj2 * /*o2*/, const Prop2 & /*p2*/) { } + + /** + * @brief Indicates whether the scanner may stop + * + * The scanner will stop if this method returns true. This feature can be used to + * terminate the scan process early if the outcome is known. + */ + bool stop () const { return false; } +}; + +/** + * @brief A box scanner framework (twofold version) + * + * This implementation provides a box scanner for two different types. Apart from + * that it is similar to the uniform-type box scanner. + * + * It will not report interactions within the Obj1 or Obj2 group, but only + * interactions between Obj1 and Obj2 objects. + */ +template +class box_scanner2 +{ +public: + typedef Obj1 object_type1; + typedef Obj2 object_type2; + typedef std::vector > container_type1; + typedef std::vector > container_type2; + typedef typename container_type1::iterator iterator_type1; + typedef typename container_type2::iterator iterator_type2; + + /** + * @brief Default ctor + */ + box_scanner2 (bool report_progress = false, const std::string &progress_desc = std::string ()) + : m_fill_factor (2), m_scanner_thr (100), + m_report_progress (report_progress), m_progress_desc (progress_desc) + { + // .. nothing yet .. + } + + /** + * @brief Sets the scanner threshold + * + * This value determines for how many elements the implementation switches to the scanner + * implementation instead of the plain element-by-element interaction test. + * The default value is 100. + */ + void set_scanner_threshold (size_t n) + { + m_scanner_thr = n; + } + + /** + * @brief Gets the scanner threshold + */ + size_t scanner_threshold () const + { + return m_scanner_thr; + } + + /** + * @brief Sets the fill factor + * + * The fill factor determines how many new entries will be collected for a band. + * A fill factor of 2 means that the number of elements in the band will be + * doubled after elements outside of the band have been removed. + * The default fill factor is 2. + */ + void set_fill_factor (double ff) + { + m_fill_factor = ff; + } + + /** + * @brief Gets the fill factor + */ + double fill_factor () const + { + return m_fill_factor; + } + + /** + * @brief Reserve for n elements of Obj1 type + */ + void reserve1 (size_t n) + { + m_pp1.reserve (n); + } + + /** + * @brief Reserve for n elements of Obj2 type + */ + void reserve2 (size_t n) + { + m_pp2.reserve (n); + } + + /** + * @brief Clears the container + */ + void clear () + { + m_pp1.clear (); + m_pp2.clear (); + } + + /** + * @brief Inserts a new object of type Obj1 into the scanner + * + * The object's pointer is stored, so the object must remain valid until the + * scanner does not need it any longer. An additional property can be attached to + * the object which will be stored along with the object. + */ + void insert1 (const Obj1 *obj, const Prop1 &prop) + { + m_pp1.push_back (std::make_pair (obj, prop)); + } + + /** + * @brief Inserts a new object of type Obj2 into the scanner + * + * The object's pointer is stored, so the object must remain valid until the + * scanner does not need it any longer. An additional property can be attached to + * the object which will be stored along with the object. + */ + void insert2 (const Obj2 *obj, const Prop2 &prop) + { + m_pp2.push_back (std::make_pair (obj, prop)); + } + + /** + * @brief Get the interactions between the stored objects + * + * Two objects interact if the boxes of the objects enlarged by the given value overlap. + * The enlargement is specified in units of width and height, i.e. half of the enlargement + * is applied to one side before the overlap check. + * + * An enlargement of 1 means that boxes have to touch only in order to get an interaction. + * + * The box scanner will report all interactions of type Obj1 and Obj2 objects to the receiver object. + * See box_scanner_receiver2 for details about the methods that this object must provide. + * + * The box converter 1 must be capable of converting the Obj1 object into a box. + * It must provide a box_type typedef. The box converter 2 must be able to convert Obj2 to + * a box. The box type of both box converters must be identical. + * + * The scanner process can be terminated early by making the receiver's + * stop() method return true. In this case, this method will return false. + * Otherwise it will return true. + */ + template + bool process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ()) + { + typedef typename BoxConvert1::box_type box_type; // must be same as BoxConvert2::box_type + typedef typename box_type::coord_type coord_type; + typedef bs_side_compare_func > bottom_side_compare_func1; + typedef bs_side_compare_func > left_side_compare_func1; + typedef bs_side_compare_vs_const_func > below_func1; + typedef bs_side_compare_vs_const_func > left_func1; + typedef bs_side_compare_func > bottom_side_compare_func2; + typedef bs_side_compare_func > left_side_compare_func2; + typedef bs_side_compare_vs_const_func > below_func2; + typedef bs_side_compare_vs_const_func > left_func2; + + // sort out the entries with an empty bbox (we must not put that into sort) + + typename container_type1::iterator wi1 = m_pp1.begin (); + for (typename container_type1::iterator ri1 = m_pp1.begin (); ri1 != m_pp1.end (); ++ri1) { + if (! bc1 (*ri1->first).empty ()) { + if (wi1 != ri1) { + *wi1 = *ri1; + } + ++wi1; + } else { + // we call finish on empty elements though + rec.finish1 (ri1->first, ri1->second); + } + } + + if (wi1 != m_pp1.end ()) { + m_pp1.erase (wi1, m_pp1.end ()); + } + + typename container_type2::iterator wi2 = m_pp2.begin (); + for (typename container_type2::iterator ri2 = m_pp2.begin (); ri2 != m_pp2.end (); ++ri2) { + if (! bc2 (*ri2->first).empty ()) { + if (wi2 != ri2) { + *wi2 = *ri2; + } + ++wi2; + } else { + // we call finish on empty elements though + rec.finish2 (ri2->first, ri2->second); + } + } + + if (wi2 != m_pp2.end ()) { + m_pp2.erase (wi2, m_pp2.end ()); + } + + if (m_pp1.empty () || m_pp2.empty ()) { + + // trivial case + + for (iterator_type1 i = m_pp1.begin (); i != m_pp1.end (); ++i) { + rec.finish1 (i->first, i->second); + } + for (iterator_type2 i = m_pp2.begin (); i != m_pp2.end (); ++i) { + rec.finish2 (i->first, i->second); + } + + } else if (m_pp1.size () + m_pp2.size () <= m_scanner_thr) { + + // below m_scanner_thr elements use the brute force approach which is faster in that case + + for (iterator_type1 i = m_pp1.begin (); i != m_pp1.end (); ++i) { + box_type b1 = bc1 (*i->first); + for (iterator_type2 j = m_pp2.begin (); j != m_pp2.end (); ++j) { + if (bs_boxes_overlap (b1, bc2 (*j->first), enl)) { + rec.add (i->first, i->second, j->first, j->second); + if (rec.stop ()) { + return false; + } + } + } + } + + for (iterator_type1 i = m_pp1.begin (); i != m_pp1.end (); ++i) { + rec.finish1 (i->first, i->second); + } + for (iterator_type2 i = m_pp2.begin (); i != m_pp2.end (); ++i) { + rec.finish2 (i->first, i->second); + } + + } else { + + std::set > seen1; + std::set > seen2; + + std::sort (m_pp1.begin (), m_pp1.end (), bottom_side_compare_func1 (bc1)); + std::sort (m_pp2.begin (), m_pp2.end (), bottom_side_compare_func2 (bc2)); + + coord_type y = std::min (bc1 (*m_pp1.front ().first).bottom (), bc2 (*m_pp2.front ().first).bottom ()); + + iterator_type1 current1 = m_pp1.begin (); + iterator_type1 future1 = m_pp1.begin (); + iterator_type2 current2 = m_pp2.begin (); + iterator_type2 future2 = m_pp2.begin (); + + std::auto_ptr progress (0); + if (m_report_progress) { + if (m_progress_desc.empty ()) { + progress.reset (new tl::RelativeProgress (tl::to_string (tr ("Processing")), m_pp1.size () + m_pp2.size (), 1000)); + } else { + progress.reset (new tl::RelativeProgress (m_progress_desc, m_pp1.size () + m_pp2.size (), 1000)); + } + } + + while (future1 != m_pp1.end () || future2 != m_pp2.end ()) { + + iterator_type1 cc1 = current1; + iterator_type2 cc2 = current2; + current1 = std::partition (current1, future1, below_func1 (bc1, y + 1 - enl)); + current2 = std::partition (current2, future2, below_func2 (bc2, y + 1 - enl)); + + while (cc1 != current1) { + rec.finish1 (cc1->first, cc1->second); + typename std::set >::iterator s; + s = seen1.lower_bound (std::make_pair (cc1->first, (const Obj2 *)0)); + while (s != seen1.end () && s->first == cc1->first) { + seen1.erase (s++); + } + ++cc1; + } + + while (cc2 != current2) { + rec.finish2 (cc2->first, cc2->second); + typename std::set >::iterator s; + s = seen2.lower_bound (std::make_pair (cc2->first, (const Obj1 *)0)); + while (s != seen2.end () && s->first == cc2->first) { + seen2.erase (s++); + } + ++cc2; + } + + // add at least the required items per band + size_t min_band_size = size_t ((future1 - current1) * m_fill_factor) + size_t ((future2 - current2) * m_fill_factor); + coord_type yy = y; + do { + if (future1 != m_pp1.end () && future2 != m_pp2.end ()) { + yy = std::min (bc1 (*future1->first).bottom (), bc2 (*future2->first).bottom ()); + } else if (future1 != m_pp1.end ()) { + yy = bc1 (*future1->first).bottom (); + } else { + yy = bc2 (*future2->first).bottom (); + } + while (future1 != m_pp1.end () && bc1 (*future1->first).bottom () == yy) { + ++future1; + } + while (future2 != m_pp2.end () && bc2 (*future2->first).bottom () == yy) { + ++future2; + } + } while ((future1 != m_pp1.end () || future2 != m_pp2.end ()) && size_t (future1 - current1) + size_t (future2 - current2) < min_band_size); + + if (current1 != future1 && current2 != future2) { + + std::sort (current1, future1, left_side_compare_func1 (bc1)); + std::sort (current2, future2, left_side_compare_func2 (bc2)); + + iterator_type1 c1 = current1; + iterator_type1 f1 = current1; + iterator_type2 c2 = current2; + iterator_type2 f2 = current2; + + coord_type x = std::min (bc1 (*c1->first).left (), bc2 (*c2->first).left ()); + + while (f1 != future1 || f2 != future2) { + + c1 = std::partition (c1, f1, left_func1 (bc1, x + 1 - enl)); + c2 = std::partition (c2, f2, left_func2 (bc2, x + 1 - enl)); + + // add at least the required items per band + size_t min_box_size = size_t ((f1 - c1) * m_fill_factor) + size_t ((f2 - c2) * m_fill_factor); + coord_type xx = x; + do { + if (f1 != future1 && f2 != future2) { + xx = std::min (bc1 (*f1->first).left (), bc2 (*f2->first).left ()); + } else if (f1 != future1) { + xx = bc1 (*f1->first).left (); + } else if (f2 != future2) { + xx = bc2 (*f2->first).left (); + } + while (f1 != future1 && bc1 (*f1->first).left () == xx) { + ++f1; + } + while (f2 != future2 && bc2 (*f2->first).left () == xx) { + ++f2; + } + } while ((f1 != future1 || f2 != future2) && size_t (f1 - c1) + size_t (f2 - c2) < min_box_size); + + if (c1 != f1 && c2 != f2) { + for (iterator_type1 i = c1; i != f1; ++i) { + for (iterator_type2 j = c2; j < f2; ++j) { + if (bs_boxes_overlap (bc1 (*i->first), bc2 (*j->first), enl)) { + if (seen1.insert (std::make_pair (i->first, j->first)).second) { + seen2.insert (std::make_pair (j->first, i->first)); + rec.add (i->first, i->second, j->first, j->second); + if (rec.stop ()) { + return false; + } + } + } + } + } + } + + x = xx; + + if (m_report_progress) { + progress->set (std::min (f1 - m_pp1.begin (), f2 - m_pp2.begin ())); + } + + } + + } + + y = yy; + + } + + while (current1 != m_pp1.end ()) { + rec.finish1 (current1->first, current1->second); + ++current1; + } + while (current2 != m_pp2.end ()) { + rec.finish2 (current2->first, current2->second); + ++current2; + } + + } + + return true; + + } + +private: + container_type1 m_pp1; + container_type2 m_pp2; + double m_fill_factor; + size_t m_scanner_thr; + bool m_report_progress; + std::string m_progress_desc; +}; + /** * @brief A cluster template that stores properties * diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index 66490629d..7d29d5d13 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -295,7 +295,7 @@ CellMapping::create_from_names (const db::Layout &layout_a, db::cell_index_type } std::vector -CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type /*cell_index_a*/, const db::Layout &layout_b, db::cell_index_type cell_index_b) +CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type /*cell_index_a*/, const db::Layout &layout_b, db::cell_index_type cell_index_b, const std::set *exclude_cells) { std::vector new_cells; std::vector new_cells_b; @@ -305,7 +305,7 @@ CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type / called_b.insert (cell_index_b); for (std::set::const_iterator b = called_b.begin (); b != called_b.end (); ++b) { - if (m_b2a_mapping.find (*b) == m_b2a_mapping.end ()) { + if (m_b2a_mapping.find (*b) == m_b2a_mapping.end () && (! exclude_cells || exclude_cells->find (*b) == exclude_cells->end ())) { db::cell_index_type new_cell = layout_a.add_cell (layout_b.cell_name (*b)); new_cells.push_back (new_cell); new_cells_b.push_back (*b); @@ -335,7 +335,7 @@ CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type / db::CellInstArray bci = bi.cell_inst (); bci.object ().cell_index (new_cells [i]); - bci.transform_into (db::ICplxTrans (mag)); + bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ()); if (bi.has_prop_id ()) { pa.insert (db::CellInstArrayWithProperties (bci, pm (bi.prop_id ()))); diff --git a/src/db/db/dbCellMapping.h b/src/db/db/dbCellMapping.h index 89f7b7b45..63740b1ca 100644 --- a/src/db/db/dbCellMapping.h +++ b/src/db/db/dbCellMapping.h @@ -30,6 +30,8 @@ #include "dbTypes.h" #include +#include +#include namespace db { @@ -179,6 +181,19 @@ public: return m_b2a_mapping; } + /** + * @brief Creates mappings for all cells not mapped yet + * + * When constructing a cell mapping by explicit mapping (map (a, b)), some cells may be + * left unmapped. This method allows creating mappings for these missing cells by adding + * new cells and the corresponding instances into the target layout_a. + * + * If given, "exclude_cells" can specify a list of cells not to map. + * + * The returned vector lists the new cells. + */ + std::vector create_missing_mapping (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b, const std::set *exclude_cells = 0); + private: void extract_unique (std::map >::const_iterator cand, std::map &unique_mapping, @@ -187,8 +202,6 @@ private: void dump_mapping (const std::map > &candidates, const db::Layout &layout_a, const db::Layout &layout_b); - std::vector create_missing_mapping (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b); - std::map m_b2a_mapping; }; diff --git a/src/db/db/dbCellVariants.cc b/src/db/db/dbCellVariants.cc new file mode 100644 index 000000000..1ff5c9f76 --- /dev/null +++ b/src/db/db/dbCellVariants.cc @@ -0,0 +1,436 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbCellVariants.h" +#include "tlUtils.h" + +namespace db +{ + +VariantsCollectorBase::VariantsCollectorBase () + : mp_red () +{ + // .. nothing yet .. +} + +VariantsCollectorBase::VariantsCollectorBase (const TransformationReducer *red) + : mp_red (red) +{ + // .. nothing yet .. +} + +void +VariantsCollectorBase::collect (const db::Layout &layout, const db::Cell &top_cell) +{ + tl_assert (mp_red != 0); + + // The top cell gets a "variant" with unit transformation + m_variants [top_cell.cell_index ()].insert (std::make_pair (db::ICplxTrans (), 1)); + + std::set called; + top_cell.collect_called_cells (called); + + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + + if (called.find (*c) == called.end ()) { + continue; + } + + // collect the parent variants per parent cell + + std::map > variants_per_parent_cell; + for (db::Cell::parent_inst_iterator pi = layout.cell (*c).begin_parent_insts (); ! pi.at_end (); ++pi) { + std::map &variants = variants_per_parent_cell [pi->inst ().object ().cell_index ()]; + add_variant (variants, pi->child_inst ().cell_inst (), mp_red->is_translation_invariant ()); + } + + // compute the resulting variants + + std::map &new_variants = m_variants [*c]; + + for (std::map >::const_iterator pv = variants_per_parent_cell.begin (); pv != variants_per_parent_cell.end (); ++pv) { + product (variants (pv->first), pv->second, new_variants); + } + + } +} + +void +VariantsCollectorBase::separate_variants (db::Layout &layout, db::Cell &top_cell, std::map > *var_table) +{ + tl_assert (mp_red != 0); + + db::LayoutLocker locker (&layout); + + std::set called; + top_cell.collect_called_cells (called); + called.insert (top_cell.cell_index ()); + + // create new cells for the variants + + std::map > var_table_intern; + if (! var_table) { + var_table = &var_table_intern; + } + + for (db::Layout::bottom_up_const_iterator c = layout.begin_bottom_up (); c != layout.end_bottom_up (); ++c) { + + if (called.find (*c) == called.end ()) { + continue; + } + + db::Cell &cell = layout.cell (*c); + + std::map &vv = m_variants [*c]; + if (vv.size () > 1) { + + std::map &vt = (*var_table) [*c]; + + std::vector inst; + inst.reserve (cell.cell_instances ()); + + for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) { + inst.push_back (db::CellInstArrayWithProperties (i->cell_inst (), i->prop_id ())); + } + + cell.clear_insts (); + + int index = 0; + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v, ++index) { + + db::cell_index_type ci_var; + + if (v != vv.begin ()) { + + std::string var_name = layout.cell_name (*c); + var_name += "$VAR" + tl::to_string (index); + + ci_var = layout.add_cell (var_name.c_str ()); + copy_shapes (layout, ci_var, *c); + + // a new entry for the variant + m_variants [ci_var].insert (*v); + + } else { + ci_var = *c; + } + + vt.insert (std::make_pair (v->first, ci_var)); + create_var_instances (layout.cell (ci_var), inst, v->first, *var_table, mp_red->is_translation_invariant ()); + + } + + // correct the first (remaining) entry + std::pair v1 = *vt.begin (); + vv.clear (); + vv.insert (v1); + + } else { + + // if the children of this cell are separated, map the instances to the new variants + bool needs_update = false; + for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end () && ! needs_update; ++cc) { + if (var_table->find (*cc) != var_table->end ()) { + needs_update = true; + } + } + + if (needs_update) { + + std::vector inst; + inst.reserve (cell.cell_instances ()); + + for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) { + inst.push_back (db::CellInstArrayWithProperties (i->cell_inst (), i->prop_id ())); + } + + cell.clear_insts (); + create_var_instances (cell, inst, vv.begin ()->first, *var_table, mp_red->is_translation_invariant ()); + + } + + } + + } +} + +void +VariantsCollectorBase::commit_shapes (db::Layout &layout, db::Cell &top_cell, unsigned int layer, std::map > &to_commit) +{ + tl_assert (mp_red != 0); + + if (to_commit.empty ()) { + return; + } + + // NOTE: this implementation suffers from accumulation of propagated shapes: we add more levels of propagated + // shapes if required. We don't clean up, because we do not know when a shape collection stops being required. + + db::LayoutLocker locker (&layout); + + std::set called; + top_cell.collect_called_cells (called); + called.insert (top_cell.cell_index ()); + + for (db::Layout::bottom_up_const_iterator c = layout.begin_bottom_up (); c != layout.end_bottom_up (); ++c) { + + if (called.find (*c) == called.end ()) { + continue; + } + + db::Cell &cell = layout.cell (*c); + + std::map &vvc = m_variants [*c]; + if (vvc.size () > 1) { + + for (std::map::const_iterator vc = vvc.begin (); vc != vvc.end (); ++vc) { + + for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) { + + std::map >::const_iterator tc = to_commit.find (i->cell_index ()); + if (tc != to_commit.end ()) { + + const std::map &vt = tc->second; + + // NOTE: this will add one more commit slot for propagation ... but we don't clean up. + // When would a cleanup happen? + std::map &propagated = to_commit [*c]; + + for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end (); ++ia) { + + db::ICplxTrans t = i->complex_trans (*ia); + db::ICplxTrans rt = mp_red->reduce (vc->first * t); + std::map::const_iterator v = vt.find (rt); + if (v != vt.end ()) { + + db::Shapes &ps = propagated [vc->first]; + tl::ident_map pm; + + for (db::Shapes::shape_iterator si = v->second.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + ps.insert (*si, t, pm); + } + + } + + } + + } + + } + + } + + } else { + + // single variant -> we can commit any shapes we have kept for this cell directly to the cell + + std::map >::iterator l = to_commit.find (*c); + if (l != to_commit.end ()) { + tl_assert (l->second.size () == 1); + cell.shapes (layer).insert (l->second.begin ()->second); + to_commit.erase (l); + } + + // for child cells, pull everything that needs to be committed to the parent + + for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) { + + std::map >::const_iterator tc = to_commit.find (i->cell_index ()); + if (tc != to_commit.end ()) { + + const std::map &vt = tc->second; + + for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end (); ++ia) { + + db::ICplxTrans t = i->complex_trans (*ia); + db::ICplxTrans rt = mp_red->reduce (vvc.begin ()->first * t); + std::map::const_iterator v = vt.find (rt); + + if (v != vt.end ()) { + + tl::ident_map pm; + + for (db::Shapes::shape_iterator si = v->second.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + cell.shapes (layer).insert (*si, t, pm); + } + + } + + } + + } + + } + + } + + } +} + +const std::map & +VariantsCollectorBase::variants (db::cell_index_type ci) const +{ + std::map >::const_iterator v = m_variants.find (ci); + static std::map empty_set; + if (v == m_variants.end ()) { + return empty_set; + } else { + return v->second; + } +} + +bool +VariantsCollectorBase::has_variants () const +{ + for (std::map >::const_iterator i = m_variants.begin (); i != m_variants.end (); ++i) { + if (i->second.size () > 1) { + return true; + } + } + return false; +} + +void +VariantsCollectorBase::add_variant (std::map &variants, const db::CellInstArray &inst, bool tl_invariant) const +{ + if (tl_invariant) { + add_variant_tl_invariant (variants, inst); + } else { + add_variant_non_tl_invariant (variants, inst); + } +} + +void +VariantsCollectorBase::add_variant_non_tl_invariant (std::map &variants, const db::CellInstArray &inst) const +{ + if (inst.is_complex ()) { + for (db::CellInstArray::iterator i = inst.begin (); ! i.at_end (); ++i) { + variants [mp_red->reduce (inst.complex_trans (*i))] += 1; + } + } else { + for (db::CellInstArray::iterator i = inst.begin (); ! i.at_end (); ++i) { + variants [db::ICplxTrans (mp_red->reduce (*i))] += 1; + } + } +} + +void +VariantsCollectorBase::add_variant_tl_invariant (std::map &variants, const db::CellInstArray &inst) const +{ + if (inst.is_complex ()) { + variants [mp_red->reduce (inst.complex_trans ())] += inst.size (); + } else { + variants [db::ICplxTrans (mp_red->reduce (inst.front ()))] += inst.size (); + } +} + +void +VariantsCollectorBase::product (const std::map &v1, const std::map &v2, std::map &prod) const +{ + for (std::map::const_iterator i = v1.begin (); i != v1.end (); ++i) { + for (std::map::const_iterator j = v2.begin (); j != v2.end (); ++j) { + prod [mp_red->reduce (i->first * j->first)] += i->second * j->second; + } + } +} + +void +VariantsCollectorBase::copy_shapes (db::Layout &layout, db::cell_index_type ci_to, db::cell_index_type ci_from) const +{ + db::Cell &to = layout.cell (ci_to); + const db::Cell &from = layout.cell (ci_from); + for (db::Layout::layer_iterator li = layout.begin_layers (); li != layout.end_layers (); ++li) { + to.shapes ((*li).first) = from.shapes ((*li).first); + } +} + +void +VariantsCollectorBase::create_var_instances (db::Cell &in_cell, std::vector &inst, const db::ICplxTrans &for_var, const std::map > &var_table, bool tl_invariant) const +{ + if (tl_invariant) { + create_var_instances_tl_invariant (in_cell, inst, for_var, var_table); + } else { + create_var_instances_non_tl_invariant (in_cell, inst, for_var, var_table); + } +} + +void +VariantsCollectorBase::create_var_instances_non_tl_invariant (db::Cell &in_cell, std::vector &inst, const db::ICplxTrans &for_var, const std::map > &var_table) const +{ + for (std::vector::const_iterator i = inst.begin (); i != inst.end (); ++i) { + + std::map >::const_iterator f = var_table.find (i->object ().cell_index ()); + if (f == var_table.end ()) { + + in_cell.insert (*i); + + } else { + + const std::map &vt = f->second; + + for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end (); ++ia) { + + db::ICplxTrans rt = mp_red->reduce (for_var * i->complex_trans (*ia)); + std::map::const_iterator v = vt.find (rt); + tl_assert (v != vt.end ()); + + in_cell.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (v->second), i->complex_trans (*ia)), i->properties_id ())); + + } + + } + + } +} + +void +VariantsCollectorBase::create_var_instances_tl_invariant (db::Cell &in_cell, std::vector &inst, const db::ICplxTrans &for_var, const std::map > &var_table) const +{ + for (std::vector::const_iterator i = inst.begin (); i != inst.end (); ++i) { + + std::map >::const_iterator f = var_table.find (i->object ().cell_index ()); + if (f == var_table.end ()) { + + in_cell.insert (*i); + + } else { + + const std::map &vt = f->second; + + std::map::const_iterator v; + + db::ICplxTrans rt = mp_red->reduce (for_var * i->complex_trans ()); + v = vt.find (rt); + tl_assert (v != vt.end ()); + + db::CellInstArrayWithProperties new_inst = *i; + new_inst.object ().cell_index (v->second); + in_cell.insert (new_inst); + + } + + } +} + +} + diff --git a/src/db/db/dbCellVariants.h b/src/db/db/dbCellVariants.h new file mode 100644 index 000000000..f8333c6c3 --- /dev/null +++ b/src/db/db/dbCellVariants.h @@ -0,0 +1,277 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbCellVariants +#define HDR_dbCellVariants + +#include "dbCommon.h" + +#include "dbTrans.h" +#include "dbLayout.h" + +#include + +namespace db +{ + +/** + * @brief The reducer interface + * + * The transformation reducer is used by the variant builder to provide a + * reduced version of the transformation. Variants are built based on this + * reduced transformation. + * + * Reduction must satisfy the modulo condition: + * + * reduce(A*B) = reduce(reduce(A)*reduce(B)) + */ +class DB_PUBLIC TransformationReducer +{ +public: + TransformationReducer () { } + virtual ~TransformationReducer () { } + + virtual db::Trans reduce (const db::Trans &trans) const = 0; + virtual db::ICplxTrans reduce (const db::ICplxTrans &trans) const = 0; + virtual bool is_translation_invariant () const { return true; } +}; + +/** + * @brief An orientation reducer + * + * This reducer incarnation reduces the transformation to it's rotation/mirror part. + */ +struct DB_PUBLIC OrientationReducer + : public TransformationReducer +{ + db::ICplxTrans reduce (const db::ICplxTrans &trans) const + { + db::ICplxTrans res (trans); + res.disp (db::Vector ()); + res.mag (1.0); + return res; + } + + db::Trans reduce (const db::Trans &trans) const + { + return db::Trans (trans.fp_trans ()); + } +}; + +/** + * @brief A magnification reducer + * + * This reducer incarnation reduces the transformation to it's scaling part. + */ +struct DB_PUBLIC MagnificationReducer + : public TransformationReducer +{ + db::ICplxTrans reduce (const db::ICplxTrans &trans) const + { + return db::ICplxTrans (trans.mag ()); + } + + db::Trans reduce (const db::Trans &) const + { + return db::Trans (); + } +}; + +/** + * @brief A magnification and orientation reducer + * + * This reducer incarnation reduces the transformation to it's rotation/mirror/magnification part (2d matrix) + */ +struct DB_PUBLIC MagnificationAndOrientationReducer + : public TransformationReducer +{ + db::ICplxTrans reduce (const db::ICplxTrans &trans) const + { + db::ICplxTrans res (trans); + res.disp (db::Vector ()); + return res; + } + + db::Trans reduce (const db::Trans &trans) const + { + return db::Trans (trans.fp_trans ()); + } +}; + +/** + * @brief A grid reducer + * + * This reducer incarnation reduces the transformation to it's displacement modulo a grid + */ +struct DB_PUBLIC GridReducer + : public TransformationReducer +{ + GridReducer (db::Coord grid) + : m_grid (grid) + { + // .. nothing yet .. + } + + db::ICplxTrans reduce (const db::ICplxTrans &trans) const + { + // NOTE: we need to keep magnification, angle and mirror so when combining the + // reduced transformations, the result will be equivalent to reducing the combined + // transformation. + db::ICplxTrans res (trans); + res.disp (db::Vector (mod (trans.disp ().x ()), mod (trans.disp ().y ()))); + return res; + } + + db::Trans reduce (const db::Trans &trans) const + { + db::Trans res (trans); + res.disp (db::Vector (mod (trans.disp ().x ()), mod (trans.disp ().y ()))); + return res; + } + + bool is_translation_invariant () const { return false; } + +private: + db::Coord m_grid; + + inline db::Coord mod (db::Coord c) const + { + if (c < 0) { + c = m_grid - (-c) % m_grid; + if (c == m_grid) { + return 0; + } else { + return c; + } + } else { + return c % m_grid; + } + } +}; + +/** + * @brief A class computing variants for cells according to a given criterion + * + * The cell variants are build from the cell instances and are accumulated over + * the hierarchy path. + */ +class DB_PUBLIC VariantsCollectorBase +{ +public: + /** + * @brief Creates a variant collector without a transformation reducer + */ + VariantsCollectorBase (); + + /** + * @brief Creates a variant collector with the given reducer + */ + VariantsCollectorBase (const TransformationReducer *red); + + /** + * @brief Collects cell variants for the given layout starting from the top cell + */ + void collect (const db::Layout &layout, const db::Cell &top_cell); + + /** + * @brief Creates cell variants for singularization of the different variants + * + * After this method can been used, all cells with more than one variant are separated and + * the corresponding instances are updated. + * + * If given, *var_table will be filled with a map giving the new cell and variant against + * the old cell for all cells with more than one variant. + */ + void separate_variants (db::Layout &layout, db::Cell &top_cell, std::map > *var_table = 0); + + /** + * @brief Commits the shapes for different variants to the current cell hierarchy + * + * This is an alternative approach and will push the variant shapes into the parent hierarchy. + * "to_commit" initially is a set of shapes to commit for the given cell and variant. + * This map is modified during the algorithm and should be discarded later. + */ + void commit_shapes (db::Layout &layout, db::Cell &top_cell, unsigned int layer, std::map > &to_commit); + + /** + * @brief Gets the variants for a given cell + * + * The keys of the map are the variants, the values is the instance count of the variant + * (as seen from the top cell). + */ + const std::map &variants (db::cell_index_type ci) const; + + /** + * @brief Returns true, if variants have been built + */ + bool has_variants () const; + +private: + std::map > m_variants; + const TransformationReducer *mp_red; + + void add_variant (std::map &variants, const db::CellInstArray &inst, bool tl_invariant) const; + void add_variant_non_tl_invariant (std::map &variants, const db::CellInstArray &inst) const; + void add_variant_tl_invariant (std::map &variants, const db::CellInstArray &inst) const; + void product (const std::map &v1, const std::map &v2, std::map &prod) const; + void copy_shapes (db::Layout &layout, db::cell_index_type ci_to, db::cell_index_type ci_from) const; + void create_var_instances (db::Cell &in_cell, std::vector &inst, const db::ICplxTrans &for_var, const std::map > &var_table, bool tl_invariant) const; + void create_var_instances_non_tl_invariant (db::Cell &in_cell, std::vector &inst, const db::ICplxTrans &for_var, const std::map > &var_table) const; + void create_var_instances_tl_invariant (db::Cell &in_cell, std::vector &inst, const db::ICplxTrans &for_var, const std::map > &var_table) const; +}; + +/** + * @brief A template using a specific transformation reducer + */ +template +class DB_PUBLIC_TEMPLATE cell_variants_collector + : public VariantsCollectorBase +{ +public: + /** + * @brief Creates a variant collector without a transformation reducer + */ + cell_variants_collector () + : VariantsCollectorBase (&m_red) + { + // .. nothing yet .. + } + + /** + * @brief Creates a variant collector with the given reducer + * + * The collector will take ownership over the reducer + */ + cell_variants_collector (const RED &red) + : VariantsCollectorBase (&m_red), m_red (red) + { + // .. nothing yet .. + } + +private: + RED m_red; +}; + +} // namespace db + +#endif + diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc new file mode 100644 index 000000000..d1d193158 --- /dev/null +++ b/src/db/db/dbCircuit.cc @@ -0,0 +1,578 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbCircuit.h" +#include "dbNetlist.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// Circuit class implementation + +Circuit::Circuit () + : mp_netlist (0), + m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), + m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), + m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), + m_device_by_name (this, &Circuit::begin_devices, &Circuit::end_devices), + m_subcircuit_by_name (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), + m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets), + m_index (0) +{ + m_devices.changed ().add (this, &Circuit::devices_changed); + m_nets.changed ().add (this, &Circuit::nets_changed); + m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed); +} + +Circuit::Circuit (const Circuit &other) + : gsi::ObjectBase (other), tl::Object (other), mp_netlist (0), + m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), + m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), + m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), + m_device_by_name (this, &Circuit::begin_devices, &Circuit::end_devices), + m_subcircuit_by_name (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), + m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets), + m_index (0) +{ + operator= (other); + m_devices.changed ().add (this, &Circuit::devices_changed); + m_nets.changed ().add (this, &Circuit::nets_changed); + m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed); +} + +Circuit::~Circuit () +{ + m_devices.changed ().remove (this, &Circuit::devices_changed); + m_nets.changed ().remove (this, &Circuit::nets_changed); + m_subcircuits.changed ().remove (this, &Circuit::subcircuits_changed); + + // the default destructor will make the nets access "this" to unregister the + // objects - hence we have to do this explicitly. + m_nets.clear (); + m_subcircuits.clear (); + m_devices.clear (); +} + +Circuit &Circuit::operator= (const Circuit &other) +{ + if (this != &other) { + + clear (); + + m_name = other.m_name; + m_pins = other.m_pins; + + std::map device_table; + for (const_device_iterator i = other.begin_devices (); i != other.end_devices (); ++i) { + Device *d = new Device (*i); + device_table [i.operator-> ()] = d; + add_device (d); + } + + std::map sc_table; + for (const_subcircuit_iterator i = other.begin_subcircuits (); i != other.end_subcircuits (); ++i) { + SubCircuit *sc = new SubCircuit (*i); + sc_table [i.operator-> ()] = sc; + add_subcircuit (sc); + } + + for (const_net_iterator i = other.begin_nets (); i != other.end_nets (); ++i) { + + // translate the net + Net *n = new Net (); + n->set_cluster_id (i->cluster_id ()); + n->set_name (i->name ()); + add_net (n); + + for (Net::const_terminal_iterator p = i->begin_terminals (); p != i->end_terminals (); ++p) { + std::map::const_iterator m = device_table.find (p->device ()); + tl_assert (m != device_table.end ()); + n->add_terminal (NetTerminalRef (m->second, p->terminal_id ())); + } + + for (Net::const_pin_iterator p = i->begin_pins (); p != i->end_pins (); ++p) { + n->add_pin (NetPinRef (p->pin_id ())); + } + + for (Net::const_subcircuit_pin_iterator p = i->begin_subcircuit_pins (); p != i->end_subcircuit_pins (); ++p) { + std::map::const_iterator m = sc_table.find (p->subcircuit ()); + tl_assert (m != sc_table.end ()); + n->add_subcircuit_pin (NetSubcircuitPinRef (m->second, p->pin_id ())); + } + + } + + } + + return *this; +} + +void Circuit::set_netlist (Netlist *netlist) +{ + mp_netlist = netlist; +} + +const Pin *Circuit::pin_by_id (size_t id) const +{ + if (id >= m_pins.size ()) { + return 0; + } else { + return &m_pins [id]; + } +} + +const Pin *Circuit::pin_by_name (const std::string &name) const +{ + for (Circuit::const_pin_iterator p = begin_pins (); p != end_pins (); ++p) { + if (p->name () == name) { + return p.operator-> (); + } + } + return 0; +} + +void Circuit::devices_changed () +{ + m_device_by_id.invalidate (); + m_device_by_name.invalidate (); +} + +void Circuit::subcircuits_changed () +{ + m_subcircuit_by_id.invalidate (); + m_subcircuit_by_name.invalidate (); + + if (mp_netlist) { + mp_netlist->invalidate_topology (); + } +} + +void Circuit::nets_changed () +{ + m_net_by_cluster_id.invalidate (); + m_net_by_name.invalidate (); +} + +void Circuit::clear () +{ + m_name.clear (); + m_pins.clear (); + m_devices.clear (); + m_nets.clear (); + m_subcircuits.clear (); +} + +void Circuit::set_name (const std::string &name) +{ + m_name = name; + if (mp_netlist) { + mp_netlist->m_circuit_by_name.invalidate (); + } +} + +void Circuit::set_cell_index (const db::cell_index_type ci) +{ + m_cell_index = ci; + if (mp_netlist) { + mp_netlist->m_circuit_by_cell_index.invalidate (); + } +} + +Circuit::child_circuit_iterator Circuit::begin_children () +{ + tl_assert (mp_netlist != 0); + return mp_netlist->child_circuits (this).begin (); +} + +Circuit::child_circuit_iterator Circuit::end_children () +{ + tl_assert (mp_netlist != 0); + return mp_netlist->child_circuits (this).end (); +} + +Circuit::const_child_circuit_iterator Circuit::begin_children () const +{ + tl_assert (mp_netlist != 0); + return reinterpret_cast &> (mp_netlist->child_circuits (const_cast (this))).begin (); +} + +Circuit::const_child_circuit_iterator Circuit::end_children () const +{ + tl_assert (mp_netlist != 0); + return reinterpret_cast &> (mp_netlist->child_circuits (const_cast (this))).end (); +} + +Circuit::child_circuit_iterator Circuit::begin_parents () +{ + tl_assert (mp_netlist != 0); + return mp_netlist->parent_circuits (this).begin (); +} + +Circuit::child_circuit_iterator Circuit::end_parents () +{ + tl_assert (mp_netlist != 0); + return mp_netlist->parent_circuits (this).end (); +} + +Circuit::const_child_circuit_iterator Circuit::begin_parents () const +{ + tl_assert (mp_netlist != 0); + return reinterpret_cast &> (mp_netlist->parent_circuits (const_cast (this))).begin (); +} + +Circuit::const_child_circuit_iterator Circuit::end_parents () const +{ + tl_assert (mp_netlist != 0); + return reinterpret_cast &> (mp_netlist->parent_circuits (const_cast (this))).end (); +} + +const Pin &Circuit::add_pin (const std::string &name) +{ + m_pins.push_back (Pin (name)); + m_pins.back ().set_id (m_pins.size () - 1); + return m_pins.back (); +} + +void Circuit::add_net (Net *net) +{ + m_nets.push_back (net); + net->set_circuit (this); +} + +void Circuit::remove_net (Net *net) +{ + m_nets.erase (net); +} + +void Circuit::add_device (Device *device) +{ + device->set_circuit (this); + + size_t id = 0; + if (! m_devices.empty ()) { + tl_assert (m_devices.back () != 0); + id = m_devices.back ()->id (); + } + device->set_id (id + 1); + + m_devices.push_back (device); +} + +void Circuit::remove_device (Device *device) +{ + m_devices.erase (device); +} + +void Circuit::add_subcircuit (SubCircuit *subcircuit) +{ + subcircuit->set_circuit (this); + + size_t id = 0; + if (! m_subcircuits.empty ()) { + tl_assert (m_subcircuits.back () != 0); + id = m_subcircuits.back ()->id (); + } + subcircuit->set_id (id + 1); + + m_subcircuits.push_back (subcircuit); +} + +void Circuit::remove_subcircuit (SubCircuit *subcircuit) +{ + m_subcircuits.erase (subcircuit); +} + +void Circuit::register_ref (SubCircuit *r) +{ + m_refs.push_back (r); +} + +void Circuit::unregister_ref (SubCircuit *r) +{ + m_refs.erase (r); +} + +void Circuit::translate_circuits (const std::map &map) +{ + for (subcircuit_iterator i = m_subcircuits.begin (); i != m_subcircuits.end (); ++i) { + std::map::const_iterator m = map.find (i->circuit_ref ()); + tl_assert (m != map.end ()); + i->set_circuit_ref (m->second); + } +} + +void Circuit::translate_device_classes (const std::map &map) +{ + for (device_iterator i = m_devices.begin (); i != m_devices.end (); ++i) { + std::map::const_iterator m = map.find (i->device_class ()); + tl_assert (m != map.end ()); + i->set_device_class (m->second); + } +} + +void Circuit::translate_device_abstracts (const std::map &map) +{ + for (device_iterator i = m_devices.begin (); i != m_devices.end (); ++i) { + if (i->device_abstract ()) { + std::map::const_iterator m = map.find (i->device_abstract ()); + tl_assert (m != map.end ()); + i->set_device_abstract (m->second); + } + } +} + +void Circuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter) +{ + if (m_pin_refs.size () < pin_id + 1) { + m_pin_refs.resize (pin_id + 1, Net::pin_iterator ()); + } + m_pin_refs [pin_id] = iter; +} + +const Net *Circuit::net_for_pin (size_t pin_id) const +{ + if (pin_id < m_pin_refs.size ()) { + Net::pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::pin_iterator ()) { + return p->net (); + } + } + return 0; +} + +void Circuit::connect_pin (size_t pin_id, Net *net) +{ + if (net_for_pin (pin_id) == net) { + return; + } + + if (pin_id < m_pin_refs.size ()) { + Net::pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::pin_iterator () && p->net ()) { + p->net ()->erase_pin (p); + } + m_pin_refs [pin_id] = Net::pin_iterator (); + } + + if (net) { + net->add_pin (NetPinRef (pin_id)); + } +} + +void Circuit::purge_nets () +{ + std::vector nets_to_be_purged; + for (net_iterator n = begin_nets (); n != end_nets (); ++n) { + if (n->is_floating ()) { + nets_to_be_purged.push_back (n.operator-> ()); + } + } + for (std::vector::const_iterator n = nets_to_be_purged.begin (); n != nets_to_be_purged.end (); ++n) { + delete *n; + } +} + +/** + * @brief Sanity check for device to be removed + */ +static void check_device_before_remove (db::Circuit *c, const db::Device *d) +{ + if (d->device_class () == 0) { + throw tl::Exception (tl::to_string (tr ("Internal error: No device class after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name ()); + } + const std::vector &pd = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (d->net_for_terminal (p->id ()) != 0) { + throw tl::Exception (tl::to_string (tr ("Internal error: Terminal still connected after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name () + ", terminal=" + p->name ()); + } + } +} + +bool Circuit::combine_parallel_devices (const db::DeviceClass &cls) +{ + typedef std::vector key_type; + std::map > combination_candidates; + + bool any = false; + + // identify the candidates for combination - all devices sharing the same nets + // are candidates for combination in parallel mode + for (device_iterator d = begin_devices (); d != end_devices (); ++d) { + + if (tl::id_of (d->device_class ()) != tl::id_of (&cls)) { + continue; + } + + key_type k; + const std::vector &terminals = cls.terminal_definitions (); + for (std::vector::const_iterator p = terminals.begin (); p != terminals.end (); ++p) { + const db::Net *n = d->net_for_terminal (p->id ()); + if (n) { + k.push_back (n); + } + } + + std::sort (k.begin (), k.end ()); + k.erase (std::unique (k.begin (), k.end ()), k.end ()); + combination_candidates[k].push_back (d.operator-> ()); + + } + + // actually combine the devices + for (std::map >::iterator cc = combination_candidates.begin (); cc != combination_candidates.end (); ++cc) { + + std::vector &cl = cc->second; + for (size_t i = 0; i < cl.size () - 1; ++i) { + for (size_t j = i + 1; j < cl.size (); ) { + if (cls.combine_devices (cl [i], cl [j])) { + check_device_before_remove (this, cl [j]); // sanity check + delete cl [j]; + cl.erase (cl.begin () + j); + any = true; + } else { + ++j; + } + } + } + + } + + return any; +} + +static std::pair attached_two_devices (db::Net &net, const db::DeviceClass &cls) +{ + if (net.begin_pins () != net.end_pins ()) { + return std::make_pair ((db::Device *) 0, (db::Device *) 0); + } + + db::Device *d1 = 0, *d2 = 0; + + Net::terminal_iterator p = net.begin_terminals (); + if (p == net.end_terminals () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) { + return std::make_pair ((db::Device *) 0, (db::Device *) 0); + } else { + d1 = p->device (); + } + + ++p; + if (p == net.end_terminals () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) { + return std::make_pair ((db::Device *) 0, (db::Device *) 0); + } else { + d2 = p->device (); + } + + ++p; + if (p != net.end_terminals () || d1 == d2 || !d1 || !d2) { + return std::make_pair ((db::Device *) 0, (db::Device *) 0); + } else { + return std::make_pair (d1, d2); + } +} + +template +static bool same_or_swapped (const std::pair &p1, const std::pair &p2) +{ + return (p1.first == p2.first && p1.second == p2.second) || (p1.first == p2.second && p1.second == p2.first); +} + +bool Circuit::combine_serial_devices(const db::DeviceClass &cls) +{ + bool any = false; + + for (net_iterator n = begin_nets (); n != end_nets (); ++n) { + + std::pair dd = attached_two_devices (*n, cls); + if (! dd.first) { + continue; + } + + // The net is an internal node: the devices attached to this internal node are + // combination candidates if the number of nets emerging from the attached device pair (not counting + // the internal node we just found) does not exceed the number of pins available for the + // new device. + + std::vector other_nets; + + const std::vector &terminals = cls.terminal_definitions (); + for (std::vector::const_iterator p = terminals.begin (); p != terminals.end (); ++p) { + db::Net *on; + on = dd.first->net_for_terminal (p->id ()); + if (on && ! same_or_swapped (dd, attached_two_devices (*on, cls))) { + other_nets.push_back (on); + } + on = dd.second->net_for_terminal (p->id ()); + if (on && ! same_or_swapped (dd, attached_two_devices (*on, cls))) { + other_nets.push_back (on); + } + } + + std::sort (other_nets.begin (), other_nets.end ()); + other_nets.erase (std::unique (other_nets.begin (), other_nets.end ()), other_nets.end ()); + + if (other_nets.size () <= cls.terminal_definitions().size ()) { + + // found a combination candidate + if (cls.combine_devices (dd.first, dd.second)) { + check_device_before_remove (this, dd.second); // sanity check + delete dd.second; + any = true; + } + + } + + } + + return any; +} + +void Circuit::combine_devices () +{ + tl_assert (netlist () != 0); + + for (Netlist::device_class_iterator dc = netlist ()->begin_device_classes (); dc != netlist ()->end_device_classes (); ++dc) { + + // repeat the combination step unless no combination happens - this is required to take care of combinations that arise after + // other combinations have been realized. + bool any = true; + while (any) { + + any = false; + + if (dc->supports_parallel_combination ()) { + if (combine_parallel_devices (*dc)) { + any = true; + } + } + if (dc->supports_serial_combination ()) { + if (combine_serial_devices (*dc)) { + any = true; + } + } + + } + + } +} + +} diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h new file mode 100644 index 000000000..276ac520b --- /dev/null +++ b/src/db/db/dbCircuit.h @@ -0,0 +1,635 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbCircuit +#define _HDR_dbCircuit + +#include "dbCommon.h" +#include "dbTypes.h" +#include "dbNet.h" +#include "dbDevice.h" +#include "dbPin.h" +#include "dbSubCircuit.h" +#include "dbNetlistUtils.h" + +#include "tlObject.h" +#include "tlObjectCollection.h" +#include "tlVector.h" +#include "gsiObject.h" + +namespace db +{ + +class Netlist; + +/** + * @brief A circuit + * + * A circuit is a list of nets, of subcircuit references and actual + * devices. + */ +class DB_PUBLIC Circuit + : public gsi::ObjectBase, public tl::Object +{ +public: + typedef tl::vector pin_list; + typedef pin_list::const_iterator const_pin_iterator; + typedef pin_list::iterator pin_iterator; + typedef tl::shared_collection device_list; + typedef device_list::const_iterator const_device_iterator; + typedef device_list::iterator device_iterator; + typedef tl::shared_collection net_list; + typedef net_list::const_iterator const_net_iterator; + typedef net_list::iterator net_iterator; + typedef tl::shared_collection subcircuit_list; + typedef subcircuit_list::const_iterator const_subcircuit_iterator; + typedef subcircuit_list::iterator subcircuit_iterator; + typedef tl::weak_collection::const_iterator const_refs_iterator; + typedef tl::weak_collection::iterator refs_iterator; + typedef tl::vector::const_iterator child_circuit_iterator; + typedef tl::vector::const_iterator const_child_circuit_iterator; + typedef tl::vector::const_iterator parent_circuit_iterator; + typedef tl::vector::const_iterator const_parent_circuit_iterator; + + /** + * @brief Constructor + * + * Creates an empty circuit. + */ + Circuit (); + + /** + * @brief Copy constructor + */ + Circuit (const Circuit &other); + + /** + * @brief Destructor + */ + ~Circuit (); + + /** + * @brief Assignment + */ + Circuit &operator= (const Circuit &other); + + /** + * @brief Gets the netlist the circuit lives in + * This pointer is 0 if the circuit is not part of a netlist. + */ + Netlist *netlist () + { + return mp_netlist; + } + + /** + * @brief Gets the netlist the circuit lives in (const version) + * This pointer is 0 if the circuit is not part of a netlist. + */ + const Netlist *netlist () const + { + return mp_netlist; + } + + /** + * @brief Clears the circuit + */ + void clear (); + + /** + * @brief Sets the name of the circuit + */ + void set_name (const std::string &name); + + /** + * @brief Gets the name of the circuit + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief The index of the circuit in the netlist + * CAUTION: this attribute is used for internal purposes and may not be valid always. + */ + size_t index () const + { + return m_index; + } + + /** + * @brief Sets the layout cell reference for this circuit + * + * The layout cell reference links a circuit to a layout cell. + */ + void set_cell_index (const db::cell_index_type ci); + + /** + * @brief Gets the layout cell index + */ + db::cell_index_type cell_index () const + { + return m_cell_index; + } + + /** + * @brief Gets the references to this circuit (begin, non-const version) + * This iterator will deliver all subcircuits referencing this circuit + */ + refs_iterator begin_refs () + { + return m_refs.begin (); + } + + /** + * @brief Gets the references to this circuit (end, non-const version) + * This iterator will deliver all subcircuits referencing this circuit + */ + refs_iterator end_refs () + { + return m_refs.end (); + } + + /** + * @brief Gets the references to this circuit (begin, const version) + * This iterator will deliver all subcircuits referencing this circuit + */ + const_refs_iterator begin_refs () const + { + return m_refs.begin (); + } + + /** + * @brief Gets the child circuits iterator (begin) + * The child circuits are the circuits referenced by all subcircuits + * in the circuit. + */ + child_circuit_iterator begin_children (); + + /** + * @brief Gets the child circuits iterator (end) + */ + child_circuit_iterator end_children (); + + /** + * @brief Gets the child circuits iterator (begin, const version) + * The child circuits are the circuits referenced by all subcircuits + * in the circuit. + */ + const_child_circuit_iterator begin_children () const; + + /** + * @brief Gets the child circuits iterator (end, const version) + */ + const_child_circuit_iterator end_children () const; + + /** + * @brief Gets the parent circuits iterator (begin) + * The parents circuits are the circuits referencing this circuit via subcircuits. + */ + parent_circuit_iterator begin_parents (); + + /** + * @brief Gets the parent circuits iterator (end) + */ + parent_circuit_iterator end_parents (); + + /** + * @brief Gets the parent circuits iterator (begin, const version) + * The parents circuits are the circuits referencing this circuit via subcircuits. + */ + const_parent_circuit_iterator begin_parents () const; + + /** + * @brief Gets the parent circuits iterator (end, const version) + */ + const_parent_circuit_iterator end_parents () const; + + /** + * @brief Gets the references to this circuit (end, const version) + * This iterator will deliver all subcircuits referencing this circuit + */ + const_refs_iterator end_refs () const + { + return m_refs.end (); + } + + /** + * @brief Adds a pin to this circuit + * The circuit takes over ownership of the object. + */ + const Pin &add_pin(const std::string &name); + + /** + * @brief Begin iterator for the pins of the circuit (non-const version) + */ + pin_iterator begin_pins () + { + return m_pins.begin (); + } + + /** + * @brief End iterator for the pins of the circuit (non-const version) + */ + pin_iterator end_pins () + { + return m_pins.end (); + } + + /** + * @brief Gets the pin count + */ + size_t pin_count () const + { + return m_pins.size (); + } + + /** + * @brief Gets the pin by ID (the ID is basically the index) + */ + const Pin *pin_by_id (size_t id) const; + + /** + * @brief Gets the pin by name + * + * If there is no pin with that name, null is returned. + * NOTE: this is a linear search, so it's performance may not be good for many pins. + */ + const Pin *pin_by_name (const std::string &name) const; + + /** + * @brief Begin iterator for the pins of the circuit (const version) + */ + const_pin_iterator begin_pins () const + { + return m_pins.begin (); + } + + /** + * @brief End iterator for the pins of the circuit (const version) + */ + const_pin_iterator end_pins () const + { + return m_pins.end (); + } + + /** + * @brief Adds a net to this circuit + * + * The circuit takes over ownership of the object. + */ + void add_net (Net *net); + + /** + * @brief Deletes a net from the circuit + */ + void remove_net (Net *net); + + /** + * @brief Begin iterator for the nets of the circuit (non-const version) + */ + net_iterator begin_nets () + { + return m_nets.begin (); + } + + /** + * @brief End iterator for the nets of the circuit (non-const version) + */ + net_iterator end_nets () + { + return m_nets.end (); + } + + /** + * @brief Begin iterator for the nets of the circuit (const version) + */ + const_net_iterator begin_nets () const + { + return m_nets.begin (); + } + + /** + * @brief End iterator for the nets of the circuit (const version) + */ + const_net_iterator end_nets () const + { + return m_nets.end (); + } + + /** + * @brief Gets the net from a given cluster ID (const version) + * + * If the cluster ID is not valid, null is returned. + */ + const Net *net_by_cluster_id (size_t cluster_id) const + { + return (const_cast (this)->net_by_cluster_id (cluster_id)); + } + + /** + * @brief Gets the net from a given cluster ID (non-const version) + * + * If the cluster ID is not valid, null is returned. + */ + Net *net_by_cluster_id (size_t cluster_id) + { + return m_net_by_cluster_id.object_by (cluster_id); + } + + /** + * @brief Gets the net from a given name (const version) + * + * If the name is not valid, null is returned. + */ + const Net *net_by_name (const std::string &name) const + { + return (const_cast (this)->net_by_name (name)); + } + + /** + * @brief Gets the net from a given name (non-const version) + * + * If the name is not valid, null is returned. + */ + Net *net_by_name (const std::string &name) + { + return m_net_by_name.object_by (name); + } + + /** + * @brief Adds a device to this circuit + * + * The circuit takes over ownership of the object. + */ + void add_device (Device *device); + + /** + * @brief Deletes a device from the circuit + */ + void remove_device (Device *device); + + /** + * @brief Gets the device from a given ID (const version) + * + * If the ID is not valid, null is returned. + */ + const Device *device_by_id (size_t id) const + { + return (const_cast (this)->device_by_id (id)); + } + + /** + * @brief Gets the device from a given ID (non-const version) + * + * If the ID is not valid, null is returned. + */ + Device *device_by_id (size_t id) + { + return m_device_by_id.object_by (id); + } + + /** + * @brief Gets the device from a given name (const version) + * + * If the name is not valid, null is returned. + */ + const Device *device_by_name (const std::string &name) const + { + return (const_cast (this)->device_by_name (name)); + } + + /** + * @brief Gets the device from a given name (non-const version) + * + * If the name is not valid, null is returned. + */ + Device *device_by_name (const std::string &name) + { + return m_device_by_name.object_by (name); + } + + /** + * @brief Begin iterator for the devices of the circuit (non-const version) + */ + device_iterator begin_devices () + { + return m_devices.begin (); + } + + /** + * @brief End iterator for the devices of the circuit (non-const version) + */ + device_iterator end_devices () + { + return m_devices.end (); + } + + /** + * @brief Begin iterator for the devices of the circuit (const version) + */ + const_device_iterator begin_devices () const + { + return m_devices.begin (); + } + + /** + * @brief End iterator for the devices of the circuit (const version) + */ + const_device_iterator end_devices () const + { + return m_devices.end (); + } + + /** + * @brief Adds a subcircuit to this circuit + * + * The circuit takes over ownership of the object. + */ + void add_subcircuit (SubCircuit *subcircuit); + + /** + * @brief Deletes a subcircuit from the circuit + */ + void remove_subcircuit (SubCircuit *subcircuit); + + /** + * @brief Gets the subcircuit from a given ID (const version) + * + * If the ID is not valid, null is returned. + */ + const SubCircuit *subcircuit_by_id (size_t id) const + { + return (const_cast (this)->subcircuit_by_id (id)); + } + + /** + * @brief Gets the subcircuit from a given ID (non-const version) + * + * If the ID is not valid, null is returned. + */ + SubCircuit *subcircuit_by_id (size_t id) + { + return m_subcircuit_by_id.object_by (id); + } + + /** + * @brief Gets the subcircuit from a given name (const version) + * + * If the name is not valid, null is returned. + */ + const SubCircuit *subcircuit_by_name (const std::string &name) const + { + return (const_cast (this)->subcircuit_by_name (name)); + } + + /** + * @brief Gets the subcircuit from a given name (non-const version) + * + * If the name is not valid, null is returned. + */ + SubCircuit *subcircuit_by_name (const std::string &name) + { + return m_subcircuit_by_name.object_by (name); + } + + /** + * @brief Begin iterator for the subcircuits of the circuit (non-const version) + */ + subcircuit_iterator begin_subcircuits () + { + return m_subcircuits.begin (); + } + + /** + * @brief End iterator for the subcircuits of the circuit (non-const version) + */ + subcircuit_iterator end_subcircuits () + { + return m_subcircuits.end (); + } + + /** + * @brief Begin iterator for the subcircuits of the circuit (const version) + */ + const_subcircuit_iterator begin_subcircuits () const + { + return m_subcircuits.begin (); + } + + /** + * @brief End iterator for the subcircuits of the circuit (const version) + */ + const_subcircuit_iterator end_subcircuits () const + { + return m_subcircuits.end (); + } + + /** + * @brief Gets the connected net for a pin with the given id + * + * Returns 0 if the pin is not connected to a net. + */ + const Net *net_for_pin (size_t pin_id) const; + + /** + * @brief Gets the connected net for a pin with the given id (non-const version) + * + * Returns 0 if the pin is not connected to a net. + */ + Net *net_for_pin (size_t pin_id) + { + return const_cast (((const Circuit *) this)->net_for_pin (pin_id)); + } + + /** + * @brief Connects the given pin to the given net + * If the net is 0 the pin is disconnected. + * If non-null, a NetPinRef object will be inserted into the + * net and connected with the given pin. + */ + void connect_pin (size_t pin_id, Net *net); + + /** + * @brief Purge unused nets + * + * This method will purge all nets which return "floating". + */ + void purge_nets (); + + /** + * @brief Combine devices + * + * This method will combine devices that can be combined according + * to their device classes "combine_devices" method. + */ + void combine_devices (); + +private: + friend class Netlist; + friend class Net; + friend class SubCircuit; + friend class Device; + + std::string m_name; + db::cell_index_type m_cell_index; + net_list m_nets; + pin_list m_pins; + device_list m_devices; + subcircuit_list m_subcircuits; + Netlist *mp_netlist; + std::vector m_pin_refs; + object_by_attr > m_device_by_id; + object_by_attr > m_subcircuit_by_id; + object_by_attr > m_net_by_cluster_id; + object_by_attr > m_device_by_name; + object_by_attr > m_subcircuit_by_name; + object_by_attr > m_net_by_name; + tl::weak_collection m_refs; + size_t m_index; + + void set_index (size_t index) + { + m_index = index; + } + + void set_pin_ref_for_pin (size_t ppin_id, Net::pin_iterator iter); + + void register_ref (SubCircuit *sc); + void unregister_ref (SubCircuit *sc); + + void translate_circuits (const std::map &map); + void translate_device_classes (const std::map &map); + void translate_device_abstracts (const std::map &map); + void set_netlist (Netlist *netlist); + bool combine_parallel_devices (const db::DeviceClass &cls); + bool combine_serial_devices (const db::DeviceClass &cls); + + void devices_changed (); + void subcircuits_changed (); + void nets_changed (); +}; + +} + +#endif diff --git a/src/db/db/dbDeepEdgePairs.cc b/src/db/db/dbDeepEdgePairs.cc new file mode 100644 index 000000000..316da3271 --- /dev/null +++ b/src/db/db/dbDeepEdgePairs.cc @@ -0,0 +1,336 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbDeepEdgePairs.h" +#include "dbCellGraphUtils.h" +#include "dbDeepEdges.h" +#include "dbDeepRegion.h" +#include "dbCellMapping.h" +#include "dbLayoutUtils.h" + +#include + +namespace db +{ + +/** + * @brief An iterator delegate for the deep edge pair collection + * TODO: this is kind of redundant with OriginalLayerIterator .. + */ +class DB_PUBLIC DeepEdgePairsIterator + : public EdgePairsIteratorDelegate +{ +public: + DeepEdgePairsIterator (const db::RecursiveShapeIterator &iter) + : m_iter (iter) + { + set (); + } + + virtual ~DeepEdgePairsIterator () { } + + virtual bool at_end () const + { + return m_iter.at_end (); + } + + virtual void increment () + { + ++m_iter; + set (); + } + + virtual const value_type *get () const + { + return &m_edge_pair; + } + + virtual EdgePairsIteratorDelegate *clone () const + { + return new DeepEdgePairsIterator (*this); + } + +private: + friend class EdgePairs; + + db::RecursiveShapeIterator m_iter; + mutable value_type m_edge_pair; + + void set () const { + if (! m_iter.at_end ()) { + m_iter.shape ().edge_pair (m_edge_pair); + m_edge_pair.transform (m_iter.trans ()); + } + } +}; + + +DeepEdgePairs::DeepEdgePairs () + : AsIfFlatEdgePairs (), m_deep_layer () +{ + // .. nothing yet .. +} + +DeepEdgePairs::DeepEdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss) + : AsIfFlatEdgePairs (), m_deep_layer (dss.create_edge_pair_layer (si)) +{ + // .. nothing yet .. +} + +DeepEdgePairs::DeepEdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans) + : AsIfFlatEdgePairs (), m_deep_layer (dss.create_edge_pair_layer (si, trans)) +{ + // .. nothing yet .. +} + +DeepEdgePairs::DeepEdgePairs (const DeepEdgePairs &other) + : AsIfFlatEdgePairs (other), + m_deep_layer (other.m_deep_layer.copy ()) +{ + // .. nothing yet .. +} + +DeepEdgePairs::DeepEdgePairs (const DeepLayer &dl) + : AsIfFlatEdgePairs (), m_deep_layer (dl) +{ + // .. nothing yet .. +} + +DeepEdgePairs::~DeepEdgePairs () +{ + // .. nothing yet .. +} + +EdgePairsDelegate *DeepEdgePairs::clone () const +{ + return new DeepEdgePairs (*this); +} + +EdgePairsIteratorDelegate *DeepEdgePairs::begin () const +{ + return new DeepEdgePairsIterator (begin_iter ().first); +} + +std::pair DeepEdgePairs::begin_iter () const +{ + const db::Layout &layout = m_deep_layer.layout (); + if (layout.cells () == 0) { + + return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); + + } else { + + const db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + db::RecursiveShapeIterator iter (m_deep_layer.layout (), top_cell, m_deep_layer.layer ()); + return std::make_pair (iter, db::ICplxTrans ()); + + } +} + +size_t DeepEdgePairs::size () const +{ + size_t n = 0; + + const db::Layout &layout = m_deep_layer.layout (); + db::CellCounter cc (&layout); + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + n += cc.weight (*c) * layout.cell (*c).shapes (m_deep_layer.layer ()).size (); + } + + return n; +} + +std::string DeepEdgePairs::to_string (size_t nmax) const +{ + return db::AsIfFlatEdgePairs::to_string (nmax); +} + +Box DeepEdgePairs::bbox () const +{ + return m_deep_layer.initial_cell ().bbox (m_deep_layer.layer ()); +} + +bool DeepEdgePairs::empty () const +{ + return begin_iter ().first.at_end (); +} + +const db::EdgePair *DeepEdgePairs::nth (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat edge pair collections"))); +} + +bool DeepEdgePairs::has_valid_edge_pairs () const +{ + return false; +} + +const db::RecursiveShapeIterator *DeepEdgePairs::iter () const +{ + return 0; +} + +EdgePairsDelegate * +DeepEdgePairs::add_in_place (const EdgePairs &other) +{ + if (other.empty ()) { + return this; + } + + const DeepEdgePairs *other_deep = dynamic_cast (other.delegate ()); + if (other_deep) { + + deep_layer ().add_from (other_deep->deep_layer ()); + + } else { + + // non-deep to deep merge (flat) + + db::Shapes &shapes = deep_layer ().initial_cell ().shapes (deep_layer ().layer ()); + for (db::EdgePairs::const_iterator p = other.begin (); ! p.at_end (); ++p) { + shapes.insert (*p); + } + + } + + return this; +} + +EdgePairsDelegate *DeepEdgePairs::add (const EdgePairs &other) const +{ + if (other.empty ()) { + return clone (); + } else if (empty ()) { + return other.delegate ()->clone (); + } else { + DeepEdgePairs *new_edge_pairs = dynamic_cast (clone ()); + new_edge_pairs->add_in_place (other); + return new_edge_pairs; + } +} + +EdgePairsDelegate *DeepEdgePairs::filter_in_place (const EdgePairFilterBase &filter) +{ + // TODO: implement + return AsIfFlatEdgePairs::filter_in_place (filter); +} + +EdgePairsDelegate *DeepEdgePairs::filtered (const EdgePairFilterBase &filter) const +{ + // TODO: implement + return AsIfFlatEdgePairs::filtered (filter); +} + +RegionDelegate *DeepEdgePairs::polygons (db::Coord e) const +{ + db::DeepLayer new_layer = m_deep_layer.derived (); + db::Layout &layout = const_cast (m_deep_layer.layout ()); + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + db::Shapes &output = c->shapes (new_layer.layer ()); + for (db::Shapes::shape_iterator s = c->shapes (m_deep_layer.layer ()).begin (db::ShapeIterator::EdgePairs); ! s.at_end (); ++s) { + db::Polygon poly = s->edge_pair ().normalized ().to_polygon (e); + if (poly.vertices () >= 3) { + output.insert (poly); + } + } + } + + return new db::DeepRegion (new_layer); +} + +EdgesDelegate *DeepEdgePairs::generic_edges (bool first, bool second) const +{ + db::DeepLayer new_layer = m_deep_layer.derived (); + db::Layout &layout = const_cast (m_deep_layer.layout ()); + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + db::Shapes &output = c->shapes (new_layer.layer ()); + for (db::Shapes::shape_iterator s = c->shapes (m_deep_layer.layer ()).begin (db::ShapeIterator::EdgePairs); ! s.at_end (); ++s) { + db::EdgePair ep = s->edge_pair (); + if (first) { + output.insert (ep.first ()); + } + if (second) { + output.insert (ep.second ()); + } + } + } + + return new db::DeepEdges (new_layer); +} + +EdgesDelegate *DeepEdgePairs::edges () const +{ + return generic_edges (true, true); +} + +EdgesDelegate *DeepEdgePairs::first_edges () const +{ + return generic_edges (true, false); +} + +EdgesDelegate *DeepEdgePairs::second_edges () const +{ + return generic_edges (false, true); +} + +EdgePairsDelegate *DeepEdgePairs::in (const EdgePairs &other, bool invert) const +{ + // TODO: implement + return AsIfFlatEdgePairs::in (other, invert); +} + +bool DeepEdgePairs::equals (const EdgePairs &other) const +{ + const DeepEdgePairs *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && &other_delegate->m_deep_layer.layout () == &m_deep_layer.layout () + && other_delegate->m_deep_layer.layer () == m_deep_layer.layer ()) { + return true; + } else { + return AsIfFlatEdgePairs::equals (other); + } +} + +bool DeepEdgePairs::less (const EdgePairs &other) const +{ + const DeepEdgePairs *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && &other_delegate->m_deep_layer.layout () == &m_deep_layer.layout ()) { + return other_delegate->m_deep_layer.layer () < m_deep_layer.layer (); + } else { + return AsIfFlatEdgePairs::less (other); + } +} + +void DeepEdgePairs::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + m_deep_layer.insert_into (layout, into_cell, into_layer); +} + +void DeepEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const +{ + m_deep_layer.insert_into_as_polygons (layout, into_cell, into_layer, enl); +} + + +} diff --git a/src/db/db/dbDeepEdgePairs.h b/src/db/db/dbDeepEdgePairs.h new file mode 100644 index 000000000..34360ea86 --- /dev/null +++ b/src/db/db/dbDeepEdgePairs.h @@ -0,0 +1,105 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbDeepEdgePairs +#define HDR_dbDeepEdgePairs + +#include "dbCommon.h" + +#include "dbAsIfFlatEdgePairs.h" +#include "dbDeepShapeStore.h" +#include "dbEdgePairs.h" + +namespace db { + +/** + * @brief Provides hierarchical edges implementation + */ +class DB_PUBLIC DeepEdgePairs + : public db::AsIfFlatEdgePairs +{ +public: + DeepEdgePairs (); + DeepEdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss); + DeepEdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans); + + DeepEdgePairs (const DeepEdgePairs &other); + DeepEdgePairs (const DeepLayer &dl); + + virtual ~DeepEdgePairs (); + + EdgePairsDelegate *clone () const; + + virtual EdgePairsIteratorDelegate *begin () const; + virtual std::pair begin_iter () const; + + virtual size_t size () const; + virtual std::string to_string (size_t) const; + virtual Box bbox () const; + virtual bool empty () const; + virtual const db::EdgePair *nth (size_t n) const; + virtual bool has_valid_edge_pairs () const; + virtual const db::RecursiveShapeIterator *iter () const; + + virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter); + virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const; + + virtual EdgePairsDelegate *add_in_place (const EdgePairs &other); + virtual EdgePairsDelegate *add (const EdgePairs &other) const; + + virtual RegionDelegate *polygons (db::Coord e) const; + virtual EdgesDelegate *edges () const; + virtual EdgesDelegate *first_edges () const; + virtual EdgesDelegate *second_edges () const; + + virtual EdgePairsDelegate *in (const EdgePairs &, bool) const; + + virtual bool equals (const EdgePairs &other) const; + virtual bool less (const EdgePairs &other) const; + + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + virtual void insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const; + + const DeepLayer &deep_layer () const + { + return m_deep_layer; + } + + DeepLayer &deep_layer () + { + return m_deep_layer; + } + +private: + DeepEdgePairs &operator= (const DeepEdgePairs &other); + + DeepLayer m_deep_layer; + + void init (); + EdgesDelegate *generic_edges (bool first, bool second) const; +}; + +} + +#endif + diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc new file mode 100644 index 000000000..e40c54898 --- /dev/null +++ b/src/db/db/dbDeepEdges.cc @@ -0,0 +1,1429 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbEdges.h" +#include "dbEdgesUtils.h" +#include "dbRegion.h" +#include "dbDeepEdges.h" +#include "dbDeepRegion.h" +#include "dbDeepEdgePairs.h" +#include "dbHierNetworkProcessor.h" +#include "dbCellGraphUtils.h" +#include "dbCellVariants.h" +#include "dbEdgeBoolean.h" +#include "dbCellMapping.h" +#include "dbLayoutUtils.h" +#include "dbLocalOperation.h" +#include "dbLocalOperationUtils.h" +#include "dbHierProcessor.h" +#include "dbEmptyEdges.h" + +namespace db +{ + +/** + * @brief An iterator delegate for the deep edge collection + * TODO: this is kind of redundant with OriginalLayerIterator .. + */ +class DB_PUBLIC DeepEdgesIterator + : public EdgesIteratorDelegate +{ +public: + DeepEdgesIterator (const db::RecursiveShapeIterator &iter) + : m_iter (iter) + { + set (); + } + + virtual ~DeepEdgesIterator () { } + + virtual bool at_end () const + { + return m_iter.at_end (); + } + + virtual void increment () + { + ++m_iter; + set (); + } + + virtual const value_type *get () const + { + return &m_edge; + } + + virtual EdgesIteratorDelegate *clone () const + { + return new DeepEdgesIterator (*this); + } + +private: + friend class Edges; + + db::RecursiveShapeIterator m_iter; + mutable value_type m_edge; + + void set () const + { + if (! m_iter.at_end ()) { + m_iter.shape ().edge (m_edge); + m_edge.transform (m_iter.trans ()); + } + } +}; + +// ------------------------------------------------------------------------------------------------------------- +// DeepEdges implementation + +DeepEdges::DeepEdges (const RecursiveShapeIterator &si, DeepShapeStore &dss, bool as_edges) + : AsIfFlatEdges (), m_deep_layer (dss.create_edge_layer (si, as_edges)), m_merged_edges () +{ + init (); +} + +DeepEdges::DeepEdges (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool as_edges, bool merged_semantics) + : AsIfFlatEdges (), m_deep_layer (dss.create_edge_layer (si, as_edges, trans)), m_merged_edges () +{ + init (); + set_merged_semantics (merged_semantics); +} + +DeepEdges::DeepEdges () + : AsIfFlatEdges () +{ + init (); +} + +DeepEdges::DeepEdges (const DeepLayer &dl) + : AsIfFlatEdges (), m_deep_layer (dl) +{ + init (); +} + +DeepEdges::~DeepEdges () +{ + // .. nothing yet .. +} + +DeepEdges::DeepEdges (const DeepEdges &other) + : AsIfFlatEdges (other), + m_deep_layer (other.m_deep_layer.copy ()), + m_merged_edges_valid (other.m_merged_edges_valid), + m_is_merged (other.m_is_merged) +{ + if (m_merged_edges_valid) { + m_merged_edges = other.m_merged_edges; + } +} + +void DeepEdges::init () +{ + m_merged_edges_valid = false; + m_merged_edges = db::DeepLayer (); + m_is_merged = false; +} + +EdgesDelegate * +DeepEdges::clone () const +{ + return new DeepEdges (*this); +} + +void DeepEdges::merged_semantics_changed () +{ + // .. nothing yet .. +} + +EdgesIteratorDelegate * +DeepEdges::begin () const +{ + return new DeepEdgesIterator (begin_iter ().first); +} + +EdgesIteratorDelegate * +DeepEdges::begin_merged () const +{ + if (! merged_semantics ()) { + return begin (); + } else { + return new DeepEdgesIterator (begin_merged_iter ().first); + } +} + +std::pair +DeepEdges::begin_iter () const +{ + const db::Layout &layout = m_deep_layer.layout (); + if (layout.cells () == 0) { + + return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); + + } else { + + const db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + db::RecursiveShapeIterator iter (m_deep_layer.layout (), top_cell, m_deep_layer.layer ()); + return std::make_pair (iter, db::ICplxTrans ()); + + } +} + +std::pair +DeepEdges::begin_merged_iter () const +{ + if (! merged_semantics ()) { + + return begin_iter (); + + } else { + + ensure_merged_edges_valid (); + + const db::Layout &layout = m_merged_edges.layout (); + if (layout.cells () == 0) { + + return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); + + } else { + + const db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + db::RecursiveShapeIterator iter (m_merged_edges.layout (), top_cell, m_merged_edges.layer ()); + return std::make_pair (iter, db::ICplxTrans ()); + + } + + } +} + +bool +DeepEdges::empty () const +{ + return begin_iter ().first.at_end (); +} + +bool +DeepEdges::is_merged () const +{ + return m_is_merged; +} + +const db::Edge * +DeepEdges::nth (size_t /*n*/) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat edge collections"))); +} + +bool +DeepEdges::has_valid_edges () const +{ + return false; +} + +bool +DeepEdges::has_valid_merged_edges () const +{ + return merged_semantics (); +} + +const db::RecursiveShapeIterator * +DeepEdges::iter () const +{ + return 0; +} + +bool DeepEdges::equals (const Edges &other) const +{ + const DeepEdges *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && &other_delegate->m_deep_layer.layout () == &m_deep_layer.layout () + && other_delegate->m_deep_layer.layer () == m_deep_layer.layer ()) { + return true; + } else { + return AsIfFlatEdges::equals (other); + } +} + +bool DeepEdges::less (const Edges &other) const +{ + const DeepEdges *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && &other_delegate->m_deep_layer.layout () == &m_deep_layer.layout ()) { + return other_delegate->m_deep_layer.layer () < m_deep_layer.layer (); + } else { + return AsIfFlatEdges::less (other); + } +} + +namespace { + +class ClusterMerger +{ +public: + ClusterMerger (unsigned int layer, const db::hier_clusters &hc, bool report_progress, const std::string &progress_desc) + : m_layer (layer), mp_hc (&hc), m_scanner (report_progress, progress_desc) + { + // .. nothing yet .. + } + + void set_base_verbosity (int /*vb*/) + { + /* TODO: No such thing currently: + m_scanner.set_base_verbosity (vb); + */ + } + + db::Shapes &merged (size_t cid, db::cell_index_type ci, bool initial = true) + { + std::map, db::Shapes>::iterator s = m_merged_cluster.find (std::make_pair (cid, ci)); + + // some sanity checks: initial clusters are single-use, are never generated twice and cannot be retrieved again + if (initial) { + tl_assert (s == m_merged_cluster.end ()); + m_done.insert (std::make_pair (cid, ci)); + } else { + tl_assert (m_done.find (std::make_pair (cid, ci)) == m_done.end ()); + } + + if (s != m_merged_cluster.end ()) { + return s->second; + } + + s = m_merged_cluster.insert (std::make_pair (std::make_pair (cid, ci), db::Shapes (false))).first; + + const db::connected_clusters &cc = mp_hc->clusters_per_cell (ci); + const db::local_cluster &c = cc.cluster_by_id (cid); + + std::list > merged_child_clusters; + + const db::connected_clusters::connections_type &conn = cc.connections_for_cluster (cid); + for (db::connected_clusters::connections_type::const_iterator i = conn.begin (); i != conn.end (); ++i) { + const db::Shapes &cc_shapes = merged (i->id (), i->inst_cell_index (), false); + merged_child_clusters.push_back (std::make_pair (&cc_shapes, i->inst_trans ())); + } + + // collect the edges to merge .. + + std::list heap; + m_scanner.clear (); + + for (std::list >::const_iterator i = merged_child_clusters.begin (); i != merged_child_clusters.end (); ++i) { + for (db::Shapes::shape_iterator s = i->first->begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + if (s->is_edge ()) { + heap.push_back (s->edge ().transformed (i->second)); + m_scanner.insert (&heap.back (), 0); + } + } + } + + for (db::local_cluster::shape_iterator s = c.begin (m_layer); !s.at_end (); ++s) { + heap.push_back (*s); + m_scanner.insert (&heap.back (), 0); + } + + // .. and run the merge operation + + s->second.clear (); + EdgeBooleanClusterCollector cluster_collector (&s->second, EdgeOr); + m_scanner.process (cluster_collector, 1, db::box_convert ()); + + return s->second; + } + +private: + std::map, db::Shapes> m_merged_cluster; + std::set > m_done; + unsigned int m_layer; + const db::hier_clusters *mp_hc; + db::box_scanner m_scanner; +}; + +} + +void +DeepEdges::ensure_merged_edges_valid () const +{ + if (! m_merged_edges_valid) { + + if (m_is_merged) { + + // NOTE: this will reuse the deep layer reference + m_merged_edges = m_deep_layer; + + } else { + + m_merged_edges = m_deep_layer.derived (); + + tl::SelfTimer timer (tl::verbosity () > base_verbosity (), "Ensure merged polygons"); + + db::Layout &layout = const_cast (m_deep_layer.layout ()); + + db::hier_clusters hc; + db::Connectivity conn; + conn.connect (m_deep_layer); + hc.set_base_verbosity (base_verbosity() + 10); + hc.build (layout, m_deep_layer.initial_cell (), db::ShapeIterator::Edges, conn); + + // collect the clusters and merge them into big polygons + // NOTE: using the ClusterMerger we merge bottom-up forming bigger and bigger polygons. This is + // hopefully more efficient that collecting everything and will lead to reuse of parts. + + ClusterMerger cm (m_deep_layer.layer (), hc, report_progress (), progress_desc ()); + cm.set_base_verbosity (base_verbosity () + 10); + + // TODO: iterate only over the called cells? + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + const db::connected_clusters &cc = hc.clusters_per_cell (c->cell_index ()); + for (db::connected_clusters::all_iterator cl = cc.begin_all (); ! cl.at_end (); ++cl) { + if (cc.is_root (*cl)) { + db::Shapes &s = cm.merged (*cl, c->cell_index ()); + c->shapes (m_merged_edges.layer ()).insert (s); + s.clear (); // not needed anymore + } + } + } + + } + + m_merged_edges_valid = true; + + } +} + +void +DeepEdges::set_is_merged (bool f) +{ + m_is_merged = f; + m_merged_edges_valid = false; +} + +void +DeepEdges::insert_into (db::Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + m_deep_layer.insert_into (layout, into_cell, into_layer); +} + +size_t DeepEdges::size () const +{ + size_t n = 0; + + const db::Layout &layout = m_deep_layer.layout (); + db::CellCounter cc (&layout); + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + n += cc.weight (*c) * layout.cell (*c).shapes (m_deep_layer.layer ()).size (); + } + + return n; +} + +Box DeepEdges::bbox () const +{ + return m_deep_layer.initial_cell ().bbox (m_deep_layer.layer ()); +} + +DeepEdges::length_type DeepEdges::length (const db::Box &box) const +{ + if (box.empty ()) { + + ensure_merged_edges_valid (); + + db::MagnificationReducer red; + db::cell_variants_collector vars (red); + vars.collect (m_merged_edges.layout (), m_merged_edges.initial_cell ()); + + DeepEdges::length_type l = 0; + + const db::Layout &layout = m_merged_edges.layout (); + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + DeepEdges::length_type lc = 0; + for (db::ShapeIterator s = layout.cell (*c).shapes (m_merged_edges.layer ()).begin (db::ShapeIterator::Edges); ! s.at_end (); ++s) { + lc += s->edge ().length (); + } + const std::map &vv = vars.variants (*c); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + double mag = v->first.mag (); + l += v->second * lc * mag; + } + } + + return l; + + } else { + // In the clipped case fall back to flat mode + return db::AsIfFlatEdges::length (box); + } +} + +std::string DeepEdges::to_string (size_t nmax) const +{ + return db::AsIfFlatEdges::to_string (nmax); +} + +EdgesDelegate *DeepEdges::process_in_place (const EdgeProcessorBase &filter) +{ + // TODO: implement to be really in-place + return processed (filter); +} + +EdgesDelegate * +DeepEdges::processed (const EdgeProcessorBase &filter) const +{ + return processed_impl (filter); +} + +EdgePairsDelegate * +DeepEdges::processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &filter) const +{ + return processed_impl (filter); +} + +RegionDelegate * +DeepEdges::processed_to_polygons (const EdgeToPolygonProcessorBase &filter) const +{ + return processed_impl (filter); +} + +namespace +{ + +template struct delivery; + +template <> +struct delivery +{ + delivery (db::Layout *layout, db::Shapes *shapes) + : mp_layout (layout), mp_shapes (shapes) + { } + + void put (const db::Polygon &result) + { + tl::MutexLocker locker (&mp_layout->lock ()); + mp_shapes->insert (db::PolygonRef (result, mp_layout->shape_repository ())); + } + +private: + db::Layout *mp_layout; + db::Shapes *mp_shapes; +}; + +template +struct delivery +{ + delivery (db::Layout *, db::Shapes *shapes) + : mp_shapes (shapes) + { } + + void put (const Result &result) + { + mp_shapes->insert (result); + } + +private: + db::Shapes *mp_shapes; +}; + +} + +template +OutputContainer * +DeepEdges::processed_impl (const edge_processor &filter) const +{ + if (! filter.requires_raw_input ()) { + ensure_merged_edges_valid (); + } + + std::auto_ptr vars; + if (filter.vars ()) { + + vars.reset (new db::VariantsCollectorBase (filter.vars ())); + + vars->collect (m_deep_layer.layout (), m_deep_layer.initial_cell ()); + + if (filter.wants_variants ()) { + const_cast (m_deep_layer).separate_variants (*vars); + } + + } + + db::Layout &layout = const_cast (m_deep_layer.layout ()); + + std::vector heap; + std::map > to_commit; + + std::auto_ptr res (new OutputContainer (m_deep_layer.derived ())); + if (filter.result_must_not_be_merged ()) { + res->set_merged_semantics (false); + } + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &s = c->shapes (filter.requires_raw_input () ? m_deep_layer.layer () : m_merged_edges.layer ()); + + if (vars.get ()) { + + const std::map &vv = vars->variants (c->cell_index ()); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + + db::Shapes *st; + if (vv.size () == 1) { + st = & c->shapes (res->deep_layer ().layer ()); + } else { + st = & to_commit [c->cell_index ()] [v->first]; + } + + delivery delivery (&layout, st); + + const db::ICplxTrans &tr = v->first; + db::ICplxTrans trinv = tr.inverted (); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) { + heap.clear (); + filter.process (si->edge ().transformed (tr), heap); + for (typename std::vector::const_iterator i = heap.begin (); i != heap.end (); ++i) { + delivery.put (i->transformed (trinv)); + } + } + + } + + } else { + + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + delivery delivery (&layout, &st); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) { + filter.process (si->edge (), heap); + for (typename std::vector::const_iterator i = heap.begin (); i != heap.end (); ++i) { + delivery.put (*i); + } + } + + } + + } + + if (! to_commit.empty () && vars.get ()) { + res->deep_layer ().commit_shapes (*vars, to_commit); + } + + if (filter.result_is_merged ()) { + res->set_is_merged (true); + } + return res.release (); +} + +EdgesDelegate * +DeepEdges::filter_in_place (const EdgeFilterBase &filter) +{ + // TODO: implement to be really in-place + return filtered (filter); +} + +EdgesDelegate * +DeepEdges::filtered (const EdgeFilterBase &filter) const +{ + if (! filter.requires_raw_input ()) { + ensure_merged_edges_valid (); + } + + std::auto_ptr vars; + if (filter.vars ()) { + + vars.reset (new db::VariantsCollectorBase (filter.vars ())); + + vars->collect (m_deep_layer.layout (), m_deep_layer.initial_cell ()); + + if (filter.wants_variants ()) { + const_cast (m_deep_layer).separate_variants (*vars); + } + + } + + db::Layout &layout = const_cast (m_deep_layer.layout ()); + std::map > to_commit; + + std::auto_ptr res (new db::DeepEdges (m_deep_layer.derived ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &s = c->shapes (filter.requires_raw_input () ? m_deep_layer.layer () : m_merged_edges.layer ()); + + if (vars.get ()) { + + const std::map &vv = vars->variants (c->cell_index ()); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + + db::Shapes *st; + if (vv.size () == 1) { + st = & c->shapes (res->deep_layer ().layer ()); + } else { + st = & to_commit [c->cell_index ()] [v->first]; + } + + const db::ICplxTrans &tr = v->first; + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) { + if (filter.selected (si->edge ().transformed (tr))) { + st->insert (*si); + } + } + + } + + } else { + + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) { + if (filter.selected (si->edge ())) { + st.insert (*si); + } + } + + } + + } + + if (! to_commit.empty () && vars.get ()) { + res->deep_layer ().commit_shapes (*vars, to_commit); + } + + if (! filter.requires_raw_input ()) { + res->set_is_merged (true); + } + return res.release (); +} + +EdgesDelegate *DeepEdges::merged_in_place () +{ + ensure_merged_edges_valid (); + + // NOTE: this makes both layers share the same resource + m_deep_layer = m_merged_edges; + + return this; +} + +EdgesDelegate *DeepEdges::merged () const +{ + ensure_merged_edges_valid (); + + db::Layout &layout = const_cast (m_merged_edges.layout ()); + + std::auto_ptr res (new db::DeepEdges (m_merged_edges.derived ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + c->shapes (res->deep_layer ().layer ()) = c->shapes (m_merged_edges.layer ()); + } + + res->set_is_merged (true); + return res.release (); +} + +DeepLayer +DeepEdges::and_or_not_with (const DeepEdges *other, bool and_op) const +{ + DeepLayer dl_out (m_deep_layer.derived ()); + + db::EdgeBoolAndOrNotLocalOperation op (and_op); + + db::local_processor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell ()); + proc.set_base_verbosity (base_verbosity ()); + proc.set_threads (m_deep_layer.store ()->threads ()); + proc.set_area_ratio (m_deep_layer.store ()->max_area_ratio ()); + proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ()); + + proc.run (&op, m_deep_layer.layer (), other->deep_layer ().layer (), dl_out.layer ()); + + return dl_out; +} + +DeepLayer +DeepEdges::edge_region_op (const DeepRegion *other, bool outside, bool include_borders) const +{ + DeepLayer dl_out (m_deep_layer.derived ()); + + db::EdgeToPolygonLocalOperation op (outside, include_borders); + + db::local_processor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell ()); + proc.set_base_verbosity (base_verbosity ()); + proc.set_threads (m_deep_layer.store ()->threads ()); + proc.set_area_ratio (m_deep_layer.store ()->max_area_ratio ()); + proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ()); + + proc.run (&op, m_deep_layer.layer (), other->deep_layer ().layer (), dl_out.layer ()); + + return dl_out; +} + +EdgesDelegate *DeepEdges::and_with (const Edges &other) const +{ + const DeepEdges *other_deep = dynamic_cast (other.delegate ()); + + if (empty () || other.empty ()) { + + // Nothing to do + return new EmptyEdges (); + + } else if (! other_deep) { + + return AsIfFlatEdges::and_with (other); + + } else { + + return new DeepEdges (and_or_not_with (other_deep, true)); + + } +} + +EdgesDelegate *DeepEdges::and_with (const Region &other) const +{ + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + + if (empty ()) { + + // Nothing to do + return new EmptyEdges (); + + } else if (other.empty ()) { + + // Nothing to do + return clone (); + + } else if (! other_deep) { + + return AsIfFlatEdges::not_with (other); + + } else { + + return new DeepEdges (edge_region_op (other_deep, false /*outside*/, true /*include borders*/)); + + } +} + +EdgesDelegate *DeepEdges::not_with (const Edges &other) const +{ + const DeepEdges *other_deep = dynamic_cast (other.delegate ()); + + if (empty ()) { + + // Nothing to do + return new EmptyEdges (); + + } else if (other.empty ()) { + + // Nothing to do + return clone (); + + } else if (! other_deep) { + + return AsIfFlatEdges::not_with (other); + + } else { + + return new DeepEdges (and_or_not_with (other_deep, false)); + + } +} + +EdgesDelegate *DeepEdges::not_with (const Region &other) const +{ + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + + if (empty ()) { + + // Nothing to do + return new EmptyEdges (); + + } else if (other.empty ()) { + + // Nothing to do + return clone (); + + } else if (! other_deep) { + + return AsIfFlatEdges::not_with (other); + + } else { + + return new DeepEdges (edge_region_op (other_deep, true /*outside*/, true /*include borders*/)); + + } +} + +EdgesDelegate *DeepEdges::xor_with (const Edges &other) const +{ + const DeepEdges *other_deep = dynamic_cast (other.delegate ()); + + if (empty ()) { + + // Nothing to do + return other.delegate ()->clone (); + + } else if (other.empty ()) { + + // Nothing to do + return clone (); + + } else if (! other_deep) { + + return AsIfFlatEdges::xor_with (other); + + } else { + + // Implement XOR as (A-B)+(B-A) - only this implementation + // is compatible with the local processor scheme + DeepLayer n1 (and_or_not_with (other_deep, false)); + DeepLayer n2 (other_deep->and_or_not_with (this, false)); + + n1.add_from (n2); + return new DeepEdges (n1); + + } +} + +EdgesDelegate *DeepEdges::or_with (const Edges &other) const +{ + // NOTE: in the hierarchical case we don't do a merge on "or": just map to add + return add (other); +} + +EdgesDelegate * +DeepEdges::add_in_place (const Edges &other) +{ + if (other.empty ()) { + return this; + } + + const DeepEdges *other_deep = dynamic_cast (other.delegate ()); + if (other_deep) { + + deep_layer ().add_from (other_deep->deep_layer ()); + + } else { + + // non-deep to deep merge (flat) + + db::Shapes &shapes = deep_layer ().initial_cell ().shapes (deep_layer ().layer ()); + for (db::Edges::const_iterator p = other.begin (); ! p.at_end (); ++p) { + shapes.insert (*p); + } + + } + + set_is_merged (false); + return this; +} + +EdgesDelegate *DeepEdges::add (const Edges &other) const +{ + if (other.empty ()) { + return clone (); + } else if (empty ()) { + return other.delegate ()->clone (); + } else { + DeepEdges *new_edges = dynamic_cast (clone ()); + new_edges->add_in_place (other); + return new_edges; + } +} + +EdgesDelegate *DeepEdges::inside_part (const Region &other) const +{ + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + + if (empty ()) { + + // Nothing to do + return new EmptyEdges (); + + } else if (other.empty ()) { + + // Nothing to do + return clone (); + + } else if (! other_deep) { + + return AsIfFlatEdges::not_with (other); + + } else { + + return new DeepEdges (edge_region_op (other_deep, false /*outside*/, false /*include borders*/)); + + } +} + +EdgesDelegate *DeepEdges::outside_part (const Region &other) const +{ + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + + if (empty ()) { + + // Nothing to do + return new EmptyEdges (); + + } else if (other.empty ()) { + + // Nothing to do + return clone (); + + } else if (! other_deep) { + + return AsIfFlatEdges::not_with (other); + + } else { + + return new DeepEdges (edge_region_op (other_deep, true /*outside*/, false /*include borders*/)); + + } +} + +RegionDelegate *DeepEdges::extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const +{ + ensure_merged_edges_valid (); + + std::auto_ptr res (new db::DeepRegion (m_merged_edges.derived ())); + + db::Layout &layout = const_cast (m_merged_edges.layout ()); + db::Cell &top_cell = const_cast (m_merged_edges.initial_cell ()); + + // TODO: there is a special case when we'd need a MagnificationAndOrientationReducer: + // dots formally don't have an orientation, hence the interpretation is x and y. + db::MagnificationReducer red; + db::cell_variants_collector vars (red); + vars.collect (m_merged_edges.layout (), m_merged_edges.initial_cell ()); + + std::map > to_commit; + + if (join) { + + // In joined mode we need to create a special cluster which connects all joined edges + db::DeepLayer joined = m_merged_edges.derived (); + + db::hier_clusters hc; + db::Connectivity conn (db::Connectivity::EdgesConnectByPoints); + conn.connect (m_merged_edges); + hc.set_base_verbosity (base_verbosity () + 10); + hc.build (layout, m_merged_edges.initial_cell (), db::ShapeIterator::Edges, conn); + + // TODO: iterate only over the called cells? + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const std::map &vv = vars.variants (c->cell_index ()); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + + db::Shapes *out; + if (vv.size () == 1) { + out = & c->shapes (res->deep_layer ().layer ()); + } else { + out = & to_commit [c->cell_index ()][v->first]; + } + + const db::connected_clusters &cc = hc.clusters_per_cell (c->cell_index ()); + for (db::connected_clusters::all_iterator cl = cc.begin_all (); ! cl.at_end (); ++cl) { + + if (cc.is_root (*cl)) { + + PolygonRefToShapesGenerator prgen (&layout, out); + polygon_transformation_filter ptrans (&prgen, v->first.inverted ()); + JoinEdgesCluster jec (&ptrans, ext_b, ext_e, ext_o, ext_i); + + std::list heap; + for (db::recursive_cluster_shape_iterator rcsi (hc, m_merged_edges.layer (), c->cell_index (), *cl); ! rcsi.at_end (); ++rcsi) { + heap.push_back (rcsi->transformed (v->first * rcsi.trans ())); + jec.add (&heap.back (), 0); + } + + jec.finish (); + + } + + } + + } + + } + + } else { + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const std::map &vv = vars.variants (c->cell_index ()); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + + db::Shapes *out; + if (vv.size () == 1) { + out = & c->shapes (res->deep_layer ().layer ()); + } else { + out = & to_commit [c->cell_index ()][v->first]; + } + + for (db::Shapes::shape_iterator si = c->shapes (m_merged_edges.layer ()).begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) { + out->insert (extended_edge (si->edge ().transformed (v->first), ext_b, ext_e, ext_o, ext_i).transformed (v->first.inverted ())); + } + + } + + } + + } + + // propagate results from variants + vars.commit_shapes (layout, top_cell, res->deep_layer ().layer (), to_commit); + + return res.release (); +} + +namespace +{ + +class Edge2EdgeInteractingLocalOperation + : public local_operation +{ +public: + Edge2EdgeInteractingLocalOperation (bool inverse) + : m_inverse (inverse) + { + // .. nothing yet .. + } + + virtual void compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::unordered_set &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const + { + db::box_scanner scanner; + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j)); + } + } + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::Edge &subject = interactions.subject_shape (i->first); + scanner.insert (&subject, 0); + } + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + scanner.insert (o.operator-> (), 1); + } + + if (m_inverse) { + + std::unordered_set interacting; + edge_interaction_filter > filter (interacting); + scanner.process (filter, 1, db::box_convert ()); + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::Edge &subject = interactions.subject_shape (i->first); + if (interacting.find (subject) == interacting.end ()) { + result.insert (subject); + } + } + + } else { + + edge_interaction_filter > filter (result); + scanner.process (filter, 1, db::box_convert ()); + + } + + } + + virtual on_empty_intruder_mode on_empty_intruder_hint () const + { + if (m_inverse) { + return Copy; + } else { + return Drop; + } + } + + virtual std::string description () const + { + return tl::to_string (tr ("Select interacting edges")); + } + +private: + bool m_inverse; +}; + +class Edge2PolygonInteractingLocalOperation + : public local_operation +{ +public: + Edge2PolygonInteractingLocalOperation (bool inverse) + : m_inverse (inverse) + { + // .. nothing yet .. + } + + virtual void compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::unordered_set &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const + { + db::box_scanner2 scanner; + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j)); + } + } + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::Edge &subject = interactions.subject_shape (i->first); + scanner.insert1 (&subject, 0); + } + + std::list heap; + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + heap.push_back (o->obj ().transformed (o->trans ())); + scanner.insert2 (& heap.back (), 1); + } + + if (m_inverse) { + + std::unordered_set interacting; + edge_to_region_interaction_filter > filter (interacting); + scanner.process (filter, 1, db::box_convert (), db::box_convert ()); + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::Edge &subject = interactions.subject_shape (i->first); + if (interacting.find (subject) == interacting.end ()) { + result.insert (subject); + } + } + + } else { + + edge_to_region_interaction_filter > filter (result); + scanner.process (filter, 1, db::box_convert (), db::box_convert ()); + + } + } + + virtual on_empty_intruder_mode on_empty_intruder_hint () const + { + if (m_inverse) { + return Copy; + } else { + return Drop; + } + } + + virtual std::string description () const + { + return tl::to_string (tr ("Select interacting edges")); + } + +private: + bool m_inverse; +}; + +} + +EdgesDelegate * +DeepEdges::selected_interacting_generic (const Region &other, bool inverse) const +{ + const db::DeepRegion *other_deep = dynamic_cast (other.delegate ()); + if (! other_deep) { + return db::AsIfFlatEdges::selected_interacting_generic (other, inverse); + } + + ensure_merged_edges_valid (); + + DeepLayer dl_out (m_deep_layer.derived ()); + + db::Edge2PolygonInteractingLocalOperation op (inverse); + + db::local_processor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell ()); + proc.set_base_verbosity (base_verbosity ()); + proc.set_threads (m_deep_layer.store ()->threads ()); + + proc.run (&op, m_merged_edges.layer (), other_deep->deep_layer ().layer (), dl_out.layer ()); + + return new db::DeepEdges (dl_out); +} + +EdgesDelegate * +DeepEdges::selected_interacting_generic (const Edges &other, bool inverse) const +{ + const db::DeepEdges *other_deep = dynamic_cast (other.delegate ()); + if (! other_deep) { + return db::AsIfFlatEdges::selected_interacting_generic (other, inverse); + } + + ensure_merged_edges_valid (); + + DeepLayer dl_out (m_deep_layer.derived ()); + + db::Edge2EdgeInteractingLocalOperation op (inverse); + + db::local_processor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell ()); + proc.set_base_verbosity (base_verbosity ()); + proc.set_threads (m_deep_layer.store ()->threads ()); + + proc.run (&op, m_merged_edges.layer (), other_deep->deep_layer ().layer (), dl_out.layer ()); + + return new db::DeepEdges (dl_out); +} + +EdgesDelegate *DeepEdges::selected_interacting (const Edges &other) const +{ + return selected_interacting_generic (other, false); +} + +EdgesDelegate *DeepEdges::selected_not_interacting (const Edges &other) const +{ + return selected_interacting_generic (other, true); +} + +EdgesDelegate *DeepEdges::selected_interacting (const Region &other) const +{ + return selected_interacting_generic (other, false); +} + +EdgesDelegate *DeepEdges::selected_not_interacting (const Region &other) const +{ + return selected_interacting_generic (other, true); +} + +EdgesDelegate *DeepEdges::in (const Edges &other, bool invert) const +{ + // TODO: is there a cheaper way? + return AsIfFlatEdges::in (other, invert); +} + +namespace +{ + +class CheckLocalOperation + : public local_operation +{ +public: + CheckLocalOperation (const EdgeRelationFilter &check, bool has_other) + : m_check (check), m_has_other (has_other) + { + // .. nothing yet .. + } + + virtual void compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::unordered_set &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const + { + edge2edge_check_for_edges > edge_check (m_check, result, m_has_other); + + db::box_scanner scanner; + std::set others; + + if (m_has_other) { + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j)); + } + } + + size_t n = 0; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::Edge &subject = interactions.subject_shape (i->first); + scanner.insert (& subject, n); + n += 2; + } + + n = 1; + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + scanner.insert (o.operator-> (), n); + n += 2; + } + + } else { + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + others.insert (interactions.subject_shape (i->first)); + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j)); + } + } + + size_t n = 0; + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + scanner.insert (o.operator-> (), n); + n += 2; + } + + } + + scanner.process (edge_check, m_check.distance (), db::box_convert ()); + } + + virtual db::Coord dist () const + { + // TODO: will the distance be sufficient? Or should we take somewhat more? + return m_check.distance (); + } + + virtual on_empty_intruder_mode on_empty_intruder_hint () const + { + return Drop; + } + + virtual std::string description () const + { + return tl::to_string (tr ("Generic DRC check")); + } + +private: + EdgeRelationFilter m_check; + bool m_has_other; +}; + +} + +EdgePairsDelegate * +DeepEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + const db::DeepEdges *other_deep = 0; + if (other) { + other_deep = dynamic_cast (other->delegate ()); + if (! other_deep) { + return db::AsIfFlatEdges::run_check (rel, other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + } + + ensure_merged_edges_valid (); + + EdgeRelationFilter check (rel, d, metrics); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + std::auto_ptr res (new db::DeepEdgePairs (m_merged_edges.derived ())); + + db::CheckLocalOperation op (check, other_deep != 0); + + db::local_processor proc (const_cast (&m_deep_layer.layout ()), + const_cast (&m_deep_layer.initial_cell ()), + other_deep ? &other_deep->deep_layer ().layout () : const_cast (&m_deep_layer.layout ()), + other_deep ? &other_deep->deep_layer ().initial_cell () : const_cast (&m_deep_layer.initial_cell ())); + + proc.set_base_verbosity (base_verbosity ()); + proc.set_threads (m_deep_layer.store ()->threads ()); + + proc.run (&op, m_merged_edges.layer (), other_deep ? other_deep->deep_layer ().layer () : m_merged_edges.layer (), res->deep_layer ().layer ()); + + return res.release (); +} + +} diff --git a/src/db/db/dbDeepEdges.h b/src/db/db/dbDeepEdges.h new file mode 100644 index 000000000..a114d7e66 --- /dev/null +++ b/src/db/db/dbDeepEdges.h @@ -0,0 +1,185 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbDeepEdges +#define HDR_dbDeepEdges + +#include "dbCommon.h" + +#include "dbAsIfFlatEdges.h" +#include "dbDeepShapeStore.h" +#include "dbEdgePairs.h" + +namespace db { + +class Edges; +class DeepRegion; + +/** + * @brief Provides hierarchical edges implementation + */ +class DB_PUBLIC DeepEdges + : public db::AsIfFlatEdges +{ +public: + DeepEdges (); + DeepEdges (const RecursiveShapeIterator &si, DeepShapeStore &dss, bool as_edges = true); + DeepEdges (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool as_edges = true, bool merged_semantics = true); + + DeepEdges (const DeepEdges &other); + DeepEdges (const DeepLayer &dl); + + virtual ~DeepEdges (); + + EdgesDelegate *clone () const; + + virtual EdgesIteratorDelegate *begin () const; + virtual EdgesIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + virtual bool is_merged () const; + + virtual const db::Edge *nth (size_t n) const; + virtual bool has_valid_edges () const; + virtual bool has_valid_merged_edges () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual bool equals (const Edges &other) const; + virtual bool less (const Edges &other) const; + + virtual size_t size () const; + virtual Box bbox () const; + + virtual DeepEdges::length_type length (const db::Box &) const; + + virtual std::string to_string (size_t nmax) const; + + virtual EdgePairsDelegate *width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::WidthRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairsDelegate *space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairsDelegate *enclosing_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::OverlapRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairsDelegate *overlap_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::WidthRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairsDelegate *separation_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairsDelegate *inside_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::InsideRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter); + virtual EdgesDelegate *filtered (const EdgeFilterBase &) const; + virtual EdgesDelegate *process_in_place (const EdgeProcessorBase &); + virtual EdgesDelegate *processed (const EdgeProcessorBase &) const; + virtual EdgePairsDelegate *processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &filter) const; + virtual RegionDelegate *processed_to_polygons (const EdgeToPolygonProcessorBase &filter) const; + + virtual EdgesDelegate *merged_in_place (); + virtual EdgesDelegate *merged () const; + + virtual EdgesDelegate *and_with (const Edges &other) const; + virtual EdgesDelegate *and_with (const Region &other) const; + + virtual EdgesDelegate *not_with (const Edges &other) const; + virtual EdgesDelegate *not_with (const Region &other) const; + + virtual EdgesDelegate *xor_with (const Edges &other) const; + + virtual EdgesDelegate *or_with (const Edges &other) const; + + virtual EdgesDelegate *add_in_place (const Edges &other); + virtual EdgesDelegate *add (const Edges &other) const; + + virtual EdgesDelegate *inside_part (const Region &other) const; + virtual EdgesDelegate *outside_part (const Region &other) const; + + virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const; + + virtual EdgesDelegate *selected_interacting (const Edges &) const; + virtual EdgesDelegate *selected_not_interacting (const Edges &) const; + virtual EdgesDelegate *selected_interacting (const Region &) const; + virtual EdgesDelegate *selected_not_interacting (const Region &) const; + + virtual EdgesDelegate *in (const Edges &, bool) const; + + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + + const DeepLayer &deep_layer () const + { + return m_deep_layer; + } + + DeepLayer &deep_layer () + { + return m_deep_layer; + } + +protected: + virtual void merged_semantics_changed (); + void set_is_merged (bool f); + +private: + friend class DeepRegion; + + DeepEdges &operator= (const DeepEdges &other); + + DeepLayer m_deep_layer; + mutable DeepLayer m_merged_edges; + mutable bool m_merged_edges_valid; + bool m_is_merged; + + void init (); + void ensure_merged_edges_valid () const; + DeepLayer and_or_not_with(const DeepEdges *other, bool and_op) const; + DeepLayer edge_region_op (const DeepRegion *other, bool outside, bool include_borders) const; + EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + EdgesDelegate *selected_interacting_generic (const Edges &edges, bool invert) const; + EdgesDelegate *selected_interacting_generic (const Region ®ion, bool invert) const; + template OutputContainer *processed_impl (const edge_processor &filter) const; +}; + +} + +#endif + diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc new file mode 100644 index 000000000..0ee8b7195 --- /dev/null +++ b/src/db/db/dbDeepRegion.cc @@ -0,0 +1,1754 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbDeepRegion.h" +#include "dbDeepShapeStore.h" +#include "dbEmptyRegion.h" +#include "dbEmptyEdgePairs.h" +#include "dbRegion.h" +#include "dbRegionUtils.h" +#include "dbDeepEdges.h" +#include "dbDeepEdgePairs.h" +#include "dbShapeProcessor.h" +#include "dbFlatRegion.h" +#include "dbHierProcessor.h" +#include "dbCellMapping.h" +#include "dbLayoutUtils.h" +#include "dbHierNetworkProcessor.h" +#include "dbCellGraphUtils.h" +#include "dbPolygonTools.h" +#include "dbCellVariants.h" +#include "dbLocalOperationUtils.h" +#include "tlTimer.h" + +namespace db +{ + +/** + * @brief An iterator delegate for the deep region + * TODO: this is kind of redundant with OriginalLayerIterator .. + */ +class DB_PUBLIC DeepRegionIterator + : public RegionIteratorDelegate +{ +public: + typedef db::Polygon value_type; + + DeepRegionIterator (const db::RecursiveShapeIterator &iter) + : m_iter (iter) + { + set (); + } + + virtual ~DeepRegionIterator () { } + + virtual bool at_end () const + { + return m_iter.at_end (); + } + + virtual void increment () + { + ++m_iter; + set (); + } + + virtual const value_type *get () const + { + return &m_polygon; + } + + virtual RegionIteratorDelegate *clone () const + { + return new DeepRegionIterator (*this); + } + +private: + friend class Region; + + db::RecursiveShapeIterator m_iter; + mutable value_type m_polygon; + + void set () const + { + if (! m_iter.at_end ()) { + m_iter.shape ().polygon (m_polygon); + m_polygon.transform (m_iter.trans (), false); + } + } +}; + +// ------------------------------------------------------------------------------------------------------------- +// DeepRegion implementation + +DeepRegion::DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) + : AsIfFlatRegion (), m_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count)), m_merged_polygons () +{ + init (); +} + +DeepRegion::DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics, double area_ratio, size_t max_vertex_count) + : AsIfFlatRegion (), m_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count, trans)), m_merged_polygons () +{ + init (); + set_merged_semantics (merged_semantics); +} + +DeepRegion::DeepRegion () + : AsIfFlatRegion () +{ + init (); +} + +DeepRegion::DeepRegion (const DeepLayer &dl) + : AsIfFlatRegion (), m_deep_layer (dl) +{ + init (); +} + +DeepRegion::~DeepRegion () +{ + // .. nothing yet .. +} + +DeepRegion::DeepRegion (const DeepRegion &other) + : AsIfFlatRegion (other), + m_deep_layer (other.m_deep_layer.copy ()), + m_merged_polygons_valid (other.m_merged_polygons_valid), + m_is_merged (other.m_is_merged) +{ + if (m_merged_polygons_valid) { + m_merged_polygons = other.m_merged_polygons; + } +} + +void DeepRegion::init () +{ + m_merged_polygons_valid = false; + m_merged_polygons = db::DeepLayer (); + m_is_merged = false; +} + +RegionDelegate * +DeepRegion::clone () const +{ + return new DeepRegion (*this); +} + +void DeepRegion::merged_semantics_changed () +{ + // .. nothing yet .. +} + +RegionIteratorDelegate * +DeepRegion::begin () const +{ + return new DeepRegionIterator (begin_iter ().first); +} + +RegionIteratorDelegate * +DeepRegion::begin_merged () const +{ + if (! merged_semantics ()) { + return begin (); + } else { + return new DeepRegionIterator (begin_merged_iter ().first); + } +} + +std::pair +DeepRegion::begin_iter () const +{ + const db::Layout &layout = m_deep_layer.layout (); + if (layout.cells () == 0) { + + return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); + + } else { + + const db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + db::RecursiveShapeIterator iter (m_deep_layer.layout (), top_cell, m_deep_layer.layer ()); + return std::make_pair (iter, db::ICplxTrans ()); + + } +} + +std::pair +DeepRegion::begin_merged_iter () const +{ + if (! merged_semantics ()) { + + return begin_iter (); + + } else { + + ensure_merged_polygons_valid (); + + const db::Layout &layout = m_merged_polygons.layout (); + if (layout.cells () == 0) { + + return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); + + } else { + + const db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + db::RecursiveShapeIterator iter (m_merged_polygons.layout (), top_cell, m_merged_polygons.layer ()); + return std::make_pair (iter, db::ICplxTrans ()); + + } + + } +} + +bool +DeepRegion::empty () const +{ + return begin_iter ().first.at_end (); +} + +bool +DeepRegion::is_merged () const +{ + return m_is_merged; +} + +const db::Polygon * +DeepRegion::nth (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to polygons is available only for flat regions"))); +} + +bool +DeepRegion::has_valid_polygons () const +{ + return false; +} + +bool +DeepRegion::has_valid_merged_polygons () const +{ + return false; +} + +const db::RecursiveShapeIterator * +DeepRegion::iter () const +{ + return 0; +} + +bool +DeepRegion::equals (const Region &other) const +{ + const DeepRegion *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && &other_delegate->m_deep_layer.layout () == &m_deep_layer.layout () + && other_delegate->m_deep_layer.layer () == m_deep_layer.layer ()) { + return true; + } else { + return AsIfFlatRegion::equals (other); + } +} + +bool +DeepRegion::less (const Region &other) const +{ + const DeepRegion *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && &other_delegate->m_deep_layer.layout () == &m_deep_layer.layout ()) { + return other_delegate->m_deep_layer.layer () < m_deep_layer.layer (); + } else { + return AsIfFlatRegion::less (other); + } +} + +namespace { + +class ClusterMerger +{ +public: + ClusterMerger (unsigned int layer, db::Layout &layout, const db::hier_clusters &hc, bool min_coherence, bool report_progress, const std::string &progress_desc) + : m_layer (layer), mp_layout (&layout), mp_hc (&hc), m_min_coherence (min_coherence), m_ep (report_progress, progress_desc) + { + // .. nothing yet .. + } + + void set_base_verbosity (int vb) + { + m_ep.set_base_verbosity (vb); + } + + db::Shapes &merged (size_t cid, db::cell_index_type ci, unsigned int min_wc = 0) + { + return compute_merged (cid, ci, true, min_wc); + } + + void erase (size_t cid, db::cell_index_type ci) + { + m_merged_cluster.erase (std::make_pair (cid, ci)); + } + +private: + std::map, db::Shapes> m_merged_cluster; + unsigned int m_layer; + db::Layout *mp_layout; + const db::hier_clusters *mp_hc; + bool m_min_coherence; + db::EdgeProcessor m_ep; + + db::Shapes &compute_merged (size_t cid, db::cell_index_type ci, bool initial, unsigned int min_wc) + { + std::map, db::Shapes>::iterator s = m_merged_cluster.find (std::make_pair (cid, ci)); + + // some sanity checks: initial clusters are single-use, are never generated twice and cannot be retrieved again + if (initial) { + tl_assert (s == m_merged_cluster.end ()); + } + + if (s != m_merged_cluster.end ()) { + return s->second; + } + + s = m_merged_cluster.insert (std::make_pair (std::make_pair (cid, ci), db::Shapes (false))).first; + + const db::connected_clusters &cc = mp_hc->clusters_per_cell (ci); + const db::local_cluster &c = cc.cluster_by_id (cid); + + if (min_wc > 0) { + + // We cannot merge bottom-up in min_wc mode, so we just use the recursive cluster iterator + + m_ep.clear (); + + size_t pi = 0; + + for (db::recursive_cluster_shape_iterator s = db::recursive_cluster_shape_iterator (*mp_hc, m_layer, ci, cid); !s.at_end (); ++s) { + db::Polygon poly = s->obj (); + poly.transform (s.trans () * db::ICplxTrans (s->trans ())); + m_ep.insert (poly, pi++); + } + + } else { + + std::list > merged_child_clusters; + + const db::connected_clusters::connections_type &conn = cc.connections_for_cluster (cid); + for (db::connected_clusters::connections_type::const_iterator i = conn.begin (); i != conn.end (); ++i) { + const db::Shapes &cc_shapes = compute_merged (i->id (), i->inst_cell_index (), false, min_wc); + merged_child_clusters.push_back (std::make_pair (&cc_shapes, i->inst_trans ())); + } + + m_ep.clear (); + + size_t pi = 0; + + for (std::list >::const_iterator i = merged_child_clusters.begin (); i != merged_child_clusters.end (); ++i) { + for (db::Shapes::shape_iterator s = i->first->begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + if (s->is_polygon ()) { + db::Polygon poly; + s->polygon (poly); + m_ep.insert (poly.transformed (i->second), pi++); + } + } + } + + for (db::local_cluster::shape_iterator s = c.begin (m_layer); !s.at_end (); ++s) { + db::Polygon poly = s->obj (); + poly.transform (s->trans ()); + m_ep.insert (poly, pi++); + } + + } + + // and run the merge step + db::MergeOp op (min_wc); + db::PolygonRefToShapesGenerator pr (mp_layout, &s->second); + db::PolygonGenerator pg (pr, false /*don't resolve holes*/, m_min_coherence); + m_ep.process (pg, op); + + return s->second; + } +}; + +} + +void +DeepRegion::ensure_merged_polygons_valid () const +{ + if (! m_merged_polygons_valid) { + + if (m_is_merged) { + + // NOTE: this will reuse the deep layer reference + m_merged_polygons = m_deep_layer; + + } else { + + m_merged_polygons = m_deep_layer.derived (); + + tl::SelfTimer timer (tl::verbosity () > base_verbosity (), "Ensure merged polygons"); + + db::Layout &layout = const_cast (m_deep_layer.layout ()); + + db::hier_clusters hc; + db::Connectivity conn; + conn.connect (m_deep_layer); + hc.set_base_verbosity (base_verbosity () + 10); + hc.build (layout, m_deep_layer.initial_cell (), db::ShapeIterator::Polygons, conn); + + // collect the clusters and merge them into big polygons + // NOTE: using the ClusterMerger we merge bottom-up forming bigger and bigger polygons. This is + // hopefully more efficient that collecting everything and will lead to reuse of parts. + + ClusterMerger cm (m_deep_layer.layer (), layout, hc, min_coherence (), report_progress (), progress_desc ()); + cm.set_base_verbosity (base_verbosity () + 10); + + // TODO: iterate only over the called cells? + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + const db::connected_clusters &cc = hc.clusters_per_cell (c->cell_index ()); + for (db::connected_clusters::all_iterator cl = cc.begin_all (); ! cl.at_end (); ++cl) { + if (cc.is_root (*cl)) { + db::Shapes &s = cm.merged (*cl, c->cell_index ()); + c->shapes (m_merged_polygons.layer ()).insert (s); + cm.erase (*cl, c->cell_index ()); // not needed anymore + } + } + } + + } + + m_merged_polygons_valid = true; + + } +} + +void +DeepRegion::set_is_merged (bool f) +{ + m_is_merged = f; + m_merged_polygons_valid = false; +} + +void +DeepRegion::insert_into (db::Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + m_deep_layer.insert_into (layout, into_cell, into_layer); +} + +RegionDelegate * +DeepRegion::and_with (const Region &other) const +{ + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + + if (empty () || other.empty ()) { + + // Nothing to do + return new EmptyRegion (); + + } else if (! other_deep) { + + return AsIfFlatRegion::and_with (other); + + } else { + + return new DeepRegion (and_or_not_with (other_deep, true)); + + } +} + +RegionDelegate * +DeepRegion::not_with (const Region &other) const +{ + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + + if (empty ()) { + + // Nothing to do + return new EmptyRegion (); + + } else if (other.empty ()) { + + // Nothing to do + return clone (); + + } else if (! other_deep) { + + return AsIfFlatRegion::not_with (other); + + } else { + + return new DeepRegion (and_or_not_with (other_deep, false)); + + } +} + +DeepLayer +DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const +{ + DeepLayer dl_out (m_deep_layer.derived ()); + + db::BoolAndOrNotLocalOperation op (and_op); + + db::local_processor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell ()); + proc.set_base_verbosity (base_verbosity ()); + proc.set_threads (m_deep_layer.store ()->threads ()); + proc.set_area_ratio (m_deep_layer.store ()->max_area_ratio ()); + proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ()); + + proc.run (&op, m_deep_layer.layer (), other->deep_layer ().layer (), dl_out.layer ()); + + return dl_out; +} + +RegionDelegate * +DeepRegion::xor_with (const Region &other) const +{ + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + + if (empty ()) { + + // Nothing to do + return other.delegate ()->clone (); + + } else if (other.empty ()) { + + // Nothing to do + return clone (); + + } else if (! other_deep) { + + return AsIfFlatRegion::xor_with (other); + + } else { + + // Implement XOR as (A-B)+(B-A) - only this implementation + // is compatible with the local processor scheme + DeepLayer n1 (and_or_not_with (other_deep, false)); + DeepLayer n2 (other_deep->and_or_not_with (this, false)); + + n1.add_from (n2); + return new DeepRegion (n1); + + } +} + +RegionDelegate * +DeepRegion::add_in_place (const Region &other) +{ + if (other.empty ()) { + return this; + } + + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + if (other_deep) { + + deep_layer ().add_from (other_deep->deep_layer ()); + + } else { + + // non-deep to deep merge (flat) + + db::Shapes &shapes = deep_layer ().initial_cell ().shapes (deep_layer ().layer ()); + db::PolygonRefToShapesGenerator pr (const_cast (& deep_layer ().layout ()), &shapes); + for (db::Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { + pr.put (*p); + } + + } + + set_is_merged (false); + return this; +} + +RegionDelegate * +DeepRegion::add (const Region &other) const +{ + if (other.empty ()) { + return clone (); + } else if (empty ()) { + return other.delegate ()->clone (); + } else { + DeepRegion *new_region = dynamic_cast (clone ()); + new_region->add_in_place (other); + return new_region; + } +} + +static int is_box_from_iter (db::RecursiveShapeIterator i) +{ + if (i.at_end ()) { + return true; + } + + if (i->is_box ()) { + ++i; + if (i.at_end ()) { + return true; + } + } else if (i->is_path () || i->is_polygon ()) { + db::Polygon poly; + i->polygon (poly); + if (poly.is_box ()) { + ++i; + if (i.at_end ()) { + return true; + } + } + } + + return false; +} + +bool +DeepRegion::is_box () const +{ + return is_box_from_iter (begin_iter ().first); +} + +size_t +DeepRegion::size () const +{ + size_t n = 0; + + const db::Layout &layout = m_deep_layer.layout (); + db::CellCounter cc (&layout); + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + n += cc.weight (*c) * layout.cell (*c).shapes (m_deep_layer.layer ()).size (); + } + + return n; +} + +DeepRegion::area_type +DeepRegion::area (const db::Box &box) const +{ + if (box.empty ()) { + + ensure_merged_polygons_valid (); + + db::cell_variants_collector vars; + vars.collect (m_merged_polygons.layout (), m_merged_polygons.initial_cell ()); + + DeepRegion::area_type a = 0; + + const db::Layout &layout = m_merged_polygons.layout (); + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + DeepRegion::area_type ac = 0; + for (db::ShapeIterator s = layout.cell (*c).shapes (m_merged_polygons.layer ()).begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + ac += s->area (); + } + const std::map &vv = vars.variants (*c); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + double mag = v->first.mag (); + a += v->second * ac * mag * mag; + } + } + + return a; + + } else { + // In the clipped case fall back to flat mode + return db::AsIfFlatRegion::area (box); + } +} + +DeepRegion::perimeter_type +DeepRegion::perimeter (const db::Box &box) const +{ + if (box.empty ()) { + + ensure_merged_polygons_valid (); + + db::cell_variants_collector vars; + vars.collect (m_merged_polygons.layout (), m_merged_polygons.initial_cell ()); + + DeepRegion::perimeter_type p = 0; + + const db::Layout &layout = m_merged_polygons.layout (); + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + DeepRegion::perimeter_type pc = 0; + for (db::ShapeIterator s = layout.cell (*c).shapes (m_merged_polygons.layer ()).begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + pc += s->perimeter (); + } + const std::map &vv = vars.variants (*c); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + double mag = v->first.mag (); + p += v->second * pc * mag; + } + } + + return p; + + } else { + // In the clipped case fall back to flat mode + return db::AsIfFlatRegion::perimeter (box); + } +} + +Box +DeepRegion::bbox () const +{ + return m_deep_layer.initial_cell ().bbox (m_deep_layer.layer ()); +} + +std::string +DeepRegion::to_string (size_t nmax) const +{ + return db::AsIfFlatRegion::to_string (nmax); +} + +EdgePairsDelegate * +DeepRegion::grid_check (db::Coord gx, db::Coord gy) const +{ + if (gx < 0 || gy < 0) { + throw tl::Exception (tl::to_string (tr ("Grid check requires a positive grid value"))); + } + + if (gx != gy) { + // no way doing this hierarchically ? + return db::AsIfFlatRegion::grid_check (gx, gy); + } + + if (gx == 0) { + return new EmptyEdgePairs (); + } + + ensure_merged_polygons_valid (); + + db::Layout &layout = m_merged_polygons.layout (); + + db::cell_variants_collector vars (gx); + vars.collect (layout, m_merged_polygons.initial_cell ()); + + std::map > to_commit; + std::auto_ptr res (new db::DeepEdgePairs (m_merged_polygons.derived ())); + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &shapes = c->shapes (m_merged_polygons.layer ()); + + const std::map &vv = vars.variants (c->cell_index ()); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + + db::Shapes *markers; + if (vv.size () == 1) { + markers = & c->shapes (res->deep_layer ().layer ()); + } else { + markers = & to_commit [c->cell_index ()] [v->first]; + } + + for (db::Shapes::shape_iterator si = shapes.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + + db::Polygon poly; + si->polygon (poly); + + AsIfFlatRegion::produce_markers_for_grid_check (poly, v->first, gx, gy, *markers); + + } + + } + + } + + // propagate the markers with a similar algorithm used for producing the variants + res->deep_layer ().commit_shapes (vars, to_commit); + + return res.release (); +} + +EdgePairsDelegate * +DeepRegion::angle_check (double min, double max, bool inverse) const +{ + ensure_merged_polygons_valid (); + + db::Layout &layout = m_merged_polygons.layout (); + + std::auto_ptr res (new db::DeepEdgePairs (m_merged_polygons.derived ())); + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &shapes = c->shapes (m_merged_polygons.layer ()); + db::Shapes &markers = c->shapes (res->deep_layer ().layer ()); + + for (db::Shapes::shape_iterator si = shapes.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + db::Polygon poly; + si->polygon (poly); + AsIfFlatRegion::produce_markers_for_angle_check (poly, db::UnitTrans (), min, max, inverse, markers); + } + + } + + return res.release (); +} + +RegionDelegate * +DeepRegion::snapped (db::Coord gx, db::Coord gy) +{ + if (gx < 0 || gy < 0) { + throw tl::Exception (tl::to_string (tr ("Snapping requires a positive grid value"))); + } + + if (gx != gy) { + // no way doing this hierarchically ? + return db::AsIfFlatRegion::snapped (gx, gy); + } + + if (! gx) { + return this; + } + + ensure_merged_polygons_valid (); + + db::cell_variants_collector vars (gx); + + vars.collect (m_merged_polygons.layout (), m_merged_polygons.initial_cell ()); + + // NOTE: m_merged_polygons is mutable, so why is the const_cast needed? + const_cast (m_merged_polygons).separate_variants (vars); + + db::Layout &layout = m_merged_polygons.layout (); + std::vector heap; + + std::auto_ptr res (new db::DeepRegion (m_merged_polygons.derived ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const std::map &v = vars.variants (c->cell_index ()); + tl_assert (v.size () == size_t (1)); + const db::ICplxTrans &tr = v.begin ()->first; + db::ICplxTrans trinv = tr.inverted (); + + const db::Shapes &s = c->shapes (m_merged_polygons.layer ()); + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + db::PolygonRefToShapesGenerator pr (&layout, &st); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + + db::Polygon poly; + si->polygon (poly); + poly.transform (tr); + pr.put (snapped_polygon (poly, gx, gy, heap).transformed (trinv)); + + } + + } + + return res.release (); +} + +EdgesDelegate * +DeepRegion::edges (const EdgeFilterBase *filter) const +{ + ensure_merged_polygons_valid (); + + std::auto_ptr vars; + + if (filter && filter->vars ()) { + + vars.reset (new db::VariantsCollectorBase (filter->vars ())); + + vars->collect (m_merged_polygons.layout (), m_merged_polygons.initial_cell ()); + + // NOTE: m_merged_polygons is mutable, so why is the const_cast needed? + const_cast (m_merged_polygons).separate_variants (*vars); + + } + + db::Layout &layout = m_merged_polygons.layout (); + + std::auto_ptr res (new db::DeepEdges (m_merged_polygons.derived ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + db::ICplxTrans tr; + if (vars.get ()) { + const std::map &v = vars->variants (c->cell_index ()); + tl_assert (v.size () == size_t (1)); + tr = v.begin ()->first; + } + + const db::Shapes &s = c->shapes (m_merged_polygons.layer ()); + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + + db::Polygon poly; + si->polygon (poly); + + for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { + if (! filter || filter->selected ((*e).transformed (tr))) { + st.insert (*e); + } + } + + } + + } + + res->set_is_merged (true); + return res.release (); +} + +RegionDelegate * +DeepRegion::process_in_place (const PolygonProcessorBase &filter) +{ + // TODO: implement to be really in-place + return processed (filter); +} + +EdgesDelegate * +DeepRegion::processed_to_edges (const PolygonToEdgeProcessorBase &filter) const +{ + return processed_impl (filter); +} + +EdgePairsDelegate * +DeepRegion::processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &filter) const +{ + return processed_impl (filter); +} + +RegionDelegate * +DeepRegion::processed (const PolygonProcessorBase &filter) const +{ + return processed_impl (filter); +} + +namespace +{ + +template struct delivery; + +template <> +struct delivery +{ + delivery (db::Layout *layout, db::Shapes *shapes) + : mp_layout (layout), mp_shapes (shapes) + { } + + void put (const db::Polygon &result) + { + tl::MutexLocker locker (&mp_layout->lock ()); + mp_shapes->insert (db::PolygonRef (result, mp_layout->shape_repository ())); + } + +private: + db::Layout *mp_layout; + db::Shapes *mp_shapes; +}; + +template +struct delivery +{ + delivery (db::Layout *, db::Shapes *shapes) + : mp_shapes (shapes) + { } + + void put (const Result &result) + { + mp_shapes->insert (result); + } + +private: + db::Shapes *mp_shapes; +}; + +} + +template +OutputContainer * +DeepRegion::processed_impl (const polygon_processor &filter) const +{ + if (! filter.requires_raw_input ()) { + ensure_merged_polygons_valid (); + } + + std::auto_ptr vars; + if (filter.vars ()) { + + vars.reset (new db::VariantsCollectorBase (filter.vars ())); + + vars->collect (m_deep_layer.layout (), m_deep_layer.initial_cell ()); + + if (filter.wants_variants ()) { + const_cast (m_deep_layer).separate_variants (*vars); + } + + } + + db::Layout &layout = const_cast (m_deep_layer.layout ()); + + std::vector heap; + std::map > to_commit; + + std::auto_ptr res (new OutputContainer (m_deep_layer.derived ())); + if (filter.result_must_not_be_merged ()) { + res->set_merged_semantics (false); + } + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &s = c->shapes (filter.requires_raw_input () ? m_deep_layer.layer () : m_merged_polygons.layer ()); + + if (vars.get ()) { + + const std::map &vv = vars->variants (c->cell_index ()); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + + db::Shapes *st; + if (vv.size () == 1) { + st = & c->shapes (res->deep_layer ().layer ()); + } else { + st = & to_commit [c->cell_index ()] [v->first]; + } + + delivery delivery (&layout, st); + + const db::ICplxTrans &tr = v->first; + db::ICplxTrans trinv = tr.inverted (); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + db::Polygon poly; + si->polygon (poly); + poly.transform (tr); + heap.clear (); + filter.process (poly, heap); + for (typename std::vector::const_iterator i = heap.begin (); i != heap.end (); ++i) { + delivery.put (i->transformed (trinv)); + } + } + + } + + } else { + + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + delivery delivery (&layout, &st); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + db::Polygon poly; + si->polygon (poly); + heap.clear (); + filter.process (poly, heap); + for (typename std::vector::const_iterator i = heap.begin (); i != heap.end (); ++i) { + delivery.put (*i); + } + } + + } + + } + + if (! to_commit.empty () && vars.get ()) { + res->deep_layer ().commit_shapes (*vars, to_commit); + } + + if (filter.result_is_merged ()) { + res->set_is_merged (true); + } + return res.release (); +} + +RegionDelegate * +DeepRegion::filter_in_place (const PolygonFilterBase &filter) +{ + // TODO: implement to be really in-place + return filtered (filter); +} + +RegionDelegate * +DeepRegion::filtered (const PolygonFilterBase &filter) const +{ + if (! filter.requires_raw_input ()) { + ensure_merged_polygons_valid (); + } + + std::auto_ptr vars; + if (filter.vars ()) { + + vars.reset (new db::VariantsCollectorBase (filter.vars ())); + + vars->collect (m_deep_layer.layout (), m_deep_layer.initial_cell ()); + + if (filter.wants_variants ()) { + const_cast (m_deep_layer).separate_variants (*vars); + } + + } + + db::Layout &layout = const_cast (m_deep_layer.layout ()); + std::map > to_commit; + + std::auto_ptr res (new db::DeepRegion (m_deep_layer.derived ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &s = c->shapes (filter.requires_raw_input () ? m_deep_layer.layer () : m_merged_polygons.layer ()); + + if (vars.get ()) { + + const std::map &vv = vars->variants (c->cell_index ()); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + + db::Shapes *st; + if (vv.size () == 1) { + st = & c->shapes (res->deep_layer ().layer ()); + } else { + st = & to_commit [c->cell_index ()] [v->first]; + } + + const db::ICplxTrans &tr = v->first; + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + db::Polygon poly; + si->polygon (poly); + if (filter.selected (poly.transformed (tr))) { + st->insert (*si); + } + } + + } + + } else { + + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + db::Polygon poly; + si->polygon (poly); + if (filter.selected (poly)) { + st.insert (*si); + } + } + + } + + } + + if (! to_commit.empty () && vars.get ()) { + res->deep_layer ().commit_shapes (*vars, to_commit); + } + + if (! filter.requires_raw_input ()) { + res->set_is_merged (true); + } + return res.release (); +} + +RegionDelegate * +DeepRegion::merged_in_place () +{ + ensure_merged_polygons_valid (); + + // NOTE: this makes both layers share the same resource + m_deep_layer = m_merged_polygons; + + set_is_merged (true); + return this; +} + +RegionDelegate * +DeepRegion::merged_in_place (bool min_coherence, unsigned int min_wc) +{ + return merged (min_coherence, min_wc); +} + +RegionDelegate * +DeepRegion::merged () const +{ + ensure_merged_polygons_valid (); + + db::Layout &layout = const_cast (m_merged_polygons.layout ()); + + std::auto_ptr res (new db::DeepRegion (m_merged_polygons.derived ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + c->shapes (res->deep_layer ().layer ()) = c->shapes (m_merged_polygons.layer ()); + } + + res->deep_layer ().layer (); + + res->set_is_merged (true); + return res.release (); +} + +RegionDelegate * +DeepRegion::merged (bool min_coherence, unsigned int min_wc) const +{ + tl::SelfTimer timer (tl::verbosity () > base_verbosity (), "Ensure merged polygons"); + + db::Layout &layout = const_cast (m_deep_layer.layout ()); + + db::hier_clusters hc; + db::Connectivity conn; + conn.connect (m_deep_layer); + hc.set_base_verbosity (base_verbosity () + 10); + hc.build (layout, m_deep_layer.initial_cell (), db::ShapeIterator::Polygons, conn); + + // collect the clusters and merge them into big polygons + // NOTE: using the ClusterMerger we merge bottom-up forming bigger and bigger polygons. This is + // hopefully more efficient that collecting everything and will lead to reuse of parts. + + DeepLayer dl_out (m_deep_layer.derived ()); + + ClusterMerger cm (m_deep_layer.layer (), layout, hc, min_coherence, report_progress (), progress_desc ()); + cm.set_base_verbosity (base_verbosity () + 10); + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + const db::connected_clusters &cc = hc.clusters_per_cell (c->cell_index ()); + for (db::connected_clusters::all_iterator cl = cc.begin_all (); ! cl.at_end (); ++cl) { + if (cc.is_root (*cl)) { + db::Shapes &s = cm.merged (*cl, c->cell_index (), min_wc); + c->shapes (dl_out.layer ()).insert (s); + cm.erase (*cl, c->cell_index ()); // not needed anymore + } + } + } + + db::DeepRegion *res = new db::DeepRegion (dl_out); + res->set_is_merged (true); + return res; +} + +RegionDelegate * +DeepRegion::sized (coord_type d, unsigned int mode) const +{ + ensure_merged_polygons_valid (); + + db::Layout &layout = m_merged_polygons.layout (); + + db::cell_variants_collector vars; + vars.collect (m_merged_polygons.layout (), m_merged_polygons.initial_cell ()); + + // NOTE: m_merged_polygons is mutable, so why is the const_cast needed? + const_cast (m_merged_polygons).separate_variants (vars); + + std::auto_ptr res (new db::DeepRegion (m_merged_polygons.derived ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const std::map &v = vars.variants (c->cell_index ()); + tl_assert (v.size () == size_t (1)); + double mag = v.begin ()->first.mag (); + db::Coord d_with_mag = db::coord_traits::rounded (d / mag); + + const db::Shapes &s = c->shapes (m_merged_polygons.layer ()); + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + + db::PolygonRefToShapesGenerator pr (&layout, &st); + db::PolygonGenerator pg2 (pr, false /*don't resolve holes*/, true /*min. coherence*/); + db::SizingPolygonFilter siz (pg2, d_with_mag, d_with_mag, mode); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + db::Polygon poly; + si->polygon (poly); + siz.put (poly); + } + + } + + // in case of negative sizing the output polygons will still be merged (on positive sizing they might + // overlap after size and are not necessarily merged) + if (d < 0) { + res->set_is_merged (true); + } + + return res.release (); +} + +namespace +{ + +struct XYAnisotropyAndMagnificationReducer + : public db::TransformationReducer +{ + db::ICplxTrans reduce (const db::ICplxTrans &trans) const + { + double a = trans.angle (); + if (a > 180.0 - db::epsilon) { + a -= 180.0; + } + return db::ICplxTrans (trans.mag (), a, false, db::Vector ()); + } + + db::Trans reduce (const db::Trans &trans) const + { + return db::Trans (trans.angle () % 2, false, db::Vector ()); + } +}; + +} + +RegionDelegate * +DeepRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const +{ + if (dx == dy) { + return sized (dx, mode); + } + + ensure_merged_polygons_valid (); + + db::Layout &layout = m_merged_polygons.layout (); + + db::cell_variants_collector vars; + vars.collect (m_merged_polygons.layout (), m_merged_polygons.initial_cell ()); + + // NOTE: m_merged_polygons is mutable, so why is the const_cast needed? + const_cast (m_merged_polygons).separate_variants (vars); + + std::auto_ptr res (new db::DeepRegion (m_merged_polygons.derived ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const std::map &v = vars.variants (c->cell_index ()); + tl_assert (v.size () == size_t (1)); + double mag = v.begin ()->first.mag (); + double angle = v.begin ()->first.angle (); + + db::Coord dx_with_mag = db::coord_traits::rounded (dx / mag); + db::Coord dy_with_mag = db::coord_traits::rounded (dy / mag); + if (fabs (angle - 90.0) < 45.0) { + // TODO: how to handle x/y swapping on arbitrary angles? + std::swap (dx_with_mag, dy_with_mag); + } + + const db::Shapes &s = c->shapes (m_merged_polygons.layer ()); + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + + db::PolygonRefToShapesGenerator pr (&layout, &st); + db::PolygonGenerator pg2 (pr, false /*don't resolve holes*/, true /*min. coherence*/); + db::SizingPolygonFilter siz (pg2, dx_with_mag, dy_with_mag, mode); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + db::Polygon poly; + si->polygon (poly); + siz.put (poly); + } + + } + + // in case of negative sizing the output polygons will still be merged (on positive sizing they might + // overlap after size and are not necessarily merged) + if (dx < 0 && dy < 0) { + res->set_is_merged (true); + } + + return res.release (); +} + +RegionDelegate * +DeepRegion::in (const Region &other, bool invert) const +{ + // TODO: this can be optimized maybe ... + return db::AsIfFlatRegion::in (other, invert); +} + +namespace +{ + +class CheckLocalOperation + : public local_operation +{ +public: + CheckLocalOperation (const EdgeRelationFilter &check, bool different_polygons, bool has_other) + : m_check (check), m_different_polygons (different_polygons), m_has_other (has_other) + { + // .. nothing yet .. + } + + virtual void compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::unordered_set &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const + { + edge2edge_check > edge_check (m_check, result, m_different_polygons, m_has_other); + poly2poly_check > poly_check (edge_check); + + std::list heap; + db::box_scanner scanner; + + if (m_has_other) { + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j)); + } + } + + size_t n = 0; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const db::PolygonRef &subject = interactions.subject_shape (i->first); + heap.push_back (subject.obj ().transformed (subject.trans ())); + scanner.insert (& heap.back (), n); + n += 2; + } + + n = 1; + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + heap.push_back (o->obj ().transformed (o->trans ())); + scanner.insert (& heap.back (), n); + n += 2; + } + + } else { + + std::set polygons; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + polygons.insert (interactions.subject_shape (i->first)); + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + polygons.insert (interactions.intruder_shape (*j)); + } + } + + size_t n = 0; + for (std::set::const_iterator o = polygons.begin (); o != polygons.end (); ++o) { + heap.push_back (o->obj ().transformed (o->trans ())); + scanner.insert (& heap.back (), n); + n += 2; + } + + } + + do { + scanner.process (poly_check, m_check.distance (), db::box_convert ()); + } while (edge_check.prepare_next_pass ()); + } + + virtual db::Coord dist () const + { + // TODO: will the distance be sufficient? Or should we take somewhat more? + return m_check.distance (); + } + + virtual on_empty_intruder_mode on_empty_intruder_hint () const + { + return m_different_polygons ? Drop : Ignore; + } + + virtual std::string description () const + { + return tl::to_string (tr ("Generic DRC check")); + } + +private: + EdgeRelationFilter m_check; + bool m_different_polygons; + bool m_has_other; +}; + +} + +EdgePairsDelegate * +DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + const db::DeepRegion *other_deep = 0; + if (other) { + other_deep = dynamic_cast (other->delegate ()); + if (! other_deep) { + return db::AsIfFlatRegion::run_check (rel, different_polygons, other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + } + + ensure_merged_polygons_valid (); + + EdgeRelationFilter check (rel, d, metrics); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + std::auto_ptr res (new db::DeepEdgePairs (m_merged_polygons.derived ())); + + db::CheckLocalOperation op (check, different_polygons, other_deep != 0); + + db::local_processor proc (const_cast (&m_deep_layer.layout ()), + const_cast (&m_deep_layer.initial_cell ()), + other_deep ? &other_deep->deep_layer ().layout () : const_cast (&m_deep_layer.layout ()), + other_deep ? &other_deep->deep_layer ().initial_cell () : const_cast (&m_deep_layer.initial_cell ())); + + proc.set_base_verbosity (base_verbosity ()); + proc.set_threads (m_deep_layer.store ()->threads ()); + + proc.run (&op, m_merged_polygons.layer (), other_deep ? other_deep->deep_layer ().layer () : m_merged_polygons.layer (), res->deep_layer ().layer ()); + + return res.release (); +} + +EdgePairsDelegate * +DeepRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + ensure_merged_polygons_valid (); + + EdgeRelationFilter check (rel, d, metrics); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + db::Layout &layout = m_merged_polygons.layout (); + + std::auto_ptr res (new db::DeepEdgePairs (m_merged_polygons.derived ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &shapes = c->shapes (m_merged_polygons.layer ()); + db::Shapes &result = c->shapes (res->deep_layer ().layer ()); + + for (db::Shapes::shape_iterator s = shapes.begin (db::ShapeIterator::Polygons); ! s.at_end (); ++s) { + + edge2edge_check edge_check (check, result, false, false); + poly2poly_check poly_check (edge_check); + + db::Polygon poly; + s->polygon (poly); + + do { + poly_check.enter (poly, 0); + } while (edge_check.prepare_next_pass ()); + + } + + } + + return res.release (); +} + +namespace +{ + +class InteractingLocalOperation + : public local_operation +{ +public: + InteractingLocalOperation (int mode, bool touching, bool inverse) + : m_mode (mode), m_touching (touching), m_inverse (inverse) + { + // .. nothing yet .. + } + + virtual void compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::unordered_set &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const + { + m_ep.clear (); + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j)); + } + } + + size_t n = 1; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i, ++n) { + const db::PolygonRef &subject = interactions.subject_shape (i->first); + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + m_ep.insert (*e, n); + } + } + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end(); ++e) { + m_ep.insert (*e, 0); + } + } + + db::InteractionDetector id (m_mode, 0); + id.set_include_touching (m_touching); + db::EdgeSink es; + m_ep.process (es, id); + id.finish (); + + n = 0; + std::set selected; + for (db::InteractionDetector::iterator i = id.begin (); i != id.end () && i->first == 0; ++i) { + ++n; + selected.insert (i->second); + } + + n = 1; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i, ++n) { + if ((selected.find (n) == selected.end ()) == m_inverse) { + const db::PolygonRef &subject = interactions.subject_shape (i->first); + result.insert (subject); + } + } + } + + virtual on_empty_intruder_mode on_empty_intruder_hint () const + { + if ((m_mode <= 0) != m_inverse) { + return Drop; + } else { + return Copy; + } + } + + virtual std::string description () const + { + return tl::to_string (tr ("Select regions by their geometric relation (interacting, inside, outside ..)")); + } + +private: + int m_mode; + bool m_touching; + bool m_inverse; + mutable db::EdgeProcessor m_ep; +}; + +struct ResultInserter +{ + ResultInserter (db::Layout *layout, std::unordered_set &result) + : mp_layout (layout), mp_result (&result) + { + // .. nothing yet .. + } + + void insert (const db::Polygon &p) + { + (*mp_result).insert (db::PolygonRef (p, mp_layout->shape_repository ())); + } + +private: + db::Layout *mp_layout; + std::unordered_set *mp_result; +}; + +class InteractingWithEdgeLocalOperation + : public local_operation +{ +public: + InteractingWithEdgeLocalOperation (bool inverse) + : m_inverse (inverse) + { + // .. nothing yet .. + } + + virtual void compute_local (db::Layout *layout, const shape_interactions &interactions, std::unordered_set &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const + { + m_scanner.clear (); + + ResultInserter inserter (layout, result); + region_to_edge_interaction_filter filter (inserter, m_inverse); + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + m_scanner.insert2 (& interactions.intruder_shape (*j), 0); + } + } + + std::list heap; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + const db::PolygonRef &subject = interactions.subject_shape (i->first); + heap.push_back (subject.obj ().transformed (subject.trans ())); + + m_scanner.insert1 (&heap.back (), 0); + if (m_inverse) { + filter.preset (&heap.back ()); + } + + } + + m_scanner.process (filter, 1, db::box_convert (), db::box_convert ()); + if (m_inverse) { + filter.fill_output (); + } + } + + virtual on_empty_intruder_mode on_empty_intruder_hint () const + { + if (!m_inverse) { + return Drop; + } else { + return Copy; + } + } + + virtual std::string description () const + { + return tl::to_string (tr ("Select regions by their geometric relation (interacting, inside, outside ..)")); + } + +private: + bool m_inverse; + mutable db::box_scanner2 m_scanner; +}; + +} + +RegionDelegate * +DeepRegion::selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const +{ + // with these flag set to true, the resulting polygons are broken again. + bool split_after = false; + + const db::DeepRegion *other_deep = dynamic_cast (other.delegate ()); + if (! other_deep) { + return db::AsIfFlatRegion::selected_interacting_generic (other, mode, touching, inverse); + } + + ensure_merged_polygons_valid (); + + DeepLayer dl_out (m_deep_layer.derived ()); + + db::InteractingLocalOperation op (mode, touching, inverse); + + db::local_processor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell ()); + proc.set_base_verbosity (base_verbosity ()); + proc.set_threads (m_deep_layer.store ()->threads ()); + if (split_after) { + proc.set_area_ratio (m_deep_layer.store ()->max_area_ratio ()); + proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ()); + } + + proc.run (&op, m_merged_polygons.layer (), other_deep->deep_layer ().layer (), dl_out.layer ()); + + db::DeepRegion *res = new db::DeepRegion (dl_out); + if (! split_after) { + res->set_is_merged (true); + } + return res; +} + +RegionDelegate * +DeepRegion::selected_interacting_generic (const Edges &other, bool inverse) const +{ + // with these flag set to true, the resulting polygons are broken again. + bool split_after = false; + + const db::DeepEdges *other_deep = dynamic_cast (other.delegate ()); + if (! other_deep) { + return db::AsIfFlatRegion::selected_interacting_generic (other, inverse); + } + + ensure_merged_polygons_valid (); + + DeepLayer dl_out (m_deep_layer.derived ()); + + db::InteractingWithEdgeLocalOperation op (inverse); + + db::local_processor proc (const_cast (&m_deep_layer.layout ()), const_cast (&m_deep_layer.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell ()); + proc.set_base_verbosity (base_verbosity ()); + proc.set_threads (m_deep_layer.store ()->threads ()); + if (split_after) { + proc.set_area_ratio (m_deep_layer.store ()->max_area_ratio ()); + proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ()); + } + + proc.run (&op, m_merged_polygons.layer (), other_deep->deep_layer ().layer (), dl_out.layer ()); + + db::DeepRegion *res = new db::DeepRegion (dl_out); + if (! split_after) { + res->set_is_merged (true); + } + return res; +} + +} diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h new file mode 100644 index 000000000..6ede2cee0 --- /dev/null +++ b/src/db/db/dbDeepRegion.h @@ -0,0 +1,254 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbDeepRegion +#define HDR_dbDeepRegion + +#include "dbCommon.h" + +#include "dbAsIfFlatRegion.h" +#include "dbDeepShapeStore.h" + +namespace db { + +/** + * @brief A deep, polygon-set delegate + */ +class DB_PUBLIC DeepRegion + : public AsIfFlatRegion +{ +public: + typedef db::layer polygon_layer_type; + typedef polygon_layer_type::iterator polygon_iterator_type; + + DeepRegion (); + DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio = 0.0, size_t max_vertex_count = 0); + DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics = true, double area_ratio = 0.0, size_t max_vertex_count = 0); + + DeepRegion (const DeepRegion &other); + DeepRegion (const DeepLayer &dl); + + virtual ~DeepRegion (); + + RegionDelegate *clone () const; + + virtual RegionIteratorDelegate *begin () const; + virtual RegionIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + virtual bool is_merged () const; + + virtual const db::Polygon *nth (size_t n) const; + virtual bool has_valid_polygons () const; + virtual bool has_valid_merged_polygons () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; + + virtual bool is_box () const; + virtual size_t size () const; + + virtual area_type area (const db::Box &box) const; + virtual perimeter_type perimeter (const db::Box &box) const; + virtual Box bbox () const; + + virtual std::string to_string (size_t nmax) const; + + virtual RegionDelegate *and_with (const Region &other) const; + virtual RegionDelegate *not_with (const Region &other) const; + virtual RegionDelegate *xor_with (const Region &other) const; + + virtual RegionDelegate *add_in_place (const Region &other); + virtual RegionDelegate *add (const Region &other) const; + + EdgePairsDelegate *width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_single_polygon_check (db::WidthRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, false, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, true, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_single_polygon_check (db::SpaceRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::OverlapRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::WidthRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairsDelegate *inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::InsideRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairsDelegate *grid_check (db::Coord gx, db::Coord gy) const; + virtual EdgePairsDelegate *angle_check (double min, double max, bool inverse) const; + + virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy) + { + return snapped (gx, gy); + } + + virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy); + + virtual EdgesDelegate *edges (const EdgeFilterBase *) const; + + virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter); + virtual RegionDelegate *processed (const PolygonProcessorBase &filter) const; + virtual EdgesDelegate *processed_to_edges (const PolygonToEdgeProcessorBase &filter) const; + virtual EdgePairsDelegate *processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &filter) const; + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter); + virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const; + + virtual RegionDelegate *merged_in_place (); + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); + + virtual RegionDelegate *merged () const; + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const; + + virtual RegionDelegate *sized (coord_type d, unsigned int mode) const; + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const; + + virtual RegionDelegate *selected_outside (const Region &other) const + { + return selected_interacting_generic (other, 1, false, false); + } + + virtual RegionDelegate *selected_not_outside (const Region &other) const + { + return selected_interacting_generic (other, 1, false, true); + } + + virtual RegionDelegate *selected_inside (const Region &other) const + { + return selected_interacting_generic (other, -1, true, false); + } + + virtual RegionDelegate *selected_not_inside (const Region &other) const + { + return selected_interacting_generic (other, -1, true, true); + } + + virtual RegionDelegate *selected_interacting (const Region &other) const + { + return selected_interacting_generic (other, 0, true, false); + } + + virtual RegionDelegate *selected_not_interacting (const Region &other) const + { + return selected_interacting_generic (other, 0, true, true); + } + + virtual RegionDelegate *selected_interacting (const Edges &other) const + { + return selected_interacting_generic (other, false); + } + + virtual RegionDelegate *selected_not_interacting (const Edges &other) const + { + return selected_interacting_generic (other, true); + } + + virtual RegionDelegate *selected_overlapping (const Region &other) const + { + return selected_interacting_generic (other, 0, false, false); + } + + virtual RegionDelegate *selected_not_overlapping (const Region &other) const + { + return selected_interacting_generic (other, 0, false, true); + } + + virtual RegionDelegate *in (const Region &other, bool invert) const; + + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + + const DeepLayer &deep_layer () const + { + return m_deep_layer; + } + + DeepLayer &deep_layer () + { + return m_deep_layer; + } + +protected: + virtual void merged_semantics_changed (); + void set_is_merged (bool f); + +private: + friend class DeepEdges; + + DeepRegion &operator= (const DeepRegion &other); + + DeepLayer m_deep_layer; + mutable DeepLayer m_merged_polygons; + mutable bool m_merged_polygons_valid; + bool m_is_merged; + + void init (); + void ensure_merged_polygons_valid () const; + DeepLayer and_or_not_with(const DeepRegion *other, bool and_op) const; + EdgePairsDelegate *run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + EdgePairsDelegate *run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + RegionDelegate *selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const; + RegionDelegate *selected_interacting_generic (const Edges &other, bool inverse) const; + + const DeepLayer &merged_deep_layer () const + { + return m_merged_polygons; + } + + template OutputContainer *processed_impl (const polygon_processor &filter) const; +}; + +} + +#endif + diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc new file mode 100644 index 000000000..1e0c1e96b --- /dev/null +++ b/src/db/db/dbDeepShapeStore.cc @@ -0,0 +1,902 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbDeepShapeStore.h" +#include "dbCellMapping.h" +#include "dbLayoutUtils.h" +#include "dbRegion.h" +#include "dbDeepRegion.h" + +#include "tlTimer.h" + +namespace db +{ + +// ---------------------------------------------------------------------------------- + +DeepLayer::DeepLayer () + : mp_store (), m_layout (0), m_layer (0) +{ + // .. nothing yet .. +} + +DeepLayer::DeepLayer (const Region ®ion) + : mp_store (), m_layout (0), m_layer (0) +{ + const db::DeepRegion *dr = dynamic_cast (region.delegate ()); + tl_assert (dr != 0); + *this = dr->deep_layer (); +} + +DeepLayer::DeepLayer (const DeepLayer &x) + : mp_store (x.mp_store), m_layout (x.m_layout), m_layer (x.m_layer) +{ + if (mp_store.get ()) { + mp_store->add_ref (m_layout, m_layer); + } +} + +DeepLayer::DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int layer) + : mp_store (store), m_layout (layout), m_layer (layer) +{ + if (store) { + store->add_ref (layout, layer); + } +} + +DeepLayer &DeepLayer::operator= (const DeepLayer &other) +{ + if (this != &other) { + if (mp_store.get ()) { + mp_store->remove_ref (m_layout, m_layer); + } + mp_store = other.mp_store; + m_layout = other.m_layout; + m_layer = other.m_layer; + if (mp_store.get ()) { + mp_store->add_ref (m_layout, m_layer); + } + } + + return *this; +} + + +DeepLayer::~DeepLayer () +{ + if (mp_store.get ()) { + mp_store->remove_ref (m_layout, m_layer); + } +} + +DeepLayer +DeepLayer::derived () const +{ + return DeepLayer (const_cast (mp_store.get ()), m_layout, const_cast (layout ()).insert_layer ()); +} + +DeepLayer +DeepLayer::copy () const +{ + DeepLayer new_layer (derived ()); + + db::DeepShapeStore *non_const_store = const_cast (mp_store.get ()); + non_const_store->layout (m_layout).copy_layer (m_layer, new_layer.layer ()); + + return new_layer; +} + +void +DeepLayer::add_from (const DeepLayer &dl) +{ + if (&dl.layout () == &layout ()) { + + // intra-layout merge + + layout ().copy_layer (dl.layer (), layer ()); + + } else { + + // inter-layout merge + + db::cell_index_type into_cell = initial_cell ().cell_index (); + db::Layout *into_layout = &layout (); + db::cell_index_type source_cell = dl.initial_cell ().cell_index (); + const db::Layout *source_layout = &dl.layout (); + + db::CellMapping cm; + cm.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_cell); + + // Actually copy the shapes + + std::map lm; + lm.insert (std::make_pair (dl.layer (), layer ())); + + std::vector source_cells; + source_cells.push_back (source_cell); + db::copy_shapes (*into_layout, *source_layout, db::ICplxTrans (), source_cells, cm.table (), lm); + + } +} + +void +DeepLayer::insert_into (db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + check_dss (); + const_cast (mp_store.get ())->insert (*this, into_layout, into_cell, into_layer); +} + +void +DeepLayer::insert_into_as_polygons (db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const +{ + check_dss (); + const_cast (mp_store.get ())->insert_as_polygons (*this, into_layout, into_cell, into_layer, enl); +} + +bool DeepLayer::operator< (const DeepLayer &other) const +{ + if (mp_store.get () != other.mp_store.get ()) { + return mp_store.get () < other.mp_store.get (); + } + if (m_layout != other.m_layout) { + return m_layout < other.m_layout; + } + if (m_layer != other.m_layer) { + return m_layer < other.m_layer; + } + return false; +} + +bool DeepLayer::operator== (const DeepLayer &other) const +{ + if (mp_store.get () != other.mp_store.get ()) { + return false; + } + if (m_layout != other.m_layout) { + return false; + } + if (m_layer != other.m_layer) { + return false; + } + return true; +} + +db::Layout & +DeepLayer::layout () +{ + check_dss (); + return mp_store->layout (m_layout); +} + +const db::Layout & +DeepLayer::layout () const +{ + check_dss (); + return const_cast (mp_store.get ())->layout (m_layout); +} + +db::Cell & +DeepLayer::initial_cell () +{ + check_dss (); + return mp_store->initial_cell (m_layout); +} + +const db::Cell & +DeepLayer::initial_cell () const +{ + check_dss (); + return mp_store->const_initial_cell (m_layout); +} + +void +DeepLayer::check_dss () const +{ + if (mp_store.get () == 0) { + throw tl::Exception (tl::to_string (tr ("Heap lost: the DeepShapeStore container no longer exists"))); + } +} + +// ---------------------------------------------------------------------------------- + +struct DeepShapeStore::LayoutHolder +{ + LayoutHolder (const db::ICplxTrans &trans) + : refs (0), layout (false), builder (&layout, trans), m_empty_layer (std::numeric_limits::max ()) + { + // .. nothing yet .. + } + + unsigned int empty_layer () const + { + if (m_empty_layer == std::numeric_limits::max ()) { + const_cast (this)->make_empty_layer (); + } + return m_empty_layer; + } + + void add_layer_ref (unsigned int layer) + { + layer_refs [layer] += 1; + } + + bool remove_layer_ref (unsigned int layer) + { + if ((layer_refs[layer] -= 1) <= 0) { + layout.delete_layer (layer); + layer_refs.erase (layer); + return true; + } else { + return false; + } + } + + int refs; + db::Layout layout; + db::HierarchyBuilder builder; + std::map layer_refs; + +private: + unsigned int m_empty_layer; + + void make_empty_layer () + { + m_empty_layer = layout.insert_layer (); + layer_refs [m_empty_layer] += 1; // the empty layer is not deleted + } +}; + +// ---------------------------------------------------------------------------------- + +static size_t s_instance_count = 0; + +DeepShapeStore::DeepShapeStore () + : m_threads (1), m_max_area_ratio (3.0), m_max_vertex_count (16), m_text_property_name (), m_text_enlargement (-1) +{ + ++s_instance_count; +} + +DeepShapeStore::DeepShapeStore (const std::string &topcell_name, double dbu) + : m_threads (1), m_max_area_ratio (3.0), m_max_vertex_count (16), m_text_property_name (), m_text_enlargement (-1) +{ + ++s_instance_count; + + m_layouts.push_back (new LayoutHolder (db::ICplxTrans ())); + m_layouts.back ()->layout.dbu (dbu); + m_layouts.back ()->layout.add_cell (topcell_name.c_str ()); +} + +DeepShapeStore::~DeepShapeStore () +{ + --s_instance_count; + + for (std::vector::iterator h = m_layouts.begin (); h != m_layouts.end (); ++h) { + delete *h; + } + m_layouts.clear (); +} + +DeepLayer DeepShapeStore::create_from_flat (const db::Region ®ion, bool for_netlist, double max_area_ratio, size_t max_vertex_count, const db::ICplxTrans &trans) +{ + // reuse existing layer + std::pair lff = layer_for_flat (region); + if (lff.first) { + return lff.second; + } + + require_singular (); + + unsigned int layer = layout ().insert_layer (); + + if (max_area_ratio == 0.0) { + max_area_ratio = m_max_area_ratio; + } + if (max_vertex_count == 0) { + max_vertex_count = m_max_vertex_count; + } + + db::Shapes *shapes = &initial_cell ().shapes (layer); + db::Box world = db::Box::world (); + + // The chain of operators for producing clipped and reduced polygon references + db::PolygonReferenceHierarchyBuilderShapeReceiver refs (&layout (), m_text_enlargement, m_text_property_name); + db::ReducingHierarchyBuilderShapeReceiver red (&refs, max_area_ratio, max_vertex_count); + + // try to maintain the texts on top level - go through shape iterator + std::pair ii = region.begin_iter (); + db::ICplxTrans ttop = trans * ii.second; + while (! ii.first.at_end ()) { + + if (for_netlist && ii.first->is_text () && ii.first.layout () && ii.first.cell () != ii.first.top_cell ()) { + // Skip texts on levels below top cell. For the reasoning see the description of this method. + } else { + red.push (*ii.first, ttop * ii.first.trans (), world, 0, shapes); + } + + ++ii.first; + + } + + DeepLayer dl (this, 0 /*singular layout index*/, layer); + m_layers_for_flat [tl::id_of (region.delegate ())] = std::make_pair (dl.layout_index (), dl.layer ()); + m_flat_region_id [std::make_pair (dl.layout_index (), dl.layer ())] = tl::id_of (region.delegate ()); + return dl; +} + +std::pair DeepShapeStore::layer_for_flat (const db::Region ®ion) const +{ + return layer_for_flat (tl::id_of (region.delegate ())); +} + +std::pair DeepShapeStore::layer_for_flat (size_t region_id) const +{ + std::map >::const_iterator lff = m_layers_for_flat.find (region_id); + if (lff == m_layers_for_flat.end ()) { + return std::make_pair (false, DeepLayer ()); + } else { + return std::make_pair (true, DeepLayer (const_cast (this), lff->second.first, lff->second.second)); + } +} + +bool DeepShapeStore::is_singular () const +{ + return m_layouts.size () == 1; +} + +void DeepShapeStore::require_singular () const +{ + if (! is_singular ()) { + throw tl::Exception (tl::to_string (tr ("Internal error: deep shape store isn't singular. This may happen if you try to mix hierarchical layers from different sources our you use clipping."))); + } +} + +Cell &DeepShapeStore::initial_cell(unsigned int n) +{ + db::Layout &ly = layout (n); + tl_assert (ly.cells () > 0); + return ly.cell (*ly.begin_top_down ()); +} + +const db::Cell &DeepShapeStore::const_initial_cell (unsigned int n) const +{ + const db::Layout &ly = const_layout (n); + tl_assert (ly.cells () > 0); + return ly.cell (*ly.begin_top_down ()); +} + +void DeepShapeStore::set_text_enlargement (int enl) +{ + m_text_enlargement = enl; +} + +void DeepShapeStore::set_text_property_name (const tl::Variant &pn) +{ + m_text_property_name = pn; +} + +bool DeepShapeStore::is_valid_layout_index (unsigned int n) const +{ + return (n < (unsigned int) m_layouts.size () && m_layouts[n] != 0); +} + +const db::Layout &DeepShapeStore::const_layout (unsigned int n) const +{ + tl_assert (is_valid_layout_index (n)); + return m_layouts [n]->layout; +} + +db::Layout &DeepShapeStore::layout (unsigned int n) +{ + tl_assert (is_valid_layout_index (n)); + return m_layouts [n]->layout; +} + +size_t DeepShapeStore::instance_count () +{ + return s_instance_count; +} + +void DeepShapeStore::set_threads (int n) +{ + m_threads = n; +} + +void DeepShapeStore::set_max_area_ratio (double ar) +{ + m_max_area_ratio = ar; +} + +void DeepShapeStore::set_max_vertex_count (size_t n) +{ + m_max_vertex_count = n; +} + +void DeepShapeStore::add_ref (unsigned int layout, unsigned int layer) +{ + tl::MutexLocker locker (&m_lock); + + tl_assert (layout < (unsigned int) m_layouts.size () && m_layouts[layout] != 0); + + m_layouts[layout]->refs += 1; + m_layouts[layout]->add_layer_ref (layer); +} + +void DeepShapeStore::remove_ref (unsigned int layout, unsigned int layer) +{ + tl::MutexLocker locker (&m_lock); + + tl_assert (layout < (unsigned int) m_layouts.size () && m_layouts[layout] != 0); + + if (m_layouts[layout]->remove_layer_ref (layer)) { + + // remove from flat region cross ref if required + std::map, size_t>::iterator fri = m_flat_region_id.find (std::make_pair (layout, layer)); + if (fri != m_flat_region_id.end ()) { + m_layers_for_flat.erase (fri->second); + m_flat_region_id.erase (fri); + } + + } + + if ((m_layouts[layout]->refs -= 1) <= 0) { + delete m_layouts[layout]; + m_layouts[layout] = 0; + } +} + +unsigned int +DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) +{ + layout_map_type::iterator l = m_layout_map.find (std::make_pair (si, trans)); + if (l == m_layout_map.end () || m_layouts[l->second] == 0) { + + unsigned int layout_index; + + if (l != m_layout_map.end ()) { + // reuse discarded entry + layout_index = l->second; + m_layouts[layout_index] = new LayoutHolder (trans); + } else { + layout_index = (unsigned int) m_layouts.size (); + m_layouts.push_back (new LayoutHolder (trans)); + } + + db::Layout &layout = m_layouts[layout_index]->layout; + layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier); + if (si.layout ()) { + layout.dbu (si.layout ()->dbu () / trans.mag ()); + } + + m_layout_map[std::make_pair (si, trans)] = layout_index; + return layout_index; + + } else { + return l->second; + } +} + +void DeepShapeStore::make_layout (unsigned int layout_index, const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) +{ + tl_assert (m_layout_map.find (std::make_pair (si, trans)) == m_layout_map.end ()); + + while (m_layouts.size () <= layout_index) { + m_layouts.push_back (0); + } + + m_layouts[layout_index] = new LayoutHolder (trans); + + db::Layout &layout = m_layouts[layout_index]->layout; + layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier); + if (si.layout ()) { + layout.dbu (si.layout ()->dbu () / trans.mag ()); + } + + m_layout_map[std::make_pair (si, trans)] = layout_index; +} + +DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio, size_t max_vertex_count, const db::ICplxTrans &trans) +{ + if (max_area_ratio == 0.0) { + max_area_ratio = m_max_area_ratio; + } + if (max_vertex_count == 0) { + max_vertex_count = m_max_vertex_count; + } + + unsigned int layout_index = layout_for_iter (si, trans); + + db::Layout &layout = m_layouts[layout_index]->layout; + db::HierarchyBuilder &builder = m_layouts[layout_index]->builder; + + unsigned int layer_index = layout.insert_layer (); + builder.set_target_layer (layer_index); + + // The chain of operators for producing clipped and reduced polygon references + db::PolygonReferenceHierarchyBuilderShapeReceiver refs (& layout, m_text_enlargement, m_text_property_name); + db::ReducingHierarchyBuilderShapeReceiver red (&refs, max_area_ratio, max_vertex_count); + db::ClippingHierarchyBuilderShapeReceiver clip (&red); + + // Build the working hierarchy from the recursive shape iterator + try { + + tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy"))); + + builder.set_shape_receiver (&clip); + db::RecursiveShapeIterator (si).push (& builder); + builder.set_shape_receiver (0); + + } catch (...) { + builder.set_shape_receiver (0); + throw; + } + + return DeepLayer (this, layout_index, layer_index); +} + +DeepLayer DeepShapeStore::empty_layer (unsigned int layout_index) const +{ + return DeepLayer (const_cast (this), layout_index, m_layouts[layout_index]->empty_layer ()); +} + +DeepLayer DeepShapeStore::empty_layer () const +{ + require_singular (); + return empty_layer (0); +} + +DeepLayer DeepShapeStore::create_custom_layer (const db::RecursiveShapeIterator &si, HierarchyBuilderShapeReceiver *pipe, const db::ICplxTrans &trans) +{ + unsigned int layout_index = layout_for_iter (si, trans); + + db::Layout &layout = m_layouts[layout_index]->layout; + db::HierarchyBuilder &builder = m_layouts[layout_index]->builder; + + unsigned int layer_index = layout.insert_layer (); + builder.set_target_layer (layer_index); + + // Build the working hierarchy from the recursive shape iterator + try { + + tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy"))); + + builder.set_shape_receiver (pipe); + db::RecursiveShapeIterator (si).push (& builder); + builder.set_shape_receiver (0); + + } catch (...) { + builder.set_shape_receiver (0); + throw; + } + + return DeepLayer (this, layout_index, layer_index); +} + +DeepLayer DeepShapeStore::create_copy (const DeepLayer &source, HierarchyBuilderShapeReceiver *pipe) +{ + tl_assert (source.store () == this); + + unsigned int from_layer_index = source.layer (); + db::Layout &ly = layout (); + + unsigned int layer_index = ly.insert_layer (); + + // Build the working hierarchy from the recursive shape iterator + tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy"))); + + db::Box region = db::Box::world (); + db::ICplxTrans trans; + + for (db::Layout::iterator c = ly.begin (); c != ly.end (); ++c) { + db::Shapes &into = c->shapes (layer_index); + const db::Shapes &from = c->shapes (from_layer_index); + for (db::Shapes::shape_iterator s = from.begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + pipe->push (*s, trans, region, 0, &into); + } + } + + return DeepLayer (this, source.layout_index (), layer_index); +} + +DeepLayer DeepShapeStore::create_edge_layer (const db::RecursiveShapeIterator &si, bool as_edges, const db::ICplxTrans &trans) +{ + unsigned int layout_index = layout_for_iter (si, trans); + + db::Layout &layout = m_layouts[layout_index]->layout; + db::HierarchyBuilder &builder = m_layouts[layout_index]->builder; + + unsigned int layer_index = layout.insert_layer (); + builder.set_target_layer (layer_index); + + // The chain of operators for producing edges + db::EdgeBuildingHierarchyBuilderShapeReceiver refs (as_edges); + + // Build the working hierarchy from the recursive shape iterator + try { + + tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy"))); + + builder.set_shape_receiver (&refs); + db::RecursiveShapeIterator (si).push (& builder); + builder.set_shape_receiver (0); + + } catch (...) { + builder.set_shape_receiver (0); + throw; + } + + return DeepLayer (this, layout_index, layer_index); +} + +DeepLayer DeepShapeStore::create_edge_pair_layer (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) +{ + unsigned int layout_index = layout_for_iter (si, trans); + + db::Layout &layout = m_layouts[layout_index]->layout; + db::HierarchyBuilder &builder = m_layouts[layout_index]->builder; + + unsigned int layer_index = layout.insert_layer (); + builder.set_target_layer (layer_index); + + // The chain of operators for producing the edge pairs + db::EdgePairBuildingHierarchyBuilderShapeReceiver refs; + + // Build the working hierarchy from the recursive shape iterator + try { + + tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy"))); + + builder.set_shape_receiver (&refs); + db::RecursiveShapeIterator (si).push (& builder); + builder.set_shape_receiver (0); + + } catch (...) { + builder.set_shape_receiver (0); + throw; + } + + return DeepLayer (this, layout_index, layer_index); +} + +void +DeepShapeStore::invalidate_hier () +{ + m_delivery_mapping_cache.clear (); +} + +void +DeepShapeStore::issue_variants (unsigned int layout_index, const std::map > &var_map) +{ + invalidate_hier (); + + db::HierarchyBuilder &builder = m_layouts [layout_index]->builder; + for (std::map >::const_iterator i = var_map.begin (); i != var_map.end (); ++i) { + for (std::map::const_iterator j = i->second.begin (); j != i->second.end (); ++j) { + builder.register_variant (i->first, j->second); + } + } +} + +const db::CellMapping & +DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set *excluded_cells) +{ + const db::Layout *source_layout = &m_layouts [layout_index]->layout; + if (source_layout->begin_top_down () == source_layout->end_top_cells ()) { + // empty source - nothing to do. + static db::CellMapping cm; + return cm; + } + + db::cell_index_type source_top = *source_layout->begin_top_down(); + + db::HierarchyBuilder &original_builder = m_layouts [layout_index]->builder; + + // Derive a cell mapping for source to target. We reuse any existing mapping for returning the + // shapes into the original layout. + + DeliveryMappingCacheKey key (layout_index, tl::id_of (into_layout), into_cell); + + std::map::iterator cm = m_delivery_mapping_cache.find (key); + if (cm == m_delivery_mapping_cache.end ()) { + + cm = m_delivery_mapping_cache.insert (std::make_pair (key, db::CellMapping ())).first; + + if (into_layout == original_builder.source ().layout () && &into_layout->cell (into_cell) == original_builder.source ().top_cell ()) { + + // This is the case of mapping back to the original. In this case we can use the information + // provided inside the original hierarchy builders. They list the source cells and the target cells + // create from them. We need to consider however, that the hierarchy builder is allowed to create + // variants which we cannot map. + + for (HierarchyBuilder::cell_map_type::const_iterator m = original_builder.begin_cell_map (); m != original_builder.end_cell_map (); ++m) { + + HierarchyBuilder::cell_map_type::const_iterator mm = m; + ++mm; + bool skip = original_builder.is_variant (m->second); // skip variant cells + while (mm != original_builder.end_cell_map () && mm->first.first == m->first.first && ! skip) { + // we have cell variants and cannot simply map + ++mm; + ++m; + skip = true; + } + + if (! skip) { + cm->second.map (m->second, m->first.first); + } + + } + + } else if (into_layout->cells () == 1) { + + // Another simple case is mapping into an empty (or single-top-cell-only) layout, where we can use "create_from_single_full". + cm->second.create_single_mapping (*into_layout, into_cell, *source_layout, source_top); + + } else { + + cm->second.create_from_geometry (*into_layout, into_cell, *source_layout, source_top); + + } + + // Add new cells for the variants and (possible) devices which are cells added during the device + // extraction process + cm->second.create_missing_mapping (*into_layout, into_cell, *source_layout, source_top, excluded_cells); + + } + + return cm->second; +} + +namespace +{ + class DeepShapeStoreToShapeTransformer + : public ShapesTransformer + { + public: + DeepShapeStoreToShapeTransformer (const DeepShapeStore &dss, const db::Layout &layout) + : mp_layout (& layout) + { + // gets the text annotation property ID - + // this is how the texts are passed for annotating the net names + m_text_annot_name_id = std::pair (false, 0); + if (! dss.text_property_name ().is_nil ()) { + m_text_annot_name_id = mp_layout->properties_repository ().get_id_of_name (dss.text_property_name ()); + } + } + + void insert_transformed (Shapes &into, const Shapes &from, const ICplxTrans &trans, PropertyMapper &pm) const + { + if (! m_text_annot_name_id.first) { + + // fast shortcut + into.insert_transformed (from, trans, pm); + + } else { + + for (db::Shapes::shape_iterator i = from.begin (db::ShapeIterator::All); ! i.at_end (); ++i) { + + bool is_text = false; + + if (i->prop_id () > 0) { + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (i->prop_id ()); + + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end () && ! is_text; ++j) { + if (j->first == m_text_annot_name_id.second) { + + db::Text text (j->second.to_string (), db::Trans (i->bbox ().center () - db::Point ())); + text.transform (trans); + if (into.layout ()) { + into.insert (db::TextRef (text, into.layout ()->shape_repository ())); + } else { + into.insert (text); + } + + is_text = true; + + } + } + + } + + if (! is_text) { + into.insert (*i, trans, pm); + } + + } + + } + + } + + private: + std::pair m_text_annot_name_id; + const db::Layout *mp_layout; + }; +} + +void +DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) +{ + db::LayoutLocker locker (into_layout); + + const db::Layout &source_layout = deep_layer.layout (); + if (source_layout.begin_top_down () == source_layout.end_top_cells ()) { + // empty source - nothing to do. + return; + } + + // prepare the transformation + db::ICplxTrans trans (source_layout.dbu () / into_layout->dbu ()); + + // prepare a layer map + std::map lm; + lm.insert (std::make_pair (deep_layer.layer (), into_layer)); + + // prepare a cell mapping + const db::CellMapping &cm = cell_mapping_to_original (deep_layer.layout_index (), into_layout, into_cell); + + // prepare a vector with the source cells + std::vector source_cells; + source_cells.push_back (*source_layout.begin_top_down()); + + // prepare a transformer to convert text-annotated markers back to texts (without transformation however) + DeepShapeStoreToShapeTransformer dsst (*this, source_layout); + + // actually copy the shapes + db::copy_shapes (*into_layout, source_layout, trans, source_cells, cm.table (), lm, &dsst); +} + +void +DeepShapeStore::insert_as_polygons (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) +{ + // prepare a temporary layer with the polygons + DeepLayer tmp = deep_layer.derived (); + + db::Layout &layout = const_cast (deep_layer.layout ()); + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + db::Shapes &out = c->shapes (tmp.layer ()); + for (db::Shapes::shape_iterator s = c->shapes (deep_layer.layer ()); ! s.at_end (); ++s) { + + if (s->is_edge_pair ()) { + + out.insert (s->edge_pair ().normalized ().to_simple_polygon (enl)); + + } else if (s->is_path () || s->is_polygon () || s->is_box ()) { + + db::Polygon poly; + s->polygon (poly); + out.insert (poly); + + } + + } + + } + + // and insert this one + insert (tmp, into_layout, into_cell, into_layer); +} + +} + diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h new file mode 100644 index 000000000..2246e8a18 --- /dev/null +++ b/src/db/db/dbDeepShapeStore.h @@ -0,0 +1,679 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbDeepShapeStore +#define HDR_dbDeepShapeStore + +#include "dbCommon.h" + +#include "tlObject.h" +#include "tlStableVector.h" +#include "tlThreads.h" +#include "dbLayout.h" +#include "dbRecursiveShapeIterator.h" +#include "dbHierarchyBuilder.h" +#include "gsiObject.h" + +#include +#include + +namespace db { + +class DeepShapeStore; +class Region; + +/** + * @brief Represents a shape collection from the deep shape store + * + * This is a lightweight class pointing into the deep shape store. + * DeepLayer objects are issued by the DeepShapeStore class. + */ +class DB_PUBLIC DeepLayer +{ +public: + /** + * @brief Default constructor + */ + DeepLayer (); + + /** + * @brief Destructor + */ + ~DeepLayer (); + + /** + * @brief The constructor from the detailed information + * Use this constructor if you know what you're doing. + */ + DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int layer); + + /** + * @brief Conversion operator from Region to DeepLayer + * This requires the Region to be a DeepRegion. Otherwise, this constructor will assert + */ + DeepLayer (const Region ®ion); + + /** + * @brief Copy constructor + */ + DeepLayer (const DeepLayer &other); + + /** + * @brief Assignment + */ + DeepLayer &operator= (const DeepLayer &other); + + /** + * @brief Less operator + */ + bool operator< (const DeepLayer &other) const; + + /** + * @brief Equality operator + */ + bool operator== (const DeepLayer &other) const; + + /** + * @brief Gets the layout object + * The return value is guaranteed to be non-null. + */ + Layout &layout(); + + /** + * @brief Gets the layout object (const version) + */ + const db::Layout &layout () const; + + /** + * @brief Gets the layout object + * The return value is guaranteed to be non-null. + */ + Cell &initial_cell(); + + /** + * @brief Gets the initial cell object (const version) + */ + const db::Cell &initial_cell () const; + + /** + * @brief Gets the layer + */ + unsigned int layer () const + { + return m_layer; + } + + /** + * @brief Gets the layout index + */ + unsigned int layout_index () const + { + return m_layout; + } + + /** + * @brief Inserts the layer into the given layout, starting from the given cell and into the given layer + */ + void insert_into (Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) const; + + /** + * @brief Inserts the edge pairs layer into the given layout, starting from the given cell and into the given layer + * The edge pairs are converted to polygons with the given enlargement. + */ + void insert_into_as_polygons (db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const; + + /** + * @brief Creates a derived new deep layer + * Derived layers use the same layout and context, but are initially + * empty layers for use as output layers on the same hierarchy. + */ + DeepLayer derived () const; + + /** + * @brief Creates a copy of this layer + */ + DeepLayer copy () const; + + /** + * @brief Adds shapes from another deep layer to this one + */ + void add_from (const DeepLayer &dl); + + /** + * @brief Separates cell variants (see DeepShapeStore::separate_variants) + */ + template + void separate_variants (VarCollector &collector); + + /** + * @brief Commits shapes for variants to the existing cell hierarchy + * + * The "to_propagate" collection is a set of shapes per cell and variant. The + * algorithm will put these shapes into the existing hierarchy putting the + * shapes into the proper parent cells to resolve variants. + * + * This map will be modified by the algorithm and should be discarded + * later. + */ + template + void commit_shapes (VarCollector &collector, std::map > &to_propagate); + + /** + * @brief Gets the shape store object + * This is a pure const version to prevent manipulation of the store. + * This method is intended to fetch configuration options from the store. + */ + const DeepShapeStore *store () const + { + check_dss (); + return mp_store.get (); + } + +private: + friend class DeepShapeStore; + + void check_dss () const; + + tl::weak_ptr mp_store; + unsigned int m_layout; + unsigned int m_layer; +}; + +struct DB_PUBLIC RecursiveShapeIteratorCompareForTargetHierarchy +{ + bool operator () (const std::pair &a, const std::pair &b) const + { + int cmp_iter = db::compare_iterators_with_respect_to_target_hierarchy (a.first, b.first); + if (cmp_iter != 0) { + return cmp_iter < 0; + } + return a.second < b.second; + } +}; + +/** + * @brief The "deep shape store" is a working model for the hierarchical ("deep") processor + * + * The deep shape store keeps temporary data for the deep shape processor. + * It mainly consists of layout objects holding the hierarchy trees and layers + * for the actual shapes. + * + * The deep shape store provides the basis for working with deep regions. On preparation, + * shapes are copied into the deep shape store. After fininishing, the shapes are copied + * back into the original layout. The deep shape store provides the methods and + * algorithms for doing the preparation and transfer. + */ +class DB_PUBLIC DeepShapeStore + : public tl::Object, public gsi::ObjectBase +{ +public: + /** + * @brief The default constructor + */ + DeepShapeStore (); + + /** + * @brief The constructor for a singular DSS + * + * This DSS will be initialized with one layout and the given database unit + * and top level cell name. + */ + DeepShapeStore (const std::string &topcell_name, double dbu); + + /** + * @brief The destructor + */ + ~DeepShapeStore (); + + /** + * @brief Returns true, if the DeepShapeStore is singular + * + * A "singular" shape store needs a single layout to keep the information. + * This is the case, if all Regions derived from it share the same origin + * and do not use clipping or region selection. Singular shape stores are + * required for netlist extraction for example. + * + * For a singular shape store, "layout()" will return the layout + * object and "initial_cell()" will return the initial cell of the + * only layout. + */ + bool is_singular () const; + + /** + * @brief Creates a new layer from a flat region (or the region is made flat) + * + * This method is intended for use with singular-created DSS objects (see + * singular constructor). + * + * After a flat layer has been created for a region, it can be retrieved + * from the region later with layer_for_flat (region). + * + * If for_netlist is true, texts will be skipped except on top level. The + * reasoning is that texts below top level may create name clashes if they + * are used for net names. + */ + DeepLayer create_from_flat (const db::Region ®ion, bool for_netlist, double max_area_ratio = 0.0, size_t max_vertex_count = 0, const db::ICplxTrans &trans = db::ICplxTrans ()); + + /** + * @brief Gets the layer for a given flat region. + * + * If a layer has been created for a flat region with create_from_flat, it can be retrieved with this method. + * The first return value is true in this case. + */ + std::pair layer_for_flat (const db::Region ®ion) const; + + /** + * @brief Same as layer_for_flat, but takes a region Id + */ + std::pair layer_for_flat (size_t region_id) const; + + /** + * @brief Creates a layout with the given iterator and transformation for the given index + * + * This method is intended for classes that need more control over the layouts per index + * (LayoutToNetlist). + */ + void make_layout (unsigned int layout_index, const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans = db::ICplxTrans ()); + + /** + * @brief Inserts a polygon layer into the deep shape store + * + * This method will create a new layer inside the deep shape store as a + * working copy of the original layer. Preparation involves re-shaping + * the polygons so their bounding box is a better approximation and the + * polygon complexity is reduced. For this, the polygons are split + * into parts satisfying the area ratio (bounding box vs. polygon area) + * and maximum vertex count constraints. + */ + DeepLayer create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio = 0.0, size_t max_vertex_count = 0, const ICplxTrans &trans = db::ICplxTrans ()); + + /** + * @brief Inserts an edge layer into the deep shape store + * + * This method will create a new layer inside the deep shape store as a + * working copy of the original layer. This method creates a layer + * for edges. + * + * If "as_edges" is true, polygons and boxes will be converted into edges. Otherwise + * only edge objects are taken from the shape iterator. Note that the shape iterator + * must be configured to deliver all shape types if "as_edges" is true. + */ + DeepLayer create_edge_layer (const db::RecursiveShapeIterator &si, bool as_edges, const ICplxTrans &trans = db::ICplxTrans ()); + + /** + * @brief Inserts an edge pair layer into the deep shape store + * + * This method will create a new layer inside the deep shape store as a + * working copy of the original layer. This method creates a layer + * for edge pairs. + */ + DeepLayer create_edge_pair_layer (const db::RecursiveShapeIterator &si, const ICplxTrans &trans = db::ICplxTrans ()); + + /** + * @brief Inserts a polygon layer into the deep shape store using a custom preparation pipeline + * + * This method will create a new layer inside the deep shapes store and + * feed it through the given pipeline. The pipeline may perform shape translations and + * finally will feed the target hierarchy. + */ + DeepLayer create_custom_layer (const db::RecursiveShapeIterator &si, HierarchyBuilderShapeReceiver *pipe, const ICplxTrans &trans = db::ICplxTrans ()); + + /** + * @brief Creates a deep layer as a copy from an existing one + */ + DeepLayer create_copy (const DeepLayer &source, HierarchyBuilderShapeReceiver *pipe); + + /** + * @brief Gets the empty working layer + * + * This method will deliver an empty layer for the given layout index. CAUTION: don't modify this layer as it may + * be reused. + */ + DeepLayer empty_layer (unsigned int layout_index) const; + + /** + * @brief Gets the empty working layer for the singular layout + */ + DeepLayer empty_layer () const; + + /** + * @brief Inserts the deep layer's shapes into some target layout + */ + void insert (const DeepLayer &layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer); + + /** + * @brief Inserts the deep layer's edge pairs into some target layout + * + * The edge pairs are converted to polygons with the given enlargement. + */ + void insert_as_polygons (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl); + + /** + * @brief Gets the cell mapping suitable to returning a layout from the deep shape store into the original layout hierarchy + * + * If necessary, this method will modify the original layout and add new cells. + * "layout_index" is the layout to return to it's original. "into_layout" is the original layout, "into_cell" + * the original cell. + * + * "excluded_cells" - if not 0 - will exclude the given cells (and flatten them). + */ + const db::CellMapping &cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set *excluded_cells = 0); + + /** + * @brief Create cell variants from the given variant collector + * + * To use this method, first create a variant collector (db::cell_variant_collector) with the required + * reducer and collect the variants. Then call this method on the desired layout index to create the variants. + */ + template + void separate_variants (unsigned int layout_index, VarCollector &coll) + { + tl_assert (is_valid_layout_index (layout_index)); + + std::map > var_map; + coll.separate_variants (layout (layout_index), initial_cell (layout_index), &var_map); + if (var_map.empty ()) { + // nothing to do. + return; + } + + issue_variants (layout_index, var_map); + } + + /** + * @brief Commits shapes for variants to the existing cell hierarchy + * + * To use this method, first create a variant collector (db::cell_variant_collector) with the required + * reducer and collect the variants. Then call this method on the desired layout index to commit the shapes for the + * respective variants. + */ + template + void commit_shapes (unsigned int layout_index, VarCollector &coll, unsigned int layer, std::map > &to_commit) + { + tl_assert (is_valid_layout_index (layout_index)); + + coll.commit_shapes (layout (layout_index), initial_cell (layout_index), layer, to_commit); + } + + /** + * @brief For testing + */ + static size_t instance_count (); + + /** + * @brief Gets the nth layout (const version) + */ + const db::Layout &const_layout (unsigned int n) const; + + /** + * @brief Gets the nth layout (non-const version) + * + * Don't try to mess too much with the layout object, you'll screw up the internals. + */ + db::Layout &layout (unsigned int n); + + /** + * @brief Gets the initial cell of the nth layout (const version) + */ + const db::Cell &const_initial_cell (unsigned int n) const; + + /** + * @brief Gets the initial cell of the nth layout (non-const version) + * + * Don't try to mess too much with the cell object, you'll screw up the internals. + */ + db::Cell &initial_cell (unsigned int n); + + /** + * @brief Gets the singular layout (const version) + * + * This method will throw an exception if the deep shape store is not singular. + */ + const db::Layout &const_layout () const + { + require_singular (); + return const_layout (0); + } + + /** + * @brief Gets the singular layout (non-const version) + * + * This method will throw an exception if the deep shape store is not singular. + * Don't try to mess too much with the layout object, you'll screw up the internals. + */ + db::Layout &layout () + { + require_singular (); + return layout (0); + } + + /** + * @brief Gets the initial cell of the singular layout (const version) + * + * This method will throw an exception if the deep shape store is not singular. + */ + const db::Cell &const_initial_cell () const + { + require_singular (); + return const_initial_cell (0); + } + + /** + * @brief Gets the initial cell of the singular layout (non-const version) + * + * This method will throw an exception if the deep shape store is not singular. + * Don't try to mess too much with the cell object, you'll screw up the internals. + */ + db::Cell &initial_cell () + { + require_singular (); + return initial_cell (0); + } + + /** + * @brief Gets the number of layouts + */ + unsigned int layouts () const + { + return (unsigned int) m_layouts.size (); + } + + /** + * @brief Gets a value indicating whether the given index is a valid layout index + */ + bool is_valid_layout_index (unsigned int n) const; + + /** + * @brief The deep shape store also keeps the number of threads to allocate for the hierarchical processor + * + * This is a kind of hack, but it's convenient. + */ + void set_threads (int n); + + /** + * @brief Gets the number of threads + */ + int threads () const + { + return m_threads; + } + + /** + * @brief Sets the maximum vertex count default value + * + * This parameter is used to simplify complex polygons. It is used by + * create_polygon_layer with the default parameters. It's also used by + * boolean operations when they deliver their output. + */ + void set_max_vertex_count (size_t n); + + /** + * @brief Gets the maximum vertex count + */ + size_t max_vertex_count () const + { + return m_max_vertex_count; + } + + /** + * @brief Sets the max. area ratio for bounding box vs. polygon area + * + * This parameter is used to simplify complex polygons. It is used by + * create_polygon_layer with the default parameters. It's also used by + * boolean operations when they deliver their output. + */ + void set_max_area_ratio (double ar); + + /** + * @brief Gets the max. area ratio + */ + double max_area_ratio () const + { + return m_max_area_ratio; + } + + /** + * @brief Sets the text property name + * + * If set to a non-null variant, text strings are attached to the generated boxes + * as properties with this particular name. This option has an effect only if the + * text_enlargement property is not negative. + * By default, the name is empty. + */ + void set_text_property_name (const tl::Variant &pn); + + /** + * @brief Gets the text property name + */ + const tl::Variant &text_property_name () const + { + return m_text_property_name; + } + + /** + * @brief Sets the text enlargement value + * + * If set to a non-negative value, text objects are converted to boxes with the + * given enlargement (width = 2 * enlargement). The box centers are identical + * to the original location of the text. + * If this value is negative (the default), texts are ignored. + */ + void set_text_enlargement (int enl); + + /** + * @brief Gets the text enlargement value + */ + int text_enlargement () const + { + return m_text_enlargement; + } + +private: + friend class DeepLayer; + + struct LayoutHolder; + + void invalidate_hier (); + void add_ref (unsigned int layout, unsigned int layer); + void remove_ref (unsigned int layout, unsigned int layer); + + unsigned int layout_for_iter (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans); + + void require_singular () const; + + void issue_variants (unsigned int layout, const std::map > &var_map); + + typedef std::map, unsigned int, RecursiveShapeIteratorCompareForTargetHierarchy> layout_map_type; + + // no copying + DeepShapeStore (const DeepShapeStore &); + DeepShapeStore &operator= (const DeepShapeStore &); + + std::vector m_layouts; + std::map > m_layers_for_flat; + std::map, size_t> m_flat_region_id; + layout_map_type m_layout_map; + int m_threads; + double m_max_area_ratio; + size_t m_max_vertex_count; + tl::Variant m_text_property_name; + int m_text_enlargement; + tl::Mutex m_lock; + + struct DeliveryMappingCacheKey + { + // NOTE: we shouldn't keep pointers here as the layouts may get deleted and recreated with the same address. + // But as we don't access these objects that's fairly safe. + DeliveryMappingCacheKey (unsigned int _from_index, tl::id_type _into_layout, db::cell_index_type _into_cell) + : from_index (_from_index), into_layout (_into_layout), into_cell (_into_cell) + { + // .. nothing yet .. + } + + bool operator< (const DeliveryMappingCacheKey &other) const + { + if (from_index != other.from_index) { + return from_index < other.from_index; + } + if (into_layout != other.into_layout) { + return into_layout < other.into_layout; + } + return into_cell m_delivery_mapping_cache; +}; + +template +void DeepLayer::separate_variants (VarCollector &collector) +{ + check_dss (); + mp_store->separate_variants (m_layout, collector); +} + +template +void DeepLayer::commit_shapes (VarCollector &collector, std::map > &to_commit) +{ + check_dss (); + mp_store->commit_shapes (m_layout, collector, layer (), to_commit); +} + +} + +namespace tl +{ + + // disable copying of the deep shape store object + template <> struct type_traits : public type_traits { + typedef tl::false_tag has_copy_constructor; + }; + +} + +#endif + diff --git a/src/db/db/dbDevice.cc b/src/db/db/dbDevice.cc new file mode 100644 index 000000000..e803cec08 --- /dev/null +++ b/src/db/db/dbDevice.cc @@ -0,0 +1,190 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbDevice.h" +#include "dbCircuit.h" +#include "dbDeviceClass.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// Device class implementation + +Device::Device () + : mp_device_class (0), mp_device_abstract (0), m_id (0), mp_circuit (0) +{ + // .. nothing yet .. +} + +Device::~Device () +{ + for (std::vector::const_iterator t = m_terminal_refs.begin (); t != m_terminal_refs.end (); ++t) { + if (*t != Net::terminal_iterator () && (*t)->net ()) { + (*t)->net ()->erase_terminal (*t); + } + } +} + +Device::Device (DeviceClass *device_class, const std::string &name) + : mp_device_class (device_class), mp_device_abstract (0), m_name (name), m_id (0), mp_circuit (0) +{ + // .. nothing yet .. +} + +Device::Device (DeviceClass *device_class, DeviceAbstract *device_abstract, const std::string &name) + : mp_device_class (device_class), mp_device_abstract (device_abstract), m_name (name), m_id (0), mp_circuit (0) +{ + // .. nothing yet .. +} + +Device::Device (const Device &other) + : tl::Object (other), mp_device_class (0), mp_device_abstract (0), m_id (0), mp_circuit (0) +{ + operator= (other); +} + +Device &Device::operator= (const Device &other) +{ + if (this != &other) { + m_name = other.m_name; + m_position = other.m_position; + m_parameters = other.m_parameters; + mp_device_class = other.mp_device_class; + mp_device_abstract = other.mp_device_abstract; + } + return *this; +} + +std::string Device::expanded_name () const +{ + if (name ().empty ()) { + return "$" + tl::to_string (id ()); + } else { + return name (); + } +} + +void Device::set_circuit (Circuit *circuit) +{ + mp_circuit = circuit; +} + +void Device::set_name (const std::string &n) +{ + m_name = n; + if (mp_circuit) { + mp_circuit->m_device_by_name.invalidate (); + } +} + +void Device::set_position (const db::DPoint &pt) +{ + m_position = pt; +} + +void Device::set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter) +{ + if (m_terminal_refs.size () < terminal_id + 1) { + m_terminal_refs.resize (terminal_id + 1, Net::terminal_iterator ()); + } + m_terminal_refs [terminal_id] = iter; +} + +const Net *Device::net_for_terminal (size_t terminal_id) const +{ + if (terminal_id < m_terminal_refs.size ()) { + Net::terminal_iterator p = m_terminal_refs [terminal_id]; + if (p != Net::terminal_iterator ()) { + return p->net (); + } + } + return 0; +} + +void Device::connect_terminal (size_t terminal_id, Net *net) +{ + if (net_for_terminal (terminal_id) == net) { + return; + } + + if (terminal_id < m_terminal_refs.size ()) { + Net::terminal_iterator p = m_terminal_refs [terminal_id]; + if (p != Net::terminal_iterator () && p->net ()) { + p->net ()->erase_terminal (p); + } + m_terminal_refs [terminal_id] = Net::terminal_iterator (); + } + + if (net) { + net->add_terminal (NetTerminalRef (this, terminal_id)); + } +} + +double Device::parameter_value (size_t param_id) const +{ + if (m_parameters.size () > param_id) { + return m_parameters [param_id]; + } else if (mp_device_class) { + const db::DeviceParameterDefinition *pd = mp_device_class->parameter_definition (param_id); + if (pd) { + return pd->default_value (); + } + } + return 0.0; +} + +void Device::set_parameter_value (size_t param_id, double v) +{ + if (m_parameters.size () <= param_id) { + + // resize the parameter vector with default values + size_t from_size = m_parameters.size (); + m_parameters.resize (param_id + 1, 0.0); + + if (mp_device_class) { + for (size_t n = from_size; n < param_id; ++n) { + const db::DeviceParameterDefinition *pd = mp_device_class->parameter_definition (n); + if (pd) { + m_parameters [n] = pd->default_value (); + } + } + } + + } + + m_parameters [param_id] = v; +} + +double Device::parameter_value (const std::string &name) const +{ + return device_class () ? parameter_value (device_class ()->parameter_id_for_name (name)) : 0.0; +} + +void Device::set_parameter_value (const std::string &name, double v) +{ + if (device_class ()) { + set_parameter_value (device_class ()->parameter_id_for_name (name), v); + } +} + +} diff --git a/src/db/db/dbDevice.h b/src/db/db/dbDevice.h new file mode 100644 index 000000000..5b12d25d5 --- /dev/null +++ b/src/db/db/dbDevice.h @@ -0,0 +1,265 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbDevice +#define _HDR_dbDevice + +#include "dbCommon.h" +#include "dbNet.h" +#include "dbPoint.h" + +#include "tlObject.h" + +#include + +namespace db +{ + +class Circuit; +class DeviceClass; +class DeviceAbstract; + +/** + * @brief An actual device + * + * This class represents the incarnation of a specific device. + * The device has a class which specifies a type. This class + * is intended for subclassing. + * A specific device subclass is supposed to correspond to + * a specific device class. + */ +class DB_PUBLIC Device + : public tl::Object +{ +public: + typedef std::vector > global_connections; + typedef global_connections::const_iterator global_connections_iterator; + + /** + * @brief Default constructor + */ + Device (); + + /** + * @brief The constructor + */ + Device (DeviceClass *device_class, const std::string &name = std::string ()); + + /** + * @brief The constructor + */ + Device (DeviceClass *device_class, DeviceAbstract *device_abstract, const std::string &name = std::string ()); + + /** + * @brief Copy constructor + */ + Device (const Device &other); + + /** + * @brief Assignment + */ + Device &operator= (const Device &other); + + /** + * @brief Destructor + */ + ~Device (); + + /** + * @brief Gets the device class + */ + const DeviceClass *device_class () const + { + return mp_device_class; + } + + /** + * @brief Sets the device class + */ + void set_device_class (DeviceClass *dc) + { + mp_device_class = dc; + } + + /** + * @brief Gets the device abstract + */ + const DeviceAbstract *device_abstract () const + { + return mp_device_abstract; + } + + /** + * @brief Sets the device abstract + */ + void set_device_abstract (DeviceAbstract *dm) + { + mp_device_abstract = dm; + } + + /** + * @brief Gets the device ID + * The ID is a unique integer which identifies the device. + * It can be used to retrieve the device from the circuit using Circuit::device_by_id. + * When assigned, the device ID is not 0. + */ + size_t id () const + { + return m_id; + } + + /** + * @brief Gets the circuit the device lives in (const version) + * This pointer is 0 if the device isn't added to a circuit + */ + const Circuit *circuit () const + { + return mp_circuit; + } + + /** + * @brief Gets the circuit the device lives in (non-const version) + * This pointer is 0 if the device isn't added to a circuit + */ + Circuit *circuit () + { + return mp_circuit; + } + + /** + * @brief Sets the name + */ + void set_name (const std::string &n); + + /** + * @brief Gets the name + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Gets a name which always non-empty + * This method will pick a name like "$" if the explicit name is empty. + */ + std::string expanded_name () const; + + /** + * @brief Sets the device position + * The device position should be the center of the recognition shape or something similar. + * Giving the device a position allows combining multiple devices with the same + * relative geometry into a single cell. + * The position has to be given in micrometer units. + */ + void set_position (const db::DPoint &pos); + + /** + * @brief Gets the device position + */ + const db::DPoint &position () const + { + return m_position; + } + + /** + * @brief Gets the net attached to a specific terminal + * Returns 0 if no net is attached. + */ + const Net *net_for_terminal (size_t terminal_id) const; + + /** + * @brief Gets the net attached to a specific terminal (non-const version) + * Returns 0 if no net is attached. + */ + Net *net_for_terminal (size_t terminal_id) + { + return const_cast (((const Device *) this)->net_for_terminal (terminal_id)); + } + + /** + * @brief Connects the given terminal to the given net + * If the net is 0 the terminal is disconnected. + * If non-null, a NetTerminalRef object will be inserted into the + * net and connected with the given terminal. + * If the terminal is connected to a global net, it will be + * disconnected from there. + */ + void connect_terminal (size_t terminal_id, Net *net); + + /** + * @brief Gets the value for the parameter with the given ID + */ + double parameter_value (size_t param_id) const; + + /** + * @brief Sets the value for the parameter with the given ID + */ + void set_parameter_value (size_t param_id, double v); + + /** + * @brief Gets the value for the parameter with the given name + * If the name is not valid, an exception is thrown. + */ + double parameter_value (const std::string &name) const; + + /** + * @brief Sets the value for the parameter with the given name + * If the name is not valid, an exception is thrown. + */ + void set_parameter_value (const std::string &name, double v); + +private: + friend class Circuit; + friend class Net; + + DeviceClass *mp_device_class; + DeviceAbstract *mp_device_abstract; + std::string m_name; + db::DPoint m_position; + std::vector m_terminal_refs; + std::vector m_parameters; + size_t m_id; + Circuit *mp_circuit; + + /** + * @brief Sets the terminal reference for a specific terminal + */ + void set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter); + + /** + * @brief Sets the device ID + */ + void set_id (size_t id) + { + m_id = id; + } + + /** + * @brief Sets the circuit + */ + void set_circuit (Circuit *circuit); +}; + +} + +#endif diff --git a/src/db/db/dbDeviceAbstract.cc b/src/db/db/dbDeviceAbstract.cc new file mode 100644 index 000000000..ea8d296cd --- /dev/null +++ b/src/db/db/dbDeviceAbstract.cc @@ -0,0 +1,101 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbDeviceAbstract.h" +#include "dbCircuit.h" +#include "dbNetlist.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// DeviceAbstract class implementation + +DeviceAbstract::DeviceAbstract () + : m_name (), mp_device_class (0), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) +{ + // .. nothing yet .. +} + +DeviceAbstract::~DeviceAbstract () +{ + // .. nothing yet .. +} + +DeviceAbstract::DeviceAbstract (db::DeviceClass *device_class, const std::string &name) + : m_name (name), mp_device_class (device_class), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) +{ + // .. nothing yet .. +} + +DeviceAbstract::DeviceAbstract (const DeviceAbstract &other) + : tl::Object (other), mp_device_class (0), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) +{ + operator= (other); +} + +DeviceAbstract &DeviceAbstract::operator= (const DeviceAbstract &other) +{ + if (this != &other) { + m_name = other.m_name; + mp_device_class = other.mp_device_class; + m_cell_index = other.m_cell_index; + m_terminal_cluster_ids = other.m_terminal_cluster_ids; + } + return *this; +} + +void DeviceAbstract::set_netlist (Netlist *netlist) +{ + mp_netlist = netlist; +} + +void DeviceAbstract::set_name (const std::string &n) +{ + m_name = n; + if (mp_netlist) { + mp_netlist->m_device_abstract_by_name.invalidate (); + } +} + +void DeviceAbstract::set_cell_index (db::cell_index_type ci) +{ + m_cell_index = ci; + if (mp_netlist) { + mp_netlist->m_device_abstract_by_cell_index.invalidate (); + } +} + +size_t DeviceAbstract::cluster_id_for_terminal (size_t terminal_id) const +{ + return terminal_id < m_terminal_cluster_ids.size () ? m_terminal_cluster_ids [terminal_id] : 0; +} + +void DeviceAbstract::set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id) +{ + if (m_terminal_cluster_ids.size () <= terminal_id) { + m_terminal_cluster_ids.resize (terminal_id + 1, 0); + } + m_terminal_cluster_ids [terminal_id] = cluster_id; +} + +} diff --git a/src/db/db/dbDeviceAbstract.h b/src/db/db/dbDeviceAbstract.h new file mode 100644 index 000000000..54ffba93c --- /dev/null +++ b/src/db/db/dbDeviceAbstract.h @@ -0,0 +1,164 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbDeviceAbstract +#define _HDR_dbDeviceAbstract + +#include "dbCommon.h" +#include "dbNet.h" +#include "dbPoint.h" + +#include "tlObject.h" + +#include + +namespace db +{ + +class Netlist; + +/** + * @brief A device abstract + * + * A device abstract represents the geometrical properties of a device. It basically links + * to a cell and clusters for indicating the terminal geometry of the device. + */ +class DB_PUBLIC DeviceAbstract + : public tl::Object +{ +public: + /** + * @brief Default constructor + */ + DeviceAbstract (); + + /** + * @brief The constructor + */ + DeviceAbstract (db::DeviceClass *device_class, const std::string &name = std::string ()); + + /** + * @brief Copy constructor + */ + DeviceAbstract (const DeviceAbstract &other); + + /** + * @brief Assignment + */ + DeviceAbstract &operator= (const DeviceAbstract &other); + + /** + * @brief Destructor + */ + ~DeviceAbstract (); + + /** + * @brief Gets the device class + */ + const DeviceClass *device_class () const + { + return mp_device_class; + } + + /** + * @brief Sets the device class + */ + void set_device_class (DeviceClass *dc) + { + mp_device_class = dc; + } + + /** + * @brief Gets the netlist the device lives in (const version) + * This pointer is 0 if the device abstract isn't added to a netlist + */ + const Netlist *netlist () const + { + return mp_netlist; + } + + /** + * @brief Gets the netlist the device lives in (non-const version) + * This pointer is 0 if the device abstract isn't added to a netlist + */ + Netlist *netlist () + { + return mp_netlist; + } + + /** + * @brief Sets the name + */ + void set_name (const std::string &n); + + /** + * @brief Gets the name + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the device cell index + * In the layout, a device is represented by a cell. This attribute gives the index of this + * cell. + */ + void set_cell_index (db::cell_index_type ci); + + /** + * @brief Gets the device cell index + */ + db::cell_index_type cell_index () const + { + return m_cell_index; + } + + /** + * @brief Gets the cluster ID for a given terminal + * This attribute connects the device terminal with a terminal cluster + */ + size_t cluster_id_for_terminal (size_t terminal_id) const; + + /** + * @brief Sets the cluster ID for a given terminal + */ + void set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id); + +private: + friend class Netlist; + + std::string m_name; + db::DeviceClass *mp_device_class; + db::cell_index_type m_cell_index; + std::vector m_terminal_cluster_ids; + Netlist *mp_netlist; + + /** + * @brief Sets the netlist + */ + void set_netlist (Netlist *netlist); +}; + +} + +#endif diff --git a/src/db/db/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc new file mode 100644 index 000000000..e21ff80ea --- /dev/null +++ b/src/db/db/dbDeviceClass.cc @@ -0,0 +1,139 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbDeviceClass.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// DeviceClass class implementation + +DeviceClass::DeviceClass () + : mp_netlist (0) +{ + // .. nothing yet .. +} + +DeviceClass::DeviceClass (const DeviceClass &other) + : gsi::ObjectBase (other), tl::Object (other), tl::UniqueId (other), mp_netlist (0) +{ + operator= (other); +} + +DeviceClass &DeviceClass::operator= (const DeviceClass &other) +{ + if (this != &other) { + m_terminal_definitions = other.m_terminal_definitions; + m_name = other.m_name; + m_description = other.m_description; + } + return *this; +} + +const DeviceTerminalDefinition &DeviceClass::add_terminal_definition (const DeviceTerminalDefinition &pd) +{ + m_terminal_definitions.push_back (pd); + m_terminal_definitions.back ().set_id (m_terminal_definitions.size () - 1); + return m_terminal_definitions.back (); +} + +void DeviceClass::clear_terminal_definitions () +{ + m_terminal_definitions.clear (); +} + +const DeviceTerminalDefinition *DeviceClass::terminal_definition (size_t id) const +{ + if (id < m_terminal_definitions.size ()) { + return & m_terminal_definitions [id]; + } else { + return 0; + } +} + +const DeviceParameterDefinition &DeviceClass::add_parameter_definition (const DeviceParameterDefinition &pd) +{ + m_parameter_definitions.push_back (pd); + m_parameter_definitions.back ().set_id (m_parameter_definitions.size () - 1); + return m_parameter_definitions.back (); +} + +void DeviceClass::clear_parameter_definitions () +{ + m_parameter_definitions.clear (); +} + +const DeviceParameterDefinition *DeviceClass::parameter_definition (size_t id) const +{ + if (id < m_parameter_definitions.size ()) { + return & m_parameter_definitions [id]; + } else { + return 0; + } +} + +bool DeviceClass::has_parameter_with_name (const std::string &name) const +{ + const std::vector &pd = parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (i->name () == name) { + return true; + } + } + return false; +} + +size_t DeviceClass::parameter_id_for_name (const std::string &name) const +{ + const std::vector &pd = parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (i->name () == name) { + return i->id (); + } + } + throw tl::Exception (tl::to_string (tr ("Invalid parameter name")) + ": '" + name + "'"); +} + +bool DeviceClass::has_terminal_with_name (const std::string &name) const +{ + const std::vector &td = terminal_definitions (); + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + if (i->name () == name) { + return true; + } + } + return false; +} + +size_t DeviceClass::terminal_id_for_name (const std::string &name) const +{ + const std::vector &td = terminal_definitions (); + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + if (i->name () == name) { + return i->id (); + } + } + throw tl::Exception (tl::to_string (tr ("Invalid terminal name")) + ": '" + name + "'"); +} + +} diff --git a/src/db/db/dbDeviceClass.h b/src/db/db/dbDeviceClass.h new file mode 100644 index 000000000..8c4b34aca --- /dev/null +++ b/src/db/db/dbDeviceClass.h @@ -0,0 +1,422 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbDeviceClass +#define _HDR_dbDeviceClass + +#include "dbCommon.h" + +#include "gsiObject.h" +#include "tlObject.h" +#include "tlUniqueId.h" + +#include +#include + +namespace db +{ + +class Netlist; +class Device; + +/** + * @brief A device terminal definition + */ +class DB_PUBLIC DeviceTerminalDefinition +{ +public: + /** + * @brief Creates an empty device terminal definition + */ + DeviceTerminalDefinition () + : m_name (), m_description (), m_id (0) + { + // .. nothing yet .. + } + + /** + * @brief Creates a device terminal definition with the given name and description + */ + DeviceTerminalDefinition (const std::string &name, const std::string &description) + : m_name (name), m_description (description), m_id (0) + { + // .. nothing yet .. + } + + /** + * @brief Gets the terminal name + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the terminal name + */ + void set_name (const std::string &n) + { + m_name = n; + } + + /** + * @brief Gets the terminal description + */ + const std::string &description () const + { + return m_description; + } + + /** + * @brief Sets the terminal description + */ + void set_description (const std::string &d) + { + m_description = d; + } + + /** + * @brief Gets the terminal ID + */ + size_t id () const + { + return m_id; + } + +private: + friend class DeviceClass; + + std::string m_name, m_description; + size_t m_id; + + void set_id (size_t id) + { + m_id = id; + } +}; + +/** + * @brief A device parameter definition + */ +class DB_PUBLIC DeviceParameterDefinition +{ +public: + /** + * @brief Creates an empty device parameter definition + */ + DeviceParameterDefinition () + : m_name (), m_description (), m_default_value (0.0), m_id (0) + { + // .. nothing yet .. + } + + /** + * @brief Creates a device parameter definition with the given name and description + */ + DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0) + : m_name (name), m_description (description), m_default_value (default_value), m_id (0) + { + // .. nothing yet .. + } + + /** + * @brief Gets the parameter name + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the parameter name + */ + void set_name (const std::string &n) + { + m_name = n; + } + + /** + * @brief Gets the parameter description + */ + const std::string &description () const + { + return m_description; + } + + /** + * @brief Sets the parameter description + */ + void set_description (const std::string &d) + { + m_description = d; + } + + /** + * @brief Gets the parameter default value + */ + double default_value () const + { + return m_default_value; + } + + /** + * @brief Sets the parameter description + */ + void set_default_value (double d) + { + m_default_value = d; + } + + /** + * @brief Gets the parameter ID + */ + size_t id () const + { + return m_id; + } + +private: + friend class DeviceClass; + + std::string m_name, m_description; + double m_default_value; + size_t m_id; + + void set_id (size_t id) + { + m_id = id; + } +}; + +/** + * @brief A device class + * + * A device class describes a type of device. + */ +class DB_PUBLIC DeviceClass + : public gsi::ObjectBase, public tl::Object, public tl::UniqueId +{ +public: + typedef size_t terminal_id_type; + + /** + * @brief Constructor + * + * Creates an empty circuit. + */ + DeviceClass (); + + /** + * @brief Copy constructor + * NOTE: do not use this copy constructor as the device class + * is intended to subclassing. + */ + DeviceClass (const DeviceClass &other); + + /** + * @brief Assignment + * NOTE: do not use this copy constructor as the device class + * is intended to subclassing. + */ + DeviceClass &operator= (const DeviceClass &other); + + /** + * @brief Gets the netlist the device class lives in + */ + db::Netlist *netlist () + { + return mp_netlist; + } + + /** + * @brief Gets the netlist the device class lives in (const version) + */ + const db::Netlist *netlist () const + { + return mp_netlist; + } + + /** + * @brief Gets the name of the device class + * + * The name is a formal name which identifies the class. + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Sets the device name + */ + void set_name (const std::string &n) + { + m_name = n; + } + + /** + * @brief Gets the description text for the device class + * + * The description text is a human-readable text that + * identifies the device class. + */ + const std::string &description () const + { + return m_description; + } + + /** + * @brief Sets the description text + */ + void set_description (const std::string &d) + { + m_description = d; + } + + /** + * @brief Gets the terminal definitions + * + * The terminal definitions indicate what terminals the device offers. + * The number of terminals is constant per class. The index of the terminal + * is used as an ID of the terminal, hence the order must be static. + */ + const std::vector &terminal_definitions () const + { + return m_terminal_definitions; + } + + /** + * @brief Adds a terminal definition + */ + const DeviceTerminalDefinition &add_terminal_definition (const DeviceTerminalDefinition &pd); + + /** + * @brief Clears the terminal definition + */ + void clear_terminal_definitions (); + + /** + * @brief Gets the terminal definition from the ID + */ + const DeviceTerminalDefinition *terminal_definition (size_t id) const; + + /** + * @brief Gets the parameter definitions + */ + const std::vector ¶meter_definitions () const + { + return m_parameter_definitions; + } + + /** + * @brief Adds a parameter definition + */ + const DeviceParameterDefinition &add_parameter_definition (const DeviceParameterDefinition &pd); + + /** + * @brief Clears the parameter definition + */ + void clear_parameter_definitions (); + + /** + * @brief Gets the parameter definition from the ID + */ + const DeviceParameterDefinition *parameter_definition (size_t id) const; + + /** + * @brief Returns true, if the device has a parameter with the given name + */ + bool has_parameter_with_name (const std::string &name) const; + + /** + * @brief Returns the parameter ID for the parameter with the given name + * If the name is invalid, an exception is thrown. + */ + size_t parameter_id_for_name (const std::string &name) const; + + /** + * @brief Returns true, if the device has a terminal with the given name + */ + bool has_terminal_with_name (const std::string &name) const; + + /** + * @brief Returns the parameter ID for the terminal with the given name + * If the name is invalid, an exception is thrown. + */ + size_t terminal_id_for_name (const std::string &name) const; + + /** + * @brief Clears the circuit + */ + virtual DeviceClass *clone () const + { + return new DeviceClass (*this); + } + + /** + * @brief Combines two devices + * + * This method shall test, whether the two devices can be combined. Both devices + * are guaranteed to share the same device class (this). + * If they cannot be combined, this method shall do nothing and return false. + * If they can be combined, this method shall reconnect the nets of the first + * device and entirely disconnect the nets of the second device. + * The second device will be deleted afterwards. + */ + virtual bool combine_devices (db::Device * /*a*/, db::Device * /*b*/) const + { + return false; + } + + /** + * @brief Returns true if the device class supports device combination in parallel mode + */ + virtual bool supports_parallel_combination () const + { + return false; + } + + /** + * @brief Returns true if the device class supports device combination in serial mode + */ + virtual bool supports_serial_combination () const + { + return false; + } + +private: + friend class Netlist; + + std::string m_name, m_description; + std::vector m_terminal_definitions; + std::vector m_parameter_definitions; + db::Netlist *mp_netlist; + + void set_netlist (db::Netlist *nl) + { + mp_netlist = nl; + } +}; + +} + +#endif diff --git a/src/db/db/dbEdge.h b/src/db/db/dbEdge.h index e3959c620..923125e52 100644 --- a/src/db/db/dbEdge.h +++ b/src/db/db/dbEdge.h @@ -336,7 +336,14 @@ public: template edge &transform (const Tr &t) { - *this = edge (t * m_p1, t * m_p2); + if (t.is_mirror ()) { + // NOTE: in case of mirroring transformations we swap p1 and p2. The reasoning is that + // this way we maintain the orientation semantics: "right" of the edge is "inside" of + // an area. + *this = edge (t * m_p2, t * m_p1); + } else { + *this = edge (t * m_p1, t * m_p2); + } return *this; } @@ -353,7 +360,14 @@ public: template edge transformed (const Tr &t) const { - return edge (t * m_p1, t * m_p2); + if (t.is_mirror ()) { + // NOTE: in case of mirroring transformations we swap p1 and p2. The reasoning is that + // this way we maintain the orientation semantics: "right" of the edge is "inside" of + // an area. + return edge (t * m_p2, t * m_p1); + } else { + return edge (t * m_p1, t * m_p2); + } } /** @@ -914,7 +928,7 @@ public: } /** - * @brief Swap the points of the edge + * @brief Swaps the points of the edge */ edge &swap_points () { diff --git a/src/db/db/dbEdgeBoolean.cc b/src/db/db/dbEdgeBoolean.cc new file mode 100644 index 000000000..546257bb0 --- /dev/null +++ b/src/db/db/dbEdgeBoolean.cc @@ -0,0 +1,24 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbEdgeBoolean.h" + diff --git a/src/db/db/dbEdgeBoolean.h b/src/db/db/dbEdgeBoolean.h new file mode 100644 index 000000000..126960546 --- /dev/null +++ b/src/db/db/dbEdgeBoolean.h @@ -0,0 +1,241 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbEdgeBoolean +#define HDR_dbEdgeBoolean + +#include "dbEdge.h" +#include "dbEdgesDelegate.h" +#include "dbBoxScanner.h" + +#include "tlIntervalMap.h" + +namespace db +{ + +struct OrJoinOp +{ + void operator() (int &v, int n) + { + v += n; + } +}; + +struct AndJoinOp +{ + void operator() (int &v, int n) + { + if (n == 0) { + v = 0; + } + } +}; + +struct NotJoinOp +{ + void operator() (int &v, int n) + { + if (n != 0) { + v = 0; + } + } +}; + +struct XorJoinOp +{ + void operator() (int &v, int n) + { + if (n != 0) { + if (v == 0) { + v = (n > 0 ? 1 : -1); + } else { + v = 0; + } + } + } +}; + +template +struct EdgeBooleanCluster + : public db::cluster +{ + typedef db::Edge::coord_type coord_type; + + EdgeBooleanCluster (OutputContainer *output, EdgeBoolOp op) + : mp_output (output), m_op (op) + { + // .. nothing yet .. + } + + void finish () + { + // determine base edge (longest overall edge) + + // shortcut for single edge + if (begin () + 1 == end ()) { + if (begin ()->second == 0) { + if (m_op != EdgeAnd) { + mp_output->insert (*(begin ()->first)); + } + } else { + if (m_op != EdgeAnd && m_op != EdgeNot) { + mp_output->insert (*(begin ()->first)); + } + } + return; + } + + db::Edge r = *begin ()->first; + double l1 = 0.0, l2 = r.double_length (); + double n = 1.0 / l2; + db::Point p1 = r.p1 (), p2 = r.p2 (); + + for (iterator o = begin () + 1; o != end (); ++o) { + double ll1 = db::sprod (db::Vector (o->first->p1 () - r.p1 ()), r.d ()) * n; + double ll2 = db::sprod (db::Vector (o->first->p2 () - r.p1 ()), r.d ()) * n; + if (ll1 < l1) { + p1 = o->first->p1 (); + l1 = ll1; + } + if (ll2 < l1) { + p1 = o->first->p2 (); + l1 = ll2; + } + if (ll1 > l2) { + p2 = o->first->p1 (); + l2 = ll1; + } + if (ll2 > l2) { + p2 = o->first->p2 (); + l2 = ll2; + } + } + + db::Vector d = db::Vector (p2 - p1); + n = 1.0 / d.double_length (); + + OrJoinOp or_jop; + AndJoinOp and_jop; + NotJoinOp not_jop; + XorJoinOp xor_jop; + + tl::interval_map a, b; + a.add (0, db::coord_traits::rounded (d.double_length ()), 0, or_jop); + b.add (0, db::coord_traits::rounded (d.double_length ()), 0, or_jop); + + for (iterator o = begin (); o != end (); ++o) { + db::Coord l1 = db::coord_traits::rounded (db::sprod (db::Vector (o->first->p1 () - p1), d) * n); + db::Coord l2 = db::coord_traits::rounded (db::sprod (db::Vector (o->first->p2 () - p1), d) * n); + if (o->second == 0 || m_op == EdgeOr) { + if (l1 < l2) { + a.add (l1, l2, 1, or_jop); + } else if (l1 > l2) { + a.add (l2, l1, -1, or_jop); + } + } else { + if (l1 < l2) { + b.add (l1, l2, 1, or_jop); + } else { + b.add (l2, l1, -1, or_jop); + } + } + } + + tl::interval_map q; + for (tl::interval_map::const_iterator ia = a.begin (); ia != a.end (); ++ia) { + q.add (ia->first.first, ia->first.second, ia->second > 0 ? 1 : (ia->second < 0 ? -1 : 0), or_jop); + } + + if (b.begin () == b.end ()) { + + // optimize for empty b + if (m_op != EdgeAnd) { + for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { + if (ib->second > 0) { + mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.first * n)), p1 + db::Vector (d * (ib->first.second * n)))); + } else if (ib->second < 0) { + mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.second * n)), p1 + db::Vector (d * (ib->first.first * n)))); + } + } + } + + } else { + + if (m_op == EdgeAnd) { + for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { + q.add (ib->first.first, ib->first.second, ib->second, and_jop); + } + } else if (m_op == EdgeNot) { + for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { + q.add (ib->first.first, ib->first.second, ib->second, not_jop); + } + } else if (m_op == EdgeXor) { + for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { + q.add (ib->first.first, ib->first.second, ib->second, xor_jop); + } + } + + for (tl::interval_map::const_iterator iq = q.begin (); iq != q.end (); ++iq) { + if (iq->second > 0) { + mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.first * n)), p1 + db::Vector (d * (iq->first.second * n)))); + } else if (iq->second < 0) { + mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.second * n)), p1 + db::Vector (d * (iq->first.first * n)))); + } + } + + } + + } + +private: + OutputContainer *mp_output; + db::EdgeBoolOp m_op; +}; + +template +struct EdgeBooleanClusterCollector + : public db::cluster_collector > +{ + EdgeBooleanClusterCollector (OutputContainer *output, EdgeBoolOp op) + : db::cluster_collector > (EdgeBooleanCluster (output, op), op != EdgeAnd /*report single*/) + { + // .. nothing yet .. + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + // Select edges which are: + // 1.) not degenerate + // 2.) parallel with some tolerance of roughly 1 dbu + // 3.) connected + if (! o1->is_degenerate () && ! o2->is_degenerate () + && fabs ((double) db::vprod (*o1, *o2)) < db::coord_traits::prec_distance () * std::min (o1->double_length (), o2->double_length ()) + && (o1->p1 () == o2->p1 () || o1->p1 () == o2->p2 () || o1->p2 () == o2->p1 () || o1->p2 () == o2->p2 () || o1->coincident (*o2))) { + db::cluster_collector >::add (o1, p1, o2, p2); + } + } +}; + +} + +#endif + diff --git a/src/db/db/dbEdgePair.h b/src/db/db/dbEdgePair.h index 2af2405cc..6704abe16 100644 --- a/src/db/db/dbEdgePair.h +++ b/src/db/db/dbEdgePair.h @@ -53,7 +53,7 @@ public: typedef db::coord_traits coord_traits; typedef typename coord_traits::distance_type distance_type; typedef typename coord_traits::area_type area_type; - typedef db::object_tag< edge > tag; + typedef db::object_tag< edge_pair > tag; /** * @brief The default constructor. @@ -89,6 +89,24 @@ public: // .. nothing else .. } + /** + * @brief The (dummy) translation operator + */ + void translate (const edge_pair &d, db::generic_repository &, db::ArrayRepository &) + { + *this = d; + } + + /** + * @brief The (dummy) translation operator + */ + template + void translate (const edge_pair &d, const T &t, db::generic_repository &, db::ArrayRepository &) + { + *this = d; + transform (t); + } + /** * @brief A less operator to establish a sorting order. */ diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index d3c1d795b..63376b59e 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -24,6 +24,10 @@ #include "dbCommon.h" #include "dbEdgePairs.h" +#include "dbEmptyEdgePairs.h" +#include "dbFlatEdgePairs.h" +#include "dbDeepEdgePairs.h" +#include "dbOriginalLayerEdgePairs.h" #include "dbEdges.h" #include "dbRegion.h" @@ -34,164 +38,152 @@ namespace db { -void -EdgePairs::insert (const db::Edge &e1, const db::Edge &e2) +EdgePairs::EdgePairs () + : mp_delegate (new EmptyEdgePairs ()) { - m_edge_pairs.push_back (db::EdgePair (e1, e2)); - m_bbox_valid = false; + // .. nothing yet .. } -void -EdgePairs::insert (const edge_pair_type &ep) +EdgePairs::~EdgePairs () { - m_edge_pairs.push_back (ep); - m_bbox_valid = false; + delete mp_delegate; + mp_delegate = 0; } -bool -EdgePairs::operator== (const db::EdgePairs &other) const +EdgePairs::EdgePairs (EdgePairsDelegate *delegate) + : mp_delegate (delegate) { - if (empty () != other.empty ()) { - return false; - } - if (size () != other.size ()) { - return false; - } - db::EdgePairs::const_iterator o1 = begin (); - db::EdgePairs::const_iterator o2 = other.begin (); - while (o1 != end () && o2 != other.end ()) { - if (*o1 != *o2) { - return false; - } - ++o1; - ++o2; - } - return true; + // .. nothing yet .. } -bool -EdgePairs::operator< (const db::EdgePairs &other) const +EdgePairs::EdgePairs (const EdgePairs &other) + : gsi::ObjectBase (), mp_delegate (other.mp_delegate->clone ()) { - if (empty () != other.empty ()) { - return empty () < other.empty (); - } - if (size () != other.size ()) { - return (size () < other.size ()); - } - db::EdgePairs::const_iterator o1 = begin (); - db::EdgePairs::const_iterator o2 = other.begin (); - while (o1 != end () && o2 != other.end ()) { - if (*o1 != *o2) { - return *o1 < *o2; - } - ++o1; - ++o2; - } - return false; + // .. nothing yet .. } -db::EdgePairs & -EdgePairs::operator+= (const db::EdgePairs &other) +EdgePairs &EdgePairs::operator= (const EdgePairs &other) { - if (! other.empty ()) { - m_edge_pairs.insert (m_edge_pairs.end (), other.begin (), other.end ()); - m_bbox_valid = false; + if (this != &other) { + set_delegate (other.mp_delegate->clone ()); } return *this; } -std::string -EdgePairs::to_string (size_t nmax) const +EdgePairs::EdgePairs (const RecursiveShapeIterator &si) { - std::ostringstream os; - const_iterator ep; - for (ep = begin (); ep != end () && nmax != 0; ++ep, --nmax) { - if (ep != begin ()) { - os << ";"; + mp_delegate = new OriginalLayerEdgePairs (si); +} + +EdgePairs::EdgePairs (const RecursiveShapeIterator &si, const db::ICplxTrans &trans) +{ + mp_delegate = new OriginalLayerEdgePairs (si, trans); +} + +EdgePairs::EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss) +{ + mp_delegate = new DeepEdgePairs (si, dss); +} + +EdgePairs::EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans) +{ + mp_delegate = new DeepEdgePairs (si, dss, trans); +} + +template +void EdgePairs::insert (const Sh &shape) +{ + flat_edge_pairs ()->insert (shape); +} + +template DB_PUBLIC void EdgePairs::insert (const db::EdgePair &); + +void EdgePairs::insert (const db::Shape &shape) +{ + flat_edge_pairs ()->insert (shape); +} + +template +void EdgePairs::insert (const db::Shape &shape, const T &trans) +{ + flat_edge_pairs ()->insert (shape, trans); +} + +template DB_PUBLIC void EdgePairs::insert (const db::Shape &, const db::ICplxTrans &); +template DB_PUBLIC void EdgePairs::insert (const db::Shape &, const db::Trans &); +template DB_PUBLIC void EdgePairs::insert (const db::Shape &, const db::Disp &); + +void EdgePairs::clear () +{ + set_delegate (new EmptyEdgePairs ()); +} + +void EdgePairs::reserve (size_t n) +{ + flat_edge_pairs ()->reserve (n); +} + +template +EdgePairs &EdgePairs::transform (const T &trans) +{ + flat_edge_pairs ()->transform (trans); + return *this; +} + +// explicit instantiations +template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::ICplxTrans &); +template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::Trans &); +template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::Disp &); + +const db::RecursiveShapeIterator & +EdgePairs::iter () const +{ + static db::RecursiveShapeIterator def_iter; + const db::RecursiveShapeIterator *i = mp_delegate->iter (); + return *(i ? i : &def_iter); +} + +void EdgePairs::polygons (Region &output, db::Coord e) const +{ + output.set_delegate (mp_delegate->polygons (e)); +} + +void EdgePairs::edges (Edges &output) const +{ + output.set_delegate (mp_delegate->edges ()); +} + +void EdgePairs::first_edges (Edges &output) const +{ + output.set_delegate (mp_delegate->first_edges ()); +} + +void EdgePairs::second_edges (Edges &output) const +{ + output.set_delegate (mp_delegate->second_edges ()); +} + +void EdgePairs::set_delegate (EdgePairsDelegate *delegate) +{ + if (delegate != mp_delegate) { + delete mp_delegate; + mp_delegate = delegate; + } +} + +FlatEdgePairs *EdgePairs::flat_edge_pairs () +{ + FlatEdgePairs *edge_pairs = dynamic_cast (mp_delegate); + if (! edge_pairs) { + edge_pairs = new FlatEdgePairs (); + if (mp_delegate) { + edge_pairs->EdgePairsDelegate::operator= (*mp_delegate); + edge_pairs->insert_seq (begin ()); } - os << ep->to_string (); + set_delegate (edge_pairs); } - if (ep != end ()) { - os << "..."; - } - return os.str (); -} -void -EdgePairs::clear () -{ - m_edge_pairs.clear (); - m_bbox = db::Box (); - m_bbox_valid = true; -} - -void -EdgePairs::polygons (Region &output, db::Coord e) const -{ - for (const_iterator ep = begin (); ep != end (); ++ep) { - db::Polygon poly = ep->normalized ().to_polygon (e); - if (poly.vertices () >= 3) { - output.insert (poly); - } - } -} - -void -EdgePairs::edges (Edges &output) const -{ - for (const_iterator ep = begin (); ep != end (); ++ep) { - output.insert (ep->first ()); - output.insert (ep->second ()); - } -} - -void -EdgePairs::first_edges (Edges &output) const -{ - for (const_iterator ep = begin (); ep != end (); ++ep) { - output.insert (ep->first ()); - } -} - -void -EdgePairs::second_edges (Edges &output) const -{ - for (const_iterator ep = begin (); ep != end (); ++ep) { - output.insert (ep->second ()); - } -} - -void -EdgePairs::init () -{ - m_bbox_valid = false; - m_report_progress = false; -} - -void -EdgePairs::ensure_bbox_valid () const -{ - if (! m_bbox_valid) { - m_bbox = db::Box (); - for (const_iterator ep = begin (); ep != end (); ++ep) { - m_bbox += db::Box (ep->first ().p1 (), ep->first ().p2 ()); - m_bbox += db::Box (ep->second ().p1 (), ep->second ().p2 ()); - } - m_bbox_valid = true; - } -} - -void -EdgePairs::disable_progress () -{ - m_report_progress = false; -} - -void -EdgePairs::enable_progress (const std::string &progress_desc) -{ - m_report_progress = true; - m_progress_desc = progress_desc; + return edge_pairs; } } diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 3d285ceb3..e6393a0a8 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -24,14 +24,198 @@ #ifndef HDR_dbEdgePairs #define HDR_dbEdgePairs -#include "dbEdge.h" -#include "dbEdgePair.h" +#include "dbEdgePairsDelegate.h" +#include "dbShape.h" +#include "dbRecursiveShapeIterator.h" + +#include "gsiObject.h" + +#include namespace db { -class Region; +class EdgePairFilterBase; +class FlatEdgePairs; +class EmptyEdgePairs; class Edges; +class Region; +class DeepShapeStore; +class TransformationReducer; + +/** + * @brief An edge pair set iterator + * + * The iterator delivers the edge pairs of the edge pair set + */ +class DB_PUBLIC EdgePairsIterator +{ +public: + typedef EdgePairsIteratorDelegate::value_type value_type; + typedef const value_type &reference; + typedef const value_type *pointer; + typedef std::forward_iterator_tag iterator_category; + typedef void difference_type; + + /** + * @brief Default constructor + */ + EdgePairsIterator () + : mp_delegate (0) + { + // .. nothing yet .. + } + + /** + * @brief Constructor from a delegate + * The iterator will take ownership over the delegate + */ + EdgePairsIterator (EdgePairsIteratorDelegate *delegate) + : mp_delegate (delegate) + { + // .. nothing yet .. + } + + /** + * @brief Destructor + */ + ~EdgePairsIterator () + { + delete mp_delegate; + mp_delegate = 0; + } + + /** + * @brief Copy constructor and assignment + */ + EdgePairsIterator (const EdgePairsIterator &other) + : mp_delegate (0) + { + operator= (other); + } + + /** + * @brief Assignment + */ + EdgePairsIterator &operator= (const EdgePairsIterator &other) + { + if (this != &other) { + delete mp_delegate; + mp_delegate = other.mp_delegate ? other.mp_delegate->clone () : 0; + } + return *this; + } + + /** + * @Returns true, if the iterator is at the end + */ + bool at_end () const + { + return mp_delegate == 0 || mp_delegate->at_end (); + } + + /** + * @brief Increment + */ + EdgePairsIterator &operator++ () + { + if (mp_delegate) { + mp_delegate->increment (); + } + return *this; + } + + /** + * @brief Access + */ + reference operator* () const + { + const value_type *value = operator-> (); + tl_assert (value != 0); + return *value; + } + + /** + * @brief Access + */ + pointer operator-> () const + { + return mp_delegate ? mp_delegate->get () : 0; + } + +private: + EdgePairsIteratorDelegate *mp_delegate; +}; + +/** + * @brief A helper class allowing delivery of addressable edges + * + * In some applications (i.e. box scanner), edges need to be taken + * by address. The edge set cannot always deliver adressable edges. + * This class help providing this ability by keeping a temporary copy + * if required. + */ + +class DB_PUBLIC AddressableEdgePairDelivery +{ +public: + AddressableEdgePairDelivery () + : m_iter (), m_valid (false) + { + // .. nothing yet .. + } + + AddressableEdgePairDelivery (const EdgePairsIterator &iter, bool valid) + : m_iter (iter), m_valid (valid) + { + if (! m_valid && ! m_iter.at_end ()) { + m_heap.push_back (*m_iter); + } + } + + bool at_end () const + { + return m_iter.at_end (); + } + + AddressableEdgePairDelivery &operator++ () + { + ++m_iter; + if (! m_valid && ! m_iter.at_end ()) { + m_heap.push_back (*m_iter); + } + return *this; + } + + const db::EdgePair *operator-> () const + { + if (m_valid) { + return m_iter.operator-> (); + } else { + return &m_heap.back (); + } + } + +private: + EdgePairsIterator m_iter; + bool m_valid; + std::list m_heap; +}; + +class EdgePairs; + +/** + * @brief A base class for edge pair filters + */ +class DB_PUBLIC EdgePairFilterBase +{ +public: + EdgePairFilterBase () { } + virtual ~EdgePairFilterBase () { } + + virtual bool selected (const db::EdgePair &edge) const = 0; + virtual const TransformationReducer *vars () const = 0; +}; /** * @brief A set of edge pairs @@ -46,92 +230,173 @@ class Edges; * can be converted to polygons or to individual edges. */ class DB_PUBLIC EdgePairs + : public gsi::ObjectBase { public: + typedef db::Coord coord_type; + typedef db::coord_traits coord_traits; typedef db::EdgePair edge_pair_type; - typedef std::vector::const_iterator const_iterator; + typedef db::Vector vector_type; + typedef db::Point point_type; + typedef db::Box box_type; + typedef coord_traits::distance_type distance_type; + typedef EdgePairsIterator const_iterator; /** * @brief Default constructor * * This constructor creates an empty edge pair set. */ - EdgePairs () - { - init (); - } + EdgePairs (); /** - * @brief Equality + * @brief Destructor */ - bool operator== (const db::EdgePairs &other) const; + ~EdgePairs (); /** - * @brief Inequality + * @brief Constructor from a delegate + * + * The region will take ownership of the delegate. */ - bool operator!= (const db::EdgePairs &other) const + EdgePairs (EdgePairsDelegate *delegate); + + /** + * @brief Copy constructor + */ + EdgePairs (const EdgePairs &other); + + /** + * @brief Assignment + */ + EdgePairs &operator= (const EdgePairs &other); + + /** + * @brief Constructor from an object + * + * Creates an edge pair set representing a single instance of that object + */ + explicit EdgePairs (const db::EdgePair &s) + : mp_delegate (0) { - return !operator== (other); + insert (s); } /** - * @brief Less operator - */ - bool operator< (const db::EdgePairs &other) const; - - /** - * @brief Joining of edge pair sets + * @brief Constructor from an object * - * This method will combine the edge pairs from "other" with the egdes of "this". + * Creates an edge pair set representing a single instance of that object */ - db::EdgePairs operator+ (const db::EdgePairs &other) const + explicit EdgePairs (const db::Shape &s) + : mp_delegate (0) { - db::EdgePairs d (*this); - d += other; - return d; + insert (s); } /** - * @brief In-place joining of edge pair sets + * @brief Sequence constructor + * + * Creates an edge set from a sequence of objects. The objects need to be edge pairs. + * This version accepts iterators of the begin ... end style. */ - db::EdgePairs &operator+= (const db::EdgePairs &other); + template + explicit EdgePairs (const Iter &b, const Iter &e) + : mp_delegate (0) + { + reserve (e - b); + for (Iter i = b; i != e; ++i) { + insert (*i); + } + } /** - * @brief Begin iterator of the edge pair set + * @brief Constructor from a RecursiveShapeIterator * - * The iterator delivers the edge pairs of the edge pair set. + * Creates an edge pair set from a recursive shape iterator. This allows to feed an edge pair set + * from a hierarchy of cells. + */ + explicit EdgePairs (const RecursiveShapeIterator &si); + + /** + * @brief Constructor from a RecursiveShapeIterator with a transformation + * + * Creates an edge pair set from a recursive shape iterator. This allows to feed an edge pair set + * from a hierarchy of cells. The transformation is useful to scale to a specific + * DBU for example. + */ + explicit EdgePairs (const RecursiveShapeIterator &si, const db::ICplxTrans &trans); + + /** + * @brief Constructor from a RecursiveShapeIterator providing a deep representation + * + * This version will create a hierarchical edge pair collection. The DeepShapeStore needs to be provided + * during the lifetime of the collection and acts as a heap for optimized data. + */ + explicit EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss); + + /** + * @brief Constructor from a RecursiveShapeIterator providing a deep representation with transformation + */ + explicit EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans); + + /** + * @brief Gets the underlying delegate object + */ + EdgePairsDelegate *delegate () const + { + return mp_delegate; + } + + /** + * @brief Iterator of the edge pair set + * + * The iterator delivers the edges of the edge pair set. + * It follows the at_end semantics. */ const_iterator begin () const { - return m_edge_pairs.begin (); + return EdgePairsIterator (mp_delegate->begin ()); } /** - * @brief End iterator of the edge pair set - * - * The iterator delivers the edge pairs of the edge pair set. + * @brief Delivers a RecursiveShapeIterator pointing to the edge pairs plus the necessary transformation */ - const_iterator end () const + std::pair begin_iter () const { - return m_edge_pairs.end (); + return mp_delegate->begin_iter (); } /** - * @brief Insert an edge pair into the edge pair set + * @brief Inserts the given shape (working object) into the edge pair set */ - void insert (const db::Edge &e1, const db::Edge &e2); + template + void insert (const Sh &shape); /** - * @brief Insert an edge pair into the edge pair set + * @brief Inserts an edge pair made from two edges */ - void insert (const edge_pair_type &ep); + void insert (const db::Edge &e1, const db::Edge &e2) + { + insert (db::EdgePair (e1, e2)); + } + + /** + * @brief Insert a shape reference into the edge pair set + */ + void insert (const db::Shape &shape); + + /** + * @brief Insert a transformed shape into the edge pair set + */ + template + void insert (const db::Shape &shape, const T &trans); /** * @brief Returns true if the edge pair set is empty */ bool empty () const { - return m_edge_pairs.empty (); + return mp_delegate->empty (); } /** @@ -139,87 +404,68 @@ public: */ size_t size () const { - return m_edge_pairs.size (); + return mp_delegate->size (); } /** * @brief Returns a string representing the edge pair set + * + * nmax specifies how many edge pairs are included (set to std::numeric_limits::max() for "all". */ - std::string to_string (size_t nmax = 10) const; + std::string to_string (size_t nmax = 10) const + { + return mp_delegate->to_string (nmax); + } /** - * @brief Clear the edge pair set + * @brief Clears the edge set */ void clear (); /** - * @brief Reserve memory for the given number of polygons + * @brief Reserve memory for the given number of edges */ - void reserve (size_t n) - { - m_edge_pairs.reserve (n); - } + void reserve (size_t n); /** * @brief Returns the bounding box of the edge pair set */ Box bbox () const { - ensure_bbox_valid (); - return m_bbox; + return mp_delegate->bbox (); } /** - * @brief Filters the edge pair set + * @brief Filters the edge pairs * * This method will keep all edge pairs for which the filter returns true. + * Merged semantics applies. */ - template - EdgePairs &filter (F &filter) + EdgePairs &filter (const EdgePairFilterBase &filter) { - std::vector ::iterator ew = m_edge_pairs.begin (); - for (const_iterator e = begin (); e != end (); ++e) { - if (filter (*e)) { - *ew++ = *e; - } - } - m_edge_pairs.erase (ew, m_edge_pairs.end ()); + set_delegate (mp_delegate->filter_in_place (filter)); return *this; } /** * @brief Returns the filtered edge pairs * - * This method will return a new region with only those edge pairs which + * This method will return a new region with only those edge pairs which * conform to the filter criterion. */ - template - EdgePairs filtered (F &filter) const + EdgePairs filtered (const EdgePairFilterBase &filter) const { - EdgePairs d; - for (const_iterator e = begin (); e != end (); ++e) { - if (filter (*e)) { - d.insert (*e); - } - } - return d; + return EdgePairs (mp_delegate->filtered (filter)); } /** - * @brief Transform the edge pair set + * @brief Transforms the edge pair set */ template - EdgePairs &transform (const T &trans) - { - for (std::vector ::iterator e = m_edge_pairs.begin (); e != m_edge_pairs.end (); ++e) { - e->transform (trans); - } - m_bbox_valid = false; - return *this; - } + EdgePairs &transform (const T &trans); /** - * @brief Returns the transformed edge set + * @brief Returns the transformed edge pair set */ template EdgePairs transformed (const T &trans) const @@ -230,17 +476,124 @@ public: } /** - * @brief Swap with the other region + * @brief Swaps with the other edge set */ void swap (db::EdgePairs &other) { - std::swap (m_edge_pairs, other.m_edge_pairs); - std::swap (m_bbox, other.m_bbox); - std::swap (m_bbox_valid, other.m_bbox_valid); + std::swap (other.mp_delegate, mp_delegate); } /** - * @brief Convert to polygons + * @brief Joining of edge pair set + * + * This method joins the edge pair sets. + */ + EdgePairs operator+ (const EdgePairs &other) const + { + return EdgePairs (mp_delegate->add (other)); + } + + /** + * @brief In-place edge pair set joining + */ + EdgePairs &operator+= (const EdgePairs &other) + { + set_delegate (mp_delegate->add_in_place (other)); + return *this; + } + + /** + * @brief Returns all edge pairs which are in the other edge pair set + * + * This method will return all edge pairs which are part of another edge pair set. + * The match is done exactly. + * The "invert" flag can be used to invert the sense, i.e. with + * "invert" set to true, this method will return all edge pairs not + * in the other edge pair set. + */ + EdgePairs in (const EdgePairs &other, bool invert = false) const + { + return EdgePairs (mp_delegate->in (other, invert)); + } + + /** + * @brief Returns the nth edge pair + * + * This operation is available only for flat regions - i.e. such for which + * "has_valid_edge_pairs" is true. + */ + const db::EdgePair *nth (size_t n) const + { + return mp_delegate->nth (n); + } + + /** + * @brief Forces flattening of the edge pair collection + * + * This method will turn any edge pair collection into a flat one. + */ + void flatten () + { + flat_edge_pairs (); + } + + /** + * @brief Returns true, if the edge pair set has valid edges stored within itself + * + * If the region has valid edges, it is permissable to use the edge pair's addresses + * from the iterator. Furthermore, the random access operator nth() is available. + */ + bool has_valid_edge_pairs () const + { + return mp_delegate->has_valid_edge_pairs (); + } + + /** + * @brief Returns an addressable delivery for edge pairs + * + * This object allows accessing the edge pairs by address, even if they + * are not delivered from a container. The magic is a heap object + * inside the delivery object. Hence, the deliver object must persist + * as long as the addresses are required. + */ + AddressableEdgePairDelivery addressable_edge_pairs () const + { + return AddressableEdgePairDelivery (begin (), has_valid_edge_pairs ()); + } + + /** + * @brief Gets the internal iterator + * + * This method is intended for users who know what they are doing + */ + const db::RecursiveShapeIterator &iter () const; + + /** + * @brief Equality + */ + bool operator== (const db::EdgePairs &other) const + { + return mp_delegate->equals (other); + } + + /** + * @brief Inequality + */ + bool operator!= (const db::EdgePairs &other) const + { + return ! mp_delegate->equals (other); + } + + /** + * @brief Less operator + */ + bool operator< (const db::EdgePairs &other) const + { + return mp_delegate->less (other); + } + + /** + * @brief Converts to polygons * * Note: because of the include hierarchy we can't use a direct return value. * @@ -263,7 +616,7 @@ public: */ void edges (Edges &output) const; - /* + /** * @brief Returns the first edges * * Note: because of the include hierarchy we can't use a direct return value. @@ -273,7 +626,7 @@ public: */ void first_edges (Edges &output) const; - /* + /** * @brief Returns the second edges * * Note: because of the include hierarchy we can't use a direct return value. @@ -288,22 +641,44 @@ public: * * @param progress_text The description text of the progress object */ - void enable_progress (const std::string &progress_desc = std::string ()); + void enable_progress (const std::string &progress_desc = std::string ()) + { + mp_delegate->enable_progress (progress_desc); + } /** * @brief Disable progress reporting */ - void disable_progress (); + void disable_progress () + { + mp_delegate->disable_progress (); + } + + /** + * @brief Inserts the edge pair collection into the given layout, cell and layer + * If the edge pair collection is a hierarchical region, the hierarchy is copied into the + * layout's hierarchy. + */ + void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const + { + return mp_delegate->insert_into (layout, into_cell, into_layer); + } + + /** + * @brief Inserts the edge pair collection into the given layout, cell and layer as polygons with the given enlargement + * If the edge pair collection is a hierarchical region, the hierarchy is copied into the + * layout's hierarchy. + */ + void insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const + { + return mp_delegate->insert_into_as_polygons (layout, into_cell, into_layer, enl); + } private: - std::vector m_edge_pairs; - mutable db::Box m_bbox; - mutable bool m_bbox_valid; - bool m_report_progress; - std::string m_progress_desc; + EdgePairsDelegate *mp_delegate; - void init (); - void ensure_bbox_valid () const; + void set_delegate (EdgePairsDelegate *delegate); + FlatEdgePairs *flat_edge_pairs (); }; } diff --git a/src/db/db/dbEdgePairsDelegate.cc b/src/db/db/dbEdgePairsDelegate.cc new file mode 100644 index 000000000..470b8f3e4 --- /dev/null +++ b/src/db/db/dbEdgePairsDelegate.cc @@ -0,0 +1,67 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbEdgePairsDelegate.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- + +EdgePairsDelegate::EdgePairsDelegate () +{ + m_report_progress = false; +} + +EdgePairsDelegate::EdgePairsDelegate (const EdgePairsDelegate &other) +{ + operator= (other); +} + +EdgePairsDelegate & +EdgePairsDelegate::operator= (const EdgePairsDelegate &other) +{ + if (this != &other) { + m_report_progress = other.m_report_progress; + } + return *this; +} + +EdgePairsDelegate::~EdgePairsDelegate () +{ + // .. nothing yet .. +} + +void EdgePairsDelegate::enable_progress (const std::string &progress_desc) +{ + m_report_progress = true; + m_progress_desc = progress_desc; +} + +void EdgePairsDelegate::disable_progress () +{ + m_report_progress = false; +} + +} + diff --git a/src/db/db/dbEdgePairsDelegate.h b/src/db/db/dbEdgePairsDelegate.h new file mode 100644 index 000000000..c7eb85082 --- /dev/null +++ b/src/db/db/dbEdgePairsDelegate.h @@ -0,0 +1,142 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbEdgePairsDelegate +#define HDR_dbEdgePairsDelegate + +#include "dbCommon.h" + +#include "dbEdgePair.h" +#include "tlUniqueId.h" + +namespace db { + +class RecursiveShapeIterator; +class EdgePairs; +class EdgePairFilterBase; +class RegionDelegate; +class EdgesDelegate; +class Layout; + +/** + * @brief The edge pair set iterator delegate + */ +class DB_PUBLIC EdgePairsIteratorDelegate +{ +public: + EdgePairsIteratorDelegate () { } + virtual ~EdgePairsIteratorDelegate () { } + + typedef db::EdgePair value_type; + + virtual bool at_end () const = 0; + virtual void increment () = 0; + virtual const value_type *get () const = 0; + virtual EdgePairsIteratorDelegate *clone () const = 0; +}; + +/** + * @brief The delegate for the actual edge set implementation + */ +class DB_PUBLIC EdgePairsDelegate + : public tl::UniqueId +{ +public: + typedef db::Coord coord_type; + typedef db::coord_traits coord_traits; + typedef db::EdgePair edge_pair_type; + typedef db::Vector vector_type; + typedef db::Point point_type; + typedef db::Box box_type; + + EdgePairsDelegate (); + virtual ~EdgePairsDelegate (); + + EdgePairsDelegate (const EdgePairsDelegate &other); + EdgePairsDelegate &operator= (const EdgePairsDelegate &other); + + virtual EdgePairsDelegate *clone () const = 0; + + void enable_progress (const std::string &progress_desc); + void disable_progress (); + + // dummy features to harmonize the interface of region, edges and edge pair delegates + void set_merged_semantics (bool) { } + bool merged_semantics () const { return false; } + void set_is_merged (bool) { } + bool is_merged () const { return false; } + + virtual std::string to_string (size_t nmax) const = 0; + + virtual EdgePairsIteratorDelegate *begin () const = 0; + virtual std::pair begin_iter () const = 0; + + virtual bool empty () const = 0; + virtual size_t size () const = 0; + + virtual Box bbox () const = 0; + + virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter) = 0; + virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &filter) const = 0; + + virtual RegionDelegate *polygons (db::Coord e) const = 0; + virtual EdgesDelegate *edges () const = 0; + virtual EdgesDelegate *first_edges () const = 0; + virtual EdgesDelegate *second_edges () const = 0; + + virtual EdgePairsDelegate *add_in_place (const EdgePairs &other) = 0; + virtual EdgePairsDelegate *add (const EdgePairs &other) const = 0; + + virtual EdgePairsDelegate *in (const EdgePairs &other, bool invert) const = 0; + + virtual const db::EdgePair *nth (size_t n) const = 0; + virtual bool has_valid_edge_pairs () const = 0; + + virtual const db::RecursiveShapeIterator *iter () const = 0; + + virtual bool equals (const EdgePairs &other) const = 0; + virtual bool less (const EdgePairs &other) const = 0; + + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const = 0; + virtual void insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const = 0; + +protected: + const std::string &progress_desc () const + { + return m_progress_desc; + } + + bool report_progress () const + { + return m_report_progress; + } + +private: + bool m_report_progress; + std::string m_progress_desc; +}; + +} + +#endif + diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 756f6af72..2447a47b8 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -975,7 +975,7 @@ BooleanOp2::compare_ns () const // EdgeProcessor implementation EdgeProcessor::EdgeProcessor (bool report_progress, const std::string &progress_desc) - : m_report_progress (report_progress), m_progress_desc (progress_desc) + : m_report_progress (report_progress), m_progress_desc (progress_desc), m_base_verbosity (30) { mp_work_edges = new std::vector (); mp_cpvector = new std::vector (); @@ -1006,6 +1006,12 @@ EdgeProcessor::enable_progress (const std::string &progress_desc) m_progress_desc = progress_desc; } +void +EdgeProcessor::set_base_verbosity (int bv) +{ + m_base_verbosity = bv; +} + void EdgeProcessor::reserve (size_t n) { @@ -1534,7 +1540,7 @@ get_intersections_per_band_any (std::vector &cutpoints, std::vector void EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) { - tl::SelfTimer timer (tl::verbosity () >= 31, "EdgeProcessor: process"); + tl::SelfTimer timer (tl::verbosity () >= m_base_verbosity, "EdgeProcessor: process"); bool prefer_touch = op.prefer_touch (); bool selects_edges = op.selects_edges (); @@ -1749,7 +1755,7 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) #endif - tl::SelfTimer timer2 (tl::verbosity () >= 41, "EdgeProcessor: production"); + tl::SelfTimer timer2 (tl::verbosity () >= m_base_verbosity + 10, "EdgeProcessor: production"); // step 4: compute the result edges diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index 9cca1308a..2ba2521b8 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -319,7 +319,7 @@ private: * based on a generic operator. */ template -class DB_PUBLIC GenericMerge +class DB_PUBLIC_TEMPLATE GenericMerge : public EdgeEvaluatorBase { public: @@ -606,6 +606,14 @@ public: */ void disable_progress (); + /** + * @brief Base verbosity for timer reporting + * + * The default value is 30. Basic timing will be reported for > base_verbosity, detailed timing + * for > base_verbosity + 10. + */ + void set_base_verbosity (int bv); + /** * @brief Reserve space for at least n edges */ @@ -943,6 +951,7 @@ private: std::vector *mp_cpvector; bool m_report_progress; std::string m_progress_desc; + int m_base_verbosity; static size_t count_edges (const db::Polygon &q) { diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index 7fc1681d0..aca8c0baf 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -20,1340 +20,268 @@ */ - #include "dbEdges.h" -#include "dbEdgePairs.h" +#include "dbDeepEdges.h" +#include "dbOriginalLayerEdges.h" +#include "dbEmptyEdges.h" +#include "dbFlatEdges.h" #include "dbRegion.h" -#include "dbLayoutUtils.h" -#include "dbBoxConvert.h" -#include "dbBoxScanner.h" -#include "dbPolygonTools.h" - -#include "tlIntervalMap.h" -#include "tlVariant.h" - -#include namespace db { -Edges::Edges (const RecursiveShapeIterator &si, bool as_edges) - : m_edges (false), m_merged_edges (false) +namespace { - init (); - if (! as_edges) { - m_iter = si; - } else { - for (RecursiveShapeIterator s = si; ! s.at_end (); ++s) { - insert (s.shape (), s.trans ()); + +// ------------------------------------------------------------------------------------------------------------- +// Smoothing processor + +class EdgeSegmentSelector + : public EdgeProcessorBase +{ +public: + EdgeSegmentSelector (int mode, db::Edges::length_type length, double fraction) + : m_mode (mode), m_length (length), m_fraction (fraction) + { } + + virtual void process (const db::Edge &edge, std::vector &res) const + { + double l = std::max (edge.double_length () * m_fraction, double (m_length)); + + if (m_mode < 0) { + + res.push_back (db::Edge (edge.p1 (), db::Point (db::DPoint (edge.p1 ()) + db::DVector (edge.d ()) * (l / edge.double_length ())))); + + } else if (m_mode > 0) { + + res.push_back (db::Edge (db::Point (db::DPoint (edge.p2 ()) - db::DVector (edge.d ()) * (l / edge.double_length ())), edge.p2 ())); + + } else { + + db::DVector dl = db::DVector (edge.d ()) * (0.5 * l / edge.double_length ()); + db::DPoint center = db::DPoint (edge.p1 ()) + db::DVector (edge.p2 () - edge.p1 ()) * 0.5; + + res.push_back (db::Edge (db::Point (center - dl), db::Point (center + dl))); + + } + } + + virtual const TransformationReducer *vars () const { return &m_vars; } + virtual bool result_is_merged () const { return false; } + virtual bool requires_raw_input () const { return false; } + virtual bool result_must_not_be_merged () const { return m_length <= 0; } + virtual bool wants_variants () const { return true; } + +private: + int m_mode; + db::Edges::length_type m_length; + double m_fraction; + db::MagnificationReducer m_vars; +}; + +} + +// ------------------------------------------------------------------------------------------------------------- +// Edges implementation + +Edges::Edges () + : mp_delegate (new EmptyEdges ()) +{ + // .. nothing yet .. +} + +Edges::Edges (EdgesDelegate *delegate) + : mp_delegate (delegate) +{ + // .. nothing yet .. +} + +Edges::Edges (const Edges &other) + : gsi::ObjectBase (), mp_delegate (other.mp_delegate->clone ()) +{ + // .. nothing yet .. +} + +Edges::~Edges () +{ + delete mp_delegate; + mp_delegate = 0; +} + +Edges &Edges::operator= (const Edges &other) +{ + if (this != &other) { + set_delegate (other.mp_delegate->clone (), false); + } + return *this; +} + +Edges::Edges (const RecursiveShapeIterator &si, bool as_edges) +{ + if (! as_edges) { + mp_delegate = new OriginalLayerEdges (si); + } else { + FlatEdges *fe = new FlatEdges (); + mp_delegate = fe; + for (RecursiveShapeIterator s = si; ! s.at_end (); ++s) { + fe->insert (s.shape (), s.trans ()); } } - m_bbox_valid = false; - m_is_merged = false; } Edges::Edges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool as_edges, bool merged_semantics) - : m_edges (false), m_merged_edges (false) { - init (); if (! as_edges) { - m_iter = si; - m_iter_trans = trans; + mp_delegate = new OriginalLayerEdges (si, trans, merged_semantics); } else { + FlatEdges *fe = new FlatEdges (); + fe->set_merged_semantics (merged_semantics); + mp_delegate = fe; for (RecursiveShapeIterator s = si; ! s.at_end (); ++s) { - insert (s.shape (), trans * s.trans ()); + fe->insert (s.shape (), trans * s.trans ()); } } - m_bbox_valid = false; - m_is_merged = false; - m_merged_semantics = merged_semantics; } -bool -Edges::operator== (const db::Edges &other) const +Edges::Edges (const RecursiveShapeIterator &si, DeepShapeStore &dss, bool as_edges) { - if (empty () != other.empty ()) { - return false; - } - if (size () != other.size ()) { - return false; - } - db::Edges::const_iterator o1 = begin (); - db::Edges::const_iterator o2 = other.begin (); - while (! o1.at_end () && ! o2.at_end ()) { - if (*o1 != *o2) { - return false; - } - ++o1; - ++o2; - } - return true; + mp_delegate = new DeepEdges (si, dss, as_edges); } -bool -Edges::operator< (const db::Edges &other) const +Edges::Edges (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool as_edges, bool merged_semantics) { - if (empty () != other.empty ()) { - return empty () < other.empty (); - } - if (size () != other.size ()) { - return (size () < other.size ()); - } - db::Edges::const_iterator o1 = begin (); - db::Edges::const_iterator o2 = other.begin (); - while (! o1.at_end () && ! o2.at_end ()) { - if (*o1 != *o2) { - return *o1 < *o2; - } - ++o1; - ++o2; - } - return false; + mp_delegate = new DeepEdges (si, dss, trans, as_edges, merged_semantics); } -std::string -Edges::to_string (size_t nmax) const +const db::RecursiveShapeIterator & +Edges::iter () const { - std::ostringstream os; - const_iterator e = begin (); - bool first = true; - for ( ; ! e.at_end () && nmax != 0; ++e, --nmax) { - if (! first) { - os << ";"; - } - first = false; - os << e->to_string (); - } - if (! e.at_end ()) { - os << "..."; - } - return os.str (); -} - -void -Edges::swap (Edges &other) -{ - std::swap (m_is_merged, other.m_is_merged); - std::swap (m_merged_semantics, other.m_merged_semantics); - m_edges.swap (other.m_edges); - m_merged_edges.swap (other.m_merged_edges); - std::swap (m_bbox, other.m_bbox); - std::swap (m_bbox_valid, other.m_bbox_valid); - std::swap (m_merged_edges_valid, other.m_merged_edges_valid); - std::swap (m_iter, other.m_iter); - std::swap (m_iter_trans, other.m_iter_trans); + static db::RecursiveShapeIterator def_iter; + const db::RecursiveShapeIterator *i = mp_delegate->iter (); + return *(i ? i : &def_iter); } void -Edges::invalidate_cache () +Edges::set_delegate (EdgesDelegate *delegate, bool keep_attributes) { - m_bbox_valid = false; - m_merged_edges.clear (); - m_merged_edges_valid = false; -} - -void -Edges::disable_progress () -{ - m_report_progress = false; -} - -void -Edges::enable_progress (const std::string &progress_desc) -{ - m_report_progress = true; - m_progress_desc = progress_desc; -} - -Edges::distance_type -Edges::length (const db::Box &box) const -{ - distance_type l = 0; - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - - if (box.empty () || (box.contains (e->p1 ()) && box.contains (e->p2 ()))) { - l += e->length (); - } else { - - std::pair ce = e->clipped (box); - if (ce.first) { - - db::Coord dx = ce.second.dx (); - db::Coord dy = ce.second.dy (); - db::Coord x = ce.second.p1 ().x (); - db::Coord y = ce.second.p1 ().y (); - if ((dx == 0 && x == box.left () && dy < 0) || - (dx == 0 && x == box.right () && dy > 0) || - (dy == 0 && y == box.top () && dx < 0) || - (dy == 0 && y == box.bottom () && dx > 0)) { - // not counted -> box is at outside side of the edge - } else { - l += ce.second.length (); - } - - } - + if (delegate != mp_delegate) { + if (keep_attributes && mp_delegate && delegate) { + // copy attributes (threads, min_coherence etc.) from old to new + *delegate = *mp_delegate; } - - } - - return l; -} - -Edges -Edges::start_segments (db::Edges::length_type length, double fraction) const -{ - Edges edges; - edges.reserve (m_edges.size ()); - - // zero-length edges would vanish in merged sematics, so we don't set it now - if (length == 0) { - edges.set_merged_semantics (false); - } - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - double l = std::max (e->double_length () * fraction, double (length)); - edges.insert (db::Edge (e->p1 (), db::Point (db::DPoint (e->p1 ()) + db::DVector (e->d()) * (l / e->double_length ())))); - } - - return edges; -} - -Edges -Edges::end_segments (db::Edges::length_type length, double fraction) const -{ - Edges edges; - edges.reserve (m_edges.size ()); - - // zero-length edges would vanish in merged sematics, so we don't set it now - if (length == 0) { - edges.set_merged_semantics (false); - } - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - double l = std::max (e->double_length () * fraction, double (length)); - edges.insert (db::Edge (db::Point (db::DPoint (e->p2 ()) - db::DVector (e->d()) * (l / e->double_length ())), e->p2 ())); - } - - return edges; -} - -Edges -Edges::centers (db::Edges::length_type length, double fraction) const -{ - Edges edges; - edges.reserve (m_edges.size ()); - - // zero-length edges would vanish in merged sematics, so we don't set it now - if (length == 0) { - edges.set_merged_semantics (false); - } - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - double l = std::max (e->double_length () * fraction, double (length)); - db::DVector dl = db::DVector (e->d()) * (0.5 * l / e->double_length ()); - db::DPoint center = db::DPoint (e->p1 ()) + db::DVector (e->p2 () - e->p1 ()) * 0.5; - edges.insert (db::Edge (db::Point (center - dl), db::Point (center + dl))); - } - - return edges; -} - -void -Edges::edge_region_op (const Region &other, bool outside, bool include_borders) -{ - // shortcuts - if (other.empty ()) { - if (! outside) { - clear (); - } - return; - } else if (empty ()) { - return; - } - - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - for (db::Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { - if (p->box ().touches (bbox ())) { - ep.insert (*p, 0); - } - } - - for (const_iterator e = begin (); ! e.at_end (); ++e) { - ep.insert (*e, 1); - } - - invalidate_cache (); - - db::EdgeShapeGenerator cc (m_edges, true /*clear*/); - db::EdgePolygonOp op (outside, include_borders); - ep.process (cc, op); - - set_valid_edges (); - - m_is_merged = false; -} - -namespace -{ - -struct OrJoinOp -{ - void operator() (int &v, int n) - { - v += n; - } -}; - -struct AndJoinOp -{ - void operator() (int &v, int n) - { - if (n == 0) { - v = 0; - } - } -}; - -struct NotJoinOp -{ - void operator() (int &v, int n) - { - if (n != 0) { - v = 0; - } - } -}; - -struct XorJoinOp -{ - void operator() (int &v, int n) - { - if (n != 0) { - if (v == 0) { - v = (n > 0 ? 1 : -1); - } else { - v = 0; - } - } - } -}; - -struct EdgeBooleanCluster - : public db::cluster -{ - typedef db::Edge::coord_type coord_type; - - EdgeBooleanCluster (db::Edges *output, db::Edges::BoolOp op) - : mp_output (output), m_op (op) - { - // .. nothing yet .. - } - - void finish () - { - // determine base edge (longest overall edge) - - // shortcut for single edge - if (begin () + 1 == end ()) { - if (begin ()->second == 0) { - if (m_op != db::Edges::And) { - mp_output->insert (*(begin ()->first)); - } - } else { - if (m_op != db::Edges::And && m_op != db::Edges::Not) { - mp_output->insert (*(begin ()->first)); - } - } - return; - } - - db::Edge r = *begin ()->first; - double l1 = 0.0, l2 = r.double_length (); - double n = 1.0 / l2; - db::Point p1 = r.p1 (), p2 = r.p2 (); - - for (iterator o = begin () + 1; o != end (); ++o) { - double ll1 = db::sprod (db::Vector (o->first->p1 () - r.p1 ()), r.d ()) * n; - double ll2 = db::sprod (db::Vector (o->first->p2 () - r.p1 ()), r.d ()) * n; - if (ll1 < l1) { - p1 = o->first->p1 (); - l1 = ll1; - } - if (ll2 < l1) { - p1 = o->first->p2 (); - l1 = ll2; - } - if (ll1 > l2) { - p2 = o->first->p1 (); - l2 = ll1; - } - if (ll2 > l2) { - p2 = o->first->p2 (); - l2 = ll2; - } - } - - db::Vector d = db::Vector (p2 - p1); - n = 1.0 / d.double_length (); - - OrJoinOp or_jop; - AndJoinOp and_jop; - NotJoinOp not_jop; - XorJoinOp xor_jop; - - tl::interval_map a, b; - a.add (0, db::coord_traits::rounded (d.double_length ()), 0, or_jop); - b.add (0, db::coord_traits::rounded (d.double_length ()), 0, or_jop); - - for (iterator o = begin (); o != end (); ++o) { - db::Coord l1 = db::coord_traits::rounded (db::sprod (db::Vector (o->first->p1 () - p1), d) * n); - db::Coord l2 = db::coord_traits::rounded (db::sprod (db::Vector (o->first->p2 () - p1), d) * n); - if (o->second == 0 || m_op == db::Edges::Or) { - if (l1 < l2) { - a.add (l1, l2, 1, or_jop); - } else if (l1 > l2) { - a.add (l2, l1, -1, or_jop); - } - } else { - if (l1 < l2) { - b.add (l1, l2, 1, or_jop); - } else { - b.add (l2, l1, -1, or_jop); - } - } - } - - tl::interval_map q; - for (tl::interval_map::const_iterator ia = a.begin (); ia != a.end (); ++ia) { - q.add (ia->first.first, ia->first.second, ia->second > 0 ? 1 : (ia->second < 0 ? -1 : 0), or_jop); - } - - if (b.begin () == b.end ()) { - - // optimize for empty b - if (m_op != db::Edges::And) { - for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { - if (ib->second > 0) { - mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.first * n)), p1 + db::Vector (d * (ib->first.second * n)))); - } else if (ib->second < 0) { - mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.second * n)), p1 + db::Vector (d * (ib->first.first * n)))); - } - } - } - - } else { - - if (m_op == db::Edges::And) { - for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { - q.add (ib->first.first, ib->first.second, ib->second, and_jop); - } - } else if (m_op == db::Edges::Not) { - for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { - q.add (ib->first.first, ib->first.second, ib->second, not_jop); - } - } else if (m_op == db::Edges::Xor) { - for (tl::interval_map::const_iterator ib = b.begin (); ib != b.end (); ++ib) { - q.add (ib->first.first, ib->first.second, ib->second, xor_jop); - } - } - - for (tl::interval_map::const_iterator iq = q.begin (); iq != q.end (); ++iq) { - if (iq->second > 0) { - mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.first * n)), p1 + db::Vector (d * (iq->first.second * n)))); - } else if (iq->second < 0) { - mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.second * n)), p1 + db::Vector (d * (iq->first.first * n)))); - } - } - - } - - } - -private: - db::Edges *mp_output; - db::Edges::BoolOp m_op; -}; - -struct EdgeBooleanClusterCollector - : public db::cluster_collector -{ - EdgeBooleanClusterCollector (db::Edges *output, Edges::BoolOp op) - : db::cluster_collector (EdgeBooleanCluster (output, op), op != Edges::And /*report single*/) - { - // .. nothing yet .. - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - // Select edges which are: - // 1.) not degenerate - // 2.) parallel with some tolerance of roughly 1 dbu - // 3.) connected - if (! o1->is_degenerate () && ! o2->is_degenerate () - && fabs ((double) db::vprod (*o1, *o2)) < db::coord_traits::prec_distance () * std::min (o1->double_length (), o2->double_length ()) - && (o1->p1 () == o2->p1 () || o1->p1 () == o2->p2 () || o1->p2 () == o2->p1 () || o1->p2 () == o2->p2 () || o1->coincident (*o2))) { - db::cluster_collector::add (o1, p1, o2, p2); - } - } -}; - -} - -void -Edges::inplace_boolean (const Edges *other, Edges::BoolOp op) -{ - Edges out = boolean (other, op); - swap (out); -} - -Edges -Edges::boolean (const Edges *other, Edges::BoolOp op) const -{ - Edges output; - EdgeBooleanClusterCollector cluster_collector (&output, op); - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (m_edges.size () + (other ? other->size () : 0)); - - ensure_valid_edges (); - for (const_iterator e = begin (); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (&*e, 0); - } - } - if (other) { - other->ensure_valid_edges (); - for (const_iterator e = other->begin (); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (&*e, 1); - } - } - } - - scanner.process (cluster_collector, 1, db::box_convert ()); - - output.m_is_merged = true; - return output; -} - -void -Edges::ensure_valid_merged_edges () const -{ - // If no merged semantics applies or we will deliver the original - // edges as merged ones, we need to make sure those are valid - // ones (with a unique memory address) - if (! m_merged_semantics || m_is_merged) { - ensure_valid_edges (); - } else { - ensure_merged_edges_valid (); + delete mp_delegate; + mp_delegate = delegate; } } void -Edges::ensure_merged_edges_valid () const -{ - if (! m_merged_edges_valid) { - - m_merged_edges.clear (); - - Edges tmp; - EdgeBooleanClusterCollector cluster_collector (&tmp, Or); - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (m_edges.size ()); - - ensure_valid_edges (); - for (const_iterator e = begin (); ! e.at_end (); ++e) { - if (! e->is_degenerate ()) { - scanner.insert (&*e, 0); - } - } - - scanner.process (cluster_collector, 1, db::box_convert ()); - - m_merged_edges.swap (tmp.m_edges); - m_merged_edges_valid = true; - - } -} - -Edges & -Edges::operator+= (const Edges &other) -{ - invalidate_cache (); - - if (! has_valid_edges ()) { - - m_edges.clear (); - - size_t n = 0; - for (const_iterator e = begin (); ! e.at_end (); ++e) { - ++n; - } - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - ++n; - } - - m_edges.reserve (db::Edge::tag (), n); - - for (const_iterator e = begin (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - - set_valid_edges (); - - } else if (! other.has_valid_edges ()) { - - size_t n = m_edges.size (); - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - ++n; - } - - m_edges.reserve (db::Edge::tag (), n); - - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - - } else { - m_edges.insert (other.m_edges.get_layer ().begin (), other.m_edges.get_layer ().end ()); - } - - m_is_merged = false; - return *this; -} - -namespace -{ - -/** - * @brief A helper class for the edge to region interaction functionality which acts as an edge pair receiver - * - * Note: This special scanner uses pointers to two different objects: edges and polygons. - * It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate - * pointers to edges. - * - * There is a special box converter which is able to sort that out as well. - */ -template -class edge_to_region_interaction_filter - : public db::box_scanner_receiver -{ -public: - edge_to_region_interaction_filter (OutputContainer &output) - : mp_output (&output) - { - // .. nothing yet .. - } - - void add (const char *o1, size_t p1, const char *o2, size_t p2) - { - const db::Edge *e = 0; - const db::Polygon *p = 0; - - // Note: edges have property 0 and have even-valued pointers. - // Polygons have property 1 and odd-valued pointers. - if (p1 == 0 && p2 == 1) { - e = reinterpret_cast (o1); - p = reinterpret_cast (o2 - 1); - } else if (p1 == 1 && p2 == 0) { - e = reinterpret_cast (o2); - p = reinterpret_cast (o1 - 1); - } - - if (e && p && m_seen.find (e) == m_seen.end ()) { - if (db::interact (*p, *e)) { - m_seen.insert (e); - mp_output->insert (*e); - } - } - } - -private: - OutputContainer *mp_output; - std::set m_seen; -}; - -/** - * @brief A special box converter that splits the pointers into polygon and edge pointers - */ -struct EdgeOrRegionBoxConverter -{ - typedef db::Box box_type; - - db::Box operator() (const char &c) const - { - // Note: edges have property 0 and have even-valued pointers. - // Polygons have property 1 and odd-valued pointers. - const char *cp = &c; - if ((size_t (cp) & 1) == 1) { - // it's a polygon - return (reinterpret_cast (cp - 1))->box (); - } else { - // it's an edge - const db::Edge *e = reinterpret_cast (cp); - return db::Box (e->p1 (), e->p2 ()); - } - } -}; - -} - -Edges & -Edges::select_interacting (const Region &other) -{ - // shortcuts - if (other.empty ()) { - clear (); - return *this; - } else if (empty ()) { - return *this; - } - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_merged_edges (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert ((char *) &*e, 0); - } - - other.ensure_valid_polygons (); - for (Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { - scanner.insert ((char *) &*p + 1, 1); - } - - Edges output; - edge_to_region_interaction_filter filter (output); - EdgeOrRegionBoxConverter bc; - scanner.process (filter, 1, bc); - - swap (output); - return *this; -} - -Edges & -Edges::select_not_interacting (const Region &other) -{ - // shortcuts - if (other.empty () || empty ()) { - return *this; - } - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_merged_edges (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert ((char *) &*e, 0); - } - - other.ensure_valid_polygons (); - for (Region::const_iterator p = other.begin (); ! p.at_end (); ++p) { - scanner.insert ((char *) &*p + 1, 1); - } - - std::set interacting; - edge_to_region_interaction_filter > filter (interacting); - EdgeOrRegionBoxConverter bc; - scanner.process (filter, 1, bc); - - Edges output; - for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { - if (interacting.find (*o) == interacting.end ()) { - output.insert (*o); - } - } - - swap (output); - return *this; -} - -namespace -{ - -/** - * @brief A helper class for the edge interaction functionality which acts as an edge pair receiver - */ -template -class edge_interaction_filter - : public db::box_scanner_receiver -{ -public: - edge_interaction_filter (OutputContainer &output) - : mp_output (&output) - { - // .. nothing yet .. - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - // Select the edges which intersect - if (p1 != p2) { - const db::Edge *o = p1 > p2 ? o2 : o1; - const db::Edge *oo = p1 > p2 ? o1 : o2; - if (o->intersect (*oo)) { - if (m_seen.insert (o).second) { - mp_output->insert (*o); - } - } - } - } - -private: - OutputContainer *mp_output; - std::set m_seen; -}; - -} - -Edges & -Edges::select_interacting (const Edges &other) -{ - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_merged_edges (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert (&*e, 0); - } - other.ensure_valid_edges (); - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - scanner.insert (&*e, 1); - } - - Edges output; - edge_interaction_filter filter (output); - scanner.process (filter, 1, db::box_convert ()); - - swap (output); - return *this; -} - -Edges & -Edges::select_not_interacting (const Edges &other) -{ - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_merged_edges (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert (&*e, 0); - } - other.ensure_valid_edges (); - for (const_iterator e = other.begin (); ! e.at_end (); ++e) { - scanner.insert (&*e, 1); - } - - std::set interacting; - edge_interaction_filter > filter (interacting); - scanner.process (filter, 1, db::box_convert ()); - - Edges output; - for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { - if (interacting.find (*o) == interacting.end ()) { - output.insert (*o); - } - } - - swap (output); - return *this; -} - -namespace -{ - -struct JoinEdgesCluster - : public db::cluster -{ - typedef db::Edge::coord_type coord_type; - - JoinEdgesCluster (db::Region *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i) - : mp_output (output), m_ext_b (ext_b), m_ext_e (ext_e), m_ext_o (ext_o), m_ext_i (ext_i) - { - // .. nothing yet .. - } - - void finish () - { - std::multimap objects_by_p1; - std::multimap objects_by_p2; - for (iterator o = begin (); o != end (); ++o) { - if (o->first->p1 () != o->first->p2 ()) { - objects_by_p1.insert (std::make_pair (o->first->p1 (), o)); - objects_by_p2.insert (std::make_pair (o->first->p2 (), o)); - } - } - - while (! objects_by_p2.empty ()) { - - tl_assert (! objects_by_p1.empty ()); - - // Find the beginning of a new sequence - std::multimap::iterator j0 = objects_by_p1.begin (); - std::multimap::iterator j = j0; - do { - std::multimap::iterator jj = objects_by_p2.find (j->first); - if (jj == objects_by_p2.end ()) { - break; - } else { - j = objects_by_p1.find (jj->second->first->p1 ()); - tl_assert (j != objects_by_p1.end ()); - } - } while (j != j0); - - iterator i = j->second; - - // determine a sequence - // TODO: this chooses any solution in case of forks. Choose a specific one? - std::vector pts; - pts.push_back (i->first->p1 ()); - - do { - - // record the next point - pts.push_back (i->first->p2 ()); - - // remove the edge as it's taken - std::multimap::iterator jj; - for (jj = objects_by_p2.find (i->first->p2 ()); jj != objects_by_p2.end () && jj->first == i->first->p2 (); ++jj) { - if (jj->second == i) { - break; - } - } - tl_assert (jj != objects_by_p2.end () && jj->second == i); - objects_by_p2.erase (jj); - objects_by_p1.erase (j); - - // process along the edge to the next one - // TODO: this chooses any solution in case of forks. Choose a specific one? - j = objects_by_p1.find (i->first->p2 ()); - if (j != objects_by_p1.end ()) { - i = j->second; - } else { - break; - } - - } while (true); - - bool cyclic = (pts.back () == pts.front ()); - - if (! cyclic) { - - // non-cyclic sequence - db::Path path (pts.begin (), pts.end (), 0, m_ext_b, m_ext_e, false); - std::vector hull; - path.hull (hull, m_ext_o, m_ext_i); - db::Polygon poly; - poly.assign_hull (hull.begin (), hull.end ()); - mp_output->insert (poly); - - } else { - - // we have a loop: form a contour by using the polygon size functions and a "Not" to form the hole - db::Polygon poly; - poly.assign_hull (pts.begin (), pts.end ()); - - db::EdgeProcessor ep; - db::RegionPolygonSink ps (*mp_output, false); - db::PolygonGenerator pg (ps, false, true); - - int mode_a = -1, mode_b = -1; - - if (m_ext_o == 0) { - ep.insert (poly, 0); - } else { - db::Polygon sized_poly (poly); - sized_poly.size (m_ext_o, m_ext_o, 2 /*sizing mode*/); - ep.insert (sized_poly, 0); - mode_a = 1; - } - - if (m_ext_i == 0) { - ep.insert (poly, 1); - } else { - db::Polygon sized_poly (poly); - sized_poly.size (-m_ext_i, -m_ext_i, 2 /*sizing mode*/); - ep.insert (sized_poly, 1); - mode_b = 1; - } - - db::BooleanOp2 op (db::BooleanOp::ANotB, mode_a, mode_b); - ep.process (pg, op); - - } - - } - } - -private: - db::Region *mp_output; - coord_type m_ext_b, m_ext_e, m_ext_o, m_ext_i; -}; - -struct JoinEdgesClusterCollector - : public db::cluster_collector -{ - typedef db::Edge::coord_type coord_type; - - JoinEdgesClusterCollector (db::Region *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i) - : db::cluster_collector (JoinEdgesCluster (output, ext_b, ext_e, ext_o, ext_i), true) - { - // .. nothing yet .. - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - if (o1->p2 () == o2->p1 () || o1->p1 () == o2->p2 ()) { - db::cluster_collector::add (o1, p1, o2, p2); - } - } -}; - -} - -void -Edges::extended (Region &output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const -{ - if (join) { - - JoinEdgesClusterCollector cluster_collector (&output, ext_b, ext_e, ext_o, ext_i); - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size ()); - - ensure_valid_edges (); - size_t n = 0; - for (const_iterator e = begin (); ! e.at_end (); ++e) { - scanner.insert (&*e, n); - ++n; - } - - scanner.process (cluster_collector, 1, db::box_convert ()); - - } else { - - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - - db::DVector d; - if (e->is_degenerate ()) { - d = db::DVector (1.0, 0.0); - } else { - d = db::DVector (e->d ()) * (1.0 / e->double_length ()); - } - - db::DVector n (-d.y (), d.x ()); - - db::Point pts[4] = { - db::Point (db::DPoint (e->p1 ()) - d * double (ext_b) + n * double (ext_o)), - db::Point (db::DPoint (e->p2 ()) + d * double (ext_e) + n * double (ext_o)), - db::Point (db::DPoint (e->p2 ()) + d * double (ext_e) - n * double (ext_i)), - db::Point (db::DPoint (e->p1 ()) - d * double (ext_b) - n * double (ext_i)), - }; - - db::Polygon poly; - poly.assign_hull (pts + 0, pts + 4); - output.insert (poly); - - } - - } -} - -Edges -Edges::in (const Edges &other, bool invert) const -{ - std::set op; - for (const_iterator o = other.begin_merged (); ! o.at_end (); ++o) { - op.insert (*o); - } - - Edges r; - for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { - if ((op.find (*o) == op.end ()) == invert) { - r.insert (*o); - } - } - - return r; -} - -void -Edges::init () -{ - m_report_progress = false; - m_bbox_valid = true; - m_is_merged = true; - m_merged_semantics = true; - m_merged_edges_valid = false; -} - -void -Edges::ensure_bbox_valid () const -{ - if (! m_bbox_valid) { - m_bbox = db::Box (); - for (const_iterator e = begin (); ! e.at_end (); ++e) { - m_bbox += db::Box (e->p1 (), e->p2 ()); - } - m_bbox_valid = true; - } -} - -Edges::const_iterator -Edges::begin_merged () const -{ - if (! m_merged_semantics || m_is_merged) { - return begin (); - } else { - ensure_merged_edges_valid (); - return db::EdgesIterator (m_merged_edges.get_layer ().begin (), m_merged_edges.get_layer ().end ()); - } -} - -std::pair -Edges::begin_iter () const -{ - if (has_valid_edges ()) { - return std::make_pair (db::RecursiveShapeIterator (m_edges), db::ICplxTrans ()); - } else { - return std::make_pair (m_iter, m_iter_trans); - } -} - -std::pair -Edges::begin_merged_iter () const -{ - if (! m_merged_semantics || m_is_merged) { - return begin_iter (); - } else { - ensure_merged_edges_valid (); - return std::make_pair (db::RecursiveShapeIterator (m_merged_edges), db::ICplxTrans ()); - } -} - -void -Edges::insert (const db::Edge &edge) -{ - ensure_valid_edges (); - m_edges.insert (edge); - m_is_merged = false; - invalidate_cache (); -} - -void -Edges::insert (const db::Box &box) -{ - if (! box.empty ()) { - ensure_valid_edges (); - m_edges.insert (db::Edge (box.lower_left (), box.upper_left ())); - m_edges.insert (db::Edge (box.upper_left (), box.upper_right ())); - m_edges.insert (db::Edge (box.upper_right (), box.lower_right ())); - m_edges.insert (db::Edge (box.lower_right (), box.lower_left ())); - m_is_merged = false; - invalidate_cache (); - } -} - -void -Edges::insert (const db::Path &path) -{ - if (path.points () > 0) { - insert (path.polygon ()); - } -} - -void -Edges::insert (const db::Polygon &polygon) -{ - ensure_valid_edges (); - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - m_is_merged = false; - invalidate_cache (); -} - -void -Edges::insert (const db::SimplePolygon &polygon) -{ - ensure_valid_edges (); - for (db::SimplePolygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - m_is_merged = false; - invalidate_cache (); -} - -void Edges::clear () { - m_edges.clear (); - m_bbox = db::Box (); - m_bbox_valid = true; - m_is_merged = true; - m_merged_edges.clear (); - m_merged_edges_valid = true; - m_iter = db::RecursiveShapeIterator (); - m_iter_trans = db::ICplxTrans (); -} - -namespace -{ - -/** - * @brief A helper class for the DRC functionality which acts as an edge pair receiver - * - * If will perform a edge by edge check using the provided EdgeRelationFilter - */ -class Edge2EdgeCheck - : public db::box_scanner_receiver -{ -public: - Edge2EdgeCheck (const EdgeRelationFilter &check, EdgePairs &output, bool requires_different_layers) - : mp_check (&check), mp_output (&output) - { - m_requires_different_layers = requires_different_layers; - } - - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) - { - // Overlap or inside checks require input from different layers - if (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0) { - - // 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)); - - db::EdgePair ep; - if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { - mp_output->insert (ep); - } - - } - } - -private: - const EdgeRelationFilter *mp_check; - EdgePairs *mp_output; - bool m_requires_different_layers; -}; - -} - -EdgePairs -Edges::run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const -{ - EdgePairs result; - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + (other ? other->size () : 0)); - - ensure_valid_merged_edges (); - size_t n = 0; - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - scanner.insert (&*e, n); - n += 2; - } - - if (other) { - other->ensure_valid_merged_edges (); - n = 1; - for (const_iterator e = other->begin_merged (); ! e.at_end (); ++e) { - scanner.insert (&*e, n); - n += 2; - } - } - - EdgeRelationFilter check (rel, d, metrics); - check.set_include_zero (other != 0); - check.set_whole_edges (whole_edges); - check.set_ignore_angle (ignore_angle); - check.set_min_projection (min_projection); - check.set_max_projection (max_projection); - - Edge2EdgeCheck edge_check (check, result, other != 0); - scanner.process (edge_check, d, db::box_convert ()); - - return result; -} - -size_t -Edges::size () const -{ - if (! has_valid_edges ()) { - // If we have an iterator, we have to do it the hard way .. - size_t n = 0; - for (const_iterator e = begin (); ! e.at_end (); ++e) { - ++n; - } - return n; - } else { - return m_edges.size (); - } -} - -void -Edges::set_merged_semantics (bool f) -{ - if (f != m_merged_semantics) { - m_merged_semantics = f; - m_merged_edges.clear (); - m_merged_edges_valid = false; - } + set_delegate (new EmptyEdges ()); } void -Edges::set_valid_edges () +Edges::reserve (size_t n) { - m_iter = db::RecursiveShapeIterator (); + flat_edges ()->reserve (n); } -void -Edges::ensure_valid_edges () const +void Edges::processed (Region &output, const EdgeToPolygonProcessorBase &filter) const { - if (! has_valid_edges ()) { + output.set_delegate (mp_delegate->processed_to_polygons (filter)); +} - m_edges.clear (); +void Edges::extended (Region &output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const +{ + output.set_delegate (mp_delegate->extended (ext_b, ext_e, ext_o, ext_i, join)); +} - size_t n = 0; - for (const_iterator e = begin (); ! e.at_end (); ++e) { - ++n; +Edges Edges::start_segments (length_type length, double fraction) const +{ + return Edges (mp_delegate->processed (EdgeSegmentSelector (-1, length, fraction))); +} + +Edges Edges::end_segments (length_type length, double fraction) const +{ + return Edges (mp_delegate->processed (EdgeSegmentSelector (1, length, fraction))); +} + +Edges Edges::centers (length_type length, double fraction) const +{ + return Edges (mp_delegate->processed (EdgeSegmentSelector (0, length, fraction))); +} + +template +Edges &Edges::transform (const T &trans) +{ + flat_edges ()->transform (trans); + return *this; +} + +// explicit instantiations +template DB_PUBLIC Edges &Edges::transform (const db::ICplxTrans &); +template DB_PUBLIC Edges &Edges::transform (const db::Trans &); +template DB_PUBLIC Edges &Edges::transform (const db::Disp &); + +template +void Edges::insert (const Sh &shape) +{ + flat_edges ()->insert (shape); +} + +template DB_PUBLIC void Edges::insert (const db::Box &); +template DB_PUBLIC void Edges::insert (const db::SimplePolygon &); +template DB_PUBLIC void Edges::insert (const db::Polygon &); +template DB_PUBLIC void Edges::insert (const db::Path &); +template DB_PUBLIC void Edges::insert (const db::Edge &); + +void Edges::insert (const db::Shape &shape) +{ + flat_edges ()->insert (shape); +} + +template +void Edges::insert (const db::Shape &shape, const T &trans) +{ + flat_edges ()->insert (shape, trans); +} + +template DB_PUBLIC void Edges::insert (const db::Shape &, const db::ICplxTrans &); +template DB_PUBLIC void Edges::insert (const db::Shape &, const db::Trans &); +template DB_PUBLIC void Edges::insert (const db::Shape &, const db::Disp &); + +FlatEdges * +Edges::flat_edges () +{ + FlatEdges *edges = dynamic_cast (mp_delegate); + if (! edges) { + edges = new FlatEdges (); + if (mp_delegate) { + edges->EdgesDelegate::operator= (*mp_delegate); + edges->insert_seq (begin ()); } - m_edges.reserve (db::Edge::tag (), n); - - for (const_iterator e = begin (); ! e.at_end (); ++e) { - m_edges.insert (*e); - } - - m_iter = db::RecursiveShapeIterator (); - + set_delegate (edges); } + + return edges; } -} // namespace db +} namespace tl { template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::Edges &b) { - db::Edge e; + db::Edge p; - if (! ex.try_read (e)) { + if (! ex.try_read (p)) { return false; } - b.insert (e); + b.insert (p); while (ex.test (";")) { - ex.read (e); - b.insert (e); + ex.read (p); + b.insert (p); } return true; @@ -1362,9 +290,8 @@ namespace tl template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::Edges &b) { if (! test_extractor_impl (ex, b)) { - ex.error (tl::to_string (tr ("Expected an edge collection specification"))); + ex.error (tl::to_string (tr ("Expected an edge set specification"))); } } } - diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 660a78ad5..84de5a6b4 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -20,173 +20,104 @@ */ - #ifndef HDR_dbEdges #define HDR_dbEdges #include "dbCommon.h" - -#include "dbTypes.h" -#include "dbEdge.h" -#include "dbTrans.h" -#include "dbShape.h" -#include "dbShapes.h" -#include "dbShapes2.h" -#include "dbEdgePairRelations.h" -#include "dbEdgePairs.h" +#include "dbEdgesDelegate.h" #include "dbRecursiveShapeIterator.h" -#include "tlString.h" +#include "dbCellVariants.h" + +#include "gsiObject.h" + +#include namespace db { -class Edges; +class EdgeFilterBase; +class FlatEdges; +class EmptyEdges; +class DeepShapeStore; /** - * @brief An edge length filter for use with Edges::filter or Edges::filtered + * @brief An edge set iterator * - * This filter has two parameters: lmin and lmax. - * It will filter all edges for which the length is >= lmin and < lmax. - * There is an "invert" flag which allows to select all edges not - * matching the criterion. + * The iterator delivers the edges of the edge set */ - -struct DB_PUBLIC EdgeLengthFilter +class DB_PUBLIC EdgesIterator { - typedef db::Edge::distance_type length_type; +public: + typedef EdgesIteratorDelegate::value_type value_type; + typedef const value_type &reference; + typedef const value_type *pointer; + typedef std::forward_iterator_tag iterator_category; + typedef void difference_type; /** - * @brief Constructor - * - * @param lmin The minimum length - * @param lmax The maximum length - * @param inverse If set to true, only polygons not matching this criterion will be filtered + * @brief Default constructor */ - EdgeLengthFilter (length_type lmin, length_type lmax, bool inverse) - : m_lmin (lmin), m_lmax (lmax), m_inverse (inverse) + EdgesIterator () + : mp_delegate (0) { // .. nothing yet .. } /** - * @brief Returns true if the edge length matches the criterion + * @brief Constructor from a delegate + * The iterator will take ownership over the delegate */ - bool operator() (const db::Edge &edge) const + EdgesIterator (EdgesIteratorDelegate *delegate) + : mp_delegate (delegate) { - length_type l = edge.length (); - if (! m_inverse) { - return l >= m_lmin && l < m_lmax; - } else { - return ! (l >= m_lmin && l < m_lmax); + // .. nothing yet .. + } + + /** + * @brief Destructor + */ + ~EdgesIterator () + { + delete mp_delegate; + mp_delegate = 0; + } + + /** + * @brief Copy constructor and assignment + */ + EdgesIterator (const EdgesIterator &other) + : mp_delegate (0) + { + operator= (other); + } + + /** + * @brief Assignment + */ + EdgesIterator &operator= (const EdgesIterator &other) + { + if (this != &other) { + delete mp_delegate; + mp_delegate = other.mp_delegate ? other.mp_delegate->clone () : 0; } + return *this; } -private: - length_type m_lmin, m_lmax; - bool m_inverse; -}; - -/** - * @brief An edge orientation filter for use with Edges::filter or Edges::filtered - * - * This filter has two parameters: amin and amax. - * It will filter all edges for which the orientation angle is >= amin and < amax. - * The orientation angle is measured in degree against the x axis in the mathematical sense. - * There is an "invert" flag which allows to select all edges not - * matching the criterion. - */ - -struct DB_PUBLIC EdgeOrientationFilter -{ - /** - * @brief Constructor - * - * @param amin The minimum angle (measured against the x axis) - * @param amax The maximum angle (measured against the x axis) - * @param inverse If set to true, only polygons not matching this criterion will be filtered - * - * This filter will filter out all edges whose angle against x axis - * is larger or equal to amin and less than amax. - */ - EdgeOrientationFilter (double amin, double amax, bool inverse) - : m_inverse (inverse), m_exact (false) - { - m_emin = db::DVector (cos (amin * M_PI / 180.0), sin (amin * M_PI / 180.0)); - m_emax = db::DVector (cos (amax * M_PI / 180.0), sin (amax * M_PI / 180.0)); - } - - /** - * @brief Constructor - * - * @param a The angle (measured against the x axis) - * @param inverse If set to true, only polygons not matching this criterion will be filtered - * - * This filter will filter out all edges whose angle against x axis - * is equal to a. - */ - EdgeOrientationFilter (double a, bool inverse) - : m_inverse (inverse), m_exact (true) - { - m_emin = db::DVector (cos (a * M_PI / 180.0), sin (a * M_PI / 180.0)); - } - - /** - * @brief Returns true if the edge orientation matches the criterion - */ - bool operator() (const db::Edge &edge) const - { - int smin = db::vprod_sign (m_emin, db::DVector (edge.d ())); - if (m_exact) { - if (! m_inverse) { - return smin == 0; - } else { - return smin != 0; - } - } else { - int smax = db::vprod_sign (m_emax, db::DVector (edge.d ())); - if (! m_inverse) { - return (smin >= 0 && smax < 0) || (smax > 0 && smin <= 0); - } else { - return ! ((smin >= 0 && smax < 0) || (smax > 0 && smin <= 0)); - } - } - } - -private: - db::DVector m_emin, m_emax; - bool m_inverse; - bool m_exact; -}; - -/** - * @brief A edge collection iterator - * - * The iterator delivers the edges of the edge collection - */ - -class DB_PUBLIC EdgesIterator -{ -public: - typedef db::Edge value_type; - typedef const db::Edge &reference; - typedef const db::Edge *pointer; - typedef std::forward_iterator_tag iterator_category; - typedef void difference_type; - /** * @Returns true, if the iterator is at the end */ bool at_end () const { - return m_from == m_to && m_rec_iter.at_end (); + return mp_delegate == 0 || mp_delegate->at_end (); } /** * @brief Increment */ - EdgesIterator &operator++ () + EdgesIterator &operator++ () { - inc (); - set (); + if (mp_delegate) { + mp_delegate->increment (); + } return *this; } @@ -195,11 +126,9 @@ public: */ reference operator* () const { - if (m_rec_iter.at_end ()) { - return *m_from; - } else { - return m_edge; - } + const value_type *value = operator-> (); + tl_assert (value != 0); + return *value; } /** @@ -207,72 +136,70 @@ public: */ pointer operator-> () const { - if (m_rec_iter.at_end ()) { - return &*m_from; + return mp_delegate ? mp_delegate->get () : 0; + } + +private: + EdgesIteratorDelegate *mp_delegate; +}; + +/** + * @brief A helper class allowing delivery of addressable edges + * + * In some applications (i.e. box scanner), edges need to be taken + * by address. The edge set cannot always deliver adressable edges. + * This class help providing this ability by keeping a temporary copy + * if required. + */ + +class DB_PUBLIC AddressableEdgeDelivery +{ +public: + AddressableEdgeDelivery () + : m_iter (), m_valid (false) + { + // .. nothing yet .. + } + + AddressableEdgeDelivery (const EdgesIterator &iter, bool valid) + : m_iter (iter), m_valid (valid) + { + if (! m_valid && ! m_iter.at_end ()) { + m_heap.push_back (*m_iter); + } + } + + bool at_end () const + { + return m_iter.at_end (); + } + + AddressableEdgeDelivery &operator++ () + { + ++m_iter; + if (! m_valid && ! m_iter.at_end ()) { + m_heap.push_back (*m_iter); + } + return *this; + } + + const db::Edge *operator-> () const + { + if (m_valid) { + return m_iter.operator-> (); } else { - return &m_edge; + return &m_heap.back (); } } private: - friend class Edges; - - typedef db::layer edge_layer_type; - typedef edge_layer_type::iterator iterator_type; - - db::RecursiveShapeIterator m_rec_iter; - db::ICplxTrans m_iter_trans; - db::Edge m_edge; - iterator_type m_from, m_to; - - /** - * @brief ctor from a recursive shape iterator - */ - EdgesIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) - : m_rec_iter (iter), m_iter_trans (trans), m_from (), m_to () - { - // NOTE: the following initialization appears to be required on some compilers - // (specifically MacOS/clang) to ensure the proper initialization of the iterators - m_from = m_to; - set (); - } - - /** - * @brief ctor from a range of edges inside a vector - */ - EdgesIterator (iterator_type from, iterator_type to) - : m_from (from), m_to (to) - { - // no required yet: set (); - } - - /** - * @brief Establish the iterator at the current position - */ - void set () - { - while (! m_rec_iter.at_end () && ! m_rec_iter.shape ().is_edge ()) { - inc (); - } - if (! m_rec_iter.at_end ()) { - m_rec_iter.shape ().edge (m_edge); - m_edge.transform (m_iter_trans * m_rec_iter.trans ()); - } - } - - /** - * @brief Increment the iterator - */ - void inc () - { - if (! m_rec_iter.at_end ()) { - ++m_rec_iter; - } else { - ++m_from; - } - } + EdgesIterator m_iter; + bool m_valid; + std::list m_heap; }; +class Edges; + /** * @brief An edge set * @@ -287,7 +214,8 @@ private: * Such edges are basically points which have some applications, i.e. as markers for certain locations. */ -class DB_PUBLIC Edges +class DB_PUBLIC Edges + : public gsi::ObjectBase { public: typedef db::Coord coord_type; @@ -296,48 +224,105 @@ public: typedef db::Vector vector_type; typedef db::Point point_type; typedef db::Box box_type; - typedef coord_traits::distance_type distance_type; - typedef db::Edge::distance_type length_type; + typedef coord_traits::distance_type length_type; + typedef coord_traits::distance_type distance_type; typedef EdgesIterator const_iterator; - enum BoolOp { Or, Not, Xor, And }; - /** + /** * @brief Default constructor * - * This constructor creates an empty edge set. + * Creates an empty edge set. */ - Edges () - : m_edges (false), m_merged_edges (false) + Edges (); + + /** + * @brief Destructor + */ + ~Edges (); + + /** + * @brief Constructor from a delegate + * + * The region will take ownership of the delegate. + */ + Edges (EdgesDelegate *delegate); + + /** + * @brief Copy constructor + */ + Edges (const Edges &other); + + /** + * @brief Assignment + */ + Edges &operator= (const Edges &other); + + /** + * @brief Constructor from a box + * + * Creates an edge set representing the contour of the box + */ + explicit Edges (const db::Box &s) + : mp_delegate (0) { - init (); + insert (s); } /** - * @brief Constructor from an object + * @brief Constructor from a simple polygon * - * Creates a region representing a single instance of that object. - * The object is converted to a polygon and the edges of that polygon are inserted. + * Creates an edge set representing the contour of the polygon */ - template - Edges (const Sh &s) - : m_edges (false), m_merged_edges (false) + explicit Edges (const db::SimplePolygon &s) + : mp_delegate (0) + { + insert (s); + } + + /** + * @brief Constructor from a polygon + * + * Creates an edge set representing the contour of the polygon + */ + explicit Edges (const db::Polygon &s) + : mp_delegate (0) + { + insert (s); + } + + /** + * @brief Constructor from a path + * + * Creates an edge set representing the contour of the path + */ + explicit Edges (const db::Path &s) + : mp_delegate (0) + { + insert (s); + } + + /** + * @brief Constructor from an edge + * + * Creates an edge set representing the single edge + */ + explicit Edges (const db::Edge &s) + : mp_delegate (0) { - init (); insert (s); } /** * @brief Sequence constructor * - * Creates a region from a sequence of objects. The objects can be edges, boxes, - * polygons, paths or shapes. This version accepts iterators of the begin ... end + * Creates an edge set from a sequence of objects. The objects can be boxes, + * polygons, paths, edges or shapes. This version accepts iterators of the begin ... end * style. */ template - Edges (const Iter &b, const Iter &e) - : m_edges (false), m_merged_edges (false) + explicit Edges (const Iter &b, const Iter &e) + : mp_delegate (0) { - init (); reserve (e - b); for (Iter i = b; i != e; ++i) { insert (*i); @@ -347,39 +332,78 @@ public: /** * @brief Constructor from a RecursiveShapeIterator * - * Creates a region from a recursive shape iterator. This allows to feed an edge set + * Creates an edge set from a recursive shape iterator. This allows to feed an edge set * from a hierarchy of cells. - * - * If as_edges is false, only edges will be taken from the recursive shape iterator. - * That is somewhat more efficient since it can avoid a copy in some cases. If as_edges - * is false, shapes will be converted to edges. */ - Edges (const RecursiveShapeIterator &si, bool as_edges = true); + explicit Edges (const RecursiveShapeIterator &si, bool as_edges = true); /** * @brief Constructor from a RecursiveShapeIterator with a transformation * - * Creates a region from a recursive shape iterator. This allows to feed an edge set + * Creates an edge set from a recursive shape iterator. This allows to feed an edge set * from a hierarchy of cells. The transformation is useful to scale to a specific * DBU for example. - * - * If as_edges is true, only edges will be taken from the recursive shape iterator. - * That is somewhat more efficient since it can avoid a copy in some cases. If as_edges - * is false, shapes will be converted to edges. */ - Edges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool as_edges = true, bool merged_semantics = true); + explicit Edges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool as_edges = true, bool merged_semantics = true); + + /** + * @brief Constructor from a RecursiveShapeIterator providing a deep representation + * + * This version will create a hierarchical edge collection. The DeepShapeStore needs to be provided + * during the lifetime of the collection and acts as a heap for optimized data. + */ + explicit Edges (const RecursiveShapeIterator &si, DeepShapeStore &dss, bool as_edges = true); + + /** + * @brief Constructor from a RecursiveShapeIterator providing a deep representation with transformation + */ + explicit Edges (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool as_edges = true, bool merged_semantics = true); + + /** + * @brief Gets the underlying delegate object + */ + EdgesDelegate *delegate () const + { + return mp_delegate; + } + + /** + * @brief Sets the base verbosity + * + * Setting this value will make timing measurements appear at least at + * the given verbosity level and more detailed timing at the given level + * plus 10. The default level is 30. + */ + void set_base_verbosity (int vb) + { + mp_delegate->set_base_verbosity (vb); + } + + /** + * @brief Gets the base verbosity + */ + unsigned int base_verbosity () const + { + return mp_delegate->base_verbosity (); + } /** * @brief Enable progress reporting * * @param progress_text The description text of the progress object */ - void enable_progress (const std::string &progress_desc = std::string ()); + void enable_progress (const std::string &desc = std::string ()) + { + mp_delegate->enable_progress (desc); + } /** * @brief Disable progress reporting */ - void disable_progress (); + void disable_progress () + { + mp_delegate->disable_progress (); + } /** * @brief Iterator of the edge set @@ -389,157 +413,136 @@ public: */ const_iterator begin () const { - if (has_valid_edges ()) { - return const_iterator (m_edges.get_layer ().begin (), m_edges.get_layer ().end ()); - } else { - return const_iterator (m_iter, m_iter_trans); - } + return EdgesIterator (mp_delegate->begin ()); } /** - * @brief Returns the merged edges if merge semantics applies + * @brief Returns the merged edges if merge semantics applies * * If merge semantics is not enabled, this iterator delivers the individual edges. */ - const_iterator begin_merged () const; + const_iterator begin_merged () const + { + return EdgesIterator (mp_delegate->begin_merged ()); + } /** * @brief Delivers a RecursiveShapeIterator pointing to the edges plus the necessary transformation */ - std::pair begin_iter () const; + std::pair begin_iter () const + { + return mp_delegate->begin_iter (); + } /** * @brief Delivers a RecursiveShapeIterator pointing to the merged edges plus the necessary transformation */ - std::pair begin_merged_iter () const; - - /** - * @brief Insert an edge into the edge set - */ - void insert (const db::Edge &edge); - - /** - * @brief Insert a box into the edge set - * - * This method will insert all edges the box is composed of. - */ - void insert (const db::Box &box); - - /** - * @brief Insert a path into the edge set - * - * This method will insert all edges the path is composed of. - */ - void insert (const db::Path &path); - - /** - * @brief Insert a simple polygon into the edge set - * - * This method will insert all edges the polygon is composed of. - */ - void insert (const db::SimplePolygon &polygon); - - /** - * @brief Insert a polygon into the edge set - * - * This method will insert all edges the polygon is composed of. - */ - void insert (const db::Polygon &polygon); - - /** - * @brief Insert a shape into the region - * - * If the shape is a polygon-type, the shape is converted to a - * polygon and it's edges are inserted into the edge set. - * If the shape is an edge, the edge is inserted into the edge set. - */ - void insert (const db::Shape &shape) + std::pair begin_merged_iter () const { - if (shape.is_edge ()) { - insert (shape.edge ()); - } else if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - db::Polygon polygon; - shape.polygon (polygon); - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - insert (*e); - } - } + return mp_delegate->begin_merged_iter (); } + /** + * @brief Inserts the given shape (working object) into the edge set + */ + template + void insert (const Sh &shape); + + /** + * @brief Insert a shape reference into the edge set + */ + void insert (const db::Shape &shape); + /** * @brief Insert a transformed shape into the edge set */ template - void insert (const db::Shape &shape, const T &trans) - { - if (shape.is_edge ()) { - insert (edge_type (trans * shape.edge ())); - } else if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - db::Polygon polygon; - shape.polygon (polygon); - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - insert (edge_type (trans * *e)); - } - } - } + void insert (const db::Shape &shape, const T &trans); /** - * @brief Returns true if the region is empty + * @brief Returns true if the edge set is empty */ bool empty () const { - return has_valid_edges () && m_edges.empty (); + return mp_delegate->empty (); } /** - * @brief Returns the number of polygons in the region + * @brief Returns the number of edges in the edge set */ - size_t size () const; + size_t size () const + { + return mp_delegate->size (); + } /** - * @brief Returns a string representing the region + * @brief Returns a string representing the edge set * - * nmax specifies how many polygons are included (set to std::numeric_limits::max() for "all". + * nmax specifies how many edges are included (set to std::numeric_limits::max() for "all". */ - std::string to_string (size_t nmax = 10) const; + std::string to_string (size_t nmax = 10) const + { + return mp_delegate->to_string (nmax); + } /** - * @brief Clear the edge set + * @brief Clears the edge set */ void clear (); /** * @brief Reserve memory for the given number of edges */ - void reserve (size_t n) - { - m_edges.reserve (db::Edge::tag (), n); - } + void reserve (size_t n); /** * @brief Sets the merged-semantics flag * - * If merged semantics is enabled (the default), coherent polygons will be considered - * as single regions and artificial edges such as cut-lines will not be considered. - * Merged semantics thus is equivalent to considering coherent areas rather than - * single polygons. + * If merged semantics is enabled (the default), colinear edges will be considered + * as single edges. */ - void set_merged_semantics (bool f); + void set_merged_semantics (bool f) + { + mp_delegate->set_merged_semantics (f); + } /** * @brief Gets the merged-semantics flag */ bool merged_semantics () const { - return m_merged_semantics; + return mp_delegate->merged_semantics (); } /** - * @brief Returns true if the region is merged + * @brief Enables or disables strict handling + * + * Strict handling means to leave away some optimizations. Specifically the + * output of boolean operations will be merged even if one input is empty. + * Without strict handling, the operation will be optimized and output + * won't be merged. + * + * Strict handling is disabled by default. + */ + void set_strict_handling (bool f) + { + mp_delegate->set_strict_handling (f); + } + + /** + * @brief Gets a valid indicating whether strict handling is enabled + */ + bool strict_handling () const + { + return mp_delegate->strict_handling (); + } + + /** + * @brief Returns true if the edge set is merged */ bool is_merged () const { - return m_is_merged; + return mp_delegate->is_merged (); } /** @@ -550,88 +553,224 @@ public: * If a box is given, the computation is restricted to that box. * Edges coincident with the box edges are counted only if the form outer edges at the box edge. */ - length_type length (const db::Box &box = db::Box ()) const; - - /** - * @brief Returns the bounding box of the region - */ - Box bbox () const + length_type length (const db::Box &box = db::Box ()) const { - ensure_bbox_valid (); - return m_bbox; + return mp_delegate->length (box); } /** - * @brief Filters the edge set + * @brief Returns the bounding box of the edge set + */ + Box bbox () const + { + return mp_delegate->bbox (); + } + + /** + * @brief Filters the edges * * This method will keep all edges for which the filter returns true. - * Merged semantics applies. + * Merged semantics applies. In merged semantics, the filter will run over + * all merged edges. */ - template - Edges &filter (F &filter) + Edges &filter (const EdgeFilterBase &filter) { - edge_iterator_type ew = m_edges.get_layer ().begin (); - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - if (filter (*e)) { - if (ew == m_edges.get_layer ().end ()) { - m_edges.get_layer ().insert (*e); - ew = m_edges.get_layer ().end (); - } else { - m_edges.get_layer ().replace (ew++, *e); - } - } - } - m_edges.get_layer ().erase (ew, m_edges.get_layer ().end ()); - m_merged_edges.clear (); - m_is_merged = m_merged_semantics; - m_iter = db::RecursiveShapeIterator (); + set_delegate (mp_delegate->filter_in_place (filter)); return *this; } /** * @brief Returns the filtered edges * - * This method will return a new region with only those edges which + * This method will return a new region with only those edges which * conform to the filter criterion. - * Merged semantics applies. */ - template - Edges filtered (F &filter) const + Edges filtered (const EdgeFilterBase &filter) const { - Edges d; - for (const_iterator e = begin_merged (); ! e.at_end (); ++e) { - if (filter (*e)) { - d.insert (*e); - } - } - return d; + return Edges (mp_delegate->filtered (filter)); } /** - * @brief Returns all edges found in the other edge collection as well + * @brief Processes the (merged) edges * - * If "invert" is true, all edges not found in the other collection - * are returned. + * This method will keep all edges which the processor returns. + * The processing filter can apply modifications too. These modifications will be + * kept in the output edge collection. + * + * Merged semantics applies. In merged semantics, the filter will run over + * all merged edges. */ - Edges in (const Edges &other, bool invert) const; - - /** - * @brief Transform the edge set - */ - template - Edges &transform (const T &trans) + Edges &process (const EdgeProcessorBase &filter) { - if (! trans.is_unity ()) { - ensure_valid_edges (); - for (edge_iterator_type e = m_edges.get_layer ().begin (); e != m_edges.get_layer ().end (); ++e) { - m_edges.get_layer ().replace (e, e->transformed (trans)); - } - m_iter_trans = db::ICplxTrans (trans) * m_iter_trans; - m_bbox_valid = false; - } + set_delegate (mp_delegate->process_in_place (filter)); return *this; } + /** + * @brief Returns the processed edges + * + * This method will keep all edges which the processor returns. + * The processing filter can apply modifications too. These modifications will be + * kept in the output edge collection. + * + * Merged semantics applies. In merged semantics, the filter will run over + * all merged edges. + * + * This method will return a new edge collection with the modified and filtered edges. + */ + Edges processed (const EdgeProcessorBase &filter) const + { + return Edges (mp_delegate->processed (filter)); + } + + /** + * @brief Processes the edges into polygons + * + * This method will run the processor over all edges and return a region + * with the outputs of the processor. + * + * Merged semantics applies. In merged semantics, the filter will run over + * all merged edges. + */ + void processed (Region &output, const EdgeToPolygonProcessorBase &filter) const; + + /** + * @brief Processes the edges into edge pairs + * + * This method will run the processor over all edges and return an edge pair collection + * with the outputs of the processor. + * + * Merged semantics applies. In merged semantics, the filter will run over + * all merged edges. + */ + EdgePairs processed (const EdgeToEdgePairProcessorBase &filter) const + { + return EdgePairs (mp_delegate->processed_to_edge_pairs (filter)); + } + + /** + * @brief Applies a width check and returns EdgePairs which correspond to violation markers + * + * The width check will create a edge pairs if the width of the area between the + * edges is less than the specified threshold d. Without "whole_edges", the parts of + * the edges are returned which violate the condition. If "whole_edges" is true, the + * result will contain the complete edges participating in the result. + * + * "Width" refers to the space between the "inside" sides of the edges. + * + * The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected" + * metrics are available. + * + * ignore_angle allows specification of a maximum angle the edges can have to not participate + * in the check. By choosing 90 degree, edges having an angle of 90 degree and larger are not checked, + * but acute corners are for example. + * + * With min_projection and max_projection it is possible to specify how edges must be related + * to each other. If the length of the projection of either edge on the other is >= min_projection + * or < max_projection, the edges are considered for the check. + * + * The order of the edges in the resulting edge pairs is undefined. + * + * Merged semantics applies. + */ + EdgePairs width_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return EdgePairs (mp_delegate->width_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); + } + + /** + * @brief Applies a space check and returns EdgePairs which correspond to violation markers + * + * "Space" refers to the space between the "outside" sides of the edges. + * + * For the parameters see \width_check. The space check reports edges for which the space is + * less than the specified threshold d. + * + * Merged semantics applies. + */ + EdgePairs space_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return EdgePairs (mp_delegate->space_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); + } + + /** + * @brief Applies an enclosing check and returns EdgePairs which correspond to violation markers + * + * The check will return true for edges from this edge set and the other edge set, where the other edge + * is located on the "inside" side of the edge from this edge set, the orientation is parallel + * and the distance is less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs enclosing_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return EdgePairs (mp_delegate->enclosing_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); + } + + /** + * @brief Applies an overlap check and returns EdgePairs which correspond to violation markers + * + * The check will return true for edges from this edge set and the other edge set, where the other edge + * is located on the "inside" side of the edge from this edge set, the orientation is anti-parallel + * and the distance is less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs overlap_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return EdgePairs (mp_delegate->overlap_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); + } + + /** + * @brief Applies an separation check and returns EdgePairs which correspond to violation markers + * + * The check will return true for edges from this edge set and the other edge set, where the other edge + * is located on the "outside" side of the edge from this edge set, the orientation is anti-parallel + * and the distance is less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs separation_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return EdgePairs (mp_delegate->separation_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); + } + + /** + * @brief Applies a inside check and returns EdgePairs which correspond to violation markers + * + * The check will return true for edges from this edge set and the other edge set, where the other edge + * is located on the "outide" side of the edge from this edge set, the orientation is parallel + * and the distance is less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs inside_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return EdgePairs (mp_delegate->inside_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); + } + + /** + * @brief Transforms the edge set + */ + template + Edges &transform (const T &trans); + /** * @brief Returns the transformed edge set */ @@ -644,17 +783,180 @@ public: } /** - * @brief Swap with the other region + * @brief Swaps with the other edge set */ - void swap (db::Edges &other); + void swap (db::Edges &other) + { + std::swap (other.mp_delegate, mp_delegate); + } + + /** + * @brief Merges the edge set + * + * This method merges the edges of the edge set if they are not merged already. + * It returns a reference to this edge set. + * An out-of-place merge version is "merged". + */ + Edges &merge () + { + set_delegate (mp_delegate->merged_in_place ()); + return *this; + } + + /** + * @brief Returns the merged edge set + * + * This is the out-of-place merge. It returns a new edge set but does not modify + * the edge set it is called on. An in-place version is "merge". + */ + Edges merged () const + { + return Edges (mp_delegate->merged ()); + } + + /** + * @brief Boolean AND operator + */ + Edges operator& (const Edges &other) const + { + return Edges (mp_delegate->and_with (other)); + } + + /** + * @brief In-place boolean AND operator + * + * This method does not necessarily merge the edge set. To ensure the edge set + * is merged, call merge afterwards. + */ + Edges &operator&= (const Edges &other) + { + set_delegate (mp_delegate->and_with (other)); + return *this; + } + + /** + * @brief Boolean AND operator with a region + */ + Edges operator& (const Region &other) const + { + return Edges (mp_delegate->and_with (other)); + } + + /** + * @brief In-place boolean AND operator with a region + * + * This method will keep all edges inside the given region. + */ + Edges &operator&= (const Region &other) + { + set_delegate (mp_delegate->and_with (other)); + return *this; + } + + /** + * @brief Boolean NOT operator + */ + Edges operator- (const Edges &other) const + { + return Edges (mp_delegate->not_with (other)); + } + + /** + * @brief In-place boolean NOT operator + * + * This method does not necessarily merge the edge set. To ensure the edge set + * is merged, call merge afterwards. + */ + Edges &operator-= (const Edges &other) + { + set_delegate (mp_delegate->not_with (other)); + return *this; + } + + /** + * @brief Boolean NOT operator with a region + */ + Edges operator- (const Region &other) const + { + return Edges (mp_delegate->not_with (other)); + } + + /** + * @brief In-place boolean NOT operator with a region + * + * This method will remove all edges inside the given region. + */ + Edges &operator-= (const Region &other) + { + set_delegate (mp_delegate->not_with (other)); + return *this; + } + + /** + * @brief Boolean XOR operator + */ + Edges operator^ (const Edges &other) const + { + return Edges (mp_delegate->xor_with (other)); + } + + /** + * @brief In-place boolean XOR operator + * + * This method does not necessarily merge the edge set. To ensure the edge set + * is merged, call merge afterwards. + */ + Edges &operator^= (const Edges &other) + { + set_delegate (mp_delegate->xor_with (other)); + return *this; + } + + /** + * @brief Boolean OR operator + * + * This method merges the edges of both edge sets. + */ + Edges operator| (const Edges &other) const + { + return Edges (mp_delegate->or_with (other)); + } + + /** + * @brief In-place boolean OR operator + */ + Edges &operator|= (const Edges &other) + { + set_delegate (mp_delegate->or_with (other)); + return *this; + } + + /** + * @brief Joining of edge set + * + * This method joins the edge sets but does not merge them afterwards. + */ + Edges operator+ (const Edges &other) const + { + return Edges (mp_delegate->add (other)); + } + + /** + * @brief In-place edge set joining + */ + Edges &operator+= (const Edges &other) + { + set_delegate (mp_delegate->add_in_place (other)); + return *this; + } /** * @brief returns the extended edges * - * Edges are extended by creating a rectangle on each edge. The rectangle is constructed on the - * edge by applying the extensions given by ext_o, ext_b, ext_e, ext_i at the outside, the + * Edges are extended by creating a rectangle on each edge. The rectangle is constructed on the + * edge by applying the extensions given by ext_o, ext_b, ext_e, ext_i at the outside, the * beginning, the end and the inside. - * If the edge is laid flat pointing from left to right, the outside is at the top, the inside + * If the edge is laid flat pointing from left to right, the outside is at the top, the inside * is at the bottom. * * For degenerated edges with length 0, the orientation is assumed to the horizontal. The extended @@ -662,7 +964,7 @@ public: * and ext_e to the right. * * If the joined parameter is set to true, adjacent edges are joined before the extension is applied. - * A the join points, the extension is created similar to what the sizing function does. + * A the join points, the extension is created similar to what the sizing function does. * * Note: the output is given as an out parameter since because of the include hierarchy we can't use * Region as a return value directly. @@ -676,9 +978,9 @@ public: * * The length of the part can be choosen by length or a fraction of the original length. * If length and fraction are 0, a point at the beginning of the edge will be created. - * If length is non-zero and fraction is 0, a segment in the direction of the edge + * If length is non-zero and fraction is 0, a segment in the direction of the edge * with the given length is created, even if the length is larger than the original - * edge. + * edge. * * If fraction is given and length is 0, the segment will have a length which is the specified * fraction of the original edge. @@ -712,153 +1014,6 @@ public: */ Edges centers (length_type length, double fraction) const; - /** - * @brief Boolean AND operator - * - * This operation returns the parts of the edges which coincide with edges from "other" - * After this operation the edges are not necessarily merged. - */ - Edges operator& (const Edges &other) const - { - return boolean (&other, And); - } - - /** - * @brief In-place boolean AND operator - */ - Edges &operator&= (const Edges &other) - { - inplace_boolean (&other, And); - return *this; - } - - /** - * @brief Boolean AND operator with a region - * - * This operation returns the parts of the edges which are inside the given region. - * Edges on the borders of the polygons are included in the edge set. - * As a side effect, the edges are made non-intersecting by introducing cut points where - * edges intersect. - */ - Edges operator& (const Region &other) const - { - Edges d (*this); - d &= other; - return d; - } - - /** - * @brief In-place boolean AND operator with a region - */ - Edges &operator&= (const Region &other) - { - edge_region_op (other, false /*inside*/, true /*include borders*/); - return *this; - } - - /** - * @brief Boolean NOT operator - * - * This operation returns the parts of the edges which do not coincide with edges from "other" - * After this operation the edges are not necessarily merged. - */ - Edges operator- (const Edges &other) const - { - return boolean (&other, Not); - } - - /** - * @brief In-place boolean NOT operator - */ - Edges &operator-= (const Edges &other) - { - inplace_boolean (&other, Not); - return *this; - } - - /** - * @brief Boolean NOT operator with a region - * - * This operation returns the parts of the edges which are outside the given region. - * Edges on the borders of the polygons are removed from the edge set. - * As a side effect, the edges are made non-intersecting by introducing cut points where - * edges intersect. - */ - Edges operator- (const Region &other) const - { - Edges d (*this); - d -= other; - return d; - } - - /** - * @brief In-place boolean NOT operator with a region - */ - Edges &operator-= (const Region &other) - { - edge_region_op (other, true /*outside*/, true /*include borders*/); - return *this; - } - - /** - * @brief Boolean XOR operator - * - * This operation returns the parts of the edges which do not coincide with edges from "other" - * and vice versa. - * After this operation the edges are not necessarily merged. - */ - Edges operator^ (const Edges &other) const - { - return boolean (&other, Xor); - } - - /** - * @brief In-place boolean XOR operator - */ - Edges &operator^= (const Edges &other) - { - inplace_boolean (&other, Xor); - return *this; - } - - /** - * @brief Joining of edge sets - * - * This method will combine the edges from "other" with the egdes of "this". - * After this operation the edges are not necessarily merged. - */ - Edges operator+ (const Edges &other) const - { - Edges d (*this); - d += other; - return d; - } - - /** - * @brief In-place joining of edge sets - */ - Edges &operator+= (const Edges &other); - - /** - * @brief Boolean OR operator - * - * This method will combine the edges from "other" with the egdes of "this". - * After this operation the edges are usually merged. - */ - Edges operator| (const Edges &other) const - { - return boolean (&other, Or); - } - - /** - * @brief In-place boolean OR operator - */ - Edges &operator|= (const Edges &other) - { - inplace_boolean (&other, Or); - return *this; - } - /** * @brief Select the edges inside the given region * @@ -869,7 +1024,7 @@ public: */ Edges &select_inside_part (const Region &other) { - edge_region_op (other, false /*inside*/, false /*don't include borders*/); + set_delegate (mp_delegate->inside_part (other)); return *this; } @@ -880,9 +1035,7 @@ public: */ Edges inside_part (const Region &other) const { - Edges d (*this); - d.select_inside_part (other); - return d; + return Edges (mp_delegate->inside_part (other)); } /** @@ -895,7 +1048,7 @@ public: */ Edges &select_outside_part (const Region &other) { - edge_region_op (other, true /*outside*/, false /*don't include borders*/); + set_delegate (mp_delegate->outside_part (other)); return *this; } @@ -906,9 +1059,7 @@ public: */ Edges outside_part (const Region &other) const { - Edges d (*this); - d.select_outside_part (other); - return d; + return Edges (mp_delegate->outside_part (other)); } /** @@ -917,7 +1068,11 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_interacting (const Region &other); + Edges &select_interacting (const Region &other) + { + set_delegate (mp_delegate->selected_interacting (other)); + return *this; + } /** * @brief Returns all edges of this edge set which overlap or touch with polygons from the region @@ -926,9 +1081,7 @@ public: */ Edges selected_interacting (const Region &other) const { - Edges d (*this); - d.select_interacting (other); - return d; + return Edges (mp_delegate->selected_interacting (other)); } /** @@ -937,7 +1090,11 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_not_interacting (const Region &other); + Edges &select_not_interacting (const Region &other) + { + set_delegate (mp_delegate->selected_not_interacting (other)); + return *this; + } /** * @brief Returns all edges of this edge set which do not overlap or touch with polygons from the region @@ -946,9 +1103,7 @@ public: */ Edges selected_not_interacting (const Region &other) const { - Edges d (*this); - d.select_not_interacting (other); - return d; + return Edges (mp_delegate->selected_not_interacting (other)); } /** @@ -957,7 +1112,11 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_interacting (const Edges &other); + Edges &select_interacting (const Edges &other) + { + set_delegate (mp_delegate->selected_interacting (other)); + return *this; + } /** * @brief Returns all edges of this edge set which overlap or touch with edges from the other edge set @@ -966,9 +1125,7 @@ public: */ Edges selected_interacting (const Edges &other) const { - Edges d (*this); - d.select_interacting (other); - return d; + return Edges (mp_delegate->selected_interacting (other)); } /** @@ -977,7 +1134,11 @@ public: * Merged semantics applies. If merged semantics is chosen, the connected edge parts will be * selected as a whole. */ - Edges &select_not_interacting (const Edges &other); + Edges &select_not_interacting (const Edges &other) + { + set_delegate (mp_delegate->selected_not_interacting (other)); + return *this; + } /** * @brief Returns all edges of this edge set which do not overlap or touch with edges from the other edge set @@ -986,234 +1147,136 @@ public: */ Edges selected_not_interacting (const Edges &other) const { - Edges d (*this); - d.select_not_interacting (other); - return d; + return Edges (mp_delegate->selected_not_interacting (other)); } /** - * @brief Merge the edge set + * @brief Returns all edges which are in the other edge set * - * This method merges the edges of the edge set if they are not merged already. - * It returns a reference to this edge set. - * Edges are merged by joining them if one edge is a continuation of another. - * An out-of-place merge version is "merged". - */ - Edges &merge () - { - if (! is_merged ()) { - inplace_boolean (0, Or); - } - return *this; - } - - /* - * @brief Returns the merged edge set - * - * This is the out-of-place merge. It returns a new edge set but does not modify - * the edge set it is called on. An in-place version is "merge". - */ - Edges merged () const - { - return boolean (0, Or); - } - - /** - * @brief Applies a width check and returns EdgePairs which correspond to violation markers - * - * The width check will create a edge pairs if the width of the area between the - * edges is less than the specified threshold d. Without "whole_edges", the parts of - * the edges are returned which violate the condition. If "whole_edges" is true, the - * result will contain the complete edges participating in the result. - * - * "Width" refers to the space between the "inside" sides of the edges. - * - * The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected" - * metrics are available. - * - * ignore_angle allows specification of a maximum angle the edges can have to not participate - * in the check. By choosing 90 degree, edges having an angle of 90 degree and larger are not checked, - * but acute corners are for example. - * - * With min_projection and max_projection it is possible to specify how edges must be related - * to each other. If the length of the projection of either edge on the other is >= min_projection - * or < max_projection, the edges are considered for the check. - * - * The order of the edges in the resulting edge pairs is undefined. + * This method will return all edges which are part of another edge set. + * The match is done exactly. + * The "invert" flag can be used to invert the sense, i.e. with + * "invert" set to true, this method will return all edges not + * in the other edge set. * * Merged semantics applies. */ - EdgePairs width_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + Edges in (const Edges &other, bool invert = false) const { - return run_check (db::WidthRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + return Edges (mp_delegate->in (other, invert)); } /** - * @brief Applies a space check and returns EdgePairs which correspond to violation markers + * @brief Returns the nth edge * - * "Space" refers to the space between the "outside" sides of the edges. - * - * For the parameters see \width_check. The space check reports edges for which the space is - * less than the specified threshold d. - * - * Merged semantics applies. - */ - EdgePairs space_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::SpaceRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies an enclosing check and returns EdgePairs which correspond to violation markers - * - * The check will return true for edges from this edge set and the other edge set, where the other edge - * is located on the "inside" side of the edge from this edge set, the orientation is parallel - * and the distance is less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs enclosing_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::OverlapRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies an overlap check and returns EdgePairs which correspond to violation markers - * - * The check will return true for edges from this edge set and the other edge set, where the other edge - * is located on the "inside" side of the edge from this edge set, the orientation is anti-parallel - * and the distance is less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs overlap_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::WidthRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies an separation check and returns EdgePairs which correspond to violation markers - * - * The check will return true for edges from this edge set and the other edge set, where the other edge - * is located on the "outside" side of the edge from this edge set, the orientation is anti-parallel - * and the distance is less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs separation_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::SpaceRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Applies a inside check and returns EdgePairs which correspond to violation markers - * - * The check will return true for edges from this edge set and the other edge set, where the other edge - * is located on the "outide" side of the edge from this edge set, the orientation is parallel - * and the distance is less than the specified threshold d. - * - * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". - * - * For the other parameters see \width_check. - * - * Merged semantics applies. - */ - EdgePairs inside_check (const Edges &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const - { - return run_check (db::InsideRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); - } - - /** - * @brief Returns the nth edge - * - * This method will force the edges to be inside the edge vector and will invalidate any iterator. - * If that happens, the method may be costly. - * The iterator should be used whenever possible. + * This operation is available only for flat regions - i.e. such for which "has_valid_edges" is true. */ const db::Edge *nth (size_t n) const { - ensure_valid_edges (); - return n < m_edges.size () ? &m_edges.get_layer ().begin () [n] : 0; + return mp_delegate->nth (n); + } + + /** + * @brief Forces flattening of the edge collection + * + * This method will turn any edge collection into a flat one. + */ + void flatten () + { + flat_edges (); } /** * @brief Returns true, if the edge set has valid edges stored within itself + * + * If the region has valid edges, it is permissable to use the edge's addresses + * from the iterator. Furthermore, the random access operator nth() is available. */ bool has_valid_edges () const { - return m_iter.at_end (); + return mp_delegate->has_valid_edges (); } /** - * @brief Ensures the edge collection has valid edges + * @brief Returns an addressable delivery for edges * - * This method is const since it has const semantics. + * This object allows accessing the edges by address, even if they + * are not delivered from a container. The magic is a heap object + * inside the delivery object. Hence, the deliver object must persist + * as long as the addresses are required. */ - void ensure_valid_edges () const; + AddressableEdgeDelivery addressable_edges () const + { + return AddressableEdgeDelivery (begin (), has_valid_edges ()); + } /** - * @brief Ensures the edge collection has valid merged edges + * @brief Returns true, if the edge set has valid merged edges stored within itself * - * It will make sure that begin_merged will deliver an - * iterator to an edge with a unique memory location. + * If the region has valid merged edges, it is permissable to use the edge's addresses + * from the merged edge iterator. Furthermore, the random access operator nth() is available. */ - void ensure_valid_merged_edges () const; + bool has_valid_merged_edges () const + { + return mp_delegate->has_valid_merged_edges (); + } + + /** + * @brief Returns an addressable delivery for merged polygons + */ + AddressableEdgeDelivery addressable_merged_edges () const + { + return AddressableEdgeDelivery (begin_merged (), has_valid_merged_edges ()); + } + + /** + * @brief Gets the internal iterator + * + * This method is intended for users who know what they are doing + */ + const db::RecursiveShapeIterator &iter () const; /** * @brief Equality */ - bool operator== (const db::Edges &other) const; + bool operator== (const db::Edges &other) const + { + return mp_delegate->equals (other); + } /** * @brief Inequality */ bool operator!= (const db::Edges &other) const { - return !operator== (other); + return ! mp_delegate->equals (other); } /** * @brief Less operator */ - bool operator< (const db::Edges &other) const; + bool operator< (const db::Edges &other) const + { + return mp_delegate->less (other); + } + + /** + * @brief Inserts the edge collection into the given layout, cell and layer + * If the edge collection is a hierarchical region, the hierarchy is copied into the + * layout's hierarchy. + */ + void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const + { + return mp_delegate->insert_into (layout, into_cell, into_layer); + } private: - typedef db::layer edge_layer_type; - typedef edge_layer_type::iterator edge_iterator_type; + friend class EdgePairs; - bool m_is_merged; - bool m_merged_semantics; - mutable db::Shapes m_edges; - mutable db::Shapes m_merged_edges; - mutable db::Box m_bbox; - mutable bool m_bbox_valid; - mutable bool m_merged_edges_valid; - mutable db::RecursiveShapeIterator m_iter; - db::ICplxTrans m_iter_trans; - bool m_report_progress; - std::string m_progress_desc; + EdgesDelegate *mp_delegate; - void init (); - void invalidate_cache (); - void set_valid_edges (); - void ensure_bbox_valid () const; - void ensure_merged_edges_valid () const; - EdgePairs run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - void inplace_boolean (const Edges *other, BoolOp op); - Edges boolean (const Edges *other, BoolOp op) const; - void edge_region_op (const Region &other, bool outside, bool include_borders); + void set_delegate (EdgesDelegate *delegate, bool keep_attributes = true); + FlatEdges *flat_edges (); }; } // namespace db @@ -1221,7 +1284,7 @@ private: namespace tl { /** - * @brief The type traits for the edges type + * @brief The type traits for the region type */ template <> struct type_traits : public type_traits @@ -1235,4 +1298,3 @@ namespace tl } #endif - diff --git a/src/db/db/dbEdgesDelegate.cc b/src/db/db/dbEdgesDelegate.cc new file mode 100644 index 000000000..d160e0b46 --- /dev/null +++ b/src/db/db/dbEdgesDelegate.cc @@ -0,0 +1,91 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbEdgesDelegate.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- + +EdgesDelegate::EdgesDelegate () +{ + m_base_verbosity = 30; + m_report_progress = false; + m_merged_semantics = true; + m_strict_handling = false; +} + +EdgesDelegate::EdgesDelegate (const EdgesDelegate &other) +{ + operator= (other); +} + +EdgesDelegate & +EdgesDelegate::operator= (const EdgesDelegate &other) +{ + if (this != &other) { + m_base_verbosity = other.m_base_verbosity; + m_report_progress = other.m_report_progress; + m_merged_semantics = other.m_merged_semantics; + m_strict_handling = other.m_strict_handling; + } + return *this; +} + +EdgesDelegate::~EdgesDelegate () +{ + // .. nothing yet .. +} + +void EdgesDelegate::set_base_verbosity (int vb) +{ + m_base_verbosity = vb; +} + +void EdgesDelegate::enable_progress (const std::string &progress_desc) +{ + m_report_progress = true; + m_progress_desc = progress_desc; +} + +void EdgesDelegate::disable_progress () +{ + m_report_progress = false; +} + +void EdgesDelegate::set_merged_semantics (bool f) +{ + if (f != m_merged_semantics) { + m_merged_semantics = f; + merged_semantics_changed (); + } +} + +void EdgesDelegate::set_strict_handling (bool f) +{ + m_strict_handling = f; +} + +} + diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h new file mode 100644 index 000000000..420e406e1 --- /dev/null +++ b/src/db/db/dbEdgesDelegate.h @@ -0,0 +1,317 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbEdgesDelegate +#define HDR_dbEdgesDelegate + +#include "dbCommon.h" + +#include "dbEdge.h" +#include "dbEdgePairs.h" +#include "dbEdgePairRelations.h" +#include "tlUniqueId.h" + +#include + +namespace db { + +/** + * @brief A base class for edge filters + */ +class DB_PUBLIC EdgeFilterBase +{ +public: + /** + * @brief Constructor + */ + EdgeFilterBase () { } + + virtual ~EdgeFilterBase () { } + + /** + * @brief Filters the edge + * If this method returns true, the polygon is kept. Otherwise it's discarded. + */ + virtual bool selected (const db::Edge &edge) const = 0; + + /** + * @brief Returns the transformation reducer for building cell variants + * This method may return 0. In this case, not cell variants are built. + */ + virtual const TransformationReducer *vars () const = 0; + + /** + * @brief Returns true, if the filter wants raw (not merged) input + */ + virtual bool requires_raw_input () const = 0; + + /** + * @brief Returns true, if the filter wants to build variants + * If not true, the filter accepts shape propagation as variant resolution. + */ + virtual bool wants_variants () const = 0; +}; + +/** + * @brief A template base class for edge processors + * + * A polygon processor can turn a edge into something else. + */ +template +class DB_PUBLIC edge_processor +{ +public: + /** + * @brief Constructor + */ + edge_processor () { } + + /** + * @brief Destructor + */ + virtual ~edge_processor () { } + + /** + * @brief Performs the actual processing + * This method will take the input edge from "edge" and puts the results into "res". + * "res" can be empty - in this case, the edge will be skipped. + */ + virtual void process (const db::Edge &edge, std::vector &res) const = 0; + + /** + * @brief Returns the transformation reducer for building cell variants + * This method may return 0. In this case, not cell variants are built. + */ + virtual const TransformationReducer *vars () const = 0; + + /** + * @brief Returns true, if the result of this operation can be regarded "merged" always. + */ + virtual bool result_is_merged () const = 0; + + /** + * @brief Returns true, if the result of this operation must not be merged. + * This feature can be used, if the result represents "degenerated" objects such + * as point-like edges. These must not be merged. Otherwise they disappear. + */ + virtual bool result_must_not_be_merged () const = 0; + + /** + * @brief Returns true, if the processor wants raw (not merged) input + */ + virtual bool requires_raw_input () const = 0; + + /** + * @brief Returns true, if the processor wants to build variants + * If not true, the processor accepts shape propagation as variant resolution. + */ + virtual bool wants_variants () const = 0; +}; + +/** + * @brief A edge processor base class + */ +class DB_PUBLIC EdgeProcessorBase + : public edge_processor +{ + // .. nothing yet .. +}; + +/** + * @brief An edge-to-polygon processor base class + */ +class DB_PUBLIC EdgeToPolygonProcessorBase + : public edge_processor +{ + // .. nothing yet .. +}; + +/** + * @brief An edge-to-edge pair processor base class + */ +class DB_PUBLIC EdgeToEdgePairProcessorBase + : public edge_processor +{ + // .. nothing yet .. +}; + +/** + * @brief A common definition for the boolean operations available on edges + */ +enum EdgeBoolOp { EdgeOr, EdgeNot, EdgeXor, EdgeAnd }; + +class RecursiveShapeIterator; +class EdgeFilterBase; +class EdgePairsDelegate; +class RegionDelegate; + +/** + * @brief The edge set iterator delegate + */ +class DB_PUBLIC EdgesIteratorDelegate +{ +public: + EdgesIteratorDelegate () { } + virtual ~EdgesIteratorDelegate () { } + + typedef db::Edge value_type; + + virtual bool at_end () const = 0; + virtual void increment () = 0; + virtual const value_type *get () const = 0; + virtual EdgesIteratorDelegate *clone () const = 0; +}; + +/** + * @brief The delegate for the actual edge set implementation + */ +class DB_PUBLIC EdgesDelegate + : public tl::UniqueId +{ +public: + typedef db::Coord coord_type; + typedef db::coord_traits coord_traits; + typedef db::Edge edge_type; + typedef db::Vector vector_type; + typedef db::Point point_type; + typedef db::Box box_type; + typedef coord_traits::distance_type distance_type; + typedef coord_traits::distance_type length_type; + + EdgesDelegate (); + virtual ~EdgesDelegate (); + + EdgesDelegate (const EdgesDelegate &other); + EdgesDelegate &operator= (const EdgesDelegate &other); + + virtual EdgesDelegate *clone () const = 0; + + void set_base_verbosity (int vb); + int base_verbosity () const + { + return m_base_verbosity; + } + + void enable_progress (const std::string &progress_desc); + void disable_progress (); + + void set_merged_semantics (bool f); + bool merged_semantics () const + { + return m_merged_semantics; + } + + void set_strict_handling (bool f); + bool strict_handling () const + { + return m_strict_handling; + } + + virtual std::string to_string (size_t nmax) const = 0; + + virtual EdgesIteratorDelegate *begin () const = 0; + virtual EdgesIteratorDelegate *begin_merged () const = 0; + + virtual std::pair begin_iter () const = 0; + virtual std::pair begin_merged_iter () const = 0; + + virtual bool empty () const = 0; + virtual bool is_merged () const = 0; + virtual size_t size () const = 0; + + virtual distance_type length (const db::Box &box) const = 0; + virtual Box bbox () const = 0; + + virtual EdgePairsDelegate *width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairsDelegate *space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairsDelegate *enclosing_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairsDelegate *overlap_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairsDelegate *separation_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairsDelegate *inside_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + + virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter) = 0; + virtual EdgesDelegate *filtered (const EdgeFilterBase &filter) const = 0; + virtual EdgesDelegate *process_in_place (const EdgeProcessorBase &filter) = 0; + virtual EdgesDelegate *processed (const EdgeProcessorBase &filter) const = 0; + virtual EdgePairsDelegate *processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &filter) const = 0; + virtual RegionDelegate *processed_to_polygons (const EdgeToPolygonProcessorBase &filter) const = 0; + + virtual EdgesDelegate *merged_in_place () = 0; + virtual EdgesDelegate *merged () const = 0; + + virtual EdgesDelegate *and_with (const Edges &other) const = 0; + virtual EdgesDelegate *and_with (const Region &other) const = 0; + virtual EdgesDelegate *not_with (const Edges &other) const = 0; + virtual EdgesDelegate *not_with (const Region &other) const = 0; + virtual EdgesDelegate *xor_with (const Edges &other) const = 0; + virtual EdgesDelegate *or_with (const Edges &other) const = 0; + virtual EdgesDelegate *add_in_place (const Edges &other) = 0; + virtual EdgesDelegate *add (const Edges &other) const = 0; + + virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const = 0; + + virtual EdgesDelegate *inside_part (const Region &other) const = 0; + virtual EdgesDelegate *outside_part (const Region &other) const = 0; + virtual EdgesDelegate *selected_interacting (const Region &other) const = 0; + virtual EdgesDelegate *selected_not_interacting (const Region &other) const = 0; + virtual EdgesDelegate *selected_interacting (const Edges &other) const = 0; + virtual EdgesDelegate *selected_not_interacting (const Edges &other) const = 0; + + virtual EdgesDelegate *in (const Edges &other, bool invert) const = 0; + + virtual const db::Edge *nth (size_t n) const = 0; + virtual bool has_valid_edges () const = 0; + virtual bool has_valid_merged_edges () const = 0; + + virtual const db::RecursiveShapeIterator *iter () const = 0; + + virtual bool equals (const Edges &other) const = 0; + virtual bool less (const Edges &other) const = 0; + + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const = 0; + +protected: + const std::string &progress_desc () const + { + return m_progress_desc; + } + + bool report_progress () const + { + return m_report_progress; + } + + virtual void merged_semantics_changed () { } + +private: + bool m_merged_semantics; + bool m_strict_handling; + bool m_report_progress; + std::string m_progress_desc; + int m_base_verbosity; +}; + +} + +#endif + diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc new file mode 100644 index 000000000..c7c71faf0 --- /dev/null +++ b/src/db/db/dbEdgesUtils.cc @@ -0,0 +1,177 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbEdgesUtils.h" +#include "dbRegion.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// JoinEdgesCluster implementation + +JoinEdgesCluster::JoinEdgesCluster (db::PolygonSink *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i) + : mp_output (output), m_ext_b (ext_b), m_ext_e (ext_e), m_ext_o (ext_o), m_ext_i (ext_i) +{ + // .. nothing yet .. +} + +void +JoinEdgesCluster::finish () +{ + std::multimap objects_by_p1; + std::multimap objects_by_p2; + for (iterator o = begin (); o != end (); ++o) { + if (o->first->p1 () != o->first->p2 ()) { + objects_by_p1.insert (std::make_pair (o->first->p1 (), o)); + objects_by_p2.insert (std::make_pair (o->first->p2 (), o)); + } + } + + while (! objects_by_p2.empty ()) { + + tl_assert (! objects_by_p1.empty ()); + + // Find the beginning of a new sequence + std::multimap::iterator j0 = objects_by_p1.begin (); + std::multimap::iterator j = j0; + do { + std::multimap::iterator jj = objects_by_p2.find (j->first); + if (jj == objects_by_p2.end ()) { + break; + } else { + j = objects_by_p1.find (jj->second->first->p1 ()); + tl_assert (j != objects_by_p1.end ()); + } + } while (j != j0); + + iterator i = j->second; + + // determine a sequence + // TODO: this chooses any solution in case of forks. Choose a specific one? + std::vector pts; + pts.push_back (i->first->p1 ()); + + do { + + // record the next point + pts.push_back (i->first->p2 ()); + + // remove the edge as it's taken + std::multimap::iterator jj; + for (jj = objects_by_p2.find (i->first->p2 ()); jj != objects_by_p2.end () && jj->first == i->first->p2 (); ++jj) { + if (jj->second == i) { + break; + } + } + tl_assert (jj != objects_by_p2.end () && jj->second == i); + objects_by_p2.erase (jj); + objects_by_p1.erase (j); + + // process along the edge to the next one + // TODO: this chooses any solution in case of forks. Choose a specific one? + j = objects_by_p1.find (i->first->p2 ()); + if (j != objects_by_p1.end ()) { + i = j->second; + } else { + break; + } + + } while (true); + + bool cyclic = (pts.back () == pts.front ()); + + if (! cyclic) { + + // non-cyclic sequence + db::Path path (pts.begin (), pts.end (), 0, m_ext_b, m_ext_e, false); + std::vector hull; + path.hull (hull, m_ext_o, m_ext_i); + db::Polygon poly; + poly.assign_hull (hull.begin (), hull.end ()); + mp_output->put (poly); + + } else { + + // we have a loop: form a contour by using the polygon size functions and a "Not" to form the hole + db::Polygon poly; + poly.assign_hull (pts.begin (), pts.end ()); + + db::EdgeProcessor ep; + db::PolygonGenerator pg (*mp_output, false, true); + + int mode_a = -1, mode_b = -1; + + if (m_ext_o == 0) { + ep.insert (poly, 0); + } else { + db::Polygon sized_poly (poly); + sized_poly.size (m_ext_o, m_ext_o, 2 /*sizing mode*/); + ep.insert (sized_poly, 0); + mode_a = 1; + } + + if (m_ext_i == 0) { + ep.insert (poly, 1); + } else { + db::Polygon sized_poly (poly); + sized_poly.size (-m_ext_i, -m_ext_i, 2 /*sizing mode*/); + ep.insert (sized_poly, 1); + mode_b = 1; + } + + db::BooleanOp2 op (db::BooleanOp::ANotB, mode_a, mode_b); + ep.process (pg, op); + + } + + } +} + +// ------------------------------------------------------------------------------------------------------------- +// extended_edge implementation + +db::Polygon +extended_edge (const db::Edge &edge, db::Coord ext_b, db::Coord ext_e, db::Coord ext_o, db::Coord ext_i) +{ + db::DVector d; + if (edge.is_degenerate ()) { + d = db::DVector (1.0, 0.0); + } else { + d = db::DVector (edge.d ()) * (1.0 / edge.double_length ()); + } + + db::DVector n (-d.y (), d.x ()); + + db::Point pts[4] = { + db::Point (db::DPoint (edge.p1 ()) - d * double (ext_b) + n * double (ext_o)), + db::Point (db::DPoint (edge.p2 ()) + d * double (ext_e) + n * double (ext_o)), + db::Point (db::DPoint (edge.p2 ()) + d * double (ext_e) - n * double (ext_i)), + db::Point (db::DPoint (edge.p1 ()) - d * double (ext_b) - n * double (ext_i)), + }; + + db::Polygon poly; + poly.assign_hull (pts + 0, pts + 4); + return poly; +} + +} diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h new file mode 100644 index 000000000..8377bdde6 --- /dev/null +++ b/src/db/db/dbEdgesUtils.h @@ -0,0 +1,337 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbEdgesUtils +#define HDR_dbEdgesUtils + +#include "dbCommon.h" +#include "dbEdges.h" +#include "dbBoxScanner.h" +#include "dbPolygonTools.h" + +namespace db { + +class PolygonSink; + +/** + * @brief An edge length filter for use with Edges::filter or Edges::filtered + * + * This filter has two parameters: lmin and lmax. + * It will filter all edges for which the length is >= lmin and < lmax. + * There is an "invert" flag which allows to select all edges not + * matching the criterion. + */ + +struct DB_PUBLIC EdgeLengthFilter + : public EdgeFilterBase +{ + typedef db::Edge::distance_type length_type; + + /** + * @brief Constructor + * + * @param lmin The minimum length + * @param lmax The maximum length + * @param inverse If set to true, only polygons not matching this criterion will be filtered + */ + EdgeLengthFilter (length_type lmin, length_type lmax, bool inverse) + : m_lmin (lmin), m_lmax (lmax), m_inverse (inverse) + { + // .. nothing yet .. + } + + /** + * @brief Returns true if the edge length matches the criterion + */ + virtual bool selected (const db::Edge &edge) const + { + length_type l = edge.length (); + if (! m_inverse) { + return l >= m_lmin && l < m_lmax; + } else { + return ! (l >= m_lmin && l < m_lmax); + } + } + + /** + * @brief This filter is isotropic + */ + virtual const TransformationReducer *vars () const + { + return &m_vars; + } + + /** + * @brief Requires merged input + */ + virtual bool requires_raw_input () const + { + return false; + } + + /** + * @brief Wants to build variants + */ + virtual bool wants_variants () const + { + return true; + } + +private: + length_type m_lmin, m_lmax; + bool m_inverse; + db::MagnificationReducer m_vars; +}; + +/** + * @brief An edge orientation filter for use with Edges::filter or Edges::filtered + * + * This filter has two parameters: amin and amax. + * It will filter all edges for which the orientation angle is >= amin and < amax. + * The orientation angle is measured in degree against the x axis in the mathematical sense. + * There is an "invert" flag which allows to select all edges not + * matching the criterion. + */ + +struct DB_PUBLIC EdgeOrientationFilter + : public EdgeFilterBase +{ + /** + * @brief Constructor + * + * @param amin The minimum angle (measured against the x axis) + * @param amax The maximum angle (measured against the x axis) + * @param inverse If set to true, only edges not matching this criterion will be filtered + * + * This filter will filter out all edges whose angle against x axis + * is larger or equal to amin and less than amax. + */ + EdgeOrientationFilter (double amin, double amax, bool inverse) + : m_inverse (inverse), m_exact (false) + { + m_emin = db::DVector (cos (amin * M_PI / 180.0), sin (amin * M_PI / 180.0)); + m_emax = db::DVector (cos (amax * M_PI / 180.0), sin (amax * M_PI / 180.0)); + } + + /** + * @brief Constructor + * + * @param a The angle (measured against the x axis) + * @param inverse If set to true, only edges not matching this criterion will be filtered + * + * This filter will filter out all edges whose angle against x axis + * is equal to a. + */ + EdgeOrientationFilter (double a, bool inverse) + : m_inverse (inverse), m_exact (true) + { + m_emin = db::DVector (cos (a * M_PI / 180.0), sin (a * M_PI / 180.0)); + } + + /** + * @brief Returns true if the edge orientation matches the criterion + */ + virtual bool selected (const db::Edge &edge) const + { + int smin = db::vprod_sign (m_emin, db::DVector (edge.d ())); + if (m_exact) { + if (! m_inverse) { + return smin == 0; + } else { + return smin != 0; + } + } else { + int smax = db::vprod_sign (m_emax, db::DVector (edge.d ())); + if (! m_inverse) { + return (smin >= 0 && smax < 0) || (smax > 0 && smin <= 0); + } else { + return ! ((smin >= 0 && smax < 0) || (smax > 0 && smin <= 0)); + } + } + } + + /** + * @brief This filter is not isotropic + */ + virtual const TransformationReducer *vars () const + { + return &m_vars; + } + + /** + * @brief Requires merged input + */ + virtual bool requires_raw_input () const + { + return false; + } + + /** + * @brief Wants to build variants + */ + virtual bool wants_variants () const + { + return true; + } + +private: + db::DVector m_emin, m_emax; + bool m_inverse; + bool m_exact; + db::MagnificationAndOrientationReducer m_vars; +}; + +/** + * @brief A helper class for the edge interaction functionality which acts as an edge pair receiver + */ +template +class edge_interaction_filter + : public db::box_scanner_receiver +{ +public: + edge_interaction_filter (OutputContainer &output) + : mp_output (&output) + { + // .. nothing yet .. + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + // Select the edges which intersect + if (p1 != p2) { + const db::Edge *o = p1 > p2 ? o2 : o1; + const db::Edge *oo = p1 > p2 ? o1 : o2; + if (o->intersect (*oo)) { + if (m_seen.insert (o).second) { + mp_output->insert (*o); + } + } + } + } + +private: + OutputContainer *mp_output; + std::set m_seen; +}; + +/** + * @brief A helper class for the edge to region interaction functionality which acts as an edge pair receiver + * + * Note: This special scanner uses pointers to two different objects: edges and polygons. + * It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate + * pointers to edges. + * + * There is a special box converter which is able to sort that out as well. + */ +template +class edge_to_region_interaction_filter + : public db::box_scanner_receiver2 +{ +public: + edge_to_region_interaction_filter (OutputContainer &output) + : mp_output (&output) + { + // .. nothing yet .. + } + + void add (const db::Edge *e, size_t, const db::Polygon *p, size_t) + { + if (m_seen.find (e) == m_seen.end ()) { + if (db::interact (*p, *e)) { + m_seen.insert (e); + mp_output->insert (*e); + } + } + } + +private: + OutputContainer *mp_output; + std::set m_seen; +}; + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + * + * If will perform a edge by edge check using the provided EdgeRelationFilter + */ +template +class edge2edge_check_for_edges + : public db::box_scanner_receiver +{ +public: + edge2edge_check_for_edges (const EdgeRelationFilter &check, Output &output, bool requires_different_layers) + : mp_check (&check), mp_output (&output) + { + m_requires_different_layers = requires_different_layers; + } + + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) + { + // Overlap or inside checks require input from different layers + if (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0) { + + // 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)); + + db::EdgePair ep; + if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { + mp_output->insert (ep); + } + + } + } + +private: + const EdgeRelationFilter *mp_check; + Output *mp_output; + bool m_requires_different_layers; +}; + +/** + * @brief A helper class to turn joined edge sequences into polygons + * + * This object is an edge cluster so it can connect to a cluster collector + * driven by a box scanner. + */ +struct JoinEdgesCluster + : public db::cluster +{ + typedef db::Edge::coord_type coord_type; + + JoinEdgesCluster (db::PolygonSink *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i); + void finish (); + +private: + db::PolygonSink *mp_output; + coord_type m_ext_b, m_ext_e, m_ext_o, m_ext_i; +}; + +/** + * @brief Implements the extension algorithm to turn an edge into a polygon + */ +db::Polygon extended_edge (const db::Edge &edge, db::Coord ext_b, db::Coord ext_e, db::Coord ext_o, db::Coord ext_i); + +} // namespace db + +#endif diff --git a/src/db/db/dbEmptyEdgePairs.cc b/src/db/db/dbEmptyEdgePairs.cc new file mode 100644 index 000000000..d26aa0956 --- /dev/null +++ b/src/db/db/dbEmptyEdgePairs.cc @@ -0,0 +1,99 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbEmptyEdgePairs.h" +#include "dbEmptyRegion.h" +#include "dbEmptyEdges.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- + +EmptyEdgePairs::EmptyEdgePairs () +{ + // .. nothing yet .. +} + +EmptyEdgePairs::EmptyEdgePairs (const EmptyEdgePairs &other) + : EdgePairsDelegate (other) +{ + // .. nothing yet .. +} + +EdgePairsDelegate * +EmptyEdgePairs::clone () const +{ + return new EmptyEdgePairs (*this); +} + +RegionDelegate * +EmptyEdgePairs::polygons (db::Coord) const +{ + return new EmptyRegion (); +} + +EdgesDelegate * +EmptyEdgePairs::edges () const +{ + return new EmptyEdges (); +} + +EdgesDelegate * +EmptyEdgePairs::first_edges () const +{ + return new EmptyEdges (); +} + +EdgesDelegate * +EmptyEdgePairs::second_edges () const +{ + return new EmptyEdges (); +} + +EdgePairsDelegate * +EmptyEdgePairs::add_in_place (const EdgePairs &other) +{ + return add (other); +} + +EdgePairsDelegate * +EmptyEdgePairs::add (const EdgePairs &other) const +{ + return other.delegate ()->clone (); +} + +bool +EmptyEdgePairs::equals (const EdgePairs &other) const +{ + return other.empty (); +} + +bool +EmptyEdgePairs::less (const EdgePairs &other) const +{ + return other.empty () ? false : true; +} + +} + diff --git a/src/db/db/dbEmptyEdgePairs.h b/src/db/db/dbEmptyEdgePairs.h new file mode 100644 index 000000000..428cab421 --- /dev/null +++ b/src/db/db/dbEmptyEdgePairs.h @@ -0,0 +1,87 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbEmptyEdgePairs +#define HDR_dbEmptyEdgePairs + +#include "dbCommon.h" + +#include "dbEdgePairsDelegate.h" +#include "dbRecursiveShapeIterator.h" + +namespace db { + +/** + * @brief The delegate for the actual edge set implementation + */ +class DB_PUBLIC EmptyEdgePairs + : public EdgePairsDelegate +{ +public: + EmptyEdgePairs (); + EmptyEdgePairs (const EmptyEdgePairs &other); + + virtual EdgePairsDelegate *clone () const; + + virtual std::string to_string (size_t) const { return std::string (); } + + virtual EdgePairsIteratorDelegate *begin () const { return 0; } + virtual std::pair begin_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + + virtual bool empty () const { return true; } + virtual size_t size () const { return 0; } + + virtual Box bbox () const { return Box (); } + + virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &) { return this; } + virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const { return new EmptyEdgePairs (); } + + virtual RegionDelegate *polygons (db::Coord e) const; + virtual EdgesDelegate *edges () const; + virtual EdgesDelegate *first_edges () const; + virtual EdgesDelegate *second_edges () const; + + virtual EdgePairsDelegate *add_in_place (const EdgePairs &other); + virtual EdgePairsDelegate *add (const EdgePairs &other) const; + + virtual EdgePairsDelegate *in (const EdgePairs &, bool) const { return new EmptyEdgePairs (); } + + virtual const db::EdgePair *nth (size_t) const { tl_assert (false); } + virtual bool has_valid_edge_pairs () const { return true; } + + virtual const db::RecursiveShapeIterator *iter () const { return 0; } + + virtual bool equals (const EdgePairs &other) const; + virtual bool less (const EdgePairs &other) const; + + virtual void insert_into (Layout *, db::cell_index_type, unsigned int) const { } + virtual void insert_into_as_polygons (Layout *, db::cell_index_type, unsigned int, db::Coord) const { } + +private: + EmptyEdgePairs &operator= (const EmptyEdgePairs &other); +}; + +} + +#endif + diff --git a/src/db/db/dbEmptyEdges.cc b/src/db/db/dbEmptyEdges.cc new file mode 100644 index 000000000..211172085 --- /dev/null +++ b/src/db/db/dbEmptyEdges.cc @@ -0,0 +1,154 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbEmptyEdges.h" +#include "dbEmptyEdgePairs.h" +#include "dbEmptyRegion.h" +#include "dbEdges.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// EmptyEdges implementation + +EmptyEdges::EmptyEdges () +{ + // .. nothing yet .. +} + +EmptyEdges::~EmptyEdges () +{ + // .. nothing yet .. +} + +EmptyEdges::EmptyEdges (const EmptyEdges &other) + : EdgesDelegate (other) +{ + // .. nothing yet .. +} + +EdgesDelegate * +EmptyEdges::clone () const +{ + return new EmptyEdges (*this); +} + +RegionDelegate * +EmptyEdges::extended (coord_type, coord_type, coord_type, coord_type, bool) const +{ + return new EmptyRegion (); +} + +EdgePairsDelegate * +EmptyEdges::processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &) const +{ + return new EmptyEdgePairs (); +} + +RegionDelegate * +EmptyEdges::processed_to_polygons (const EdgeToPolygonProcessorBase &) const +{ + return new EmptyRegion (); +} + +EdgePairsDelegate * +EmptyEdges::width_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyEdges::space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyEdges::enclosing_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyEdges::overlap_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyEdges::separation_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyEdges::inside_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgesDelegate * +EmptyEdges::add_in_place (const Edges &other) +{ + return add (other); +} + +EdgesDelegate * +EmptyEdges::add (const Edges &other) const +{ + return other.delegate ()->clone (); +} + +EdgesDelegate * +EmptyEdges::xor_with (const Edges &other) const +{ + return or_with (other); +} + +EdgesDelegate * +EmptyEdges::or_with (const Edges &other) const +{ + if (other.empty ()) { + return new EmptyEdges (); + } else if (! other.strict_handling ()) { + return other.delegate ()->clone (); + } else { + return other.delegate ()->merged (); + } +} + +bool +EmptyEdges::equals (const Edges &other) const +{ + return other.empty (); +} + +bool +EmptyEdges::less (const Edges &other) const +{ + return other.empty () ? false : true; +} + +} + diff --git a/src/db/db/dbEmptyEdges.h b/src/db/db/dbEmptyEdges.h new file mode 100644 index 000000000..8f590bc32 --- /dev/null +++ b/src/db/db/dbEmptyEdges.h @@ -0,0 +1,114 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbEmptyEdges +#define HDR_dbEmptyEdges + +#include "dbCommon.h" +#include "dbEdgesDelegate.h" +#include "dbRecursiveShapeIterator.h" + +namespace db { + +/** + * @brief An empty Edges + */ +class DB_PUBLIC EmptyEdges + : public EdgesDelegate +{ +public: + EmptyEdges (); + virtual ~EmptyEdges (); + + EmptyEdges (const EmptyEdges &other); + EdgesDelegate *clone () const; + + virtual EdgesIteratorDelegate *begin () const { return 0; } + virtual EdgesIteratorDelegate *begin_merged () const { return 0; } + + virtual std::pair begin_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + virtual std::pair begin_merged_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + + virtual bool empty () const { return true; } + virtual size_t size () const { return 0; } + virtual std::string to_string (size_t) const { return std::string (); } + virtual bool is_merged () const { return true; } + virtual distance_type length (const db::Box &) const { return 0; } + virtual Box bbox () const { return db::Box (); } + + virtual EdgePairsDelegate *width_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const; + virtual EdgePairsDelegate *space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const; + virtual EdgePairsDelegate *enclosing_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const; + virtual EdgePairsDelegate *overlap_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const; + virtual EdgePairsDelegate *separation_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const; + virtual EdgePairsDelegate *inside_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const; + + virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &) { return this; } + virtual EdgesDelegate *filtered (const EdgeFilterBase &) const { return new EmptyEdges (); } + virtual EdgesDelegate *process_in_place (const EdgeProcessorBase &) { return this; } + virtual EdgesDelegate *processed (const EdgeProcessorBase &) const { return new EmptyEdges (); } + virtual EdgePairsDelegate *processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &) const; + virtual RegionDelegate *processed_to_polygons (const EdgeToPolygonProcessorBase &) const; + + virtual EdgesDelegate *merged_in_place () { return this; } + virtual EdgesDelegate *merged () const { return new EmptyEdges (); } + + virtual EdgesDelegate *and_with (const Edges &) const { return new EmptyEdges (); } + virtual EdgesDelegate *and_with (const Region &) const { return new EmptyEdges (); } + virtual EdgesDelegate *not_with (const Edges &) const { return new EmptyEdges (); } + virtual EdgesDelegate *not_with (const Region &) const { return new EmptyEdges (); } + virtual EdgesDelegate *xor_with (const Edges &other) const; + virtual EdgesDelegate *or_with (const Edges &other) const; + virtual EdgesDelegate *add_in_place (const Edges &other); + virtual EdgesDelegate *add (const Edges &other) const; + + virtual RegionDelegate *extended (coord_type, coord_type, coord_type, coord_type, bool) const; + + virtual EdgesDelegate *inside_part (const Region &) const { return new EmptyEdges (); } + virtual EdgesDelegate *outside_part (const Region &) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_interacting (const Edges &) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_not_interacting (const Edges &) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_interacting (const Region &) const { return new EmptyEdges (); } + virtual EdgesDelegate *selected_not_interacting (const Region &) const { return new EmptyEdges (); } + + virtual EdgesDelegate *in (const Edges &, bool) const { return new EmptyEdges (); } + + virtual const db::Edge *nth (size_t) const { tl_assert (false); } + virtual bool has_valid_edges () const { return true; } + virtual bool has_valid_merged_edges () const { return true; } + + virtual const db::RecursiveShapeIterator *iter () const { return 0; } + + virtual bool equals (const Edges &other) const; + virtual bool less (const Edges &other) const; + + virtual void insert_into (Layout *, db::cell_index_type, unsigned int) const { } + +private: + EmptyEdges &operator= (const EmptyEdges &other); +}; + +} // namespace db + +#endif + diff --git a/src/db/db/dbEmptyRegion.cc b/src/db/db/dbEmptyRegion.cc new file mode 100644 index 000000000..ff66619b4 --- /dev/null +++ b/src/db/db/dbEmptyRegion.cc @@ -0,0 +1,178 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbEmptyRegion.h" +#include "dbEmptyEdges.h" +#include "dbEmptyEdgePairs.h" +#include "dbRegion.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// EmptyRegion implementation + +EmptyRegion::EmptyRegion () +{ + // .. nothing yet .. +} + +EmptyRegion::~EmptyRegion () +{ + // .. nothing yet .. +} + +EmptyRegion::EmptyRegion (const EmptyRegion &other) + : RegionDelegate (other) +{ + // .. nothing yet .. +} + +RegionDelegate * +EmptyRegion::clone () const +{ + return new EmptyRegion (*this); +} + +RegionDelegate * +EmptyRegion::add_in_place (const Region &other) +{ + return add (other); +} + +RegionDelegate * +EmptyRegion::add (const Region &other) const +{ + return other.delegate ()->clone (); +} + +RegionDelegate * +EmptyRegion::xor_with (const Region &other) const +{ + return or_with (other); +} + +RegionDelegate * +EmptyRegion::or_with (const Region &other) const +{ + if (other.empty ()) { + return new EmptyRegion (); + } else if (! other.strict_handling ()) { + return other.delegate ()->clone (); + } else { + return other.delegate ()->merged (); + } +} + +EdgesDelegate * +EmptyRegion::processed_to_edges (const PolygonToEdgeProcessorBase &) const +{ + return new EmptyEdges (); +} + +EdgePairsDelegate * +EmptyRegion::processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyRegion::width_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyRegion::space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyRegion::isolated_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyRegion::notch_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyRegion::enclosing_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyRegion::overlap_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyRegion::separation_check (const Region &, db::Coord, bool , metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyRegion::inside_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyRegion::grid_check (db::Coord, db::Coord) const +{ + return new EmptyEdgePairs (); +} + +EdgePairsDelegate * +EmptyRegion::angle_check (double, double, bool) const +{ + return new EmptyEdgePairs (); +} + +EdgesDelegate * +EmptyRegion::edges (const EdgeFilterBase *) const +{ + return new EmptyEdges (); +} + +bool +EmptyRegion::equals (const Region &other) const +{ + return other.empty (); +} + +bool +EmptyRegion::less (const Region &other) const +{ + return other.empty () ? false : true; +} + +} + diff --git a/src/db/db/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h new file mode 100644 index 000000000..d57a8e512 --- /dev/null +++ b/src/db/db/dbEmptyRegion.h @@ -0,0 +1,129 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbEmptyRegion +#define HDR_dbEmptyRegion + +#include "dbCommon.h" +#include "dbRegionDelegate.h" + +namespace db { + +/** + * @brief An empty Region + */ +class DB_PUBLIC EmptyRegion + : public RegionDelegate +{ +public: + EmptyRegion (); + virtual ~EmptyRegion (); + + EmptyRegion (const EmptyRegion &other); + RegionDelegate *clone () const; + + virtual RegionIteratorDelegate *begin () const { return 0; } + virtual RegionIteratorDelegate *begin_merged () const { return 0; } + + virtual std::pair begin_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + virtual std::pair begin_merged_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + + virtual bool empty () const { return true; } + virtual size_t size () const { return 0; } + virtual std::string to_string (size_t) const { return std::string (); } + + virtual bool is_box () const { return false; } + virtual bool is_merged () const { return true; } + virtual area_type area (const db::Box &) const { return 0; } + virtual perimeter_type perimeter (const db::Box &) const { return 0; } + + virtual Box bbox () const { return Box (); } + + virtual EdgePairsDelegate *width_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const; + virtual EdgePairsDelegate *space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const; + virtual EdgePairsDelegate *isolated_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const; + virtual EdgePairsDelegate *notch_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const; + virtual EdgePairsDelegate *enclosing_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const; + virtual EdgePairsDelegate *overlap_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const; + virtual EdgePairsDelegate *separation_check (const Region &, db::Coord, bool , metrics_type, double, distance_type, distance_type) const; + virtual EdgePairsDelegate *inside_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const; + virtual EdgePairsDelegate *grid_check (db::Coord, db::Coord) const; + virtual EdgePairsDelegate *angle_check (double, double, bool) const; + + virtual RegionDelegate *snapped_in_place (db::Coord, db::Coord) { return this; } + virtual RegionDelegate *snapped (db::Coord, db::Coord) { return new EmptyRegion (); } + + virtual EdgesDelegate *edges (const EdgeFilterBase *) const; + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &) { return this; } + virtual RegionDelegate *filtered (const PolygonFilterBase &) const { return new EmptyRegion (); } + virtual RegionDelegate *process_in_place (const PolygonProcessorBase &) { return this; } + virtual RegionDelegate *processed (const PolygonProcessorBase &) const { return new EmptyRegion (); } + virtual EdgesDelegate *processed_to_edges (const PolygonToEdgeProcessorBase &) const; + virtual EdgePairsDelegate *processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &) const; + + virtual RegionDelegate *merged_in_place () { return this; } + virtual RegionDelegate *merged_in_place (bool, unsigned int) { return this; } + virtual RegionDelegate *merged () const { return new EmptyRegion (); } + virtual RegionDelegate *merged (bool, unsigned int) const { return new EmptyRegion (); } + + virtual RegionDelegate *sized (coord_type, unsigned int) const { return new EmptyRegion (); } + virtual RegionDelegate *sized (coord_type, coord_type, unsigned int) const { return new EmptyRegion (); } + + virtual RegionDelegate *and_with (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *not_with (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *xor_with (const Region &other) const; + virtual RegionDelegate *or_with (const Region &other) const; + virtual RegionDelegate *add_in_place (const Region &other); + virtual RegionDelegate *add (const Region &other) const; + + virtual RegionDelegate *selected_outside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_outside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_inside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_inside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_interacting (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_interacting (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_interacting (const Edges &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_interacting (const Edges &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_overlapping (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_overlapping (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *in (const Region &, bool) const { return new EmptyRegion (); } + + virtual bool has_valid_polygons () const { return true; } + virtual bool has_valid_merged_polygons () const { return true; } + virtual const db::Polygon *nth (size_t) const { tl_assert (false); } + + virtual const db::RecursiveShapeIterator *iter () const { return 0; } + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; + + virtual void insert_into (Layout *, db::cell_index_type, unsigned int) const { } + +private: + EmptyRegion &operator= (const EmptyRegion &other); +}; + +} // namespace db + +#endif + diff --git a/src/db/db/dbFlatEdgePairs.cc b/src/db/db/dbFlatEdgePairs.cc new file mode 100644 index 000000000..87d8eb689 --- /dev/null +++ b/src/db/db/dbFlatEdgePairs.cc @@ -0,0 +1,218 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbFlatEdgePairs.h" +#include "dbEmptyEdgePairs.h" +#include "dbEdgePairs.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// FlatEdgePairs implementation + +FlatEdgePairs::FlatEdgePairs () + : AsIfFlatEdgePairs (), m_edge_pairs (false) +{ + // .. nothing yet .. +} + +FlatEdgePairs::~FlatEdgePairs () +{ + // .. nothing yet .. +} + +FlatEdgePairs::FlatEdgePairs (const FlatEdgePairs &other) + : AsIfFlatEdgePairs (other), m_edge_pairs (false) +{ + m_edge_pairs = other.m_edge_pairs; +} + +FlatEdgePairs::FlatEdgePairs (const db::Shapes &edge_pairs) + : AsIfFlatEdgePairs (), m_edge_pairs (edge_pairs) +{ + // .. nothing yet .. +} + +void FlatEdgePairs::invalidate_cache () +{ + invalidate_bbox (); +} + +void FlatEdgePairs::reserve (size_t n) +{ + m_edge_pairs.reserve (db::EdgePair::tag (), n); +} + +EdgePairsIteratorDelegate *FlatEdgePairs::begin () const +{ + return new FlatEdgePairsIterator (m_edge_pairs.get_layer ().begin (), m_edge_pairs.get_layer ().end ()); +} + +std::pair FlatEdgePairs::begin_iter () const +{ + return std::make_pair (db::RecursiveShapeIterator (m_edge_pairs), db::ICplxTrans ()); +} + +bool FlatEdgePairs::empty () const +{ + return m_edge_pairs.empty (); +} + +size_t FlatEdgePairs::size () const +{ + return m_edge_pairs.size (); +} + +Box FlatEdgePairs::compute_bbox () const +{ + m_edge_pairs.update_bbox (); + return m_edge_pairs.bbox (); +} + +EdgePairsDelegate * +FlatEdgePairs::filter_in_place (const EdgePairFilterBase &filter) +{ + edge_pair_iterator_type pw = m_edge_pairs.get_layer ().begin (); + for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + if (pw == m_edge_pairs.get_layer ().end ()) { + m_edge_pairs.get_layer ().insert (*p); + pw = m_edge_pairs.get_layer ().end (); + } else { + m_edge_pairs.get_layer ().replace (pw++, *p); + } + } + } + + m_edge_pairs.get_layer ().erase (pw, m_edge_pairs.get_layer ().end ()); + + return this; +} + +EdgePairsDelegate *FlatEdgePairs::add (const EdgePairs &other) const +{ + std::auto_ptr new_edge_pairs (new FlatEdgePairs (*this)); + new_edge_pairs->invalidate_cache (); + + FlatEdgePairs *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + new_edge_pairs->raw_edge_pairs ().insert (other_flat->raw_edge_pairs ().get_layer ().begin (), other_flat->raw_edge_pairs ().get_layer ().end ()); + + } else { + + size_t n = new_edge_pairs->raw_edge_pairs ().size (); + for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + new_edge_pairs->raw_edge_pairs ().reserve (db::EdgePair::tag (), n); + + for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { + new_edge_pairs->raw_edge_pairs ().insert (*p); + } + + } + + return new_edge_pairs.release (); +} + +EdgePairsDelegate *FlatEdgePairs::add_in_place (const EdgePairs &other) +{ + invalidate_cache (); + + FlatEdgePairs *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + m_edge_pairs.insert (other_flat->raw_edge_pairs ().get_layer ().begin (), other_flat->raw_edge_pairs ().get_layer ().end ()); + + } else { + + size_t n = m_edge_pairs.size (); + for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + m_edge_pairs.reserve (db::EdgePair::tag (), n); + + for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { + m_edge_pairs.insert (*p); + } + + } + + return this; +} + +const db::EdgePair *FlatEdgePairs::nth (size_t n) const +{ + return n < m_edge_pairs.size () ? &m_edge_pairs.get_layer ().begin () [n] : 0; +} + +bool FlatEdgePairs::has_valid_edge_pairs () const +{ + return true; +} + +const db::RecursiveShapeIterator *FlatEdgePairs::iter () const +{ + return 0; +} + +void +FlatEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const +{ + db::Shapes &out = layout->cell (into_cell).shapes (into_layer); + for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { + out.insert (p->normalized ().to_simple_polygon (enl)); + } +} + +void +FlatEdgePairs::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + layout->cell (into_cell).shapes (into_layer).insert (m_edge_pairs); +} + +void +FlatEdgePairs::insert (const db::EdgePair &ep) +{ + m_edge_pairs.insert (ep); + invalidate_cache (); +} + +void +FlatEdgePairs::insert (const db::Shape &shape) +{ + if (shape.is_edge_pair ()) { + + db::EdgePair ep; + shape.edge_pair (ep); + insert (ep); + + } +} + +} + diff --git a/src/db/db/dbFlatEdgePairs.h b/src/db/db/dbFlatEdgePairs.h new file mode 100644 index 000000000..bb21fb64f --- /dev/null +++ b/src/db/db/dbFlatEdgePairs.h @@ -0,0 +1,171 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbFlatEdgePairs +#define HDR_dbFlatEdgePairs + +#include "dbCommon.h" + +#include "dbAsIfFlatEdgePairs.h" +#include "dbShapes.h" + +namespace db { + +/** + * @brief An iterator delegate for the flat edge pair set + */ +class DB_PUBLIC FlatEdgePairsIterator + : public EdgePairsIteratorDelegate +{ +public: + typedef db::layer edge_pair_layer_type; + typedef edge_pair_layer_type::iterator iterator_type; + + FlatEdgePairsIterator (iterator_type from, iterator_type to) + : m_from (from), m_to (to) + { + // .. nothing yet .. + } + + virtual bool at_end () const + { + return m_from == m_to; + } + + virtual void increment () + { + ++m_from; + } + + virtual const value_type *get () const + { + return m_from.operator-> (); + } + + virtual EdgePairsIteratorDelegate *clone () const + { + return new FlatEdgePairsIterator (*this); + } + +private: + friend class EdgePairs; + + iterator_type m_from, m_to; +}; + +/** + * @brief The delegate for the actual edge pair set implementation + */ +class DB_PUBLIC FlatEdgePairs + : public AsIfFlatEdgePairs +{ +public: + typedef db::layer edge_pair_layer_type; + typedef edge_pair_layer_type::iterator edge_pair_iterator_type; + + FlatEdgePairs (); + FlatEdgePairs (const db::Shapes &edges); + + FlatEdgePairs (const FlatEdgePairs &other); + + virtual ~FlatEdgePairs (); + + EdgePairsDelegate *clone () const + { + return new FlatEdgePairs (*this); + } + + void reserve (size_t); + + virtual EdgePairsIteratorDelegate *begin () const; + virtual std::pair begin_iter () const; + + virtual bool empty () const; + virtual size_t size () const; + + virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter); + + virtual EdgePairsDelegate *add_in_place (const EdgePairs &other); + virtual EdgePairsDelegate *add (const EdgePairs &other) const; + + virtual const db::EdgePair *nth (size_t n) const; + virtual bool has_valid_edge_pairs () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + virtual void insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const; + + void insert (const db::EdgePair &edge_pair); + void insert (const db::Shape &shape); + + template + void insert (const db::Shape &shape, const T &trans) + { + if (shape.is_edge_pair ()) { + + db::EdgePair ep; + shape.edge_pair (ep); + ep.transform (trans); + insert (ep); + + } + } + + template + void insert_seq (const Iter &seq) + { + for (Iter i = seq; ! i.at_end (); ++i) { + insert (*i); + } + } + + template + void transform (const Trans &trans) + { + if (! trans.is_unity ()) { + for (edge_pair_iterator_type p = m_edge_pairs.template get_layer ().begin (); p != m_edge_pairs.template get_layer ().end (); ++p) { + m_edge_pairs.get_layer ().replace (p, p->transformed (trans)); + } + invalidate_cache (); + } + } + + db::Shapes &raw_edge_pairs () { return m_edge_pairs; } + +protected: + virtual Box compute_bbox () const; + void invalidate_cache (); + +private: + friend class AsIfFlatEdgePairs; + + FlatEdgePairs &operator= (const FlatEdgePairs &other); + + mutable db::Shapes m_edge_pairs; +}; + +} + +#endif + diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc new file mode 100644 index 000000000..75c977bee --- /dev/null +++ b/src/db/db/dbFlatEdges.cc @@ -0,0 +1,402 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbFlatEdges.h" +#include "dbEmptyEdges.h" +#include "dbEdgeBoolean.h" +#include "dbEdges.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// FlatEdges implementation + +FlatEdges::FlatEdges () + : AsIfFlatEdges (), m_edges (false), m_merged_edges (false) +{ + init (); +} + +FlatEdges::~FlatEdges () +{ + // .. nothing yet .. +} + +FlatEdges::FlatEdges (const FlatEdges &other) + : AsIfFlatEdges (other), m_edges (false), m_merged_edges (false) +{ + init (); + + m_is_merged = other.m_is_merged; + m_edges = other.m_edges; + m_merged_edges = other.m_merged_edges; + m_merged_edges_valid = other.m_merged_edges_valid; +} + +FlatEdges::FlatEdges (const db::Shapes &edges, bool is_merged) + : AsIfFlatEdges (), m_edges (edges), m_merged_edges (false) +{ + init (); + + m_is_merged = is_merged; +} + +FlatEdges::FlatEdges (bool is_merged) + : AsIfFlatEdges (), m_edges (false), m_merged_edges (false) +{ + init (); + + m_is_merged = is_merged; +} + +void FlatEdges::set_is_merged (bool m) +{ + m_is_merged = m; +} + +void FlatEdges::invalidate_cache () +{ + invalidate_bbox (); + m_merged_edges.clear (); + m_merged_edges_valid = false; +} + +void FlatEdges::init () +{ + m_is_merged = true; + m_merged_edges_valid = false; +} + +void FlatEdges::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + layout->cell (into_cell).shapes (into_layer).insert (m_edges); +} + +void FlatEdges::merged_semantics_changed () +{ + m_merged_edges.clear (); + m_merged_edges_valid = false; +} + +void FlatEdges::reserve (size_t n) +{ + m_edges.reserve (db::Edge::tag (), n); +} + +void +FlatEdges::ensure_merged_edges_valid () const +{ + if (! m_merged_edges_valid) { + + m_merged_edges.clear (); + + db::Shapes tmp (false); + EdgeBooleanClusterCollector cluster_collector (&tmp, EdgeOr); + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (m_edges.size ()); + + for (EdgesIterator e (begin ()); ! e.at_end (); ++e) { + if (! e->is_degenerate ()) { + scanner.insert (&*e, 0); + } + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + m_merged_edges.swap (tmp); + m_merged_edges_valid = true; + + } +} + +EdgesIteratorDelegate *FlatEdges::begin () const +{ + return new FlatEdgesIterator (m_edges.get_layer ().begin (), m_edges.get_layer ().end ()); +} + +EdgesIteratorDelegate *FlatEdges::begin_merged () const +{ + if (! merged_semantics () || m_is_merged) { + return begin (); + } else { + ensure_merged_edges_valid (); + return new FlatEdgesIterator (m_merged_edges.get_layer ().begin (), m_merged_edges.get_layer ().end ()); + } +} + +std::pair FlatEdges::begin_iter () const +{ + return std::make_pair (db::RecursiveShapeIterator (m_edges), db::ICplxTrans ()); +} + +std::pair FlatEdges::begin_merged_iter () const +{ + if (! merged_semantics () || m_is_merged) { + return begin_iter (); + } else { + ensure_merged_edges_valid (); + return std::make_pair (db::RecursiveShapeIterator (m_merged_edges), db::ICplxTrans ()); + } +} + +bool FlatEdges::empty () const +{ + return m_edges.empty (); +} + +size_t FlatEdges::size () const +{ + return m_edges.size (); +} + +bool FlatEdges::is_merged () const +{ + return m_is_merged; +} + +Box FlatEdges::compute_bbox () const +{ + m_edges.update_bbox (); + return m_edges.bbox (); +} + +EdgesDelegate * +FlatEdges::processed_in_place (const EdgeProcessorBase &filter) +{ + std::vector edge_res; + + edge_iterator_type pw = m_edges.get_layer ().begin (); + for (EdgesIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) { + + edge_res.clear (); + filter.process (*p, edge_res); + + for (std::vector::const_iterator pr = edge_res.begin (); pr != edge_res.end (); ++pr) { + if (pw == m_edges.get_layer ().end ()) { + m_edges.get_layer ().insert (*pr); + pw = m_edges.get_layer ().end (); + } else { + m_edges.get_layer ().replace (pw++, *pr); + } + } + + } + + m_edges.get_layer ().erase (pw, m_edges.get_layer ().end ()); + m_merged_edges.clear (); + m_is_merged = filter.result_is_merged () && merged_semantics (); + + return this; +} + +EdgesDelegate * +FlatEdges::filter_in_place (const EdgeFilterBase &filter) +{ + edge_iterator_type pw = m_edges.get_layer ().begin (); + for (EdgesIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + if (pw == m_edges.get_layer ().end ()) { + m_edges.get_layer ().insert (*p); + pw = m_edges.get_layer ().end (); + } else { + m_edges.get_layer ().replace (pw++, *p); + } + } + } + + m_edges.get_layer ().erase (pw, m_edges.get_layer ().end ()); + m_merged_edges.clear (); + m_is_merged = merged_semantics (); + + return this; +} + +EdgesDelegate *FlatEdges::add (const Edges &other) const +{ + std::auto_ptr new_region (new FlatEdges (*this)); + new_region->invalidate_cache (); + new_region->set_is_merged (false); + + FlatEdges *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + new_region->raw_edges ().insert (other_flat->raw_edges ().get_layer ().begin (), other_flat->raw_edges ().get_layer ().end ()); + + } else { + + size_t n = new_region->raw_edges ().size (); + for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + new_region->raw_edges ().reserve (db::Edge::tag (), n); + + for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) { + new_region->raw_edges ().insert (*p); + } + + } + + return new_region.release (); +} + +EdgesDelegate *FlatEdges::add_in_place (const Edges &other) +{ + invalidate_cache (); + m_is_merged = false; + + FlatEdges *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + m_edges.insert (other_flat->raw_edges ().get_layer ().begin (), other_flat->raw_edges ().get_layer ().end ()); + + } else { + + size_t n = m_edges.size (); + for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + m_edges.reserve (db::Edge::tag (), n); + + for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) { + m_edges.insert (*p); + } + + } + + return this; +} + +const db::Edge *FlatEdges::nth (size_t n) const +{ + return n < m_edges.size () ? &m_edges.get_layer ().begin () [n] : 0; +} + +bool FlatEdges::has_valid_edges () const +{ + return true; +} + +bool FlatEdges::has_valid_merged_edges () const +{ + return true; +} + +const db::RecursiveShapeIterator *FlatEdges::iter () const +{ + return 0; +} + +void +FlatEdges::insert (const db::Box &box) +{ + if (! box.empty () && box.width () > 0 && box.height () > 0) { + + bool was_empty = empty (); + + m_edges.insert (db::Edge (box.lower_left (), box.upper_left ())); + m_edges.insert (db::Edge (box.upper_left (), box.upper_right ())); + m_edges.insert (db::Edge (box.upper_right (), box.lower_right ())); + m_edges.insert (db::Edge (box.lower_right (), box.lower_left ())); + + if (was_empty) { + + m_is_merged = true; + update_bbox (box); + + } else { + + m_is_merged = false; + invalidate_cache (); + + } + + } +} + +void +FlatEdges::insert (const db::Path &path) +{ + if (path.points () > 0) { + insert (path.polygon ()); + } +} + +void +FlatEdges::insert (const db::Polygon &polygon) +{ + if (polygon.holes () > 0 || polygon.vertices () > 0) { + for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + m_edges.insert (*e); + } + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatEdges::insert (const db::SimplePolygon &polygon) +{ + if (polygon.vertices () > 0) { + for (db::SimplePolygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + m_edges.insert (*e); + } + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatEdges::insert (const db::Edge &edge) +{ + if (! empty ()) { + m_is_merged = false; + } + + m_edges.insert (edge); + invalidate_cache (); +} + +void +FlatEdges::insert (const db::Shape &shape) +{ + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + + db::Polygon poly; + shape.polygon (poly); + insert (poly); + + } else if (shape.is_edge ()) { + + db::Edge edge; + shape.edge (edge); + insert (edge); + + } +} + +} + diff --git a/src/db/db/dbFlatEdges.h b/src/db/db/dbFlatEdges.h new file mode 100644 index 000000000..e1a80f014 --- /dev/null +++ b/src/db/db/dbFlatEdges.h @@ -0,0 +1,206 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbFlatEdges +#define HDR_dbFlatEdges + +#include "dbCommon.h" + +#include "dbAsIfFlatEdges.h" +#include "dbShapes.h" +#include "dbShapes2.h" + +namespace db { + +/** + * @brief An iterator delegate for the flat region + */ +class DB_PUBLIC FlatEdgesIterator + : public EdgesIteratorDelegate +{ +public: + typedef db::layer edge_layer_type; + typedef edge_layer_type::iterator iterator_type; + + FlatEdgesIterator (iterator_type from, iterator_type to) + : m_from (from), m_to (to) + { + // .. nothing yet .. + } + + virtual bool at_end () const + { + return m_from == m_to; + } + + virtual void increment () + { + ++m_from; + } + + virtual const value_type *get () const + { + return m_from.operator-> (); + } + + virtual EdgesIteratorDelegate *clone () const + { + return new FlatEdgesIterator (*this); + } + +private: + friend class Edges; + + iterator_type m_from, m_to; +}; + +/** + * @brief A flat, edge-set delegate + */ +class DB_PUBLIC FlatEdges + : public AsIfFlatEdges +{ +public: + typedef db::layer edge_layer_type; + typedef edge_layer_type::iterator edge_iterator_type; + + FlatEdges (); + FlatEdges (const db::Shapes &edges, bool is_merged); + FlatEdges (bool is_merged); + + FlatEdges (const FlatEdges &other); + + virtual ~FlatEdges (); + + EdgesDelegate *clone () const + { + return new FlatEdges (*this); + } + + void reserve (size_t); + + virtual EdgesIteratorDelegate *begin () const; + virtual EdgesIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + virtual size_t size () const; + virtual bool is_merged () const; + + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + + virtual EdgesDelegate *processed_in_place (const EdgeProcessorBase &filter); + virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter); + + virtual EdgesDelegate *add_in_place (const Edges &other); + virtual EdgesDelegate *add (const Edges &other) const; + + virtual const db::Edge *nth (size_t n) const; + virtual bool has_valid_edges () const; + virtual bool has_valid_merged_edges () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + void insert (const db::Box &box); + void insert (const db::Path &path); + void insert (const db::SimplePolygon &polygon); + void insert (const db::Polygon &polygon); + void insert (const db::Edge &edge); + void insert (const db::Shape &shape); + + template + void insert (const db::Shape &shape, const T &trans) + { + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + + db::Polygon poly; + shape.polygon (poly); + poly.transform (trans); + insert (poly); + + } else if (shape.is_edge ()) { + + db::Edge edge; + shape.edge (edge); + edge.transform (trans); + insert (edge); + + } + } + + template + void insert (const Iter &b, const Iter &e) + { + reserve (size () + (e - b)); + for (Iter i = b; i != e; ++i) { + insert (*i); + } + } + + template + void insert_seq (const Iter &seq) + { + for (Iter i = seq; ! i.at_end (); ++i) { + insert (*i); + } + } + + template + void transform (const Trans &trans) + { + if (! trans.is_unity ()) { + for (edge_iterator_type p = m_edges.template get_layer ().begin (); p != m_edges.get_layer ().end (); ++p) { + m_edges.get_layer ().replace (p, p->transformed (trans)); + } + invalidate_cache (); + } + } + +protected: + virtual void merged_semantics_changed (); + virtual Box compute_bbox () const; + void invalidate_cache (); + void set_is_merged (bool m); + +private: + friend class AsIfFlatEdges; + + db::Shapes &raw_edges () { return m_edges; } + + FlatEdges &operator= (const FlatEdges &other); + + bool m_is_merged; + mutable db::Shapes m_edges; + mutable db::Shapes m_merged_edges; + mutable bool m_merged_edges_valid; + + void init (); + void ensure_merged_edges_valid () const; +}; + +} + +#endif + diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc new file mode 100644 index 000000000..1ab7ebaa9 --- /dev/null +++ b/src/db/db/dbFlatRegion.cc @@ -0,0 +1,471 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbFlatRegion.h" +#include "dbEmptyRegion.h" +#include "dbRegion.h" +#include "dbShapeProcessor.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// FlatRegion implementation + +FlatRegion::FlatRegion () + : AsIfFlatRegion (), m_polygons (false), m_merged_polygons (false) +{ + init (); +} + +FlatRegion::~FlatRegion () +{ + // .. nothing yet .. +} + +FlatRegion::FlatRegion (const FlatRegion &other) + : AsIfFlatRegion (other), m_polygons (false), m_merged_polygons (false) +{ + init (); + + m_is_merged = other.m_is_merged; + m_polygons = other.m_polygons; + m_merged_polygons = other.m_merged_polygons; + m_merged_polygons_valid = other.m_merged_polygons_valid; +} + +FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged) + : AsIfFlatRegion (), m_polygons (polygons), m_merged_polygons (false) +{ + init (); + + m_is_merged = is_merged; +} + +FlatRegion::FlatRegion (bool is_merged) + : AsIfFlatRegion (), m_polygons (false), m_merged_polygons (false) +{ + init (); + + m_is_merged = is_merged; +} + +void FlatRegion::set_is_merged (bool m) +{ + m_is_merged = m; +} + +void FlatRegion::invalidate_cache () +{ + invalidate_bbox (); + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + +void FlatRegion::init () +{ + m_is_merged = true; + m_merged_polygons_valid = false; +} + +void FlatRegion::merged_semantics_changed () +{ + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + +void FlatRegion::reserve (size_t n) +{ + m_polygons.reserve (db::Polygon::tag (), n); +} + +void +FlatRegion::ensure_merged_polygons_valid () const +{ + if (! m_merged_polygons_valid) { + + m_merged_polygons.clear (); + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + ep.set_base_verbosity (base_verbosity ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + // and run the merge step + db::MergeOp op (0); + db::ShapeGenerator pc (m_merged_polygons); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + m_merged_polygons_valid = true; + + } +} + +RegionIteratorDelegate *FlatRegion::begin () const +{ + return new FlatRegionIterator (m_polygons.get_layer ().begin (), m_polygons.get_layer ().end ()); +} + +RegionIteratorDelegate *FlatRegion::begin_merged () const +{ + if (! merged_semantics () || m_is_merged) { + return begin (); + } else { + ensure_merged_polygons_valid (); + return new FlatRegionIterator (m_merged_polygons.get_layer ().begin (), m_merged_polygons.get_layer ().end ()); + } +} + +std::pair FlatRegion::begin_iter () const +{ + return std::make_pair (db::RecursiveShapeIterator (m_polygons), db::ICplxTrans ()); +} + +std::pair FlatRegion::begin_merged_iter () const +{ + if (! merged_semantics () || m_is_merged) { + return begin_iter (); + } else { + ensure_merged_polygons_valid (); + return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ()); + } +} + +bool FlatRegion::empty () const +{ + return m_polygons.empty (); +} + +size_t FlatRegion::size () const +{ + return m_polygons.size (); +} + +bool FlatRegion::is_merged () const +{ + return m_is_merged; +} + +Box FlatRegion::compute_bbox () const +{ + m_polygons.update_bbox (); + return m_polygons.bbox (); +} + +RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter) +{ + polygon_iterator_type pw = m_polygons.get_layer ().begin (); + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + if (pw == m_polygons.get_layer ().end ()) { + m_polygons.get_layer ().insert (*p); + pw = m_polygons.get_layer ().end (); + } else { + m_polygons.get_layer ().replace (pw++, *p); + } + } + } + + m_polygons.get_layer ().erase (pw, m_polygons.get_layer ().end ()); + m_merged_polygons.clear (); + m_is_merged = merged_semantics (); + + return this; +} + +RegionDelegate *FlatRegion::process_in_place (const PolygonProcessorBase &filter) +{ + std::vector poly_res; + + polygon_iterator_type pw = m_polygons.get_layer ().begin (); + for (RegionIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) { + + poly_res.clear (); + filter.process (*p, poly_res); + + for (std::vector::const_iterator pr = poly_res.begin (); pr != poly_res.end (); ++pr) { + if (pw == m_polygons.get_layer ().end ()) { + m_polygons.get_layer ().insert (*pr); + pw = m_polygons.get_layer ().end (); + } else { + m_polygons.get_layer ().replace (pw++, *pr); + } + } + + } + + m_polygons.get_layer ().erase (pw, m_polygons.get_layer ().end ()); + m_merged_polygons.clear (); + m_is_merged = filter.result_is_merged () && merged_semantics (); + + if (filter.result_must_not_be_merged ()) { + set_merged_semantics (false); + } + + return this; +} + +RegionDelegate *FlatRegion::merged_in_place () +{ + if (! m_is_merged) { + + if (m_merged_polygons_valid) { + + m_polygons.swap (m_merged_polygons); + m_merged_polygons.clear (); + m_is_merged = true; + return this; + + } else { + return merged_in_place (min_coherence (), 0); + } + + } else { + return this; + } +} + +RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int min_wc) +{ + if (empty ()) { + + // ignore empty + return new EmptyRegion (); + + } else if (is_box ()) { + + // take box only if min_wc == 0, otherwise clear + if (min_wc > 0) { + return new EmptyRegion (); + } + + } else { + + invalidate_cache (); + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + ep.set_base_verbosity (base_verbosity ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + // and run the merge step + db::MergeOp op (min_wc); + db::ShapeGenerator pc (m_polygons, true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); + ep.process (pg, op); + + m_is_merged = true; + + } + + return this; +} + +RegionDelegate *FlatRegion::merged () const +{ + if (! m_is_merged) { + + if (m_merged_polygons_valid) { + return new FlatRegion (m_merged_polygons, true); + } else { + return AsIfFlatRegion::merged (min_coherence (), 0); + } + + } else { + return clone (); + } +} + +RegionDelegate *FlatRegion::add (const Region &other) const +{ + std::auto_ptr new_region (new FlatRegion (*this)); + new_region->invalidate_cache (); + new_region->set_is_merged (false); + + FlatRegion *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + new_region->raw_polygons ().insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); + + } else { + + size_t n = new_region->raw_polygons ().size (); + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + new_region->raw_polygons ().reserve (db::Polygon::tag (), n); + + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + + } + + return new_region.release (); +} + +RegionDelegate *FlatRegion::add_in_place (const Region &other) +{ + invalidate_cache (); + m_is_merged = false; + + FlatRegion *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + m_polygons.insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); + + } else { + + size_t n = m_polygons.size (); + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + m_polygons.reserve (db::Polygon::tag (), n); + + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + m_polygons.insert (*p); + } + + } + + return this; +} + +const db::Polygon *FlatRegion::nth (size_t n) const +{ + return n < m_polygons.size () ? &m_polygons.get_layer ().begin () [n] : 0; +} + +bool FlatRegion::has_valid_polygons () const +{ + return true; +} + +bool FlatRegion::has_valid_merged_polygons () const +{ + return true; +} + +const db::RecursiveShapeIterator *FlatRegion::iter () const +{ + return 0; +} + +void FlatRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + layout->cell (into_cell).shapes (into_layer).insert (m_polygons); +} + +void +FlatRegion::insert (const db::Box &box) +{ + if (! box.empty () && box.width () > 0 && box.height () > 0) { + + if (empty ()) { + + m_polygons.insert (db::Polygon (box)); + m_is_merged = true; + update_bbox (box); + + } else { + + m_polygons.insert (db::Polygon (box)); + m_is_merged = false; + invalidate_cache (); + + } + + } +} + +void +FlatRegion::insert (const db::Path &path) +{ + if (path.points () > 0) { + m_polygons.insert (path.polygon ()); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::Polygon &polygon) +{ + if (polygon.holes () > 0 || polygon.vertices () > 0) { + m_polygons.insert (polygon); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::SimplePolygon &polygon) +{ + if (polygon.vertices () > 0) { + db::Polygon poly; + poly.assign_hull (polygon.begin_hull (), polygon.end_hull ()); + m_polygons.insert (poly); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::Shape &shape) +{ + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + db::Polygon poly; + shape.polygon (poly); + m_polygons.insert (poly); + m_is_merged = false; + invalidate_cache (); + } +} + +} + diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h new file mode 100644 index 000000000..5fe5e50ec --- /dev/null +++ b/src/db/db/dbFlatRegion.h @@ -0,0 +1,205 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbFlatRegion +#define HDR_dbFlatRegion + +#include "dbCommon.h" + +#include "dbAsIfFlatRegion.h" +#include "dbShapes.h" +#include "dbShapes2.h" + +namespace db { + +/** + * @brief An iterator delegate for the flat region + */ +class DB_PUBLIC FlatRegionIterator + : public RegionIteratorDelegate +{ +public: + typedef db::layer polygon_layer_type; + typedef polygon_layer_type::iterator iterator_type; + + FlatRegionIterator (iterator_type from, iterator_type to) + : m_from (from), m_to (to) + { + // .. nothing yet .. + } + + virtual bool at_end () const + { + return m_from == m_to; + } + + virtual void increment () + { + ++m_from; + } + + virtual const value_type *get () const + { + return m_from.operator-> (); + } + + virtual RegionIteratorDelegate *clone () const + { + return new FlatRegionIterator (*this); + } + +private: + friend class Region; + + iterator_type m_from, m_to; +}; + +/** + * @brief A flat, polygon-set delegate + */ +class DB_PUBLIC FlatRegion + : public AsIfFlatRegion +{ +public: + typedef db::layer polygon_layer_type; + typedef polygon_layer_type::iterator polygon_iterator_type; + + FlatRegion (); + FlatRegion (const db::Shapes &polygons, bool is_merged); + FlatRegion (bool is_merged); + + FlatRegion (const FlatRegion &other); + + virtual ~FlatRegion (); + + RegionDelegate *clone () const + { + return new FlatRegion (*this); + } + + void reserve (size_t); + + virtual RegionIteratorDelegate *begin () const; + virtual RegionIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + virtual size_t size () const; + virtual bool is_merged () const; + + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + + virtual RegionDelegate *merged_in_place (); + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); + virtual RegionDelegate *merged () const; + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const + { + return db::AsIfFlatRegion::merged (min_coherence, min_wc); + } + + virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter); + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter); + + virtual RegionDelegate *add_in_place (const Region &other); + virtual RegionDelegate *add (const Region &other) const; + + virtual const db::Polygon *nth (size_t n) const; + virtual bool has_valid_polygons () const; + virtual bool has_valid_merged_polygons () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + void insert (const db::Box &box); + void insert (const db::Path &path); + void insert (const db::SimplePolygon &polygon); + void insert (const db::Polygon &polygon); + void insert (const db::Shape &shape); + + template + void insert (const db::Shape &shape, const T &trans) + { + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + db::Polygon poly; + shape.polygon (poly); + poly.transform (trans); + insert (poly); + } + } + + template + void insert (const Iter &b, const Iter &e) + { + reserve (size () + (e - b)); + for (Iter i = b; i != e; ++i) { + insert (*i); + } + } + + template + void insert_seq (const Iter &seq) + { + for (Iter i = seq; ! i.at_end (); ++i) { + insert (*i); + } + } + + template + void transform (const Trans &trans) + { + if (! trans.is_unity ()) { + for (polygon_iterator_type p = m_polygons.get_layer ().begin (); p != m_polygons.get_layer ().end (); ++p) { + m_polygons.get_layer ().replace (p, p->transformed (trans)); + } + invalidate_cache (); + } + } + + db::Shapes &raw_polygons () { return m_polygons; } + +protected: + virtual void merged_semantics_changed (); + virtual Box compute_bbox () const; + void invalidate_cache (); + void set_is_merged (bool m); + +private: + friend class AsIfFlatRegion; + friend class Region; + + FlatRegion &operator= (const FlatRegion &other); + + bool m_is_merged; + mutable db::Shapes m_polygons; + mutable db::Shapes m_merged_polygons; + mutable bool m_merged_polygons_valid; + + void init (); + void ensure_merged_polygons_valid () const; +}; + +} + +#endif + diff --git a/src/db/db/dbHash.h b/src/db/db/dbHash.h index ef46366d3..5c14ce12a 100644 --- a/src/db/db/dbHash.h +++ b/src/db/db/dbHash.h @@ -238,6 +238,18 @@ namespace std } }; + /** + * @brief A hash function for a displacement transformation + */ + template + struct hash > + { + size_t operator() (const db::disp_trans &t) const + { + return hfunc (t.disp ()); + } + }; + /** * @brief Hash value for a complex transformation */ @@ -295,6 +307,54 @@ namespace std } }; + /** + * @brief A hash function for a shape reference + */ + template + struct hash > + { + size_t operator() (const db::shape_ref &o) const + { + return hfunc (std::hash () (*o.ptr ()), std::hash () (o.trans ())); + } + }; + + /** + * @brief A hash function for a polygon reference + */ + template + struct hash > + { + size_t operator() (const db::polygon_ref &o) const + { + return std::hash > () (o); + } + }; + + /** + * @brief A hash function for a path reference + */ + template + struct hash > + { + size_t operator() (const db::path_ref &o) const + { + return std::hash > () (o); + } + }; + + /** + * @brief A hash function for a text reference + */ + template + struct hash > + { + size_t operator() (const db::text_ref &o) const + { + return std::hash > () (o); + } + }; + /** * @brief A hash value for a db::LayerProperties object */ @@ -328,6 +388,38 @@ namespace std return hfunc (hf2 (p.second), h); } }; + + /** + * @brief Generic hash for an unordered set + */ + template + struct hash > + { + size_t operator() (const std::unordered_set &o) const + { + size_t hf = 0; + for (typename std::unordered_set::const_iterator i = o.begin (); i != o.end (); ++i) { + hf = hfunc (hf, std::hash () (*i)); + } + return hf; + } + }; + + /** + * @brief Generic hash for an ordered set + */ + template + struct hash > + { + size_t operator() (const std::set &o) const + { + size_t hf = 0; + for (typename std::set::const_iterator i = o.begin (); i != o.end (); ++i) { + hf = hfunc (hf, std::hash () (*i)); + } + return hf; + } + }; } #endif diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc new file mode 100644 index 000000000..45c2623e0 --- /dev/null +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -0,0 +1,2411 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbHierNetworkProcessor.h" +#include "dbShape.h" +#include "dbShapes.h" +#include "dbInstElement.h" +#include "dbPolygon.h" +#include "dbPolygonTools.h" +#include "dbBoxScanner.h" +#include "dbDeepRegion.h" +#include "tlProgress.h" +#include "tlLog.h" +#include "tlTimer.h" + +#include +#include +#include +#include + +namespace db +{ + +// ------------------------------------------------------------------------------ +// Connectivity implementation + +Connectivity::Connectivity () + : m_ec (Connectivity::EdgesConnectCollinear) +{ + // .. nothing yet .. +} + +Connectivity::Connectivity (edge_connectivity_type ec) + : m_ec (ec) +{ + // .. nothing yet .. +} + +void +Connectivity::connect (unsigned int la, unsigned int lb) +{ + m_connected [la].insert (lb); + m_connected [lb].insert (la); + m_all_layers.insert (la); + m_all_layers.insert (lb); +} + +void +Connectivity::connect (unsigned int l) +{ + m_connected [l].insert (l); + m_all_layers.insert (l); +} + +void +Connectivity::connect (const db::DeepLayer &l) +{ + connect (l.layer ()); +} + +void +Connectivity::connect (const db::DeepLayer &la, const db::DeepLayer &lb) +{ + connect (la.layer (), lb.layer ()); +} + +Connectivity::global_nets_type s_empty_global_nets; + +Connectivity::global_nets_iterator +Connectivity::begin_global_connections (unsigned int l) const +{ + std::map::const_iterator g = m_global_connections.find (l); + if (g != m_global_connections.end ()) { + return g->second.begin (); + } else { + return s_empty_global_nets.begin (); + } +} + +Connectivity::global_nets_iterator +Connectivity::end_global_connections (unsigned int l) const +{ + std::map::const_iterator g = m_global_connections.find (l); + if (g != m_global_connections.end ()) { + return g->second.end (); + } else { + return s_empty_global_nets.end (); + } +} + +size_t +Connectivity::connect_global (unsigned int l, const std::string &gn) +{ + size_t id = global_net_id (gn); + m_global_connections [l].insert (id); + m_all_layers.insert (l); + return id; +} + +size_t +Connectivity::connect_global (const db::DeepLayer &l, const std::string &gn) +{ + return connect_global (l.layer (), gn); +} + +const std::string & +Connectivity::global_net_name (size_t id) const +{ + tl_assert (id < m_global_net_names.size ()); + return m_global_net_names [id]; +} + +size_t +Connectivity::global_net_id (const std::string &gn) +{ + for (std::vector::const_iterator i = m_global_net_names.begin (); i != m_global_net_names.end (); ++i) { + if (*i == gn) { + size_t id = i - m_global_net_names.begin (); + return id; + } + } + + size_t id = m_global_net_names.size (); + m_global_net_names.push_back (gn); + return id; +} + +Connectivity::layer_iterator +Connectivity::begin_layers () const +{ + return m_all_layers.begin (); +} + +Connectivity::layer_iterator +Connectivity::end_layers () const +{ + return m_all_layers.end (); +} + +Connectivity::layers_type s_empty_layers; + +Connectivity::layer_iterator +Connectivity::begin_connected (unsigned int layer) const +{ + std::map::const_iterator i = m_connected.find (layer); + if (i == m_connected.end ()) { + return s_empty_layers.begin (); + } else { + return i->second.begin (); + } +} + +Connectivity::layer_iterator +Connectivity::end_connected (unsigned int layer) const +{ + std::map::const_iterator i = m_connected.find (layer); + if (i == m_connected.end ()) { + return s_empty_layers.end (); + } else { + return i->second.end (); + } +} + +template +static bool +interaction_test (const db::PolygonRef &a, const db::PolygonRef &b, const Trans &trans, db::Connectivity::edge_connectivity_type) +{ + // TODO: this could be part of db::interact (including transformation) + if (a.obj ().is_box () && b.obj ().is_box ()) { + return db::interact (a.obj ().box ().transformed (a.trans ()), b.obj ().box ().transformed (trans * Trans (b.trans ()))); + } else { + return db::interact (a.obj ().transformed (a.trans ()), b.obj ().transformed (trans * Trans (b.trans ()))); + } +} + +template +static bool +interaction_test (const db::PolygonRef &a, const db::PolygonRef &b, const db::unit_trans &, db::Connectivity::edge_connectivity_type) +{ + // TODO: this could be part of db::interact (including transformation) + if (a.obj ().is_box () && b.obj ().is_box ()) { + return db::interact (a.obj ().box ().transformed (a.trans ()), b.obj ().box ().transformed (b.trans ())); + } else { + return db::interact (a.obj ().transformed (a.trans ()), b.obj ().transformed (b.trans ())); + } +} + +template +static bool +interaction_test (const db::Edge &a, const db::Edge &b, const Trans &trans, db::Connectivity::edge_connectivity_type ec) +{ + db::Edge bt = b.transformed (trans); + if (ec == db::Connectivity::EdgesConnectByPoints) { + return a.p2 () == bt.p1 () || a.p1 () == bt.p2 (); + } else { + return a.parallel (bt) && a.intersect (bt); + } +} + +template +static bool +interaction_test (const db::Edge &a, const db::Edge &b, const db::unit_trans &, db::Connectivity::edge_connectivity_type ec) +{ + if (ec == db::Connectivity::EdgesConnectByPoints) { + return a.p2 () == b.p1 () || a.p1 () == b.p2 (); + } else { + return a.parallel (b) && a.intersect (b); + } +} + +template +bool Connectivity::interacts (const T &a, unsigned int la, const T &b, unsigned int lb, const Trans &trans) const +{ + std::map::const_iterator i = m_connected.find (la); + if (i == m_connected.end () || i->second.find (lb) == i->second.end ()) { + return false; + } else { + return interaction_test (a, b, trans, m_ec); + } +} + +// explicit instantiations +template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, const db::PolygonRef &b, unsigned int lb, const db::UnitTrans &trans) const; +template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, const db::PolygonRef &b, unsigned int lb, const db::ICplxTrans &trans) const; +template DB_PUBLIC bool Connectivity::interacts (const db::Edge &a, unsigned int la, const db::Edge &b, unsigned int lb, const db::UnitTrans &trans) const; +template DB_PUBLIC bool Connectivity::interacts (const db::Edge &a, unsigned int la, const db::Edge &b, unsigned int lb, const db::ICplxTrans &trans) const; + +// ------------------------------------------------------------------------------ +// local_cluster implementation + +template +local_cluster::local_cluster (size_t id) + : m_id (id), m_needs_update (false), m_size (0) +{ + // .. nothing yet .. +} + +template +void +local_cluster::clear () +{ + m_shapes.clear (); + m_needs_update = false; + m_size = 0; + m_bbox = box_type (); + m_attrs.clear (); + m_global_nets.clear (); +} + +template +void +local_cluster::set_global_nets (const global_nets &gn) +{ + m_global_nets = gn; +} + +template +void +local_cluster::add_attr (attr_id a) +{ + if (a > 0) { + m_attrs.insert (a); + } +} + +template +void +local_cluster::add (const T &s, unsigned int la) +{ + m_shapes[la].insert (s); + m_needs_update = true; + ++m_size; +} + +template +void +local_cluster::join_with (const local_cluster &other) +{ + for (typename std::map::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) { + tree_type &tree = m_shapes[s->first]; + tree.insert (s->second.begin (), s->second.end ()); + } + + m_attrs.insert (other.m_attrs.begin (), other.m_attrs.end ()); + m_global_nets.insert (other.m_global_nets.begin (), other.m_global_nets.end ()); + m_size += other.size (); + + m_needs_update = true; +} + +template +void +local_cluster::ensure_sorted () +{ + if (! m_needs_update) { + return; + } + + // sort the shape trees + for (typename std::map::iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + s->second.sort (db::box_convert ()); + } + + // recompute bounding box + m_bbox = box_type (); + db::box_convert bc; + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + for (typename tree_type::const_iterator i = s->second.begin (); i != s->second.end (); ++i) { + m_bbox += bc (*i); + } + } + + m_needs_update = false; +} + +template +class DB_PUBLIC hnp_interaction_receiver + : public box_scanner_receiver2 +{ +public: + typedef typename local_cluster::box_type box_type; + + hnp_interaction_receiver (const Connectivity &conn, const db::ICplxTrans &trans) + : mp_conn (&conn), m_any (false), m_trans (trans) + { + // .. nothing yet .. + } + + void add (const T *s1, unsigned int l1, const T *s2, unsigned int l2) + { + if (mp_conn->interacts (*s1, l1, *s2, l2, m_trans)) { + m_any = true; + } + } + + bool stop () const + { + return m_any; + } + +private: + const Connectivity *mp_conn; + bool m_any; + db::ICplxTrans m_trans; +}; + +template +struct transformed_box +{ + typedef db::box_convert base_bc; + typedef typename T::box_type box_type; + + transformed_box (const Trans &trans) + : m_trans (trans) + { + // .. nothing yet .. + } + + box_type operator() (const T &t) const + { + return m_bc (t).transformed (m_trans); + } + +private: + base_bc m_bc; + Trans m_trans; +}; + +template +typename local_cluster::shape_iterator local_cluster::begin (unsigned int l) const +{ + static tree_type s_empty_tree; + + typename std::map::const_iterator i = m_shapes.find (l); + if (i == m_shapes.end ()) { + return s_empty_tree.begin_flat (); + } else { + return i->second.begin_flat (); + } +} + +template +bool +local_cluster::interacts (const db::Cell &cell, const db::ICplxTrans &trans, const Connectivity &conn) const +{ + db::box_convert bc; + + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + + db::Box box; + + Connectivity::layer_iterator le = conn.end_connected (s->first); + for (Connectivity::layer_iterator l = conn.begin_connected (s->first); l != le; ++l) { + box += cell.bbox (*l); + } + + if (! box.empty () && ! s->second.begin_touching (box.transformed (trans), bc).at_end ()) { + return true; + } + + } + + return false; +} + +template +bool +local_cluster::interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn) const +{ + db::box_convert bc; + + const_cast *> (this)->ensure_sorted (); + + box_type common = other.bbox ().transformed (trans) & bbox (); + if (common.empty ()) { + return false; + } + + box_type common_for_other = common.transformed (trans.inverted ()); + + // shortcut evaluation for disjunct layers + + std::set ll1; + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + if (! s->second.begin_touching (common, bc).at_end ()) { + ll1.insert (s->first); + } + } + + if (ll1.empty ()) { + return false; + } + + std::set ll2; + for (typename std::map::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) { + if (! s->second.begin_touching (common_for_other, bc).at_end ()) { + ll2.insert (s->first); + } + } + + if (ll2.empty ()) { + return false; + } + + bool any = false; + + for (std::set::const_iterator i = ll1.begin (); i != ll1.end () && !any; ++i) { + db::Connectivity::layer_iterator je = conn.end_connected (*i); + for (db::Connectivity::layer_iterator j = conn.begin_connected (*i); j != je && !any; ++j) { + any = (ll2.find (*j) != ll2.end ()); + } + } + if (! any) { + return false; + } + + // detailed analysis + + db::box_scanner2 scanner; + transformed_box bc_t (trans); + + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + for (typename tree_type::touching_iterator i = s->second.begin_touching (common, bc); ! i.at_end (); ++i) { + scanner.insert1 (i.operator-> (), s->first); + } + } + + for (typename std::map::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) { + for (typename tree_type::touching_iterator i = s->second.begin_touching (common_for_other, bc); ! i.at_end (); ++i) { + scanner.insert2 (i.operator-> (), s->first); + } + } + + hnp_interaction_receiver rec (conn, trans); + return ! scanner.process (rec, 1 /*==touching*/, bc, bc_t); +} + +template +double local_cluster::area_ratio () const +{ + box_type bx = bbox (); + if (bx.empty ()) { + return 0.0; + } + + db::box_convert bc; + + // just the sum of the areas of the bounding boxes - this is precise if no overlaps happen and the + // polygons are rather rectangular. This criterion is coarse enough to prevent recursion in the split + // algorithm and still be fine enough - consider that we a planning to use splitted polygons for + // which the bbox is a fairly good approximation. + typename box_type::area_type a = 0; + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + for (typename tree_type::const_iterator i = s->second.begin (); i != s->second.end (); ++i) { + a += bc (*i).area (); + } + } + + return (a == 0 ? 0.0 : double (bx.area ()) / double (a)); +} + +template +std::vector +local_cluster::layers () const +{ + std::vector l; + l.reserve (m_shapes.size ()); + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + l.push_back (s->first); + } + return l; +} + +template +size_t split_cluster (const local_cluster &cl, double max_area_ratio, Iter &output) +{ + if (cl.area_ratio () < max_area_ratio) { + return 0; // no splitting happened + } + + db::box_convert bc; + typename local_cluster::box_type bx = cl.bbox (); + + int xthr = bx.width () > bx.height () ? bx.center ().x () : bx.left (); + int ythr = bx.width () > bx.height () ? bx.bottom () : bx.center ().y (); + + // split along the longer axis - decide the position according to the bbox center + + local_cluster a (cl.id ()), b (cl.id ()); + + std::vector layers = cl.layers (); + + for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + for (typename local_cluster::shape_iterator s = cl.begin (*l); ! s.at_end (); ++s) { + typename local_cluster::box_type::point_type sc = bc (*s).center (); + if (sc.x () < xthr || sc.y () < ythr) { + a.add (*s, *l); + } else { + b.add (*s, *l); + } + } + } + + if (a.size () == 0 || b.size () == 0) { + // give up to prevent infinite recursion + return 0; + } + + // split further if required + size_t na = split_cluster (a, max_area_ratio, output); + size_t nb = split_cluster (b, max_area_ratio, output); + + if (na == 0) { + *output++ = a; + na = 1; + } + + if (nb == 0) { + *output++ = b; + nb = 1; + } + + return na + nb; +} + +template +template +size_t local_cluster::split (double max_area_ratio, Iter &output) const +{ + return split_cluster (*this, max_area_ratio, output); +} + +// explicit instantiations +template class DB_PUBLIC local_cluster; +template class DB_PUBLIC local_cluster; +template DB_PUBLIC size_t local_cluster::split > > > (double, std::back_insert_iterator > > &) const; +template DB_PUBLIC size_t local_cluster::split > > > (double, std::back_insert_iterator > > &) const; + +// ------------------------------------------------------------------------------ +// local_clusters implementation + +template +local_clusters::local_clusters () + : m_needs_update (false), m_next_dummy_id (0) +{ + // .. nothing yet .. +} + +template +void local_clusters::clear () +{ + m_needs_update = false; + m_clusters.clear (); + m_bbox = box_type (); + m_next_dummy_id = 0; +} + +template +const local_cluster & +local_clusters::cluster_by_id (typename local_cluster::id_type id) const +{ + tl_assert (id > 0); + + if (id > m_clusters.size ()) { + + // dummy connectors are not real ones - they just carry an arbitrary + // ID. Still they need to be treated as empty ones. + static local_cluster empty_cluster; + return empty_cluster; + + } else { + + // by convention the ID is the index + 1 so 0 can be used as "nil" + return m_clusters.objects ().item (id - 1); + + } +} + +template +void +local_clusters::remove_cluster (typename local_cluster::id_type id) +{ + if (id == 0 || id > m_clusters.size ()) { + return; + } + + // TODO: this const_cast is required. But we know what we're doing ... + // NOTE: we cannot really delete a cluster as this would shift the indexes. So + // we just clear them. + local_cluster *to_delete = const_cast *> (& m_clusters.objects ().item (id - 1)); + to_delete->clear (); + m_needs_update = true; +} + +template +void +local_clusters::join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id) +{ + tl_assert (id > 0); + if (with_id == 0 || with_id > m_clusters.size () || id > m_clusters.size ()) { + return; + } + + // TODO: this const_cast is required. But we know what we're doing ... + local_cluster *with = const_cast *> (& m_clusters.objects ().item (with_id - 1)); + local_cluster *first = const_cast *> (& m_clusters.objects ().item (id - 1)); + first->join_with (*with); + + // NOTE: we cannot really delete a cluster as this would shift the indexes. So + // we just clear them. + with->clear (); + + m_needs_update = true; +} + +template +local_cluster * +local_clusters::insert () +{ + typename tree_type::iterator i = m_clusters.insert (local_cluster ()); + i->set_id (i.index () + 1); + m_needs_update = true; + return i.operator-> (); +} + +template +void +local_clusters::ensure_sorted () +{ + if (! m_needs_update) { + return; + } + + // sort the shape trees + m_clusters.sort (local_cluster_box_convert ()); + + // recompute bounding box + m_bbox = box_type (); + for (typename tree_type::const_iterator i = m_clusters.begin (); i != m_clusters.end (); ++i) { + m_bbox += i->bbox (); + } + + m_needs_update = false; +} + +namespace +{ + +template +struct cluster_building_receiver + : public db::box_scanner_receiver > +{ + typedef typename local_cluster::id_type id_type; + typedef std::pair > shape_value; + typedef std::vector shape_vector; + typedef std::set global_nets; + typedef std::pair cluster_value; + + cluster_building_receiver (const db::Connectivity &conn) + : mp_conn (&conn) + { + // .. nothing yet.. + } + + void generate_clusters (local_clusters &clusters) + { + // build the resulting clusters + for (typename std::list::const_iterator c = m_clusters.begin (); c != m_clusters.end (); ++c) { + + // TODO: reserve? + local_cluster *cluster = clusters.insert (); + for (typename shape_vector::const_iterator s = c->first.begin (); s != c->first.end (); ++s) { + cluster->add (*s->first, s->second.first); + cluster->add_attr (s->second.second); + } + + cluster->set_global_nets (c->second); + + } + } + + void add (const T *s1, std::pair p1, const T *s2, std::pair p2) + { + if (! mp_conn->interacts (*s1, p1.first, *s2, p2.first)) { + return; + } + + typename std::map::iterator>::iterator ic1 = m_shape_to_clusters.find (s1); + typename std::map::iterator>::iterator ic2 = m_shape_to_clusters.find (s2); + + if (ic1 == m_shape_to_clusters.end ()) { + + if (ic2 == m_shape_to_clusters.end ()) { + + m_clusters.push_back (cluster_value ()); + typename std::list::iterator c = --m_clusters.end (); + c->first.push_back (std::make_pair (s1, p1)); + c->first.push_back (std::make_pair (s2, p2)); + + m_shape_to_clusters.insert (std::make_pair (s1, c)); + m_shape_to_clusters.insert (std::make_pair (s2, c)); + + } else { + + ic2->second->first.push_back (std::make_pair (s1, p1)); + m_shape_to_clusters.insert (std::make_pair (s1, ic2->second)); + + } + + } else if (ic2 == m_shape_to_clusters.end ()) { + + ic1->second->first.push_back (std::make_pair (s2, p2)); + m_shape_to_clusters.insert (std::make_pair (s2, ic1->second)); + + } else if (ic1->second != ic2->second) { + + // join clusters: use the larger one as the target + + if (ic1->second->first.size () < ic2->second->first.size ()) { + join (ic2->second, ic1->second); + } else { + join (ic1->second, ic2->second); + } + + } + } + + void finish (const T *s, std::pair p) + { + // if the shape has not been handled yet, insert a single cluster with only this shape + typename std::map::iterator>::iterator ic = m_shape_to_clusters.find (s); + if (ic == m_shape_to_clusters.end ()) { + + m_clusters.push_back (cluster_value ()); + typename std::list::iterator c = --m_clusters.end (); + c->first.push_back (std::make_pair (s, p)); + + ic = m_shape_to_clusters.insert (std::make_pair (s, c)).first; + + } + + // consider connections to global nets + + db::Connectivity::global_nets_iterator ge = mp_conn->end_global_connections (p.first); + for (db::Connectivity::global_nets_iterator g = mp_conn->begin_global_connections (p.first); g != ge; ++g) { + + typename std::map::iterator>::iterator icg = m_global_to_clusters.find (*g); + + if (icg == m_global_to_clusters.end ()) { + + ic->second->second.insert (*g); + m_global_to_clusters.insert (std::make_pair (*g, ic->second)); + + } else if (ic->second != icg->second) { + + // join clusters + if (ic->second->first.size () < icg->second->first.size ()) { + join (icg->second, ic->second); + } else { + join (ic->second, icg->second); + } + + } + + } + } + +private: + const db::Connectivity *mp_conn; + std::map::iterator> m_shape_to_clusters; + std::map::iterator> m_global_to_clusters; + std::list m_clusters; + + void join (typename std::list::iterator ic1, typename std::list::iterator ic2) + { + ic1->first.insert (ic1->first.end (), ic2->first.begin (), ic2->first.end ()); + ic1->second.insert (ic2->second.begin (), ic2->second.end ()); + + for (typename shape_vector::const_iterator i = ic2->first.begin (); i != ic2->first.end (); ++i) { + m_shape_to_clusters [i->first] = ic1; + } + for (typename global_nets::const_iterator i = ic2->second.begin (); i != ic2->second.end (); ++i) { + m_global_to_clusters [*i] = ic1; + } + + m_clusters.erase (ic2); + } +}; + +} + +template +void +local_clusters::build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence, bool report_progress) +{ + static std::string desc = tl::to_string (tr ("Building local clusters")); + + db::box_scanner > bs (report_progress, desc); + typename T::tag object_tag; + db::box_convert bc; + + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + const db::Shapes &shapes = cell.shapes (*l); + for (db::Shapes::shape_iterator s = shapes.begin (shape_flags); ! s.at_end (); ++s) { + bs.insert (s->basic_ptr (object_tag), std::make_pair (*l, (unsigned int) s->prop_id ())); + } + } + + cluster_building_receiver rec (conn); + bs.process (rec, 1 /*==touching*/, bc); + rec.generate_clusters (*this); + + if (attr_equivalence && attr_equivalence->size () > 0) { + apply_attr_equivalences (*attr_equivalence); + } +} + +template +void +local_clusters::apply_attr_equivalences (const tl::equivalence_clusters &attr_equivalence) +{ + tl::equivalence_clusters eq; + + // collect all local attributes (the ones which are present in attr_equivalence) into "eq" + // and form equivalences for multi-attribute clusters. + for (const_iterator c = begin (); c != end (); ++c) { + typename local_cluster::attr_iterator a0; + for (typename local_cluster::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) { + if (attr_equivalence.has_attribute (*a)) { + if (a0 == typename local_cluster::attr_iterator ()) { + a0 = a; + } + eq.same (*a0, *a); + } + } + } + + // apply the equivalences implied by attr_equivalence + eq.apply_equivalences (attr_equivalence); + + // identify the layout clusters joined into one attribute cluster and join them + + std::map::cluster_id_type, std::set > c2c; + + for (const_iterator c = begin (); c != end (); ++c) { + for (typename local_cluster::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) { + tl::equivalence_clusters::cluster_id_type cl = attr_equivalence.cluster_id (*a); + if (cl > 0) { + c2c [cl].insert (c->id ()); + } + } + } + + for (std::map::cluster_id_type, std::set >::const_iterator c = c2c.begin (); c != c2c.end (); ++c) { + if (c->second.size () > 1) { + std::set::const_iterator cl0 = c->second.begin (); + std::set::const_iterator cl = cl0; + while (++cl != c->second.end ()) { + join_cluster_with (*cl0, *cl); + } + } + } +} + +// explicit instantiations +template class DB_PUBLIC local_clusters; +template class DB_PUBLIC local_clusters; + +// ------------------------------------------------------------------------------ +// connected_clusters_iterator implementation + +template +connected_clusters_iterator::connected_clusters_iterator (const connected_clusters &c) + : m_lc_iter (c.begin ()) +{ + size_t max_id = 0; + for (typename connected_clusters::const_iterator i = c.begin (); i != c.end (); ++i) { + if (i->id () > max_id) { + max_id = i->id (); + } + } + + m_x_iter = c.m_connections.lower_bound (max_id + 1); + m_x_iter_end = c.m_connections.end (); +} + +// explicit instantiations +template class DB_PUBLIC connected_clusters_iterator; +template class DB_PUBLIC connected_clusters_iterator; + +// ------------------------------------------------------------------------------ +// connected_clusters implementation + +template +const typename connected_clusters::connections_type & +connected_clusters::connections_for_cluster (typename local_cluster::id_type id) const +{ + typename std::map::id_type, connections_type>::const_iterator c = m_connections.find (id); + if (c == m_connections.end ()) { + static connections_type empty_connections; + return empty_connections; + } else { + return c->second; + } +} + +template +void +connected_clusters::add_connection (typename local_cluster::id_type id, const ClusterInstance &inst) +{ + m_connections [id].push_back (inst); + m_rev_connections [inst] = id; +} + +template +void +connected_clusters::join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id) +{ + if (id == with_id) { + return; + } + + // join the shape clusters + local_clusters::join_cluster_with (id, with_id); + + // handle the connections by translating + + const connections_type &to_join = connections_for_cluster (with_id); + connections_type &target = m_connections [id]; + target.insert (target.end (), to_join.begin (), to_join.end ()); + + for (connections_type::const_iterator c = to_join.begin (); c != to_join.end (); ++c) { + m_rev_connections [*c] = id; + } + + m_connections.erase (with_id); +} + +template +typename local_cluster::id_type +connected_clusters::find_cluster_with_connection (const ClusterInstance &inst) const +{ + typename std::map::id_type>::const_iterator rc = m_rev_connections.find (inst); + if (rc != m_rev_connections.end ()) { + return rc->second; + } else { + return 0; + } +} + +// explicit instantiations +template class DB_PUBLIC connected_clusters; +template class DB_PUBLIC connected_clusters; + +// ------------------------------------------------------------------------------ +// connected_clusters implementation + +template +class DB_PUBLIC cell_clusters_box_converter +{ +public: + typedef db::simple_bbox_tag complexity; + typedef typename hier_clusters::box_type box_type; + + cell_clusters_box_converter (const db::Layout &layout, const hier_clusters &tree) + : mp_layout (&layout), mp_tree (&tree) + { + // .. nothing yet .. + } + + const box_type &operator() (const db::CellInst &cell_inst) const + { + return (*this) (cell_inst.cell_index ()); + } + + const box_type &operator() (db::cell_index_type cell_index) const + { + typename std::map::const_iterator b = m_cache.find (cell_index); + if (b != m_cache.end ()) { + + return b->second; + + } else { + + const connected_clusters &clusters = mp_tree->clusters_per_cell (cell_index); + box_type box = clusters.bbox (); + + const db::Cell &cell = mp_layout->cell (cell_index); + for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) { + const db::CellInstArray &inst_array = inst->cell_inst (); + box += inst_array.bbox (*this); + } + + return m_cache.insert (std::make_pair (cell_index, box)).first->second; + + } + } + +private: + mutable std::map m_cache; + const db::Layout *mp_layout; + const hier_clusters *mp_tree; +}; + +// ------------------------------------------------------------------------------ +// hier_clusters implementation + +template +hier_clusters::hier_clusters () + : m_base_verbosity (20) +{ + // .. nothing yet .. +} + +template +void hier_clusters::set_base_verbosity (int bv) +{ + m_base_verbosity = bv; +} + +template +void hier_clusters::clear () +{ + m_per_cell_clusters.clear (); +} + +template +void +hier_clusters::build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence) +{ + clear (); + cell_clusters_box_converter cbc (layout, *this); + do_build (cbc, layout, cell, shape_flags, conn, attr_equivalence); +} + +namespace +{ + +/** + * @brief The central interaction tester between clusters on a hierarchical level + * + * This receiver is both used for the instance-to-instance and the local-to-instance + * interactions. It is employed on cell level for in two box scanners: one + * investigating the instance-to-instance interactions and another one invesitating + * local cluster to instance interactions. + */ +template +struct hc_receiver + : public db::box_scanner_receiver, + public db::box_scanner_receiver2, unsigned int, db::Instance, unsigned int> +{ +public: + typedef typename hier_clusters::box_type box_type; + typedef typename local_cluster::id_type id_type; + typedef std::map connector_map; + + struct ClusterInstanceInteraction + { + ClusterInstanceInteraction (size_t _cluster_id, size_t _other_cluster_id, const std::vector &_other_path) + : cluster_id (_cluster_id), other_cluster_id (_other_cluster_id), other_path (_other_path) + { } + + size_t cluster_id; + size_t other_cluster_id; + std::vector other_path; + }; + + /** + * @brief Constructor + */ + hc_receiver (const db::Layout &layout, const db::Cell &cell, db::connected_clusters &cell_clusters, hier_clusters &tree, const cell_clusters_box_converter &cbc, const db::Connectivity &conn) + : mp_layout (&layout), mp_cell (&cell), mp_tree (&tree), mp_cbc (&cbc), mp_conn (&conn) + { + mp_cell_clusters = &cell_clusters; + } + + /** + * @brief Receiver main event for instance-to-instance interactions + */ + void add (const db::Instance *i1, unsigned int /*p1*/, const db::Instance *i2, unsigned int /*p2*/) + { + std::vector p; + db::ICplxTrans t; + add_pair (box_type::world (), *i1, p, t, *i2, p, t); + } + + /** + * @brief Single-instance treatment - may be required because of interactions between array members + */ + void finish (const db::Instance *i, unsigned int /*p1*/) + { + if (i->size () > 1) { + add_single_inst (*i); + } + } + + /** + * @brief Receiver main event for local-to-instance interactions + */ + void add (const local_cluster *c1, unsigned int /*p1*/, const db::Instance *i2, unsigned int /*p2*/) + { + std::vector p; + db::ICplxTrans t; + add_pair (*c1, *i2, p, t); + } + + bool stop () const + { + return false; + } + + /** + * @brief Finally join the clusters in the join set + * + * This step is postponed because doing this while the iteration happens would + * invalidate the box trees. + */ + void finish_cluster_to_instance_interactions () + { + for (typename std::list::const_iterator ii = m_ci_interactions.begin (); ii != m_ci_interactions.end (); ++ii) { + + ClusterInstance other_key = make_path (ii->other_cluster_id, ii->other_path); + + id_type other = mp_cell_clusters->find_cluster_with_connection (other_key); + if (other > 0) { + + // we found a child cluster that connects two clusters on our own level: + // we must join them into one, but not now. We're still iterating and + // would invalidate the box trees. So keep this now and combine the clusters later. + mark_to_join (other, ii->cluster_id); + + } else { + mp_cell_clusters->add_connection (ii->cluster_id, other_key); + } + + } + + for (typename std::list >::const_iterator sc = m_cm2join_sets.begin (); sc != m_cm2join_sets.end (); ++sc) { + + if (sc->empty ()) { + // dropped ones are empty + continue; + } + + typename std::set::const_iterator c = sc->begin (); + typename std::set::const_iterator cc = c; + for (++cc; cc != sc->end (); ++cc) { + mp_cell_clusters->join_cluster_with (*c, *cc); + } + + } + } + +private: + const db::Layout *mp_layout; + const db::Cell *mp_cell; + db::connected_clusters *mp_cell_clusters; + hier_clusters *mp_tree; + const cell_clusters_box_converter *mp_cbc; + const db::Connectivity *mp_conn; + typedef std::list > join_set_list; + std::map m_cm2join_map; + join_set_list m_cm2join_sets; + std::list m_ci_interactions; + + /** + * @brief Handles the cluster interactions between two instances or instance arrays + * @param common The region under investigation (seen from the top level) + * @param i1 The index of the child cell 1 + * @param p1 The instantiation path to the child cell (not including i1) + * @param t1 The accumulated transformation of the path, not including i1 + * @param i2 The index of the child cell 2 + * @param p2 The instantiation path to the child cell (not including i2) + * @param t2 The accumulated transformation of the path, not including i2 + */ + void add_pair (const box_type &common, const db::Instance &i1, const std::vector &p1, const db::ICplxTrans &t1, const db::Instance &i2, const std::vector &p2, const db::ICplxTrans &t2) + { + box_type bb1 = (*mp_cbc) (i1.cell_index ()); + box_type b1 = i1.cell_inst ().bbox (*mp_cbc).transformed (t1); + + box_type bb2 = (*mp_cbc) (i2.cell_index ()); + box_type b2 = i2.cell_inst ().bbox (*mp_cbc).transformed (t2); + + box_type common_all = common & b1 & b2; + + if (common_all.empty ()) { + return; + } + + db::ICplxTrans t1i = t1.inverted (); + db::ICplxTrans t2i = t2.inverted (); + + for (db::CellInstArray::iterator ii1 = i1.begin_touching (common_all.transformed (t1i), mp_layout); ! ii1.at_end (); ++ii1) { + + db::ICplxTrans tt1 = t1 * i1.complex_trans (*ii1); + box_type ib1 = bb1.transformed (tt1); + + std::vector pp1; + pp1.reserve (p1.size () + 1); + pp1.insert (pp1.end (), p1.begin (), p1.end ()); + pp1.push_back (ClusterInstElement (i1.cell_index (), i1.complex_trans (*ii1), i1.prop_id ())); + + for (db::CellInstArray::iterator ii2 = i2.begin_touching (ib1.transformed (t2i), mp_layout); ! ii2.at_end (); ++ii2) { + + db::ICplxTrans tt2 = t2 * i2.complex_trans (*ii2); + box_type ib2 = bb2.transformed (tt2); + + box_type common12 = ib1 & ib2 & common; + + if (! common12.empty ()) { + + std::vector pp2; + pp2.reserve (p2.size () + 1); + pp2.insert (pp2.end (), p2.begin (), p2.end ()); + pp2.push_back (ClusterInstElement (i2.cell_index (), i2.complex_trans (*ii2), i2.prop_id ())); + + add_single_pair (common12, i1.cell_index (), pp1, tt1, i2.cell_index (), pp2, tt2); + + // dive into cell of ii2 + const db::Cell &cell2 = mp_layout->cell (i2.cell_index ()); + for (db::Cell::touching_iterator jj2 = cell2.begin_touching (common12.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { + add_pair (common12, i1, p1, t1, *jj2, pp2, tt2); + } + + } + + } + + box_type common1 = ib1 & b2 & common; + + if (! common1.empty ()) { + + // dive into cell of ii1 + const db::Cell &cell1 = mp_layout->cell (i1.cell_index ()); + for (db::Cell::touching_iterator jj1 = cell1.begin_touching (common1.transformed (tt1.inverted ())); ! jj1.at_end (); ++jj1) { + add_pair (common1, *jj1, pp1, tt1, i2, p2, t2); + } + + } + + } + } + + /** + * @brief Handles the cluster interactions between two specific instances + * @param common The region under investigation (seen from the top level) + * @param ci1 The cell index of the child cell 1 + * @param p1 The instantiation path to the child cell (last element is the instance to ci1) + * @param t1 The accumulated transformation of the path p1 + * @param ci2 The cell index of the child cell 2 + * @param p2 The instantiation path to the child cell (last element is the instance to ci2) + * @param t2 The accumulated transformation of the path p2 + */ + void add_single_pair (const box_type &common, + db::cell_index_type ci1, const std::vector &p1, const db::ICplxTrans &t1, + db::cell_index_type ci2, const std::vector &p2, const db::ICplxTrans &t2) + { + const db::Cell &cell2 = mp_layout->cell (ci2); + + const db::local_clusters &cl1 = mp_tree->clusters_per_cell (ci1); + const db::local_clusters &cl2 = mp_tree->clusters_per_cell (ci2); + + db::ICplxTrans t1i = t1.inverted (); + db::ICplxTrans t2i = t2.inverted (); + db::ICplxTrans t21 = t1i * t2; + + // NOTE: make_path may disturb the iteration (because of modification), hence + // we first collect and then process the interactions. + std::vector > interactions; + + for (typename db::local_clusters::touching_iterator i = cl1.begin_touching (common.transformed (t1i)); ! i.at_end (); ++i) { + + // skip the test, if this cluster doesn't interact with the whole cell2 + if (! i->interacts (cell2, t21, *mp_conn)) { + continue; + } + + box_type bc1 = common & i->bbox ().transformed (t1); + for (typename db::local_clusters::touching_iterator j = cl2.begin_touching (bc1.transformed (t2i)); ! j.at_end (); ++j) { + + if (i->interacts (*j, t21, *mp_conn)) { + interactions.push_back (std::make_pair (i->id (), j->id ())); + } + + } + + } + + for (std::vector >::const_iterator ii = interactions.begin (); ii != interactions.end (); ++ii) { + + ClusterInstance k1 = make_path (ii->first, p1); + ClusterInstance k2 = make_path (ii->second, p2); + + id_type x1 = mp_cell_clusters->find_cluster_with_connection (k1); + id_type x2 = mp_cell_clusters->find_cluster_with_connection (k2); + + if (x1 == 0) { + + if (x2 == 0) { + + id_type connector = mp_cell_clusters->insert_dummy (); + mp_cell_clusters->add_connection (connector, k1); + mp_cell_clusters->add_connection (connector, k2); + + } else { + mp_cell_clusters->add_connection (x2, k1); + } + + } else if (x2 == 0) { + + mp_cell_clusters->add_connection (x1, k2); + + } else if (x1 != x2) { + + // for instance-to-instance interactions the number of connections is more important for the + // cost of the join operation: make the one with more connections the target + if (mp_cell_clusters->connections_for_cluster (x1).size () < mp_cell_clusters->connections_for_cluster (x2).size ()) { + std::swap (x1, x2); + } + + mp_cell_clusters->join_cluster_with (x1, x2); + mp_cell_clusters->remove_cluster (x2); + + } + + } + } + + /** + * @brief Single instance treatment + */ + void add_single_inst (const db::Instance &i) + { + box_type bb = (*mp_cbc) (i.cell_index ()); + const db::Cell &cell = mp_layout->cell (i.cell_index ()); + + for (db::CellInstArray::iterator ii = i.begin (); ! ii.at_end (); ++ii) { + + db::ICplxTrans tt = i.complex_trans (*ii); + box_type ib = bb.transformed (tt); + + std::vector pp; + pp.push_back (ClusterInstElement (i.cell_index (), i.complex_trans (*ii), i.prop_id ())); + + bool any = false; + bool first = true; + + for (db::CellInstArray::iterator ii2 = i.begin_touching (ib, mp_layout); ! ii2.at_end (); ++ii2) { + + db::ICplxTrans tt2 = i.complex_trans (*ii2); + if (tt.equal (tt2)) { + // skip the initial instance + continue; + } + + box_type ib2 = bb.transformed (tt2); + + if (ib.touches (ib2)) { + + std::vector pp2; + pp2.push_back (ClusterInstElement (i.cell_index (), i.complex_trans (*ii2), i.prop_id ())); + + box_type common = (ib & ib2); + add_single_pair (common, i.cell_index (), pp, tt, i.cell_index (), pp2, tt2); + + // dive into cell of ii2 - this is a self-interaction of a cell with parts of itself + // as these self-interactions are expected to be the same always (regular array), we can skip this test the next times. + if (first) { + for (db::Cell::touching_iterator jj2 = cell.begin_touching (common.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { + std::vector p; + db::ICplxTrans t; + add_pair (common, i, p, t, *jj2, pp2, tt2); + } + } + + any = true; + + } + + } + + first = false; + + // we don't expect more to happen on the next instance + if (! any) { + break; + } + + } + } + + /** + * @brief Handles a local clusters vs. the clusters of a specific child instance or instance array + * @param c1 The local cluster + * @param i2 The index of the child cell + * @param p2 The instantiation path to the child cell (not including i2) + * @param t2 The accumulated transformation of the path, not including i2 + */ + void add_pair (const local_cluster &c1, const db::Instance &i2, const std::vector &p2, const db::ICplxTrans &t2) + { + box_type b1 = c1.bbox (); + + box_type bb2 = (*mp_cbc) (i2.cell_index ()); + + const db::Cell &cell2 = mp_layout->cell (i2.cell_index ()); + + box_type b2 = i2.cell_inst ().bbox (*mp_cbc).transformed (t2); + + if (! b1.touches (b2)) { + return; + } + + std::vector pp2; + pp2.reserve (p2.size () + 1); + pp2.insert (pp2.end (), p2.begin (), p2.end ()); + pp2.push_back (ClusterInstElement ()); + + for (db::CellInstArray::iterator ii2 = i2.begin_touching ((b1 & b2).transformed (t2.inverted ()), mp_layout); ! ii2.at_end (); ++ii2) { + + db::ICplxTrans tt2 = t2 * i2.complex_trans (*ii2); + box_type ib2 = bb2.transformed (tt2); + + if (b1.touches (ib2) && c1.interacts (cell2, tt2, *mp_conn)) { + + pp2.back () = ClusterInstElement (i2.cell_index (), i2.complex_trans (*ii2), i2.prop_id ()); + add_single_pair (c1, i2.cell_index (), pp2, tt2); + + // dive into cell of ii2 + for (db::Cell::touching_iterator jj2 = cell2.begin_touching ((b1 & ib2).transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { + add_pair (c1, *jj2, pp2, tt2); + } + + } + + } + } + + /** + * @brief Handles a local clusters vs. the clusters of a specific child instance + * @param c1 The local cluster + * @param ci2 The cell index of the cell investigated + * @param p2 The instantiation path to the child cell (last element is the instance to ci2) + * @param t2 The accumulated transformation of the path + */ + void add_single_pair (const local_cluster &c1, + db::cell_index_type ci2, const std::vector &p2, const db::ICplxTrans &t2) + { + // NOTE: make_path may disturb the iteration (because of modification), hence + // we first collect and then process the interactions. + + const db::local_clusters &cl2 = mp_tree->clusters_per_cell (ci2); + + for (typename db::local_clusters::touching_iterator j = cl2.begin_touching (c1.bbox ().transformed (t2.inverted ())); ! j.at_end (); ++j) { + + if (c1.interacts (*j, t2, *mp_conn)) { + m_ci_interactions.push_back (ClusterInstanceInteraction (c1.id (), j->id (), p2)); + } + + } + } + + /** + * @brief Inserts a pair of clusters to join + */ + void mark_to_join (id_type a, id_type b) + { + if (a == b) { + // shouldn't happen, but duplicate instances may trigger this + return; + } + + typename std::map::const_iterator x = m_cm2join_map.find (a); + typename std::map::const_iterator y = m_cm2join_map.find (b); + + if (x == m_cm2join_map.end ()) { + + if (y == m_cm2join_map.end ()) { + + m_cm2join_sets.push_back (std::set ()); + m_cm2join_sets.back ().insert (a); + m_cm2join_sets.back ().insert (b); + + m_cm2join_map [a] = --m_cm2join_sets.end (); + m_cm2join_map [b] = --m_cm2join_sets.end (); + + } else { + + y->second->insert (a); + m_cm2join_map [a] = y->second; + + } + + } else if (y == m_cm2join_map.end ()) { + + x->second->insert (b); + m_cm2join_map [b] = x->second; + + } else if (x->second != y->second) { + + // join two superclusters + typename join_set_list::iterator yset = y->second; + x->second->insert (yset->begin (), yset->end ()); + for (typename std::set::const_iterator i = yset->begin (); i != yset->end (); ++i) { + m_cm2join_map [*i] = x->second; + } + m_cm2join_sets.erase (yset); + + } + +#if defined(DEBUG_HIER_NETWORK_PROCESSOR) + // concistency check for debugging + for (typename std::map::const_iterator j = m_cm2join_map.begin (); j != m_cm2join_map.end (); ++j) { + tl_assert (j->second->find (j->first) != j->second->end ()); + } + + for (typename std::list >::const_iterator i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) { + for (typename std::set::const_iterator j = i->begin(); j != i->end(); ++j) { + tl_assert(m_cm2join_map.find (*j) != m_cm2join_map.end ()); + tl_assert(m_cm2join_map[*j] == i); + } + } + + // the sets must be disjunct + std::set all; + for (typename std::list >::const_iterator i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) { + for (typename std::set::const_iterator j = i->begin(); j != i->end(); ++j) { + tl_assert(all.find (*j) == all.end()); + all.insert(*j); + } + } +#endif + + } + + /** + * @brief Makes a valid path to a child cluster + * + * Cluster connections can only cross one level of hierarchy. This method + * creates necessary dummy entries for the given path. + */ + ClusterInstance make_path (id_type id, const std::vector &path) const + { + return mp_tree->make_path (*mp_layout, *mp_cell, id, path); + } +}; + +template +struct cell_inst_clusters_box_converter +{ + typedef typename local_cluster::box_type box_type; + typedef db::simple_bbox_tag complexity; + + cell_inst_clusters_box_converter (const cell_clusters_box_converter &cbc) + : mp_cbc (&cbc) + { + // .. nothing yet .. + } + + box_type operator() (const db::Instance &inst) const + { + return inst.cell_inst ().bbox (*mp_cbc); + } + +private: + const cell_clusters_box_converter *mp_cbc; +}; + +} + +template +ClusterInstance +hier_clusters::make_path (const db::Layout &layout, const db::Cell &cell, size_t id, const std::vector &path) +{ + std::vector::const_iterator p = path.end (); + tl_assert (p != path.begin ()); + + while (true) { + + --p; + + ClusterInstance ci (id, p->inst_cell_index (), p->inst_trans (), p->inst_prop_id ()); + if (p == path.begin ()) { + + // if we're attaching to a child which is root yet, we need to promote the + // cluster to the parent in all places + connected_clusters &child_cc = clusters_per_cell (p->inst_cell_index ()); + if (child_cc.is_root (id)) { + + const db::Cell &child_cell = layout.cell (p->inst_cell_index ()); + for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { + + db::Instance child_inst = pi->child_inst (); + + connected_clusters &parent_cc = clusters_per_cell (pi->parent_cell_index ()); + for (db::CellInstArray::iterator pii = child_inst.begin (); ! pii.at_end (); ++pii) { + + ClusterInstance ci2 (id, child_inst.cell_index (), child_inst.complex_trans (*pii), child_inst.prop_id ()); + if (cell.cell_index () != pi->parent_cell_index () || ci != ci2) { + + size_t id_dummy; + + const typename db::local_cluster::global_nets &gn = child_cc.cluster_by_id (id).get_global_nets (); + if (gn.empty ()) { + id_dummy = parent_cc.insert_dummy (); + } else { + local_cluster *lc = parent_cc.insert (); + lc->set_global_nets (gn); + id_dummy = lc->id (); + } + + parent_cc.add_connection (id_dummy, ci2); + + } + + } + + } + + child_cc.reset_root (id); + + } + + return ci; + + } + + db::cell_index_type pci = p [-1].inst_cell_index (); + connected_clusters &target_cc = clusters_per_cell (pci); + size_t parent_cluster = target_cc.find_cluster_with_connection (ci); + + if (parent_cluster > 0) { + + // taken parent + id = parent_cluster; + + } else { + + size_t id_new = 0; + + // if we're attaching to a child which is root yet, we need to promote the + // cluster to the parent in all places + connected_clusters &child_cc = clusters_per_cell (p->inst_cell_index ()); + if (child_cc.is_root (id)) { + + const db::Cell &child_cell = layout.cell (p->inst_cell_index ()); + for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { + + db::Instance child_inst = pi->child_inst (); + + connected_clusters &parent_cc = clusters_per_cell (pi->parent_cell_index ()); + for (db::CellInstArray::iterator pii = child_inst.begin (); ! pii.at_end (); ++pii) { + + size_t id_dummy; + + const typename db::local_cluster::global_nets &gn = child_cc.cluster_by_id (id).get_global_nets (); + if (gn.empty ()) { + id_dummy = parent_cc.insert_dummy (); + } else { + local_cluster *lc = parent_cc.insert (); + lc->set_global_nets (gn); + id_dummy = lc->id (); + } + + ClusterInstance ci2 (id, child_inst.cell_index (), child_inst.complex_trans (*pii), child_inst.prop_id ()); + parent_cc.add_connection (id_dummy, ci2); + + if (pci == pi->parent_cell_index () && ci == ci2) { + id_new = id_dummy; + } + + } + + } + + child_cc.reset_root (id); + + } + + // no parent -> create vertical connector + id = id_new; + tl_assert (id != 0); + + } + + } +} + +template +void +hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence) +{ + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity, tl::to_string (tr ("Computing shape clusters"))); + + std::set called; + cell.collect_called_cells (called); + called.insert (cell.cell_index ()); + + // first build all local clusters + + { + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 10, tl::to_string (tr ("Computing local shape clusters"))); + tl::RelativeProgress progress (tl::to_string (tr ("Computing local clusters")), called.size (), 1); + + for (std::set::const_iterator c = called.begin (); c != called.end (); ++c) { + build_local_cluster (layout, layout.cell (*c), shape_flags, conn, attr_equivalence); + ++progress; + } + } + + // build the hierarchical connections bottom-up and for all cells whose children are computed already + + { + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 10, tl::to_string (tr ("Computing hierarchical shape clusters"))); + tl::RelativeProgress progress (tl::to_string (tr ("Computing hierarchical clusters")), called.size (), 1); + + std::set done; + std::vector todo; + for (db::Layout::bottom_up_const_iterator c = layout.begin_bottom_up (); c != layout.end_bottom_up (); ++c) { + + if (called.find (*c) != called.end ()) { + + bool all_available = true; + const db::Cell &cell = layout.cell (*c); + for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end () && all_available; ++cc) { + all_available = (done.find (*cc) != done.end ()); + } + + if (all_available) { + todo.push_back (*c); + } else { + tl_assert (! todo.empty ()); + build_hier_connections_for_cells (cbc, layout, todo, conn, progress); + done.insert (todo.begin (), todo.end ()); + todo.clear (); + todo.push_back (*c); + } + + } + + } + + build_hier_connections_for_cells (cbc, layout, todo, conn, progress); + } +} + +template +void +hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence) +{ + std::string msg = tl::to_string (tr ("Computing local clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ())); + if (tl::verbosity () >= m_base_verbosity + 20) { + tl::log << msg; + } + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 20, msg); + + connected_clusters &local = m_per_cell_clusters [cell.cell_index ()]; + local.build_clusters (cell, shape_flags, conn, attr_equivalence, tl::verbosity () >= m_base_verbosity + 30); +} + +template +void +hier_clusters::build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn, tl::RelativeProgress &progress) +{ + for (std::vector::const_iterator c = cells.begin (); c != cells.end (); ++c) { + build_hier_connections (cbc, layout, layout.cell (*c), conn); + ++progress; + } +} + +namespace { + +class GlobalNetClusterMaker +{ +public: + typedef std::pair, std::set > entry_type; + typedef std::list entry_list; + typedef entry_list::const_iterator entry_iterator; + + void + add (const std::set &global_nets, size_t cluster_id) + { + add (global_nets, ClusterInstance (cluster_id)); + } + + void + add (const std::set &global_nets, const ClusterInstance &inst) + { + if (global_nets.empty ()) { + return; + } + + std::set::const_iterator g0 = global_nets.begin (); + + std::map::iterator k = m_global_net_to_entries.find (*g0); + if (k == m_global_net_to_entries.end ()) { + + m_entries.push_back (entry_type ()); + m_entries.back ().first.insert (*g0); + + k = m_global_net_to_entries.insert (std::make_pair (*g0, --m_entries.end ())).first; + + } + + k->second->second.insert (inst); + + for (std::set::const_iterator g = ++g0; g != global_nets.end (); ++g) { + + std::map::iterator j = m_global_net_to_entries.find (*g); + if (j == m_global_net_to_entries.end ()) { + + k->second->first.insert (*g); + k->second->second.insert (inst); + + m_global_net_to_entries.insert (std::make_pair (*g, k->second)); + + } else if (k->second != j->second) { + + // joining required + k->second->first.insert (j->second->first.begin (), j->second->first.end ()); + k->second->second.insert (j->second->second.begin (), j->second->second.end ()); + + m_entries.erase (j->second); + j->second = k->second; + + } + + } + } + + entry_iterator begin () const + { + return m_entries.begin (); + } + + entry_iterator end () const + { + return m_entries.end (); + } + +private: + entry_list m_entries; + std::map m_global_net_to_entries; +}; + +} + +template +void +hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn) +{ + std::string msg = tl::to_string (tr ("Computing hierarchical clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ())); + if (tl::verbosity () >= m_base_verbosity + 20) { + tl::log << msg; + } + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 20, msg); + + connected_clusters &local = m_per_cell_clusters [cell.cell_index ()]; + + // NOTE: this is a receiver for both the child-to-child and + // local to child interactions. + std::auto_ptr > rec (new hc_receiver (layout, cell, local, *this, cbc, conn)); + cell_inst_clusters_box_converter cibc (cbc); + + // The box scanner needs pointers so we have to first store the instances + // delivered by the cell's iterator (which does not deliver real pointer). + + std::vector inst_storage; + + // TODO: there should be a cell.size () for this ... + size_t n = 0; + for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) { + n += 1; + } + + inst_storage.reserve (n); + for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) { + inst_storage.push_back (*inst); + } + + // handle instance to instance connections + + { + static std::string desc = tl::to_string (tr ("Instance to instance treatment")); + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 30, desc); + + bool report_progress = tl::verbosity () >= m_base_verbosity + 30; + db::box_scanner bs (report_progress, desc); + + for (std::vector::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) { + bs.insert (inst.operator-> (), 0); + } + + bs.process (*rec, 1 /*touching*/, cibc); + } + + // handle local to instance connections + + { + std::list > heap; + double area_ratio = 10.0; + + static std::string desc = tl::to_string (tr ("Local to instance treatment")); + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 30, desc); + + bool report_progress = tl::verbosity () >= m_base_verbosity + 30; + db::box_scanner2, unsigned int, db::Instance, unsigned int> bs2 (report_progress, desc); + + for (typename connected_clusters::const_iterator c = local.begin (); c != local.end (); ++c) { + + // we do not actually need the original clusters. For a better performance we optimize the + // area ratio and split, but we keep the ID the same. + std::back_insert_iterator > > iout = std::back_inserter (heap); + size_t n = c->split (area_ratio, iout); + if (n == 0) { + bs2.insert1 (c.operator-> (), 0); + } else { + typename std::list >::iterator h = heap.end (); + while (n-- > 0) { + bs2.insert1 ((--h).operator-> (), 0); + } + } + } + + for (std::vector::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) { + bs2.insert2 (inst.operator-> (), 0); + } + + bs2.process (*rec, 1 /*touching*/, local_cluster_box_convert (), cibc); + } + + // join local clusters which got connected by child clusters + rec->finish_cluster_to_instance_interactions (); + rec.reset (0); + + // finally connect global nets + { + static std::string desc = tl::to_string (tr ("Global net treatment")); + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 30, desc); + + GlobalNetClusterMaker global_net_clusters; + + // insert the global nets from the subcircuits which need connection + + for (std::vector::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) { + + const db::connected_clusters &cc = m_per_cell_clusters [inst->cell_index ()]; + for (typename db::connected_clusters::const_iterator cl = cc.begin (); cl != cc.end (); ++cl) { + + if (! cl->get_global_nets ().empty ()) { + for (db::Instance::cell_inst_array_type::iterator i = inst->begin (); !i.at_end (); ++i) { + global_net_clusters.add (cl->get_global_nets (), db::ClusterInstance (cl->id (), inst->cell_index (), inst->complex_trans (*i), inst->prop_id ())); + } + } + + } + } + + // insert the global nets from here + + for (typename db::connected_clusters::const_iterator cl = local.begin (); cl != local.end (); ++cl) { + if (! cl->get_global_nets ().empty ()) { + global_net_clusters.add (cl->get_global_nets (), db::ClusterInstance (cl->id ())); + } + } + + // now global_net_clusters knows what clusters need to be made for the global nets + + for (GlobalNetClusterMaker::entry_iterator ge = global_net_clusters.begin (); ge != global_net_clusters.end (); ++ge) { + + db::local_cluster *gc = local.insert (); + gc->set_global_nets (ge->first); + // NOTE: don't use the gc pointer - it may become invalid during make_path (will also do a local.insert) + size_t gcid = gc->id (); + + for (std::set::const_iterator ci = ge->second.begin (); ci != ge->second.end (); ++ci) { + + if (! ci->has_instance ()) { + + local.join_cluster_with (gcid, ci->id ()); + local.remove_cluster (ci->id ()); + + } else { + + std::vector p; + p.push_back (*ci); + ClusterInstance k = make_path (layout, cell, ci->id (), p); + + size_t other_id = local.find_cluster_with_connection (k); + if (other_id == gcid) { + // shouldn't happen, but duplicate instances may trigger this + } else if (other_id) { + local.join_cluster_with (gcid, other_id); + local.remove_cluster (other_id); + } else { + local.add_connection (gcid, k); + } + + } + + } + + } + + } +} + +template +const connected_clusters & +hier_clusters::clusters_per_cell (db::cell_index_type cell_index) const +{ + typename std::map >::const_iterator c = m_per_cell_clusters.find (cell_index); + if (c == m_per_cell_clusters.end ()) { + static connected_clusters empty; + return empty; + } else { + return c->second; + } +} + +template +connected_clusters & +hier_clusters::clusters_per_cell (db::cell_index_type cell_index) +{ + typename std::map >::iterator c = m_per_cell_clusters.find (cell_index); + if (c == m_per_cell_clusters.end ()) { + c = m_per_cell_clusters.insert (std::make_pair (cell_index, connected_clusters ())).first; + } + return c->second; +} + +template void insert_transformed (db::Layout &layout, db::Shapes &shapes, const Shape &s, const Trans &t); + +template void insert_transformed (db::Layout &layout, db::Shapes &shapes, const db::PolygonRef &s, const Trans &t) +{ + db::Polygon poly = s.obj (); + poly.transform (s.trans ()); + if (! t.is_unity ()) { + poly.transform (t); + } + shapes.insert (db::PolygonRef (poly, layout.shape_repository ())); +} + +template void insert_transformed (db::Layout & /*layout*/, db::Shapes &shapes, const db::Edge &s, const Trans &t) +{ + shapes.insert (s.transformed (t)); +} + +template +void +hier_clusters::return_to_hierarchy (db::Layout &layout, const std::map &lm) const +{ + for (db::Layout::bottom_up_iterator c = layout.begin_bottom_up (); c != layout.end_bottom_up (); ++c) { + + const db::connected_clusters &cc = clusters_per_cell (*c); + db::Cell &target_cell = layout.cell (*c); + + for (typename db::connected_clusters::all_iterator lc = cc.begin_all (); ! lc.at_end (); ++lc) { + + if (cc.is_root (*lc)) { + + for (typename std::map::const_iterator m = lm.begin (); m != lm.end (); ++m) { + + db::Shapes &shapes = target_cell.shapes (m->second); + + for (recursive_cluster_shape_iterator si (*this, m->first, *c, *lc); ! si.at_end (); ++si) { + insert_transformed (layout, shapes, *si, si.trans ()); + } + + } + + } + + } + + } +} + +// explicit instantiations +template class DB_PUBLIC hier_clusters; +template class DB_PUBLIC hier_clusters; + +// ------------------------------------------------------------------------------ +// recursive_cluster_shape_iterator implementation + +template +recursive_cluster_shape_iterator::recursive_cluster_shape_iterator (const hier_clusters &hc, unsigned int layer, db::cell_index_type ci, typename local_cluster::id_type id) + : mp_hc (&hc), m_layer (layer), m_id (id) +{ + down (ci, id, db::ICplxTrans ()); + + while (m_shape_iter.at_end () && ! m_conn_iter_stack.empty ()) { + next_conn (); + } +} + +template +std::vector recursive_cluster_shape_iterator::inst_path () const +{ + std::vector p; + if (!m_conn_iter_stack.empty ()) { + p.reserve (m_conn_iter_stack.size ()); + for (size_t i = 0; i < m_conn_iter_stack.size () - 1; ++i) { + p.push_back (*m_conn_iter_stack [i].first); + } + } + return p; +} + +template +recursive_cluster_shape_iterator &recursive_cluster_shape_iterator::operator++ () +{ + ++m_shape_iter; + + while (m_shape_iter.at_end () && ! m_conn_iter_stack.empty ()) { + next_conn (); + } + + return *this; +} + +template +void recursive_cluster_shape_iterator::skip_cell () +{ + m_shape_iter = typename db::local_cluster::shape_iterator (); + + do { + + up (); + if (m_conn_iter_stack.empty ()) { + return; + } + + ++m_conn_iter_stack.back ().first; + + } while (m_conn_iter_stack.back ().first == m_conn_iter_stack.back ().second); + + while (m_shape_iter.at_end () && ! m_conn_iter_stack.empty ()) { + next_conn (); + } +} + +template +void recursive_cluster_shape_iterator::next_conn () +{ + if (m_conn_iter_stack.back ().first != m_conn_iter_stack.back ().second) { + + const ClusterInstance &cli = *m_conn_iter_stack.back ().first; + down (cli.inst_cell_index (), cli.id (), cli.inst_trans ()); + + } else { + + while (m_conn_iter_stack.back ().first == m_conn_iter_stack.back ().second) { + + up (); + if (m_conn_iter_stack.empty ()) { + return; + } + + ++m_conn_iter_stack.back ().first; + + } + + } +} + +template +void recursive_cluster_shape_iterator::up () +{ + m_conn_iter_stack.pop_back (); + m_trans_stack.pop_back (); + m_cell_index_stack.pop_back (); +} + +template +void recursive_cluster_shape_iterator::down (db::cell_index_type ci, typename db::local_cluster::id_type id, const db::ICplxTrans &t) +{ + const connected_clusters &clusters = mp_hc->clusters_per_cell (ci); + const typename connected_clusters::connections_type &conn = clusters.connections_for_cluster (id); + + if (! m_trans_stack.empty ()) { + m_trans_stack.push_back (m_trans_stack.back () * t); + } else { + m_trans_stack.push_back (t); + } + + m_cell_index_stack.push_back (ci); + m_conn_iter_stack.push_back (std::make_pair (conn.begin (), conn.end ())); + + const local_cluster &cluster = mp_hc->clusters_per_cell (cell_index ()).cluster_by_id (cluster_id ()); + m_shape_iter = cluster.begin (m_layer); +} + +// explicit instantiations +template class DB_PUBLIC recursive_cluster_shape_iterator; +template class DB_PUBLIC recursive_cluster_shape_iterator; + +// ------------------------------------------------------------------------------ +// recursive_cluster_iterator implementation + +template +recursive_cluster_iterator::recursive_cluster_iterator (const hier_clusters &hc, db::cell_index_type ci, typename local_cluster::id_type id) + : mp_hc (&hc), m_id (id) +{ + down (ci, id); +} + +template +std::vector recursive_cluster_iterator::inst_path () const +{ + std::vector p; + if (!m_conn_iter_stack.empty ()) { + p.reserve (m_conn_iter_stack.size ()); + for (size_t i = 0; i < m_conn_iter_stack.size () - 1; ++i) { + p.push_back (*m_conn_iter_stack [i].first); + } + } + return p; +} + +template +recursive_cluster_iterator &recursive_cluster_iterator::operator++ () +{ + next_conn (); + return *this; +} + +template +void recursive_cluster_iterator::next_conn () +{ + while (m_conn_iter_stack.back ().first == m_conn_iter_stack.back ().second) { + + up (); + if (m_conn_iter_stack.empty ()) { + return; + } + + ++m_conn_iter_stack.back ().first; + + } + + if (m_conn_iter_stack.back ().first != m_conn_iter_stack.back ().second) { + + const ClusterInstance &cli = *m_conn_iter_stack.back ().first; + down (cli.inst_cell_index (), cli.id ()); + + } +} + +template +void recursive_cluster_iterator::up () +{ + m_conn_iter_stack.pop_back (); + m_cell_index_stack.pop_back (); +} + +template +void recursive_cluster_iterator::down (db::cell_index_type ci, typename db::local_cluster::id_type id) +{ + const connected_clusters &clusters = mp_hc->clusters_per_cell (ci); + const typename connected_clusters::connections_type &conn = clusters.connections_for_cluster (id); + + m_cell_index_stack.push_back (ci); + m_conn_iter_stack.push_back (std::make_pair (conn.begin (), conn.end ())); +} + +// explicit instantiations +template class DB_PUBLIC recursive_cluster_iterator; +template class DB_PUBLIC recursive_cluster_iterator; + +// ------------------------------------------------------------------------------ +// incoming_cluster_connections implementation + +template +incoming_cluster_connections::incoming_cluster_connections (const db::Layout &layout, const db::Cell &cell, const hier_clusters &hc) + : mp_layout (const_cast (&layout)), mp_hc (const_cast *> (&hc)) +{ + cell.collect_called_cells (m_called_cells); + m_called_cells.insert (cell.cell_index ()); +} + +template +bool +incoming_cluster_connections::has_incoming (db::cell_index_type ci, size_t cluster_id) const +{ + std::map >::const_iterator i = m_incoming.find (ci); + if (i == m_incoming.end ()) { + ensure_computed (ci); + i = m_incoming.find (ci); + tl_assert (i != m_incoming.end ()); + } + + tl_assert (i != m_incoming.end ()); + return (i->second.find (cluster_id) != i->second.end ()); +} + +template +const typename incoming_cluster_connections::incoming_connections & +incoming_cluster_connections::incoming (db::cell_index_type ci, size_t cluster_id) const +{ + std::map >::const_iterator i = m_incoming.find (ci); + if (i == m_incoming.end ()) { + ensure_computed (ci); + i = m_incoming.find (ci); + tl_assert (i != m_incoming.end ()); + } + + std::map::const_iterator ii = i->second.find (cluster_id); + if (ii != i->second.end ()) { + return ii->second; + } else { + static incoming_connections empty; + return empty; + } +} + +template +void +incoming_cluster_connections::ensure_computed (db::cell_index_type ci) const +{ + tl_assert (mp_layout.get () != 0); + m_incoming.insert (std::make_pair (ci, std::map ())); + + const db::Cell &cell = mp_layout->cell (ci); + for (db::Cell::parent_cell_iterator pc = cell.begin_parent_cells (); pc != cell.end_parent_cells (); ++pc) { + if (m_called_cells.find (*pc) != m_called_cells.end ()) { + ensure_computed_parent (*pc); + } + } + + m_called_cells.erase (ci); +} + +template +void +incoming_cluster_connections::ensure_computed_parent (db::cell_index_type ci) const +{ + ensure_computed (ci); + + const connected_clusters &cc = ((const hier_clusters *) mp_hc.get ())->clusters_per_cell (ci); + for (typename connected_clusters::connections_iterator x = cc.begin_connections (); x != cc.end_connections (); ++x) { + for (typename connected_clusters::connections_type::const_iterator xx = x->second.begin (); xx != x->second.end (); ++xx) { + m_incoming [xx->inst_cell_index ()][xx->id ()].push_back (IncomingClusterInstance (ci, x->first, *xx)); + } + } +} + +// explicit instantiations +template class DB_PUBLIC incoming_cluster_connections; +template class DB_PUBLIC incoming_cluster_connections; + +} diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h new file mode 100644 index 000000000..6fef35daa --- /dev/null +++ b/src/db/db/dbHierNetworkProcessor.h @@ -0,0 +1,1208 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbHierNetworkProcessor +#define HDR_dbHierNetworkProcessor + +#include "dbCommon.h" +#include "dbTrans.h" +#include "dbBoxConvert.h" +#include "dbBoxTree.h" +#include "dbCell.h" +#include "dbInstElement.h" +#include "tlEquivalenceClusters.h" + +#include +#include +#include + +namespace tl { + class RelativeProgress; +} + +namespace db { + +class DeepLayer; + +/** + * @brief Defines the connectivity + * + * Connectivity is defined in terms of layers. Certain layer pairs + * are connected when shapes on their layers interact. + * Connectivity includes intra-layer connectivity - i.e. + * shapes on a layer are not connected by default. They need to + * be connected explicitly using "connect(layer)". + */ +class DB_PUBLIC Connectivity +{ +public: + typedef std::set layers_type; + typedef layers_type::const_iterator layer_iterator; + typedef std::set global_nets_type; + typedef global_nets_type::const_iterator global_nets_iterator; + + /** + * @brief Specifies the edge connectivity mode + */ + enum edge_connectivity_type + { + /** + * @brief Edges connect if they are collinear + */ + EdgesConnectCollinear, + + /** + * @brief Edges connect if the end point of one edge is the start point of the other edge + */ + EdgesConnectByPoints + }; + + /** + * @brief Creates a connectivity object without any connections + */ + Connectivity (); + + /** + * @brief Creates a connectivity object without connections and the given edge connectivity mode + */ + Connectivity (edge_connectivity_type ec); + + /** + * @brief Adds intra-layer connectivity for layer l + */ + void connect (unsigned int l); + + /** + * @brief Adds inter-layer connectivity + */ + void connect (unsigned int la, unsigned int lb); + + /** + * @brief Adds a connection to a global net + */ + size_t connect_global (unsigned int l, const std::string &gn); + + /** + * @brief Adds intra-layer connectivity for layer l + * This is a convenience method that takes a db::DeepLayer object. + * It is assumed that all those layers originate from the same deep shape store. + */ + void connect (const db::DeepLayer &l); + + /** + * @brief Adds inter-layer connectivity + * This is a convenience method that takes a db::DeepLayer object. + * It is assumed that all those layers originate from the same deep shape store. + */ + void connect (const db::DeepLayer &la, const db::DeepLayer &lb); + + /** + * @brief Adds a connection to a global net + */ + size_t connect_global (const db::DeepLayer &la, const std::string &gn); + + /** + * @brief Gets the global net name per ID + */ + const std::string &global_net_name (size_t id) const; + + /** + * @brief Gets the global net ID for the given name + */ + size_t global_net_id (const std::string &gn); + + /** + * @brief Begin iterator for the layers involved + */ + layer_iterator begin_layers () const; + + /** + * @brief End iterator for the layers involved + */ + layer_iterator end_layers () const; + + /** + * @brief Begin iterator for the layers connected to a specific layer + */ + layer_iterator begin_connected (unsigned int layer) const; + + /** + * @brief End iterator for the layers connected to a specific layer + */ + layer_iterator end_connected (unsigned int layer) const; + + /** + * @brief Begin iterator for the global connections for a specific layer + */ + global_nets_iterator begin_global_connections (unsigned int layer) const; + + /** + * @brief End iterator for the layers connected to a specific layer + */ + global_nets_iterator end_global_connections (unsigned int layer) const; + + /** + * @brief Returns true, if the given shapes on the given layers interact + * + * This method accepts a transformation. This transformation is applied + * to the b shape before checking against a. + */ + template + bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb, const Trans &trans) const; + + /** + * @brief Returns true, if the given shapes on the given layers interact + */ + template + bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb) const + { + return interacts (a, la, b, lb, UnitTrans ()); + } + +private: + layers_type m_all_layers; + std::map m_connected; + std::vector m_global_net_names; + std::map m_global_connections; + edge_connectivity_type m_ec; +}; + +/** + * @brief Represents a cluster of shapes + * + * A cluster of shapes is a set of shapes of type T which are connected in terms + * of a given connectivity. The shapes will still be organised in layers. + */ +template +class DB_PUBLIC local_cluster +{ +public: + typedef size_t id_type; + typedef typename T::box_type box_type; + typedef db::unstable_box_tree > tree_type; + typedef typename tree_type::flat_iterator shape_iterator; + typedef size_t attr_id; + typedef std::set attr_set; + typedef attr_set::const_iterator attr_iterator; + typedef size_t global_net_id; + typedef std::set global_nets; + typedef global_nets::const_iterator global_nets_iterator; + + /** + * @brief Creates an empty cluster + */ + local_cluster (size_t id = 0); + + /** + * @brief Clears the cluster + */ + void clear (); + + /** + * @brief Adds a shape with the given layer to the cluster + */ + void add (const T &s, unsigned int la); + + /** + * @brief Joins this cluster with the other one + * + * This will copy all shapes from the other cluster into ourself. + */ + void join_with (const local_cluster &other); + + /** + * @brief Gets the cluster's ID + * + * The ID is a unique identifier for the cluster. An ID value of 0 is reserved for + * "no cluster". + */ + id_type id () const + { + return m_id; + } + + /** + * @brief Tests whether this cluster interacts with another cluster + * + * "trans" is the transformation which is applied to the other cluster before + * the test. + */ + bool interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn) const; + + /** + * @brief Tests whether this cluster interacts with the given cell + * + * "trans" is the transformation which is applied to the cell before + * the test. + */ + bool interacts (const db::Cell &cell, const db::ICplxTrans &trans, const Connectivity &conn) const; + + /** + * @brief Gets the bounding box of this cluster + */ + const box_type &bbox () const + { + const_cast *> (this)->ensure_sorted (); // also updates bbox + return m_bbox; + } + + /** + * @brief Computes the "area ratio" of the cluster - this is a rough approximation of the area covered + * The algorithm used assumes no overlap between the polygons of the cluster. + */ + double area_ratio () const; + + /** + * @brief Splits the cluster into multiple other clusters until the desired area ratio is achieved. + * The result is sent to the output iterator. The return value is the number of clusters produced. + * If the area ratio of the cluster is below the limit, no splitting happens and 0 is returned. + */ + template + size_t split (double max_area_ratio, Iter &output) const; + + /** + * @brief Gets a vector of layers inside the cluster + */ + std::vector layers () const; + + /** + * @brief Gets the total number of shapes in this cluster + */ + size_t size () const + { + return m_size; + } + + /** + * @brief Gets the shape iterator for a given layer + */ + shape_iterator begin (unsigned int l) const; + + /** + * @brief Adds the given attribute to the attribute set + * + * Attributes are arbitary IDs attached to clusters. + * The attribute value 0 is reserved for "no attribute" and is not + * put into the set. + */ + void add_attr (attr_id a); + + /** + * @brief Gets the attribute iterator (begin) + */ + attr_iterator begin_attr () const + { + return m_attrs.begin (); + } + + /** + * @brief Gets the attribute iterator (end) + */ + attr_iterator end_attr () const + { + return m_attrs.end (); + } + + /** + * @brief Gets the global net IDs (begin) + */ + global_nets_iterator begin_global_nets () const + { + return m_global_nets.begin (); + } + + /** + * @brief Gets the global net IDs (end) + */ + global_nets_iterator end_global_nets () const + { + return m_global_nets.end (); + } + + /** + * @brief Gets the global nets set + */ + const global_nets &get_global_nets () const + { + return m_global_nets; + } + + /** + * @brief Sets the global nets + */ + void set_global_nets (const global_nets &gn); + +private: + template friend class local_clusters; + template friend class hnp_interaction_receiver; + + void set_id (id_type id) + { + m_id = id; + } + + const T &shape (unsigned int l, size_t index) const + { + typename std::map::const_iterator s = m_shapes.find (l); + tl_assert (s != m_shapes.end ()); + return s->second.objects () [index]; + } + + void ensure_sorted (); + + id_type m_id; + bool m_needs_update; + std::map m_shapes; + box_type m_bbox; + attr_set m_attrs; + global_nets m_global_nets; + size_t m_size; +}; + +/** + * @brief A box converter for the local_cluster class + */ +template +struct DB_PUBLIC local_cluster_box_convert +{ + typedef typename local_cluster::box_type box_type; + typedef typename db::simple_bbox_tag complexity; + + box_type operator() (const local_cluster &c) const + { + return c.bbox (); + } +}; + +/** + * @brief A collection of clusters + * + * Clusters are identified by their ID. This collection + * supports cluster lookup by a box region and building + * the clusters from a cell's shapes. + */ +template +class DB_PUBLIC local_clusters +{ +public: + typedef typename local_cluster::id_type id_type; + typedef typename local_cluster::box_type box_type; + typedef typename local_cluster::attr_id attr_id; + typedef db::box_tree, local_cluster_box_convert > tree_type; + typedef typename tree_type::touching_iterator touching_iterator; + typedef typename tree_type::const_iterator const_iterator; + + /** + * @brief Creates an empty collection + */ + local_clusters (); + + /** + * @brief Gets the cluster by ID + */ + const local_cluster &cluster_by_id (typename local_cluster::id_type id) const; + + /** + * @brief Clears the clusters + */ + void clear (); + + /** + * @brief Removes a cluster with the given ID + */ + void remove_cluster (typename local_cluster::id_type id); + + /** + * @brief Joins a cluster with another one + * + * This will also remove the other cluster. + */ + void join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id); + + /** + * @brief Gets the bounding box of the clusters + */ + box_type bbox () const + { + const_cast *> (this)->ensure_sorted (); + return m_bbox; + } + + /** + * @brief Gets the clusters (begin iterator) + */ + const_iterator begin () const + { + return m_clusters.begin (); + } + + /** + * @brief Gets the clusters (end iterator) + */ + const_iterator end () const + { + return m_clusters.end (); + } + + /** + * @brief Gets a value indicating whether the cluster set is empty + */ + bool empty () const + { + return m_clusters.empty (); + } + + /** + * @brief Gets the clusters touching a given region + */ + touching_iterator begin_touching (const box_type &box) const + { + const_cast *> (this)->ensure_sorted (); + return m_clusters.begin_touching (box, local_cluster_box_convert ()); + } + + /** + * @brief Builds this collection from a cell and the given connectivity + * + * This method will only build the local clusters. Child cells + * are not taken into account. Only the shape types listed in + * shape_flags are taken. + * + * If attr_equivalence is non-null, all clusters with attributes + * listed as equivalent in this object are joined. Additional + * cluster joining may happen in this case, because multi-attribute + * assignment might create connections too. + */ + void build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence = 0, bool report_progress = false); + + /** + * @brief Creates and inserts a new clusters + * + * NOTE: the object should not be modified after sorting has taken place. + */ + local_cluster *insert (); + + /** + * @brief Allocates a new ID for dummy clusters + * + * Dummy cluster ID's will deliver empty clusters. Used for connectors. + */ + typename local_cluster::id_type insert_dummy () + { + return --m_next_dummy_id; + } + +private: + void ensure_sorted (); + + bool m_needs_update; + box_type m_bbox; + tree_type m_clusters; + size_t m_next_dummy_id; + + void apply_attr_equivalences (const tl::equivalence_clusters &attr_equivalence); +}; + +/** + * @brief The instance information for a cluster + */ +class DB_PUBLIC ClusterInstElement +{ +public: + ClusterInstElement (const db::InstElement &ie) + { + if (ie.array_inst.at_end ()) { + + m_inst_cell_index = std::numeric_limits::max (); + m_inst_trans = db::ICplxTrans (); + m_inst_prop_id = 0; + + } else { + + m_inst_cell_index = ie.inst_ptr.cell_index (); + m_inst_trans = ie.complex_trans (); + m_inst_prop_id = ie.inst_ptr.prop_id (); + + } + } + + ClusterInstElement (db::cell_index_type inst_cell_index, const db::ICplxTrans &inst_trans, db::properties_id_type inst_prop_id) + : m_inst_cell_index (inst_cell_index), m_inst_trans (inst_trans), m_inst_prop_id (inst_prop_id) + { + // .. nothing yet .. + } + + ClusterInstElement () + : m_inst_cell_index (std::numeric_limits::max ()), m_inst_trans (), m_inst_prop_id (0) + { + // .. nothing yet .. + } + + /** + * @brief Returns true, if the cluster does not have an instance + */ + bool has_instance () const + { + return m_inst_cell_index != std::numeric_limits::max (); + } + + /** + * @brief Gets the cell index of the cell which is instantiated + */ + db::cell_index_type inst_cell_index () const + { + return m_inst_cell_index; + } + + /** + * @brief Gets the instance transformation + */ + const db::ICplxTrans &inst_trans () const + { + return m_inst_trans; + } + + /** + * @brief Gets the instance properties id + */ + db::properties_id_type inst_prop_id () const + { + return m_inst_prop_id; + } + + /** + * @brief Equality + */ + bool operator== (const ClusterInstElement &other) const + { + return m_inst_cell_index == other.m_inst_cell_index && m_inst_trans == other.m_inst_trans && m_inst_prop_id == other.m_inst_prop_id; + } + + /** + * @brief Inequality + */ + bool operator!= (const ClusterInstElement &other) const + { + return ! operator== (other); + } + + /** + * @brief Less operator + */ + bool operator< (const ClusterInstElement &other) const + { + if (m_inst_cell_index != other.m_inst_cell_index) { + return m_inst_cell_index < other.m_inst_cell_index; + } + if (m_inst_trans != other.m_inst_trans) { + return m_inst_trans < other.m_inst_trans; + } + return m_inst_prop_id < other.m_inst_prop_id; + } + +private: + db::cell_index_type m_inst_cell_index; + db::ICplxTrans m_inst_trans; + db::properties_id_type m_inst_prop_id; +}; + +/** + * @brief A connection to a cluster in a child instance + */ +class DB_PUBLIC ClusterInstance + : public ClusterInstElement +{ +public: + ClusterInstance (size_t id, db::cell_index_type inst_cell_index, const db::ICplxTrans &inst_trans, db::properties_id_type inst_prop_id) + : ClusterInstElement (inst_cell_index, inst_trans, inst_prop_id), m_id (id) + { + // .. nothing yet .. + } + + ClusterInstance (size_t id, const db::InstElement &inst_element) + : ClusterInstElement (inst_element), m_id (id) + { + // .. nothing yet .. + } + + ClusterInstance (size_t id) + : ClusterInstElement (), m_id (id) + { + // .. nothing yet .. + } + + ClusterInstance () + : ClusterInstElement (), m_id (0) + { + // .. nothing yet .. + } + + /** + * @brief Gets the cluster ID + */ + size_t id () const + { + return m_id; + } + + /** + * @brief Equality + */ + bool operator== (const ClusterInstance &other) const + { + return m_id == other.m_id && ClusterInstElement::operator== (other); + } + + /** + * @brief Inequality + */ + bool operator!= (const ClusterInstance &other) const + { + return ! operator== (other); + } + + /** + * @brief Less operator + */ + bool operator< (const ClusterInstance &other) const + { + if (m_id != other.m_id) { + return m_id < other.m_id; + } + return ClusterInstElement::operator< (other); + } + +private: + size_t m_id; +}; + +template class hier_clusters; +template class connected_clusters; + +/** + * @brief An iterator delivering all clusters of a connected_clusters set + */ +template +class DB_PUBLIC connected_clusters_iterator +{ +public: + typedef typename local_cluster::id_type value_type; + + connected_clusters_iterator (const connected_clusters &c); + + connected_clusters_iterator &operator++ () + { + if (! m_lc_iter.at_end ()) { + ++m_lc_iter; + } else if (m_x_iter != m_x_iter_end) { + ++m_x_iter; + } + return *this; + } + + bool at_end () const + { + return m_lc_iter.at_end () && m_x_iter == m_x_iter_end; + } + + value_type operator* () const + { + if (m_lc_iter.at_end ()) { + return m_x_iter->first; + } else { + return m_lc_iter->id (); + } + } + +private: + typename local_clusters::const_iterator m_lc_iter; + typedef std::list connections_type; + typename std::map::id_type, connections_type>::const_iterator m_x_iter, m_x_iter_end; +}; + +/** + * @brief Local clusters with connections to clusters from child cells + * + * Clusters can get connected. There are incoming connections (from above the hierarchy) + * and outgoing connections (down to a child cell). + * + * "root" clusters are some that don't have incoming connections. There are only + * root clusters or clusters which are connected from every parent cell. There are no + * "half connected" clusters. + */ +template +class DB_PUBLIC connected_clusters + : public local_clusters +{ +public: + typedef typename local_clusters::id_type id_type; + typedef std::list connections_type; + typedef typename local_clusters::box_type box_type; + typedef connected_clusters_iterator all_iterator; + typedef typename std::map::id_type, connections_type>::const_iterator connections_iterator; + + /** + * @brief Constructor + */ + connected_clusters () + : local_clusters () + { + // .. nothing yet .. + } + + /** + * @brief Gets the connections for a given cluster ID + */ + const connections_type &connections_for_cluster (typename local_cluster::id_type id) const; + + /** + * @brief Reverse "connections_for_cluster" + * Finds the cluster which has a connection given by inst. + * Returns 0 if the given connection does not exist. + */ + typename local_cluster::id_type find_cluster_with_connection (const ClusterInstance &inst) const; + + /** + * @brief Adds a connection between a local cluster and one from a child instance + */ + void add_connection (typename local_cluster::id_type, const ClusterInstance &inst); + + /** + * @brief Joins the cluster id with the cluster with_id + * + * The "with_id" cluster is removed. All connections of "with_id" are transferred to the + * first one. All shapes of "with_id" are transferred to "id". + */ + void join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id); + + /** + * @brief An iterator delivering all clusters (even the connectors) + * + * This iterator will deliver ID's rather than cluster objects. + */ + all_iterator begin_all () const + { + return connected_clusters_iterator (*this); + } + + /** + * @brief Begin iterator for the connections + * + * The iterated object is a pair or (cluster id, connections_type). + */ + connections_iterator begin_connections () const + { + return m_connections.begin (); + } + + /** + * @brief Begin iterator for the connections + */ + connections_iterator end_connections () const + { + return m_connections.end (); + } + + /** + * @brief Gets a value indicating whether the cluster set is empty + */ + bool empty () const + { + return local_clusters::empty () && m_connections.empty (); + } + + /** + * @brief Returns true, if the given cluster ID is a root cluster + */ + bool is_root (id_type id) const + { + return m_connected_clusters.find (id) == m_connected_clusters.end (); + } + + /** + * @brief Resets the root status of a cluster + * CAUTION: don't call this method unless you know what you're doing. + */ + void reset_root (id_type id) + { + m_connected_clusters.insert (id); + } + +private: + template friend class connected_clusters_iterator; + + std::map m_connections; + std::map::id_type> m_rev_connections; + std::set m_connected_clusters; +}; + +template class cell_clusters_box_converter; + +/** + * @brief A hierarchical representation of clusters + * + * Hierarchical clusters + */ +template +class DB_PUBLIC hier_clusters + : public tl::Object +{ +public: + typedef typename local_cluster::box_type box_type; + + /** + * @brief Creates an empty set of clusters + */ + hier_clusters (); + + /** + * @brief Sets the base verbosity + * + * The default value is 30. Basic timing will be reported for > base_verbosity, detailed timing + * for > base_verbosity + 10. + */ + void set_base_verbosity (int bv); + + /** + * @brief Builds a hierarchy of clusters from a cell hierarchy and given connectivity + */ + void build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence = 0); + + /** + * @brief Gets the connected clusters for a given cell + */ + const connected_clusters &clusters_per_cell (db::cell_index_type cell_index) const; + + /** + * @brief Gets the connected clusters for a given cell (non-const version) + */ + connected_clusters &clusters_per_cell (db::cell_index_type cell_index); + + /** + * @brief Writes the net shapes back to the original hierarchy + * + * The layout object is supposed to be the original layout or one with identical cell indexes. + * "lm" is a layer mapping table from the connection layer indexes to the target layer + * indexes. + * + * The backannotation process usually involves propagation of shapes up in the hierarchy + * to resolve variants. + */ + void return_to_hierarchy (db::Layout &layout, const std::map &lm) const; + + /** + * @brief Clears this collection + */ + void clear (); + + /** + * @brief Makes a valid path to a child cluster + * + * Cluster connections can only cross one level of hierarchy. This method + * creates necessary dummy entries for the given path. + */ + ClusterInstance make_path (const db::Layout &layout, const db::Cell &cell, size_t id, const std::vector &path); + +private: + void build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence); + void build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn); + void build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn, tl::RelativeProgress &progress); + void do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence = 0); + + std::map > m_per_cell_clusters; + int m_base_verbosity; +}; + +/** + * @brief A recursive shape iterator for the shapes of a cluster + * + * This iterator will deliver the shapes of a cluster including the shapes for the + * connected child clusters. + * + * This iterator applies to one layer. + */ +template +class DB_PUBLIC recursive_cluster_shape_iterator +{ +public: + typedef T value_type; + typedef const T &reference; + typedef const T *pointer; + + /** + * @brief Constructor + */ + recursive_cluster_shape_iterator (const hier_clusters &hc, unsigned int layer, db::cell_index_type ci, typename local_cluster::id_type id); + + /** + * @brief Returns a value indicating whether there are any more shapes + */ + bool at_end () const + { + return m_shape_iter.at_end (); + } + + /** + * @brief Returns the shape (untransformed) + */ + reference operator* () const + { + return *m_shape_iter; + } + + /** + * @brief Returns the shape pointer (untransformed) + */ + pointer operator-> () const + { + return m_shape_iter.operator-> (); + } + + /** + * @brief Returns the instantiation path of the current cluster + * + * The call path's root is the initial cell + */ + std::vector inst_path () const; + + /** + * @brief Returns the transformation applicable for transforming the shape to the root cluster + */ + const db::ICplxTrans &trans () const + { + return m_trans_stack.back (); + } + + /** + * @brief Returns the cell index the shape lives in + */ + db::cell_index_type cell_index () const + { + return m_cell_index_stack.back (); + } + + /** + * @brief Returns the id of the current cluster + */ + typename db::local_cluster::id_type cluster_id () const + { + if (m_conn_iter_stack.size () <= 1) { + return m_id; + } else { + return m_conn_iter_stack [m_conn_iter_stack.size () - 2].first->id (); + } + } + + /** + * @brief Increment operator + */ + recursive_cluster_shape_iterator &operator++ (); + + /** + * @brief Skips the current cell and advances to the next cell and shape + */ + void skip_cell (); + +private: + typedef typename db::connected_clusters::connections_type connections_type; + + const hier_clusters *mp_hc; + std::vector m_trans_stack; + std::vector m_cell_index_stack; + std::vector > m_conn_iter_stack; + typename db::local_cluster::shape_iterator m_shape_iter; + unsigned int m_layer; + typename db::local_cluster::id_type m_id; + + void next_conn (); + void up (); + void down (db::cell_index_type ci, typename db::local_cluster::id_type id, const db::ICplxTrans &t); +}; + +/** + * @brief A recursive cluster iterator for the clusters itself + * + * This iterator will deliver the child clusters of a specific cluster. + */ +template +class DB_PUBLIC recursive_cluster_iterator +{ +public: + /** + * @brief Constructor + */ + recursive_cluster_iterator (const hier_clusters &hc, db::cell_index_type ci, typename local_cluster::id_type id); + + /** + * @brief Returns a value indicating whether there are any more shapes + */ + bool at_end () const + { + return m_cell_index_stack.empty (); + } + + /** + * @brief Returns the cell index the shape lives in + */ + db::cell_index_type cell_index () const + { + return m_cell_index_stack.back (); + } + + /** + * @brief Returns the id of the current cluster + */ + typename db::local_cluster::id_type cluster_id () const + { + if (m_conn_iter_stack.size () <= 1) { + return m_id; + } else { + return m_conn_iter_stack [m_conn_iter_stack.size () - 2].first->id (); + } + } + + /** + * @brief Returns the instantiation path of the current cluster + * + * The call path's root is the initial cell + */ + std::vector inst_path () const; + + /** + * @brief Increment operator + */ + recursive_cluster_iterator &operator++ (); + +private: + typedef typename db::connected_clusters::connections_type connections_type; + + const hier_clusters *mp_hc; + std::vector m_cell_index_stack; + std::vector > m_conn_iter_stack; + typename db::local_cluster::id_type m_id; + + void next_conn (); + void up (); + void down (db::cell_index_type ci, typename db::local_cluster::id_type id); +}; + +/** + * @brief A connection to a cluster from a parent cluster + */ +class DB_PUBLIC IncomingClusterInstance +{ +public: + IncomingClusterInstance (db::cell_index_type pc, size_t parent_cluster_id, const ClusterInstance &inst) + : m_parent_cell (pc), m_parent_cluster_id (parent_cluster_id), m_inst (inst) + { + // .. nothing yet .. + } + + IncomingClusterInstance () + : m_parent_cell (0), m_parent_cluster_id (0), m_inst () + { + // .. nothing yet .. + } + + /** + * @brief Gets the cell index of the parent cell + */ + size_t parent_cell () const + { + return m_parent_cell; + } + + /** + * @brief Gets the cluster ID from which the cluster is connected to + * The parent cluster lives in the parent cell + */ + size_t parent_cluster_id () const + { + return m_parent_cluster_id; + } + + /** + * @brief Gets the instance path + */ + const ClusterInstance &inst () const + { + return m_inst; + } + + /** + * @brief Equality + */ + bool operator== (const IncomingClusterInstance &other) const + { + return m_parent_cluster_id == other.m_parent_cluster_id && m_parent_cell == other.m_parent_cell && m_inst == other.m_inst; + } + + /** + * @brief Less operator + */ + bool operator< (const IncomingClusterInstance &other) const + { + if (m_parent_cluster_id != other.m_parent_cluster_id) { + return m_parent_cluster_id < other.m_parent_cluster_id; + } + if (m_parent_cell != other.m_parent_cell) { + return m_parent_cell < other.m_parent_cell; + } + return m_inst < other.m_inst; + } + +private: + db::cell_index_type m_parent_cell; + size_t m_parent_cluster_id; + ClusterInstance m_inst; +}; + +/** + * @brief A class holding the parent relationships for clusters of cells + * + * This class can be used to quickly identify the connections made to a specific cluster from a parent cluster. + */ +template +class incoming_cluster_connections +{ +public: + typedef std::list incoming_connections; + + incoming_cluster_connections (const db::Layout &layout, const db::Cell &cell, const hier_clusters &hc); + + bool has_incoming (db::cell_index_type ci, size_t cluster_id) const; + const incoming_connections &incoming (db::cell_index_type ci, size_t cluster_id) const; + +private: + mutable std::set m_called_cells; + mutable std::map > m_incoming; + tl::weak_ptr mp_layout; + tl::weak_ptr > mp_hc; + + void ensure_computed (db::cell_index_type ci) const; + void ensure_computed_parent (db::cell_index_type ci) const; +}; + +} + +#endif diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc new file mode 100644 index 000000000..a88743c18 --- /dev/null +++ b/src/db/db/dbHierProcessor.cc @@ -0,0 +1,1619 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbHierProcessor.h" +#include "dbBoxScanner.h" +#include "dbRecursiveShapeIterator.h" +#include "dbBoxConvert.h" +#include "dbEdgeProcessor.h" +#include "dbPolygonGenerators.h" +#include "dbLocalOperationUtils.h" +#include "tlLog.h" +#include "tlTimer.h" +#include "tlInternational.h" + +// --------------------------------------------------------------------------------------------- +// Cronology debugging support (TODO: experimental) + +#if defined(HAVE_CRONOLOGY) + +#include + +CRONOLOGY_MAKE_EVENT(event_compute_contexts, "Compute contexts") +CRONOLOGY_MAKE_EVENT(event_compute_contexts_unlocked, "Compute contexts (unlocked)") + +cronology::events::event_collection collect_events; + +#define CRONOLOGY_COLLECTION_BRACKET(event_name) cronology::EventBracket __bracket##event_name (collect_events.threadwise ()->event ().event ()); + +CRONOLOGY_MAKE_EVENT(event_compute_results, "Compute results") +CRONOLOGY_MAKE_EVENT(event_compute_local_cell, "Compute local cell results") +CRONOLOGY_MAKE_EVENT(event_propagate, "Propagate local cell results") + +cronology::events::event_collection compute_events; + +#define CRONOLOGY_COMPUTE_BRACKET(event_name) cronology::EventBracket __bracket##event_name (compute_events.threadwise ()->event ().event ()); + +#else +#define CRONOLOGY_COLLECTION_BRACKET(event_name) +#define CRONOLOGY_COMPUTE_BRACKET(event_name) +#endif + +namespace db +{ + +// --------------------------------------------------------------------------------------------- +// Shape reference translator + +template class shape_reference_translator; +template class shape_reference_translator_with_trans; + +template +class shape_reference_translator +{ +public: + typedef typename Ref::shape_type shape_type; + typedef typename Ref::trans_type ref_trans_type; + + shape_reference_translator (db::Layout *target_layout) + : mp_layout (target_layout) + { + // .. nothing yet .. + } + + Ref operator() (const Ref &ref) const + { + typename std::unordered_map::const_iterator m = m_cache.find (ref.ptr ()); + if (m != m_cache.end ()) { + + return Ref (m->second, ref.trans ()); + + } else { + + const shape_type *ptr; + { + tl::MutexLocker locker (&mp_layout->lock ()); + ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (ref.obj ()); + } + + m_cache[ref.ptr ()] = ptr; + return Ref (ptr, ref.trans ()); + + } + } + + template + Ref operator() (const Ref &ref, const Trans &tr) const + { + shape_type sh = ref.obj ().transformed (tr * Trans (ref.trans ())); + ref_trans_type red_trans; + sh.reduce (red_trans); + + typename std::unordered_map::const_iterator m = m_cache_by_shape.find (sh); + if (m != m_cache_by_shape.end ()) { + + return Ref (m->second, red_trans); + + } else { + + const shape_type *ptr; + { + tl::MutexLocker locker (&mp_layout->lock ()); + ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (sh); + } + + m_cache_by_shape[sh] = ptr; + return Ref (ptr, red_trans); + + } + } + +private: + db::Layout *mp_layout; + mutable std::unordered_map m_cache; + mutable std::unordered_map m_cache_by_shape; +}; + +template <> +class shape_reference_translator +{ +public: + typedef typename db::Edge shape_type; + + shape_reference_translator (db::Layout * /*target_layout*/) + { + // .. nothing yet .. + } + + const shape_type &operator() (const shape_type &s) const + { + return s; + } + + template + shape_type operator() (const shape_type &s, const Trans &tr) const + { + return s.transformed (tr); + } +}; + +template +class shape_reference_translator_with_trans_from_shape_ref +{ +public: + typedef typename Ref::shape_type shape_type; + typedef typename Ref::trans_type ref_trans_type; + + shape_reference_translator_with_trans_from_shape_ref (db::Layout *target_layout, const Trans &trans) + : mp_layout (target_layout), m_trans (trans), m_ref_trans (trans), m_bare_trans (Trans (m_ref_trans.inverted ()) * trans) + { + // .. nothing yet .. + } + + Ref operator() (const Ref &ref) const + { + typename std::unordered_map >::const_iterator m = m_cache.find (ref.ptr ()); + if (m != m_cache.end ()) { + + return Ref (m->second.first, ref_trans_type (m_trans * Trans (ref.trans ())) * m->second.second); + + } else { + + shape_type sh = ref.obj ().transformed (m_bare_trans); + ref_trans_type red_trans; + sh.reduce (red_trans); + + const shape_type *ptr; + { + tl::MutexLocker locker (&mp_layout->lock ()); + ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (sh); + } + + m_cache[ref.ptr ()] = std::make_pair (ptr, red_trans); + + return Ref (ptr, ref_trans_type (m_trans * Trans (ref.trans ())) * red_trans); + + } + } + +private: + db::Layout *mp_layout; + Trans m_trans; + ref_trans_type m_ref_trans; + Trans m_bare_trans; + mutable std::unordered_map > m_cache; +}; + +template +class shape_reference_translator_with_trans + : public shape_reference_translator_with_trans_from_shape_ref +{ +public: + shape_reference_translator_with_trans (db::Layout *target_layout, const Trans &trans) + : shape_reference_translator_with_trans_from_shape_ref (target_layout, trans) + { + // .. nothing yet .. + } +}; + +template +class shape_reference_translator_with_trans +{ +public: + typedef Sh shape_type; + + shape_reference_translator_with_trans (db::Layout * /*target_layout*/, const Trans &trans) + : m_trans (trans) + { + // .. nothing yet .. + } + + shape_type operator() (const shape_type &s) const + { + return s.transformed (m_trans); + } + +private: + Trans m_trans; +}; + +// --------------------------------------------------------------------------------------------- + +/** + * @brief Safe enlargement of a box + * Boxes must not vanish when augmented for overlapping queries. Hence we must not make + * the boxes shrinked too much on enlarge. + */ +db::Box safe_box_enlarged (const db::Box &box, db::Coord dx, db::Coord dy) +{ + if (box.empty ()) { + return box; + } else { + db::Coord w2 = db::Coord (box.width () / 2); + db::Coord h2 = db::Coord (box.height () / 2); + if (dx + w2 < 0) { + dx = -w2; + } + if (dy + h2 < 0) { + dy = -h2; + } + return box.enlarged (db::Vector (dx, dy)); + } +} + +// --------------------------------------------------------------------------------------------- +// LocalProcessorCellContext implementation + +template +local_processor_cell_context::local_processor_cell_context () +{ + // .. nothing yet .. +} + +template +local_processor_cell_context::local_processor_cell_context (const local_processor_cell_context &other) + : m_propagated (other.m_propagated), m_drops (other.m_drops) +{ + // .. nothing yet .. +} + +template +void +local_processor_cell_context::add (db::local_processor_cell_context *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst) +{ + m_drops.push_back (local_processor_cell_drop (parent_context, parent, cell_inst)); +} + +template +void +local_processor_cell_context::propagate (const std::unordered_set &res) +{ + if (res.empty ()) { + return; + } + + for (typename std::vector >::const_iterator d = m_drops.begin (); d != m_drops.end (); ++d) { + + tl_assert (d->parent_context != 0); + tl_assert (d->parent != 0); + + db::Layout *subject_layout = d->parent->layout (); + shape_reference_translator_with_trans rt (subject_layout, d->cell_inst); + std::vector new_refs; + new_refs.reserve (res.size ()); + for (typename std::unordered_set::const_iterator r = res.begin (); r != res.end (); ++r) { + new_refs.push_back (rt (*r)); + } + + { + tl::MutexLocker locker (&d->parent_context->lock ()); + d->parent_context->propagated ().insert (new_refs.begin (), new_refs.end ()); + } + + } +} + +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; + +// --------------------------------------------------------------------------------------------- +// LocalProcessorCellContexts implementation + +template +local_processor_cell_contexts::local_processor_cell_contexts () + : mp_intruder_cell (0) +{ + // .. nothing yet .. +} + +template +local_processor_cell_contexts::local_processor_cell_contexts (const db::Cell *intruder_cell) + : mp_intruder_cell (intruder_cell) +{ + // .. nothing yet .. +} + +template +db::local_processor_cell_context * +local_processor_cell_contexts::find_context (const context_key_type &intruders) +{ + typename std::unordered_map >::iterator c = m_contexts.find (intruders); + return c != m_contexts.end () ? &c->second : 0; +} + +template +db::local_processor_cell_context * +local_processor_cell_contexts::create (const context_key_type &intruders) +{ + return &m_contexts[intruders]; +} + +template +static void +subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout *layout, const db::local_processor *proc) +{ + if (other.empty ()) { + return; + } + + size_t max_vertex_count = proc->max_vertex_count (); + double area_ratio = proc->area_ratio (); + + db::EdgeProcessor ep; + ep.set_base_verbosity (proc->base_verbosity () + 30); + + size_t p1 = 0, p2 = 1; + + for (std::unordered_set::const_iterator i = res.begin (); i != res.end (); ++i) { + const db::PolygonRef &subject = *i; + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + for (std::unordered_set::const_iterator i = other.begin (); i != other.end (); ++i) { + const db::PolygonRef &subject = *i; + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + res.clear (); + db::BooleanOp op (db::BooleanOp::ANotB); + db::PolygonRefGenerator pr (layout, res); + db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); + db::PolygonGenerator pg (splitter, true, true); + ep.process (pg, op); +} + +template +static void +subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout * /*layout*/, const db::local_processor * /*proc*/) +{ + // for edges, we don't use a boolean core but just set intersection + for (typename std::unordered_set::const_iterator o = other.begin (); o != other.end (); ++o) { + res.erase (*o); + } +} + +namespace { + +template +struct context_sorter +{ + bool operator () (const std::pair::context_key_type *, db::local_processor_cell_context *> &a, + const std::pair::context_key_type *, db::local_processor_cell_context *> &b) + { + return *a.first < *b.first; + } +}; + +} + +template +void +local_processor_cell_contexts::compute_results (const local_processor_contexts &contexts, db::Cell *cell, const local_operation *op, unsigned int output_layer, const local_processor *proc) +{ + CRONOLOGY_COMPUTE_BRACKET(event_compute_results) + + bool first = true; + std::unordered_set common; + + int index = 0; + int total = int (m_contexts.size ()); + + // NOTE: we use the ordering provided by key_type::operator< rather than the unordered map to achieve + // reproducability across different platforms. unordered_map is faster, but for processing them, + // strict ordering is a more robust choice. + std::vector *> > sorted_contexts; + sorted_contexts.reserve (m_contexts.size ()); + for (typename std::unordered_map >::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) { + sorted_contexts.push_back (std::make_pair (&c->first, &c->second)); + } + + std::sort (sorted_contexts.begin (), sorted_contexts.end (), context_sorter ()); + + for (typename std::vector *> >::const_iterator c = sorted_contexts.begin (); c != sorted_contexts.end (); ++c) { + + ++index; + + if (tl::verbosity () >= proc->base_verbosity () + 20) { + tl::log << tr ("Computing local results for ") << cell->layout ()->cell_name (cell->cell_index ()) << " (context " << index << "/" << total << ")"; + } + + if (first) { + + { + tl::MutexLocker locker (&c->second->lock ()); + common = c->second->propagated (); + } + + CRONOLOGY_COMPUTE_BRACKET(event_compute_local_cell) + proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, *c->first, common); + first = false; + + } else { + + std::unordered_set res; + { + tl::MutexLocker locker (&c->second->lock ()); + res = c->second->propagated (); + } + + { + CRONOLOGY_COMPUTE_BRACKET(event_compute_local_cell) + proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, *c->first, res); + } + + if (common.empty ()) { + + CRONOLOGY_COMPUTE_BRACKET(event_propagate) + c->second->propagate (res); + + } else if (res != common) { + + CRONOLOGY_COMPUTE_BRACKET(event_propagate) + + std::unordered_set lost; + for (typename std::unordered_set::const_iterator i = common.begin (); i != common.end (); ++i) { + if (res.find (*i) == res.end ()) { + lost.insert (*i); + } + } + + if (! lost.empty ()) { + + subtract (lost, res, cell->layout (), proc); + + if (! lost.empty ()) { + subtract (common, lost, cell->layout (), proc); + for (typename std::vector *> >::const_iterator cc = sorted_contexts.begin (); cc != c; ++cc) { + cc->second->propagate (lost); + } + } + + } + + std::unordered_set gained; + for (typename std::unordered_set::const_iterator i = res.begin (); i != res.end (); ++i) { + if (common.find (*i) == common.end ()) { + gained.insert (*i); + } + } + + if (! gained.empty ()) { + + subtract (gained, common, cell->layout (), proc); + + if (! gained.empty ()) { + c->second->propagate (gained); + } + + } + + } + + } + + } + + proc->push_results (cell, output_layer, common); +} + +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; + +// --------------------------------------------------------------------------------------------- + +template +shape_interactions::shape_interactions () + : m_id (0) +{ + // .. nothing yet .. +} + +template +bool +shape_interactions::has_intruder_shape_id (unsigned int id) const +{ + return m_intruder_shapes.find (id) != m_intruder_shapes.end (); +} + +template +bool +shape_interactions::has_subject_shape_id (unsigned int id) const +{ + return m_subject_shapes.find (id) != m_subject_shapes.end (); +} + +template +void +shape_interactions::add_intruder_shape (unsigned int id, const TI &shape) +{ + m_intruder_shapes [id] = shape; +} + +template +void +shape_interactions::add_subject_shape (unsigned int id, const TS &shape) +{ + m_subject_shapes [id] = shape; +} + +template +void +shape_interactions::add_subject (unsigned int id, const TS &shape) +{ + m_subject_shapes [id] = shape; + m_interactions.insert (std::make_pair (id, container::value_type::second_type ())); +} + +template +void +shape_interactions::add_interaction (unsigned int subject_id, unsigned int intruder_id) +{ + m_interactions [subject_id].push_back (intruder_id); +} + +template +const std::vector & +shape_interactions::intruders_for (unsigned int subject_id) const +{ + iterator i = m_interactions.find (subject_id); + if (i == m_interactions.end ()) { + static std::vector empty; + return empty; + } else { + return i->second; + } +} + +template +const TS & +shape_interactions::subject_shape (unsigned int id) const +{ + typename std::unordered_map::const_iterator i = m_subject_shapes.find (id); + if (i == m_subject_shapes.end ()) { + static TS s; + return s; + } else { + return i->second; + } +} + +template +const TI & +shape_interactions::intruder_shape (unsigned int id) const +{ + typename std::unordered_map::const_iterator i = m_intruder_shapes.find (id); + if (i == m_intruder_shapes.end ()) { + static TI s; + return s; + } else { + return i->second; + } +} + +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; +template class DB_PUBLIC shape_interactions; + +// --------------------------------------------------------------------------------------------- +// Helper classes for the LocalProcessor + +namespace +{ + +template unsigned int shape_flags (); + +template <> +inline unsigned int shape_flags () +{ + return 1 << db::ShapeIterator::PolygonRef; +} + +template <> +inline unsigned int shape_flags () +{ + return db::ShapeIterator::Edges; +} + +template +struct interaction_registration_shape2shape + : db::box_scanner_receiver2 +{ +public: + interaction_registration_shape2shape (db::Layout *layout, shape_interactions *result) + : mp_result (result), mp_layout (layout) + { + // nothing yet .. + } + + void add (const TS *ref1, unsigned int id1, const TI *ref2, unsigned int id2) + { + mp_result->add_subject_shape (id1, *ref1); + + if (mp_layout) { + // In order to guarantee the refs come from the subject layout, we'd need to + // rewrite them + if (!mp_result->has_intruder_shape_id (id2)) { + db::shape_reference_translator rt (mp_layout); + mp_result->add_intruder_shape (id2, rt (*ref2)); + } + } else { + mp_result->add_intruder_shape (id2, *ref2); + } + + mp_result->add_interaction (id1, id2); + } + +private: + shape_interactions *mp_result; + db::Layout *mp_layout; +}; + +template +struct interaction_registration_shape1 + : db::box_scanner_receiver2 +{ +public: + interaction_registration_shape1 (shape_interactions *result) + : mp_result (result) + { + // nothing yet .. + } + + void add (const TS *ref1, unsigned int id1, const TI *ref2, unsigned int id2) + { + mp_result->add_subject_shape (id1, *ref1); + mp_result->add_intruder_shape (id2, *ref2); + mp_result->add_interaction (id1, id2); + } + +private: + shape_interactions *mp_result; +}; + +template +struct interaction_registration_shape1 + : db::box_scanner_receiver +{ +public: + interaction_registration_shape1 (shape_interactions *result) + : mp_result (result) + { + // nothing yet .. + } + + void add (const T *ref1, unsigned int id1, const T *ref2, unsigned int id2) + { + mp_result->add_subject_shape (id1, *ref1); + mp_result->add_intruder_shape (id2, *ref2); + mp_result->add_interaction (id1, id2); + } + +private: + shape_interactions *mp_result; +}; + +template +struct interaction_registration_shape2inst + : db::box_scanner_receiver2 +{ +public: + interaction_registration_shape2inst (db::Layout *subject_layout, const db::Layout *intruder_layout, unsigned int intruder_layer, db::Coord dist, shape_interactions *result) + : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_intruder_layer (intruder_layer), m_dist (dist), mp_result (result) + { + // nothing yet .. + } + + void add (const TS *ref, unsigned int id1, const db::CellInstArray *inst, unsigned int inst_id) + { + const db::Cell &intruder_cell = mp_intruder_layout->cell (inst->object ().cell_index ()); + db::box_convert inst_bc (*mp_intruder_layout, m_intruder_layer); + mp_result->add_subject_shape (id1, *ref); + + // Find all instance array members that potentially interact with the shape and use + // add_shapes_from_intruder_inst on them + db::Box ref_box = db::box_convert () (*ref); + for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (ref_box, m_dist - 1, m_dist - 1), inst_bc); !n.at_end (); ++n) { + db::ICplxTrans tn = inst->complex_trans (*n); + db::Box region = ref_box.transformed (tn.inverted ()).enlarged (db::Vector (m_dist, m_dist)) & intruder_cell.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist)); + if (! region.empty ()) { + add_shapes_from_intruder_inst (id1, intruder_cell, tn, inst_id, region); + } + } + } + +private: + db::Layout *mp_subject_layout; + const db::Layout *mp_intruder_layout; + unsigned int m_intruder_layer; + db::Coord m_dist; + shape_interactions *mp_result; + std::unordered_map m_inst_shape_ids; + + void add_shapes_from_intruder_inst (unsigned int id1, const db::Cell &intruder_cell, const db::ICplxTrans &tn, unsigned int /*inst_id*/, const db::Box ®ion) + { + db::shape_reference_translator rt (mp_subject_layout); + + // Look up all shapes from the intruder instance which interact with the subject shape + // (given through region) + // TODO: should be lighter, cache, handle arrays .. + db::RecursiveShapeIterator si (*mp_intruder_layout, intruder_cell, m_intruder_layer, region); + si.shape_flags (shape_flags ()); + while (! si.at_end ()) { + + // NOTE: we intentionally rewrite to the *subject* layout - this way polygon refs in the context come from the + // subject, not from the intruder. + TI ref2 = rt (*si.shape ().basic_ptr (typename TI::tag ()), tn * si.trans ()); + + // reuse the same id for shapes from the same instance -> this avoid duplicates with different IDs on + // the intruder side. + typename std::unordered_map::const_iterator k = m_inst_shape_ids.find (ref2); + if (k == m_inst_shape_ids.end ()) { + + k = m_inst_shape_ids.insert (std::make_pair (ref2, mp_result->next_id ())).first; + mp_result->add_intruder_shape (k->second, ref2); + + } + + mp_result->add_interaction (id1, k->second); + + ++si; + + } + } +}; + +static bool +instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, unsigned int layer1, const db::Layout *layout2, const db::CellInstArray *inst2, unsigned int layer2, db::Coord dist) +{ + // TODO: this algorithm is not in particular effective for identical arrays + + const db::Cell &cell1 = layout1->cell (inst1->object ().cell_index ()); + const db::Cell &cell2 = layout2->cell (inst2->object ().cell_index ()); + db::box_convert inst2_bc (*layout2, layer2); + + std::unordered_set relative_trans_seen; + + for (db::CellInstArray::iterator n = inst1->begin (); ! n.at_end (); ++n) { + + db::ICplxTrans tn1 = inst1->complex_trans (*n); + db::ICplxTrans tni1 = tn1.inverted (); + db::Box ibox1 = tn1 * cell1.bbox (layer1).enlarged (db::Vector (dist, dist)); + + if (! ibox1.empty ()) { + + // TODO: in some cases, it may be possible to optimize this for arrays + + for (db::CellInstArray::iterator k = inst2->begin_touching (safe_box_enlarged (ibox1, -1, -1), inst2_bc); ! k.at_end (); ++k) { + + if (inst1 == inst2 && *n == *k) { + // skip self-interactions - this is handled inside the cell + continue; + } + + db::ICplxTrans tn2 = inst2->complex_trans (*k); + + // NOTE: we need to enlarge both subject *and* intruder boxes - either ubject comes close to intruder or the other way around + db::Box ibox2 = tn2 * cell2.bbox (layer2).enlarged (db::Vector (dist, dist)); + + db::ICplxTrans tn21 = tni1 * tn2; + if (! relative_trans_seen.insert (tn21).second) { + // this relative transformation was already seen + continue; + } + + db::Box cbox = ibox1 & ibox2; + if (! cbox.empty ()) { + + db::ICplxTrans tni2 = tn2.inverted (); + + // not very strong, but already useful: the cells interact if there is a layer1 in cell1 + // in the common box and a layer2 in the cell2 in the common box + if (! db::RecursiveShapeIterator (*layout1, cell1, layer1, tni1 * cbox, true).at_end () && + ! db::RecursiveShapeIterator (*layout2, cell2, layer2, tni2 * cbox, true).at_end ()) { + return true; + } + + } + + } + + } + + } + + return false; +} + +template +struct interaction_registration_inst2inst + : db::box_scanner_receiver2 +{ +public: + typedef std::pair, std::unordered_set > interaction_value_type; + + interaction_registration_inst2inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, db::Coord dist, std::unordered_map *result) + : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_subject_layer (subject_layer), m_intruder_layer (intruder_layer), m_dist (dist), mp_result (result) + { + // nothing yet .. + } + + void add (const db::CellInstArray *inst1, unsigned int id1, const db::CellInstArray *inst2, unsigned int id2) + { + // NOTE: self-interactions are possible for arrays: different elements of the + // array may interact which is a cell-external interaction. + if (mp_subject_layout != mp_intruder_layout || id1 != id2 || inst1->size () > 1) { + + bool ignore = false; + if (mp_subject_layout == mp_intruder_layout && m_subject_layer == m_intruder_layer) { + if (m_interactions.find (std::make_pair (id2, id1)) != m_interactions.end ()) { + // for self interactions ignore the reverse interactions + ignore = true; + } else { + m_interactions.insert (std::make_pair (id1, id2)); + } + } + + if (! ignore && instances_interact (mp_subject_layout, inst1, m_subject_layer, mp_intruder_layout, inst2, m_intruder_layer, m_dist)) { + (*mp_result) [inst1].first.insert (inst2); + } + + } + } + +private: + const db::Layout *mp_subject_layout, *mp_intruder_layout; + unsigned int m_subject_layer, m_intruder_layer; + db::Coord m_dist; + std::unordered_map, std::unordered_set > > *mp_result; + std::unordered_set > m_interactions; +}; + +template +static bool +instance_shape_interacts (const db::Layout *layout, const db::CellInstArray *inst, unsigned int layer, const T &ref, db::Coord dist) +{ + const db::Cell &cell = layout->cell (inst->object ().cell_index ()); + db::box_convert inst_bc (*layout, layer); + db::Box rbox = db::box_convert () (ref); + + for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (rbox, dist - 1, dist - 1), inst_bc); ! n.at_end (); ++n) { + + db::ICplxTrans tn = inst->complex_trans (*n); + db::Box cbox = (tn * cell.bbox (layer)).enlarged (db::Vector (dist, dist)) & rbox.enlarged (db::Vector (dist, dist)); + + if (! cbox.empty ()) { + + db::ICplxTrans tni = tn.inverted (); + + // not very strong, but already useful: the cells interact if there is a layer in cell + // in the common box + if (! db::RecursiveShapeIterator (*layout, cell, layer, tni * cbox, true).at_end ()) { + return true; + } + + } + + } + + return false; +} + +template +struct interaction_registration_inst2shape + : db::box_scanner_receiver2 +{ +public: + interaction_registration_inst2shape (const db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, std::unordered_map, std::unordered_set > > *result) + : mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), mp_result (result) + { + // nothing yet .. + } + + void add (const db::CellInstArray *inst, unsigned int, const T *ref, unsigned int) + { + if (instance_shape_interacts (mp_subject_layout, inst, m_subject_layer, *ref, m_dist)) { + (*mp_result) [inst].second.insert (*ref); + } + } + +private: + const db::Layout *mp_subject_layout; + unsigned int m_subject_layer; + db::Coord m_dist; + std::unordered_map, std::unordered_set > > *mp_result; +}; + +} + +// --------------------------------------------------------------------------------------------- +// LocalProcessorContextComputationTask implementation + +template +local_processor_context_computation_task::local_processor_context_computation_task (const local_processor *proc, local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist) + : tl::Task (), + mp_proc (proc), mp_contexts (&contexts), mp_parent_context (parent_context), + mp_subject_parent (subject_parent), mp_subject_cell (subject_cell), m_subject_cell_inst (subject_cell_inst), + mp_intruder_cell (intruder_cell), m_dist (dist) +{ + // This is quick, but will take away the intruders from the caller + m_intruders.swap (intruders); +} + +template +void +local_processor_context_computation_task::perform () +{ + mp_proc->compute_contexts (*mp_contexts, mp_parent_context, mp_subject_parent, mp_subject_cell, m_subject_cell_inst, mp_intruder_cell, m_intruders, m_dist); +} + +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; +template class DB_PUBLIC local_processor_context_computation_task; + +// --------------------------------------------------------------------------------------------- +// LocalProcessorResultComputationTask implementation + +template +local_processor_result_computation_task::local_processor_result_computation_task (const local_processor *proc, local_processor_contexts &contexts, db::Cell *cell, local_processor_cell_contexts *cell_contexts, const local_operation *op, unsigned int output_layer) + : mp_proc (proc), mp_contexts (&contexts), mp_cell (cell), mp_cell_contexts (cell_contexts), mp_op (op), m_output_layer (output_layer) +{ + // .. nothing yet .. +} + +template +void +local_processor_result_computation_task::perform () +{ + mp_cell_contexts->compute_results (*mp_contexts, mp_cell, mp_op, m_output_layer, mp_proc); + + // erase the contexts we don't need any longer + { + tl::MutexLocker locker (& mp_contexts->lock ()); + +#if defined(ENABLE_DB_HP_SANITY_ASSERTIONS) + std::set *> td; + for (typename db::local_processor_cell_contexts::iterator i = mp_cell_contexts->begin (); i != mp_cell_contexts->end (); ++i) { + td.insert (&i->second); + } + for (typename db::local_processor_cell_contexts::contexts_per_cell_type::iterator pcc = mp_contexts->context_map ().begin (); pcc != mp_contexts->context_map ().end (); ++pcc) { + for (typename db::local_processor_cell_contexts::iterator i = pcc->second.begin (); i != pcc->second.end (); ++i) { + for (typename db::local_processor_cell_context::drop_iterator j = i->second.begin_drops (); j != i->second.end_drops (); ++j) { + if (td.find (j->parent_context) != td.end ()) { + tl_assert (false); + } + } + } + } +#endif + + mp_contexts->context_map ().erase (mp_cell); + } +} + +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; +template class DB_PUBLIC local_processor_result_computation_task; + +// --------------------------------------------------------------------------------------------- +// LocalProcessor implementation + +template +local_processor::local_processor (db::Layout *layout, db::Cell *top) + : mp_subject_layout (layout), mp_intruder_layout (layout), mp_subject_top (top), mp_intruder_top (top), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_base_verbosity (30) +{ + // .. nothing yet .. +} + +template +local_processor::local_processor (db::Layout *subject_layout, db::Cell *subject_top, const db::Layout *intruder_layout, const db::Cell *intruder_top) + : mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), mp_subject_top (subject_top), mp_intruder_top (intruder_top), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_base_verbosity (30) +{ + // .. nothing yet .. +} + +template +std::string local_processor::description (const local_operation *op) const +{ + if (op && m_description.empty ()) { + return op->description (); + } else { + return m_description; + } +} + +template +void local_processor::run (local_operation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer) +{ + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity, tl::to_string (tr ("Executing ")) + description (op)); + + local_processor_contexts contexts; + compute_contexts (contexts, op, subject_layer, intruder_layer); + compute_results (contexts, op, output_layer); +} + +template +void local_processor::push_results (db::Cell *cell, unsigned int output_layer, const std::unordered_set &result) const +{ + if (! result.empty ()) { + tl::MutexLocker locker (&cell->layout ()->lock ()); + cell->shapes (output_layer).insert (result.begin (), result.end ()); + } +} + +template +void local_processor::compute_contexts (local_processor_contexts &contexts, const local_operation *op, unsigned int subject_layer, unsigned int intruder_layer) const +{ + try { + + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 10, tl::to_string (tr ("Computing contexts for ")) + description (op)); + + if (m_nthreads > 0) { + mp_cc_job.reset (new tl::Job > (m_nthreads)); + } else { + mp_cc_job.reset (0); + } + + contexts.clear (); + contexts.set_intruder_layer (intruder_layer); + contexts.set_subject_layer (subject_layer); + + typename local_processor_cell_contexts::context_key_type intruders; + issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist ()); + + if (mp_cc_job.get ()) { + mp_cc_job->start (); + mp_cc_job->wait (); + } + + } catch (...) { + mp_cc_job.reset (0); + throw; + } +} + +template +void local_processor::issue_compute_contexts (local_processor_contexts &contexts, + db::local_processor_cell_context *parent_context, + db::Cell *subject_parent, + db::Cell *subject_cell, + const db::ICplxTrans &subject_cell_inst, + const db::Cell *intruder_cell, + typename local_processor_cell_contexts::context_key_type &intruders, + db::Coord dist) const +{ + bool is_small_job = subject_cell->begin ().at_end (); + + if (! is_small_job && mp_cc_job.get ()) { + mp_cc_job->schedule (new local_processor_context_computation_task (this, contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist)); + } else { + compute_contexts (contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist); + } +} + +template +void local_processor::compute_contexts (local_processor_contexts &contexts, + db::local_processor_cell_context *parent_context, + db::Cell *subject_parent, + db::Cell *subject_cell, + const db::ICplxTrans &subject_cell_inst, + const db::Cell *intruder_cell, + const typename local_processor_cell_contexts::context_key_type &intruders, + db::Coord dist) const +{ + CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts) + + if (tl::verbosity () >= m_base_verbosity + 20) { + if (! subject_parent) { + tl::log << tr ("Computing context for top cell ") << mp_subject_layout->cell_name (subject_cell->cell_index ()); + } else { + tl::log << tr ("Computing context for ") << mp_subject_layout->cell_name (subject_parent->cell_index ()) << " -> " << mp_subject_layout->cell_name (subject_cell->cell_index ()) << " @" << subject_cell_inst.to_string (); + } + } + + db::local_processor_cell_context *cell_context = 0; + + // prepare a new cell context: this has to happen in a thread-safe way as we share the contexts + // object between threads + + { + tl::MutexLocker locker (& contexts.lock ()); + + db::local_processor_cell_contexts &cell_contexts = contexts.contexts_per_cell (subject_cell, intruder_cell); + +#if defined(ENABLE_DB_HP_SANITY_ASSERTIONS) + if (subject_parent) { + typename db::local_processor_cell_contexts::contexts_per_cell_type::iterator pcc = contexts.context_map ().find (subject_parent); + if (pcc == contexts.context_map ().end ()) { + tl_assert (false); + } + tl_assert (pcc->first == subject_parent); + bool any = false; + for (typename db::local_processor_cell_contexts::iterator pcci = pcc->second.begin (); pcci != pcc->second.end () && !any; ++pcci) { + any = (&pcci->second == parent_context); + } + if (!any) { + tl_assert (false); + } + } + #endif + + cell_context = cell_contexts.find_context (intruders); + if (cell_context) { + // we already have a context for this intruder scheme + cell_context->add (parent_context, subject_parent, subject_cell_inst); + return; + } + + cell_context = cell_contexts.create (intruders); + cell_context->add (parent_context, subject_parent, subject_cell_inst); + } + + // perform the actual task .. + + CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts_unlocked) + const db::Shapes *intruder_shapes = 0; + if (intruder_cell) { + intruder_shapes = &intruder_cell->shapes (contexts.intruder_layer ()); + } + + db::box_convert inst_bcs (*mp_subject_layout, contexts.subject_layer ()); + db::box_convert inst_bci (*mp_intruder_layout, contexts.intruder_layer ()); + db::box_convert inst_bcii (*mp_intruder_layout, contexts.intruder_layer ()); + + // handle top-down interactions (subject instances interacting with intruder shapes) + // and sibling interactions + + if (! subject_cell->begin ().at_end ()) { + + typedef std::pair, std::unordered_set > interaction_value_type; + + std::unordered_map interactions; + + // insert dummy interactions to handle at least the child cell vs. itself + // - this is important so we will always handle the instances unless they are + // entirely empty in the subject layer + for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { + if (! inst_bcs (i->cell_inst ()).empty ()) { + interactions.insert (std::make_pair (&i->cell_inst (), interaction_value_type ())); + } + } + +// TODO: can we shortcut this if interactions is empty? + { + db::box_scanner2 scanner; + interaction_registration_inst2inst rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.intruder_layer (), dist, &interactions); + + unsigned int id = 0; + + if (subject_cell == intruder_cell) { + + // Use the same id's for same instances - this way we can easily detect same instances + // and don't make the self-interacting + + for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { + unsigned int iid = ++id; + if (! inst_bcs (i->cell_inst ()).empty ()) { + scanner.insert1 (&i->cell_inst (), iid); + } + if (! inst_bci (i->cell_inst ()).empty ()) { + scanner.insert2 (&i->cell_inst (), iid); + } + } + + } else { + + for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { + if (! inst_bcs (i->cell_inst ()).empty ()) { + scanner.insert1 (&i->cell_inst (), ++id); + } + } + + if (intruder_cell) { + for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) { + if (! inst_bci (i->cell_inst ()).empty ()) { + scanner.insert2 (&i->cell_inst (), ++id); + } + } + } + + } + + for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { + if (! inst_bci (*i).empty ()) { + scanner.insert2 (i.operator-> (), ++id); + } + } + + scanner.process (rec, dist, inst_bcs, inst_bci); + } + +// TODO: can we shortcut this if interactions is empty? + { + db::box_scanner2 scanner; + interaction_registration_inst2shape rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions); + + for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { + if (! inst_bcs (i->cell_inst ()).empty ()) { + scanner.insert1 (&i->cell_inst (), 0); + } + } + + for (typename std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + scanner.insert2 (i.operator-> (), 0); + } + + if (intruder_shapes) { + for (db::Shapes::shape_iterator i = intruder_shapes->begin (shape_flags ()); !i.at_end (); ++i) { + scanner.insert2 (i->basic_ptr (typename TI::tag ()), 0); + } + } + + scanner.process (rec, dist, inst_bcs, db::box_convert ()); + } + + for (typename std::unordered_map::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { + + db::Cell &subject_child_cell = mp_subject_layout->cell (i->first->object ().cell_index ()); + + for (db::CellInstArray::iterator n = i->first->begin (); ! n.at_end (); ++n) { + + db::ICplxTrans tn = i->first->complex_trans (*n); + db::ICplxTrans tni = tn.inverted (); + db::Box nbox = tn * subject_child_cell.bbox (contexts.subject_layer ()).enlarged (db::Vector (dist, dist)); + + if (! nbox.empty ()) { + + typename local_processor_cell_contexts::context_key_type intruders_below; + + db::shape_reference_translator_with_trans rt (mp_subject_layout, tni); + + for (typename std::unordered_set::const_iterator p = i->second.second.begin (); p != i->second.second.end (); ++p) { + if (nbox.overlaps (db::box_convert () (*p))) { + intruders_below.second.insert (rt (*p)); + } + } + + // TODO: in some cases, it may be possible to optimize this for arrays + + for (std::unordered_set::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) { + for (db::CellInstArray::iterator k = (*j)->begin_touching (safe_box_enlarged (nbox, -1, -1), inst_bcii); ! k.at_end (); ++k) { + db::ICplxTrans tk = (*j)->complex_trans (*k); + // NOTE: no self-interactions + if (i->first != *j || tn != tk) { + intruders_below.first.insert (db::CellInstArray (db::CellInst ((*j)->object ().cell_index ()), tni * tk)); + } + } + } + + db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? &subject_child_cell : 0); + issue_compute_contexts (contexts, cell_context, subject_cell, &subject_child_cell, tn, intruder_child_cell, intruders_below, dist); + + } + + } + + } + + } +} + +template +void +local_processor::compute_results (local_processor_contexts &contexts, const local_operation *op, unsigned int output_layer) const +{ + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 10, tl::to_string (tr ("Computing results for ")) + description (op)); + + // avoids updates while we work on the layout + mp_subject_layout->update (); + db::LayoutLocker layout_update_locker (mp_subject_layout); + + if (m_nthreads > 0) { + + std::auto_ptr > > rc_job (new tl::Job > (m_nthreads)); + + // schedule computation jobs in "waves": we need to make sure they are executed + // bottom-up. So we identify a new bunch of cells each time we pass through the cell set + // and proceed until all cells are removed. + + std::vector cells_bu; + cells_bu.reserve (mp_subject_layout->cells ()); + for (db::Layout::bottom_up_const_iterator bu = mp_subject_layout->begin_bottom_up (); bu != mp_subject_layout->end_bottom_up (); ++bu) { + cells_bu.push_back (*bu); + } + + int iter = 0; + while (true) { + + ++iter; + tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 10, tl::sprintf (tl::to_string (tr ("Computing results iteration #%d")), iter)); + + bool any = false; + std::unordered_set later; + + std::vector next_cells_bu; + next_cells_bu.reserve (cells_bu.size ()); + + for (std::vector::const_iterator bu = cells_bu.begin (); bu != cells_bu.end (); ++bu) { + + typename local_processor_contexts::iterator cpc = contexts.context_map ().find (&mp_subject_layout->cell (*bu)); + if (cpc != contexts.context_map ().end ()) { + + if (later.find (*bu) == later.end ()) { + + rc_job->schedule (new local_processor_result_computation_task (this, contexts, cpc->first, &cpc->second, op, output_layer)); + any = true; + + } else { + next_cells_bu.push_back (*bu); + } + + for (db::Cell::parent_cell_iterator pc = cpc->first->begin_parent_cells (); pc != cpc->first->end_parent_cells (); ++pc) { + later.insert (*pc); + } + + } + + } + + cells_bu.swap (next_cells_bu); + + if (! any) { + break; + } + + if (rc_job.get ()) { + rc_job->start (); + rc_job->wait (); + } + + } + + } else { + + for (db::Layout::bottom_up_const_iterator bu = mp_subject_layout->begin_bottom_up (); bu != mp_subject_layout->end_bottom_up (); ++bu) { + + typename local_processor_contexts::iterator cpc = contexts.context_map ().find (&mp_subject_layout->cell (*bu)); + if (cpc != contexts.context_map ().end ()) { + cpc->second.compute_results (contexts, cpc->first, op, output_layer, this); + contexts.context_map ().erase (cpc); + } + + } + + } +} + +template +struct scan_shape2shape_same_layer +{ + void + operator () (const db::Shapes *subject_shapes, unsigned int subject_id0, const std::set &intruders, shape_interactions &interactions, db::Coord dist) const + { + db::box_scanner2 scanner; + interaction_registration_shape1 rec (&interactions); + + unsigned int id = subject_id0; + for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags ()); !i.at_end (); ++i) { + const TS *ref = i->basic_ptr (typename TS::tag ()); + scanner.insert1 (ref, id++); + } + + // TODO: can we confine this search to the subject's (sized) bounding box? + for (typename std::set::const_iterator i = intruders.begin (); i != intruders.end (); ++i) { + scanner.insert2 (i.operator-> (), interactions.next_id ()); + } + + scanner.process (rec, dist, db::box_convert (), db::box_convert ()); + } +}; + +template +struct scan_shape2shape_same_layer +{ + void + operator () (const db::Shapes *subject_shapes, unsigned int subject_id0, const std::set &intruders, shape_interactions &interactions, db::Coord dist) const + { + db::box_scanner scanner; + interaction_registration_shape1 rec (&interactions); + + unsigned int id = subject_id0; + for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags ()); !i.at_end (); ++i) { + const T *ref = i->basic_ptr (typename T::tag ()); + scanner.insert (ref, id++); + } + + // TODO: can we confine this search to the subject's (sized) bounding box? + for (typename std::set::const_iterator i = intruders.begin (); i != intruders.end (); ++i) { + scanner.insert (i.operator-> (), interactions.next_id ()); + } + + scanner.process (rec, dist, db::box_convert ()); + } +}; + +template +struct scan_shape2shape_different_layers +{ + void + operator () (db::Layout *layout, const db::Shapes *subject_shapes, const db::Shapes *intruder_shapes, unsigned int subject_id0, const std::set &intruders, shape_interactions &interactions, db::Coord dist) + { + db::box_scanner2 scanner; + interaction_registration_shape2shape rec (layout, &interactions); + + unsigned int id = subject_id0; + for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags ()); !i.at_end (); ++i) { + const TS *ref = i->basic_ptr (typename TS::tag ()); + scanner.insert1 (ref, id++); + } + + // TODO: can we confine this search to the subject's (sized) bounding box? + for (typename std::set::const_iterator i = intruders.begin (); i != intruders.end (); ++i) { + scanner.insert2 (i.operator-> (), interactions.next_id ()); + } + + if (intruder_shapes) { + // TODO: can we confine this search to the subject's (sized) bounding box? + for (db::Shapes::shape_iterator i = intruder_shapes->begin (shape_flags ()); !i.at_end (); ++i) { + scanner.insert2 (i->basic_ptr (typename TI::tag ()), interactions.next_id ()); + } + } + + scanner.process (rec, dist, db::box_convert (), db::box_convert ()); + } +}; + +template +void +local_processor::compute_local_cell (const db::local_processor_contexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation *op, const typename local_processor_cell_contexts::context_key_type &intruders, std::unordered_set &result) const +{ + const db::Shapes *subject_shapes = &subject_cell->shapes (contexts.subject_layer ()); + + const db::Shapes *intruder_shapes = 0; + if (intruder_cell) { + intruder_shapes = &intruder_cell->shapes (contexts.intruder_layer ()); + if (intruder_shapes->empty ()) { + intruder_shapes = 0; + } + } + + // local shapes vs. child cell + + shape_interactions interactions; + db::box_convert inst_bci (*mp_intruder_layout, contexts.intruder_layer ()); + + // insert dummy interactions to accommodate subject vs. nothing and assign an ID + // range for the subject shapes. + unsigned int subject_id0 = 0; + for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags ()); !i.at_end (); ++i) { + + unsigned int id = interactions.next_id (); + if (subject_id0 == 0) { + subject_id0 = id; + } + + if (op->on_empty_intruder_hint () != local_operation::Drop) { + const TS *ref = i->basic_ptr (typename TS::tag ()); + interactions.add_subject (id, *ref); + } + + } + + if (! subject_shapes->empty () && (intruder_shapes || ! intruders.second.empty ())) { + + if (subject_cell == intruder_cell && contexts.subject_layer () == contexts.intruder_layer ()) { + + scan_shape2shape_same_layer () (subject_shapes, subject_id0, intruders.second, interactions, op->dist ()); + + } else { + + db::Layout *target_layout = (mp_subject_layout == mp_intruder_layout ? 0 : mp_subject_layout); + scan_shape2shape_different_layers () (target_layout, subject_shapes, intruder_shapes, subject_id0, intruders.second, interactions, op->dist ()); + + } + + } + + if (! subject_shapes->empty () && ! ((! intruder_cell || intruder_cell->begin ().at_end ()) && intruders.first.empty ())) { + + db::box_scanner2 scanner; + interaction_registration_shape2inst rec (mp_subject_layout, mp_intruder_layout, contexts.intruder_layer (), op->dist (), &interactions); + + unsigned int id = subject_id0; + for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags ()); !i.at_end (); ++i) { + scanner.insert1 (i->basic_ptr (typename TS::tag ()), id++); + } + + unsigned int inst_id = 0; + + if (subject_cell == intruder_cell && contexts.subject_layer () == contexts.intruder_layer ()) { + + // Same cell, same layer -> no shape to child instance interactions because this will be taken care of + // by the instances themselves (and their intruders). This also means, we prefer to deal with + // interactions low in the hierarchy. + + } else if (intruder_cell) { +// TODO: can we confine this search to the subject's (sized) bounding box? + for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) { + if (! inst_bci (i->cell_inst ()).empty ()) { + scanner.insert2 (&i->cell_inst (), ++inst_id); + } + } + } + +// TODO: can we confine this search to the subject's (sized) bounding box? + for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { + if (! inst_bci (*i).empty ()) { + scanner.insert2 (i.operator-> (), ++inst_id); + } + } + + scanner.process (rec, op->dist (), db::box_convert (), inst_bci); + + } + + if (interactions.begin () != interactions.end ()) { + + if (interactions.begin_intruders () == interactions.end_intruders ()) { + + typename local_operation::on_empty_intruder_mode eh = op->on_empty_intruder_hint (); + if (eh == local_operation::Drop) { + return; + } + + } + + op->compute_local (mp_subject_layout, interactions, result, m_max_vertex_count, m_area_ratio); + + } +} + +template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; +template class DB_PUBLIC local_processor; + +} + diff --git a/src/db/db/dbHierProcessor.h b/src/db/db/dbHierProcessor.h new file mode 100644 index 000000000..1326cffa9 --- /dev/null +++ b/src/db/db/dbHierProcessor.h @@ -0,0 +1,455 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + + +#ifndef HDR_dbHierProcessor +#define HDR_dbHierProcessor + +#include "dbCommon.h" + +#include "dbLayout.h" +#include "dbLocalOperation.h" +#include "tlThreadedWorkers.h" + +#include +#include +#include +#include +#include + +#include "dbHash.h" + +namespace db +{ + +template class local_processor; +template class local_processor_cell_context; +template class local_processor_contexts; + +// TODO: move this somewhere else? +template +class DB_PUBLIC shape_interactions +{ +public: + typedef std::unordered_map > container; + typedef container::const_iterator iterator; + typedef container::value_type::second_type::const_iterator iterator2; + typedef typename std::unordered_map::const_iterator subject_iterator; + typedef typename std::unordered_map::const_iterator intruder_iterator; + + shape_interactions (); + + iterator begin () const + { + return m_interactions.begin (); + } + + iterator end () const + { + return m_interactions.end (); + } + + subject_iterator begin_subjects () const + { + return m_subject_shapes.begin (); + } + + subject_iterator end_subjects () const + { + return m_subject_shapes.end (); + } + + intruder_iterator begin_intruders () const + { + return m_intruder_shapes.begin (); + } + + intruder_iterator end_intruders () const + { + return m_intruder_shapes.end (); + } + + bool has_intruder_shape_id (unsigned int id) const; + bool has_subject_shape_id (unsigned int id) const; + void add_intruder_shape (unsigned int id, const TI &shape); + void add_subject_shape (unsigned int id, const TS &shape); + void add_subject (unsigned int id, const TS &shape); + void add_interaction (unsigned int subject_id, unsigned int intruder_id); + const std::vector &intruders_for (unsigned int subject_id) const; + const TS &subject_shape (unsigned int id) const; + const TI &intruder_shape (unsigned int id) const; + + unsigned int next_id () + { + return ++m_id; + } + +private: + std::unordered_map > m_interactions; + std::unordered_map m_subject_shapes; + std::unordered_map m_intruder_shapes; + unsigned int m_id; +}; + +// TODO: should be hidden (private data?) +template +struct DB_PUBLIC local_processor_cell_drop +{ + local_processor_cell_drop (db::local_processor_cell_context *_parent_context, db::Cell *_parent, const db::ICplxTrans &_cell_inst) + : parent_context (_parent_context), parent (_parent), cell_inst (_cell_inst) + { + // .. nothing yet .. + } + + db::local_processor_cell_context *parent_context; + db::Cell *parent; + db::ICplxTrans cell_inst; +}; + +// TODO: should be hidden (private data?) +template +class DB_PUBLIC local_processor_cell_context +{ +public: + typedef std::pair parent_inst_type; + typedef typename std::vector >::const_iterator drop_iterator; + + local_processor_cell_context (); + local_processor_cell_context (const local_processor_cell_context &other); + + void add (db::local_processor_cell_context *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst); + void propagate (const std::unordered_set &res); + + std::unordered_set &propagated () + { + return m_propagated; + } + + const std::unordered_set &propagated () const + { + return m_propagated; + } + + size_t size () const + { + return m_drops.size (); + } + + tl::Mutex &lock () + { + return m_lock; + } + + // used for debugging purposes only + drop_iterator begin_drops () const + { + return m_drops.begin (); + } + + // used for debugging purposes only + drop_iterator end_drops () const + { + return m_drops.end (); + } + +private: + std::unordered_set m_propagated; + std::vector > m_drops; + tl::Mutex m_lock; +}; + +template +class DB_PUBLIC local_processor_cell_contexts +{ +public: + typedef std::pair, std::set > context_key_type; + typedef std::unordered_map > context_map_type; + typedef typename context_map_type::const_iterator iterator; + + local_processor_cell_contexts (); + local_processor_cell_contexts (const db::Cell *intruder_cell); + + db::local_processor_cell_context *find_context (const context_key_type &intruders); + db::local_processor_cell_context *create (const context_key_type &intruders); + void compute_results (const local_processor_contexts &contexts, db::Cell *cell, const local_operation *op, unsigned int output_layer, const local_processor *proc); + + iterator begin () const + { + return m_contexts.begin (); + } + + iterator end () const + { + return m_contexts.end (); + } + +private: + const db::Cell *mp_intruder_cell; + std::unordered_map > m_contexts; +}; + +template +class DB_PUBLIC local_processor_contexts +{ +public: + typedef std::unordered_map > contexts_per_cell_type; + typedef typename contexts_per_cell_type::iterator iterator; + + local_processor_contexts () + : m_subject_layer (0), m_intruder_layer (0) + { + // .. nothing yet .. + } + + local_processor_contexts (const local_processor_contexts &other) + : m_contexts_per_cell (other.m_contexts_per_cell), m_subject_layer (other.m_subject_layer), m_intruder_layer (other.m_intruder_layer) + { + // .. nothing yet .. + } + + void clear () + { + m_contexts_per_cell.clear (); + } + + local_processor_cell_contexts &contexts_per_cell (db::Cell *subject_cell, const db::Cell *intruder_cell) + { + typename contexts_per_cell_type::iterator ctx = m_contexts_per_cell.find (subject_cell); + if (ctx == m_contexts_per_cell.end ()) { + ctx = m_contexts_per_cell.insert (std::make_pair (subject_cell, local_processor_cell_contexts (intruder_cell))).first; + } + return ctx->second; + } + + contexts_per_cell_type &context_map () + { + return m_contexts_per_cell; + } + + iterator begin () + { + return m_contexts_per_cell.begin (); + } + + iterator end () + { + return m_contexts_per_cell.end (); + } + + void set_subject_layer (unsigned int l) + { + m_subject_layer = l; + } + + unsigned int subject_layer () const + { + return m_subject_layer; + } + + void set_intruder_layer (unsigned int l) + { + m_intruder_layer = l; + } + + unsigned int intruder_layer () const + { + return m_intruder_layer; + } + + tl::Mutex &lock () const + { + return m_lock; + } + +private: + contexts_per_cell_type m_contexts_per_cell; + unsigned int m_subject_layer, m_intruder_layer; + mutable tl::Mutex m_lock; +}; + +template +class DB_PUBLIC local_processor_context_computation_task + : public tl::Task +{ +public: + local_processor_context_computation_task (const local_processor *proc, local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist); + void perform (); + +private: + const local_processor *mp_proc; + local_processor_contexts *mp_contexts; + db::local_processor_cell_context *mp_parent_context; + db::Cell *mp_subject_parent; + db::Cell *mp_subject_cell; + db::ICplxTrans m_subject_cell_inst; + const db::Cell *mp_intruder_cell; + typename local_processor_cell_contexts::context_key_type m_intruders; + db::Coord m_dist; +}; + +template +class DB_PUBLIC local_processor_context_computation_worker + : public tl::Worker +{ +public: + local_processor_context_computation_worker () + : tl::Worker () + { + // .. nothing yet .. + } + + void perform_task (tl::Task *task) + { + static_cast *> (task)->perform (); + } +}; + +template +class DB_PUBLIC local_processor_result_computation_task + : public tl::Task +{ +public: + local_processor_result_computation_task (const local_processor *proc, local_processor_contexts &contexts, db::Cell *cell, local_processor_cell_contexts *cell_contexts, const local_operation *op, unsigned int output_layer); + void perform (); + +private: + const local_processor *mp_proc; + local_processor_contexts *mp_contexts; + db::Cell *mp_cell; + local_processor_cell_contexts *mp_cell_contexts; + const local_operation *mp_op; + unsigned int m_output_layer; +}; + +template +class DB_PUBLIC local_processor_result_computation_worker + : public tl::Worker +{ +public: + local_processor_result_computation_worker () + : tl::Worker () + { + // .. nothing yet .. + } + + void perform_task (tl::Task *task) + { + static_cast *> (task)->perform (); + } +}; + +template +class DB_PUBLIC local_processor +{ +public: + local_processor (db::Layout *layout, db::Cell *top); + local_processor (db::Layout *subject_layout, db::Cell *subject_top, const db::Layout *intruder_layout, const db::Cell *intruder_cell); + void run (local_operation *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer); + void compute_contexts (local_processor_contexts &contexts, const local_operation *op, unsigned int subject_layer, unsigned int intruder_layer) const; + void compute_results (local_processor_contexts &contexts, const local_operation *op, unsigned int output_layer) const; + + void set_description (const std::string &d) + { + m_description = d; + } + + void set_base_verbosity (int vb) + { + m_base_verbosity = vb; + } + + int base_verbosity () const + { + return m_base_verbosity; + } + + void set_threads (unsigned int nthreads) + { + m_nthreads = nthreads; + } + + unsigned int threads () const + { + return m_nthreads; + } + + void set_max_vertex_count (size_t max_vertex_count) + { + m_max_vertex_count = max_vertex_count; + } + + size_t max_vertex_count () const + { + return m_max_vertex_count; + } + + void set_area_ratio (double area_ratio) + { + m_area_ratio = area_ratio; + } + + double area_ratio () const + { + return m_area_ratio; + } + +private: + template friend class local_processor_cell_contexts; + template friend class local_processor_context_computation_task; + + db::Layout *mp_subject_layout; + const db::Layout *mp_intruder_layout; + db::Cell *mp_subject_top; + const db::Cell *mp_intruder_top; + std::string m_description; + unsigned int m_nthreads; + size_t m_max_vertex_count; + double m_area_ratio; + int m_base_verbosity; + mutable std::auto_ptr > > mp_cc_job; + + std::string description (const local_operation *op) const; + void compute_contexts (db::local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist) const; + void do_compute_contexts (db::local_processor_cell_context *cell_context, const db::local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist) const; + void issue_compute_contexts (db::local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist) const; + void push_results (db::Cell *cell, unsigned int output_layer, const std::unordered_set &result) const; + void compute_local_cell (const db::local_processor_contexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation *op, const typename local_processor_cell_contexts::context_key_type &intruders, std::unordered_set &result) const; +}; + +} + +namespace tl +{ + +template +struct type_traits > : public tl::type_traits +{ + // mark "LocalProcessor" as not having a default ctor and no copy ctor + typedef tl::false_tag has_default_constructor; + typedef tl::false_tag has_copy_constructor; +}; + +} + +#endif + diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc new file mode 100644 index 000000000..a9e239d88 --- /dev/null +++ b/src/db/db/dbHierarchyBuilder.cc @@ -0,0 +1,663 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbRecursiveShapeIterator.h" +#include "dbHierarchyBuilder.h" +#include "dbClip.h" +#include "dbRegion.h" +#include "dbPolygonTools.h" + +namespace db +{ + +static HierarchyBuilderShapeInserter def_inserter; + +// ------------------------------------------------------------------------------------------- + +int +compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2) +{ + if ((iter1.layout () == 0) != (iter2.layout () == 0)) { + return (iter1.layout () == 0) < (iter2.layout () == 0); + } + if ((iter1.top_cell () == 0) != (iter2.top_cell () == 0)) { + return (iter1.top_cell () == 0) < (iter2.top_cell () == 0); + } + + // basic source (layout, top_cell) needs to be the same of course + if (iter1.layout () != iter2.layout ()) { + // NOTE: pointer compare :-( + return iter1.layout () < iter2.layout () ? -1 : 1; + } + if (iter1.top_cell ()) { + if (iter1.top_cell ()->cell_index () != iter2.top_cell ()->cell_index ()) { + return iter1.top_cell ()->cell_index () < iter2.top_cell ()->cell_index () ? -1 : 1; + } + } + + // max depth controls the main hierarchical appearance + if (iter1.max_depth () != iter2.max_depth ()) { + return iter1.max_depth () < iter2.max_depth () ? -1 : 1; + } + + // if a region is set, the hierarchical appearance is the same only if the layers and + // complex region are identical + if ((iter1.region () == db::Box::world ()) != (iter2.region () == db::Box::world ())) { + return (iter1.region () == db::Box::world ()) < (iter2.region () == db::Box::world ()) ? -1 : 1; + } + + if (iter1.region () != db::Box::world ()) { + if (iter1.has_complex_region () != iter2.has_complex_region ()) { + return iter1.has_complex_region () < iter2.has_complex_region () ? -1 : 1; + } + if (iter1.has_complex_region () && iter1.complex_region () != iter2.complex_region ()) { + return iter1.complex_region () < iter2.complex_region () ? -1 : 1; + } + if (iter1.region () != iter2.region ()) { + return iter1.region () < iter2.region () ? -1 : 1; + } + if (iter1.multiple_layers () != iter2.multiple_layers ()) { + return iter1.multiple_layers () < iter2.multiple_layers () ? -1 : 1; + } + if (iter1.multiple_layers ()) { + if (iter1.layers () != iter2.layers ()) { + return iter1.layers () < iter2.layers () ? -1 : 1; + } + } else { + if (iter1.layer () != iter2.layer ()) { + return iter1.layer () < iter2.layer () ? -1 : 1; + } + } + } + + return 0; +} + +// ------------------------------------------------------------------------------------------- + +/** + * @brief Computes the clip variant (a box set) from a cell bbox, a region and a complex region (optional) + */ +static std::pair > compute_clip_variant (const db::Box &cell_bbox, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region) +{ + if (region == db::Box::world ()) { + return std::make_pair (true, std::set ()); + } + + db::ICplxTrans trans_inv (trans.inverted ()); + db::Box region_in_cell = region.transformed (trans_inv); + + std::set clip_variant; + if (! cell_bbox.overlaps (region_in_cell)) { + // an empty clip variant should not happen, but who knows + return std::make_pair (false, std::set ()); + } + + db::Box rect_box = region_in_cell & cell_bbox; + + if (complex_region) { + + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (region, db::box_convert ()); ! cr.at_end (); ++cr) { + db::Box cr_in_cell = (*cr).transformed (trans_inv); + if (rect_box.overlaps (cr_in_cell)) { + clip_variant.insert (rect_box * cr_in_cell); + } + } + + if (clip_variant.empty ()) { + // an empty clip variant should not happen, but who knows + return std::make_pair (false, std::set ()); + } + + } else { + clip_variant.insert (rect_box); + } + + return std::make_pair (true, clip_variant); +} + +HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe) + : mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (target_layer), m_trans (trans) +{ + set_shape_receiver (pipe); +} + +HierarchyBuilder::HierarchyBuilder (db::Layout *target, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe) + : mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (0), m_trans (trans) +{ + set_shape_receiver (pipe); +} + +HierarchyBuilder::~HierarchyBuilder () +{ + // .. nothing yet .. +} + +void +HierarchyBuilder::set_shape_receiver (HierarchyBuilderShapeReceiver *pipe) +{ + mp_pipe = pipe ? pipe : &def_inserter; +} + +void +HierarchyBuilder::reset () +{ + m_initial_pass = true; + mp_initial_cell = 0; + + m_cells_to_be_filled.clear (); + m_cell_map.clear (); + m_cells_seen.clear (); + m_cell_stack.clear (); + m_cm_entry = cell_map_type::const_iterator (); + m_cm_new_entry = false; +} + +void +HierarchyBuilder::register_variant (db::cell_index_type non_var, db::cell_index_type var) +{ + // non_var (despite it's name) may be a variant created previously. + variant_to_original_target_map_type::const_iterator v = m_variants_to_original_target_map.find (non_var); + if (v != m_variants_to_original_target_map.end ()) { + non_var = v->second; + } + + m_original_targets_to_variants_map [non_var].push_back (var); + m_variants_to_original_target_map.insert (std::make_pair (var, non_var)); +} + +void +HierarchyBuilder::begin (const RecursiveShapeIterator *iter) +{ + if (m_initial_pass) { + m_source = *iter; + } else { + tl_assert (compare_iterators_with_respect_to_target_hierarchy (m_source, *iter) == 0); + } + + m_cell_stack.clear (); + m_cells_seen.clear (); + + if (! iter->layout () || ! iter->top_cell ()) { + return; + } + + std::pair > key (iter->top_cell ()->cell_index (), std::set ()); + m_cm_entry = m_cell_map.find (key); + + if (m_cm_entry == m_cell_map.end ()) { + db::cell_index_type new_top_index = mp_target->add_cell (iter->layout ()->cell_name (key.first)); + m_cm_entry = m_cell_map.insert (std::make_pair (key, new_top_index)).first; + } + + db::Cell &new_top = mp_target->cell (m_cm_entry->second); + m_cells_seen.insert (key); + + // NOTE: we consider the top cell "new" if it does not have instances. + // We can do so as the recursive shape iterator will always deliver all instances + // and not a partial set of instances. + m_cm_new_entry = new_top.begin ().at_end (); + m_cell_stack.push_back (std::make_pair (m_cm_new_entry, std::vector ())); + m_cell_stack.back ().second.push_back (&new_top); +} + +void +HierarchyBuilder::end (const RecursiveShapeIterator *iter) +{ + tl_assert (! iter->layout () || ! iter->top_cell () || m_cell_stack.size () == 1); + + m_initial_pass = false; + m_cells_seen.clear (); + mp_initial_cell = m_cell_stack.empty () ? 0 : m_cell_stack.front ().second.front (); + m_cell_stack.clear (); + m_cm_entry = cell_map_type::const_iterator (); + m_cm_new_entry = false; +} + +void +HierarchyBuilder::enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) +{ + tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != cell_map_type::const_iterator ()); + + m_cells_seen.insert (m_cm_entry->first); + + bool new_cell = (m_cells_to_be_filled.find (m_cm_entry->second) != m_cells_to_be_filled.end ()); + if (new_cell) { + m_cells_to_be_filled.erase (m_cm_entry->second); + } + + m_cell_stack.push_back (std::make_pair (new_cell, std::vector ())); + + original_target_to_variants_map_type::const_iterator v = m_original_targets_to_variants_map.find (m_cm_entry->second); + if (v != m_original_targets_to_variants_map.end ()) { + for (std::vector::const_iterator i = v->second.begin (); i != v->second.end (); ++i) { + m_cell_stack.back ().second.push_back (&mp_target->cell (*i)); + } + } else { + m_cell_stack.back ().second.push_back (&mp_target->cell (m_cm_entry->second)); + } +} + +void +HierarchyBuilder::leave_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/) +{ + m_cell_stack.pop_back (); +} + +HierarchyBuilder::new_inst_mode +HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) +{ + if (all) { + + std::pair > key (inst.object ().cell_index (), std::set ()); + m_cm_entry = m_cell_map.find (key); + m_cm_new_entry = false; + + if (m_cm_entry == m_cell_map.end ()) { + db::cell_index_type new_cell = mp_target->add_cell (iter->layout ()->cell_name (inst.object ().cell_index ())); + m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; + m_cm_new_entry = true; + m_cells_to_be_filled.insert (new_cell); + } + + // for new cells, create this instance + if (m_cell_stack.back ().first) { + db::CellInstArray new_inst (inst, &mp_target->array_repository ()); + new_inst.object () = db::CellInst (m_cm_entry->second); + new_inst.transform_into (m_trans); + for (std::vector::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) { + (*c)->insert (new_inst); + } + } + + // To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array. + return (m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip; + + } else { + + // Iterate by instance array members + return NI_all; + + } +} + +bool +HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) +{ + if (all) { + + return true; + + } else { + + db::Box cell_bbox = iter->layout ()->cell (inst.object ().cell_index ()).bbox (); + std::pair > clip_variant = compute_clip_variant (cell_bbox, trans, region, complex_region); + if (! clip_variant.first) { + return false; + } + + std::pair > key (inst.object ().cell_index (), clip_variant.second); + m_cm_entry = m_cell_map.find (key); + m_cm_new_entry = false; + + if (m_cm_entry == m_cell_map.end ()) { + std::string suffix; + if (! key.second.empty ()) { + suffix = "$CLIP_VAR"; + } + db::cell_index_type new_cell = mp_target->add_cell ((std::string (iter->layout ()->cell_name (inst.object ().cell_index ())) + suffix).c_str ()); + m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; + m_cm_new_entry = true; + m_cells_to_be_filled.insert (new_cell); + } + + // for a new cell, create this instance + if (m_cell_stack.back ().first) { + db::CellInstArray new_inst (db::CellInst (m_cm_entry->second), trans); + new_inst.transform_into (m_trans); + for (std::vector::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) { + (*c)->insert (new_inst); + } + } + + return (m_cells_seen.find (key) == m_cells_seen.end ()); + + } +} + +void +HierarchyBuilder::shape (const RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*trans*/, const db::Box ®ion, const box_tree_type *complex_region) +{ + for (std::vector::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) { + db::Shapes &shapes = (*c)->shapes (m_target_layer); + mp_pipe->push (shape, m_trans, region, complex_region, &shapes); + } +} + +// --------------------------------------------------------------------------------------------- + +ClippingHierarchyBuilderShapeReceiver::ClippingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe) + : mp_pipe (pipe ? pipe : &def_inserter) +{ + // .. nothing yet .. +} + +void +ClippingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + static db::Box world = db::Box::world (); + + if (region == world || is_inside (shape.bbox (), region, complex_region)) { + + mp_pipe->push (shape, trans, world, 0, target); + + } else if (! is_outside (shape.bbox (), region, complex_region)) { + + // clip the shape if required + if (shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) { + mp_pipe->push (shape, trans, world, 0, target); + } else if (shape.is_box ()) { + insert_clipped (shape.box (), trans, region, complex_region, target); + } else if (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { + db::Polygon poly; + shape.polygon (poly); + insert_clipped (poly, trans, region, complex_region, target); + } + + } +} + +void +ClippingHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + static db::Box world = db::Box::world (); + + if (! complex_region) { + db::Box r = shape & region; + if (! r.empty()) { + mp_pipe->push (r, trans, world, 0, target); + } + } else { + insert_clipped (shape, trans, region, complex_region, target); + } +} + +void +ClippingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + static db::Box world = db::Box::world (); + + if (region == world || (shape.box ().inside (region) && ! complex_region)) { + mp_pipe->push (shape, trans, world, 0, target); + } else { + insert_clipped (shape, trans, region, complex_region, target); + } +} + +bool +ClippingHierarchyBuilderShapeReceiver::is_inside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region) +{ + if (region == db::Box::world ()) { + return true; + } + + if (box.inside (region)) { + + db::Box rect_box = region & box; + + if (complex_region) { + + // TODO: this is not a real test for being inside a complex region + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (rect_box, db::box_convert ()); ! cr.at_end (); ++cr) { + if (rect_box.inside (*cr)) { + return true; + } + } + + } + + } + + return false; +} + +bool +ClippingHierarchyBuilderShapeReceiver::is_outside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region) +{ + if (region == db::Box::world ()) { + return false; + } + + if (box.overlaps (region)) { + + db::Box rect_box = region & box; + + if (complex_region) { + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (rect_box, db::box_convert ()); ! cr.at_end (); ++cr) { + if (rect_box.overlaps (*cr)) { + return false; + } + } + } else { + return false; + } + + } + + return true; +} + +void +ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Box &box, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + db::Box bb = box & region; + static db::Box world = db::Box::world (); + + if (complex_region) { + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (bb, db::box_convert ()); ! cr.at_end (); ++cr) { + mp_pipe->push (*cr & bb, trans, world, 0, target); + } + } else { + mp_pipe->push (bb, trans, world, 0, target); + } +} + +void +ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + std::vector clipped_poly; + static db::Box world = db::Box::world (); + + if (complex_region) { + // TODO: is this good way to clip a polygon at a complex boundary? + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (region, db::box_convert ()); ! cr.at_end (); ++cr) { + db::clip_poly (poly, *cr & region, clipped_poly); + } + } else { + db::clip_poly (poly, region, clipped_poly); + } + + for (std::vector::const_iterator p = clipped_poly.begin (); p != clipped_poly.end (); ++p) { + mp_pipe->push (*p, trans, world, 0, target); + } +} + +// --------------------------------------------------------------------------------------------- + +ReducingHierarchyBuilderShapeReceiver::ReducingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe, double area_ratio, size_t max_vertex_count) + : mp_pipe (pipe ? pipe : &def_inserter), m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count) +{ + // .. nothing yet .. +} + +void +ReducingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + if (shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) { + mp_pipe->push (shape, trans, region, complex_region, target); + } else if (shape.is_box ()) { + mp_pipe->push (shape.box (), trans, region, complex_region, target); + } else if (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { + db::Polygon poly; + shape.polygon (poly); + reduce (poly, trans, region, complex_region, target); + } +} + +void +ReducingHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + mp_pipe->push (shape, trans, region, complex_region, target); +} + +void +ReducingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + reduce (shape, trans, region, complex_region, target); +} + +void +ReducingHierarchyBuilderShapeReceiver::reduce (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + if (poly.vertices () > m_max_vertex_count || poly.area_ratio () > m_area_ratio) { + + std::vector split_polygons; + db::split_polygon (poly, split_polygons); + for (std::vector ::const_iterator sp = split_polygons.begin (); sp != split_polygons.end (); ++sp) { + reduce (*sp, trans, region, complex_region, target); + } + + } else { + mp_pipe->push (poly, trans, region, complex_region, target); + } +} + +// --------------------------------------------------------------------------------------------- + +PolygonReferenceHierarchyBuilderShapeReceiver::PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, int text_enlargement, const tl::Variant &text_prop_name) + : mp_layout (layout), m_text_enlargement (text_enlargement), m_make_text_prop (false), m_text_prop_id (0) +{ + if (! text_prop_name.is_nil ()) { + m_text_prop_id = layout->properties_repository ().prop_name_id (text_prop_name); + m_make_text_prop = true; + } +} + +void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +{ + if (shape.is_box () || shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { + + db::Polygon poly; + shape.polygon (poly); + if (! trans.is_unity ()) { + poly.transform (trans); + } + target->insert (db::PolygonRef (poly, mp_layout->shape_repository ())); + + } else if (shape.is_text () && m_text_enlargement >= 0) { + + db::Polygon poly (shape.text_trans () * db::Box (-m_text_enlargement, -m_text_enlargement, m_text_enlargement, m_text_enlargement)); + if (! trans.is_unity ()) { + poly.transform (trans); + } + db::PolygonRef pref (poly, mp_layout->shape_repository ()); + + if (m_make_text_prop) { + + db::PropertiesRepository::properties_set ps; + ps.insert (std::make_pair (m_text_prop_id, tl::Variant (shape.text_string ()))); + db::properties_id_type pid = mp_layout->properties_repository ().properties_id (ps); + + target->insert (db::PolygonRefWithProperties (pref, pid)); + + } else { + target->insert (pref); + } + + } +} + +void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +{ + target->insert (db::PolygonRef (db::Polygon (shape.transformed (trans)), mp_layout->shape_repository ())); +} + +void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +{ + target->insert (db::PolygonRef (shape.transformed (trans), mp_layout->shape_repository ())); +} + +// --------------------------------------------------------------------------------------------- + +EdgeBuildingHierarchyBuilderShapeReceiver::EdgeBuildingHierarchyBuilderShapeReceiver (bool as_edges) + : m_as_edges (as_edges) +{ + // .. nothing yet .. +} + +void EdgeBuildingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + if (m_as_edges && (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ())) { + db::Polygon poly; + shape.polygon (poly); + push (poly, trans, region, complex_region, target); + } else if (m_as_edges && shape.is_box ()) { + push (shape.box (), trans, region, complex_region, target); + } else if (shape.is_edge ()) { + target->insert (shape.edge ()); + } +} + +void EdgeBuildingHierarchyBuilderShapeReceiver::push (const db::Box &box, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +{ + if (m_as_edges && ! box.empty ()) { + target->insert (db::Edge (box.p1 (), box.upper_left ()).transformed (trans)); + target->insert (db::Edge (box.upper_left (), box.p2 ()).transformed (trans)); + target->insert (db::Edge (box.p2 (), box.lower_right ()).transformed (trans)); + target->insert (db::Edge (box.lower_right (), box.p1 ()).transformed (trans)); + } +} + +void EdgeBuildingHierarchyBuilderShapeReceiver::push (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +{ + if (m_as_edges) { + for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { + target->insert ((*e).transformed (trans)); + } + } +} + +// --------------------------------------------------------------------------------------------- + +EdgePairBuildingHierarchyBuilderShapeReceiver::EdgePairBuildingHierarchyBuilderShapeReceiver () +{ + // .. nothing yet .. +} + +void EdgePairBuildingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box & /*region*/, const db::RecursiveShapeReceiver::box_tree_type * /*complex_region*/, db::Shapes *target) +{ + if (shape.is_edge_pair ()) { + target->insert (shape.edge_pair ().transformed (trans)); + } +} + +} diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h new file mode 100644 index 000000000..a42e251bc --- /dev/null +++ b/src/db/db/dbHierarchyBuilder.h @@ -0,0 +1,325 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbHierarchyBuilder +#define HDR_dbHierarchyBuilder + +#include "dbCommon.h" + +#include "dbRecursiveShapeIterator.h" +#include "dbLayout.h" + +#include +#include +#include + +namespace db +{ + +/** + * @brief A helper function comparing two recursive shape iterators for compatibility with respect to hierarchy building + * + * This function will return -1, 0 or 1 depending on whether the two iterators + * can be used with the same builder (0) or whether they are less (-1) or greater (1). + */ +int DB_PUBLIC compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2); + +/** + * @brief A class to receive shapes from the hierarchy builder + * + * This class can be reimplemented to implement clipping and/or + * simplification. + */ +class DB_PUBLIC HierarchyBuilderShapeReceiver +{ +public: + HierarchyBuilderShapeReceiver () { } + virtual ~HierarchyBuilderShapeReceiver () { } + + virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; + virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; + virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; +}; + +/** + * @brief A shape receiver that simply pushes into the target + */ +class DB_PUBLIC HierarchyBuilderShapeInserter + : public HierarchyBuilderShapeReceiver +{ +public: + HierarchyBuilderShapeInserter () { } + + virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) + { + tl::ident_map pm; + target->insert (shape, trans, pm); + } + + virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) + { + if (trans.is_ortho ()) { + target->insert (shape.transformed (trans)); + } else { + db::Polygon poly (shape); + target->insert (poly.transformed (trans)); + } + } + + virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) + { + if (trans.is_unity ()) { + target->insert (shape); + } else { + target->insert (shape.transformed (trans)); + } + } +}; + +/** + * @brief A clipping shape receiver that forwards to another one + */ +class DB_PUBLIC ClippingHierarchyBuilderShapeReceiver + : public HierarchyBuilderShapeReceiver +{ +public: + ClippingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe = 0); + + virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + +private: + void insert_clipped (const db::Box &box, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target); + void insert_clipped (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target); + static bool is_inside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region); + static bool is_outside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region); + + HierarchyBuilderShapeReceiver *mp_pipe; +}; + +/** + * @brief A polygon reducing shape receiver that forwards to another one + */ +class DB_PUBLIC ReducingHierarchyBuilderShapeReceiver + : public HierarchyBuilderShapeReceiver +{ +public: + ReducingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe = 0, double area_ratio = 3.0, size_t max_vertex_count = 16); + + virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + +private: + void reduce (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target); + + HierarchyBuilderShapeReceiver *mp_pipe; + double m_area_ratio; + size_t m_max_vertex_count; +}; + +/** + * @brief A polygon reference generating shape receiver that feeds a shapes array after turning the shapes into PolygonRefs + */ +class DB_PUBLIC PolygonReferenceHierarchyBuilderShapeReceiver + : public HierarchyBuilderShapeReceiver +{ +public: + PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, int text_enlargement = -1, const tl::Variant &text_prop_name = tl::Variant ()); + + virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + +private: + db::Layout *mp_layout; + int m_text_enlargement; + bool m_make_text_prop; + db::property_names_id_type m_text_prop_id; +}; + +/** + * @brief An edge-generating shape receiver that feeds a shapes array after turning the shapes into edges + */ +class DB_PUBLIC EdgeBuildingHierarchyBuilderShapeReceiver + : public HierarchyBuilderShapeReceiver +{ +public: + EdgeBuildingHierarchyBuilderShapeReceiver (bool as_edges); + + virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + +private: + bool m_as_edges; +}; + +/** + * @brief An edge pair-generating shape receiver that feeds a shapes array after turning the shapes into edges + */ +class DB_PUBLIC EdgePairBuildingHierarchyBuilderShapeReceiver + : public HierarchyBuilderShapeReceiver +{ +public: + EdgePairBuildingHierarchyBuilderShapeReceiver (); + + virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } + virtual void push (const db::Polygon &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } +}; + +/** + * @brief A class building a hierarchy from a recursive shape iterator in push mode + * + * This class is a RecursiveShapeReceiver which acts on the hierarchy events and + * uses them to rebuild a hierarchy in the target layout. In can be used multiple + * times and will reuse the hierarchy as far as possible. + * + * The hierarchy builder can form clip variants for cells and clip the shapes + * according to the selected region. + * + * NOTE: the hierarchy build should not be used in multiple passes with regions + * as the hierarchy is sampled in the first pass and the hierarchy builder will + * rely on precisely the same hierarchy arrangement. This is not given with + * region selections. + */ +class DB_PUBLIC HierarchyBuilder + : public db::RecursiveShapeReceiver +{ +public: + + typedef std::map >, db::cell_index_type> cell_map_type; + typedef std::map > original_target_to_variants_map_type; + typedef std::map variant_to_original_target_map_type; + + HierarchyBuilder (db::Layout *target, unsigned int target_layer, const db::ICplxTrans &trans = db::ICplxTrans (), HierarchyBuilderShapeReceiver *pipe = 0); + HierarchyBuilder (db::Layout *target, const db::ICplxTrans &trans = db::ICplxTrans (), HierarchyBuilderShapeReceiver *pipe = 0); + virtual ~HierarchyBuilder (); + + /** + * @brief Installs a custom shape receiver + * The hierarchy builder will *NOT* take ownership of this object. + */ + void set_shape_receiver (HierarchyBuilderShapeReceiver *pipe); + + virtual void begin (const RecursiveShapeIterator *iter); + virtual void end (const RecursiveShapeIterator *iter); + virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region); + virtual void leave_cell (const RecursiveShapeIterator *iter, const db::Cell *cell); + virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all); + virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all); + virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region); + + /** + * @brief Sets the target layer - shapes will be put there + */ + void set_target_layer (unsigned int target_layer) + { + m_target_layer = target_layer; + } + + /** + * @brief Reset the builder - performs a new initial pass + */ + void reset (); + + /** + * @brief Gets the initial cell the builder produced in the target layout + */ + db::Cell *initial_cell () + { + return mp_initial_cell; + } + + /** + * @brief Gets the target layout + */ + db::Layout *target () + { + return mp_target.get (); + } + + /** + * @brief Gets the recursive shape iterator the data was taken from + */ + const db::RecursiveShapeIterator &source () const + { + return m_source; + } + + /** + * @brief Gets the iterator for the cell map + */ + cell_map_type::const_iterator begin_cell_map () const + { + return m_cell_map.begin (); + } + + /** + * @brief Gets the iterator for the cell map (end) + */ + cell_map_type::const_iterator end_cell_map () const + { + return m_cell_map.end (); + } + + /** + * @brief Marks a cell as a variant of another + * + * The first cell is either the original, non-variant target cell or itself a variant. + * The second cell will be registered as a variant of the first one. + */ + void register_variant (db::cell_index_type non_var, db::cell_index_type var); + + /** + * @brief Gets a value indicating whether the given cell is a variant cell + */ + bool is_variant (db::cell_index_type ci) const + { + return m_variants_to_original_target_map.find (ci) != m_variants_to_original_target_map.end (); + } + +private: + tl::weak_ptr mp_target; + HierarchyBuilderShapeReceiver *mp_pipe; + bool m_initial_pass; + db::RecursiveShapeIterator m_source; + cell_map_type m_cell_map; + original_target_to_variants_map_type m_original_targets_to_variants_map; + variant_to_original_target_map_type m_variants_to_original_target_map; + + std::set m_cells_seen; + std::set m_cells_to_be_filled; + cell_map_type::const_iterator m_cm_entry; + bool m_cm_new_entry; + unsigned int m_target_layer; + std::vector > > m_cell_stack; + db::Cell *mp_initial_cell; + + db::ICplxTrans m_trans; +}; + +} + +#endif diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 67c2a4289..8cb3d62b8 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -31,6 +31,7 @@ #include "dbLibraryProxy.h" #include "dbLibraryManager.h" #include "dbLibrary.h" +#include "dbRegion.h" #include "tlTimer.h" #include "tlLog.h" #include "tlInternational.h" @@ -41,6 +42,8 @@ namespace db { +static const int layout_base_verbosity = 30; + // ----------------------------------------------------------------- // The undo/redo operations @@ -638,7 +641,25 @@ Layout::delete_cell (cell_index_type id) } } -void +void +Layout::insert (db::cell_index_type cell, int layer, const db::Region ®ion) +{ + region.insert_into (this, cell, layer); +} + +void +Layout::insert (db::cell_index_type cell, int layer, const db::Edges &edges) +{ + edges.insert_into (this, cell, layer); +} + +void +Layout::insert (db::cell_index_type cell, int layer, const db::EdgePairs &edge_pairs) +{ + edge_pairs.insert_into (this, cell, layer); +} + +void Layout::flatten (const db::Cell &source_cell, db::Cell &target_cell, const db::ICplxTrans &t, int levels) { db::ICplxTrans tt = t; @@ -1271,7 +1292,7 @@ Layout::update () const void Layout::do_update () { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Sorting"))); + tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity, tl::to_string (tr ("Sorting"))); // establish a progress report since this operation can take some time. // HINT: because of some gcc bug, automatic destruction of the tl::Progress @@ -1286,12 +1307,12 @@ Layout::do_update () // the hierarchy management informations if (hier_dirty ()) { { - tl::SelfTimer timer (tl::verbosity () >= 31, "Updating relations"); + tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Updating relations"); pr->set_desc (tl::to_string (tr ("Updating relations"))); update_relations (); } { - tl::SelfTimer timer (tl::verbosity () >= 31, "Topological sort"); + tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Topological sort"); pr->set_desc (tl::to_string (tr ("Topological sorting"))); tl_assert (topological_sort ()); } @@ -1309,7 +1330,7 @@ Layout::do_update () if (bboxes_dirty ()) { { - tl::SelfTimer timer (tl::verbosity () >= 31, "Updating bounding boxes"); + tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Updating bounding boxes"); unsigned int layers = 0; pr->set (0); pr->set_desc (tl::to_string (tr ("Updating bounding boxes"))); @@ -1331,7 +1352,7 @@ Layout::do_update () } { - tl::SelfTimer timer (tl::verbosity () >= 31, "Sorting shapes"); + tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Sorting shapes"); pr->set (0); pr->set_desc (tl::to_string (tr ("Sorting shapes"))); for (bottom_up_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) { @@ -1344,7 +1365,7 @@ Layout::do_update () // sort the instance trees now, since we have computed the bboxes if (hier_dirty () || ! dirty_parents.empty ()) { - tl::SelfTimer timer (tl::verbosity () >= 31, "Sorting instances"); + tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Sorting instances"); size_t layers = 0; pr->set (0); pr->set_desc (tl::to_string (tr ("Sorting instances"))); @@ -1511,6 +1532,24 @@ Layout::insert_layer (unsigned int index, const LayerProperties &props) layer_properties_changed (); } +unsigned int +Layout::get_layer (const db::LayerProperties &lp) +{ + if (lp.is_null ()) { + // for a null layer info always create a layer + return insert_layer (); + } else { + // if we have a layer with the requested properties already, return this. + for (db::Layout::layer_iterator li = begin_layers (); li != end_layers (); ++li) { + if ((*li).second->log_equal (lp)) { + return (*li).first; + } + } + // otherwise create a new layer + return insert_layer (lp); + } +} + unsigned int Layout::waste_layer () const { diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 94e5cd184..78a45c63f 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -38,6 +38,9 @@ #include "tlException.h" #include "tlVector.h" #include "tlString.h" +#include "tlThreads.h" +#include "tlObject.h" +#include "tlUniqueId.h" #include "gsi.h" #include @@ -59,6 +62,9 @@ class Library; class LibraryProxy; class CellMapping; class LayerMapping; +class Region; +class Edges; +class EdgePairs; template class generic_repository; typedef generic_repository GenericRepository; @@ -456,7 +462,9 @@ public: class DB_PUBLIC Layout : public db::Object, public db::LayoutStateModel, - public gsi::ObjectBase + public gsi::ObjectBase, + public tl::Object, + public tl::UniqueId { public: typedef db::Box box_type; @@ -572,6 +580,15 @@ public: return m_properties_repository; } + /** + * @brief Gets the lock for the layout object + * This is a generic lock that can be used to lock modifications against multiple threads. + */ + tl::Mutex &lock () + { + return m_lock; + } + /** * @brief Collect memory statistics */ @@ -1078,6 +1095,33 @@ public: */ void flatten (db::Cell &cell, int levels, bool prune = false); + /** + * @brief Inserts a region (potentially hierarchical) into the given cell and layer + * + * If the region is flat (conceptionally), it will be put into the cell. + * If the region is hierarchical, a cell hierarchy will be built below the + * given cell. + */ + void insert (db::cell_index_type cell, int layer, const db::Region ®ion); + + /** + * @brief Inserts a edge collection (potentially hierarchical) into the given cell and layer + * + * If the edge collection is flat (conceptionally), it will be put into the cell. + * If the edge collection is hierarchical, a cell hierarchy will be built below the + * given cell. + */ + void insert (db::cell_index_type cell, int layer, const db::Edges &edges); + + /** + * @brief Inserts a edge pair collection (potentially hierarchical) into the given cell and layer + * + * If the edge pair collection is flat (conceptionally), it will be put into the cell. + * If the edge pair collection is hierarchical, a cell hierarchy will be built below the + * given cell. + */ + void insert (db::cell_index_type cell, int layer, const db::EdgePairs &edge_pairs); + /** * @brief Delete a cell plus all subcells * @@ -1435,6 +1479,14 @@ public: */ void insert_layer (unsigned int index, const LayerProperties &props = LayerProperties ()); + /** + * @brief Gets or creates a layer with the given properties + * + * If there already is a layer matching the given properties, it's index will be + * returned. Otherwise a new layer with these properties is created. + */ + unsigned int get_layer (const db::LayerProperties &props); + /** * @brief Insert a new special layer with the given properties * @@ -1630,6 +1682,7 @@ private: int m_waste_layer; bool m_editable; meta_info m_meta_info; + tl::Mutex m_lock; /** * @brief Sort the cells topologically diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc new file mode 100644 index 000000000..a0be5cfcd --- /dev/null +++ b/src/db/db/dbLayoutToNetlist.cc @@ -0,0 +1,942 @@ + + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbCommon.h" +#include "dbLayoutToNetlist.h" +#include "dbDeepRegion.h" +#include "dbShapeRepository.h" +#include "dbCellMapping.h" + +namespace db +{ + +// the iterator provides the hierarchical selection (enabling/disabling cells etc.) + +LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter) + : m_iter (iter), m_layout_index (0), m_netlist_extracted (false), m_is_flat (false) +{ + // check the iterator + if (iter.has_complex_region () || iter.region () != db::Box::world ()) { + throw tl::Exception (tl::to_string (tr ("The netlist extractor cannot work on clipped layouts"))); + } + + mp_internal_dss.reset (new db::DeepShapeStore ()); + mp_dss.reset (mp_internal_dss.get ()); + + // the dummy layer acts as a reference holder for the layout + // NOTE: this probably can be done better + db::RecursiveShapeIterator empty_iter = iter; + empty_iter.set_layers (std::vector ()); + m_dummy_layer = dss ().create_polygon_layer (empty_iter); + + init (); +} + +LayoutToNetlist::LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_index) + : mp_dss (dss), m_layout_index (layout_index), m_netlist_extracted (false), m_is_flat (false) +{ + if (dss->is_valid_layout_index (m_layout_index)) { + m_iter = db::RecursiveShapeIterator (dss->layout (m_layout_index), dss->initial_cell (m_layout_index), std::set ()); + } +} + +LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu) + : m_iter (), m_netlist_extracted (false), m_is_flat (true) +{ + mp_internal_dss.reset (new db::DeepShapeStore (topcell_name, dbu)); + mp_dss.reset (mp_internal_dss.get ()); + m_layout_index = 0 ; + + init (); +} + +LayoutToNetlist::LayoutToNetlist () + : m_iter (), mp_internal_dss (new db::DeepShapeStore ()), mp_dss (mp_internal_dss.get ()), m_layout_index (0), + m_netlist_extracted (false), m_is_flat (false) +{ + init (); +} + +LayoutToNetlist::~LayoutToNetlist () +{ + // NOTE: do this in this order because of unregistration of the layers + m_named_regions.clear (); + m_dlrefs.clear (); + mp_internal_dss.reset (0); + mp_netlist.reset (0); + m_net_clusters.clear (); +} + +void LayoutToNetlist::init () +{ + dss ().set_text_enlargement (1); + dss ().set_text_property_name (tl::Variant ("LABEL")); +} + +void LayoutToNetlist::set_threads (int n) +{ + dss ().set_threads (n); +} + +int LayoutToNetlist::threads () const +{ + return dss ().threads (); +} + +void LayoutToNetlist::set_area_ratio (double ar) +{ + dss ().set_max_area_ratio (ar); +} + +double LayoutToNetlist::area_ratio () const +{ + return dss ().max_area_ratio (); +} + +void LayoutToNetlist::set_max_vertex_count (size_t n) +{ + dss ().set_max_vertex_count (n); +} + +size_t LayoutToNetlist::max_vertex_count () const +{ + return dss ().max_vertex_count (); +} + +db::Region *LayoutToNetlist::make_layer (const std::string &n) +{ + db::RecursiveShapeIterator si (m_iter); + si.shape_flags (db::ShapeIterator::Nothing); + + std::auto_ptr region (new db::Region (si, dss ())); + if (! n.empty ()) { + register_layer (*region, n); + } + return region.release (); +} + +db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::string &n) +{ + db::RecursiveShapeIterator si (m_iter); + si.set_layer (layer_index); + si.shape_flags (db::ShapeIterator::All); + + std::auto_ptr region (new db::Region (si, dss ())); + if (! n.empty ()) { + register_layer (*region, n); + } + return region.release (); +} + +db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index, const std::string &n) +{ + db::RecursiveShapeIterator si (m_iter); + si.set_layer (layer_index); + si.shape_flags (db::ShapeIterator::Texts); + + std::auto_ptr region (new db::Region (si, dss ())); + register_layer (*region, n); + return region.release (); +} + +db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const std::string &n) +{ + db::RecursiveShapeIterator si (m_iter); + si.set_layer (layer_index); + si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes); + + std::auto_ptr region (new db::Region (si, dss ())); + register_layer (*region, n); + return region.release (); +} + +void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers) +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + if (! mp_netlist.get ()) { + mp_netlist.reset (new db::Netlist ()); + } + extractor.extract (dss (), m_layout_index, layers, *mp_netlist, m_net_clusters); +} + +void LayoutToNetlist::connect (const db::Region &l) +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + + if (! is_persisted (l)) { + throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in intra-layer connectivity for netlist extraction")))); + } + + // we need to keep a reference, so we can safely delete the region + db::DeepLayer dl = deep_layer_of (l); + m_dlrefs.insert (dl); + + m_conn.connect (dl.layer ()); +} + +void LayoutToNetlist::connect (const db::Region &a, const db::Region &b) +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + if (! is_persisted (a)) { + throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in inter-layer connectivity (first layer) for netlist extraction")))); + } + if (! is_persisted (b)) { + throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in inter-layer connectivity (second layer) for netlist extraction")))); + } + + // we need to keep a reference, so we can safely delete the region + db::DeepLayer dla = deep_layer_of (a); + db::DeepLayer dlb = deep_layer_of (b); + m_dlrefs.insert (dla); + m_dlrefs.insert (dlb); + + m_conn.connect (dla.layer (), dlb.layer ()); +} + +size_t LayoutToNetlist::connect_global (const db::Region &l, const std::string &gn) +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + if (! is_persisted (l)) { + throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in global connectivity for netlist extraction")))); + } + + // we need to keep a reference, so we can safely delete the region + db::DeepLayer dl = deep_layer_of (l); + m_dlrefs.insert (dl); + + return m_conn.connect_global (dl.layer (), gn); +} + +const std::string &LayoutToNetlist::global_net_name (size_t id) const +{ + return m_conn.global_net_name (id); +} + +size_t LayoutToNetlist::global_net_id (const std::string &name) +{ + return m_conn.global_net_id (name); +} + +void LayoutToNetlist::extract_netlist (bool join_nets_by_label) +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + if (! mp_netlist.get ()) { + mp_netlist.reset (new db::Netlist ()); + } + + db::NetlistExtractor netex; + netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters, join_nets_by_label); + + m_netlist_extracted = true; +} + +void LayoutToNetlist::set_netlist_extracted () +{ + m_netlist_extracted = true; +} + +const db::Layout *LayoutToNetlist::internal_layout () const +{ + ensure_layout (); + return &dss ().const_layout (m_layout_index); +} + +const db::Cell *LayoutToNetlist::internal_top_cell () const +{ + ensure_layout (); + return &dss ().const_initial_cell (m_layout_index); +} + +db::Layout *LayoutToNetlist::internal_layout () +{ + ensure_layout (); + return &dss ().layout (m_layout_index); +} + +db::Cell *LayoutToNetlist::internal_top_cell () +{ + ensure_layout (); + return &dss ().initial_cell (m_layout_index); +} + +void LayoutToNetlist::ensure_layout () const +{ + if (! dss ().is_valid_layout_index (m_layout_index)) { + + LayoutToNetlist *non_const_this = const_cast (this); + non_const_this->dss ().make_layout (m_layout_index, db::RecursiveShapeIterator ()); + + // the dummy layer acts as a reference holder for the layout + unsigned int dummy_layer_index = non_const_this->dss ().layout (m_layout_index).insert_layer (); + non_const_this->m_dummy_layer = db::DeepLayer (& non_const_this->dss (), m_layout_index, dummy_layer_index); + + } +} + +void LayoutToNetlist::register_layer (const db::Region ®ion, const std::string &n) +{ + if (m_named_regions.find (n) != m_named_regions.end ()) { + throw tl::Exception (tl::to_string (tr ("Layer name is already used: ")) + n); + } + + db::DeepLayer dl; + + if (m_is_flat) { + + dl = dss ().create_from_flat (region, true); + + } else { + + db::DeepRegion *delegate = dynamic_cast (region.delegate()); + if (! delegate) { + + if (region.empty ()) { + dl = dss ().empty_layer (m_layout_index); + } else { + throw tl::Exception (tl::to_string (tr ("Layer is not a deep region and cannot be registered with name: ")) + n); + } + + } else { + + if (is_persisted (region)) { + std::string prev_name = name (region); + m_named_regions.erase (prev_name); + } + + dl = delegate->deep_layer (); + + } + + } + + m_named_regions [n] = dl; + m_name_of_layer [dl.layer ()] = n; +} + +std::string LayoutToNetlist::name (const db::Region ®ion) const +{ + std::map::const_iterator n = m_name_of_layer.find (layer_of (region)); + if (n != m_name_of_layer.end ()) { + return n->second; + } else { + return std::string (); + } +} + +std::string LayoutToNetlist::name (unsigned int l) const +{ + std::map::const_iterator n = m_name_of_layer.find (l); + if (n != m_name_of_layer.end ()) { + return n->second; + } else { + return std::string (); + } +} + +bool LayoutToNetlist::is_persisted (const db::Region ®ion) const +{ + return m_name_of_layer.find (layer_of (region)) != m_name_of_layer.end (); +} + +db::Region *LayoutToNetlist::layer_by_name (const std::string &name) +{ + std::map::const_iterator l = m_named_regions.find (name); + if (l == m_named_regions.end ()) { + return 0; + } else { + return new db::Region (new db::DeepRegion (l->second)); + } +} + +db::Region *LayoutToNetlist::layer_by_index (unsigned int index) +{ + std::map::const_iterator n = m_name_of_layer.find (index); + if (n == m_name_of_layer.end ()) { + return 0; + } else { + return layer_by_name (n->second); + } +} + +db::DeepLayer LayoutToNetlist::deep_layer_of (const db::Region ®ion) const +{ + const db::DeepRegion *dr = dynamic_cast (region.delegate ()); + if (! dr) { + + std::pair lff = dss ().layer_for_flat (region); + if (lff.first) { + return lff.second; + } else if (region.empty ()) { + // provide a substitute empty layer for empty + return dss ().empty_layer (m_layout_index); + } else { + throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in netlist extraction")))); + } + + } else { + return dr->deep_layer (); + } +} + +unsigned int LayoutToNetlist::layer_of (const db::Region ®ion) const +{ + return deep_layer_of (region).layer (); +} + +db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell &cell, bool with_device_cells) +{ + std::set device_cells; + if (! with_device_cells && mp_netlist.get ()) { + for (db::Netlist::device_abstract_iterator i = mp_netlist->begin_device_abstracts (); i != mp_netlist->end_device_abstracts (); ++i) { + device_cells.insert (i->cell_index ()); + } + } + + return dss ().cell_mapping_to_original (m_layout_index, &layout, cell.cell_index (), &device_cells); +} + +db::CellMapping LayoutToNetlist::const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell) +{ + db::CellMapping cm; + if (layout.cells () == 1) { + cm.create_single_mapping (layout, cell.cell_index (), *internal_layout(), internal_top_cell()->cell_index ()); + } else { + cm.create_from_geometry (layout, cell.cell_index (), *internal_layout(), internal_top_cell()->cell_index ()); + } + return cm; +} + +db::Netlist *LayoutToNetlist::netlist () const +{ + return mp_netlist.get (); +} + +db::Netlist *LayoutToNetlist::make_netlist () +{ + if (! mp_netlist.get ()) { + mp_netlist.reset (new db::Netlist ()); + } + return mp_netlist.get (); +} + +namespace +{ + struct StopOnFirst { }; +} + +template +static bool deliver_shape (const db::PolygonRef &, StopOnFirst, const Tr &) +{ + return false; +} + +template +static bool deliver_shape (const db::PolygonRef &pr, db::Region ®ion, const Tr &tr) +{ + if (pr.obj ().is_box ()) { + region.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr)); + } else { + region.insert (pr.obj ().transformed (pr.trans ()).transformed (tr)); + } + return true; +} + +template +static bool deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const Tr &tr) +{ + if (pr.obj ().is_box ()) { + shapes.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr)); + } else { + db::Layout *layout = shapes.layout (); + if (layout) { + shapes.insert (db::PolygonRef (pr.obj ().transformed (pr.trans ()).transformed (tr), layout->shape_repository ())); + } else { + shapes.insert (pr.obj ().transformed (pr.trans ()).transformed (tr)); + } + } + return true; +} + +template +static bool deliver_shapes_of_net_recursive (const db::Netlist * /*nl*/, const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to) +{ + // deliver the net shapes + for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) { + if (! deliver_shape (*rci, to, tr * rci.trans ())) { + return false; + } + } + return true; +} + +template +static bool deliver_shapes_of_net_nonrecursive (const db::Netlist *nl, const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to) +{ + // NOTE: this scheme will deliver the shapes from the cell, including (!) + // subcells that are purged + + db::cell_index_type prev_ci = ci; + + // deliver the net shapes + for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ) { + + db::cell_index_type cci = rci.cell_index (); + if (cci != prev_ci && cci != ci && (! nl || nl->circuit_by_cell_index (cci) || nl->device_abstract_by_cell_index (cci))) { + + rci.skip_cell (); + + } else { + + if (! deliver_shape (*rci, to, tr * rci.trans ())) { + return false; + } + prev_ci = cci; + + ++rci; + + } + + } + + return true; +} + +void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to) const +{ + unsigned int lid = layer_of (of_layer); + const db::Circuit *circuit = net.circuit (); + tl_assert (circuit != 0); + + if (! recursive) { + deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), to); + } else { + deliver_shapes_of_net_recursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), to); + } +} + +db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const +{ + unsigned int lid = layer_of (of_layer); + const db::Circuit *circuit = net.circuit (); + tl_assert (circuit != 0); + + std::auto_ptr res (new db::Region ()); + + if (! recursive) { + deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), *res); + } else { + deliver_shapes_of_net_recursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), *res); + } + + return res.release (); +} + +void +LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const db::ICplxTrans &tr) const +{ + const db::Circuit *circuit = net.circuit (); + tl_assert (circuit != 0); + + build_net_rec (circuit->cell_index (), net.cluster_id (), target, target_cell, lmap, &net, net_cell_name_prefix, cell_name_prefix, device_cell_name_prefix, cmap, tr); +} + +void +LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &tc, const std::map &lmap, const db::Net *net, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const db::ICplxTrans &tr) const +{ + db::Cell *target_cell = &tc; + + if (net_cell_name_prefix) { + + const db::connected_clusters &ccl = m_net_clusters.clusters_per_cell (ci); + + bool any_connections = circuit_cell_name_prefix && ! ccl.connections_for_cluster (cid).empty (); + if (! any_connections) { + + bool consider_cell = any_connections; + for (std::map::const_iterator l = lmap.begin (); l != lmap.end () && !consider_cell; ++l) { + if (l->second) { + StopOnFirst sof; + consider_cell = !deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, sof); + } + } + + if (! consider_cell) { + // shortcut if cell is empty -> no net cell will be produced + return; + } + + } + + // make a specific cell for the net if requested + + target_cell = &target.cell (target.add_cell ((std::string (net_cell_name_prefix) + net->expanded_name ()).c_str ())); + tc.insert (db::CellInstArray (db::CellInst (target_cell->cell_index ()), db::Trans ())); + + } + + for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { + if (l->second) { + deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, target_cell->shapes (l->first)); + } + } + + if (! circuit_cell_name_prefix && ! device_cell_name_prefix) { + return; + } + + // NOTE: we propagate the magnification part of tr down, but keep the rotation/translation part in the instance + // (we want to avoid magnified instances) + db::ICplxTrans tr_wo_mag = tr * db::ICplxTrans (1.0 / tr.mag ()); + db::ICplxTrans tr_mag (tr.mag ()); + + const db::connected_clusters &clusters = m_net_clusters.clusters_per_cell (ci); + typedef db::connected_clusters::connections_type connections_type; + const connections_type &connections = clusters.connections_for_cluster (cid); + for (connections_type::const_iterator c = connections.begin (); c != connections.end (); ++c) { + + db::cell_index_type subci = c->inst_cell_index (); + size_t subcid = c->id (); + + std::map, db::cell_index_type>::const_iterator cm = cmap.find (std::make_pair (subci, subcid)); + if (cm == cmap.end ()) { + + const char *name_prefix = 0; + if (mp_netlist->device_abstract_by_cell_index (subci)) { + name_prefix = device_cell_name_prefix; + } else { + name_prefix = circuit_cell_name_prefix; + } + + if (name_prefix) { + + std::string cell_name = internal_layout ()->cell_name (subci); + + db::cell_index_type target_ci = target.add_cell ((std::string (name_prefix) + cell_name).c_str ()); + cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), target_ci)).first; + + build_net_rec (subci, subcid, target, target.cell (target_ci), lmap, 0, 0, circuit_cell_name_prefix, device_cell_name_prefix, cmap, tr_mag); + + } else { + cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), std::numeric_limits::max ())).first; + } + + } + + if (cm->second != std::numeric_limits::max ()) { + db::CellInstArray ci (db::CellInst (cm->second), tr_wo_mag * c->inst_trans ()); + ci.transform_into (tr_mag); + target_cell->insert (ci); + } + + } +} + +void +LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix) const +{ + if (! m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + + std::map, db::cell_index_type> cell_map; + + double mag = internal_layout ()->dbu () / target.dbu (); + build_net_rec (net, target, target_cell, lmap, 0, cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans (mag)); +} + +void +LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const +{ + if (! m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + + std::map, db::cell_index_type> cell_map; + double mag = internal_layout ()->dbu () / target.dbu (); + + const db::Netlist *netlist = mp_netlist.get (); + for (db::Netlist::const_circuit_iterator c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) { + + if (! cmap.has_mapping (c->cell_index ())) { + continue; + } + + bool is_top_circuit = c->begin_parents () == c->end_parents (); + + db::cell_index_type target_ci = cmap.cell_mapping (c->cell_index ()); + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + + // exlude local nets in recursive mode + if (circuit_cell_name_prefix && ! is_top_circuit && n->pin_count () > 0) { + continue; + } + + build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans (mag)); + + } + + if (circuit_cell_name_prefix) { + + // with recursive nets we skip nets in subcircuits which are connected upwards. This means, nets will + // get lost if there is no connection to this pin from the outside. Hence we need to deliver nets from + // subcircuits as part of the circuit which calls the subcircuit - but NOT in a subcircuit cell, because + // this will just apply to nets from certain instances. But the net cell name will be formed as "subcircuit:net" + + const db::Circuit &circuit = *c; + for (db::Circuit::const_subcircuit_iterator sc = circuit.begin_subcircuits (); sc != circuit.end_subcircuits (); ++sc) { + + const db::SubCircuit &subcircuit = *sc; + for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) { + + if (! subcircuit.net_for_pin (p->id ())) { + + const db::Net *n = subcircuit.circuit_ref ()->net_for_pin (p->id ()); + if (n) { + + double dbu = target.dbu (); + db::ICplxTrans tr = db::ICplxTrans (mag) * (db::CplxTrans (dbu).inverted () * subcircuit.trans () * db::CplxTrans (dbu)); + + if (net_cell_name_prefix) { + std::string ncn = std::string (net_cell_name_prefix) + subcircuit.expanded_name () + ":"; + build_net_rec (*n, target, target.cell (target_ci), lmap, ncn.c_str (), circuit_cell_name_prefix, device_cell_name_prefix, cell_map, tr); + } else { + build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, tr); + } + + } + + } + + } + + } + + } + + } +} + +db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point) +{ + return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point); +} + +size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, std::vector &rev_inst_path) +{ + db::Box local_box = trans * test_cluster.bbox (); + + const db::local_clusters &lcc = net_clusters ().clusters_per_cell (cell->cell_index ()); + for (db::local_clusters::touching_iterator i = lcc.begin_touching (local_box); ! i.at_end (); ++i) { + const db::local_cluster &lc = *i; + if (lc.interacts (test_cluster, trans, m_conn)) { + return lc.id (); + } + } + + for (db::Cell::touching_iterator i = cell->begin_touching (local_box); ! i.at_end (); ++i) { + + for (db::CellInstArray::iterator ia = i->begin_touching (local_box, internal_layout ()); ! ia.at_end (); ++ia) { + + db::ICplxTrans trans_inst = i->complex_trans (*ia); + db::ICplxTrans t = trans_inst.inverted () * trans; + size_t cluster_id = search_net (t, &internal_layout ()->cell (i->cell_index ()), test_cluster, rev_inst_path); + if (cluster_id > 0) { + rev_inst_path.push_back (db::InstElement (*i, ia)); + return cluster_id; + } + + } + + } + + return 0; +} + +db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Point &point) +{ + if (! m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + tl_assert (mp_netlist.get ()); + + db::CplxTrans dbu_trans (internal_layout ()->dbu ()); + db::VCplxTrans dbu_trans_inv = dbu_trans.inverted (); + + unsigned int layer = layer_of (of_region); + + // Prepare a test cluster + db::Box box (point - db::Vector (1, 1), point + db::Vector (1, 1)); + db::GenericRepository sr; + db::local_cluster test_cluster; + test_cluster.add (db::PolygonRef (db::Polygon (box), sr), layer); + + std::vector inst_path; + + size_t cluster_id = search_net (db::ICplxTrans (), internal_top_cell (), test_cluster, inst_path); + if (cluster_id > 0) { + + // search_net delivers the path in reverse order + std::reverse (inst_path.begin (), inst_path.end ()); + + std::vector cell_indexes; + cell_indexes.reserve (inst_path.size () + 1); + cell_indexes.push_back (internal_top_cell ()->cell_index ()); + for (std::vector::const_iterator i = inst_path.begin (); i != inst_path.end (); ++i) { + cell_indexes.push_back (i->inst_ptr.cell_index ()); + } + + db::Circuit *circuit = mp_netlist->circuit_by_cell_index (cell_indexes.back ()); + if (! circuit) { + // the circuit has probably been optimized away + return 0; + } + + db::Net *net = circuit->net_by_cluster_id (cluster_id); + if (! net) { + // the net has probably been optimized away + return 0; + } + + // follow the path up in the net hierarchy using the transformation and the upper cell index as the + // guide line + while (! inst_path.empty () && net->pin_count () > 0) { + + cell_indexes.pop_back (); + + const db::Pin *pin = circuit->pin_by_id (net->begin_pins ()->pin_id ()); + tl_assert (pin != 0); + + db::DCplxTrans dtrans = dbu_trans * inst_path.back ().complex_trans () * dbu_trans_inv; + + // try to find a parent circuit which connects to this net + db::Circuit *upper_circuit = 0; + db::Net *upper_net = 0; + for (db::Circuit::refs_iterator r = circuit->begin_refs (); r != circuit->end_refs () && ! upper_net; ++r) { + if (r->trans ().equal (dtrans) && r->circuit () && r->circuit ()->cell_index () == cell_indexes.back ()) { + upper_net = r->net_for_pin (pin->id ()); + upper_circuit = r->circuit (); + } + } + + if (upper_net) { + circuit = upper_circuit; + net = upper_net; + inst_path.pop_back (); + } else { + break; + } + + } + + return net; + + } else { + return 0; + } +} + +db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector > &diodes) +{ + // TODO: that's basically too much .. we only need the clusters + if (! m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + + db::Layout &ly = dss ().layout (m_layout_index); + double dbu = ly.dbu (); + + db::DeepLayer dl (&dss (), m_layout_index, ly.insert_layer ()); + + for (db::Layout::bottom_up_const_iterator cid = ly.begin_bottom_up (); cid != ly.end_bottom_up (); ++cid) { + + const connected_clusters &clusters = m_net_clusters.clusters_per_cell (*cid); + if (clusters.empty ()) { + continue; + } + + for (connected_clusters::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { + + if (! clusters.is_root (*c)) { + continue; + } + + db::Region rgate, rmetal; + + deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (gate), db::ICplxTrans (), rgate); + deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal); + + double agate = rgate.area () * dbu * dbu; + double ametal = rmetal.area () * dbu * dbu; + + double r = ratio; + bool skip = false; + + for (std::vector >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) { + + db::Region rdiode; + deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode); + + if (fabs (d->second) < db::epsilon) { + if (rdiode.area () > 0) { + skip = true; + } + } else { + r += rdiode.area () * dbu * dbu * d->second; + } + + } + + if (! skip) { + + if (tl::verbosity () >= 50) { + tl::info << "cell [" << ly.cell_name (*cid) << "]: agate=" << tl::to_string (agate) << ", ametal=" << tl::to_string (ametal) << ", r=" << tl::sprintf ("%.12g", r); + } + + if (agate > dbu * dbu && ametal / agate > r + db::epsilon) { + db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ()); + for (db::Region::const_iterator r = rmetal.begin_merged (); ! r.at_end (); ++r) { + shapes.insert (*r); + } + } + + } + + } + + } + + return db::Region (new db::DeepRegion (dl)); +} + +} diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h new file mode 100644 index 000000000..9bca6b617 --- /dev/null +++ b/src/db/db/dbLayoutToNetlist.h @@ -0,0 +1,575 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbLayout2Netlist +#define _HDR_dbLayout2Netlist + +#include "dbCommon.h" +#include "dbCellMapping.h" +#include "dbNetlistExtractor.h" +#include "dbNetlistDeviceExtractor.h" + +namespace db +{ + +/** + * @brief A generic framework for extracting netlists from layouts + * + * This class wraps various concepts from db::NetlistExtractor and db::NetlistDeviceExtractor + * and more. It is supposed to provide a framework for extracting a netlist from a layout. + * + * The use model of this class consists of five steps which need to be executed in this order. + * + * @ul + * @li Configuration: in this step, the LayoutToNetlist object is created and + * if required, configured. Methods to be used in this step are "set_threads", + * "set_area_ratio" or "set_max_vertex_count". The constructor for the LayoutToNetlist + * object receives a db::RecursiveShapeIterator object which basically supplies the + * hierarchy and the layout taken as input. + * @/li + * @li Preparation + * In this step, the device recognitions and extraction layers are drawn from + * the framework. Derived can now be computed using boolean operations. + * Methods to use in this step are "make_layer" and it's variants. + * Layer preparation is not necessarily required to happen before all + * other steps. Layers can be computed shortly before they are required. + * @/li + * @li Following the preparation, the devices can be extracted using "extract_devices". + * This method needs to be called for each device extractor required. Each time, + * a device extractor needs to be given plus a map of device layers. The device + * layers are device extractor specific. Either original or derived layers + * may be specified here. Layer preparation may happen between calls to "extract_devices". + * @/li + * @li Once the devices are derived, the netlist connectivity can be defined and the + * netlist extracted. The connectivity is defined with "connect" and it's + * flavours. The actual netlist extraction happens with "extract_netlist". + * @/li + * @li After netlist extraction, the information is ready to be retrieved. + * The produced netlist is available with "netlist". The Shapes of a + * specific net are available with "shapes_of_net". "probe_net" allows + * finding a net by probing a specific location. + * @li + */ +class DB_PUBLIC LayoutToNetlist + : public gsi::ObjectBase, public tl::Object +{ +public: + typedef std::map::const_iterator layer_iterator; + + /** + * @brief The constructor + * + * See the class description for details. + */ + LayoutToNetlist (const db::RecursiveShapeIterator &iter); + + /** + * @brief Alternative constructor using an external deep shape storage + * + * This constructor allows using an external DSS. It's intended to be used + * with existing DSS instances. Existing layers can be registered with + * "register_layer". The LayoutToNetlist object will hold a weak reference + * to the DSS but not own the DSS. + * + * NOTE: if using make_layer, these new layers will be created in the DSS + * given in this constructor. + */ + LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_index = 0); + + /** + * @brief Alternative constructor for flat mode + * + * In flat mode, the internal DSS will be initialized to a top-level only + * layout. All layers entered through "register_layer" or created with + * "make_layer" will become flat ones. + */ + LayoutToNetlist (const std::string &topcell_name, double dbu); + + /** + * @brief The default constructor + */ + LayoutToNetlist (); + + /** + * @brief The destructor + */ + ~LayoutToNetlist (); + + /** + * @brief Sets the number of threads to use for operations which support multiple threads + */ + void set_threads (int n); + + /** + * @brief Gets the number of threads to use + */ + int threads () const; + + /** + * @brief Sets the area_ratio parameter for the hierarchical network processor + * This parameter controls splitting of large polygons in order to reduce the + * error made by the bounding box approximation. + */ + void set_area_ratio (double ar); + + /** + * @brief Gets the area ratio + */ + double area_ratio () const; + + /** + * @brief Sets the max_vertex_count parameter for the hierarchical network processor + * This parameter controls splitting of large polygons in order to enhance performance + * for very big polygons. + */ + void set_max_vertex_count (size_t n); + + /** + * @brief Gets the max vertex count + */ + size_t max_vertex_count () const; + + /** + * @brief Register a layer under the given name + * This is a formal name for the layer. Using a name or layer properties + * (see below) enhances readability of backannotated information + * if layers are involved. Use this method to attach a name to a region + * derived by boolean operations for example. + * Named regions are persisted inside the LayoutToNetlist object. Only + * named regions can be put into "connect". + */ + void register_layer (const db::Region ®ion, const std::string &name); + + /** + * @brief Gets the name of the given region + * Returns an empty string if the region does not have a name. + */ + std::string name (const db::Region ®ion) const; + + /** + * @brief Gets the name of the given layer by index + * Returns an empty string if the layer does not have a name. + */ + std::string name (unsigned int) const; + + /** + * @brief Returns true, if the region is a persisted region + * Persisted regions have a name and are kept inside the LayoutToNetlist + * object. + */ + bool is_persisted (const db::Region ®ion) const; + + /** + * @brief Gets the region (layer) with the given name + * If the name is not valid, this method returns 0. Otherwise it + * will return a new'd Region object referencing the layer with + * the given name. It must be deleted by the caller. + */ + db::Region *layer_by_name (const std::string &name); + + /** + * @brief Gets the region (layer) by index + * If the index is not valid, this method returns 0. Otherwise it + * will return a new'd Region object referencing the layer with + * the given name. It must be deleted by the caller. + * Only named layers are managed by LayoutToNetlist and can + * be retrieved with this method. + */ + db::Region *layer_by_index (unsigned int index); + + /** + * @brief Iterates over the layer indexes and names managed by this object (begin) + */ + layer_iterator begin_layers () const + { + return m_name_of_layer.begin (); + } + + /** + * @brief Iterates over the layer indexes and names managed by this object (end) + */ + layer_iterator end_layers () const + { + return m_name_of_layer.end (); + } + + /** + * @brief Creates a new empty region + * This method returns a new'd object which must be deleted by the caller. + */ + db::Region *make_layer (const std::string &name = std::string ()); + + /** + * @brief Creates a new region representing an original layer + * "layer_index" is the layer index of the desired layer in the original layout. + * This variant produces polygons and takes texts for net name annotation. + * A variant not taking texts is "make_polygon_layer". A Variant only taking + * texts is "make_text_layer". + * All variants return a new'd object which must be deleted by the caller. + * Named regions are considered "precious". The LayoutToNetlist object will + * keep a reference on all named layers, so they persist during the lifetime + * of the LayoutToNetlist object. + * Only named layers can be used for connect (see below). + */ + db::Region *make_layer (unsigned int layer_index, const std::string &name = std::string ()); + + /** + * @brief Creates a new region representing an original layer taking texts only + * See "make_layer" for details. + */ + db::Region *make_text_layer (unsigned int layer_index, const std::string &name = std::string ()); + + /** + * @brief Creates a new region representing an original layer taking polygons and texts + * See "make_layer" for details. + */ + db::Region *make_polygon_layer (unsigned int layer_index, const std::string &name = std::string ()); + + /** + * @brief Extracts devices + * See the class description for more details. + * This method will run device extraction for the given extractor. The layer map is specific + * for the extractor and uses the region objects derived with "make_layer" and it's variants. + * + * In addition, derived regions can be passed too. Certain limitations apply. It's safe to use + * boolean operations for deriving layers. Other operations are applicable as long as they are + * capable of delivering hierarchical layers. + * + * If errors occur, the device extractor will contain theses errors. + */ + void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers); + + /** + * @brief Defines an intra-layer connection for the given layer. + * The layer is either an original layer created with "make_layer" and it's variants or + * a derived layer. Certain limitations apply. It's safe to use + * boolean operations for deriving layers. Other operations are applicable as long as they are + * capable of delivering hierarchical layers. + * Regions put into "connect" need to be named. + */ + void connect (const db::Region &l); + + /** + * @brief Defines an inter-layer connection for the given layers. + * The conditions mentioned with intra-layer "connect" apply for this method too. + * Regions put into "connect" need to be named. + */ + void connect (const db::Region &a, const db::Region &b); + + /** + * @brief Connects the given layer with a global net with the given name + * Returns the global net ID + * Regions put into "connect" need to be named. + */ + size_t connect_global (const db::Region &l, const std::string &gn); + + /** + * @brief Gets the global net name for a given global net ID + */ + const std::string &global_net_name (size_t id) const; + + /** + * @brief Gets the global net ID for a given name name + */ + size_t global_net_id (const std::string &name); + + /** + * @brief Runs the netlist extraction + * See the class description for more details. + */ + void extract_netlist (bool join_nets_by_label = true); + + /** + * @brief Marks the netlist as extracted + * NOTE: this method is provided for special cases such as netlist readers. Don't + * use it. + */ + void set_netlist_extracted (); + + /** + * @brief Gets the internal DeepShapeStore object + * + * This method is intended for special cases, i.e. for the master + * LayoutToNetlist object in the DRC environment. The DSS provided + * for DRC needs to be initialized properly for text representation. + */ + db::DeepShapeStore &dss () + { + tl_assert (mp_dss.get () != 0); + return *mp_dss; + } + + /** + * @brief Gets the internal DeepShapeStore object (const version) + * + * See the non-const version for details. + */ + const db::DeepShapeStore &dss () const + { + tl_assert (mp_dss.get () != 0); + return *mp_dss; + } + + /** + * @brief Gets the internal layout + */ + const db::Layout *internal_layout () const; + + /** + * @brief Gets the internal top cell + */ + const db::Cell *internal_top_cell () const; + + /** + * @brief Gets the internal layout (non-const version) + */ + db::Layout *internal_layout (); + + /** + * @brief Gets the internal top cell (non-const version) + */ + db::Cell *internal_top_cell (); + + /** + * @brief Gets the connectivity object + */ + const db::Connectivity &connectivity () const + { + return m_conn; + } + + /** + * @brief Gets the internal layer for a given extraction layer + * This method is required to derive the internal layer index - for example for + * investigating the cluster tree. + */ + unsigned int layer_of (const db::Region ®ion) const; + + /** + * @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout. + * If 'with_device_cells' is true, cells will be produced for devices. These are cells not corresponding to circuits, so they are disabled normally. + * Use this option, if you want to access device terminal shapes per device. + * CAUTION: This function may create new cells in "layout". + */ + db::CellMapping cell_mapping_into (db::Layout &layout, db::Cell &cell, bool with_device_cells = false); + + /** + * @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout. + * This version will not create new cells in the target layout. + * If the required cells do not exist there yet, flatting will happen. + */ + db::CellMapping const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell); + + /** + * @brief gets the netlist extracted (0 if no extraction happened yet) + */ + db::Netlist *netlist () const; + + /** + * @brief gets the netlist extracted or make on if none exists yet. + * NOTE: this method is provided for special cases like readers of persisted + * layout to netlist data. + */ + db::Netlist *make_netlist (); + + /** + * @brief Gets the hierarchical shape clusters derived in the net extraction. + * NOTE: the layer and cell indexes used inside this structure refer to the + * internal layout. + */ + const db::hier_clusters &net_clusters () const + { + return m_net_clusters; + } + + /** + * @brief Gets the hierarchical shape clusters derived in the net extraction (non-conver version) + */ + db::hier_clusters &net_clusters () + { + return m_net_clusters; + } + + /** + * @brief Returns all shapes of a specific net and layer. + * + * If "recursive" is true, the returned region will contain the shapes of + * all subcircuits too. + * + * This methods returns a new'd Region. It's the responsibility of the caller + * to delete this object. + */ + db::Region *shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const; + + /** + * @brief Delivers all shapes of a specific net and layer to the given Shapes container. + * + * If "recursive" is true, the returned region will contain the shapes of + * all subcircuits too. + * + * This methods returns a new'd Region. It's the responsibility of the caller + * to delete this object. + */ + void shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to) const; + + /** + * @brief Builds a net representation in the given layout and cell + * + * This method has two modes: recursive and top-level mode. In recursive mode, + * it will create a proper hierarchy below the given target cell to hold all subcircuits the + * net connects to. It will copy the net's parts from this subcircuits into these cells. + * + * In top-level mode, only the shapes from the net inside it's circuit are copied to + * the given target cell. No other cells are created. + * + * Recursive mode is picked when a cell name prefix is given. The new cells will be + * named like cell_name_prefix + circuit name. + * + * If a device cell name prefix is given, cells will be produced for each device abstract + * using a name like device_cell_name_prefix + device name. + * + * @param target The target layout + * @param target_cell The target cell + * @param lmap Target layer indexes (keys) and net regions (values) + * @param cell_name_prefix Chooses recursive mode if non-null + * @param device_cell_name_prefix See above + */ + void build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix) const; + + /** + * @brief Builds a full hierarchical representation of the nets + * + * This method copies all nets into cells corresponding to the circuits. It uses the cmap + * object to determine the target cell (create them with "cell_mapping_into" or "const_cell_mapping_into"). + * If no mapping is requested, the specific circuit it skipped. + * + * The method has two net annotation modes: + * * No annotation (net_cell_name_prefix == 0): the shapes will be put into the target cell simply + * * Individual subcells per net (net_cell_name_prefix != 0): for each net, a subcell is created + * and the net shapes will be put there (name of the subcell = net_cell_name_prefix + net name). + * + * In addition, net hierarchy is covered in two ways: + * * No connection indicated (circuit_cell_name_prefix == 0: the net shapes are simply put into their + * respective circuits. The connections are not indicated. + * * Subnet hierarchy (circuit_cell_name_prefix != 0): for each root net, a full hierarchy is built + * to accomodate the subnets (see build_net in recursive mode). + * + * If a device cell name prefix is given, cells will be produced for each device abstract + * using a name like device_cell_name_prefix + device name. + * + * @param cmap The mapping of internal layout to target layout for the circuit mapping + * @param target The target layout + * @param lmap Target layer indexes (keys) and net regions (values) + * @param circuit_cell_name_prefix See method description + * @param net_cell_name_prefix See method description + * @param device_cell_name_prefix See above + */ + void build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const; + + /** + * @brief Finds the net by probing a specific location on the given layer + * + * This method will find a net looking at the given layer at the specific position. + * It will traverse the hierarchy below if no shape in the requested layer is found + * in the specified location. + * + * If no net is found at all, 0 is returned. + * + * This variant accepts a micrometer-unit location. The location is given in the + * coordinate space of the initial cell. + */ + db::Net *probe_net (const db::Region &of_region, const db::DPoint &point); + + /** + * @brief Finds the net by probing a specific location on the given layer + * See the description of the other "probe_net" variant. + * This variant accepts a database-unit location. The location is given in the + * coordinate space of the initial cell. + */ + db::Net *probe_net (const db::Region &of_region, const db::Point &point); + + /** + * @brief Runs an antenna check on the extracted clusters + * + * The antenna check will traverse all clusters and run an antenna check + * for all root clusters. The antenna ratio is defined by the total + * area of all "metal" shapes divided by the total area of all "gate" shapes + * on the cluster. Of all clusters where the antenna ratio is larger than + * the limit ratio all metal shapes are copied to the output region as + * error markers. + * + * The limit ratio can be modified by the presence of connections to + * other layers (specifically designating diodes for charge removal). + * Each of these layers will modify the ratio by adding a value of + * A(diode) / Ared[um^2] to the ratio. A(diode) is the area of the + * diode layer per cluster. Both the diode layer and the Ared value + * are specified as pairs in "diodes". + * + * A special case is Ared = 0: in this case, the presence of any shapes + * on the diode layer will entirely disable the check on a cluster, + * regardless of the diode's area. + * In other words: any diode will make the net safe against antenna discharge. + */ + db::Region antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector > &diodes = std::vector > ()); + +private: + // no copying + LayoutToNetlist (const db::LayoutToNetlist &other); + LayoutToNetlist &operator= (const db::LayoutToNetlist &other); + + db::RecursiveShapeIterator m_iter; + std::auto_ptr mp_internal_dss; + tl::weak_ptr mp_dss; + unsigned int m_layout_index; + db::Connectivity m_conn; + db::hier_clusters m_net_clusters; + std::auto_ptr mp_netlist; + std::set m_dlrefs; + std::map m_named_regions; + std::map m_name_of_layer; + bool m_netlist_extracted; + bool m_is_flat; + db::DeepLayer m_dummy_layer; + + void init (); + size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, std::vector &rev_inst_path); + void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const ICplxTrans &tr) const; + void build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const Net *net, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const ICplxTrans &tr) const; + db::DeepLayer deep_layer_of (const db::Region ®ion) const; + void ensure_layout () const; +}; + +} + +namespace tl +{ + +template<> struct type_traits : public tl::type_traits +{ + // mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +} + +#endif diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.cc b/src/db/db/dbLayoutToNetlistFormatDefs.cc new file mode 100644 index 000000000..3a00f45e0 --- /dev/null +++ b/src/db/db/dbLayoutToNetlistFormatDefs.cc @@ -0,0 +1,75 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbLayoutToNetlistFormatDefs.h" + +namespace db +{ + +namespace l2n_std_format +{ + template<> DB_PUBLIC const std::string keys::version_key ("version"); + template<> DB_PUBLIC const std::string keys::description_key ("description"); + template<> DB_PUBLIC const std::string keys::top_key ("top"); + template<> DB_PUBLIC const std::string keys::unit_key ("unit"); + template<> DB_PUBLIC const std::string keys::layer_key ("layer"); + template<> DB_PUBLIC const std::string keys::connect_key ("connect"); + template<> DB_PUBLIC const std::string keys::global_key ("global"); + template<> DB_PUBLIC const std::string keys::circuit_key ("circuit"); + template<> DB_PUBLIC const std::string keys::net_key ("net"); + template<> DB_PUBLIC const std::string keys::name_key ("name"); + template<> DB_PUBLIC const std::string keys::device_key ("device"); + template<> DB_PUBLIC const std::string keys::polygon_key ("polygon"); + template<> DB_PUBLIC const std::string keys::rect_key ("rect"); + template<> DB_PUBLIC const std::string keys::terminal_key ("terminal"); + template<> DB_PUBLIC const std::string keys::abstract_key ("abstract"); + template<> DB_PUBLIC const std::string keys::param_key ("param"); + template<> DB_PUBLIC const std::string keys::location_key ("location"); + template<> DB_PUBLIC const std::string keys::rotation_key ("rotation"); + template<> DB_PUBLIC const std::string keys::mirror_key ("mirror"); + template<> DB_PUBLIC const std::string keys::scale_key ("scale"); + template<> DB_PUBLIC const std::string keys::pin_key ("pin"); + + template<> DB_PUBLIC const std::string keys::version_key ("V"); + template<> DB_PUBLIC const std::string keys::description_key ("B"); + template<> DB_PUBLIC const std::string keys::top_key ("W"); + template<> DB_PUBLIC const std::string keys::unit_key ("U"); + template<> DB_PUBLIC const std::string keys::layer_key ("L"); + template<> DB_PUBLIC const std::string keys::connect_key ("C"); + template<> DB_PUBLIC const std::string keys::global_key ("G"); + template<> DB_PUBLIC const std::string keys::circuit_key ("X"); + template<> DB_PUBLIC const std::string keys::net_key ("N"); + template<> DB_PUBLIC const std::string keys::name_key ("I"); + template<> DB_PUBLIC const std::string keys::device_key ("D"); + template<> DB_PUBLIC const std::string keys::polygon_key ("Q"); + template<> DB_PUBLIC const std::string keys::rect_key ("R"); + template<> DB_PUBLIC const std::string keys::terminal_key ("T"); + template<> DB_PUBLIC const std::string keys::abstract_key ("A"); + template<> DB_PUBLIC const std::string keys::param_key ("E"); + template<> DB_PUBLIC const std::string keys::location_key ("Y"); + template<> DB_PUBLIC const std::string keys::rotation_key ("O"); + template<> DB_PUBLIC const std::string keys::mirror_key ("M"); + template<> DB_PUBLIC const std::string keys::scale_key ("S"); + template<> DB_PUBLIC const std::string keys::pin_key ("P"); +} + +} diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h new file mode 100644 index 000000000..8bb38dbec --- /dev/null +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -0,0 +1,144 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbLayoutToNetlistFormatDefs +#define HDR_dbLayoutToNetlistFormatDefs + +#include "dbCommon.h" + +#include + +namespace db +{ + +/** + * This is the internal persistency format for LayoutToNetlist + * + * It's intentionally *not* XML to keep the overhead low. + * + * Comments are introduced by hash: # ... + * Names are words (alphanumerical plus "$", "_", ".") or enclosed in single or double quotes. + * Escape character is backslash. + * Separator is either , or whitespace. Keywords and names are case sensitive. + * Short keys are provided for compacter representation. Short keys can be + * non-alpha (e.g. "*") or empty. + * Single-valued attributes can be given without brackets. + * All dimensions are in units of database unit. + * The file follows the declaration-before-use principle + * (circuits before subcircuits, nets before use ...) + * + * Global statements: + * + * version() - file format version [short key: V] + * description() - an arbitrary description text [short key: B] + * unit() - specifies the database unit [short key: U] + * top() - specifies the name of the top circuit [short key: W] + * layer() - define a layer [short key: L] + * connect( ...) - connects layer1 with the following layers [short key: C] + * global( ...) + * - connects the shapes of the layer with the given global + * nets [short key: G] + * circuit( [circuit-def]) - circuit (cell) [short key: X] + * device( [device-abstract-def]) + * - device abstract [short key: D] + * + * [circuit-def]: + * + * net( [net-name]? [geometry-def]*) + * - net geometry [short key: N] + * A net declaration shall be there also if no geometry + * is present. The ID is a numerical shortcut for the net. + * pin( ) - outgoing pin connection [short key: P] + * device( [device-def]) + * - device with connections [short key: D] + * circuit( [circuit-def]) - subcircuit with connections [short key: X] + * + * [net-name]: + * name() - specify net name [short key: + * + * [geometry-def]: + * + * polygon( ...) - defines a polygon [short key: Q] + * "*" for or means take previous + * rect( ) + * - defines a rectangle [short key: R] + * + * [device-abstract-def]: + * + * terminal( [geometry-def]*) + * - specifies the terminal geometry [short key: T] + * + * [device-def]: + * + * location( ) - location of the device [short key Y] + * must be before terminal + * param( ) - defines a parameter [short key E] + * terminal( ) + * - specifies connection of the terminal with + * a net (short key: T) + * + * [subcircuit-def]: + * + * location( ) - location of the subcircuit [short key Y] + * rotation() - rotation angle (in degree, default is 0) [short key O] + * mirror - if specified, the instance is mirrored before rotation [short key M] + * scale() - magnification (default is 1) [short key S] + * pin( ) - specifies connection of the pin with a net [short key: P] + */ + +namespace l2n_std_format +{ + template + struct DB_PUBLIC keys + { + static const std::string version_key; + static const std::string description_key; + static const std::string top_key; + static const std::string unit_key; + static const std::string layer_key; + static const std::string connect_key; + static const std::string global_key; + static const std::string circuit_key; + static const std::string net_key; + static const std::string name_key; + static const std::string device_key; + static const std::string subcircuit_key; + static const std::string polygon_key; + static const std::string rect_key; + static const std::string terminal_key; + static const std::string abstract_key; + static const std::string param_key; + static const std::string location_key; + static const std::string rotation_key; + static const std::string mirror_key; + static const std::string scale_key; + static const std::string pin_key; + static const std::string indent1; + static const std::string indent2; + + inline static bool is_short () { return Short; } + }; +} + +} + +#endif diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc new file mode 100644 index 000000000..6dcefdade --- /dev/null +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -0,0 +1,708 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbLayoutToNetlistReader.h" +#include "dbLayoutToNetlistFormatDefs.h" +#include "dbLayoutToNetlist.h" + +namespace db +{ + +namespace l2n_std_reader { + +class Brace +{ +public: + Brace (LayoutToNetlistStandardReader *reader) : mp_reader (reader), m_checked (false) + { + m_has_brace = reader->test ("("); + } + + operator bool () + { + if (! m_has_brace) { + m_checked = true; + return false; + } else if (mp_reader->test (")")) { + m_checked = true; + return false; + } else { + return true; + } + } + + void done () + { + if (m_has_brace && ! m_checked) { + mp_reader->expect (")"); + m_checked = true; + } + } + +private: + LayoutToNetlistStandardReader *mp_reader; + bool m_checked; + bool m_has_brace; +}; + +} + +typedef l2n_std_format::keys skeys; +typedef l2n_std_format::keys lkeys; + +LayoutToNetlistStandardReader::LayoutToNetlistStandardReader (tl::InputStream &stream) + : m_stream (stream), m_path (stream.absolute_path ()) +{ + skip (); +} + +bool +LayoutToNetlistStandardReader::test (const std::string &token) +{ + skip (); + return ! at_end () && m_ex.test (token.c_str ()); +} + +void +LayoutToNetlistStandardReader::expect (const std::string &token) +{ + m_ex.expect (token.c_str ()); +} + +void +LayoutToNetlistStandardReader::read_word_or_quoted (std::string &s) +{ + m_ex.read_word_or_quoted (s); +} + +int +LayoutToNetlistStandardReader::read_int () +{ + int i = 0; + m_ex.read (i); + return i; +} + +db::Coord +LayoutToNetlistStandardReader::read_coord () +{ + db::Coord i = 0; + m_ex.read (i); + return i; +} + +double +LayoutToNetlistStandardReader::read_double () +{ + double d = 0; + m_ex.read (d); + return d; +} + +bool +LayoutToNetlistStandardReader::at_end () +{ + return (m_ex.at_end () && m_stream.at_end ()); +} + +void +LayoutToNetlistStandardReader::skip () +{ + while (m_ex.at_end () || *m_ex.skip () == '#') { + if (m_stream.at_end ()) { + return; + } + m_line = m_stream.get_line (); + m_ex = tl::Extractor (m_line.c_str ()); + } +} + +void LayoutToNetlistStandardReader::read (db::LayoutToNetlist *l2n) +{ + try { + do_read (l2n); + } catch (tl::Exception &ex) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("%s in line: %d of %s")), ex.msg (), m_stream.line_number (), m_path)); + } +} + +static db::Region &layer_by_name (db::LayoutToNetlist *l2n, const std::string &name) +{ + db::Region *l = l2n->layer_by_name (name); + if (! l) { + throw tl::Exception (tl::to_string (tr ("Not a valid layer name: ")) + name); + } + return *l; +} + +void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) +{ + int version = 0; + std::string description; + + tl_assert (l2n->internal_layout ()); + l2n->internal_layout ()->dbu (1.0); // mainly for testing + + if (l2n->internal_layout ()->cells () == 0) { + l2n->internal_layout ()->add_cell ("TOP"); + } + tl_assert (l2n->internal_top_cell () != 0); + + l2n->make_netlist (); + + while (! at_end ()) { + + if (test (skeys::version_key) || test (lkeys::version_key)) { + + Brace br (this); + version = read_int (); + br.done (); + + } else if (test (skeys::description_key) || test (lkeys::description_key)) { + + Brace br (this); + read_word_or_quoted (description); + br.done (); + + } else if (test (skeys::unit_key) || test (lkeys::unit_key)) { + + Brace br (this); + double dbu = read_double (); + l2n->internal_layout ()->dbu (dbu); + br.done (); + + } else if (test (skeys::top_key) || test (lkeys::top_key)) { + + Brace br (this); + std::string top; + read_word_or_quoted (top); + l2n->internal_layout ()->rename_cell (l2n->internal_top_cell ()->cell_index (), top.c_str ()); + br.done (); + + } else if (test (skeys::layer_key) || test (lkeys::layer_key)) { + + Brace br (this); + std::string layer; + read_word_or_quoted (layer); + delete l2n->make_layer (layer); + br.done (); + + } else if (test (skeys::connect_key) || test (lkeys::connect_key)) { + + Brace br (this); + std::string l1; + read_word_or_quoted (l1); + while (br) { + std::string l2; + read_word_or_quoted (l2); + l2n->connect (layer_by_name (l2n, l1), layer_by_name (l2n, l2)); + } + br.done (); + + } else if (test (skeys::global_key) || test (lkeys::global_key)) { + + Brace br (this); + std::string l1; + read_word_or_quoted (l1); + while (br) { + std::string g; + read_word_or_quoted (g); + l2n->connect_global (layer_by_name (l2n, l1), g); + } + br.done (); + + } else if (test (skeys::circuit_key) || test (lkeys::circuit_key)) { + + Brace br (this); + std::string name; + read_word_or_quoted (name); + + db::Circuit *circuit = new db::Circuit (); + circuit->set_name (name); + l2n->netlist ()->add_circuit (circuit); + + db::Layout *ly = l2n->internal_layout (); + std::pair ci_old = ly->cell_by_name (name.c_str ()); + db::cell_index_type ci = ci_old.first ? ci_old.second : ly->add_cell (name.c_str ()); + circuit->set_cell_index (ci); + + std::map > connections; + std::map id2net; + + while (br) { + + if (test (skeys::net_key) || test (lkeys::net_key)) { + read_net (l2n, circuit, id2net); + } else if (test (skeys::pin_key) || test (lkeys::pin_key)) { + read_pin (l2n, circuit, id2net); + } else if (test (skeys::device_key) || test (lkeys::device_key)) { + std::list conn; + db::CellInstArray ia = read_device (l2n, circuit, conn, id2net); + connections[ia] = conn; + } else if (test (skeys::circuit_key) || test (lkeys::circuit_key)) { + std::list conn; + db::CellInstArray ia = read_subcircuit (l2n, circuit, conn, id2net); + connections[ia] = conn; + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (net, pin, device or circuit expected)"))); + } + + } + br.done (); + + db::Cell &ccell = ly->cell (ci); + + // connections needs to be made after the instances (because in a readonly Instances container + // the Instance pointers will invalidate when new instances are added) + for (db::Cell::const_iterator i = ccell.begin (); ! i.at_end (); ++i) { + std::map >::const_iterator c = connections.find (i->cell_inst ()); + if (c != connections.end ()) { + for (std::list::const_iterator j = c->second.begin (); j != c->second.end (); ++j) { + l2n->net_clusters ().clusters_per_cell (ci).add_connection (j->from_cluster, db::ClusterInstance (j->to_cluster, i->cell_index (), i->complex_trans (), i->prop_id ())); + } + } + } + + } else if (test (skeys::device_key) || test (lkeys::device_key)) { + + Brace br (this); + std::string name; + read_word_or_quoted (name); + + db::DeviceAbstract *dm = new db::DeviceAbstract (); + dm->set_name (name); + l2n->netlist ()->add_device_abstract (dm); + + db::cell_index_type ci = l2n->internal_layout ()->add_cell (name.c_str ()); + dm->set_cell_index (ci); + + std::string cls; + read_word_or_quoted (cls); + + db::DeviceClass *dc = 0; + for (db::Netlist::device_class_iterator i = l2n->netlist ()->begin_device_classes (); i != l2n->netlist ()->end_device_classes (); ++i) { + if (i->name () == cls) { + dc = i.operator-> (); + } + } + + // use a generic device class unless the right one is registered already. + bool gen_dc = (dc == 0); + if (gen_dc) { + dc = new db::DeviceClass (); + dc->set_name (cls); + l2n->netlist ()->add_device_class (dc); + } + + dm->set_device_class (dc); + + while (br) { + + if (test (skeys::terminal_key) || test (lkeys::terminal_key)) { + read_abstract_terminal (l2n, dm, gen_dc ? dc : 0); + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside device abstract definition (terminal expected)"))); + } + + } + + br.done (); + + } + + } + + l2n->set_netlist_extracted (); +} + +std::pair +LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n) +{ + std::string lname; + + if (test (skeys::rect_key) || test (lkeys::rect_key)) { + + Brace br (this); + + read_word_or_quoted (lname); + unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname)); + + db::Coord l = read_coord (); + db::Coord b = read_coord (); + db::Coord r = read_coord (); + db::Coord t = read_coord (); + db::Box box (l, b, r, t); + + br.done (); + + return std::make_pair (lid, db::PolygonRef (db::Polygon (box), l2n->internal_layout ()->shape_repository ())); + + } else if (test (skeys::polygon_key) || test (lkeys::polygon_key)) { + + Brace br (this); + + read_word_or_quoted (lname); + unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname)); + + std::vector pt; + + db::Coord x = 0, y = 0; + while (br) { + if (! test ("*")) { + x = read_coord (); + } + if (! test ("*")) { + y = read_coord (); + } + pt.push_back (db::Point (x, y)); + } + + br.done (); + + db::Polygon poly; + poly.assign_hull (pt.begin (), pt.end ()); + return std::make_pair (lid, db::PolygonRef (poly, l2n->internal_layout ()->shape_repository ())); + + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside net or terminal definition (polygon or rect expected)"))); + } +} + +void +LayoutToNetlistStandardReader::read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::map &id2net) +{ + Brace br (this); + + unsigned int id = (unsigned int) read_int (); + std::string name; + + if (test (skeys::name_key) || test (lkeys::name_key)) { + Brace br_name (this); + read_word_or_quoted (name); + br_name.done (); + } + + db::Net *net = new db::Net (); + net->set_name (name); + circuit->add_net (net); + + id2net.insert (std::make_pair (id, net)); + + db::connected_clusters &cc = l2n->net_clusters ().clusters_per_cell (circuit->cell_index ()); + db::local_cluster &lc = *cc.insert (); + net->set_cluster_id (lc.id ()); + + db::Cell &cell = l2n->internal_layout ()->cell (circuit->cell_index ()); + + while (br) { + std::pair pr = read_geometry (l2n); + lc.add (pr.second, pr.first); + cell.shapes (pr.first).insert (pr.second); + } + + br.done (); +} + +void +LayoutToNetlistStandardReader::read_pin (db::LayoutToNetlist * /*l2n*/, db::Circuit *circuit, std::map &id2net) +{ + Brace br (this); + std::string name; + read_word_or_quoted (name); + unsigned int netid = (unsigned int) read_int (); + br.done (); + + const db::Pin &pin = circuit->add_pin (name); + + db::Net *net = id2net [netid]; + if (!net) { + throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid)); + } + + circuit->connect_pin (pin.id (), net); +} + +db::CellInstArray +LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs, std::map &id2net) +{ + Brace br (this); + + std::string name; + read_word_or_quoted (name); + + std::string dmname; + read_word_or_quoted (dmname); + + db::DeviceAbstract *dm = 0; + for (db::Netlist::device_abstract_iterator i = l2n->netlist ()->begin_device_abstracts (); i != l2n->netlist ()->end_device_abstracts (); ++i) { + if (i->name () == dmname) { + dm = i.operator-> (); + } + } + + if (! dm) { + throw tl::Exception (tl::to_string (tr ("Not a valid device abstract name: ")) + dmname); + } + + db::Device *device = new db::Device (); + device->set_device_class (const_cast (dm->device_class ())); + device->set_device_abstract (dm); + device->set_name (name); + circuit->add_device (device); + + db::Coord x = 0, y = 0; + + while (br) { + + if (test (skeys::location_key) || test (lkeys::location_key)) { + + Brace br2 (this); + x = read_coord (); + y = read_coord (); + br2.done (); + + } else if (test (skeys::terminal_key) || test (lkeys::terminal_key)) { + + Brace br2 (this); + std::string tname; + read_word_or_quoted (tname); + unsigned int netid = (unsigned int) read_int (); + br2.done (); + + size_t tid = std::numeric_limits::max (); + const std::vector &td = dm->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + if (t->name () == tname) { + tid = t->id (); + break; + } + } + + if (tid == std::numeric_limits::max ()) { + throw tl::Exception (tl::to_string (tr ("Not a valid terminal name: ")) + tname + tl::to_string (tr (" for device class: ")) + dm->device_class ()->name ()); + } + + db::Net *net = id2net [netid]; + if (!net) { + throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid)); + } + + device->connect_terminal (tid, net); + refs.push_back (Connections (net->cluster_id (), dm->cluster_id_for_terminal (tid))); + + } else if (test (skeys::param_key) || test (lkeys::param_key)) { + + Brace br2 (this); + std::string pname; + read_word_or_quoted (pname); + double value = read_double (); + br2.done (); + + size_t pid = std::numeric_limits::max (); + const std::vector &pd = dm->device_class ()->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (p->name () == pname) { + pid = p->id (); + break; + } + } + + // if no parameter with this name exists, create one + if (pid == std::numeric_limits::max ()) { + // TODO: this should only happen for generic devices + db::DeviceClass *dc = const_cast (dm->device_class ()); + pid = dc->add_parameter_definition (db::DeviceParameterDefinition (pname, std::string ())).id (); + } + + device->set_parameter_value (pid, value); + + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside device definition (location, param or terminal expected)"))); + } + + } + + double dbu = l2n->internal_layout ()->dbu (); + device->set_position (db::DPoint (dbu * x, dbu * y)); + + br.done (); + + // make device cell instance + db::CellInstArray inst (db::CellInst (dm->cell_index ()), db::Trans (db::Vector (x, y))); + db::Cell &ccell = l2n->internal_layout ()->cell (circuit->cell_index ()); + ccell.insert (inst); + + return inst; +} + +db::CellInstArray +LayoutToNetlistStandardReader::read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs, std::map &id2net) +{ + Brace br (this); + + std::string name; + read_word_or_quoted (name); + + std::string xname; + read_word_or_quoted (xname); + + db::Circuit *circuit_ref = l2n->netlist ()->circuit_by_name (xname); + if (! circuit_ref) { + throw tl::Exception (tl::to_string (tr ("Not a valid device circuit name: ")) + xname); + } + + db::SubCircuit *subcircuit = new db::SubCircuit (circuit_ref); + subcircuit->set_name (name); + circuit->add_subcircuit (subcircuit); + + db::Coord x = 0, y = 0; + bool mirror = false; + double angle = 0; + double mag = 1.0; + + db::InstElement ie; + bool inst_made = false; + + while (br) { + + if (test (skeys::location_key) || test (lkeys::location_key)) { + + Brace br2 (this); + x = read_coord (); + y = read_coord (); + br2.done (); + + if (inst_made) { + throw tl::Exception (tl::to_string (tr ("location key must come before pin key in subcircuit definition"))); + } + + } else if (test (skeys::rotation_key) || test (lkeys::rotation_key)) { + + Brace br2 (this); + angle = read_double (); + br2.done (); + + if (inst_made) { + throw tl::Exception (tl::to_string (tr ("rotation key must come before pin key in subcircuit definition"))); + } + + } else if (test (skeys::mirror_key) || test (lkeys::mirror_key)) { + + mirror = true; + if (inst_made) { + throw tl::Exception (tl::to_string (tr ("mirror key must come before pin key in subcircuit definition"))); + } + + } else if (test (skeys::scale_key) || test (lkeys::scale_key)) { + + Brace br2 (this); + mag = read_double (); + br2.done (); + + if (inst_made) { + throw tl::Exception (tl::to_string (tr ("scale key must come before pin key in subcircuit definition"))); + } + + } else if (test (skeys::pin_key) || test (lkeys::pin_key)) { + + Brace br2 (this); + std::string pname; + read_word_or_quoted (pname); + unsigned int netid = (unsigned int) read_int (); + br2.done (); + + const db::Pin *sc_pin = circuit_ref->pin_by_name (pname); + if (! sc_pin) { + throw tl::Exception (tl::to_string (tr ("Not a valid pin name: ")) + pname + tl::to_string (tr (" for circuit: ")) + circuit_ref->name ()); + } + + db::Net *net = id2net [netid]; + if (!net) { + throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid)); + } + + subcircuit->connect_pin (sc_pin->id (), net); + db::Net *sc_net = circuit_ref->net_for_pin (sc_pin->id ()); + if (sc_net) { + refs.push_back (Connections (net->cluster_id (), sc_net->cluster_id ())); + } + + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside subcircuit definition (location, rotation, mirror, scale or pin expected)"))); + } + + } + + br.done (); + + double dbu = l2n->internal_layout ()->dbu (); + subcircuit->set_trans (db::DCplxTrans (mag, angle, mirror, db::DVector (dbu * x, dbu * y))); + + db::CellInstArray inst (db::CellInst (circuit_ref->cell_index ()), db::ICplxTrans (mag, angle, mirror, db::Vector (x, y))); + db::Cell &ccell = l2n->internal_layout ()->cell (circuit->cell_index ()); + ccell.insert (inst); + + return inst; +} + +void +LayoutToNetlistStandardReader::read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceAbstract *dm, db::DeviceClass *dc) +{ + Brace br (this); + + std::string name; + read_word_or_quoted (name); + + size_t tid = std::numeric_limits::max (); + const std::vector &td = dm->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + if (t->name () == name) { + tid = t->id (); + break; + } + } + + // create a terminal unless one with this name already exists + if (tid == std::numeric_limits::max ()) { + if (! dc) { + throw tl::Exception (tl::to_string (tr ("Not a valid terminal name: ")) + name + tl::to_string (tr (" for device class: ")) + dm->device_class ()->name ()); + } + db::DeviceTerminalDefinition new_td (name, std::string ()); + tid = dc->add_terminal_definition (new_td).id (); + } + + db::connected_clusters &cc = l2n->net_clusters ().clusters_per_cell (dm->cell_index ()); + db::local_cluster &lc = *cc.insert (); + dm->set_cluster_id_for_terminal (tid, lc.id ()); + + db::Cell &cell = l2n->internal_layout ()->cell (dm->cell_index ()); + + while (br) { + std::pair pr = read_geometry (l2n); + lc.add (pr.second, pr.first); + cell.shapes (pr.first).insert (pr.second); + } + + br.done (); +} + +} diff --git a/src/db/db/dbLayoutToNetlistReader.h b/src/db/db/dbLayoutToNetlistReader.h new file mode 100644 index 000000000..04d2f8e74 --- /dev/null +++ b/src/db/db/dbLayoutToNetlistReader.h @@ -0,0 +1,110 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbLayoutToNetlistReader +#define HDR_dbLayoutToNetlistReader + +#include "dbCommon.h" +#include "dbPolygon.h" +#include "dbCell.h" +#include "tlStream.h" + +namespace db { + +namespace l2n_std_reader { + class Layers; + class Brace; +} + +class LayoutToNetlist; +class Circuit; +class Cell; +class DeviceAbstract; +class DeviceClass; +class Net; +class Region; + +/** + * @brief The base class for a LayoutToNetlist writer + */ +class DB_PUBLIC LayoutToNetlistReaderBase +{ +public: + LayoutToNetlistReaderBase () { } + virtual ~LayoutToNetlistReaderBase () { } + + virtual void read (db::LayoutToNetlist *l2n) = 0; +}; + +/** + * @brief The standard writer + */ +class DB_PUBLIC LayoutToNetlistStandardReader + : public LayoutToNetlistReaderBase +{ +public: + LayoutToNetlistStandardReader (tl::InputStream &stream); + + void read (db::LayoutToNetlist *l2n); + +private: + friend class l2n_std_reader::Brace; + typedef l2n_std_reader::Brace Brace; + typedef l2n_std_reader::Layers Layers; + + struct Connections + { + Connections (size_t _from_cluster, size_t _to_cluster) + : from_cluster (_from_cluster), to_cluster (_to_cluster) + { } + + size_t from_cluster, to_cluster; + }; + + tl::TextInputStream m_stream; + std::string m_path; + std::string m_line; + tl::Extractor m_ex; + + void do_read (db::LayoutToNetlist *l2n); + + bool test (const std::string &token); + void expect (const std::string &token); + void read_word_or_quoted(std::string &s); + int read_int (); + db::Coord read_coord (); + double read_double (); + bool at_end (); + void skip (); + + void read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::map &id2net); + void read_pin (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::map &id2net); + db::CellInstArray read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs, std::map &id2net); + db::CellInstArray read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list &refs, std::map &id2net); + void read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceAbstract *dm, db::DeviceClass *dc); + std::pair read_geometry (db::LayoutToNetlist *l2n); +}; + +} + +#endif + diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc new file mode 100644 index 000000000..9508c2fb8 --- /dev/null +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -0,0 +1,478 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbLayoutToNetlistWriter.h" +#include "dbLayoutToNetlist.h" +#include "dbLayoutToNetlistFormatDefs.h" + +namespace db +{ + +namespace l2n_std_format +{ + +// ------------------------------------------------------------------------------------------- +// std_writer_impl implementation + +template +class std_writer_impl +{ +public: + std_writer_impl (tl::OutputStream &stream); + + void write (const db::LayoutToNetlist *l2n); + +private: + tl::OutputStream *mp_stream; + + void write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit); + void write (const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id); + void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit, std::map &net2id); + void write (const db::LayoutToNetlist *l2n, const db::Device &device, std::map &net2id); + void write (const db::LayoutToNetlist *l2n, const db::DeviceAbstract &device_abstract); + void write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname); +}; + +static const std::string endl ("\n"); +static const std::string indent1 (" "); +static const std::string indent2 (" "); + +template +std_writer_impl::std_writer_impl (tl::OutputStream &stream) + : mp_stream (&stream) +{ + // .. nothing yet .. +} + +static std::string name_for_layer (const db::LayoutToNetlist *l2n, unsigned int l) +{ + std::string n = l2n->name (l); + if (n.empty ()) { + n = "L" + tl::to_string (l); + } + return n; +} + +template +void std_writer_impl::write (const db::LayoutToNetlist *l2n) +{ + bool any = false; + + const int version = 0; + + const db::Layout *ly = l2n->internal_layout (); + const db::Netlist *nl = l2n->netlist (); + + *mp_stream << "#%l2n-klayout" << endl; + + if (! Keys::is_short ()) { + *mp_stream << endl << "# General section" << endl << endl; + } + + if (version > 0) { + *mp_stream << Keys::version_key << "(" << version << ")" << endl; + } + *mp_stream << Keys::top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl; + *mp_stream << Keys::unit_key << "(" << ly->dbu () << ")" << endl; + + if (! Keys::is_short ()) { + *mp_stream << endl << "# Layer section" << endl; + *mp_stream << "# This section lists the mask layers (drawing or derived) and their connections." << endl; + } + + if (! Keys::is_short ()) { + *mp_stream << endl << "# Mask layers" << endl; + } + for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { + *mp_stream << Keys::layer_key << "(" << name_for_layer (l2n, *l) << ")" << endl; + } + + if (! Keys::is_short ()) { + *mp_stream << endl << "# Mask layer connectivity" << endl; + } + for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { + + db::Connectivity::layer_iterator ce = l2n->connectivity ().end_connected (*l); + db::Connectivity::layer_iterator cb = l2n->connectivity ().begin_connected (*l); + if (cb != ce) { + *mp_stream << Keys::connect_key << "(" << name_for_layer (l2n, *l); + for (db::Connectivity::layer_iterator c = l2n->connectivity ().begin_connected (*l); c != ce; ++c) { + *mp_stream << " " << name_for_layer (l2n, *c); + } + *mp_stream << ")" << endl; + } + + } + + any = false; + for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { + + db::Connectivity::global_nets_iterator ge = l2n->connectivity ().end_global_connections (*l); + db::Connectivity::global_nets_iterator gb = l2n->connectivity ().begin_global_connections (*l); + if (gb != ge) { + if (! any) { + if (! Keys::is_short ()) { + *mp_stream << endl << "# Global nets and connectivity" << endl; + } + any = true; + } + *mp_stream << Keys::global_key << "(" << name_for_layer (l2n, *l); + for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) { + *mp_stream << " " << tl::to_word_or_quoted_string (l2n->connectivity ().global_net_name (*g)); + } + *mp_stream << ")" << endl; + } + + } + + if (nl->begin_device_abstracts () != nl->end_device_abstracts () && ! Keys::is_short ()) { + *mp_stream << endl << "# Device abstracts section" << endl; + *mp_stream << "# Device abstracts list the pin shapes of the devices." << endl; + } + for (db::Netlist::const_abstract_model_iterator m = nl->begin_device_abstracts (); m != nl->end_device_abstracts (); ++m) { + if (m->device_class ()) { + *mp_stream << Keys::device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl; + write (l2n, *m); + *mp_stream << ")" << endl; + } + } + + if (! Keys::is_short ()) { + *mp_stream << endl << "# Circuit section" << endl; + *mp_stream << "# Circuits are the hierarchical building blocks of the netlist." << endl; + } + for (db::Netlist::const_bottom_up_circuit_iterator i = nl->begin_bottom_up (); i != nl->end_bottom_up (); ++i) { + const db::Circuit *x = *i; + *mp_stream << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl; + write (l2n, *x); + *mp_stream << ")" << endl; + } +} + +template +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit) +{ + std::map net2id; + unsigned int id = 0; + + if (circuit.begin_nets () != circuit.end_nets ()) { + if (! Keys::is_short ()) { + *mp_stream << endl << indent1 << "# Nets with their geometries" << endl; + } + for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { + net2id.insert (std::make_pair (n.operator-> (), ++id)); + write (l2n, *n, id); + } + } + + if (circuit.begin_pins () != circuit.end_pins ()) { + if (! Keys::is_short ()) { + *mp_stream << endl << indent1 << "# Outgoing pins and their connections to nets" << endl; + } + for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) { + const db::Net *net = circuit.net_for_pin (p->id ()); + if (net) { + *mp_stream << indent1 << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << net2id [net] << ")" << endl; + } + } + } + + if (circuit.begin_devices () != circuit.end_devices ()) { + if (! Keys::is_short ()) { + *mp_stream << endl << indent1 << "# Devices and their connections" << endl; + } + for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) { + write (l2n, *d, net2id); + } + } + + if (circuit.begin_subcircuits () != circuit.end_subcircuits ()) { + if (! Keys::is_short ()) { + *mp_stream << endl << indent1 << "# Subcircuits and their connections" << endl; + } + for (db::Circuit::const_subcircuit_iterator x = circuit.begin_subcircuits (); x != circuit.end_subcircuits (); ++x) { + write (l2n, *x, net2id); + } + } + + if (! Keys::is_short ()) { + *mp_stream << endl; + } +} + +template +void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr) +{ + db::Coord x = 0, y = 0; + bool first = true; + for (typename T::polygon_contour_iterator c = poly.begin_hull (); c != poly.end_hull (); ++c) { + + typename T::point_type pt = tr * *c; + + stream << " "; + + if (first || pt.x () != x) { + stream << pt.x (); + } else { + stream << "*"; + } + + stream << " "; + + if (first || pt.y () != y) { + stream << pt.y (); + } else { + stream << "*"; + } + + first = false; + x = pt.x (); y = pt.y (); + + } +} + +template +void std_writer_impl::write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname) +{ + db::ICplxTrans t = tr * db::ICplxTrans (s->trans ()); + + const db::Polygon &poly = s->obj (); + if (poly.is_box ()) { + + db::Box box = t * poly.box (); + *mp_stream << Keys::rect_key << "(" << lname; + *mp_stream << " " << box.left () << " " << box.bottom (); + *mp_stream << " " << box.right () << " " << box.top (); + *mp_stream << ")"; + + } else { + + *mp_stream << Keys::polygon_key << "(" << lname; + if (poly.holes () > 0) { + db::SimplePolygon sp (poly); + write_points (*mp_stream, sp, t); + } else { + write_points (*mp_stream, poly, t); + } + *mp_stream << ")"; + + } +} + +template +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id) +{ + if (! l2n->netlist ()) { + throw tl::Exception (tl::to_string (tr ("Can't write annotated netlist before extraction has been done"))); + } + + const db::hier_clusters &clusters = l2n->net_clusters (); + const db::Circuit *circuit = net.circuit (); + const db::Connectivity &conn = l2n->connectivity (); + + bool any = false; + + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + + db::cell_index_type cci = circuit->cell_index (); + db::cell_index_type prev_ci = cci; + + for (db::recursive_cluster_shape_iterator si (clusters, *l, cci, net.cluster_id ()); ! si.at_end (); ) { + + // NOTE: we don't recursive into circuits which will later be output. However, as circuits may + // vanish in "purge" but the clusters will still be there we need to recursive into clusters from + // unknown cells. + db::cell_index_type ci = si.cell_index (); + if (ci != prev_ci && ci != cci && (l2n->netlist ()->circuit_by_cell_index (ci) || l2n->netlist ()->device_abstract_by_cell_index (ci))) { + + si.skip_cell (); + + } else { + + if (! any) { + *mp_stream << indent1 << Keys::net_key << "(" << id; + if (! net.name ().empty ()) { + *mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")"; + } + *mp_stream << endl; + any = true; + } + + *mp_stream << indent2; + write (si.operator-> (), si.trans (), name_for_layer (l2n, *l)); + *mp_stream << endl; + + prev_ci = ci; + + ++si; + + } + + } + + } + + if (any) { + *mp_stream << indent1 << ")" << endl; + } else { + + *mp_stream << indent1 << Keys::net_key << "(" << id; + if (! net.name ().empty ()) { + *mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")"; + } + *mp_stream << ")" << endl; + + } +} + +template +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit, std::map &net2id) +{ + const db::Layout *ly = l2n->internal_layout (); + double dbu = ly->dbu (); + + *mp_stream << indent1 << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.expanded_name ()); + *mp_stream << " " << tl::to_word_or_quoted_string (subcircuit.circuit_ref ()->name ()); + + const db::DCplxTrans &tr = subcircuit.trans (); + if (tr.is_mag ()) { + *mp_stream << " " << Keys::scale_key << "(" << tr.mag () << ")"; + } + if (tr.is_mirror ()) { + *mp_stream << " " << Keys::mirror_key; + } + if (fabs (tr.angle ()) > 1e-6) { + *mp_stream << " " << Keys::rotation_key << "(" << tr.angle () << ")"; + } + *mp_stream << " " << Keys::location_key << "(" << tr.disp ().x () / dbu << " " << tr.disp ().y () / dbu << ")"; + + // each pin in one line for more than a few pins + bool separate_lines = (subcircuit.circuit_ref ()->pin_count () > 1); + + if (separate_lines) { + *mp_stream << endl; + } + + for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) { + const db::Net *net = subcircuit.net_for_pin (p->id ()); + if (net) { + if (separate_lines) { + *mp_stream << indent2; + } else { + *mp_stream << " "; + } + *mp_stream << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << net2id [net] << ")"; + if (separate_lines) { + *mp_stream << endl; + } + } + } + + if (separate_lines) { + *mp_stream << indent1; + } + + *mp_stream << ")" << endl; +} + +template +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::DeviceAbstract &device_abstract) +{ + const std::vector &td = device_abstract.device_class ()->terminal_definitions (); + + const db::hier_clusters &clusters = l2n->net_clusters (); + const db::Connectivity &conn = l2n->connectivity (); + + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + + *mp_stream << indent1 << Keys::terminal_key << "(" << t->name () << endl; + + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + + const db::local_cluster &lc = clusters.clusters_per_cell (device_abstract.cell_index ()).cluster_by_id (device_abstract.cluster_id_for_terminal (t->id ())); + for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { + + *mp_stream << indent2; + write (s.operator-> (), db::ICplxTrans (), name_for_layer (l2n, *l)); + *mp_stream << endl; + + } + + } + + *mp_stream << indent1 << ")" << endl; + + } +} + +template +void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Device &device, std::map &net2id) +{ + const db::Layout *ly = l2n->internal_layout (); + double dbu = ly->dbu (); + + *mp_stream << indent1 << Keys::device_key << "(" << tl::to_word_or_quoted_string (device.expanded_name ()); + + tl_assert (device.device_abstract () != 0); + *mp_stream << " " << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl; + + *mp_stream << indent2 << Keys::location_key << "(" << device.position ().x () / dbu << " " << device.position ().y () / dbu << ")" << endl; + + const std::vector &pd = device.device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + *mp_stream << indent2 << Keys::param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")" << endl; + } + + const std::vector &td = device.device_class ()->terminal_definitions (); + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + const db::Net *net = device.net_for_terminal (i->id ()); + if (net) { + *mp_stream << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << net2id [net] << ")" << endl; + } + } + + *mp_stream << indent1 << ")" << endl; +} + +} + +// ------------------------------------------------------------------------------------------- +// LayoutToNetlistStandardWriter implementation + +LayoutToNetlistStandardWriter::LayoutToNetlistStandardWriter (tl::OutputStream &stream, bool short_version) + : mp_stream (&stream), m_short_version (short_version) +{ + // .. nothing yet .. +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) +{ + if (m_short_version) { + l2n_std_format::std_writer_impl > writer (*mp_stream); + writer.write (l2n); + } else { + l2n_std_format::std_writer_impl > writer (*mp_stream); + writer.write (l2n); + } +} + +} diff --git a/src/db/db/dbLayoutToNetlistWriter.h b/src/db/db/dbLayoutToNetlistWriter.h new file mode 100644 index 000000000..117cf62a3 --- /dev/null +++ b/src/db/db/dbLayoutToNetlistWriter.h @@ -0,0 +1,64 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbLayoutToNetlistWriter +#define HDR_dbLayoutToNetlistWriter + +#include "dbCommon.h" +#include "tlStream.h" + +namespace db +{ + +class LayoutToNetlist; + +/** + * @brief The base class for a LayoutToNetlist writer + */ +class DB_PUBLIC LayoutToNetlistWriterBase +{ +public: + LayoutToNetlistWriterBase () { } + virtual ~LayoutToNetlistWriterBase () { } + + virtual void write (const db::LayoutToNetlist *l2n) = 0; +}; + +/** + * @brief The standard writer + */ +class DB_PUBLIC LayoutToNetlistStandardWriter + : public LayoutToNetlistWriterBase +{ +public: + LayoutToNetlistStandardWriter (tl::OutputStream &stream, bool short_version); + + void write (const db::LayoutToNetlist *l2n); + +private: + tl::OutputStream *mp_stream; + bool m_short_version; +}; + +} + +#endif diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index 503ab35bc..2d3f754a5 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -259,7 +259,8 @@ copy_or_propagate_shapes (db::Layout &target, db::cell_index_type source_parent_cell_index, unsigned int target_layer, unsigned int source_layer, const std::set &all_cells_to_copy, - const std::map &cell_mapping) + const std::map &cell_mapping, + const ShapesTransformer *transformer) { const db::Cell &source_cell = source.cell (source_cell_index); const db::Cell &source_parent_cell = source.cell (source_parent_cell_index); @@ -273,7 +274,7 @@ copy_or_propagate_shapes (db::Layout &target, const db::CellInstArray &cell_inst = p->child_inst ().cell_inst (); for (db::CellInstArray::iterator a = cell_inst.begin (); ! a.at_end (); ++a) { db::ICplxTrans t = db::ICplxTrans (cell_inst.complex_trans (*a)) * propagate_trans; - copy_or_propagate_shapes (target, source, trans, t, pm, source_cell_index, p->parent_cell_index (), target_layer, source_layer, all_cells_to_copy, cell_mapping); + copy_or_propagate_shapes (target, source, trans, t, pm, source_cell_index, p->parent_cell_index (), target_layer, source_layer, all_cells_to_copy, cell_mapping, transformer); } } @@ -282,8 +283,7 @@ copy_or_propagate_shapes (db::Layout &target, } else if (cm->second != DropCell) { db::Cell &target_cell = target.cell (cm->second); - target_cell.shapes (target_layer).insert_transformed (source_cell.shapes (source_layer), trans * propagate_trans, pm); - + transformer->insert_transformed (target_cell.shapes (target_layer), source_cell.shapes (source_layer), trans * propagate_trans, pm); } } @@ -294,7 +294,9 @@ copy_or_move_shapes (db::Layout &target, const std::vector &source_cells, const std::map &cell_mapping, const std::map &layer_mapping, - bool move) + const ShapesTransformer *transformer, + bool move + ) { // collect all called cells and all top level cells std::set all_top_level_cells; @@ -311,7 +313,7 @@ copy_or_move_shapes (db::Layout &target, for (std::set::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) { for (std::map::const_iterator lm = layer_mapping.begin (); lm != layer_mapping.end (); ++lm) { ++progress; - copy_or_propagate_shapes (target, source, trans, db::ICplxTrans (), pm, *c, *c, lm->second, lm->first, all_cells_to_copy, cell_mapping); + copy_or_propagate_shapes (target, source, trans, db::ICplxTrans (), pm, *c, *c, lm->second, lm->first, all_cells_to_copy, cell_mapping, transformer); if (move) { source.cell (*c).shapes (lm->first).clear (); } @@ -319,15 +321,33 @@ copy_or_move_shapes (db::Layout &target, } } +namespace +{ + class StandardShapesTransformer + : public ShapesTransformer + { + public: + void insert_transformed (Shapes &into, const Shapes &from, const ICplxTrans &trans, PropertyMapper &pm) const + { + into.insert_transformed (from, trans, pm); + } + }; +} + void copy_shapes (db::Layout &target, const db::Layout &source, const db::ICplxTrans &trans, const std::vector &source_cells, const std::map &cell_mapping, - const std::map &layer_mapping) + const std::map &layer_mapping, + const ShapesTransformer *transformer) { - copy_or_move_shapes (target, const_cast (source), trans, source_cells, cell_mapping, layer_mapping, false); + StandardShapesTransformer st; + if (! transformer) { + transformer = &st; + } + copy_or_move_shapes (target, const_cast (source), trans, source_cells, cell_mapping, layer_mapping, transformer, false); } void @@ -336,9 +356,14 @@ move_shapes (db::Layout &target, const db::ICplxTrans &trans, const std::vector &source_cells, const std::map &cell_mapping, - const std::map &layer_mapping) + const std::map &layer_mapping, + const ShapesTransformer *transformer) { - copy_or_move_shapes (target, source, trans, source_cells, cell_mapping, layer_mapping, true); + StandardShapesTransformer st; + if (! transformer) { + transformer = &st; + } + copy_or_move_shapes (target, source, trans, source_cells, cell_mapping, layer_mapping, transformer, true); } // ------------------------------------------------------------ diff --git a/src/db/db/dbLayoutUtils.h b/src/db/db/dbLayoutUtils.h index ae27790f2..374413772 100644 --- a/src/db/db/dbLayoutUtils.h +++ b/src/db/db/dbLayoutUtils.h @@ -140,6 +140,21 @@ merge_layouts (db::Layout &target, const db::Layout &source, const db::ICplxTran const std::map &layer_mapping, std::map *final_cell_mapping = 0); +/** + * @brief An interface for the shape inserter + * + * This interface is used by copy_shapes and move_shapes to insert + * a shape collection into a another one. By reimplementing this interface, + * more shape transformations can be provided. + */ +class DB_PUBLIC ShapesTransformer +{ +public: + ShapesTransformer () { } + virtual ~ShapesTransformer () { } + virtual void insert_transformed (db::Shapes &into, const db::Shapes &from, const db::ICplxTrans &trans, db::PropertyMapper &pm) const = 0; +}; + /** * @brief Copy shapes from one layout to another * @@ -155,7 +170,8 @@ copy_shapes (db::Layout &target, const db::ICplxTrans &trans, const std::vector &source_cells, const std::map &cell_mapping, - const std::map &layer_mapping); + const std::map &layer_mapping, + const ShapesTransformer *transformer = 0); /** * @brief Move shapes from one layout to another @@ -172,7 +188,8 @@ move_shapes (db::Layout &target, const db::ICplxTrans &trans, const std::vector &source_cells, const std::map &cell_mapping, - const std::map &layer_mapping); + const std::map &layer_mapping, + const ShapesTransformer *transformer = 0); /** * @brief Find an example cell instance from a child to a top cell diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc new file mode 100644 index 000000000..b219d5ca4 --- /dev/null +++ b/src/db/db/dbLocalOperation.cc @@ -0,0 +1,310 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbHierProcessor.h" +#include "dbBoxScanner.h" +#include "dbRecursiveShapeIterator.h" +#include "dbBoxConvert.h" +#include "dbEdgeProcessor.h" +#include "dbPolygonGenerators.h" +#include "dbPolygonTools.h" +#include "dbLocalOperationUtils.h" +#include "dbEdgeBoolean.h" +#include "tlLog.h" +#include "tlTimer.h" +#include "tlInternational.h" + +namespace db +{ + +// --------------------------------------------------------------------------------------------- +// BoolAndOrNotLocalOperation implementation + +BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and) + : m_is_and (is_and) +{ + // .. nothing yet .. +} + +local_operation::on_empty_intruder_mode +BoolAndOrNotLocalOperation::on_empty_intruder_hint () const +{ + return m_is_and ? local_operation::Drop : local_operation::Copy; +} + +std::string +BoolAndOrNotLocalOperation::description () const +{ + return m_is_and ? tl::to_string (tr ("AND operation")) : tl::to_string (tr ("NOT operation")); +} + +void +BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const shape_interactions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const +{ + db::EdgeProcessor ep; + + size_t p1 = 0, p2 = 1; + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j)); + } + } + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + const db::PolygonRef &subject = interactions.subject_shape (i->first); + if (others.find (subject) != others.end ()) { + if (m_is_and) { + result.insert (subject); + } + } else if (i->second.empty ()) { + // shortcut (not: keep, and: drop) + if (! m_is_and) { + result.insert (subject); + } + } else { + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + } + + if (! others.empty () || p1 > 0) { + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); + db::PolygonRefGenerator pr (layout, result); + db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); + db::PolygonGenerator pg (splitter, true, true); + ep.set_base_verbosity (50); + ep.process (pg, op); + + } +} + +// --------------------------------------------------------------------------------------------- + +SelfOverlapMergeLocalOperation::SelfOverlapMergeLocalOperation (unsigned int wrap_count) + : m_wrap_count (wrap_count) +{ + // .. nothing yet .. +} + +void SelfOverlapMergeLocalOperation::compute_local (db::Layout *layout, const shape_interactions &interactions, std::unordered_set &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + if (m_wrap_count == 0) { + return; + } + + db::EdgeProcessor ep; + + size_t p1 = 0, p2 = 1; + std::set seen; + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + if (seen.find (i->first) == seen.end ()) { + seen.insert (i->first); + const db::PolygonRef &subject = interactions.subject_shape (i->first); + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + for (db::shape_interactions::iterator2 o = i->second.begin (); o != i->second.end (); ++o) { + // don't take the same (really the same, not an identical one) shape twice - the interaction + // set does not take care to list just one copy of the same item on the intruder side. + if (seen.find (*o) == seen.end ()) { + seen.insert (*o); + const db::PolygonRef &intruder = interactions.intruder_shape (*o); + for (db::PolygonRef::polygon_edge_iterator e = intruder.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + } + + } + + db::MergeOp op (m_wrap_count - 1); + db::PolygonRefGenerator pr (layout, result); + db::PolygonGenerator pg (pr, true, true); + ep.set_base_verbosity (50); + ep.process (pg, op); +} + +SelfOverlapMergeLocalOperation::on_empty_intruder_mode SelfOverlapMergeLocalOperation::on_empty_intruder_hint () const +{ + return m_wrap_count > 1 ? local_operation::Drop : local_operation::Copy; +} + +std::string SelfOverlapMergeLocalOperation::description () const +{ + return tl::sprintf (tl::to_string (tr ("Self-overlap (wrap count %d)")), int (m_wrap_count)); +} + +// --------------------------------------------------------------------------------------------- +// EdgeBoolAndOrNotLocalOperation implementation + +EdgeBoolAndOrNotLocalOperation::EdgeBoolAndOrNotLocalOperation (bool is_and) + : m_is_and (is_and) +{ + // .. nothing yet .. +} + +local_operation::on_empty_intruder_mode +EdgeBoolAndOrNotLocalOperation::on_empty_intruder_hint () const +{ + return m_is_and ? local_operation::Drop : local_operation::Copy; +} + +std::string +EdgeBoolAndOrNotLocalOperation::description () const +{ + return m_is_and ? tl::to_string (tr ("Edge AND operation")) : tl::to_string (tr ("Edge NOT operation")); +} + +void +EdgeBoolAndOrNotLocalOperation::compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::unordered_set &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + EdgeBooleanClusterCollector > cluster_collector (&result, m_is_and ? EdgeAnd : EdgeNot); + + db::box_scanner scanner; + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j)); + } + } + + bool any_subject = false; + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + const db::Edge &subject = interactions.subject_shape (i->first); + if (others.find (subject) != others.end ()) { + if (m_is_and) { + result.insert (subject); + } + } else if (i->second.empty ()) { + // shortcut (not: keep, and: drop) + if (! m_is_and) { + result.insert (subject); + } + } else { + scanner.insert (&subject, 0); + any_subject = true; + } + + } + + if (! others.empty () || any_subject) { + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + scanner.insert (o.operator-> (), 1); + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + } +} + +// --------------------------------------------------------------------------------------------- +// EdgeToPolygonLocalOperation implementation + +EdgeToPolygonLocalOperation::EdgeToPolygonLocalOperation (bool outside, bool include_borders) + : m_outside (outside), m_include_borders (include_borders) +{ + // .. nothing yet .. +} + +local_operation::on_empty_intruder_mode +EdgeToPolygonLocalOperation::on_empty_intruder_hint () const +{ + return m_outside ? local_operation::Copy : local_operation::Drop; +} + +std::string +EdgeToPolygonLocalOperation::description () const +{ + return tl::to_string (m_outside ? tr ("Edge to polygon AND/INSIDE") : tr ("Edge to polygons NOT/OUTSIDE")); +} + +void +EdgeToPolygonLocalOperation::compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::unordered_set &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + db::EdgeProcessor ep; + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j)); + } + } + + bool any_subject = false; + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + const db::Edge &subject = interactions.subject_shape (i->first); + if (i->second.empty ()) { + // shortcut (outside: keep, otherwise: drop) + if (m_outside) { + result.insert (subject); + } + } else { + ep.insert (subject, 1); + any_subject = true; + } + + } + + if (! others.empty () || any_subject) { + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, 0); + } + } + + db::EdgeToEdgeSetGenerator cc (result); + db::EdgePolygonOp op (m_outside, m_include_borders); + ep.process (cc, op); + + } +} + +} + diff --git a/src/db/db/dbLocalOperation.h b/src/db/db/dbLocalOperation.h new file mode 100644 index 000000000..499704b29 --- /dev/null +++ b/src/db/db/dbLocalOperation.h @@ -0,0 +1,194 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + + +#ifndef HDR_dbLocalOperation +#define HDR_dbLocalOperation + +#include "dbCommon.h" + +#include "dbLayout.h" + +#include +#include +#include + +namespace db +{ + +template class shape_interactions; + +/** + * @brief A base class for "local operations" + * A local operation is any operation whose result can be computed by + * combining the results derived from individual shape pairs. + * The shape pairs can originate from different or the same layer. + * If the layers are different, one layer is the subject layer, the + * other layer is the "intruder" layer. Subject shapes are always + * considered, intruder shapes only if they interact with subject shapes. + * This class implements the actual operation. It receives a + * cluster of subject shapes vs. corresponding intruder shapes. + */ +template +class DB_PUBLIC local_operation +{ +public: + /** + * @brief Indicates the desired behaviour for subject shapes for which there is no intruder + */ + enum on_empty_intruder_mode { + /** + * @brief Don't imply a specific behaviour + */ + Ignore = 0, + + /** + * @brief Copy the subject shape + */ + Copy, + + /** + * @brief Drop the subject shape + */ + Drop + }; + + /** + * @brief Constructor + */ + local_operation () { } + + /** + * @brief Destructor + */ + virtual ~local_operation () { } + + /** + * @brief Computes the results from a given set of interacting shapes + * @param layout The layout to which the shapes belong + * @param interactions The interaction set + * @param result The container to which the results are written + */ + virtual void compute_local (db::Layout *layout, const shape_interactions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const = 0; + + /** + * @brief Indicates the desired behaviour when a shape does not have an intruder + */ + virtual on_empty_intruder_mode on_empty_intruder_hint () const { return Ignore; } + + /** + * @brief Gets a description text for this operation + */ + virtual std::string description () const = 0; + + /** + * @brief Gets the interaction distance + * A distance of means the shapes must overlap in order to interact. + */ + virtual db::Coord dist () const { return 0; } +}; + +/** + * @brief Implements a boolean AND or NOT operation + */ +class DB_PUBLIC BoolAndOrNotLocalOperation + : public local_operation +{ +public: + BoolAndOrNotLocalOperation (bool is_and); + + virtual void compute_local (db::Layout *layout, const shape_interactions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const; + virtual on_empty_intruder_mode on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + bool m_is_and; +}; + +/** + * @brief Implements a merge operation with an overlap count + * With a given wrap_count, the result will only contains shapes where + * the original shapes overlap at least "wrap_count" times. + */ +class DB_PUBLIC SelfOverlapMergeLocalOperation + : public local_operation +{ +public: + SelfOverlapMergeLocalOperation (unsigned int wrap_count); + + virtual void compute_local (db::Layout *layout, const shape_interactions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const; + virtual on_empty_intruder_mode on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + unsigned int m_wrap_count; +}; + +/** + * @brief Implements a boolean AND or NOT operation between edges + */ +class DB_PUBLIC EdgeBoolAndOrNotLocalOperation + : public local_operation +{ +public: + EdgeBoolAndOrNotLocalOperation (bool is_and); + + virtual void compute_local (db::Layout *layout, const shape_interactions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const; + virtual on_empty_intruder_mode on_empty_intruder_hint () const; + virtual std::string description () const; + + // edge interaction distance is 1 to force overlap between edges and edge/boxes + virtual db::Coord dist () const { return 1; } + +private: + bool m_is_and; +}; + +/** + * @brief Implements a boolean AND or NOT operation between edges and polygons (polygons as intruders) + * + * "AND" is implemented by "outside == false", "NOT" by "outside == true" with "include_borders == true". + * With "include_borders == false" the operations are "INSIDE" and "OUTSIDE". + */ +class DB_PUBLIC EdgeToPolygonLocalOperation + : public local_operation +{ +public: + EdgeToPolygonLocalOperation (bool outside, bool include_borders); + + virtual void compute_local (db::Layout *layout, const shape_interactions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const; + virtual on_empty_intruder_mode on_empty_intruder_hint () const; + virtual std::string description () const; + + // edge interaction distance is 1 to force overlap between edges and edge/boxes + virtual db::Coord dist () const { return m_include_borders ? 1 : 0; } + +private: + bool m_outside; + bool m_include_borders; +}; + +} + +#endif + diff --git a/src/db/db/dbLocalOperationUtils.cc b/src/db/db/dbLocalOperationUtils.cc new file mode 100644 index 000000000..4c478743e --- /dev/null +++ b/src/db/db/dbLocalOperationUtils.cc @@ -0,0 +1,99 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbLocalOperationUtils.h" +#include "dbPolygonTools.h" + +namespace db +{ + +// ----------------------------------------------------------------------------------------------- +// class PolygonRefGenerator + +PolygonRefGenerator::PolygonRefGenerator (db::Layout *layout, std::unordered_set &polyrefs) + : PolygonSink (), mp_layout (layout), mp_polyrefs (&polyrefs) +{ + // .. nothing yet .. +} + +void PolygonRefGenerator::put (const db::Polygon &polygon) +{ + tl::MutexLocker locker (&mp_layout->lock ()); + mp_polyrefs->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); +} + +// ----------------------------------------------------------------------------------------------- +// class EdgeToEdgeSetGenerator + +EdgeToEdgeSetGenerator::EdgeToEdgeSetGenerator (std::unordered_set &edges) + : mp_edges (&edges) +{ + // .. nothing yet .. +} + +void EdgeToEdgeSetGenerator::put (const db::Edge &edge) +{ + mp_edges->insert (edge); +} + +// ----------------------------------------------------------------------------------------------- +// class PolygonRefGenerator + +PolygonRefToShapesGenerator::PolygonRefToShapesGenerator (db::Layout *layout, db::Shapes *shapes) + : PolygonSink (), mp_layout (layout), mp_shapes (shapes) +{ + // .. nothing yet .. +} + +void PolygonRefToShapesGenerator::put (const db::Polygon &polygon) +{ + tl::MutexLocker locker (&mp_layout->lock ()); + mp_shapes->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); +} + +// ----------------------------------------------------------------------------------------------- +// class PolygonSplitter + +PolygonSplitter::PolygonSplitter (PolygonSink &sink, double max_area_ratio, size_t max_vertex_count) + : mp_sink (&sink), m_max_area_ratio (max_area_ratio), m_max_vertex_count (max_vertex_count) +{ + // .. nothing yet .. +} + +void +PolygonSplitter::put (const db::Polygon &poly) +{ + if ((m_max_vertex_count > 0 && poly.vertices () > m_max_vertex_count) || (m_max_area_ratio > 0.0 && poly.area_ratio () > m_max_area_ratio)) { + + std::vector split_polygons; + db::split_polygon (poly, split_polygons); + for (std::vector ::const_iterator sp = split_polygons.begin (); sp != split_polygons.end (); ++sp) { + put (*sp); + } + + } else { + mp_sink->put (poly); + } +} + +} diff --git a/src/db/db/dbLocalOperationUtils.h b/src/db/db/dbLocalOperationUtils.h new file mode 100644 index 000000000..6342f3ccc --- /dev/null +++ b/src/db/db/dbLocalOperationUtils.h @@ -0,0 +1,142 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + + +#ifndef HDR_dbLocalOperationUtils +#define HDR_dbLocalOperationUtils + +#include "dbCommon.h" + +#include "dbLayout.h" +#include "dbPolygonGenerators.h" +#include "dbHash.h" + +#include + +namespace db +{ + +template +class polygon_transformation_filter + : public PolygonSink +{ +public: + /** + * @brief Constructor specifying an external vector for storing the polygons + */ + polygon_transformation_filter (PolygonSink *output, const Trans &tr) + : mp_output (output), m_trans (tr) + { + // .. nothing yet .. + } + + /** + * @brief Implementation of the PolygonSink interface + */ + virtual void put (const db::Polygon &polygon) + { + mp_output->put (polygon.transformed (m_trans)); + } + +private: + db::PolygonSink *mp_output; + const Trans m_trans; +}; + +class PolygonRefGenerator + : public PolygonSink +{ +public: + /** + * @brief Constructor + */ + PolygonRefGenerator (db::Layout *layout, std::unordered_set &polyrefs); + + /** + * @brief Implementation of the PolygonSink interface + */ + virtual void put (const db::Polygon &polygon); + +private: + db::Layout *mp_layout; + std::unordered_set *mp_polyrefs; +}; + +class EdgeToEdgeSetGenerator + : public EdgeSink +{ +public: + /** + * @brief Constructor + */ + EdgeToEdgeSetGenerator (std::unordered_set &edges); + + /** + * @brief Implementation of the PolygonSink interface + */ + virtual void put (const db::Edge &edge); + +private: + std::unordered_set *mp_edges; +}; + +class PolygonRefToShapesGenerator + : public PolygonSink +{ +public: + /** + * @brief Constructor specifying an external vector for storing the polygons + */ + PolygonRefToShapesGenerator (db::Layout *layout, db::Shapes *shapes); + + /** + * @brief Implementation of the PolygonSink interface + */ + virtual void put (const db::Polygon &polygon); + +private: + db::Layout *mp_layout; + db::Shapes *mp_shapes; +}; + +class PolygonSplitter + : public PolygonSink +{ +public: + PolygonSplitter (PolygonSink &sink, double max_area_ratio, size_t max_vertex_count); + + virtual void put (const db::Polygon &poly); + + virtual void start () { mp_sink->start (); } + virtual void flush () { mp_sink->flush (); } + +private: + PolygonSink *mp_sink; + double m_max_area_ratio; + size_t m_max_vertex_count; +}; + +} + +#endif + diff --git a/src/db/db/dbNet.cc b/src/db/db/dbNet.cc new file mode 100644 index 000000000..69f085817 --- /dev/null +++ b/src/db/db/dbNet.cc @@ -0,0 +1,324 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbNet.h" +#include "dbDevice.h" +#include "dbDeviceClass.h" +#include "dbCircuit.h" +#include "dbSubCircuit.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// NetTerminalRef class implementation + +NetTerminalRef::NetTerminalRef () + : m_terminal_id (0), mp_device (0), mp_net (0) +{ + // .. nothing yet .. +} + +NetTerminalRef::NetTerminalRef (Device *device, size_t terminal_id) + : m_terminal_id (terminal_id), mp_device (device), mp_net (0) +{ + // .. nothing yet .. +} + +NetTerminalRef::NetTerminalRef (const NetTerminalRef &other) + : m_terminal_id (other.m_terminal_id), mp_device (other.mp_device), mp_net (0) +{ + // .. nothing yet .. +} + +NetTerminalRef &NetTerminalRef::operator= (const NetTerminalRef &other) +{ + if (this != &other) { + m_terminal_id = other.m_terminal_id; + mp_device = other.mp_device; + } + return *this; +} + +const DeviceTerminalDefinition * +NetTerminalRef::terminal_def () const +{ + const DeviceClass *dc = device_class (); + if (dc) { + return dc->terminal_definition (m_terminal_id); + } else { + return 0; + } +} + +const DeviceClass * +NetTerminalRef::device_class () const +{ + return mp_device ? mp_device->device_class () : 0; +} + +// -------------------------------------------------------------------------------- +// NetPinRef class implementation + +NetPinRef::NetPinRef () + : m_pin_id (0), mp_net (0) +{ + // .. nothing yet .. +} + +NetPinRef::NetPinRef (size_t pin_id) + : m_pin_id (pin_id), mp_net (0) +{ + // .. nothing yet .. +} + +NetPinRef::NetPinRef (const NetPinRef &other) + : m_pin_id (other.m_pin_id), mp_net (0) +{ + // .. nothing yet .. +} + +NetPinRef &NetPinRef::operator= (const NetPinRef &other) +{ + if (this != &other) { + m_pin_id = other.m_pin_id; + } + return *this; +} + +const Pin *NetPinRef::pin () const +{ + if (mp_net && mp_net->circuit ()) { + return mp_net->circuit ()->pin_by_id (m_pin_id); + } + return 0; +} + +// -------------------------------------------------------------------------------- +// NetSubcircuitPinRef class implementation + +NetSubcircuitPinRef::NetSubcircuitPinRef () + : m_pin_id (0), mp_subcircuit (0), mp_net (0) +{ + // .. nothing yet .. +} + +NetSubcircuitPinRef::NetSubcircuitPinRef (SubCircuit *circuit, size_t pin_id) + : m_pin_id (pin_id), mp_subcircuit (circuit), mp_net (0) +{ + // .. nothing yet .. +} + +NetSubcircuitPinRef::NetSubcircuitPinRef (const NetSubcircuitPinRef &other) + : m_pin_id (other.m_pin_id), mp_subcircuit (other.mp_subcircuit), mp_net (0) +{ + // .. nothing yet .. +} + +NetSubcircuitPinRef &NetSubcircuitPinRef::operator= (const NetSubcircuitPinRef &other) +{ + if (this != &other) { + m_pin_id = other.m_pin_id; + mp_subcircuit = other.mp_subcircuit; + } + return *this; +} + +const Pin *NetSubcircuitPinRef::pin () const +{ + if (mp_subcircuit && mp_subcircuit->circuit_ref ()) { + return mp_subcircuit->circuit_ref ()->pin_by_id (m_pin_id); + } + return 0; +} + +// -------------------------------------------------------------------------------- +// Net class implementation + +Net::Net () + : m_cluster_id (0), mp_circuit (0) +{ + // .. nothing yet .. +} + +Net::Net (const std::string &name) + : m_cluster_id (0), mp_circuit (0) +{ + m_name = name; +} + +Net::Net (const Net &other) + : tl::Object (other), m_cluster_id (0), mp_circuit (0) +{ + operator= (other); +} + +Net &Net::operator= (const Net &other) +{ + if (this != &other) { + + clear (); + + m_name = other.m_name; + m_cluster_id = other.m_cluster_id; + + for (const_subcircuit_pin_iterator i = other.begin_subcircuit_pins (); i != other.end_subcircuit_pins (); ++i) { + add_subcircuit_pin (*i); + } + + for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) { + add_pin (*i); + } + + for (const_terminal_iterator i = other.begin_terminals (); i != other.end_terminals (); ++i) { + add_terminal (*i); + } + + } + return *this; +} + +Net::~Net () +{ + clear (); +} + +void Net::clear () +{ + m_name.clear (); + m_cluster_id = 0; + + while (! m_terminals.empty ()) { + erase_terminal (begin_terminals ()); + } + + while (! m_pins.empty ()) { + erase_pin (begin_pins ()); + } + + while (! m_subcircuit_pins.empty ()) { + erase_subcircuit_pin (begin_subcircuit_pins ()); + } +} + +void Net::set_name (const std::string &name) +{ + m_name = name; + if (mp_circuit) { + mp_circuit->m_net_by_name.invalidate (); + } +} + +std::string Net::qname () const +{ + if (circuit ()) { + return circuit ()->name () + ":" + expanded_name (); + } else { + return expanded_name (); + } +} + +std::string Net::expanded_name () const +{ + if (name ().empty ()) { + if (cluster_id () > std::numeric_limits::max () / 2) { + // avoid printing huge ID numbers for internal cluster IDs + return "$I" + tl::to_string ((std::numeric_limits::max () - cluster_id ()) + 1); + } else { + return "$" + tl::to_string (cluster_id ()); + } + } else { + return name (); + } +} + +void Net::set_cluster_id (size_t ci) +{ + m_cluster_id = ci; + if (mp_circuit) { + mp_circuit->m_net_by_cluster_id.invalidate (); + } +} + +void Net::add_pin (const NetPinRef &pin) +{ + m_pins.push_back (pin); + NetPinRef &new_pin = m_pins.back (); + new_pin.set_net (this); + + if (mp_circuit) { + mp_circuit->set_pin_ref_for_pin (new_pin.pin_id (), --m_pins.end ()); + } +} + +void Net::add_subcircuit_pin (const NetSubcircuitPinRef &pin) +{ + m_subcircuit_pins.push_back (pin); + NetSubcircuitPinRef &new_pin = m_subcircuit_pins.back (); + new_pin.set_net (this); + + tl_assert (pin.subcircuit () != 0); + new_pin.subcircuit ()->set_pin_ref_for_pin (new_pin.pin_id (), --m_subcircuit_pins.end ()); +} + +void Net::erase_pin (pin_iterator iter) +{ + if (mp_circuit) { + mp_circuit->set_pin_ref_for_pin (iter->pin_id (), pin_iterator ()); + } + m_pins.erase (iter); +} + +void Net::erase_subcircuit_pin (subcircuit_pin_iterator iter) +{ + if (iter->subcircuit ()) { + iter->subcircuit ()->set_pin_ref_for_pin (iter->pin_id (), subcircuit_pin_iterator ()); + } + m_subcircuit_pins.erase (iter); +} + +void Net::add_terminal (const NetTerminalRef &terminal) +{ + if (! terminal.device ()) { + return; + } + + m_terminals.push_back (terminal); + NetTerminalRef &new_terminal = m_terminals.back (); + new_terminal.set_net (this); + new_terminal.device ()->set_terminal_ref_for_terminal (new_terminal.terminal_id (), --m_terminals.end ()); +} + +void Net::erase_terminal (terminal_iterator iter) +{ + if (iter->device ()) { + iter->device ()->set_terminal_ref_for_terminal (iter->terminal_id (), terminal_iterator ()); + } + m_terminals.erase (iter); +} + +void Net::set_circuit (Circuit *circuit) +{ + mp_circuit = circuit; +} + +} diff --git a/src/db/db/dbNet.h b/src/db/db/dbNet.h new file mode 100644 index 000000000..6baa895b9 --- /dev/null +++ b/src/db/db/dbNet.h @@ -0,0 +1,651 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbNet +#define _HDR_dbNet + +#include "dbCommon.h" + +#include "tlObject.h" + +#include +#include + +namespace db +{ + +class Device; +class Net; +class SubCircuit; +class Circuit; +class DeviceTerminalDefinition; +class DeviceClass; +class Pin; + +/** + * @brief A reference to a terminal of a device + * + * A terminal must always refer to a device inside the current circuit. + */ +class DB_PUBLIC NetTerminalRef +{ +public: + /** + * @brief Default constructor + */ + NetTerminalRef (); + + /** + * @brief Creates a pin reference to the given pin of the current circuit + */ + NetTerminalRef (Device *device, size_t terminal_id); + + /** + * @brief Copy constructor + */ + NetTerminalRef (const NetTerminalRef &other); + + /** + * @brief Assignment + */ + NetTerminalRef &operator= (const NetTerminalRef &other); + + /** + * @brief Comparison + */ + bool operator< (const NetTerminalRef &other) const + { + if (mp_device != other.mp_device) { + return mp_device < other.mp_device; + } + return m_terminal_id < other.m_terminal_id; + } + + /** + * @brief Equality + */ + bool operator== (const NetTerminalRef &other) const + { + return (mp_device == other.mp_device && m_terminal_id == other.m_terminal_id); + } + + /** + * @brief Gets the device reference + */ + Device *device () + { + return mp_device; + } + + /** + * @brief Gets the device reference (const version) + */ + const Device *device () const + { + return mp_device; + } + + /** + * @brief Gets the terminal index + */ + size_t terminal_id () const + { + return m_terminal_id; + } + + /** + * @brief Gets the terminal definition + * + * Returns 0 if the terminal is not a valid terminal reference. + */ + const DeviceTerminalDefinition *terminal_def () const; + + /** + * @brief Returns the device class + */ + const DeviceClass *device_class () const; + + /** + * @brief Gets the net the terminal lives in + */ + Net *net () + { + return mp_net; + } + + /** + * @brief Gets the net the terminal lives in (const version) + */ + const Net *net () const + { + return mp_net; + } + +private: + friend class Net; + + size_t m_terminal_id; + Device *mp_device; + Net *mp_net; + + /** + * @brief Sets the net the terminal lives in + */ + void set_net (Net *net) + { + mp_net = net; + } +}; + +/** + * @brief A reference to a pin inside a net + * + * This object describes a connection to an outgoing pin. + */ +class DB_PUBLIC NetPinRef +{ +public: + /** + * @brief Default constructor + */ + NetPinRef (); + + /** + * @brief Creates a pin reference to the given pin of the current circuit + */ + NetPinRef (size_t pin_id); + + /** + * @brief Copy constructor + */ + NetPinRef (const NetPinRef &other); + + /** + * @brief Assignment + */ + NetPinRef &operator= (const NetPinRef &other); + + /** + * @brief Comparison + */ + bool operator< (const NetPinRef &other) const + { + return m_pin_id < other.m_pin_id; + } + + /** + * @brief Equality + */ + bool operator== (const NetPinRef &other) const + { + return (m_pin_id == other.m_pin_id); + } + + /** + * @brief Gets the pin reference (const version) + */ + size_t pin_id () const + { + return m_pin_id; + } + + /** + * @brief Gets the pin reference from the pin id + * If the pin cannot be resolved, null is returned. + */ + const Pin *pin () const; + + /** + * @brief Gets the net the pin lives in + */ + Net *net () + { + return mp_net; + } + + /** + * @brief Gets the net the pin lives in (const version) + */ + const Net *net () const + { + return mp_net; + } + +private: + friend class Net; + + size_t m_pin_id; + Net *mp_net; + + /** + * @brief Sets the net the terminal lives in + */ + void set_net (Net *net) + { + mp_net = net; + } +}; + +/** + * @brief A reference to a pin inside a net + * + * This object describes a connection to a pin of a subcircuit. + */ +class DB_PUBLIC NetSubcircuitPinRef +{ +public: + /** + * @brief Default constructor + */ + NetSubcircuitPinRef (); + + /** + * @brief Creates a pin reference to the given pin of the given subcircuit + */ + NetSubcircuitPinRef (SubCircuit *circuit, size_t pin_id); + + /** + * @brief Copy constructor + */ + NetSubcircuitPinRef (const NetSubcircuitPinRef &other); + + /** + * @brief Assignment + */ + NetSubcircuitPinRef &operator= (const NetSubcircuitPinRef &other); + + /** + * @brief Comparison + */ + bool operator< (const NetSubcircuitPinRef &other) const + { + if (mp_subcircuit != other.mp_subcircuit) { + return mp_subcircuit < other.mp_subcircuit; + } + return m_pin_id < other.m_pin_id; + } + + /** + * @brief Equality + */ + bool operator== (const NetSubcircuitPinRef &other) const + { + return (mp_subcircuit == other.mp_subcircuit && m_pin_id == other.m_pin_id); + } + + /** + * @brief Gets the pin reference (const version) + */ + size_t pin_id () const + { + return m_pin_id; + } + + /** + * @brief Gets the pin reference from the pin id + * If the pin cannot be resolved, null is returned. + */ + const Pin *pin () const; + + /** + * @brief Gets the subcircuit reference + */ + SubCircuit *subcircuit () + { + return mp_subcircuit; + } + + /** + * @brief Gets the subcircuit reference (const version) + */ + const SubCircuit *subcircuit () const + { + return mp_subcircuit; + } + + /** + * @brief Gets the net the pin lives in + */ + Net *net () + { + return mp_net; + } + + /** + * @brief Gets the net the pin lives in (const version) + */ + const Net *net () const + { + return mp_net; + } + +private: + friend class Net; + + size_t m_pin_id; + SubCircuit *mp_subcircuit; + Net *mp_net; + + /** + * @brief Sets the net the terminal lives in + */ + void set_net (Net *net) + { + mp_net = net; + } +}; + +/** + * @brief A net + * + * A net connects terminals of devices and pins or circuits + */ +class DB_PUBLIC Net + : public tl::Object +{ +public: + typedef std::list terminal_list; + typedef terminal_list::const_iterator const_terminal_iterator; + typedef terminal_list::iterator terminal_iterator; + typedef std::list pin_list; + typedef pin_list::const_iterator const_pin_iterator; + typedef pin_list::iterator pin_iterator; + typedef std::list subcircuit_pin_list; + typedef subcircuit_pin_list::const_iterator const_subcircuit_pin_iterator; + typedef subcircuit_pin_list::iterator subcircuit_pin_iterator; + + /** + * @brief Constructor + * Creates an empty circuit. + */ + Net (); + + /** + * @brief Creates a empty net with the give name + */ + Net (const std::string &name); + + /** + * @brief Copy constructor + */ + Net (const Net &other); + + /** + * @brief Destructor + */ + ~Net (); + + /** + * @brief Assignment + */ + Net &operator= (const Net &other); + + /** + * @brief Gets the circuit the net lives in + * This pointer is 0 if the net is not part of a circuit. + */ + Circuit *circuit () + { + return mp_circuit; + } + + /** + * @brief Gets the circuit the net lives in (const version) + * This pointer is 0 if the net is not part of a circuit. + */ + const Circuit *circuit () const + { + return mp_circuit; + } + + /** + * @brief Clears the circuit + */ + void clear (); + + /** + * @brief Sets the name of the circuit + */ + void set_name (const std::string &name); + + /** + * @brief Gets the name of the circuit + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Gets the expanded name + * + * The "expanded name" is a non-empty name for the net. It uses the + * cluster ID if no name is set. + */ + std::string expanded_name () const; + + /** + * @brief Gets the qualified name + * + * The qualified name is like the expanded name, but preceeded with the + * Circuit name if known (e.g. "CIRCUIT:NET") + */ + std::string qname () const; + + /** + * @brief Sets the cluster ID of this net + * + * The cluster ID links the net to a cluster from the + * hierarchical layout netlist extractor. + */ + void set_cluster_id (size_t ci); + + /** + * @brief Gets the cluster ID + */ + size_t cluster_id () const + { + return m_cluster_id; + } + + /** + * @brief Adds a pin to this net + */ + void add_pin (const NetPinRef &pin); + + /** + * @brief Erases the given pin from this net + */ + void erase_pin (pin_iterator iter); + + /** + * @brief Begin iterator for the pins of the net (const version) + */ + const_pin_iterator begin_pins () const + { + return m_pins.begin (); + } + + /** + * @brief End iterator for the pins of the net (const version) + */ + const_pin_iterator end_pins () const + { + return m_pins.end (); + } + + /** + * @brief Begin iterator for the pins of the net (non-const version) + */ + pin_iterator begin_pins () + { + return m_pins.begin (); + } + + /** + * @brief End iterator for the pins of the net (non-const version) + */ + pin_iterator end_pins () + { + return m_pins.end (); + } + + /** + * @brief Adds a subcircuit pin to this net + */ + void add_subcircuit_pin (const NetSubcircuitPinRef &pin); + + /** + * @brief Erases the given subcircuit pin from this net + */ + void erase_subcircuit_pin (subcircuit_pin_iterator iter); + + /** + * @brief Begin iterator for the pins of the net (const version) + */ + const_subcircuit_pin_iterator begin_subcircuit_pins () const + { + return m_subcircuit_pins.begin (); + } + + /** + * @brief End iterator for the pins of the net (const version) + */ + const_subcircuit_pin_iterator end_subcircuit_pins () const + { + return m_subcircuit_pins.end (); + } + + /** + * @brief Begin iterator for the pins of the net (non-const version) + */ + subcircuit_pin_iterator begin_subcircuit_pins () + { + return m_subcircuit_pins.begin (); + } + + /** + * @brief End iterator for the pins of the net (non-const version) + */ + subcircuit_pin_iterator end_subcircuit_pins () + { + return m_subcircuit_pins.end (); + } + + /** + * @brief Adds a terminal to this net + */ + void add_terminal (const NetTerminalRef &terminal); + + /** + * @brief Erases the given terminal from this net + */ + void erase_terminal (terminal_iterator iter); + + /** + * @brief Begin iterator for the terminals of the net (const version) + */ + const_terminal_iterator begin_terminals () const + { + return m_terminals.begin (); + } + + /** + * @brief End iterator for the terminals of the net (const version) + */ + const_terminal_iterator end_terminals () const + { + return m_terminals.end (); + } + + /** + * @brief Begin iterator for the terminals of the net (non-const version) + */ + terminal_iterator begin_terminals () + { + return m_terminals.begin (); + } + + /** + * @brief End iterator for the terminals of the net (non-const version) + */ + terminal_iterator end_terminals () + { + return m_terminals.end (); + } + + /** + * @brief Returns true, if the net is floating (has no or only a single connection) + */ + bool is_floating () const + { + return (m_pins.size () + m_subcircuit_pins.size () + m_terminals.size ()) < 2; + } + + /** + * @brief Returns true, if the net is an internal node (connects two terminals only) + */ + bool is_internal () const + { + return m_pins.size () == 0 && m_subcircuit_pins.size () == 0 && m_terminals.size () == 2; + } + + /** + * @brief Returns the number of outgoing pins connected + */ + size_t pin_count () const + { + return m_pins.size (); + } + + /** + * @brief Returns the number of subcircuit pins connected + */ + size_t subcircuit_pin_count () const + { + return m_subcircuit_pins.size (); + } + + /** + * @brief Returns the number of terminals connected + */ + size_t terminal_count () const + { + return m_terminals.size (); + } + +private: + friend class Circuit; + + terminal_list m_terminals; + pin_list m_pins; + subcircuit_pin_list m_subcircuit_pins; + std::string m_name; + size_t m_cluster_id; + Circuit *mp_circuit; + + void set_circuit (Circuit *circuit); +}; + +} + +#endif diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc new file mode 100644 index 000000000..6cc04ea90 --- /dev/null +++ b/src/db/db/dbNetlist.cc @@ -0,0 +1,549 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbNetlist.h" + +#include + +namespace db +{ + +// -------------------------------------------------------------------------------- +// Netlist class implementation + +Netlist::Netlist () + : m_valid_topology (false), m_lock_count (0), + m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits), + m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits), + m_device_abstract_by_name (this, &Netlist::begin_device_abstracts, &Netlist::end_device_abstracts), + m_device_abstract_by_cell_index (this, &Netlist::begin_device_abstracts, &Netlist::end_device_abstracts) +{ + m_circuits.changed ().add (this, &Netlist::invalidate_topology); + m_circuits.changed ().add (this, &Netlist::circuits_changed); + m_device_abstracts.changed ().add (this, &Netlist::device_abstracts_changed); +} + +Netlist::Netlist (const Netlist &other) + : gsi::ObjectBase (other), tl::Object (other), m_valid_topology (false), m_lock_count (0), + m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits), + m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits), + m_device_abstract_by_name (this, &Netlist::begin_device_abstracts, &Netlist::end_device_abstracts), + m_device_abstract_by_cell_index (this, &Netlist::begin_device_abstracts, &Netlist::end_device_abstracts) +{ + operator= (other); + m_circuits.changed ().add (this, &Netlist::invalidate_topology); + m_circuits.changed ().add (this, &Netlist::circuits_changed); + m_device_abstracts.changed ().add (this, &Netlist::device_abstracts_changed); +} + +Netlist::~Netlist () +{ + m_circuits.changed ().remove (this, &Netlist::invalidate_topology); + m_circuits.changed ().remove (this, &Netlist::circuits_changed); + m_device_abstracts.changed ().remove (this, &Netlist::device_abstracts_changed); +} + +Netlist &Netlist::operator= (const Netlist &other) +{ + if (this != &other) { + + clear (); + + std::map dct; + for (const_device_class_iterator dc = other.begin_device_classes (); dc != other.end_device_classes (); ++dc) { + DeviceClass *dc_new = dc->clone (); + dct [dc.operator-> ()] = dc_new; + m_device_classes.push_back (dc_new); + } + + std::map dmt; + for (const_abstract_model_iterator dm = other.begin_device_abstracts (); dm != other.end_device_abstracts (); ++dm) { + DeviceAbstract *dm_new = new DeviceAbstract (*dm); + dmt [dm.operator-> ()] = dm_new; + m_device_abstracts.push_back (dm_new); + } + + std::map ct; + for (const_circuit_iterator i = other.begin_circuits (); i != other.end_circuits (); ++i) { + Circuit *ct_new = new Circuit (*i); + ct_new->translate_device_classes (dct); + ct_new->translate_device_abstracts (dmt); + ct [i.operator-> ()] = ct_new; + add_circuit (ct_new); + } + + for (circuit_iterator i = begin_circuits (); i != end_circuits (); ++i) { + i->translate_circuits (ct); + } + + } + return *this; +} + +void Netlist::circuits_changed () +{ + m_circuit_by_cell_index.invalidate (); + m_circuit_by_name.invalidate (); +} + +void Netlist::device_abstracts_changed () +{ + m_device_abstract_by_cell_index.invalidate (); + m_device_abstract_by_name.invalidate (); +} + +void Netlist::invalidate_topology () +{ + if (m_valid_topology) { + + m_valid_topology = false; + + if (m_lock_count == 0) { + m_top_circuits = 0; + m_top_down_circuits.clear (); + m_child_circuits.clear (); + m_parent_circuits.clear (); + } + + } +} + +namespace { + struct sort_by_index + { + bool operator () (const Circuit *a, const Circuit *b) const + { + return a->index () < b->index (); + } + }; +} + +void Netlist::validate_topology () +{ + if (m_valid_topology) { + return; + } else if (m_lock_count > 0) { + return; + } + + m_child_circuits.clear (); + m_parent_circuits.clear (); + + size_t max_index = 0; + for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { + c->set_index (max_index); + ++max_index; + } + + // build the child circuit list ... needed for the topology sorting + + m_child_circuits.reserve (max_index); + m_parent_circuits.reserve (max_index); + + for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { + + std::set children; + for (Circuit::subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { + if (sc->circuit_ref ()) { + children.insert (sc->circuit_ref ()); + } + } + + m_child_circuits.push_back (tl::vector ()); + tl::vector &cc = m_child_circuits.back (); + cc.reserve (children.size ()); + cc.insert (cc.end (), children.begin (), children.end ()); + // sort by index for better reproducibility + std::sort (cc.begin (), cc.end (), sort_by_index ()); + + std::set parents; + for (Circuit::refs_iterator sc = c->begin_refs (); sc != c->end_refs (); ++sc) { + if (sc->circuit ()) { + parents.insert (sc->circuit ()); + } + } + + m_parent_circuits.push_back (tl::vector ()); + tl::vector &pc = m_parent_circuits.back (); + pc.reserve (parents.size ()); + pc.insert (pc.end (), parents.begin (), parents.end ()); + // sort by index for better reproducibility + std::sort (pc.begin (), pc.end (), sort_by_index ()); + + } + + // do topology sorting + + m_top_circuits = 0; + m_top_down_circuits.clear (); + m_top_down_circuits.reserve (max_index); + + std::vector num_parents (max_index, 0); + + // while there are cells to treat .. + while (m_top_down_circuits.size () != max_index) { + + size_t n_top_down_circuits = m_top_down_circuits.size (); + + // Treat all circuits that do not have all parents reported. + // For all such a circuits, disable the parent counting, + // add the circuits's index to the top-down sorted list and + // increment the reported parent count in all the + // child circuits. + + for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { + if (m_parent_circuits [c->index ()].size () == num_parents [c->index ()]) { + m_top_down_circuits.push_back (c.operator-> ()); + num_parents [c->index ()] = std::numeric_limits::max (); + } + } + + // For all these a circuits, increment the reported parent instance + // count in all the child circuits. + for (tl::vector::const_iterator ii = m_top_down_circuits.begin () + n_top_down_circuits; ii != m_top_down_circuits.end (); ++ii) { + const tl::vector &cc = m_child_circuits [(*ii)->index ()]; + for (tl::vector::const_iterator icc = cc.begin (); icc != cc.end (); ++icc) { + tl_assert (num_parents [(*icc)->index ()] != std::numeric_limits::max ()); + num_parents [(*icc)->index ()] += 1; + } + } + + // If no new cells have been reported this is basically a + // sign of recursion in the graph. + if (n_top_down_circuits == m_top_down_circuits.size ()) { + throw tl::Exception (tl::to_string (tr ("Recursive hierarchy detected in netlist"))); + } + + } + + // Determine the number of top cells + for (tl::vector::const_iterator e = m_top_down_circuits.begin (); e != m_top_down_circuits.end () && m_parent_circuits [(*e)->index ()].empty (); ++e) { + ++m_top_circuits; + } + + m_valid_topology = true; +} + +void Netlist::lock () +{ + if (m_lock_count == 0) { + validate_topology (); + } + ++m_lock_count; +} + +void Netlist::unlock () +{ + if (m_lock_count > 0) { + --m_lock_count; + } +} + +const tl::vector &Netlist::child_circuits (Circuit *circuit) +{ + if (! m_valid_topology) { + validate_topology (); + } + + tl_assert (circuit->index () < m_child_circuits.size ()); + return m_child_circuits [circuit->index ()]; +} + +const tl::vector &Netlist::parent_circuits (Circuit *circuit) +{ + if (! m_valid_topology) { + validate_topology (); + } + + tl_assert (circuit->index () < m_parent_circuits.size ()); + return m_parent_circuits [circuit->index ()]; +} + +Netlist::top_down_circuit_iterator Netlist::begin_top_down () +{ + if (! m_valid_topology) { + validate_topology (); + } + return m_top_down_circuits.begin (); +} + +Netlist::top_down_circuit_iterator Netlist::end_top_down () +{ + if (! m_valid_topology) { + validate_topology (); + } + return m_top_down_circuits.end (); +} + +Netlist::const_top_down_circuit_iterator Netlist::begin_top_down () const +{ + if (! m_valid_topology) { + const_cast (this)->validate_topology (); + } + return reinterpret_cast &> (m_top_down_circuits).begin (); +} + +Netlist::const_top_down_circuit_iterator Netlist::end_top_down () const +{ + if (! m_valid_topology) { + const_cast (this)->validate_topology (); + } + return reinterpret_cast &> (m_top_down_circuits).end (); +} + +size_t Netlist::top_circuit_count () const +{ + if (! m_valid_topology) { + const_cast (this)->validate_topology (); + } + return m_top_circuits; +} + +Netlist::bottom_up_circuit_iterator Netlist::begin_bottom_up () +{ + if (! m_valid_topology) { + validate_topology (); + } + return m_top_down_circuits.rbegin (); +} + +Netlist::bottom_up_circuit_iterator Netlist::end_bottom_up () +{ + if (! m_valid_topology) { + validate_topology (); + } + return m_top_down_circuits.rend (); +} + +Netlist::const_bottom_up_circuit_iterator Netlist::begin_bottom_up () const +{ + if (! m_valid_topology) { + const_cast (this)->validate_topology (); + } + return reinterpret_cast &> (m_top_down_circuits).rbegin (); +} + +Netlist::const_bottom_up_circuit_iterator Netlist::end_bottom_up () const +{ + if (! m_valid_topology) { + const_cast (this)->validate_topology (); + } + return reinterpret_cast &> (m_top_down_circuits).rend (); +} + +void Netlist::clear () +{ + m_device_classes.clear (); + m_device_abstracts.clear (); + m_circuits.clear (); +} + +void Netlist::add_circuit (Circuit *circuit) +{ + m_circuits.push_back (circuit); + circuit->set_netlist (this); +} + +void Netlist::remove_circuit (Circuit *circuit) +{ + circuit->set_netlist (0); + m_circuits.erase (circuit); +} + +void Netlist::add_device_class (DeviceClass *device_class) +{ + m_device_classes.push_back (device_class); + device_class->set_netlist (this); +} + +void Netlist::remove_device_class (DeviceClass *device_class) +{ + device_class->set_netlist (0); + m_device_classes.erase (device_class); +} + +void Netlist::add_device_abstract (DeviceAbstract *device_abstract) +{ + m_device_abstracts.push_back (device_abstract); + device_abstract->set_netlist (this); +} + +void Netlist::remove_device_abstract (DeviceAbstract *device_abstract) +{ + device_abstract->set_netlist (0); + m_device_abstracts.erase (device_abstract); +} + +void Netlist::purge_nets () +{ + for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { + c->purge_nets (); + } +} + +void Netlist::make_top_level_pins () +{ + size_t ntop = top_circuit_count (); + for (top_down_circuit_iterator c = begin_top_down (); c != end_top_down () && ntop > 0; ++c, --ntop) { + + Circuit *circuit = *c; + + if (circuit->pin_count () == 0) { + + // create pins for the named nets and connect them + for (Circuit::net_iterator n = circuit->begin_nets (); n != circuit->end_nets (); ++n) { + if (! n->name ().empty () && n->terminal_count () + n->subcircuit_pin_count () > 0) { + Pin pin = circuit->add_pin (n->name ()); + circuit->connect_pin (pin.id (), n.operator-> ()); + } + } + + } + + } +} + +void Netlist::purge () +{ + // This locking is very important as we do not want to recompute the bottom-up list + // while iterating. + NetlistLocker locker (this); + + for (bottom_up_circuit_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) { + + Circuit *circuit = *c; + + circuit->purge_nets (); + if (circuit->begin_nets () == circuit->end_nets ()) { + + // No nets left: delete the subcircuits that refer to us and finally delete the circuit + while (circuit->begin_refs () != circuit->end_refs ()) { + delete circuit->begin_refs ().operator-> (); + } + delete circuit; + + } + + } +} + +void Netlist::combine_devices () +{ + for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { + c->combine_devices (); + } +} + +static std::string net2string (const db::Net *net) +{ + return net ? tl::to_word_or_quoted_string (net->expanded_name ()) : "(null)"; +} + +static std::string device2string (const db::Device &device) +{ + if (device.name ().empty ()) { + return "$" + tl::to_string (device.id ()); + } else { + return tl::to_word_or_quoted_string (device.name ()); + } +} + +static std::string subcircuit2string (const db::SubCircuit &subcircuit) +{ + if (subcircuit.name ().empty ()) { + return "$" + tl::to_string (subcircuit.id ()); + } else { + return tl::to_word_or_quoted_string (subcircuit.name ()); + } +} + +static std::string pin2string (const db::Pin &pin) +{ + if (pin.name ().empty ()) { + // the pin ID is zero-based and essentially the index, so we add 1 to make it compliant with the other IDs + return "$" + tl::to_string (pin.id () + 1); + } else { + return tl::to_word_or_quoted_string (pin.name ()); + } +} + +std::string Netlist::to_string () const +{ + std::string res; + for (db::Netlist::const_circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { + + std::string ps; + for (db::Circuit::const_pin_iterator p = c->begin_pins (); p != c->end_pins (); ++p) { + if (! ps.empty ()) { + ps += ","; + } + ps += pin2string (*p) + "=" + net2string (c->net_for_pin (p->id ())); + } + + res += std::string ("Circuit ") + c->name () + " (" + ps + "):\n"; + +#if 0 // for debugging + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " sc_pins=" + tl::to_string (n->subcircuit_pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n"; + } +#endif + + for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { + std::string ts; + const std::vector &td = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + if (t != td.begin ()) { + ts += ","; + } + ts += t->name () + "=" + net2string (d->net_for_terminal (t->id ())); + } + std::string ps; + const std::vector &pd = d->device_class ()->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (p != pd.begin ()) { + ps += ","; + } + ps += p->name () + "=" + tl::to_string (d->parameter_value (p->id ())); + } + res += std::string (" D") + d->device_class ()->name () + " " + device2string (*d) + " (" + ts + ") [" + ps + "]\n"; + } + + for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { + std::string ps; + const db::SubCircuit &subcircuit = *sc; + const db::Circuit *circuit = sc->circuit_ref (); + for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) { + if (p != circuit->begin_pins ()) { + ps += ","; + } + const db::Pin &pin = *p; + ps += pin2string (pin) + "=" + net2string (subcircuit.net_for_pin (pin.id ())); + } + res += std::string (" X") + circuit->name () + " " + subcircuit2string (*sc) + " (" + ps + ")\n"; + } + + } + + return res; +} + +} diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h new file mode 100644 index 000000000..86bfd9298 --- /dev/null +++ b/src/db/db/dbNetlist.h @@ -0,0 +1,478 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbNetlist +#define _HDR_dbNetlist + +#include "dbCommon.h" +#include "dbCircuit.h" +#include "dbDeviceClass.h" +#include "dbDeviceAbstract.h" + +#include "tlVector.h" + +#include + +namespace db +{ + +/** + * @brief The netlist class + * + * This class represents a hierarchical netlist. + * The main components of a netlist are circuits and device classes. + * The circuits represent cells, the device classes type of devices. + */ +class DB_PUBLIC Netlist + : public gsi::ObjectBase, public tl::Object +{ +public: + typedef tl::shared_collection circuit_list; + typedef circuit_list::const_iterator const_circuit_iterator; + typedef circuit_list::iterator circuit_iterator; + typedef tl::shared_collection device_class_list; + typedef device_class_list::const_iterator const_device_class_iterator; + typedef device_class_list::iterator device_class_iterator; + typedef tl::shared_collection device_abstract_list; + typedef device_abstract_list::const_iterator const_abstract_model_iterator; + typedef device_abstract_list::iterator device_abstract_iterator; + typedef tl::vector::const_iterator top_down_circuit_iterator; + typedef tl::vector::const_iterator const_top_down_circuit_iterator; + typedef tl::vector::const_reverse_iterator bottom_up_circuit_iterator; + typedef tl::vector::const_reverse_iterator const_bottom_up_circuit_iterator; + + /** + * @brief Constructor + * + * This constructor creates an empty hierarchical netlist + */ + Netlist (); + + /** + * @brief Copy constructor + */ + Netlist (const Netlist &other); + + /** + * @brief Destructor + */ + ~Netlist (); + + /** + * @brief Assignment + */ + Netlist &operator= (const Netlist &other); + + /** + * @brief Clears the netlist + */ + void clear (); + + /** + * @brief Returns a string representation of the netlist + * + * This method is basically intended to testing. + */ + std::string to_string () const; + + /** + * @brief Starts a sequence of operations during which topology updates are not desired + * + * If the hierarchy is modified, the topology information (top-down order, children + * and parent information) may be recomputed frequently. This may cause performance issues + * and may not be desired. + * + * Calling this method will bring the netlist into a state in which updates on the + * list will not happen. Using "unlock" will end this state. + * + * "lock" and "unlock" are incremental and can be nested. Use "NetlistLocker" for safe locking + * and unlocking. + * + * Before lock, the state will be validated, so inside the locked operation, the topology + * information will be valid with respect to the initial state. + */ + void lock (); + + /** + * @brief Ends a sequence of operations during which topology updates are not desired + * See "lock" for more details. + */ + void unlock (); + + /** + * @brief Adds a circuit to this netlist + * + * The netlist takes over ownership of the object. + */ + void add_circuit (Circuit *circuit); + + /** + * @brief Deletes a circuit from the netlist + */ + void remove_circuit (Circuit *circuit); + + /** + * @brief Begin iterator for the circuits of the netlist (non-const version) + */ + circuit_iterator begin_circuits () + { + return m_circuits.begin (); + } + + /** + * @brief End iterator for the circuits of the netlist (non-const version) + */ + circuit_iterator end_circuits () + { + return m_circuits.end (); + } + + /** + * @brief Begin iterator for the circuits of the netlist (const version) + */ + const_circuit_iterator begin_circuits () const + { + return m_circuits.begin (); + } + + /** + * @brief End iterator for the circuits of the netlist (const version) + */ + const_circuit_iterator end_circuits () const + { + return m_circuits.end (); + } + + /** + * @brief Gets the circuit with the given name + * + * If no circuit with that name exists, null is returned. + */ + Circuit *circuit_by_name (const std::string &name) + { + return m_circuit_by_name.object_by (name); + } + + /** + * @brief Gets the circuit with the given name (const version) + * + * If no circuit with that name exists, null is returned. + */ + const Circuit *circuit_by_name (const std::string &name) const + { + return m_circuit_by_name.object_by (name); + } + + /** + * @brief Gets the circuit with the given cell index + * + * If no circuit with that cell index exists, null is returned. + */ + Circuit *circuit_by_cell_index (db::cell_index_type cell_index) + { + return m_circuit_by_cell_index.object_by (cell_index); + } + + /** + * @brief Gets the circuit with the given cell index (const version) + * + * If no circuit with that cell index exists, null is returned. + */ + const Circuit *circuit_by_cell_index (db::cell_index_type cell_index) const + { + return m_circuit_by_cell_index.object_by (cell_index); + } + + /** + * @brief Gets the top-down circuits iterator (begin) + * This iterator will deliver the circuits in a top-down way - i.e. child circuits + * will always come after parent circuits. + * The first "top_circuit_count" elements will be top circuits (those which are not + * referenced by other circuits). + */ + top_down_circuit_iterator begin_top_down (); + + /** + * @brief Gets the top-down circuits iterator (end) + */ + top_down_circuit_iterator end_top_down (); + + /** + * @brief Gets the top-down circuits iterator (begin, const version) + * This iterator will deliver the circuits in a top-down way - i.e. child circuits + * will always come after parent circuits. + * The first "top_circuit_count" elements will be top circuits (those which are not + * referenced by other circuits). + */ + const_top_down_circuit_iterator begin_top_down () const; + + /** + * @brief Gets the top-down circuits iterator (end, const version) + */ + const_top_down_circuit_iterator end_top_down () const; + + /** + * @brief Gets the number of top circuits + * Top circuits are those which are not referenced by other circuits. + * In a well-formed netlist there is a single top-level circuit. + */ + size_t top_circuit_count () const; + + /** + * @brief Gets the bottom-up circuits iterator (begin) + * This iterator will deliver the circuits in a bottom-up way - i.e. child circuits + * will always come before parent circuits. + */ + bottom_up_circuit_iterator begin_bottom_up (); + + /** + * @brief Gets the bottom-up circuits iterator (end) + */ + bottom_up_circuit_iterator end_bottom_up (); + + /** + * @brief Gets the bottom-up circuits iterator (begin, const version) + * This iterator will deliver the circuits in a bottom-up way - i.e. child circuits + * will always come before parent circuits. + */ + const_bottom_up_circuit_iterator begin_bottom_up () const; + + /** + * @brief Gets the bottom-up circuits iterator (end, const version) + */ + const_bottom_up_circuit_iterator end_bottom_up () const; + + /** + * @brief Adds a device class to this netlist + * + * The netlist takes over ownership of the object. + */ + void add_device_class (DeviceClass *device_class); + + /** + * @brief Deletes a device class from the netlist + */ + void remove_device_class (DeviceClass *device_class); + + /** + * @brief Begin iterator for the device classes of the netlist (non-const version) + */ + device_class_iterator begin_device_classes () + { + return m_device_classes.begin (); + } + + /** + * @brief End iterator for the device classes of the netlist (non-const version) + */ + device_class_iterator end_device_classes () + { + return m_device_classes.end (); + } + + /** + * @brief Begin iterator for the device classes of the netlist (const version) + */ + const_device_class_iterator begin_device_classes () const + { + return m_device_classes.begin (); + } + + /** + * @brief End iterator for the device classes of the netlist (const version) + */ + const_device_class_iterator end_device_classes () const + { + return m_device_classes.end (); + } + + /** + * @brief Adds a device abstract to this netlist + * + * The netlist takes over ownership of the object. + */ + void add_device_abstract (DeviceAbstract *device_abstract); + + /** + * @brief Deletes a device abstract from the netlist + */ + void remove_device_abstract (DeviceAbstract *device_abstract); + + /** + * @brief Begin iterator for the device abstracts of the netlist (non-const version) + */ + device_abstract_iterator begin_device_abstracts () + { + return m_device_abstracts.begin (); + } + + /** + * @brief End iterator for the device abstracts of the netlist (non-const version) + */ + device_abstract_iterator end_device_abstracts () + { + return m_device_abstracts.end (); + } + + /** + * @brief Begin iterator for the device abstracts of the netlist (const version) + */ + const_abstract_model_iterator begin_device_abstracts () const + { + return m_device_abstracts.begin (); + } + + /** + * @brief End iterator for the device abstracts of the netlist (const version) + */ + const_abstract_model_iterator end_device_abstracts () const + { + return m_device_abstracts.end (); + } + + /** + * @brief Gets the device abstract with the given name + * + * If no device abstract with that name exists, null is returned. + */ + DeviceAbstract *device_abstract_by_name (const std::string &name) + { + return m_device_abstract_by_name.object_by (name); + } + + /** + * @brief Gets the device abstract with the given name (const version) + * + * If no device abstract with that name exists, null is returned. + */ + const DeviceAbstract *device_abstract_by_name (const std::string &name) const + { + return m_device_abstract_by_name.object_by (name); + } + + /** + * @brief Gets the device abstract with the given cell index + * + * If no device abstract with that cell index exists, null is returned. + */ + DeviceAbstract *device_abstract_by_cell_index (db::cell_index_type cell_index) + { + return m_device_abstract_by_cell_index.object_by (cell_index); + } + + /** + * @brief Gets the device abstract with the given cell index (const version) + * + * If no device abstract with that cell index exists, null is returned. + */ + const DeviceAbstract *device_abstract_by_cell_index (db::cell_index_type cell_index) const + { + return m_device_abstract_by_cell_index.object_by (cell_index); + } + + /** + * @brief Purge unused nets + * + * This method will purge all nets which return "floating". + */ + void purge_nets (); + + /** + * @brief Creates pins for top-level circuits + * + * This method will turn all named nets of top-level circuits (such that are not + * referenced by subcircuits) into pins. This method can be used before purge to + * avoid that purge will remove nets which are directly connecting to subcircuits. + */ + void make_top_level_pins (); + + /** + * @brief Purge unused nets, circuits and subcircuits + * + * This method will purge all nets which return "floating". Circuits which don't have any + * nets (or only floating ones) and removed. Their subcircuits are disconnected. + */ + void purge (); + + /** + * @brief Combine devices + * + * This method will combine devices that can be combined according + * to their device classes "combine_devices" method. + */ + void combine_devices (); + +private: + friend class Circuit; + friend class DeviceAbstract; + + circuit_list m_circuits; + device_class_list m_device_classes; + device_abstract_list m_device_abstracts; + bool m_valid_topology; + int m_lock_count; + tl::vector m_top_down_circuits; + tl::vector > m_child_circuits; + tl::vector > m_parent_circuits; + size_t m_top_circuits; + object_by_attr > m_circuit_by_name; + object_by_attr > m_circuit_by_cell_index; + object_by_attr > m_device_abstract_by_name; + object_by_attr > m_device_abstract_by_cell_index; + + void invalidate_topology (); + void validate_topology (); + void circuits_changed (); + void device_abstracts_changed (); + + const tl::vector &child_circuits (Circuit *circuit); + const tl::vector &parent_circuits (Circuit *circuit); +}; + +/** + * @brief A helper class using RAII for safe locking/unlocking + */ +class DB_PUBLIC NetlistLocker +{ +public: + NetlistLocker (Netlist *netlist) + : mp_netlist (netlist) + { + if (mp_netlist.get ()) { + mp_netlist->lock (); + } + } + + ~NetlistLocker () + { + if (mp_netlist.get ()) { + mp_netlist->unlock (); + } + } + +private: + tl::weak_ptr mp_netlist; +}; + +} + +#endif diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc new file mode 100644 index 000000000..05a1a59c8 --- /dev/null +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -0,0 +1,305 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbNetlistDeviceClasses.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------ +// DeviceClassTwoTerminalDevice implementation + +bool DeviceClassTwoTerminalDevice::combine_devices (Device *a, Device *b) const +{ + db::Net *na1 = a->net_for_terminal (0); + db::Net *na2 = a->net_for_terminal (1); + db::Net *nb1 = b->net_for_terminal (0); + db::Net *nb2 = b->net_for_terminal (1); + + bool res = true; + + if ((na1 == nb1 && na2 == nb2) || (na1 == nb2 && na2 == nb1)) { + + parallel (a, b); + + } else if ((na2 == nb1 || na2 == nb2) && na2->is_internal ()) { + + // serial a(B) to b(A or B) + serial (a, b); + a->connect_terminal (1, (na2 == nb1 ? nb2 : nb1)); + + } else if ((na1 == nb1 || na1 == nb2) && na1->is_internal ()) { + + // serial a(A) to b(A or B) + serial (a, b); + a->connect_terminal (0, (na1 == nb1 ? nb2 : nb1)); + + } + + if (res) { + b->connect_terminal (0, 0); + b->connect_terminal (1, 0); + return true; + } else { + return false; + } +} + + +// ------------------------------------------------------------------------------------ +// DeviceClassResistor implementation + +DB_PUBLIC size_t DeviceClassResistor::param_id_R = 0; + +DB_PUBLIC size_t DeviceClassResistor::terminal_id_A = 0; +DB_PUBLIC size_t DeviceClassResistor::terminal_id_B = 1; + +DeviceClassResistor::DeviceClassResistor () +{ + add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A")); + add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B")); + + add_parameter_definition (db::DeviceParameterDefinition ("R", "Resistance (Ohm)", 0.0)); +} + +void DeviceClassResistor::parallel (Device *a, Device *b) const +{ + double va = a->parameter_value (0); + double vb = b->parameter_value (0); + a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb)); +} + +void DeviceClassResistor::serial (Device *a, Device *b) const +{ + double va = a->parameter_value (0); + double vb = b->parameter_value (0); + a->set_parameter_value (0, va + vb); +} + +// ------------------------------------------------------------------------------------ +// DeviceClassCapacitor implementation + +DB_PUBLIC size_t DeviceClassCapacitor::param_id_C = 0; + +DB_PUBLIC size_t DeviceClassCapacitor::terminal_id_A = 0; +DB_PUBLIC size_t DeviceClassCapacitor::terminal_id_B = 1; + +DeviceClassCapacitor::DeviceClassCapacitor () +{ + add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A")); + add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B")); + + add_parameter_definition (db::DeviceParameterDefinition ("C", "Capacitance (Farad)", 0.0)); +} + +void DeviceClassCapacitor::serial (Device *a, Device *b) const +{ + double va = a->parameter_value (0); + double vb = b->parameter_value (0); + a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb)); +} + +void DeviceClassCapacitor::parallel (Device *a, Device *b) const +{ + double va = a->parameter_value (0); + double vb = b->parameter_value (0); + a->set_parameter_value (0, va + vb); +} + +// ------------------------------------------------------------------------------------ +// DeviceClassInductor implementation + +DB_PUBLIC size_t DeviceClassInductor::param_id_L = 0; + +DB_PUBLIC size_t DeviceClassInductor::terminal_id_A = 0; +DB_PUBLIC size_t DeviceClassInductor::terminal_id_B = 1; + +DeviceClassInductor::DeviceClassInductor () +{ + add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A")); + add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B")); + + add_parameter_definition (db::DeviceParameterDefinition ("L", "Inductance (Henry)", 0.0)); +} + +void DeviceClassInductor::parallel (Device *a, Device *b) const +{ + double va = a->parameter_value (0); + double vb = b->parameter_value (0); + a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb)); +} + +void DeviceClassInductor::serial (Device *a, Device *b) const +{ + double va = a->parameter_value (0); + double vb = b->parameter_value (0); + a->set_parameter_value (0, va + vb); +} + +// ------------------------------------------------------------------------------------ +// DeviceClassInductor implementation + +DB_PUBLIC size_t DeviceClassDiode::param_id_A = 0; + +DB_PUBLIC size_t DeviceClassDiode::terminal_id_A = 0; +DB_PUBLIC size_t DeviceClassDiode::terminal_id_C = 1; + +DeviceClassDiode::DeviceClassDiode () +{ + add_terminal_definition (db::DeviceTerminalDefinition ("A", "Anode")); + add_terminal_definition (db::DeviceTerminalDefinition ("C", "Cathode")); + + add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0)); +} + +bool DeviceClassDiode::combine_devices (Device *a, Device *b) const +{ + const db::Net *na1 = a->net_for_terminal (0); + const db::Net *na2 = a->net_for_terminal (1); + const db::Net *nb1 = b->net_for_terminal (0); + const db::Net *nb2 = b->net_for_terminal (1); + + // only parallel diodes can be combined and their areas will add + if (na1 == nb1 && na2 == nb2) { + + a->set_parameter_value (0, a->parameter_value (0) + b->parameter_value (0)); + b->connect_terminal (0, 0); + b->connect_terminal (1, 0); + + return true; + + } else { + return false; + } +} + +// ------------------------------------------------------------------------------------ +// DeviceClassMOS3Transistor implementation + +DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_L = 0; +DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_W = 1; +DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_AS = 2; +DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_AD = 3; +DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_PS = 4; +DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_PD = 5; + +DB_PUBLIC size_t DeviceClassMOS3Transistor::terminal_id_S = 0; +DB_PUBLIC size_t DeviceClassMOS3Transistor::terminal_id_G = 1; +DB_PUBLIC size_t DeviceClassMOS3Transistor::terminal_id_D = 2; + +DeviceClassMOS3Transistor::DeviceClassMOS3Transistor () +{ + add_terminal_definition (db::DeviceTerminalDefinition ("S", "Source")); + add_terminal_definition (db::DeviceTerminalDefinition ("G", "Gate")); + add_terminal_definition (db::DeviceTerminalDefinition ("D", "Drain")); + + add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0)); +} + +bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const +{ + const db::Net *nas = a->net_for_terminal (0); + const db::Net *nag = a->net_for_terminal (1); + const db::Net *nad = a->net_for_terminal (2); + const db::Net *nbs = b->net_for_terminal (0); + const db::Net *nbg = b->net_for_terminal (1); + const db::Net *nbd = b->net_for_terminal (2); + + // parallel transistors can be combined into one + if (((nas == nbs && nad == nbd) || (nas == nbd && nad == nbs)) && nag == nbg) { + + // for combination the gate length must be identical + if (fabs (a->parameter_value (0) - b->parameter_value (0)) < 1e-6) { + + combine_parameters (a, b); + + b->connect_terminal (0, 0); + b->connect_terminal (1, 0); + b->connect_terminal (2, 0); + + return true; + + } + + } + + return false; +} + +void DeviceClassMOS3Transistor::combine_parameters (Device *a, Device *b) const +{ + a->set_parameter_value (1, a->parameter_value (1) + b->parameter_value (1)); + a->set_parameter_value (2, a->parameter_value (2) + b->parameter_value (2)); + a->set_parameter_value (3, a->parameter_value (3) + b->parameter_value (3)); + a->set_parameter_value (4, a->parameter_value (4) + b->parameter_value (4)); + a->set_parameter_value (5, a->parameter_value (5) + b->parameter_value (5)); +} + +// ------------------------------------------------------------------------------------ +// DeviceClassMOS4Transistor implementation + +DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_B = 3; + +DeviceClassMOS4Transistor::DeviceClassMOS4Transistor () +{ + add_terminal_definition (db::DeviceTerminalDefinition ("B", "Bulk")); +} + +bool DeviceClassMOS4Transistor::combine_devices (Device *a, Device *b) const +{ + const db::Net *nas = a->net_for_terminal (0); + const db::Net *nag = a->net_for_terminal (1); + const db::Net *nad = a->net_for_terminal (2); + const db::Net *nab = a->net_for_terminal (3); + const db::Net *nbs = b->net_for_terminal (0); + const db::Net *nbg = b->net_for_terminal (1); + const db::Net *nbd = b->net_for_terminal (2); + const db::Net *nbb = b->net_for_terminal (3); + + // parallel transistors can be combined into one + if (((nas == nbs && nad == nbd) || (nas == nbd && nad == nbs)) && nag == nbg && nab == nbb) { + + // for combination the gate length must be identical + if (fabs (a->parameter_value (0) - b->parameter_value (0)) < 1e-6) { + + combine_parameters (a, b); + + b->connect_terminal (0, 0); + b->connect_terminal (1, 0); + b->connect_terminal (2, 0); + b->connect_terminal (3, 0); + + return true; + + } + + } + + return false; +} + +} diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h new file mode 100644 index 000000000..c7d4b5187 --- /dev/null +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -0,0 +1,206 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbNetlistDeviceClasses +#define _HDR_dbNetlistDeviceClasses + +#include "dbCommon.h" +#include "dbNetlist.h" + +namespace db +{ + +/** + * @brief A basic two-terminal device class + */ +class DB_PUBLIC DeviceClassTwoTerminalDevice + : public db::DeviceClass +{ +public: + virtual bool combine_devices (Device *a, Device *b) const; + + virtual void parallel (Device *a, Device *b) const = 0; + virtual void serial (Device *a, Device *b) const = 0; + virtual bool supports_parallel_combination () const { return true; } + virtual bool supports_serial_combination () const { return true; } +}; + +/** + * @brief A basic resistor device class + * A resistor defines a single parameter, "R", which is the resistance in Ohm. + * It defines two terminals, "A" and "B" for the two terminals. + */ +class DB_PUBLIC DeviceClassResistor + : public db::DeviceClassTwoTerminalDevice +{ +public: + DeviceClassResistor (); + + virtual db::DeviceClass *clone () const + { + return new DeviceClassResistor (*this); + } + + static size_t param_id_R; + + static size_t terminal_id_A; + static size_t terminal_id_B; + + virtual void parallel (Device *a, Device *b) const; + virtual void serial (Device *a, Device *b) const; +}; + +/** + * @brief A basic capacitor device class + * A capacitor defines a single parameter, "C", which is the capacitance in Farad. + * It defines two terminals, "A" and "B" for the two terminals. + */ +class DB_PUBLIC DeviceClassCapacitor + : public db::DeviceClassTwoTerminalDevice +{ +public: + DeviceClassCapacitor (); + + virtual db::DeviceClass *clone () const + { + return new DeviceClassCapacitor (*this); + } + + static size_t param_id_C; + + static size_t terminal_id_A; + static size_t terminal_id_B; + + virtual void parallel (Device *a, Device *b) const; + virtual void serial (Device *a, Device *b) const; +}; + +/** + * @brief A basic inductor device class + * An inductor defines a single parameter, "L", which is the inductance in Henry. + * It defines two terminals, "A" and "B" for the two terminals. + */ +class DB_PUBLIC DeviceClassInductor + : public db::DeviceClassTwoTerminalDevice +{ +public: + DeviceClassInductor (); + + virtual db::DeviceClass *clone () const + { + return new DeviceClassInductor (*this); + } + + static size_t param_id_L; + + static size_t terminal_id_A; + static size_t terminal_id_B; + + virtual void parallel (Device *a, Device *b) const; + virtual void serial (Device *a, Device *b) const; +}; + +/** + * @brief A basic diode device class + * A diode defines a single parameter, "A", which is the area in square micrometers (YES: micrometers, as this is the basic unit of measure + * in KLayout). + * It defines two terminals, "A" and "C" for anode and cathode. + */ +class DB_PUBLIC DeviceClassDiode + : public db::DeviceClass +{ +public: + DeviceClassDiode (); + + static size_t param_id_A; + + static size_t terminal_id_A; + static size_t terminal_id_C; + + virtual db::DeviceClass *clone () const + { + return new DeviceClassDiode (*this); + } + + virtual bool combine_devices (Device *a, Device *b) const; + virtual bool supports_parallel_combination () const { return true; } +}; + +/** + * @brief A basic MOSFET device class with three terminals + * A MOSFET defines six parameters: "W" for the gate width in micrometers, "L" for the gate length in micrometers, + * "AS" for the source area and "AD" for the drain area and "PS" and "PD" for the source and drain perimeter. + * The MOSFET device defines three terminals, "S", "D" and "G" for source, drain and gate. + */ +class DB_PUBLIC DeviceClassMOS3Transistor + : public db::DeviceClass +{ +public: + DeviceClassMOS3Transistor (); + + static size_t param_id_L; + static size_t param_id_W; + static size_t param_id_AS; + static size_t param_id_AD; + static size_t param_id_PS; + static size_t param_id_PD; + + static size_t terminal_id_S; + static size_t terminal_id_G; + static size_t terminal_id_D; + + virtual db::DeviceClass *clone () const + { + return new DeviceClassMOS3Transistor (*this); + } + + virtual bool combine_devices (Device *a, Device *b) const; + virtual bool supports_parallel_combination () const { return true; } + +protected: + void combine_parameters (Device *a, Device *b) const; +}; + +/** + * @brief A basic MOSFET device class with four terminals + * The four-terminal MOSFET behaves identical to the three-terminal one but adds one more + * terminal for the bulk. + */ +class DB_PUBLIC DeviceClassMOS4Transistor + : public DeviceClassMOS3Transistor +{ +public: + DeviceClassMOS4Transistor (); + + static size_t terminal_id_B; + + virtual db::DeviceClass *clone () const + { + return new DeviceClassMOS4Transistor (*this); + } + + virtual bool combine_devices (Device *a, Device *b) const; +}; + +} + +#endif diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc new file mode 100644 index 000000000..b2ff51302 --- /dev/null +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -0,0 +1,567 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbNetlistDeviceExtractor.h" +#include "dbRegion.h" +#include "dbHierNetworkProcessor.h" +#include "dbDeepRegion.h" + +#include "tlProgress.h" +#include "tlTimer.h" +#include "tlInternational.h" + +namespace db +{ + +// ---------------------------------------------------------------------------------------- +// NetlistDeviceExtractorError implementation + +NetlistDeviceExtractorError::NetlistDeviceExtractorError () +{ + // .. nothing yet .. +} + +NetlistDeviceExtractorError::NetlistDeviceExtractorError (const std::string &cell_name, const std::string &msg) + : m_cell_name (cell_name), m_message (msg) +{ + // .. nothing yet .. +} + +std::string NetlistDeviceExtractorError::to_string () const +{ + std::string res; + + if (! m_category_name.empty ()) { + if (m_category_description.empty ()) { + res += "[" + m_category_name + "] "; + } else { + res += "[" + m_category_description + "] "; + } + } + + res += m_message; + + if (! m_cell_name.empty ()) { + res += tl::to_string (tr (", in cell: ")) + m_cell_name; + } + + if (! m_geometry.box ().empty ()) { + res += tl::to_string (tr (", shape: ")) + m_geometry.to_string (); + } + + return res; +} + +// ---------------------------------------------------------------------------------------- +// NetlistDeviceExtractor implementation + +NetlistDeviceExtractor::NetlistDeviceExtractor (const std::string &name) + : mp_layout (0), m_cell_index (0), mp_circuit (0) +{ + m_name = name; + m_terminal_id_propname_id = 0; + m_device_class_propname_id = 0; + m_device_id_propname_id = 0; +} + +NetlistDeviceExtractor::~NetlistDeviceExtractor () +{ + // .. nothing yet .. +} + +const tl::Variant &NetlistDeviceExtractor::terminal_id_property_name () +{ + static tl::Variant name ("TERMINAL_ID"); + return name; +} + +const tl::Variant &NetlistDeviceExtractor::device_id_property_name () +{ + static tl::Variant name ("DEVICE_ID"); + return name; +} + +const tl::Variant &NetlistDeviceExtractor::device_class_property_name () +{ + static tl::Variant name ("DEVICE_CLASS"); + return name; +} + +void NetlistDeviceExtractor::initialize (db::Netlist *nl) +{ + m_layer_definitions.clear (); + mp_device_class = 0; + m_terminal_id_propname_id = 0; + m_device_id_propname_id = 0; + m_device_class_propname_id = 0; + m_netlist.reset (nl); + + setup (); +} + +static void insert_into_region (const db::PolygonRef &s, const db::ICplxTrans &tr, db::Region ®ion) +{ + region.insert (s.obj ().transformed (tr * db::ICplxTrans (s.trans ()))); +} + +void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, unsigned int layout_index, const NetlistDeviceExtractor::input_layers &layer_map, db::Netlist &nl, hier_clusters_type &clusters) +{ + initialize (&nl); + + std::vector layers; + layers.reserve (m_layer_definitions.size ()); + + for (layer_definitions::const_iterator ld = begin_layer_definitions (); ld != end_layer_definitions (); ++ld) { + + input_layers::const_iterator l = layer_map.find (ld->name); + if (l == layer_map.end ()) { + throw tl::Exception (tl::to_string (tr ("Missing input layer for device extraction: ")) + ld->name); + } + + tl_assert (l->second != 0); + db::DeepRegion *dr = dynamic_cast (l->second->delegate ()); + if (dr == 0) { + + std::pair alias = dss.layer_for_flat (tl::id_of (l->second->delegate ())); + if (alias.first) { + // use deep layer alias for a given flat one (if found) + layers.push_back (alias.second.layer ()); + } else if (l->second->empty ()) { + // provide a substitute empty layer + layers.push_back (dss.empty_layer (layout_index).layer ()); + } else { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction (device %s): must be of deep region kind")), ld->name, name ())); + } + + } else { + + if (&dr->deep_layer ().layout () != &dss.layout (layout_index) || &dr->deep_layer ().initial_cell () != &dss.initial_cell (layout_index)) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction (device %s): not originating from the same source")), ld->name, name ())); + } + + layers.push_back (dr->deep_layer ().layer ()); + + } + + } + + extract_without_initialize (dss.layout (layout_index), dss.initial_cell (layout_index), clusters, layers); +} + +void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const std::vector &layers, db::Netlist *nl, hier_clusters_type &clusters) +{ + initialize (nl); + extract_without_initialize (layout, cell, clusters, layers); +} + +void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db::Cell &cell, hier_clusters_type &clusters, const std::vector &layers) +{ + tl_assert (layers.size () == m_layer_definitions.size ()); + + typedef db::PolygonRef shape_type; + db::ShapeIterator::flags_type shape_iter_flags = db::ShapeIterator::Polygons; + + mp_layout = &layout; + m_layers = layers; + mp_clusters = &clusters; + + // terminal properties are kept in a property with the terminal_property_name name + m_terminal_id_propname_id = mp_layout->properties_repository ().prop_name_id (terminal_id_property_name ()); + m_device_id_propname_id = mp_layout->properties_repository ().prop_name_id (device_id_property_name ()); + m_device_class_propname_id = mp_layout->properties_repository ().prop_name_id (device_class_property_name ()); + + tl_assert (m_netlist.get () != 0); + + // build a cell-id-to-circuit lookup table + std::map circuits_by_cell; + for (db::Netlist::circuit_iterator c = m_netlist->begin_circuits (); c != m_netlist->end_circuits (); ++c) { + circuits_by_cell.insert (std::make_pair (c->cell_index (), c.operator-> ())); + } + + // collect the cells below the top cell + std::set all_called_cells; + all_called_cells.insert (cell.cell_index ()); + cell.collect_called_cells (all_called_cells); + + // ignore device cells from previous extractions + std::set called_cells; + for (std::set::const_iterator ci = all_called_cells.begin (); ci != all_called_cells.end (); ++ci) { + if (! m_netlist->device_abstract_by_cell_index (*ci)) { + called_cells.insert (*ci); + } + } + all_called_cells.clear (); + + // build the device clusters + + db::Connectivity device_conn = get_connectivity (layout, layers); + db::hier_clusters device_clusters; + device_clusters.build (layout, cell, shape_iter_flags, device_conn); + + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Extracting devices"))); + + // count effort and make a progress reporter + + size_t n = 0; + for (std::set::const_iterator ci = called_cells.begin (); ci != called_cells.end (); ++ci) { + db::connected_clusters cc = device_clusters.clusters_per_cell (*ci); + for (db::connected_clusters::all_iterator c = cc.begin_all (); !c.at_end(); ++c) { + if (cc.is_root (*c)) { + ++n; + } + } + } + + tl::RelativeProgress progress (tl::to_string (tr ("Extracting devices")), n, 1); + + struct ExtractorCacheValueType { + ExtractorCacheValueType () { } + db::Vector disp; + tl::vector devices; + }; + + typedef std::map, ExtractorCacheValueType> extractor_cache_type; + extractor_cache_type extractor_cache; + + // for each cell investigate the clusters + for (std::set::const_iterator ci = called_cells.begin (); ci != called_cells.end (); ++ci) { + + m_cell_index = *ci; + + std::map::const_iterator c2c = circuits_by_cell.find (*ci); + if (c2c != circuits_by_cell.end ()) { + + // reuse existing circuit + mp_circuit = c2c->second; + + } else { + + // create a new circuit for this cell + mp_circuit = new db::Circuit (); + mp_circuit->set_cell_index (*ci); + mp_circuit->set_name (layout.cell_name (*ci)); + m_netlist->add_circuit (mp_circuit); + + } + + // investigate each cluster + db::connected_clusters cc = device_clusters.clusters_per_cell (*ci); + for (db::connected_clusters::all_iterator c = cc.begin_all (); !c.at_end(); ++c) { + + // take only root clusters - others have upward connections and are not "whole" + if (! cc.is_root (*c)) { + continue; + } + + ++progress; + + // build layer geometry from the cluster found + + std::vector layer_geometry; + layer_geometry.resize (layers.size ()); + + for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + db::Region &r = layer_geometry [l - layers.begin ()]; + for (db::recursive_cluster_shape_iterator si (device_clusters, *l, *ci, *c); ! si.at_end(); ++si) { + insert_into_region (*si, si.trans (), r); + } + r.set_base_verbosity (50); + } + + db::Box box; + for (std::vector::const_iterator g = layer_geometry.begin (); g != layer_geometry.end (); ++g) { + box += g->bbox (); + } + + db::Vector disp = box.p1 () - db::Point (); + for (std::vector::iterator g = layer_geometry.begin (); g != layer_geometry.end (); ++g) { + g->transform (db::Disp (-disp)); + } + + extractor_cache_type::const_iterator ec = extractor_cache.find (layer_geometry); + if (ec == extractor_cache.end ()) { + + // do the actual device extraction + extract_devices (layer_geometry); + + // push the new devices to the layout + push_new_devices (disp); + + ExtractorCacheValueType &ecv = extractor_cache [layer_geometry]; + ecv.disp = disp; + + for (std::map >::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) { + ecv.devices.push_back (d->second.first); + } + + m_new_devices.clear (); + + } else { + + push_cached_devices (ec->second.devices, ec->second.disp, disp); + + } + + } + + } +} + +void NetlistDeviceExtractor::push_new_devices (const db::Vector &disp_cache) +{ + db::CplxTrans dbu = db::CplxTrans (mp_layout->dbu ()); + db::VCplxTrans dbu_inv = dbu.inverted (); + + for (std::map >::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) { + + db::Device *device = d->second.first; + + db::Vector disp = dbu_inv * device->position () - db::Point (); + device->set_position (device->position () + dbu * disp_cache); + + DeviceCellKey key; + + for (geometry_per_terminal_type::const_iterator t = d->second.second.begin (); t != d->second.second.end (); ++t) { + std::map > > = key.geometry [t->first]; + for (geometry_per_layer_type::const_iterator l = t->second.begin (); l != t->second.end (); ++l) { + std::set &gl = gt [l->first]; + for (std::vector::const_iterator p = l->second.begin (); p != l->second.end (); ++p) { + db::PolygonRef pr = *p; + pr.transform (db::PolygonRef::trans_type (-disp)); + gl.insert (pr); + } + } + } + + const std::vector &pd = mp_device_class->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + key.parameters.insert (std::make_pair (p->id (), device->parameter_value (p->id ()))); + } + + db::PropertiesRepository::properties_set ps; + + std::map >::iterator c = m_device_cells.find (key); + if (c == m_device_cells.end ()) { + + std::string cell_name = "D$" + mp_device_class->name (); + db::Cell &device_cell = mp_layout->cell (mp_layout->add_cell (cell_name.c_str ())); + + db::DeviceAbstract *dm = new db::DeviceAbstract (mp_device_class, mp_layout->cell_name (device_cell.cell_index ())); + m_netlist->add_device_abstract (dm); + dm->set_cell_index (device_cell.cell_index ()); + + c = m_device_cells.insert (std::make_pair (key, std::make_pair (device_cell.cell_index (), dm))).first; + + // attach the device class ID to the cell + ps.clear (); + ps.insert (std::make_pair (m_device_class_propname_id, tl::Variant (mp_device_class->name ()))); + device_cell.prop_id (mp_layout->properties_repository ().properties_id (ps)); + + for (geometry_per_terminal_type::const_iterator t = d->second.second.begin (); t != d->second.second.end (); ++t) { + + // Build a property set for the device terminal ID + ps.clear (); + ps.insert (std::make_pair (m_terminal_id_propname_id, tl::Variant (t->first))); + db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); + + // build the cell shapes + for (geometry_per_layer_type::const_iterator l = t->second.begin (); l != t->second.end (); ++l) { + + db::Shapes &shapes = device_cell.shapes (l->first); + for (std::vector::const_iterator s = l->second.begin (); s != l->second.end (); ++s) { + db::PolygonRef pr = *s; + pr.transform (db::PolygonRef::trans_type (-disp)); + shapes.insert (db::PolygonRefWithProperties (pr, pi)); + } + + } + + } + + } + + // make the cell index known to the device + device->set_device_abstract (c->second.second); + + // Build a property set for the device ID + ps.clear (); + ps.insert (std::make_pair (m_device_id_propname_id, tl::Variant (d->first))); + db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); + + db::CellInstArrayWithProperties inst (db::CellInstArray (db::CellInst (c->second.first), db::Trans (disp_cache + disp)), pi); + mp_layout->cell (m_cell_index).insert (inst); + + } +} + +void NetlistDeviceExtractor::push_cached_devices (const tl::vector &cached_devices, const db::Vector &disp_cache, const db::Vector &new_disp) +{ + db::CplxTrans dbu = db::CplxTrans (mp_layout->dbu ()); + db::VCplxTrans dbu_inv = dbu.inverted (); + db::PropertiesRepository::properties_set ps; + + for (std::vector::const_iterator d = cached_devices.begin (); d != cached_devices.end (); ++d) { + + db::Device *cached_device = *d; + db::Vector disp = dbu_inv * cached_device->position () - disp_cache - db::Point (); + + db::Device *device = new db::Device (*cached_device); + mp_circuit->add_device (device); + device->set_position (device->position () + dbu * (new_disp - disp_cache)); + + // Build a property set for the device ID + ps.clear (); + ps.insert (std::make_pair (m_device_id_propname_id, tl::Variant (device->id ()))); + db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); + + db::CellInstArrayWithProperties inst (db::CellInstArray (db::CellInst (device->device_abstract ()->cell_index ()), db::Trans (new_disp + disp)), pi); + mp_layout->cell (m_cell_index).insert (inst); + + } +} + +void NetlistDeviceExtractor::setup () +{ + // .. the default implementation does nothing .. +} + +db::Connectivity NetlistDeviceExtractor::get_connectivity (const db::Layout & /*layout*/, const std::vector & /*layers*/) const +{ + // .. the default implementation does nothing .. + return db::Connectivity (); +} + +void NetlistDeviceExtractor::extract_devices (const std::vector & /*layer_geometry*/) +{ + // .. the default implementation does nothing .. +} + +void NetlistDeviceExtractor::register_device_class (DeviceClass *device_class) +{ + if (mp_device_class != 0) { + throw tl::Exception (tl::to_string (tr ("Device class already set"))); + } + if (m_name.empty ()) { + throw tl::Exception (tl::to_string (tr ("No device extractor/device class name set"))); + } + + tl_assert (device_class != 0); + mp_device_class = device_class; + mp_device_class->set_name (m_name); + + tl_assert (m_netlist.get () != 0); + m_netlist->add_device_class (device_class); +} + +void NetlistDeviceExtractor::define_layer (const std::string &name, const std::string &description) +{ + m_layer_definitions.push_back (db::NetlistDeviceExtractorLayerDefinition (name, description, m_layer_definitions.size ())); +} + +Device *NetlistDeviceExtractor::create_device () +{ + if (mp_device_class == 0) { + throw tl::Exception (tl::to_string (tr ("No device class registered"))); + } + + tl_assert (mp_circuit != 0); + Device *device = new Device (mp_device_class); + mp_circuit->add_device (device); + return device; +} + +void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t geometry_index, const db::Polygon &polygon) +{ + tl_assert (mp_layout != 0); + tl_assert (geometry_index < m_layers.size ()); + unsigned int layer_index = m_layers [geometry_index]; + + db::PolygonRef pr (polygon, mp_layout->shape_repository ()); + std::pair &dd = m_new_devices[device->id ()]; + dd.first = device; + dd.second[terminal_id][layer_index].push_back (pr); +} + +void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Box &box) +{ + define_terminal (device, terminal_id, layer_index, db::Polygon (box)); +} + +void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Point &point) +{ + // NOTE: we add one DBU to the "point" to prevent it from vanishing + db::Vector dv (1, 1); + define_terminal (device, terminal_id, layer_index, db::Polygon (db::Box (point - dv, point + dv))); +} + +std::string NetlistDeviceExtractor::cell_name () const +{ + if (layout ()) { + return layout ()->cell_name (cell_index ()); + } else { + return std::string (); + } +} + +void NetlistDeviceExtractor::error (const std::string &msg) +{ + m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); + + if (tl::verbosity () >= 20) { + tl::error << m_errors.back ().to_string (); + } +} + +void NetlistDeviceExtractor::error (const std::string &msg, const db::DPolygon &poly) +{ + m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); + m_errors.back ().set_geometry (poly); + + if (tl::verbosity () >= 20) { + tl::error << m_errors.back ().to_string (); + } +} + +void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg) +{ + m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); + m_errors.back ().set_category_name (category_name); + m_errors.back ().set_category_description (category_description); + + if (tl::verbosity () >= 20) { + tl::error << m_errors.back ().to_string (); + } +} + +void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::DPolygon &poly) +{ + m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); + m_errors.back ().set_category_name (category_name); + m_errors.back ().set_category_description (category_description); + m_errors.back ().set_geometry (poly); + + if (tl::verbosity () >= 20) { + tl::error << m_errors.back ().to_string (); + } +} + +} diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h new file mode 100644 index 000000000..eba997f58 --- /dev/null +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -0,0 +1,546 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbNetlistDeviceExtractor +#define _HDR_dbNetlistDeviceExtractor + +#include "dbCommon.h" +#include "dbNetlist.h" +#include "dbLayout.h" +#include "dbHierNetworkProcessor.h" +#include "dbDeepShapeStore.h" +#include "dbRegion.h" + +#include "gsiObject.h" + +namespace db +{ + +/** + * @brief An error object for the netlist device extractor + * + * The device extractor will keep errors using objects of this kind. + */ +class DB_PUBLIC NetlistDeviceExtractorError +{ +public: + /** + * @brief Creates an error + */ + NetlistDeviceExtractorError (); + + /** + * @brief Creates an error with a cell name and a message (the minimum information) + */ + NetlistDeviceExtractorError (const std::string &cell_name, const std::string &msg); + + /** + * @brief The category name of the error + * Specifying the category name is optional. If a category is given, it will be used for + * the report. + */ + const std::string &category_name () const + { + return m_category_name; + } + + /** + * @brief Sets the category name + */ + void set_category_name (const std::string &s) + { + m_category_name = s; + } + + /** + * @brief The category description of the error + * Specifying the category description is optional. If a category is given, this attribute will + * be used for the category description. + */ + const std::string &category_description () const + { + return m_category_description; + } + + /** + * @brief Sets the category description + */ + void set_category_description (const std::string &s) + { + m_category_description = s; + } + + /** + * @brief Gets the geometry for this error + * Not all errors may specify a geometry. In this case, the polygon is empty. + */ + const db::DPolygon &geometry () const + { + return m_geometry; + } + + /** + * @brief Sets the geometry + */ + void set_geometry (const db::DPolygon &g) + { + m_geometry = g; + } + + /** + * @brief Gets the message for this error + */ + const std::string &message () const + { + return m_message; + } + + /** + * @brief Sets the message + */ + void set_message (const std::string &n) + { + m_message = n; + } + + /** + * @brief Gets the cell name the error occured in + */ + const std::string &cell_name () const + { + return m_cell_name; + } + + /** + * @brief Sets the cell name + */ + void set_cell_name (const std::string &n) + { + m_cell_name = n; + } + + /** + * @brief Formats this message for printing + */ + std::string to_string () const; + +private: + std::string m_cell_name; + std::string m_message; + db::DPolygon m_geometry; + std::string m_category_name, m_category_description; +}; + +/** + * @brief Specifies a single layer from the device extractor + */ +class DB_PUBLIC NetlistDeviceExtractorLayerDefinition +{ +public: + NetlistDeviceExtractorLayerDefinition () + : index (0) + { + // .. nothing yet .. + } + + NetlistDeviceExtractorLayerDefinition (const std::string &_name, const std::string &_description, size_t _index) + : name (_name), description (_description), index (_index) + { + // .. nothing yet .. + } + + /** + * @brief The formal name + */ + std::string name; + + /** + * @brief The human-readable description + */ + std::string description; + + /** + * @brief The index of the layer + */ + size_t index; +}; + +/** + * @brief Implements the device extraction for a specific setup + * + * This class can be reimplemented to provide the basic algorithms for + * device extraction. See the virtual methods below. + */ +class DB_PUBLIC NetlistDeviceExtractor + : public gsi::ObjectBase, public tl::Object +{ +public: + typedef std::list error_list; + typedef error_list::const_iterator error_iterator; + typedef std::vector layer_definitions; + typedef layer_definitions::const_iterator layer_definitions_iterator; + typedef std::map input_layers; + typedef db::hier_clusters hier_clusters_type; + + /** + * @brief Constructor + * + * The name is the name of the device class of the devices generated. + */ + NetlistDeviceExtractor (const std::string &name); + + /** + * @brief Destructor + */ + ~NetlistDeviceExtractor (); + + /** + * @brief Gets the name of the extractor and the device class + */ + const std::string &name () + { + return m_name; + } + + /** + * @brief Gets the property name for the device terminal annotation + * This name is used to attach the terminal ID to terminal shapes. + */ + static const tl::Variant &terminal_id_property_name (); + + /** + * @brief Gets the property name for the device id annotation + * This name is used to attach the device ID to instances. + */ + static const tl::Variant &device_id_property_name (); + + /** + * @brief Gets the property name for the device class annotation + * This name is used to attach the device class name to cells. + */ + static const tl::Variant &device_class_property_name (); + + /** + * @brief Performs the extraction + * + * layout and cell specify the layout and the top cell from which to perform the + * extraction. + * + * The netlist will be filled with circuits (unless not present yet) to represent the + * cells from the layout. + * + * Devices will be generated inside the netlist's circuits as they are extracted + * from the layout. Inside the layout, device terminal annotation shapes are created with the + * corresponding DeviceTerminalProperty objects attached. The will be used when extracting + * the nets later to associate nets with device terminals. + * + * The definition of the input layers is device class specific. + * + * NOTE: The extractor expects "PolygonRef" type layers. + */ + void extract (Layout &layout, Cell &cell, const std::vector &layers, Netlist *netlist, hier_clusters_type &clusters); + + /** + * @brief Extracts the devices from a list of regions + * + * This method behaves identical to the other "extract" method, but accepts + * named regions for input. These regions need to be of deep region type and + * originate from the same layout than the DeepShapeStore. + */ + void extract (DeepShapeStore &dss, unsigned int layout_index, const input_layers &layers, Netlist &netlist, hier_clusters_type &clusters); + + /** + * @brief Gets the error iterator, begin + */ + error_iterator begin_errors () + { + return m_errors.begin (); + } + + /** + * @brief Gets the error iterator, end + */ + error_iterator end_errors () + { + return m_errors.end (); + } + + /** + * @brief Returns true, if there are errors + */ + bool has_errors () const + { + return ! m_errors.empty (); + } + + /** + * @brief Gets the layer definition iterator, begin + */ + layer_definitions_iterator begin_layer_definitions () const + { + return m_layer_definitions.begin (); + } + + /** + * @brief Gets the layer definition iterator, end + */ + layer_definitions_iterator end_layer_definitions () const + { + return m_layer_definitions.end (); + } + + /** + * @brief Sets the name of the device class and the device extractor + */ + void set_name (const std::string &name) + { + m_name = name; + } + + /** + * @brief Sets up the extractor + * + * This method is supposed to set up the device extractor. This involves two basic steps: + * defining the device classe and setting up the device layers. + * + * Use "register_device_class" to register the device class you need. + * + * The device layers need to be defined by calling "define_layer" once or several times. + */ + virtual void setup (); + + /** + * @brief Gets the connectivity object used to extract the device geometry + * This method shall raise an error, if the input layer are not properly defined (e.g. + * too few etc.) + */ + virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector &layers) const; + + /** + * @brief Extracts the devices from the given shape cluster + * + * The shape cluster is a set of geometries belonging together in terms of the + * connectivity defined by "get_connectivity". The cluster might cover multiple devices, + * so the implementation needs to consider this case. The geometries are already merged. + * + * The implementation of this method shall use "create_device" to create new + * devices based on the geometry found. It shall use "define_terminal" to define + * terminals by which the nets extracted in the network extraction step connect + * to the new devices. + */ + virtual void extract_devices (const std::vector &layer_geometry); + + /** + * @brief Registers a device class + * The device class object will become owned by the netlist and must not be deleted by + * the caller. The name of the device class will be changed to the name given to + * the device extractor. + * This method shall be used inside the implementation of "setup" to register + * the device classes. + */ + void register_device_class (DeviceClass *device_class); + + /** + * @brief Defines a layer + * Each call will define one more layer for the device extraction. + * This method shall be used inside the implementation of "setup" to define + * the device layers. The actual geometries are later available to "extract_devices" + * in the order the layers are defined. + */ + void define_layer (const std::string &name, const std::string &description = std::string ()); + + /** + * @brief Creates a device + * The device object returned can be configured by the caller, e.g. set parameters. + * It will be owned by the netlist and must not be deleted by the caller. + */ + Device *create_device (); + + /** + * @brief Defines a device terminal in the layout (a polygon) + */ + void define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Polygon &polygon); + + /** + * @brief Defines a device terminal in the layout (a box) + */ + void define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Box &box); + + /** + * @brief Defines a point-like device terminal in the layout + */ + void define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Point &point); + + /** + * @brief Gets the database unit + */ + double dbu () const + { + return mp_layout->dbu (); + } + + /** + * @brief Gets the layout the shapes are taken from + * NOTE: this method is provided for testing purposes mainly. + */ + db::Layout *layout () + { + return mp_layout; + } + + /** + * @brief Gets the layout the shapes are taken from (const version) + * NOTE: this method is provided for testing purposes mainly. + */ + const db::Layout *layout () const + { + return mp_layout; + } + + /** + * @brief Gets the cell index of the current cell + * NOTE: this method is provided for testing purposes mainly. + */ + db::cell_index_type cell_index () const + { + return m_cell_index; + } + + /** + * @brief Issues an error with the given message + */ + void error (const std::string &msg); + + /** + * @brief Issues an error with the given message and error shape + */ + void error (const std::string &msg, const db::DPolygon &poly); + + /** + * @brief Issues an error with the given message and error shape + */ + void error (const std::string &msg, const db::Polygon &poly) + { + error (msg, poly.transformed (db::CplxTrans (dbu ()))); + } + + /** + * @brief Issues an error with the given category name, description and message + */ + void error (const std::string &category_name, const std::string &category_description, const std::string &msg); + + /** + * @brief Issues an error with the given category name, description and message and error shape + */ + void error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::DPolygon &poly); + + /** + * @brief Issues an error with the given category name, description and message and error shape + */ + void error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::Polygon &poly) + { + error (category_name, category_description, msg, poly.transformed (db::CplxTrans (dbu ()))); + } + + /** + * @brief Gets the name of the current cell + */ + std::string cell_name () const; + +private: + struct DeviceCellKey + { + DeviceCellKey () { } + + bool operator== (const DeviceCellKey &other) const + { + if (geometry != other.geometry) { + return false; + } + if (parameters != other.parameters) { + return false; + } + return true; + } + + bool operator< (const DeviceCellKey &other) const + { + if (geometry != other.geometry) { + return geometry < other.geometry; + } + if (parameters != other.parameters) { + return parameters < other.parameters; + } + return false; + } + + std::map > > geometry; + std::map parameters; + }; + + typedef std::map > geometry_per_layer_type; + typedef std::map geometry_per_terminal_type; + + tl::weak_ptr m_netlist; + db::Layout *mp_layout; + db::properties_id_type m_terminal_id_propname_id, m_device_id_propname_id, m_device_class_propname_id; + hier_clusters_type *mp_clusters; + db::cell_index_type m_cell_index; + db::Circuit *mp_circuit; + db::DeviceClass *mp_device_class; + std::string m_name; + layer_definitions m_layer_definitions; + std::vector m_layers; + error_list m_errors; + std::map > m_new_devices; + std::map > m_device_cells; + + // no copying + NetlistDeviceExtractor (const NetlistDeviceExtractor &); + NetlistDeviceExtractor &operator= (const NetlistDeviceExtractor &); + + /** + * @brief Initializes the extractor + * This method will produce the device classes required for the device extraction. + */ + void initialize (db::Netlist *nl); + + void extract_without_initialize (db::Layout &layout, db::Cell &cell, hier_clusters_type &clusters, const std::vector &layers); + void push_new_devices (const Vector &disp_cache); + void push_cached_devices (const tl::vector &cached_devices, const db::Vector &disp_cache, const db::Vector &new_disp); +}; + +} + +namespace tl +{ + +template<> struct type_traits : public tl::type_traits +{ + // mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +} + +#endif diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc new file mode 100644 index 000000000..91a22c45e --- /dev/null +++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc @@ -0,0 +1,162 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbNetlistDeviceExtractorClasses.h" +#include "dbNetlistDeviceClasses.h" + +namespace db +{ + +// --------------------------------------------------------------------------------- +// NetlistDeviceExtractorMOS3Transistor implementation + +NetlistDeviceExtractorMOS3Transistor::NetlistDeviceExtractorMOS3Transistor (const std::string &name) + : db::NetlistDeviceExtractor (name) +{ + // .. nothing yet .. +} + +void NetlistDeviceExtractorMOS3Transistor::setup () +{ + define_layer ("SD", "Source/drain diffusion"); + define_layer ("G", "Gate"); + define_layer ("P", "Poly"); + + register_device_class (new db::DeviceClassMOS3Transistor ()); +} + +db::Connectivity NetlistDeviceExtractorMOS3Transistor::get_connectivity (const db::Layout & /*layout*/, const std::vector &layers) const +{ + tl_assert (layers.size () >= 3); + + unsigned int diff = layers [0]; + unsigned int gate = layers [1]; + // not used for device recognition: poly (2), but used for producing the gate terminals + + // The layer definition is diff, gate + db::Connectivity conn; + // collect all connected diffusion shapes + conn.connect (diff, diff); + // collect all connected gate shapes + conn.connect (gate, gate); + // connect gate with diff to detect gate/diffusion boundary + conn.connect (diff, gate); + return conn; +} + +void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector &layer_geometry) +{ + const db::Region &rdiff = layer_geometry [0]; + const db::Region &rgates = layer_geometry [1]; + + for (db::Region::const_iterator p = rgates.begin_merged (); !p.at_end (); ++p) { + + db::Region rgate (*p); + rgate.set_base_verbosity (rgates.base_verbosity ()); + + db::Region rdiff2gate = rdiff.selected_interacting (rgate); + rdiff2gate.set_base_verbosity (rdiff.base_verbosity ()); + + if (rdiff2gate.empty ()) { + error (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p); + } else { + + unsigned int terminal_geometry_index = 0; + unsigned int gate_geometry_index = 2; + + if (rdiff2gate.size () != 2) { + error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting one gate shape (found %d) - gate shape ignored")), int (rdiff2gate.size ())), *p); + continue; + } + + db::Edges edges (rgate.edges () & rdiff2gate.edges ()); + if (edges.size () != 2) { + error (tl::sprintf (tl::to_string (tr ("Expected two edges interacting gate/diff (found %d) - width and length may be incorrect")), int (edges.size ())), *p); + continue; + } + + if (! p->is_box ()) { + error (tl::to_string (tr ("Gate shape is not a box - width and length may be incorrect")), *p); + } + + db::Device *device = create_device (); + + device->set_position (db::CplxTrans (dbu ()) * p->box ().center ()); + + device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, dbu () * edges.length () * 0.5); + device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, dbu () * (p->perimeter () - edges.length ()) * 0.5); + + int diff_index = 0; + for (db::Region::const_iterator d = rdiff2gate.begin (); !d.at_end () && diff_index < 2; ++d, ++diff_index) { + + // count the number of gate shapes attached to this shape and distribute the area of the + // diffusion region to the number of gates + size_t n = rgates.selected_interacting (db::Region (*d)).size (); + tl_assert (n > 0); + + device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_AS : db::DeviceClassMOS3Transistor::param_id_AD, dbu () * dbu () * d->area () / double (n)); + device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_PS : db::DeviceClassMOS3Transistor::param_id_PD, dbu () * d->perimeter () / double (n)); + + define_terminal (device, diff_index == 0 ? db::DeviceClassMOS3Transistor::terminal_id_S : db::DeviceClassMOS3Transistor::terminal_id_D, terminal_geometry_index, *d); + + } + + define_terminal (device, db::DeviceClassMOS3Transistor::terminal_id_G, gate_geometry_index, *p); + + // allow derived classes to modify the device + modify_device (*p, layer_geometry, device); + + // output the device for debugging + device_out (device, rdiff2gate, rgate); + + } + + } +} + +// --------------------------------------------------------------------------------- +// NetlistDeviceExtractorMOS4Transistor implementation + +NetlistDeviceExtractorMOS4Transistor::NetlistDeviceExtractorMOS4Transistor (const std::string &name) + : NetlistDeviceExtractorMOS3Transistor (name) +{ + // .. nothing yet .. +} + +void NetlistDeviceExtractorMOS4Transistor::setup () +{ + define_layer ("SD", "Source/drain diffusion"); + define_layer ("G", "Gate"); + define_layer ("P", "Poly"); + define_layer ("W", "Well"); + + register_device_class (new db::DeviceClassMOS4Transistor ()); +} + +void NetlistDeviceExtractorMOS4Transistor::modify_device (const db::Polygon &rgate, const std::vector & /*layer_geometry*/, db::Device *device) +{ + unsigned int well_geometry_index = 3; + define_terminal (device, db::DeviceClassMOS4Transistor::terminal_id_B, well_geometry_index, rgate); +} + +} diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.h b/src/db/db/dbNetlistDeviceExtractorClasses.h new file mode 100644 index 000000000..904bf37dd --- /dev/null +++ b/src/db/db/dbNetlistDeviceExtractorClasses.h @@ -0,0 +1,117 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbNetlistDeviceExtractorClasses +#define _HDR_dbNetlistDeviceExtractorClasses + +#include "dbNetlistDeviceExtractor.h" + +namespace db +{ + +/** + * @brief A device extractor for a three-terminal MOS transistor + * + * This class supplies the generic extractor for a MOS device. + * The device is defined by two basic input layers: the diffusion area + * (source and drain) and the gate area. It requires a third layer + * (poly) to put the gate terminals on. The separation between poly + * and gate allows separating the device recognition layer (gate) from the + * conductive layer. + * + * The device class produced by this extractor is DeviceClassMOS3Transistor. + * The extractor extracts the four parameters of this class: L, W, AS and AD. + * + * The diffusion area is distributed on the number of gates connecting to + * the particular source or drain area. + */ +class DB_PUBLIC NetlistDeviceExtractorMOS3Transistor + : public db::NetlistDeviceExtractor +{ +public: + NetlistDeviceExtractorMOS3Transistor (const std::string &name); + + virtual void setup (); + virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector &layers) const; + virtual void extract_devices (const std::vector &layer_geometry); + +protected: + /** + * @brief A callback when the device is produced + * This callback is provided as a debugging port + */ + virtual void device_out (const db::Device * /*device*/, const db::Region & /*diff*/, const db::Region & /*gate*/) + { + // .. no specific implementation .. + } + + /** + * @brief Allow derived classes to modify the device + */ + virtual void modify_device (const db::Polygon & /*rgate*/, const std::vector & /*layer_geometry*/, db::Device * /*device*/) + { + // .. no specific implementation .. + } + +}; + +/** + * @brief A device extractor for a four-terminal MOS transistor + * + * This class is like the MOS3Transistor extractor, but requires a forth + * input layer (Well). This layer will be used to output the bulk terminal. + * + * The device class produced by this extractor is DeviceClassMOS4Transistor. + * The extractor extracts the four parameters of this class: L, W, AS and AD. + */ +class DB_PUBLIC NetlistDeviceExtractorMOS4Transistor + : public NetlistDeviceExtractorMOS3Transistor +{ +public: + NetlistDeviceExtractorMOS4Transistor (const std::string &name); + + virtual void setup (); + +private: + virtual void modify_device (const db::Polygon &rgate, const std::vector &layer_geometry, db::Device *device); +}; + +} + +namespace tl +{ + +template<> struct type_traits : public tl::type_traits +{ + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +template<> struct type_traits : public tl::type_traits +{ + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +} + +#endif diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc new file mode 100644 index 000000000..e8ba2bade --- /dev/null +++ b/src/db/db/dbNetlistExtractor.cc @@ -0,0 +1,351 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbNetlistExtractor.h" +#include "dbDeepShapeStore.h" +#include "dbNetlistDeviceExtractor.h" + +namespace db +{ + +NetlistExtractor::NetlistExtractor () + : mp_clusters (0), mp_layout (0), mp_cell (0) +{ + // .. nothing yet .. +} + +static void +build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type net_name_id, tl::equivalence_clusters &eq) +{ + std::map > prop_by_name; + + for (db::PropertiesRepository::iterator i = layout->properties_repository ().begin (); i != layout->properties_repository ().end (); ++i) { + for (db::PropertiesRepository::properties_set::const_iterator p = i->second.begin (); p != i->second.end (); ++p) { + if (p->first == net_name_id) { + std::string nn = p->second.to_string (); + prop_by_name [nn].insert (i->first); + } + } + } + + for (std::map >::const_iterator pn = prop_by_name.begin (); pn != prop_by_name.end (); ++pn) { + std::set::const_iterator p = pn->second.begin (); + std::set::const_iterator p0 = p; + while (p != pn->second.end ()) { + eq.same (*p0, *p); + ++p; + } + } +} + +void +NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label) +{ + mp_clusters = &clusters; + mp_layout = &dss.const_layout (layout_index); + mp_cell = &dss.const_initial_cell (layout_index); + + // gets the text annotation property ID - + // this is how the texts are passed for annotating the net names + m_text_annot_name_id = std::pair (false, 0); + if (! dss.text_property_name ().is_nil ()) { + m_text_annot_name_id = mp_layout->properties_repository ().get_id_of_name (dss.text_property_name ()); + } + + m_terminal_annot_name_id = mp_layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::terminal_id_property_name ()); + m_device_annot_name_id = mp_layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::device_id_property_name ()); + + // the big part: actually extract the nets + + tl::equivalence_clusters net_name_equivalence; + if (m_text_annot_name_id.first && join_nets_by_label) { + build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, net_name_equivalence); + } + mp_clusters->build (*mp_layout, *mp_cell, db::ShapeIterator::Polygons, conn, &net_name_equivalence); + + // reverse lookup for Circuit vs. cell index + std::map circuits; + + // some circuits may be there because of device extraction + for (db::Netlist::circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + tl_assert (mp_layout->is_valid_cell_index (c->cell_index ())); + circuits.insert (std::make_pair (c->cell_index (), c.operator-> ())); + } + + std::map > pins_per_cluster_per_cell; + for (db::Layout::bottom_up_const_iterator cid = mp_layout->begin_bottom_up (); cid != mp_layout->end_bottom_up (); ++cid) { + + const connected_clusters_type &clusters = mp_clusters->clusters_per_cell (*cid); + if (clusters.empty ()) { + continue; + } + + db::DeviceAbstract *dm = nl.device_abstract_by_cell_index (*cid); + if (dm) { + // make the terminal to cluster ID connections for the device abstract from the device cells + make_device_abstract_connections (dm, clusters); + continue; + } + + // a cell makes a new circuit (or uses an existing one) + + db::Circuit *circuit = 0; + + std::map::const_iterator k = circuits.find (*cid); + if (k == circuits.end ()) { + circuit = new db::Circuit (); + nl.add_circuit (circuit); + circuit->set_name (mp_layout->cell_name (*cid)); + circuit->set_cell_index (*cid); + circuits.insert (std::make_pair (*cid, circuit)); + } else { + circuit = k->second; + } + + std::map &c2p = pins_per_cluster_per_cell [*cid]; + + std::map, db::SubCircuit *> subcircuits; + + for (connected_clusters_type::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { + + db::Net *net = new db::Net (); + net->set_cluster_id (*c); + circuit->add_net (net); + + const db::local_cluster::global_nets &gn = clusters.cluster_by_id (*c).get_global_nets (); + for (db::local_cluster::global_nets::const_iterator g = gn.begin (); g != gn.end (); ++g) { + assign_net_name (conn.global_net_name (*g), net); + } + + // make subcircuit connections (also make the subcircuits if required) from the connections of the clusters + make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell); + + // connect devices + connect_devices (circuit, clusters, *c, net); + + // collect labels to net names + collect_labels (clusters, *c, net); + + if (! clusters.is_root (*c)) { + // a non-root cluster makes a pin + size_t pin_id = make_pin (circuit, net); + c2p.insert (std::make_pair (*c, pin_id)); + } + + } + + } +} + +void +NetlistExtractor::make_device_abstract_connections (db::DeviceAbstract *dm, const connected_clusters_type &clusters) +{ + // make the terminal to cluster ID connections for the device abstract from the device cells + + if (m_terminal_annot_name_id.first) { + + for (connected_clusters_type::const_iterator dc = clusters.begin (); dc != clusters.end (); ++dc) { + + for (local_cluster_type::attr_iterator a = dc->begin_attr (); a != dc->end_attr (); ++a) { + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + if (j->first == m_terminal_annot_name_id.second) { + dm->set_cluster_id_for_terminal (j->second.to (), dc->id ()); + } + } + + } + + } + + } +} + +void NetlistExtractor::collect_labels (const connected_clusters_type &clusters, + size_t cid, + db::Net *net) +{ + // collect the properties - we know that the cluster attributes are property ID's because the + // cluster processor converts shape property IDs to attributes + + const local_cluster_type &lc = clusters.cluster_by_id (cid); + for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) { + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + + if (m_text_annot_name_id.first && j->first == m_text_annot_name_id.second) { + assign_net_name (j->second.to_string (), net); + } + + } + + } +} + +bool NetlistExtractor::instance_is_device (db::properties_id_type prop_id) const +{ + if (! prop_id || ! m_device_annot_name_id.first) { + return false; + } + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (prop_id); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + if (j->first == m_device_annot_name_id.second) { + return true; + } + } + + return false; +} + +db::Device *NetlistExtractor::device_from_instance (db::properties_id_type prop_id, db::Circuit *circuit) const +{ + if (! prop_id || ! m_device_annot_name_id.first) { + return 0; + } + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (prop_id); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + if (j->first == m_device_annot_name_id.second) { + return circuit->device_by_id (j->second.to ()); + } + } + + return 0; +} + +void NetlistExtractor::connect_devices (db::Circuit *circuit, + const connected_clusters_type &clusters, + size_t cid, + db::Net *net) +{ + const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (cid); + for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { + + db::cell_index_type inst_cell_index = i->inst_cell_index (); + db::properties_id_type inst_prop_id = i->inst_prop_id (); + + // only consider devices in this pass + db::Device *device = device_from_instance (inst_prop_id, circuit); + if (! device) { + continue; + } + + const db::local_cluster &dc = mp_clusters->clusters_per_cell (inst_cell_index).cluster_by_id (i->id ()); + + // connect the net to the terminal of the device: take the terminal ID from the properties on the + // device cluster + for (local_cluster_type::attr_iterator a = dc.begin_attr (); a != dc.end_attr (); ++a) { + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + + if (m_terminal_annot_name_id.first && j->first == m_terminal_annot_name_id.second) { + + size_t tid = j->second.to (); + device->connect_terminal (tid, net); + + } + + } + + } + + } +} + +void NetlistExtractor::make_and_connect_subcircuits (db::Circuit *circuit, + const connected_clusters_type &clusters, + size_t cid, + db::Net *net, + std::map, db::SubCircuit *> &subcircuits, + const std::map &circuits, + const std::map > &pins_per_cluster) +{ + const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (cid); + for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { + + db::cell_index_type inst_cell_index = i->inst_cell_index (); + db::properties_id_type inst_prop_id = i->inst_prop_id (); + const db::ICplxTrans &inst_trans = i->inst_trans (); + + // skip devices in this pass + if (instance_is_device (inst_prop_id)) { + continue; + } + + db::SubCircuit *subcircuit = 0; + + std::pair subcircuit_key (inst_cell_index, inst_trans); + + std::map, db::SubCircuit *>::const_iterator j = subcircuits.find (subcircuit_key); + if (j == subcircuits.end ()) { + + // make subcircuit if required + + std::map::const_iterator k = circuits.find (inst_cell_index); + tl_assert (k != circuits.end ()); // because we walk bottom-up + + subcircuit = new db::SubCircuit (k->second); + db::CplxTrans dbu_trans (mp_layout->dbu ()); + subcircuit->set_trans (dbu_trans * inst_trans * dbu_trans.inverted ()); + circuit->add_subcircuit (subcircuit); + subcircuits.insert (std::make_pair (subcircuit_key, subcircuit)); + + } else { + subcircuit = j->second; + } + + // create the pin connection to the subcircuit + std::map >::const_iterator icc2p = pins_per_cluster.find (inst_cell_index); + tl_assert (icc2p != pins_per_cluster.end ()); + std::map::const_iterator ip = icc2p->second.find (i->id ()); + tl_assert (ip != icc2p->second.end ()); + subcircuit->connect_pin (ip->second, net); + + } +} + +size_t NetlistExtractor::make_pin (db::Circuit *circuit, db::Net *net) +{ + size_t pin_id = circuit->add_pin (net->name ()).id (); + net->add_pin (db::NetPinRef (pin_id)); + + circuit->connect_pin (pin_id, net); + + return pin_id; +} + +void NetlistExtractor::assign_net_name (const std::string &n, db::Net *net) +{ + std::string nn = n; + if (! nn.empty ()) { + if (! net->name ().empty ()) { + nn = net->name () + "," + nn; + } + net->set_name (nn); + } +} + +} diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h new file mode 100644 index 000000000..edbe6a1c6 --- /dev/null +++ b/src/db/db/dbNetlistExtractor.h @@ -0,0 +1,165 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbNetlistExtractor +#define _HDR_dbNetlistExtractor + +#include "dbCommon.h" +#include "dbHierNetworkProcessor.h" + +#include + +namespace db +{ + +class DeepShapeStore; +class Netlist; +class Circuit; +class SubCircuit; +class Net; +class Device; +class DeviceAbstract; + +/** + * @brief The Netlist Extractor + * + * This is the main object responsible for extracting nets from a layout. + * + * The layout needs to be present as a DeepShapeStore shadow layout. Use hierarchical regions + * (db::Region build with a DeepShapeStore) to populate the shape store. + * + * The extraction requires a connectivity definition through db::Connectivity. + * + * In addition, the device extraction needs to happen before net extraction. + * Device extraction will pre-fill the netlist with circuits and devices and + * annotate the layout with terminal shapes, so the net extraction can connect + * to the device terminals. + * + * If the deep shape store has been configured to supply text label annotated + * markers (DeepShapeStore::set_text_property_name and DeepShapeStore::set_text_enlargement + * to at least 1), texts from layers included in the connectivity will be extracted + * as net names. If multiple texts are present, the names will be concatenated using + * comma separators. + * + * Upon extraction, the given netlist is filled with circuits (unless present already), + * subcircuits, pins and of course nets. This object also supplies access to the net's + * geometries through the clusters() method. This method delivers a hierarchical + * cluster object. The nets refer to specific clusters through their "cluster_id" + * attribute. + */ +class DB_PUBLIC NetlistExtractor +{ +public: + typedef db::hier_clusters hier_clusters_type; + typedef db::connected_clusters connected_clusters_type; + typedef db::local_cluster local_cluster_type; + + /** + * @brief NetExtractor constructor + */ + NetlistExtractor (); + + /** + * @brief Extract the nets + * See the class description for more details. + */ + void extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label = true); + +private: + hier_clusters_type *mp_clusters; + const db::Layout *mp_layout; + const db::Cell *mp_cell; + std::pair m_text_annot_name_id; + std::pair m_device_annot_name_id; + std::pair m_terminal_annot_name_id; + + void assign_net_name (const std::string &n, db::Net *net); + bool instance_is_device (db::properties_id_type prop_id) const; + db::Device *device_from_instance (db::properties_id_type prop_id, db::Circuit *circuit) const; + + /** + * @brief Make a pin connection from clusters + * + * Non-root clusters make a pin. This function creates the pin inside the given circuit. It + * returns the new pin's ID. + */ + size_t make_pin (db::Circuit *circuit, db::Net *net); + + /** + * @brief Turns the connections of a cluster into subcircuit instances + * + * Walks through the connections of a cluster and turns the connections into subcircuit pin + * connections. This will also create new subcircuit instances. + * + * Needs: + * - circuit: the current circuit that is worked on + * - clusters: the connected clusters from the net extraction step (nets plus + * connections to child cell clusters inside the current cell) + * - cid: the current cluster ID in "clusters" + * - net: the net being built + * - circuits: a lookup table of circuits vs. cell index (reverse lookup) + * - pins_per_cluster: a per-cell, reverse lookup table for the pin id per child cell clusters + * (used to find the pin ID of a subcircuit from the child cell cluster ID) + * Updates: + * - subcircuits: An cell instance to SubCircuit lookup table + */ + void make_and_connect_subcircuits (db::Circuit *circuit, + const connected_clusters_type &clusters, + size_t cid, + db::Net *net, + std::map, SubCircuit *> &subcircuits, + const std::map &circuits, + const std::map > &pins_per_cluster); + + /** + * @brief Connects the devices + * + * Devices are identified by special cells. These carry a property with the + * device class name. Inside these cells, the terminals are identified by special clusters. + * The terminal IDs are coded on these clusters are a property. + */ + void connect_devices (db::Circuit *circuit, + const connected_clusters_type &clusters, + size_t cid, + db::Net *net); + + /** + * @brief Attaches net names from text properties + * + * Texts (labels) are represented by special shapes. The texts are kept as properties. + * This method will collect all these labels and attach them to the nets as (alternative) + * names. + */ + void collect_labels (const connected_clusters_type &clusters, + size_t cid, + db::Net *net); + + /** + * @brief Makes the terminal to cluster ID connections of the device abstract + */ + void make_device_abstract_connections (db::DeviceAbstract *dm, const connected_clusters_type &clusters); + +}; + +} + +#endif diff --git a/src/db/db/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc new file mode 100644 index 000000000..e2c7586fa --- /dev/null +++ b/src/db/db/dbNetlistSpiceWriter.cc @@ -0,0 +1,404 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbNetlistSpiceWriter.h" +#include "dbNetlist.h" +#include "dbNetlistDeviceClasses.h" + +#include "tlStream.h" + +#include + +namespace db +{ + +// -------------------------------------------------------------------------------- + +NetlistSpiceWriterDelegate::NetlistSpiceWriterDelegate () + : mp_writer (0) +{ + // .. nothing yet .. +} + +NetlistSpiceWriterDelegate::~NetlistSpiceWriterDelegate () +{ + // .. nothing yet .. +} + +std::string NetlistSpiceWriterDelegate::net_to_string (const db::Net *net) const +{ + tl_assert (mp_writer != 0); + return mp_writer->net_to_string (net); +} + +std::string NetlistSpiceWriterDelegate::format_name (const std::string &name) const +{ + tl_assert (mp_writer != 0); + return mp_writer->format_name (name); +} + +void NetlistSpiceWriterDelegate::emit_line (const std::string &line) const +{ + tl_assert (mp_writer != 0); + mp_writer->emit_line (line); +} + +void NetlistSpiceWriterDelegate::emit_comment (const std::string &comment) const +{ + tl_assert (mp_writer != 0); + mp_writer->emit_comment (comment); +} + +void NetlistSpiceWriterDelegate::attach_writer (NetlistSpiceWriter *writer) +{ + mp_writer = writer; +} + +void NetlistSpiceWriterDelegate::write_header () const +{ + // .. nothing yet .. +} + +void NetlistSpiceWriterDelegate::write_device_intro (const db::DeviceClass &) const +{ + // .. nothing yet .. +} + +void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const +{ + const db::DeviceClass *dc = dev.device_class (); + const db::DeviceClassCapacitor *cap = dynamic_cast (dc); + const db::DeviceClassInductor *ind = dynamic_cast (dc); + const db::DeviceClassResistor *res = dynamic_cast (dc); + const db::DeviceClassDiode *diode = dynamic_cast (dc); + const db::DeviceClassMOS3Transistor *mos3 = dynamic_cast (dc); + const db::DeviceClassMOS4Transistor *mos4 = dynamic_cast (dc); + + std::ostringstream os; + + if (cap) { + + os << "C"; + os << format_name (dev.expanded_name ()); + os << format_terminals (dev); + os << " "; + os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassCapacitor::param_id_C)); + + } else if (ind) { + + os << "L"; + os << format_name (dev.expanded_name ()); + os << format_terminals (dev); + os << " "; + os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassInductor::param_id_L)); + + } else if (res) { + + os << "R"; + os << format_name (dev.expanded_name ()); + os << format_terminals (dev); + os << " "; + os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassResistor::param_id_R)); + + } else if (diode) { + + os << "D"; + os << format_name (dev.expanded_name ()); + os << format_terminals (dev); + + // Use "D" + device class name for the model + os << " D"; + os << format_name (dev.device_class ()->name ()); + + } else if (mos3 || mos4) { + + os << "M"; + os << format_name (dev.expanded_name ()); + os << format_terminals (dev); + + if (! mos4) { + // we assume for the MOS3 type the bulk is connected to Source + os << " "; + os << net_to_string (dev.net_for_terminal (db::DeviceClassMOS3Transistor::terminal_id_S)); + } + + // Use "M" + device class name for the model + os << " M"; + os << format_name (dev.device_class ()->name ()); + + os << " L=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_L)); + os << " W=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_W)); + os << " AS=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AS)); + os << " AD=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AD)); + os << " PS=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_PS)); + os << " PD=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_PD)); + + } else { + + // Write unknown devices as subcircuits (CAUTION: potential name clash) + os << "XD_" << format_name (dev.expanded_name ()); + os << format_terminals (dev); + os << " "; + os << format_name (dev.device_class ()->name ()); + os << " PARAMS:"; + os << format_params (dev); + + } + + emit_line (os.str ()); +} + +std::string NetlistSpiceWriterDelegate::format_terminals (const db::Device &dev) const +{ + std::ostringstream os; + + const std::vector &td = dev.device_class ()->terminal_definitions (); + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + os << " " << net_to_string (dev.net_for_terminal (i->id ())); + } + + return os.str (); +} + +std::string NetlistSpiceWriterDelegate::format_params (const db::Device &dev) const +{ + std::ostringstream os; + + const std::vector &pd = dev.device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + os << " " << i->name () << "=" << tl::to_string (dev.parameter_value (i->id ())); + } + + return os.str (); +} + +// -------------------------------------------------------------------------------- + +NetlistSpiceWriter::NetlistSpiceWriter (NetlistSpiceWriterDelegate *delegate) + : mp_netlist (0), mp_stream (0), mp_delegate (delegate) +{ + static NetlistSpiceWriterDelegate std_delegate; + if (! delegate) { + mp_delegate.reset (&std_delegate); + } +} + +NetlistSpiceWriter::~NetlistSpiceWriter () +{ + // .. nothing yet .. +} + +void NetlistSpiceWriter::write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description) +{ + mp_stream = &stream; + mp_netlist = &netlist; + mp_delegate->attach_writer (this); + + try { + + do_write (description); + + mp_stream = 0; + mp_netlist = 0; + mp_delegate->attach_writer (0); + + } catch (...) { + + mp_stream = 0; + mp_netlist = 0; + mp_delegate->attach_writer (0); + throw; + + } +} + +std::string NetlistSpiceWriter::net_to_string (const db::Net *net) const +{ + std::map::const_iterator n = m_net_to_spice_id.find (net); + if (! net || n == m_net_to_spice_id.end ()) { + // TODO: this should assert or similar + return "0"; + } else { + return tl::to_string (n->second); + } +} + +void NetlistSpiceWriter::emit_line (const std::string &line) const +{ + tl_assert (mp_stream != 0); + + int max_length = 80; + bool first = true; + + const char *cp = line.c_str (); + do { + + const char *cpn = cp; + const char *cspc = 0; + int c = 0; + + int l = first ? max_length : max_length - 2; + while (*cpn && (c < l || ! cspc)) { + if (isspace (*cpn)) { + cspc = cpn; + } + ++c; + ++cpn; + } + + if (! first) { + *mp_stream << "+ "; + } + + if (! *cpn) { + *mp_stream << cp << "\n"; + break; + } else { + while (*cp && (cp != cspc || ! cspc)) { + *mp_stream << *cp++; + } + *mp_stream << "\n"; + } + + first = false; + + while (*cp && isspace (*cp)) { + ++cp; + } + + } while (*cp); +} + +void NetlistSpiceWriter::emit_comment (const std::string &comment) const +{ + tl_assert (mp_stream != 0); + + // TODO: should do some line breaking or reduction for long lines + // or when lines contain newlines + *mp_stream << "* " << comment << "\n"; +} + +std::string NetlistSpiceWriter::format_name (const std::string &s) const +{ + // TODO: escape or replace special chars + return s; +} + +void NetlistSpiceWriter::do_write (const std::string &description) +{ + if (! description.empty ()) { + emit_comment (description); + } + + mp_delegate->write_header (); + + for (db::Netlist::const_device_class_iterator dc = mp_netlist->begin_device_classes (); dc != mp_netlist->end_device_classes (); ++dc) { + mp_delegate->write_device_intro (*dc); + } + + for (db::Netlist::const_top_down_circuit_iterator c = mp_netlist->begin_top_down (); c != mp_netlist->end_top_down (); ++c) { + + const db::Circuit &circuit = **c; + + // assign internal node numbers to the nets + m_net_to_spice_id.clear (); + size_t nid = 0; + for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { + m_net_to_spice_id.insert (std::make_pair (n.operator-> (), ++nid)); + } + + write_circuit_header (circuit); + + for (db::Circuit::const_subcircuit_iterator i = circuit.begin_subcircuits (); i != circuit.end_subcircuits (); ++i) { + write_subcircuit_call (*i); + } + + for (db::Circuit::const_device_iterator i = circuit.begin_devices (); i != circuit.end_devices (); ++i) { + + // TODO: make this configurable? + std::string comment = "device instance " + i->expanded_name () + " " + i->position ().to_string () + " " + i->device_class ()->name (); + emit_comment (comment); + + mp_delegate->write_device (*i); + + } + + write_circuit_end (circuit); + + } +} + +void NetlistSpiceWriter::write_subcircuit_call (const db::SubCircuit &subcircuit) const +{ + // TODO: make this configurable? + std::string comment = "cell instance " + subcircuit.expanded_name() + " " + subcircuit.trans ().to_string (); + emit_comment (comment); + + std::ostringstream os; + os << "X"; + os << format_name (subcircuit.expanded_name ()); + + for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) { + os << " "; + os << net_to_string (subcircuit.net_for_pin (p->id ())); + } + + os << " "; + os << format_name (subcircuit.circuit_ref ()->name ()); + + emit_line (os.str ()); +} + +void NetlistSpiceWriter::write_circuit_header (const db::Circuit &circuit) const +{ + emit_line (""); + + emit_comment ("cell " + circuit.name ()); + for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) { + emit_comment ("pin " + p->name ()); + } + + std::ostringstream os; + + os << ".SUBCKT "; + os << format_name (circuit.name ()); + + for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) { + os << " "; + os << net_to_string (circuit.net_for_pin (p->id ())); + } + + emit_line (os.str ()); + + for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { + if (! n->name ().empty ()) { + emit_comment ("net " + net_to_string (n.operator-> ()) + " " + n->name ()); + } + } +} + +void NetlistSpiceWriter::write_circuit_end (const db::Circuit &circuit) const +{ + emit_line (".ENDS " + format_name (circuit.name ())); +} + +} diff --git a/src/db/db/dbNetlistSpiceWriter.h b/src/db/db/dbNetlistSpiceWriter.h new file mode 100644 index 000000000..2a5dfb418 --- /dev/null +++ b/src/db/db/dbNetlistSpiceWriter.h @@ -0,0 +1,110 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbNetlistSpiceWriter +#define HDR_dbNetlistSpiceWriter + +#include "dbCommon.h" +#include "dbNetlistWriter.h" +#include "tlObject.h" + +#include +#include + +namespace db +{ + +class DeviceClass; +class Device; +class Net; +class NetlistSpiceWriter; +class Circuit; +class SubCircuit; + +/** + * @brief A device writer delegate for the SPICE writer + * + * This delegate is supposed to provide the mapping of devices to parametrized SPICE subcircuits. + * It is generic, so it can be used for other cases of device mapping. + */ +class DB_PUBLIC NetlistSpiceWriterDelegate + : public tl::Object +{ +public: + NetlistSpiceWriterDelegate (); + virtual ~NetlistSpiceWriterDelegate (); + + virtual void write_header () const; + virtual void write_device_intro (const db::DeviceClass &cls) const; + virtual void write_device (const db::Device &dev) const; + + std::string net_to_string (const db::Net *net) const; + void emit_line (const std::string &line) const; + void emit_comment (const std::string &comment) const; + std::string format_name (const std::string &s) const; + std::string format_terminals (const db::Device &dev) const; + std::string format_params (const db::Device &dev) const; + +private: + friend class NetlistSpiceWriter; + + NetlistSpiceWriter *mp_writer; + + void attach_writer (NetlistSpiceWriter *writer); +}; + +/** + * @brief A SPICE format writer for netlists + * + * Specialization happens through the device writer delegate. + */ +class DB_PUBLIC NetlistSpiceWriter + : public NetlistWriter +{ +public: + NetlistSpiceWriter (NetlistSpiceWriterDelegate *delegate = 0); + virtual ~NetlistSpiceWriter (); + + virtual void write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description); + +private: + friend class NetlistSpiceWriterDelegate; + + const db::Netlist *mp_netlist; + tl::OutputStream *mp_stream; + tl::weak_ptr mp_delegate; + std::map m_net_to_spice_id; + + void do_write (const std::string &description); + + std::string net_to_string (const db::Net *net) const; + void emit_line (const std::string &line) const; + void emit_comment (const std::string &comment) const; + std::string format_name (const std::string &name) const; + void write_subcircuit_call (const db::SubCircuit &subcircuit) const; + void write_circuit_header (const db::Circuit &circuit) const; + void write_circuit_end (const db::Circuit &circuit) const; +}; + +} + +#endif diff --git a/src/db/db/dbNetlistUtils.h b/src/db/db/dbNetlistUtils.h new file mode 100644 index 000000000..47911283f --- /dev/null +++ b/src/db/db/dbNetlistUtils.h @@ -0,0 +1,127 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbNetlistUtils +#define _HDR_dbNetlistUtils + +#include "dbCommon.h" + +namespace db +{ + +/** + * @brief A getter for the ID of an object + */ +template +struct id_attribute +{ + typedef size_t attr_type; + size_t operator() (const T *t) const { return t->id (); } + bool has (const T * /*t*/) const { return true; } +}; + +/** + * @brief A getter for the cluster ID of an object + */ +template +struct cluster_id_attribute +{ + typedef size_t attr_type; + attr_type operator() (const T *t) const { return t->cluster_id (); } + bool has (const T * /*t*/) const { return true; } +}; + +/** + * @brief A getter for the cluster ID of an object + */ +template +struct cell_index_attribute +{ + typedef db::cell_index_type attr_type; + attr_type operator() (const T *t) const { return t->cell_index (); } + bool has (const T * /*t*/) const { return true; } +}; + +/** + * @brief A getter for the name of an object + */ +template +struct name_attribute +{ + typedef std::string attr_type; + const attr_type &operator() (const T *t) const { return t->name (); } + bool has (const T *t) const { return ! t->name ().empty (); } +}; + +/** + * @brief An id-to-object translation table + */ +template +class object_by_attr +{ +public: + typedef typename ATTR::attr_type attr_type; + typedef typename I::value_type value_type; + + object_by_attr (T *self, I (T::*bi) (), I (T::*ei) ()) : mp_self (self), m_bi (bi), m_ei (ei), m_valid (false) + { + // .. nothing yet .. + } + + void invalidate () + { + m_valid = false; + m_map.clear (); + } + + value_type *object_by (const attr_type &attr) const + { + if (! m_valid) { + validate (); + } + typename std::map::const_iterator m = m_map.find (attr); + return m == m_map.end () ? 0 : m->second; + } + +private: + T *mp_self; + I (T::*m_bi) (); + I (T::*m_ei) (); + mutable bool m_valid; + mutable std::map m_map; + + void validate () const + { + ATTR attr; + m_map.clear (); + for (I i = (mp_self->*m_bi) (); i != (mp_self->*m_ei) (); ++i) { + if (attr.has (i.operator-> ())) { + m_map.insert (std::make_pair (attr (i.operator-> ()), i.operator-> ())); + } + } + m_valid = true; + } +}; + +} + +#endif diff --git a/src/db/db/dbNetlistWriter.cc b/src/db/db/dbNetlistWriter.cc new file mode 100644 index 000000000..2c40a9c78 --- /dev/null +++ b/src/db/db/dbNetlistWriter.cc @@ -0,0 +1,31 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbNetlistWriter.h" + +namespace db +{ + + // .. nothing yet .. + +} diff --git a/src/db/db/dbNetlistWriter.h b/src/db/db/dbNetlistWriter.h new file mode 100644 index 000000000..a97f1fd2c --- /dev/null +++ b/src/db/db/dbNetlistWriter.h @@ -0,0 +1,90 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbNetlistWriter +#define HDR_dbNetlistWriter + +#include "dbCommon.h" +#include "tlTypeTraits.h" + +#include + +namespace tl +{ + class OutputStream; +} + +namespace db +{ + +class Netlist; + +/** + * @brief A common base class for netlist writers + */ +class DB_PUBLIC NetlistWriter +{ +public: + NetlistWriter () { } + virtual ~NetlistWriter () { } + + virtual void write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description = std::string ()) = 0; +}; + +} + +namespace tl +{ + +template <> +struct type_traits + : public tl::type_traits +{ + typedef tl::false_tag has_default_constructor; + typedef tl::false_tag has_copy_constructor; +}; + +} + +#endif diff --git a/src/db/db/dbObjectWithProperties.h b/src/db/db/dbObjectWithProperties.h index bb24a4748..ed7fa6a70 100644 --- a/src/db/db/dbObjectWithProperties.h +++ b/src/db/db/dbObjectWithProperties.h @@ -29,6 +29,7 @@ #include "dbPolygon.h" #include "dbPath.h" #include "dbEdge.h" +#include "dbEdgePair.h" #include "dbText.h" #include "dbBox.h" #include "dbArray.h" @@ -196,6 +197,9 @@ typedef object_with_properties DPathRefWithProperties; typedef object_with_properties EdgeWithProperties; typedef object_with_properties DEdgeWithProperties; +typedef object_with_properties EdgePairWithProperties; +typedef object_with_properties DEdgePairWithProperties; + typedef object_with_properties TextWithProperties; typedef object_with_properties DTextWithProperties; typedef object_with_properties TextRefWithProperties; diff --git a/src/db/db/dbOriginalLayerEdgePairs.cc b/src/db/db/dbOriginalLayerEdgePairs.cc new file mode 100644 index 000000000..76b9aaa6d --- /dev/null +++ b/src/db/db/dbOriginalLayerEdgePairs.cc @@ -0,0 +1,197 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbOriginalLayerEdgePairs.h" +#include "dbEdgePairs.h" +#include "tlInternational.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// OriginalLayerEdgePairs implementation + +namespace +{ + + class OriginalLayerEdgePairsIterator + : public EdgePairsIteratorDelegate + { + public: + OriginalLayerEdgePairsIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) + : m_rec_iter (iter), m_iter_trans (trans) + { + set (); + } + + virtual bool at_end () const + { + return m_rec_iter.at_end (); + } + + virtual void increment () + { + inc (); + set (); + } + + virtual const value_type *get () const + { + return &m_edge_pair; + } + + virtual EdgePairsIteratorDelegate *clone () const + { + return new OriginalLayerEdgePairsIterator (*this); + } + + private: + friend class EdgePairs; + + db::RecursiveShapeIterator m_rec_iter; + db::ICplxTrans m_iter_trans; + db::EdgePair m_edge_pair; + + void set () + { + while (! m_rec_iter.at_end () && ! m_rec_iter.shape ().is_edge_pair ()) { + ++m_rec_iter; + } + if (! m_rec_iter.at_end ()) { + m_rec_iter.shape ().edge_pair (m_edge_pair); + m_edge_pair.transform (m_iter_trans * m_rec_iter.trans ()); + } + } + + void inc () + { + if (! m_rec_iter.at_end ()) { + ++m_rec_iter; + } + } + }; + +} + +OriginalLayerEdgePairs::OriginalLayerEdgePairs () + : AsIfFlatEdgePairs () +{ + init (); +} + +OriginalLayerEdgePairs::OriginalLayerEdgePairs (const OriginalLayerEdgePairs &other) + : AsIfFlatEdgePairs (other), + m_iter (other.m_iter), + m_iter_trans (other.m_iter_trans) +{ + // .. nothing yet .. +} + +OriginalLayerEdgePairs::OriginalLayerEdgePairs (const RecursiveShapeIterator &si) + : AsIfFlatEdgePairs (), m_iter (si) +{ + init (); +} + +OriginalLayerEdgePairs::OriginalLayerEdgePairs (const RecursiveShapeIterator &si, const db::ICplxTrans &trans) + : AsIfFlatEdgePairs (), m_iter (si), m_iter_trans (trans) +{ + init (); +} + +OriginalLayerEdgePairs::~OriginalLayerEdgePairs () +{ + // .. nothing yet .. +} + +EdgePairsDelegate * +OriginalLayerEdgePairs::clone () const +{ + return new OriginalLayerEdgePairs (*this); +} + +EdgePairsIteratorDelegate * +OriginalLayerEdgePairs::begin () const +{ + return new OriginalLayerEdgePairsIterator (m_iter, m_iter_trans); +} + +std::pair +OriginalLayerEdgePairs::begin_iter () const +{ + return std::make_pair (m_iter, m_iter_trans); +} + +bool +OriginalLayerEdgePairs::empty () const +{ + return m_iter.at_end (); +} + +const db::EdgePair * +OriginalLayerEdgePairs::nth (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat collections"))); +} + +bool +OriginalLayerEdgePairs::has_valid_edge_pairs () const +{ + return false; +} + +const db::RecursiveShapeIterator * +OriginalLayerEdgePairs::iter () const +{ + return &m_iter; +} + +bool +OriginalLayerEdgePairs::equals (const EdgePairs &other) const +{ + const OriginalLayerEdgePairs *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return true; + } else { + return AsIfFlatEdgePairs::equals (other); + } +} + +bool +OriginalLayerEdgePairs::less (const EdgePairs &other) const +{ + const OriginalLayerEdgePairs *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return false; + } else { + return AsIfFlatEdgePairs::less (other); + } +} + +void +OriginalLayerEdgePairs::init () +{ + // .. nothing yet .. +} + +} diff --git a/src/db/db/dbOriginalLayerEdgePairs.h b/src/db/db/dbOriginalLayerEdgePairs.h new file mode 100644 index 000000000..763da04a6 --- /dev/null +++ b/src/db/db/dbOriginalLayerEdgePairs.h @@ -0,0 +1,75 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbOriginalLayerEdgePairs +#define HDR_dbOriginalLayerEdgePairs + +#include "dbCommon.h" + +#include "dbAsIfFlatEdgePairs.h" +#include "dbShapes.h" +#include "dbRecursiveShapeIterator.h" + +namespace db { + +/** + * @brief An original layerregion based on a RecursiveShapeIterator + */ +class DB_PUBLIC OriginalLayerEdgePairs + : public AsIfFlatEdgePairs +{ +public: + OriginalLayerEdgePairs (); + OriginalLayerEdgePairs (const OriginalLayerEdgePairs &other); + OriginalLayerEdgePairs (const RecursiveShapeIterator &si); + OriginalLayerEdgePairs (const RecursiveShapeIterator &si, const db::ICplxTrans &trans); + virtual ~OriginalLayerEdgePairs (); + + EdgePairsDelegate *clone () const; + + virtual EdgePairsIteratorDelegate *begin () const; + virtual std::pair begin_iter () const; + + virtual bool empty () const; + + virtual const db::EdgePair *nth (size_t n) const; + virtual bool has_valid_edge_pairs () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual bool equals (const EdgePairs &other) const; + virtual bool less (const EdgePairs &other) const; + +private: + OriginalLayerEdgePairs &operator= (const OriginalLayerEdgePairs &other); + + mutable db::RecursiveShapeIterator m_iter; + db::ICplxTrans m_iter_trans; + + void init (); +}; + +} + +#endif + diff --git a/src/db/db/dbOriginalLayerEdges.cc b/src/db/db/dbOriginalLayerEdges.cc new file mode 100644 index 000000000..ee47f5771 --- /dev/null +++ b/src/db/db/dbOriginalLayerEdges.cc @@ -0,0 +1,277 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbOriginalLayerEdges.h" +#include "dbFlatEdges.h" +#include "dbEdges.h" +#include "dbEdgeBoolean.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// OriginalLayerEdges implementation + +namespace +{ + + class OriginalLayerEdgesIterator + : public EdgesIteratorDelegate + { + public: + OriginalLayerEdgesIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) + : m_rec_iter (iter), m_iter_trans (trans) + { + set (); + } + + virtual bool at_end () const + { + return m_rec_iter.at_end (); + } + + virtual void increment () + { + inc (); + set (); + } + + virtual const value_type *get () const + { + return &m_edge; + } + + virtual EdgesIteratorDelegate *clone () const + { + return new OriginalLayerEdgesIterator (*this); + } + + private: + friend class Edges; + + db::RecursiveShapeIterator m_rec_iter; + db::ICplxTrans m_iter_trans; + db::Edge m_edge; + + void set () + { + while (! m_rec_iter.at_end () && ! (m_rec_iter.shape ().is_edge () || m_rec_iter.shape ().is_path () || m_rec_iter.shape ().is_box ())) { + ++m_rec_iter; + } + if (! m_rec_iter.at_end ()) { + m_rec_iter.shape ().edge (m_edge); + m_edge.transform (m_iter_trans * m_rec_iter.trans ()); + } + } + + void inc () + { + if (! m_rec_iter.at_end ()) { + ++m_rec_iter; + } + } + }; + +} + +OriginalLayerEdges::OriginalLayerEdges () + : AsIfFlatEdges (), m_merged_edges (false) +{ + init (); +} + +OriginalLayerEdges::OriginalLayerEdges (const OriginalLayerEdges &other) + : AsIfFlatEdges (other), + m_is_merged (other.m_is_merged), + m_merged_edges (other.m_merged_edges), + m_merged_edges_valid (other.m_merged_edges_valid), + m_iter (other.m_iter), + m_iter_trans (other.m_iter_trans) +{ + // .. nothing yet .. +} + +OriginalLayerEdges::OriginalLayerEdges (const RecursiveShapeIterator &si, bool is_merged) + : AsIfFlatEdges (), m_merged_edges (false), m_iter (si) +{ + init (); + + m_is_merged = is_merged; +} + +OriginalLayerEdges::OriginalLayerEdges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) + : AsIfFlatEdges (), m_merged_edges (false), m_iter (si), m_iter_trans (trans) +{ + init (); + + m_is_merged = is_merged; + set_merged_semantics (merged_semantics); +} + +OriginalLayerEdges::~OriginalLayerEdges () +{ + // .. nothing yet .. +} + +EdgesDelegate * +OriginalLayerEdges::clone () const +{ + return new OriginalLayerEdges (*this); +} + +void +OriginalLayerEdges::merged_semantics_changed () +{ + m_merged_edges.clear (); + m_merged_edges_valid = false; +} + +EdgesIteratorDelegate * +OriginalLayerEdges::begin () const +{ + return new OriginalLayerEdgesIterator (m_iter, m_iter_trans); +} + +EdgesIteratorDelegate * +OriginalLayerEdges::begin_merged () const +{ + if (! merged_semantics () || m_is_merged) { + return begin (); + } else { + ensure_merged_edges_valid (); + return new FlatEdgesIterator (m_merged_edges.get_layer ().begin (), m_merged_edges.get_layer ().end ()); + } +} + +std::pair +OriginalLayerEdges::begin_iter () const +{ + return std::make_pair (m_iter, m_iter_trans); +} + +std::pair +OriginalLayerEdges::begin_merged_iter () const +{ + if (! merged_semantics () || m_is_merged) { + return begin_iter (); + } else { + ensure_merged_edges_valid (); + return std::make_pair (db::RecursiveShapeIterator (m_merged_edges), db::ICplxTrans ()); + } +} + +bool +OriginalLayerEdges::empty () const +{ + return m_iter.at_end (); +} + +bool +OriginalLayerEdges::is_merged () const +{ + return m_is_merged; +} + +const db::Edge * +OriginalLayerEdges::nth (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections"))); +} + +bool +OriginalLayerEdges::has_valid_edges () const +{ + return false; +} + +bool +OriginalLayerEdges::has_valid_merged_edges () const +{ + return merged_semantics () && ! m_is_merged; +} + +const db::RecursiveShapeIterator * +OriginalLayerEdges::iter () const +{ + return &m_iter; +} + +bool +OriginalLayerEdges::equals (const Edges &other) const +{ + const OriginalLayerEdges *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return true; + } else { + return AsIfFlatEdges::equals (other); + } +} + +bool +OriginalLayerEdges::less (const Edges &other) const +{ + const OriginalLayerEdges *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return false; + } else { + return AsIfFlatEdges::less (other); + } +} + +void +OriginalLayerEdges::init () +{ + m_is_merged = true; + m_merged_edges_valid = false; +} + +void +OriginalLayerEdges::ensure_merged_edges_valid () const +{ + if (! m_merged_edges_valid) { + + m_merged_edges.clear (); + + db::Shapes tmp (false); + EdgeBooleanClusterCollector cluster_collector (&tmp, EdgeOr); + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size ()); + + AddressableEdgeDelivery e (begin (), has_valid_edges ()); + + for ( ; ! e.at_end (); ++e) { + if (! e->is_degenerate ()) { + scanner.insert (e.operator-> (), 0); + } + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + m_merged_edges.swap (tmp); + m_merged_edges_valid = true; + + } +} + +} diff --git a/src/db/db/dbOriginalLayerEdges.h b/src/db/db/dbOriginalLayerEdges.h new file mode 100644 index 000000000..883e21a82 --- /dev/null +++ b/src/db/db/dbOriginalLayerEdges.h @@ -0,0 +1,88 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbOriginalLayerEdges +#define HDR_dbOriginalLayerEdges + +#include "dbCommon.h" + +#include "dbAsIfFlatEdges.h" +#include "dbShapes.h" +#include "dbRecursiveShapeIterator.h" + +namespace db { + +/** + * @brief An original layerregion based on a RecursiveShapeIterator + */ +class DB_PUBLIC OriginalLayerEdges + : public AsIfFlatEdges +{ +public: + OriginalLayerEdges (); + OriginalLayerEdges (const OriginalLayerEdges &other); + OriginalLayerEdges (const RecursiveShapeIterator &si, bool is_merged = false); + OriginalLayerEdges (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false); + virtual ~OriginalLayerEdges (); + + EdgesDelegate *clone () const; + + virtual EdgesIteratorDelegate *begin () const; + virtual EdgesIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + + virtual bool is_merged () const; + + virtual const db::Edge *nth (size_t n) const; + virtual bool has_valid_edges () const; + virtual bool has_valid_merged_edges () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual bool equals (const Edges &other) const; + virtual bool less (const Edges &other) const; + +protected: + virtual void merged_semantics_changed (); + +private: + OriginalLayerEdges &operator= (const OriginalLayerEdges &other); + + bool m_is_merged; + mutable db::Shapes m_merged_edges; + mutable bool m_merged_edges_valid; + mutable db::RecursiveShapeIterator m_iter; + db::ICplxTrans m_iter_trans; + + void init (); + void ensure_merged_edges_valid () const; +}; + +} + +#endif + diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc new file mode 100644 index 000000000..73f9aa83f --- /dev/null +++ b/src/db/db/dbOriginalLayerRegion.cc @@ -0,0 +1,301 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbOriginalLayerRegion.h" +#include "dbFlatRegion.h" +#include "dbFlatEdges.h" +#include "dbRegion.h" +#include "dbShapeProcessor.h" +#include "dbDeepEdges.h" +#include "dbDeepRegion.h" +#include "dbDeepShapeStore.h" +#include "tlGlobPattern.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// OriginalLayerRegion implementation + +namespace +{ + + class OriginalLayerRegionIterator + : public RegionIteratorDelegate + { + public: + OriginalLayerRegionIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) + : m_rec_iter (iter), m_iter_trans (trans) + { + set (); + } + + virtual bool at_end () const + { + return m_rec_iter.at_end (); + } + + virtual void increment () + { + inc (); + set (); + } + + virtual const value_type *get () const + { + return &m_polygon; + } + + virtual RegionIteratorDelegate *clone () const + { + return new OriginalLayerRegionIterator (*this); + } + + private: + friend class Region; + + db::RecursiveShapeIterator m_rec_iter; + db::ICplxTrans m_iter_trans; + db::Polygon m_polygon; + + void set () + { + while (! m_rec_iter.at_end () && ! (m_rec_iter.shape ().is_polygon () || m_rec_iter.shape ().is_path () || m_rec_iter.shape ().is_box ())) { + ++m_rec_iter; + } + if (! m_rec_iter.at_end ()) { + m_rec_iter.shape ().polygon (m_polygon); + m_polygon.transform (m_iter_trans * m_rec_iter.trans (), false); + } + } + + void inc () + { + if (! m_rec_iter.at_end ()) { + ++m_rec_iter; + } + } + }; + +} + +OriginalLayerRegion::OriginalLayerRegion () + : AsIfFlatRegion (), m_merged_polygons (false) +{ + init (); +} + +OriginalLayerRegion::OriginalLayerRegion (const OriginalLayerRegion &other) + : AsIfFlatRegion (other), + m_is_merged (other.m_is_merged), + m_merged_polygons (other.m_merged_polygons), + m_merged_polygons_valid (other.m_merged_polygons_valid), + m_iter (other.m_iter), + m_iter_trans (other.m_iter_trans) +{ + // .. nothing yet .. +} + +OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged) + : AsIfFlatRegion (), m_merged_polygons (false), m_iter (si) +{ + init (); + + m_is_merged = is_merged; +} + +OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) + : AsIfFlatRegion (), m_merged_polygons (false), m_iter (si), m_iter_trans (trans) +{ + init (); + + m_is_merged = is_merged; + set_merged_semantics (merged_semantics); +} + +OriginalLayerRegion::~OriginalLayerRegion () +{ + // .. nothing yet .. +} + +RegionDelegate * +OriginalLayerRegion::clone () const +{ + return new OriginalLayerRegion (*this); +} + +void +OriginalLayerRegion::merged_semantics_changed () +{ + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + +RegionIteratorDelegate * +OriginalLayerRegion::begin () const +{ + return new OriginalLayerRegionIterator (m_iter, m_iter_trans); +} + +RegionIteratorDelegate * +OriginalLayerRegion::begin_merged () const +{ + if (! merged_semantics () || m_is_merged) { + return begin (); + } else { + ensure_merged_polygons_valid (); + return new FlatRegionIterator (m_merged_polygons.get_layer ().begin (), m_merged_polygons.get_layer ().end ()); + } +} + +std::pair +OriginalLayerRegion::begin_iter () const +{ + return std::make_pair (m_iter, m_iter_trans); +} + +std::pair +OriginalLayerRegion::begin_merged_iter () const +{ + if (! merged_semantics () || m_is_merged) { + return begin_iter (); + } else { + ensure_merged_polygons_valid (); + return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ()); + } +} + +bool +OriginalLayerRegion::empty () const +{ + return m_iter.at_end (); +} + +bool +OriginalLayerRegion::is_merged () const +{ + return m_is_merged; +} + +const db::Polygon * +OriginalLayerRegion::nth (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to polygons is available only for flat regions"))); +} + +bool +OriginalLayerRegion::has_valid_polygons () const +{ + return false; +} + +bool +OriginalLayerRegion::has_valid_merged_polygons () const +{ + return merged_semantics () && ! m_is_merged; +} + +const db::RecursiveShapeIterator * +OriginalLayerRegion::iter () const +{ + return &m_iter; +} + +bool +OriginalLayerRegion::equals (const Region &other) const +{ + const OriginalLayerRegion *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return true; + } else { + return AsIfFlatRegion::equals (other); + } +} + +bool +OriginalLayerRegion::less (const Region &other) const +{ + const OriginalLayerRegion *other_delegate = dynamic_cast (other.delegate ()); + if (other_delegate && other_delegate->m_iter == m_iter && other_delegate->m_iter_trans == m_iter_trans) { + return false; + } else { + return AsIfFlatRegion::less (other); + } +} + +void +OriginalLayerRegion::init () +{ + m_is_merged = true; + m_merged_polygons_valid = false; +} + +void +OriginalLayerRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + db::Shapes &sh = layout->cell (into_cell).shapes (into_layer); + + // NOTE: if the source (r) is from the same layout than the shapes live in, we better + // lock the layout against updates while inserting + db::LayoutLocker locker (layout); + for (db::RecursiveShapeIterator i = m_iter; !i.at_end (); ++i) { + tl::ident_map pm; + sh.insert (*i, i.trans (), pm); + } +} + +void +OriginalLayerRegion::ensure_merged_polygons_valid () const +{ + if (! m_merged_polygons_valid) { + + m_merged_polygons.clear (); + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + ep.set_base_verbosity (base_verbosity ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + // and run the merge step + db::MergeOp op (0); + db::ShapeGenerator pc (m_merged_polygons); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + m_merged_polygons_valid = true; + + } +} + +} diff --git a/src/db/db/dbOriginalLayerRegion.h b/src/db/db/dbOriginalLayerRegion.h new file mode 100644 index 000000000..b99a651ec --- /dev/null +++ b/src/db/db/dbOriginalLayerRegion.h @@ -0,0 +1,92 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbOriginalLayerRegion +#define HDR_dbOriginalLayerRegion + +#include "dbCommon.h" + +#include "dbAsIfFlatRegion.h" + +namespace db { + +class EdgesDelegate; +class RegionDelegate; +class DeepShapeStore; + +/** + * @brief An original layerregion based on a RecursiveShapeIterator + */ +class DB_PUBLIC OriginalLayerRegion + : public AsIfFlatRegion +{ +public: + OriginalLayerRegion (); + OriginalLayerRegion (const OriginalLayerRegion &other); + OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged = false); + OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false); + virtual ~OriginalLayerRegion (); + + RegionDelegate *clone () const; + + virtual RegionIteratorDelegate *begin () const; + virtual RegionIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + + virtual bool is_merged () const; + + virtual const db::Polygon *nth (size_t n) const; + virtual bool has_valid_polygons () const; + virtual bool has_valid_merged_polygons () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; + + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; + +protected: + virtual void merged_semantics_changed (); + +private: + OriginalLayerRegion &operator= (const OriginalLayerRegion &other); + + bool m_is_merged; + mutable db::Shapes m_merged_polygons; + mutable bool m_merged_polygons_valid; + mutable db::RecursiveShapeIterator m_iter; + db::ICplxTrans m_iter_trans; + + void init (); + void ensure_merged_polygons_valid () const; +}; + +} + +#endif + diff --git a/src/db/db/dbPin.cc b/src/db/db/dbPin.cc new file mode 100644 index 000000000..e223ec8ae --- /dev/null +++ b/src/db/db/dbPin.cc @@ -0,0 +1,53 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbPin.h" +#include "tlString.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// Pin class implementation + +Pin::Pin () + : m_id (0) +{ + // .. nothing yet .. +} + +Pin::Pin (const std::string &name) + : m_name (name), m_id (0) +{ + // .. nothing yet .. +} + +std::string Pin::expanded_name () const +{ + if (name ().empty ()) { + return "$" + tl::to_string (id ()); + } else { + return name (); + } +} + +} diff --git a/src/db/db/dbPin.h b/src/db/db/dbPin.h new file mode 100644 index 000000000..2ee33cf6c --- /dev/null +++ b/src/db/db/dbPin.h @@ -0,0 +1,87 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbPin +#define _HDR_dbPin + +#include "dbCommon.h" + +#include + +namespace db +{ + +/** + * @brief The definition of a pin of a circuit + * + * A pin is some place other nets can connect to a circuit. + */ +class DB_PUBLIC Pin +{ +public: + /** + * @brief Default constructor + */ + Pin (); + + /** + * @brief Creates a pin with the given name. + */ + Pin (const std::string &name); + + /** + * @brief Gets the name of the pin + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Gets a name which always non-empty + * This method will pick a name like "$" if the explicit name is empty. + */ + std::string expanded_name () const; + + /** + * @brief Gets the ID of the pin (only pins inside circuits have valid ID's) + */ + size_t id () const + { + return m_id; + } + +private: + friend class Circuit; + + std::string m_name; + size_t m_id; + + void set_id (size_t id) + { + m_id = id; + } +}; + +} + +#endif diff --git a/src/db/db/dbPolygon.h b/src/db/db/dbPolygon.h index d3b966d6a..639d696f4 100644 --- a/src/db/db/dbPolygon.h +++ b/src/db/db/dbPolygon.h @@ -1662,7 +1662,7 @@ public: } /** - * @brief Return the number of points in the polygon + * @brief Returns the number of points in the polygon */ size_t vertices () const { @@ -1673,6 +1673,18 @@ public: return n; } + /** + * @brief Returns the area ratio between the polygon's bounding box and actual area + * + * This number is a measure how well the polygon is approximated by the bounding box. + * Values are bigger than 1 for well-formed polygons. Bigger values mean worse + * approximation. + */ + double area_ratio () const + { + return double (box ().area ()) / double (area ()); + } + /** * @brief The hull "begin" point iterator * @@ -2956,6 +2968,18 @@ public: return m_hull.size (); } + /** + * @brief Returns the area ratio between the polygon's bounding box and actual area + * + * This number is a measure how well the polygon is approximated by the bounding box. + * Values are bigger than 1 for well-formed polygons. Bigger values mean worse + * approximation. + */ + double area_ratio () const + { + return double (box ().area ()) / double (area ()); + } + void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self = false, void *parent = 0) const { db::mem_stat (stat, purpose, cat, m_hull, no_self, parent); diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index c3269d533..92bbc5278 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -360,6 +360,28 @@ RecursiveShapeIterator::confine_region (const region_type ®ion) m_needs_reinit = true; } +void +RecursiveShapeIterator::set_layer (unsigned int layer) +{ + if (m_has_layers || m_layer != layer) { + m_has_layers = false; + m_layers.clear (); + m_layer = layer; + m_needs_reinit = true; + } +} + +void +RecursiveShapeIterator::set_layers (const std::vector &layers) +{ + if (! m_has_layers || m_layers != layers) { + m_has_layers = true; + m_layers = layers; + m_layer = 0; + m_needs_reinit = true; + } +} + namespace { struct BoxTreePusher @@ -383,7 +405,7 @@ private: } void -RecursiveShapeIterator::validate () const +RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const { if (! m_needs_reinit) { return; @@ -438,8 +460,8 @@ RecursiveShapeIterator::validate () const } else if (mp_layout && (! m_has_layers || m_current_layer < m_layers.size ())) { // Ensures the trees are built properly - this is important in MT contexts (i.e. TilingProcessor) mp_layout->update (); - new_cell (); - next_shape (); + new_cell (receiver); + next_shape (receiver); } } @@ -519,7 +541,7 @@ RecursiveShapeIterator::select_all_cells () bool RecursiveShapeIterator::at_end () const { - validate (); + validate (0); return m_shape.at_end () || is_inactive (); } @@ -611,7 +633,7 @@ RecursiveShapeIterator::skip_inst_iter_for_complex_region () const } void -RecursiveShapeIterator::next () +RecursiveShapeIterator::next (RecursiveShapeReceiver *receiver) { if (! at_end ()) { @@ -622,14 +644,14 @@ RecursiveShapeIterator::next () } if (! mp_shapes && m_shape.at_end ()) { - next_shape (); + next_shape (receiver); } } } void -RecursiveShapeIterator::next_shape () const +RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const { while (at_end ()) { @@ -674,10 +696,10 @@ RecursiveShapeIterator::next_shape () const if (is_empty) { ++m_inst; - new_inst (); + new_inst (receiver); } else { - down (); + down (receiver); } } else { @@ -688,12 +710,14 @@ RecursiveShapeIterator::next_shape () const } // no more instances: up and next instance - up (); + up (receiver); ++m_inst_array; + new_inst_member (receiver); + if (m_inst_array.at_end ()) { ++m_inst; - new_inst (); + new_inst (receiver); } } @@ -704,7 +728,7 @@ RecursiveShapeIterator::next_shape () const } void -RecursiveShapeIterator::down () const +RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const { m_trans_stack.push_back (m_trans); m_cells.push_back (mp_cell); @@ -714,8 +738,10 @@ RecursiveShapeIterator::down () const m_inst_quad_id_stack.push_back (m_inst_quad_id); bool ia = is_inactive (); + bool aoi = is_all_of_instance (); mp_cell = &mp_layout->cell (m_inst->cell_index ()); set_inactive (ia); + set_all_of_instance (aoi); m_trans = m_trans * m_inst->complex_trans (*m_inst_array); @@ -761,12 +787,20 @@ RecursiveShapeIterator::down () const } - new_cell (); + if (receiver) { + receiver->enter_cell (this, cell (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); + } + + new_cell (receiver); } void -RecursiveShapeIterator::up () const +RecursiveShapeIterator::up (RecursiveShapeReceiver *receiver) const { + if (receiver) { + receiver->leave_cell (this, cell ()); + } + m_shape = shape_iterator (); m_shape_quad_id = 0; @@ -823,7 +857,7 @@ RecursiveShapeIterator::new_layer () const } void -RecursiveShapeIterator::new_cell () const +RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const { if (m_has_layers) { m_current_layer = 0; @@ -847,11 +881,11 @@ RecursiveShapeIterator::new_cell () const skip_inst_iter_for_complex_region (); } - new_inst (); + new_inst (receiver); } void -RecursiveShapeIterator::new_inst () const +RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const { // look for the next instance with a non-empty array iterator. The latter can be // empty because we use a per-layer box converter for that case what we don't for the @@ -866,12 +900,36 @@ RecursiveShapeIterator::new_inst () const } } - if (m_local_region_stack.back () != box_type::world ()) { + bool all_of_instance = false; + bool with_region = false; + + if (m_local_region_stack.back () != box_type::world () && ! m_inst->cell_inst ().bbox (m_box_convert).inside (m_local_region_stack.back ())) { + with_region = true; + } else { + // TODO: optimization potential: only report all_of_instance == false, if not entirely within the complex region + all_of_instance = m_local_complex_region_stack.empty (); + } + + RecursiveShapeReceiver::new_inst_mode ni = RecursiveShapeReceiver::NI_all; + if (receiver) { + ni = receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance); + } + + if (ni == RecursiveShapeReceiver::NI_skip) { + m_inst_array = inst_array_iterator (); + } else if (ni == RecursiveShapeReceiver::NI_single) { + // a singular iterator + m_inst_array = db::CellInstArray::iterator (m_inst->cell_inst ().front (), false); + } else if (with_region) { m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); } else { - m_inst_array = m_inst->cell_inst ().begin (); + m_inst_array = m_inst->cell_inst ().begin (); } + set_all_of_instance (all_of_instance); + + new_inst_member (receiver); + if (! m_inst_array.at_end ()) { break; } else { @@ -881,6 +939,32 @@ RecursiveShapeIterator::new_inst () const } } +void +RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const +{ + if (! m_local_complex_region_stack.empty ()) { + + // skip instance array members not part of the complex region + while (! m_inst_array.at_end ()) { + db::Box ia_box = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ()); + if (! is_outside_complex_region (ia_box)) { + break; + } else { + ++m_inst_array; + } + } + + } + + while (! m_inst_array.at_end () && receiver) { + if (receiver->new_inst_member (this, m_inst->cell_inst (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance ())) { + break; + } else { + ++m_inst_array; + } + } +} + bool RecursiveShapeIterator::is_outside_complex_region (const db::Box &box) const { @@ -891,5 +975,32 @@ RecursiveShapeIterator::is_outside_complex_region (const db::Box &box) const } } +void +RecursiveShapeIterator::push (RecursiveShapeReceiver *receiver) +{ + // force reset so we can validate with a receiver + reset (); + + receiver->begin (this); + + try { + + validate (receiver); + + while (! at_end ()) { + receiver->shape (this, *m_shape, m_trans, m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); + next (receiver); + } + + receiver->end (this); + + } catch (...) { + + receiver->end (this); + throw; + + } +} + } diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 72e919681..376b978d6 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -38,6 +38,7 @@ namespace db { class Region; +class RecursiveShapeReceiver; /** * @brief An iterator delivering shapes that touch or overlap the given region recursively @@ -265,6 +266,29 @@ public: return m_max_depth; } + /** + * @brief Specify the minimum hierarchy depth to look into + * + * A depth of 0 instructs the iterator to deliver shapes from the top level. + * 1 instructs to deliver shapes from the first child level. + * The minimum depth must be specified before the shapes are being retrieved. + */ + void min_depth (int depth) + { + if (m_min_depth != depth) { + m_min_depth = depth; + m_needs_reinit = true; + } + } + + /** + * @brief Gets the minimum hierarchy depth to search for + */ + int min_depth () const + { + return m_min_depth; + } + /** * @brief Gets the iterated shapes * @@ -417,18 +441,23 @@ public: void reset_selection (); /** - * @brief Specify the minimum hierarchy depth to look into + * @brief Returns the cells in the "enable" selection * - * A depth of 0 instructs the iterator to deliver shapes from the top level. - * 1 instructs to deliver shapes from the first child level. - * The minimum depth must be specified before the shapes are being retrieved. + * Cells in this set make the iterator become active, while cells in the + * disable selection make the iterator inactive. Only when active, the + * iterator will deliver shapes. */ - void min_depth (int depth) - { - if (m_min_depth != depth) { - m_min_depth = depth; - m_needs_reinit = true; - } + const std::set &enables () const + { + return m_start; + } + + /** + * @brief Returns the cells in the "disable" selection + */ + const std::set &disables () const + { + return m_stop; } /** @@ -445,6 +474,28 @@ public: } } + /** + * @brief Gets the shape selection flags + */ + unsigned int shape_flags () const + { + return m_shape_flags; + } + + /** + * @brief Changes the layer to be traversed + * + * This method must be used before shapes are being retrieved. Using this method may reset the iterator. + */ + void set_layer (unsigned int layer); + + /** + * @brief Changes the layers to be traversed + * + * This method must be used before shapes are being retrieved. Using this method may reset the iterator. + */ + void set_layers (const std::vector &layers); + /** * @brief Specify the property selector * @@ -479,7 +530,7 @@ public: */ unsigned int layer () const { - validate (); + validate (0); return m_layer; } @@ -514,30 +565,30 @@ public: */ const cplx_trans_type &trans () const { - validate (); + validate (0); return m_trans; } /** - * @brief Get the current depth + * @brief Gets the current depth * * Returns the number of hierarchy levels we are below top level currently. */ unsigned int depth () const { - validate (); + validate (0); return (unsigned int) m_trans_stack.size (); } /** - * @brief Get the current shape + * @brief Gets the current shape * * Returns the shape currently referred to by the recursive iterator. * This shape is not transformed yet and is located in the current cell. */ shape_type shape () const { - validate (); + validate (0); return *m_shape; } @@ -548,7 +599,7 @@ public: */ shape_type operator* () const { - validate (); + validate (0); return *m_shape; } @@ -559,7 +610,7 @@ public: */ const shape_type *operator-> () const { - validate (); + validate (0); return m_shape.operator-> (); } @@ -571,38 +622,39 @@ public: bool at_end () const; /** - * @brief Get the current cell's index + * @brief Gets the current cell's index */ db::cell_index_type cell_index () const { - validate (); - size_t c = reinterpret_cast (mp_cell); - return reinterpret_cast (c - (c & size_t (1)))->cell_index (); + return cell ()->cell_index (); } /** - * @brief Get the current cell's reference + * @brief Gets the current cell's reference */ const cell_type *cell () const { - validate (); + validate (0); size_t c = reinterpret_cast (mp_cell); - return reinterpret_cast (c - (c & size_t (1))); + return reinterpret_cast (c - (c & size_t (3))); } /** - * @brief Increment the iterator (operator version) + * @brief Increments the iterator (operator version) */ RecursiveShapeIterator &operator++() { - next (); + next (0); return *this; } /** - * @brief Increment the iterator + * @brief Increments the iterator */ - void next (); + void next () + { + next (0); + } /** * @brief Comparison of iterators - equality @@ -638,6 +690,20 @@ public: */ std::vector path () const; + /** + * @brief Push-mode delivery + * + * This method will deliver all shapes to the given receiver. + * In contrast to pull mode, this method allows tailoring the + * traversal of the hierarchy tree during iteration. + * For this purpose, the receiver has methods that receive + * events and to some extend may modify the traversal (e.g. + * return value of enter_cell). + * + * See RecursiveShapeReceiver class for more details. + */ + void push (RecursiveShapeReceiver *receiver); + private: std::vector m_layers; bool m_has_layers; @@ -681,14 +747,16 @@ private: void init_region (const box_type ®ion); void skip_shape_iter_for_complex_region () const; void skip_inst_iter_for_complex_region () const; - void validate () const; + void validate (RecursiveShapeReceiver *receiver) const; void start_shapes () const; - void next_shape () const; - void new_inst () const; - void new_cell () const; + void next (RecursiveShapeReceiver *receiver); + void next_shape (RecursiveShapeReceiver *receiver) const; + void new_inst (RecursiveShapeReceiver *receiver) const; + void new_inst_member (RecursiveShapeReceiver *receiver) const; + void new_cell (RecursiveShapeReceiver *receiver) const; void new_layer () const; - void up () const; - void down () const; + void up (RecursiveShapeReceiver *receiver) const; + void down (RecursiveShapeReceiver *receiver) const; bool is_outside_complex_region (const db::Box &box) const; @@ -701,12 +769,133 @@ private: { size_t c = reinterpret_cast (mp_cell); c -= (c & size_t (1)); - mp_cell = reinterpret_cast (c + a); + mp_cell = reinterpret_cast (c + (a ? 1 : 0)); } + + bool is_all_of_instance () const + { + return (reinterpret_cast (mp_cell) & size_t (2)) != 0; + } + + void set_all_of_instance (bool a) const + { + size_t c = reinterpret_cast (mp_cell); + c -= (c & size_t (2)); + mp_cell = reinterpret_cast (c + (a ? 2 : 0)); + } +}; + +/** + * @brief A receiver interface for "push" mode + * + * In push mode, the iterator will deliver the shapes and hierarchy transitions + * to this interface. See "RecursiveShapeIterator::push" for details about this + * mode. + * + * The receiver receives events for the start of the delivery, on each cell + * entry and on each instance (followed by a cell entry). It also receives + * the shapes. + */ +class DB_PUBLIC RecursiveShapeReceiver +{ +public: + typedef RecursiveShapeIterator::box_tree_type box_tree_type; + + /** + * @brief See new_inst for details. + */ + enum new_inst_mode { NI_all = 0, NI_single = 1, NI_skip = 2 }; + + /** + * @brief Constructor + */ + RecursiveShapeReceiver () { } + + /** + * @brief Destructor + */ + virtual ~RecursiveShapeReceiver () { } + + /** + * @brief Called once when the iterator begins pushing + */ + virtual void begin (const RecursiveShapeIterator * /*iter*/) { } + + /** + * @brief Called once after the iterator pushed everything + */ + virtual void end (const RecursiveShapeIterator * /*iter*/) { } + + /** + * @brief Enters a cell + * + * This method is called when the recursive shape iterator + * enters a new cell. It is not called for the top cell. When it is called, "iter->trans()" + * will already be updated. + * + * @param iter The iterator + * @param cell The cell which is entered + * @param region The clip box as seen from "cell" or db::Box::world if there is no clip box + * @param complex_region A complex clip region if one is supplied together with "region" + */ + virtual void enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { } + + /** + * @brief Leaves the current cell + * + * This method is the counterpart for "enter_cell". It is called when traversal of "cell" ended. + */ + virtual void leave_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/) { } + + /** + * @brief Enters a new instance + * + * This method is called before "enter_cell" and "new_inst_member" is called and will indicate the instance to follow. + * The sequence of events is + * + * new_inst(A) + * new_inst_member(A[0,0]) + * enter_cell(A) + * ... + * leave_cell(A) + * new_inst_member(A[1,0]) + * enter_cell(A) + * ... + * leave_cell(A) + * ... + * new_inst(B) + * ... + * + * The "all" parameter is true, if all instances of the array will be addressed. + * + * This method can return the following values: + * - NI_all: iterate all members through "new_inst_member" + * - NI_single: iterate a single member (the first one) + * - NI_skip: skips the whole array (not a single instance is iterated) + */ + virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return NI_all; } + + /** + * @brief Enters a new array member of the instance + * + * See "new_inst" for a description. This method adds the "trans" parameter + * which holds the complex transformation for this particular instance of + * the array. + * + * "all" is true, if an instance array is iterated in "all" mode (see new_inst). + * + * If this method returns false, this array instance (but not the whole array) is skipped and the cell is not entered. + */ + virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; } + + /** + * @brief Delivers a shape + * + * @param trans The transformation which maps the shape to the top cell. + */ + virtual void shape (const RecursiveShapeIterator * /*iter*/, const db::Shape & /*shape*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { } }; } // namespace db #endif - - diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 36ef79194..d362a7af9 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -22,1278 +22,28 @@ #include "dbRegion.h" -#include "dbLayoutUtils.h" -#include "dbEdgeProcessor.h" -#include "dbEdgePairRelations.h" -#include "dbEdges.h" -#include "dbEdgePairs.h" -#include "dbBoxConvert.h" -#include "dbBoxScanner.h" -#include "dbClip.h" +#include "dbOriginalLayerRegion.h" +#include "dbEmptyRegion.h" +#include "dbFlatRegion.h" +#include "dbDeepRegion.h" +#include "dbDeepEdges.h" +#include "dbFlatEdges.h" #include "dbPolygonTools.h" - -#include "tlVariant.h" - -#include -#include +#include "tlGlobPattern.h" namespace db { -Region::Region (const RecursiveShapeIterator &si) - : m_polygons (false), m_merged_polygons (false), m_iter (si) -{ - init (); - // Make sure we restart the iterator and late-initialize it (this makes sure - // it refers to the configuration present then) - m_iter.reset (); - m_bbox_valid = false; - m_is_merged = false; -} - -Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics) - : m_polygons (false), m_merged_polygons (false), m_iter (si), m_iter_trans (trans) -{ - init (); - // Make sure we restart the iterator and late-initialize it (this makes sure - // it refers to the configuration present then) - m_iter.reset (); - m_bbox_valid = false; - m_is_merged = false; - m_merged_semantics = merged_semantics; -} - -bool -Region::operator== (const db::Region &other) const -{ - if (empty () != other.empty ()) { - return false; - } - if (size () != other.size ()) { - return false; - } - db::Region::const_iterator o1 = begin (); - db::Region::const_iterator o2 = other.begin (); - while (! o1.at_end () && ! o2.at_end ()) { - if (*o1 != *o2) { - return false; - } - ++o1; - ++o2; - } - return true; -} - -bool -Region::operator< (const db::Region &other) const -{ - if (empty () != other.empty ()) { - return empty () < other.empty (); - } - if (size () != other.size ()) { - return (size () < other.size ()); - } - db::Region::const_iterator o1 = begin (); - db::Region::const_iterator o2 = other.begin (); - while (! o1.at_end () && ! o2.at_end ()) { - if (*o1 != *o2) { - return *o1 < *o2; - } - ++o1; - ++o2; - } - return false; -} - -size_t -Region::size () const -{ - if (! has_valid_polygons ()) { - // If we have an iterator, we have to do it the hard way .. - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - ++n; - } - return n; - } else { - return m_polygons.size (); - } -} - -void -Region::set_strict_handling (bool f) -{ - m_strict_handling = f; -} - -void -Region::set_merged_semantics (bool f) -{ - if (f != m_merged_semantics) { - m_merged_semantics = f; - m_merged_polygons.clear (); - m_merged_polygons_valid = false; - } -} - -std::string -Region::to_string (size_t nmax) const -{ - std::ostringstream os; - const_iterator p = begin (); - bool first = true; - for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) { - if (! first) { - os << ";"; - } - first = false; - os << p->to_string (); - } - if (! p.at_end ()) { - os << "..."; - } - return os.str (); -} - -Region::area_type -Region::area (const db::Box &box) const -{ - area_type a = 0; - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - if (box.empty () || p->box ().inside (box)) { - a += p->area (); - } else { - std::vector clipped; - clip_poly (*p, box, clipped); - for (std::vector::const_iterator c = clipped.begin (); c != clipped.end (); ++c) { - a += c->area (); - } - } - } - - return a; -} - -Region::perimeter_type -Region::perimeter (const db::Box &box) const -{ - perimeter_type d = 0; - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - - if (box.empty () || p->box ().inside (box)) { - d += p->perimeter (); - } else { - - for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { - - if (box.empty ()) { - d += (*e).length (); - } else { - - std::pair ce = (*e).clipped (box); - if (ce.first) { - - db::Coord dx = ce.second.dx (); - db::Coord dy = ce.second.dy (); - db::Coord x = ce.second.p1 ().x (); - db::Coord y = ce.second.p1 ().y (); - if ((dx == 0 && x == box.left () && dy < 0) || - (dx == 0 && x == box.right () && dy > 0) || - (dy == 0 && y == box.top () && dx < 0) || - (dy == 0 && y == box.bottom () && dx > 0)) { - // not counted -> box is at outside side of the edge - } else { - d += ce.second.length (); - } - - } - - } - - } - - } - - } - - return d; -} - -Region -Region::hulls () const -{ - Region region; - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - db::Polygon h; - h.assign_hull (p->begin_hull (), p->end_hull ()); - region.insert (h); - } - - return region; -} - -Region -Region::holes () const -{ - Region region; - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - for (size_t i = 0; i < p->holes (); ++i) { - db::Polygon h; - h.assign_hull (p->begin_hole ((unsigned int) i), p->end_hole ((unsigned int) i)); - region.insert (h); - } - } - - return region; -} - -Region -Region::rounded_corners (double rinner, double router, unsigned int n) const -{ - Region r; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - r.insert (db::compute_rounded (*p, rinner, router, n)); - } - return r; -} - -Region -Region::smoothed (coord_type d) const -{ - Region r; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - r.insert (db::smooth (*p, d)); - } - return r; -} - -Region -Region::in (const Region &other, bool invert) const -{ - std::set op; - for (const_iterator o = other.begin_merged (); ! o.at_end (); ++o) { - op.insert (*o); - } - - Region r; - for (const_iterator o = begin_merged (); ! o.at_end (); ++o) { - if ((op.find (*o) == op.end ()) == invert) { - r.insert (*o); - } - } - - return r; -} - -Edges -Region::edges () const -{ - Edges edges; - - size_t n = 0; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - n += p->vertices (); - } - edges.reserve (n); - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { - edges.insert (*e); - } - } - - return edges; -} - -bool -Region::is_box () const -{ - const_iterator p = begin (); - if (p.at_end ()) { - return false; - } else { - const db::Polygon &poly = *p; - ++p; - if (! p.at_end ()) { - return false; - } else { - return poly.is_box (); - } - } -} - -void -Region::swap (Region &other) -{ - std::swap (m_is_merged, other.m_is_merged); - std::swap (m_merged_semantics, other.m_merged_semantics); - std::swap (m_strict_handling, other.m_strict_handling); - std::swap (m_merge_min_coherence, other.m_merge_min_coherence); - m_polygons.swap (other.m_polygons); - m_merged_polygons.swap (other.m_merged_polygons); - std::swap (m_bbox, other.m_bbox); - std::swap (m_bbox_valid, other.m_bbox_valid); - std::swap (m_merged_polygons_valid, other.m_merged_polygons_valid); - std::swap (m_iter, other.m_iter); - std::swap (m_iter_trans, other.m_iter_trans); -} - -Region & -Region::merge () -{ - if (! m_is_merged) { - - if (m_merged_polygons_valid) { - - m_polygons.swap (m_merged_polygons); - m_merged_polygons.clear (); - m_is_merged = true; - - } else { - - merge (m_merge_min_coherence, 0); - - } - - } - - return *this; -} - -Region & -Region::merge (bool min_coherence, unsigned int min_wc) -{ - if (empty ()) { - - // ignore empty - - } else if (is_box ()) { - - // take box only if min_wc == 0, otherwise clear - if (min_wc > 0) { - clear (); - } - - } else { - - invalidate_cache (); - - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - // and run the merge step - db::MergeOp op (min_wc); - db::ShapeGenerator pc (m_polygons, true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); - ep.process (pg, op); - - set_valid_polygons (); - - m_is_merged = true; - - } - - return *this; -} - -Region & -Region::size (Region::coord_type dx, Region::coord_type dy, unsigned int mode) -{ - if (empty ()) { - - // ignore empty - - } else if (is_box () && mode >= 2) { - - // simplified handling for a box - db::Box b = bbox ().enlarged (db::Vector (dx, dy)); - m_polygons.clear (); - if (! b.empty () && b.width () > 0 && b.height () > 0) { - m_polygons.insert (db::Polygon (b)); - } else { - b = db::Box (); - } - - m_is_merged = true; - m_bbox = b; - m_bbox_valid = true; - - m_merged_polygons.clear (); - m_merged_polygons_valid = false; - set_valid_polygons (); - - } else if (! m_merged_semantics) { - - invalidate_cache (); - - // Generic case - db::Shapes output (false); - - db::ShapeGenerator pc (output, false); - db::PolygonGenerator pg (pc, false, true); - db::SizingPolygonFilter sf (pg, dx, dy, mode); - for (const_iterator p = begin (); ! p.at_end (); ++p) { - sf.put (*p); - } - - output.swap (m_polygons); - set_valid_polygons (); - - m_is_merged = false; - - } else { - - invalidate_cache (); - - // Generic case - the size operation will merge first - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - db::ShapeGenerator pc (m_polygons, true /*clear*/); - db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/); - db::SizingPolygonFilter siz (pg2, dx, dy, mode); - db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/); - db::BooleanOp op (db::BooleanOp::Or); - ep.process (pg, op); - - set_valid_polygons (); - - m_is_merged = false; - - } - - return *this; -} - -Region & -Region::operator&= (const Region &other) -{ - if (empty ()) { - - // Nothing to do - - } else if (other.empty ()) { - - clear (); - - } else if (is_box () && other.is_box ()) { - - // Simplified handling for boxes - db::Box b = bbox (); - b &= other.bbox (); - m_polygons.clear (); - if (! b.empty () && b.width () > 0 && b.height () > 0 ) { - m_polygons.insert (db::Polygon (b)); - } - - m_is_merged = true; - m_bbox = b; - m_bbox_valid = true; - - m_merged_polygons.clear (); - m_merged_polygons_valid = false; - set_valid_polygons (); - - } else if (is_box () && ! other.strict_handling ()) { - - // map AND with box to clip .. - db::Box b = bbox (); - m_polygons.clear (); - - std::vector clipped; - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - clipped.clear (); - clip_poly (*p, b, clipped); - m_polygons.insert (clipped.begin (), clipped.end ()); - } - - m_is_merged = false; - invalidate_cache (); - set_valid_polygons (); - - } else if (other.is_box () && ! m_strict_handling) { - - // map AND with box to clip .. - db::Box b = other.bbox (); - db::Shapes polygons (false); - - std::vector clipped; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - clipped.clear (); - clip_poly (*p, b, clipped); - polygons.insert (clipped.begin (), clipped.end ()); - } - - m_polygons.swap (polygons); - m_is_merged = false; - invalidate_cache (); - set_valid_polygons (); - - } else if (! bbox ().overlaps (other.bbox ())) { - - // Result will be nothing - clear (); - - } else { - - invalidate_cache (); - - // Generic case - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - n = 1; - for (const_iterator p = other.begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - - db::BooleanOp op (db::BooleanOp::And); - db::ShapeGenerator pc (m_polygons, true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, m_merge_min_coherence); - ep.process (pg, op); - - set_valid_polygons (); - - m_is_merged = true; - - } - - return *this; -} - -Region & -Region::operator-= (const Region &other) -{ - if (empty ()) { - - // Nothing to do - - } else if (other.empty () && ! m_strict_handling) { - - // Nothing to do - - } else if (! bbox ().overlaps (other.bbox ()) && ! m_strict_handling) { - - // Nothing to do - - } else { - - invalidate_cache (); - - // Generic case - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - n = 1; - for (const_iterator p = other.begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - - db::BooleanOp op (db::BooleanOp::ANotB); - db::ShapeGenerator pc (m_polygons, true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, m_merge_min_coherence); - ep.process (pg, op); - - set_valid_polygons (); - - m_is_merged = true; - - } - - return *this; -} - -Region & -Region::operator^= (const Region &other) -{ - if (empty () && ! other.strict_handling ()) { - - *this = other; - - } else if (other.empty () && ! m_strict_handling) { - - // Nothing to do - - } else if (! bbox ().overlaps (other.bbox ()) && ! m_strict_handling && ! other.strict_handling ()) { - - // Simplified handling for disjunct case - *this |= other; - - } else { - - invalidate_cache (); - - // Generic case - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - n = 1; - for (const_iterator p = other.begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - - db::BooleanOp op (db::BooleanOp::Xor); - db::ShapeGenerator pc (m_polygons, true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, m_merge_min_coherence); - ep.process (pg, op); - - set_valid_polygons (); - - m_is_merged = true; - - } - - return *this; -} - -Region & -Region::operator|= (const Region &other) -{ - if (empty () && ! other.strict_handling ()) { - - *this = other; - - } else if (other.empty () && ! m_strict_handling) { - - // Nothing to do - - } else if (! bbox ().overlaps (other.bbox ()) && ! m_strict_handling && ! other.strict_handling ()) { - - // Simplified handling for disjunct case - *this += other; - - } else { - - invalidate_cache (); - - // Generic case - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - n = 1; - for (const_iterator p = other.begin (); ! p.at_end (); ++p, n += 2) { - ep.insert (*p, n); - } - - db::BooleanOp op (db::BooleanOp::Or); - db::ShapeGenerator pc (m_polygons, true /*clear*/); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, m_merge_min_coherence); - ep.process (pg, op); - - set_valid_polygons (); - - m_is_merged = true; - - } - - return *this; -} - -Region & -Region::operator+= (const Region &other) -{ - invalidate_cache (); - - if (! has_valid_polygons ()) { - - m_polygons.clear (); - - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - ++n; - } - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - ++n; - } - - m_polygons.reserve (db::Polygon::tag (), n); - - for (const_iterator p = begin (); ! p.at_end (); ++p) { - m_polygons.insert (*p); - } - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - m_polygons.insert (*p); - } - - set_valid_polygons (); - - } else if (! other.has_valid_polygons ()) { - - size_t n = m_polygons.size (); - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - ++n; - } - - m_polygons.reserve (db::Polygon::tag (), n); - - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - m_polygons.insert (*p); - } - - } else { - m_polygons.insert (other.m_polygons.get_layer ().begin (), other.m_polygons.get_layer ().end ()); - } - - m_is_merged = false; - return *this; -} - -Region -Region::selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const -{ - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // shortcut - if (empty ()) { - return *this; - } else if (other.empty ()) { - // clear, if b is empty and - // * mode is inside or interacting and inverse is false ("inside" or "interacting") - // * mode is outside and inverse is true ("not outside") - if ((mode <= 0) != inverse) { - return Region (); - } else { - return *this; - } - } - - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - if (p->box ().touches (bbox ())) { - ep.insert (*p, 0); - } - } - - size_t n = 1; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p, ++n) { - if (mode > 0 || p->box ().touches (other.bbox ())) { - ep.insert (*p, n); - } - } - - db::InteractionDetector id (mode, 0); - id.set_include_touching (touching); - db::EdgeSink es; - ep.process (es, id); - id.finish (); - - Region out; - n = 0; - std::set selected; - for (db::InteractionDetector::iterator i = id.begin (); i != id.end () && i->first == 0; ++i) { - ++n; - selected.insert (i->second); - } - - out.reserve (n); - - n = 1; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p, ++n) { - if ((selected.find (n) == selected.end ()) == inverse) { - out.insert (*p); - } - } - - return out; -} - -void -Region::select_interacting_generic (const Region &other, int mode, bool touching, bool inverse) -{ - // shortcut - if (empty ()) { - return; - } else if (other.empty ()) { - // clear, if b is empty and - // * mode is inside or interacting and inverse is false ("inside" or "interacting") - // * mode is outside and inverse is true ("not outside") - if ((mode <= 0) != inverse) { - clear (); - } - return; - } - - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - for (const_iterator p = other.begin (); ! p.at_end (); ++p) { - if (p->box ().touches (bbox ())) { - ep.insert (*p, 0); - } - } - - size_t n = 1; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p, ++n) { - if (mode > 0 || p->box ().touches (other.bbox ())) { - ep.insert (*p, n); - } - } - - invalidate_cache (); - - db::InteractionDetector id (mode, 0); - id.set_include_touching (touching); - db::EdgeSink es; - ep.process (es, id); - id.finish (); - - db::Shapes out (false); - std::set selected; - n = 0; - for (db::InteractionDetector::iterator i = id.begin (); i != id.end () && i->first == 0; ++i) { - selected.insert (i->second); - ++n; - } - - out.reserve (db::Polygon::tag (), n); - n = 1; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p, ++n) { - if ((selected.find (n) == selected.end ()) == inverse) { - out.insert (*p); - } - } - - m_polygons.swap (out); - set_valid_polygons (); -} - namespace { -/** - * @brief A helper class for the region to edge interaction functionality - * - * Note: This special scanner uses pointers to two different objects: edges and polygons. - * It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate - * pointers to edges. - * - * There is a special box converter which is able to sort that out as well. - */ -template -class region_to_edge_interaction_filter - : public db::box_scanner_receiver -{ -public: - region_to_edge_interaction_filter (OutputContainer &output) - : mp_output (&output), m_inverse (false) - { - // .. nothing yet .. - } - - region_to_edge_interaction_filter (OutputContainer &output, const db::Region ®ion) - : mp_output (&output), m_inverse (true) - { - for (db::Region::const_iterator p = region.begin_merged (); ! p.at_end (); ++p) { - m_seen.insert (&*p); - } - } - - void add (const char *o1, size_t p1, const char *o2, size_t p2) - { - const db::Edge *e = 0; - const db::Polygon *p = 0; - - // Note: edges have property 0 and have even-valued pointers. - // Polygons have property 1 and odd-valued pointers. - if (p1 == 0 && p2 == 1) { - e = reinterpret_cast (o1); - p = reinterpret_cast (o2 - 1); - } else if (p1 == 1 && p2 == 0) { - e = reinterpret_cast (o2); - p = reinterpret_cast (o1 - 1); - } - - if (e && p && (m_seen.find (p) == 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 (db::Polygon::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 (p); - } else { - m_seen.insert (p); - mp_output->insert (*p); - } - } - - } - } - - void fill_output () - { - for (std::set::const_iterator p = m_seen.begin (); p != m_seen.end (); ++p) { - mp_output->insert (**p); - } - } - -private: - OutputContainer *mp_output; - std::set m_seen; - bool m_inverse; -}; - -/** - * @brief A special box converter that splits the pointers into polygon and edge pointers - */ -struct EdgeOrRegionBoxConverter -{ - typedef db::Box box_type; - - db::Box operator() (const char &c) const - { - // Note: edges have property 0 and have even-valued pointers. - // Polygons have property 1 and odd-valued pointers. - const char *cp = &c; - if ((size_t (cp) & 1) == 1) { - // it's a polygon - return (reinterpret_cast (cp - 1))->box (); - } else { - // it's an edge - const db::Edge *e = reinterpret_cast (cp); - return db::Box (e->p1 (), e->p2 ()); - } - } -}; - -} - -Region -Region::selected_interacting_generic (const Edges &other, bool inverse) const -{ - if (other.empty ()) { - if (! inverse) { - return Region (); - } else { - return *this; - } - } else if (empty ()) { - return *this; - } - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_polygons (); - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - scanner.insert ((char *) &*p + 1, 1); - } - - other.ensure_valid_merged_edges (); - for (Edges::const_iterator e = other.begin (); ! e.at_end (); ++e) { - scanner.insert ((char *) &*e, 0); - } - - Region output; - EdgeOrRegionBoxConverter bc; - - if (! inverse) { - region_to_edge_interaction_filter filter (output); - scanner.process (filter, 1, bc); - } else { - region_to_edge_interaction_filter filter (output, *this); - scanner.process (filter, 1, bc); - filter.fill_output (); - } - - return output; -} - -void -Region::select_interacting_generic (const Edges &other, bool inverse) -{ - // shortcut - if (other.empty ()) { - if (! inverse) { - clear (); - } - return; - } else if (empty ()) { - return; - } - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + other.size ()); - - ensure_valid_polygons (); - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - scanner.insert ((char *) &*p + 1, 1); - } - - other.ensure_valid_merged_edges (); - for (Edges::const_iterator e = other.begin (); ! e.at_end (); ++e) { - scanner.insert ((char *) &*e, 0); - } - - db::Shapes output (false); - EdgeOrRegionBoxConverter bc; - - if (! inverse) { - region_to_edge_interaction_filter filter (output); - scanner.process (filter, 1, bc); - } else { - region_to_edge_interaction_filter filter (output, *this); - scanner.process (filter, 1, bc); - filter.fill_output (); - } - - m_polygons.swap (output); - set_valid_polygons (); -} - -EdgePairs -Region::grid_check (db::Coord gx, db::Coord gy) const -{ - EdgePairs out; - - gx = std::max (db::Coord (1), gx); - gy = std::max (db::Coord (1), gy); - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - - for (size_t i = 0; i < p->holes () + 1; ++i) { - - db::Polygon::polygon_contour_iterator b, e; - - if (i == 0) { - b = p->begin_hull (); - e = p->end_hull (); - } else { - b = p->begin_hole ((unsigned int) (i - 1)); - e = p->end_hole ((unsigned int) (i - 1)); - } - - for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { - if (((*pt).x () % gx) != 0 || ((*pt).y () % gy) != 0) { - out.insert (EdgePair (db::Edge (*pt, *pt), db::Edge (*pt, *pt))); - } - } - - } - - } - - return out; -} - -static bool ac_less (double cos_a, bool gt180_a, double cos_b, bool gt180_b) -{ - if (gt180_a != gt180_b) { - return gt180_a < gt180_b; - } else { - if (gt180_a) { - return cos_a < cos_b - 1e-10; - } else { - return cos_a > cos_b + 1e-10; - } - } -} - -EdgePairs -Region::angle_check (double min, double max, bool inverse) const -{ - EdgePairs out; - - double cos_min = cos (std::max (0.0, std::min (360.0, min)) / 180.0 * M_PI); - double cos_max = cos (std::max (0.0, std::min (360.0, max)) / 180.0 * M_PI); - bool gt180_min = min > 180.0; - bool gt180_max = max > 180.0; - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - - for (size_t i = 0; i < p->holes () + 1; ++i) { - - const db::Polygon::contour_type *h = 0; - if (i == 0) { - h = &p->hull (); - } else { - h = &p->hole ((unsigned int) (i - 1)); - } - - size_t np = h->size (); - - for (size_t j = 0; j < np; ++j) { - - db::Edge e ((*h) [j], (*h) [(j + 1) % np]); - db::Edge ee (e.p2 (), (*h) [(j + 2) % np]); - double le = e.double_length (); - double lee = ee.double_length (); - - double cos_a = -db::sprod (e, ee) / (le * lee); - bool gt180_a = db::vprod_sign (e, ee) > 0; - - if ((ac_less (cos_a, gt180_a, cos_max, gt180_max) && !ac_less (cos_a, gt180_a, cos_min, gt180_min)) == !inverse) { - out.insert (EdgePair (e, ee)); - } - - } - - } - - } - - return out; -} - -static inline db::Coord snap_to_grid (db::Coord c, db::Coord g) -{ - // This form of snapping always snaps g/2 to right/top. - if (c < 0) { - c = -g * ((-c + (g - 1) / 2) / g); - } else { - c = g * ((c + g / 2) / g); - } - return c; -} - -void -Region::snap (db::Coord gx, db::Coord gy) -{ - db::Shapes polygons (false); - - gx = std::max (db::Coord (1), gx); - gy = std::max (db::Coord (1), gy); - - std::vector pts; - - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - - db::Polygon pnew; - - for (size_t i = 0; i < p->holes () + 1; ++i) { - - pts.clear (); - - db::Polygon::polygon_contour_iterator b, e; - - if (i == 0) { - b = p->begin_hull (); - e = p->end_hull (); - } else { - b = p->begin_hole ((unsigned int) (i - 1)); - e = p->end_hole ((unsigned int) (i - 1)); - } - - for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { - pts.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy))); - } - - if (i == 0) { - pnew.assign_hull (pts.begin (), pts.end ()); - } else { - pnew.insert_hole (pts.begin (), pts.end ()); - } - - } - - polygons.insert (pnew); - - } - - m_polygons.swap (polygons); - - bool was_merged = m_merged_semantics; - invalidate_cache (); - m_is_merged = was_merged; - set_valid_polygons (); -} +// ------------------------------------------------------------------------------------------------------------- +// Strange polygon processor /** * @brief A helper class to implement the strange polygon detector */ -struct StrangePolygonInsideFunc +struct StrangePolygonInsideFunc { inline bool operator() (int wc) const { @@ -1301,569 +51,638 @@ struct StrangePolygonInsideFunc } }; -Region -Region::strange_polygon_check () const +class StrangePolygonCheckProcessor + : public PolygonProcessorBase { - EdgeProcessor ep; - Region out; +public: + StrangePolygonCheckProcessor () { } - for (const_iterator p = begin (); ! p.at_end (); ++p) { - - ep.clear (); - ep.insert (*p); + virtual void process (const db::Polygon &poly, std::vector &res) const + { + EdgeProcessor ep; + ep.insert (poly); StrangePolygonInsideFunc inside; db::GenericMerge op (inside); - RegionPolygonSink pc (out); + db::PolygonContainer pc (res, false); db::PolygonGenerator pg (pc, false, false); ep.process (pg, op); } - return out; -} - -void -Region::init () -{ - m_report_progress = false; - m_bbox_valid = true; - m_is_merged = true; - m_merged_semantics = true; - m_strict_handling = false; - m_merge_min_coherence = false; - m_merged_polygons_valid = false; -} - -void -Region::disable_progress () -{ - m_report_progress = false; -} - -void -Region::enable_progress (const std::string &progress_desc) -{ - m_report_progress = true; - m_progress_desc = progress_desc; -} - -void -Region::invalidate_cache () -{ - m_bbox_valid = false; - m_merged_polygons.clear (); - m_merged_polygons_valid = false; -} - -void -Region::ensure_valid_merged_polygons () const -{ - // If no merged semantics applies or we will deliver the original - // polygons as merged ones, we need to make sure those are valid - // ones (with a unique memory address) - if (! m_merged_semantics || m_is_merged) { - ensure_valid_polygons (); - } else { - ensure_merged_polygons_valid (); - } -} - -void -Region::ensure_valid_polygons () const -{ - if (! has_valid_polygons ()) { - - m_polygons.clear (); - - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - ++n; - } - m_polygons.reserve (db::Polygon::tag (), n); - - for (const_iterator p = begin (); ! p.at_end (); ++p) { - m_polygons.insert (*p); - } - - // set valid polygons - m_iter = db::RecursiveShapeIterator (); - - } -} - -void -Region::set_valid_polygons () -{ - m_iter = db::RecursiveShapeIterator (); -} - -void -Region::ensure_bbox_valid () const -{ - if (! m_bbox_valid) { - m_bbox = db::Box (); - for (const_iterator p = begin (); ! p.at_end (); ++p) { - m_bbox += p->box (); - } - m_bbox_valid = true; - } -} - -Region::const_iterator -Region::begin_merged () const -{ - if (! m_merged_semantics || m_is_merged) { - return begin (); - } else { - ensure_merged_polygons_valid (); - return db::RegionIterator (m_merged_polygons.get_layer ().begin (), m_merged_polygons.get_layer ().end ()); - } -} - -std::pair -Region::begin_iter () const -{ - if (has_valid_polygons ()) { - return std::make_pair (db::RecursiveShapeIterator (m_polygons), db::ICplxTrans ()); - } else { - return std::make_pair (m_iter, m_iter_trans); - } -} - -std::pair -Region::begin_merged_iter () const -{ - if (! m_merged_semantics || m_is_merged) { - return begin_iter (); - } else { - ensure_merged_polygons_valid (); - return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ()); - } -} - -void -Region::ensure_merged_polygons_valid () const -{ - if (! m_merged_polygons_valid) { - - m_merged_polygons.clear (); - - db::EdgeProcessor ep (m_report_progress, m_progress_desc); - - // count edges and reserve memory - size_t n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p) { - n += p->vertices (); - } - ep.reserve (n); - - // insert the polygons into the processor - n = 0; - for (const_iterator p = begin (); ! p.at_end (); ++p, ++n) { - ep.insert (*p, n); - } - - // and run the merge step - db::MergeOp op (0); - db::ShapeGenerator pc (m_merged_polygons); - db::PolygonGenerator pg (pc, false /*don't resolve holes*/, m_merge_min_coherence); - ep.process (pg, op); - - m_merged_polygons_valid = true; - - } -} - -void -Region::insert (const db::Box &box) -{ - if (! box.empty () && box.width () > 0 && box.height () > 0) { - ensure_valid_polygons (); - m_polygons.insert (db::Polygon (box)); - m_is_merged = false; - invalidate_cache (); - } -} - -void -Region::insert (const db::Path &path) -{ - if (path.points () > 0) { - ensure_valid_polygons (); - m_polygons.insert (path.polygon ()); - m_is_merged = false; - invalidate_cache (); - } -} - -void -Region::insert (const db::Polygon &polygon) -{ - if (polygon.holes () > 0 || polygon.vertices () > 0) { - ensure_valid_polygons (); - m_polygons.insert (polygon); - m_is_merged = false; - invalidate_cache (); - } -} - -void -Region::insert (const db::SimplePolygon &polygon) -{ - if (polygon.vertices () > 0) { - ensure_valid_polygons (); - db::Polygon poly; - poly.assign_hull (polygon.begin_hull (), polygon.end_hull ()); - m_polygons.insert (poly); - m_is_merged = false; - invalidate_cache (); - } -} - -void -Region::insert (const db::Shape &shape) -{ - if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - ensure_valid_polygons (); - db::Polygon poly; - shape.polygon (poly); - m_polygons.insert (poly); - m_is_merged = false; - invalidate_cache (); - } -} - -void -Region::clear () -{ - m_polygons.clear (); - m_bbox = db::Box (); - m_bbox_valid = true; - m_is_merged = true; - m_merged_polygons.clear (); - m_merged_polygons_valid = true; - m_iter = db::RecursiveShapeIterator (); - m_iter_trans = db::ICplxTrans (); -} - -namespace { - -/** - * @brief A helper class for the DRC functionality which acts as an edge pair receiver - */ -class Edge2EdgeCheck - : public db::box_scanner_receiver -{ -public: - Edge2EdgeCheck (const EdgeRelationFilter &check, EdgePairs &output, bool different_polygons, bool requires_different_layers) - : mp_check (&check), mp_output (&output), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons), - m_pass (0) - { - m_distance = check.distance (); - } - - bool prepare_next_pass () - { - ++m_pass; - - if (m_pass == 1) { - - if (! m_ep.empty ()) { - m_ep_discarded.resize (m_ep.size (), false); - return true; - } - - } else if (m_pass == 2) { - - std::vector::const_iterator d = m_ep_discarded.begin (); - std::vector::const_iterator ep = m_ep.begin (); - while (ep != m_ep.end ()) { - tl_assert (d != m_ep_discarded.end ()); - if (! *d) { - mp_output->insert (*ep); - } - ++d; - ++ep; - } - - } - - return false; - } - - void 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 ((! m_different_polygons || p1 != p2) && (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0)) { - - // 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)); - - db::EdgePair ep; - if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { - - // found a violation: store inside the local buffer for now. In the second - // pass we will eliminate those which are shielded completely. - size_t n = m_ep.size (); - m_ep.push_back (ep); - m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n)); - m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n)); - - } - - } - - } else { - - // 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 n1, n2; - - for (unsigned int p = 0; p < 2; ++p) { - - std::pair k (*o1, p1); - for (std::multimap, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) { - n1.push_back (i->second); - } - - 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 nn; - std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn)); - - for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { - if (! m_ep_discarded [*i]) { - db::EdgePair ep = m_ep [*i].normalized (); - if (db::Edge (ep.first ().p1 (), ep.second ().p2 ()).intersect (*o2) && - db::Edge (ep.second ().p1 (), ep.first ().p2 ()).intersect (*o2)) { - m_ep_discarded [*i] = true; - } - } - } - - std::swap (o1, o2); - std::swap (p1, p2); - n1.swap (n2); - - } - - } - - } - - /** - * @brief Gets a value indicating whether the check requires different layers - */ - bool requires_different_layers () const - { - return m_requires_different_layers; - } - - /** - * @brief Sets a value indicating whether the check requires different layers - */ - void set_requires_different_layers (bool f) - { - m_requires_different_layers = f; - } - - /** - * @brief Gets a value indicating whether the check requires different layers - */ - bool different_polygons () const - { - return m_different_polygons; - } - - /** - * @brief Sets a value indicating whether the check requires different layers - */ - void set_different_polygons (bool f) - { - m_different_polygons = f; - } - - /** - * @brief Gets the distance value - */ - EdgeRelationFilter::distance_type distance () const - { - return m_distance; - } - -private: - const EdgeRelationFilter *mp_check; - EdgePairs *mp_output; - bool m_requires_different_layers; - bool m_different_polygons; - EdgeRelationFilter::distance_type m_distance; - std::vector m_ep; - std::multimap, size_t> m_e2ep; - std::vector m_ep_discarded; - unsigned int m_pass; + virtual const TransformationReducer *vars () const { return 0; } + virtual bool result_is_merged () const { return false; } + virtual bool requires_raw_input () const { return true; } + virtual bool wants_variants () const { return true; } + 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 Poly2PolyCheck - : public db::box_scanner_receiver +// ------------------------------------------------------------------------------------------------------------- +// Smoothing processor + +class SmoothingProcessor + : public PolygonProcessorBase { public: - Poly2PolyCheck (Edge2EdgeCheck &output) - : mp_output (&output) + SmoothingProcessor (db::Coord d) : m_d (d) { } + + virtual void process (const db::Polygon &poly, std::vector &res) const + { + res.push_back (db::smooth (poly, m_d)); + } + + virtual const TransformationReducer *vars () const { return &m_vars; } + virtual bool result_is_merged () const { return false; } + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return true; } + virtual bool result_must_not_be_merged () const { return false; } + +private: + db::Coord m_d; + db::MagnificationReducer m_vars; +}; + +// ------------------------------------------------------------------------------------------------------------- +// Rounded corners processor + +class RoundedCornersProcessor + : public PolygonProcessorBase +{ +public: + RoundedCornersProcessor (double rinner, double router, unsigned int n) + : m_rinner (rinner), m_router (router), m_n (n) + { } + + virtual void process (const db::Polygon &poly, std::vector &res) const + { + res.push_back (db::compute_rounded (poly, m_rinner, m_router, m_n)); + } + + virtual const TransformationReducer *vars () const { return &m_vars; } + virtual bool result_is_merged () const { return true; } // we believe so ... + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return true; } + virtual bool result_must_not_be_merged () const { return false; } + +private: + double m_rinner, m_router; + unsigned int m_n; + db::MagnificationReducer m_vars; +}; + +// ------------------------------------------------------------------------------------------------------------- +// Holes decomposition processor + +class HolesExtractionProcessor + : public PolygonProcessorBase +{ +public: + HolesExtractionProcessor () { } + + virtual void process (const db::Polygon &poly, std::vector &res) const + { + for (size_t i = 0; i < poly.holes (); ++i) { + res.push_back (db::Polygon ()); + res.back ().assign_hull (poly.begin_hole ((unsigned int) i), poly.end_hole ((unsigned int) i)); + } + } + + virtual const TransformationReducer *vars () const { return 0; } + virtual bool result_is_merged () const { return true; } // we believe so ... + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return true; } + virtual bool result_must_not_be_merged () const { return false; } +}; + +// ------------------------------------------------------------------------------------------------------------- +// Hull extraction processor + +class HullExtractionProcessor + : public PolygonProcessorBase +{ +public: + HullExtractionProcessor () { } + + virtual void process (const db::Polygon &poly, std::vector &res) const + { + res.push_back (db::Polygon ()); + res.back ().assign_hull (poly.begin_hull (), poly.end_hull ()); + } + + virtual const TransformationReducer *vars () const { return 0; } + virtual bool result_is_merged () const { return true; } // we believe so ... + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return true; } + virtual bool result_must_not_be_merged () const { return false; } +}; + +} + +// ------------------------------------------------------------------------------------------------------------- +// Region implementation + +Region::Region () + : mp_delegate (new EmptyRegion ()) +{ + // .. nothing yet .. +} + +Region::Region (RegionDelegate *delegate) + : mp_delegate (delegate) +{ + // .. nothing yet .. +} + +Region::Region (const Region &other) + : gsi::ObjectBase (), mp_delegate (other.mp_delegate->clone ()) +{ + // .. nothing yet .. +} + +Region::~Region () +{ + delete mp_delegate; + mp_delegate = 0; +} + +Region &Region::operator= (const Region &other) +{ + if (this != &other) { + set_delegate (other.mp_delegate->clone (), false); + } + return *this; +} + +Region::Region (const RecursiveShapeIterator &si) +{ + mp_delegate = new OriginalLayerRegion (si); +} + +Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics) +{ + mp_delegate = new OriginalLayerRegion (si, trans, merged_semantics); +} + +Region::Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) +{ + mp_delegate = new DeepRegion (si, dss, area_ratio, max_vertex_count); +} + +Region::Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics, double area_ratio, size_t max_vertex_count) +{ + mp_delegate = new DeepRegion (si, dss, trans, merged_semantics, area_ratio, max_vertex_count); +} + +const db::RecursiveShapeIterator & +Region::iter () const +{ + static db::RecursiveShapeIterator def_iter; + const db::RecursiveShapeIterator *i = mp_delegate->iter (); + return *(i ? i : &def_iter); +} + +void +Region::set_delegate (RegionDelegate *delegate, bool keep_attributes) +{ + if (delegate != mp_delegate) { + if (keep_attributes && delegate && mp_delegate) { + // copy the basic attributes like #threads etc. + delegate->RegionDelegate::operator= (*mp_delegate); + } + delete mp_delegate; + mp_delegate = delegate; + } +} + +void +Region::clear () +{ + set_delegate (new EmptyRegion ()); +} + +void +Region::reserve (size_t n) +{ + flat_region ()->reserve (n); +} + +template +Region &Region::transform (const T &trans) +{ + flat_region ()->transform (trans); + return *this; +} + +// explicit instantiations +template DB_PUBLIC Region &Region::transform (const db::ICplxTrans &); +template DB_PUBLIC Region &Region::transform (const db::Trans &); +template DB_PUBLIC Region &Region::transform (const db::Disp &); + +template +void Region::insert (const Sh &shape) +{ + flat_region ()->insert (shape); +} + +template DB_PUBLIC void Region::insert (const db::Box &); +template DB_PUBLIC void Region::insert (const db::SimplePolygon &); +template DB_PUBLIC void Region::insert (const db::Polygon &); +template DB_PUBLIC void Region::insert (const db::Path &); + +void Region::insert (const db::Shape &shape) +{ + flat_region ()->insert (shape); +} + +template +void Region::insert (const db::Shape &shape, const T &trans) +{ + flat_region ()->insert (shape, trans); +} + +template DB_PUBLIC void Region::insert (const db::Shape &, const db::ICplxTrans &); +template DB_PUBLIC void Region::insert (const db::Shape &, const db::Trans &); +template DB_PUBLIC void Region::insert (const db::Shape &, const db::Disp &); + +FlatRegion * +Region::flat_region () +{ + FlatRegion *region = dynamic_cast (mp_delegate); + if (! region) { + region = new FlatRegion (); + if (mp_delegate) { + region->RegionDelegate::operator= (*mp_delegate); // copy basic flags + region->insert_seq (begin ()); + region->set_is_merged (mp_delegate->is_merged ()); + } + set_delegate (region); + } + + return region; +} + +Region & +Region::size (coord_type d, unsigned int mode) +{ + set_delegate (mp_delegate->sized (d, mode)); + return *this; +} + +Region & +Region::size (coord_type dx, coord_type dy, unsigned int mode) +{ + set_delegate (mp_delegate->sized (dx, dy, mode)); + return *this; +} + +Region +Region::sized (coord_type d, unsigned int mode) const +{ + return Region (mp_delegate->sized (d, mode)); +} + +Region +Region::sized (coord_type dx, coord_type dy, unsigned int mode) const +{ + return Region (mp_delegate->sized (dx, dy, mode)); +} + +void +Region::round_corners (double rinner, double router, unsigned int n) +{ + process (RoundedCornersProcessor (rinner, router, n)); +} + +Region +Region::rounded_corners (double rinner, double router, unsigned int n) const +{ + return processed (RoundedCornersProcessor (rinner, router, n)); +} + +void +Region::smooth (coord_type d) +{ + process (SmoothingProcessor (d)); +} + +Region +Region::smoothed (coord_type d) const +{ + return processed (SmoothingProcessor (d)); +} + +void +Region::snap (db::Coord gx, db::Coord gy) +{ + set_delegate (mp_delegate->snapped_in_place (gx, gy)); +} + +Region +Region::snapped (db::Coord gx, db::Coord gy) const +{ + return Region (mp_delegate->snapped (gx, gy)); +} + +Region +Region::strange_polygon_check () const +{ + return Region (processed (StrangePolygonCheckProcessor ())); +} + +Region +Region::holes () const +{ + return Region (processed (HolesExtractionProcessor ())); +} + +Region +Region::hulls () const +{ + return Region (processed (HullExtractionProcessor ())); +} + +namespace +{ + +template +struct dot_delivery +{ + typedef Container container_type; + + dot_delivery () { // .. nothing yet .. } - - void finish (const db::Polygon *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 + void insert (const db::Point &pt, Container *container) const + { + container->insert (db::Edge (pt, pt)); + } +}; - m_scanner.clear (); - m_scanner.reserve (o->vertices ()); +template +struct box_delivery +{ + typedef Container container_type; - m_edges.clear (); - m_edges.reserve (o->vertices ()); - - for (db::Polygon::polygon_edge_iterator e = o->begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p); - } - - tl_assert (m_edges.size () == o->vertices ()); - - m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); - - } + box_delivery (db::Coord enl) + : m_d (enl, enl) + { + // .. nothing yet .. } - void add (const db::Polygon *o1, size_t p1, const db::Polygon *o2, size_t p2) - { - if ((! mp_output->different_polygons () || p1 != p2) && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) { - - m_scanner.clear (); - m_scanner.reserve (o1->vertices () + o2->vertices ()); - - m_edges.clear (); - m_edges.reserve (o1->vertices () + o2->vertices ()); - - for (db::Polygon::polygon_edge_iterator e = o1->begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p1); - } - - for (db::Polygon::polygon_edge_iterator e = o2->begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p2); - } - - tl_assert (m_edges.size () == o1->vertices () + o2->vertices ()); - - // 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 ()); - - mp_output->set_different_polygons (no_intra); - - } + void insert (const db::Point &pt, Container *container) const + { + container->insert (db::Box (pt - m_d, pt + m_d)); } private: - db::box_scanner m_scanner; - Edge2EdgeCheck *mp_output; - std::vector m_edges; + db::Vector m_d; +}; + +template +static void fill_texts (const Iter &iter, const std::string &pat, bool pattern, const Delivery &delivery, typename Delivery::container_type *container, const db::ICplxTrans &trans, const db::DeepRegion *org_deep) +{ + std::pair text_annot_name_id; + const db::Layout *layout = 0; + + if (org_deep) { + layout = &org_deep->deep_layer ().layout (); + const db::DeepShapeStore *store = org_deep->deep_layer ().store (); + if (! store->text_property_name ().is_nil ()) { + text_annot_name_id = layout->properties_repository ().get_id_of_name (store->text_property_name ()); + } + } + + tl::GlobPattern glob_pat; + bool all = false; + if (pattern) { + if (pat == "*") { + all = true; + } else { + glob_pat = tl::GlobPattern (pat); + } + } + + for (Iter si = iter; ! si.at_end (); ++si) { + + bool is_text = false; + std::string text_string; + + if (si->is_text ()) { + + // a raw text + is_text = true; + text_string = si->text_string (); + + } else if (layout && text_annot_name_id.first && si->prop_id () > 0) { + + // a text marker + const db::PropertiesRepository::properties_set &ps = layout->properties_repository ().properties (si->prop_id ()); + + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end () && ! is_text; ++j) { + if (j->first == text_annot_name_id.second) { + text_string = j->second.to_string (); + is_text = true; + } + } + + } + + if (is_text && + (all || (pattern && glob_pat.match (text_string)) || (!pattern && text_string == pat))) { + delivery.insert (si.trans () * (trans * si->bbox ().center ()), container); + } + + } +} + +template +class text_shape_receiver + : public db::HierarchyBuilderShapeReceiver +{ +public: + text_shape_receiver (const Delivery &delivery, const std::string &pat, bool pattern, const db::DeepRegion *org_deep) + : m_delivery (delivery), m_glob_pat (), m_all (false), m_pattern (pattern), m_pat (pat), m_text_annot_name_id (false, 0), mp_layout (0) + { + if (org_deep) { + mp_layout = & org_deep->deep_layer ().layout (); + const db::DeepShapeStore *store = org_deep->deep_layer ().store (); + if (! store->text_property_name ().is_nil ()) { + m_text_annot_name_id = mp_layout->properties_repository ().get_id_of_name (store->text_property_name ()); + } + } + + if (pattern) { + if (m_pat == "*") { + m_all = true; + } else { + m_glob_pat = tl::GlobPattern (pat); + } + } + } + + virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) + { + bool is_text = false; + std::string text_string; + + if (shape.is_text ()) { + + // a raw text + is_text = true; + text_string = shape.text_string (); + + } else if (mp_layout && m_text_annot_name_id.first && shape.prop_id () > 0) { + + // a text marker + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (shape.prop_id ()); + + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end () && ! is_text; ++j) { + if (j->first == m_text_annot_name_id.second) { + text_string = j->second.to_string (); + is_text = true; + } + } + + } + + if (is_text && + (m_all || (m_pattern && m_glob_pat.match (text_string)) || (!m_pattern && text_string == m_pat))) { + + db::Point pt = shape.bbox ().center (); + + if (! complex_region) { + if (region.contains (pt)) { + m_delivery.insert (pt.transformed (trans), target); + } + } else { + if (! complex_region->begin_overlapping (db::Box (pt, pt), db::box_convert ()).at_end ()) { + m_delivery.insert (pt.transformed (trans), target); + } + } + + } + } + + virtual void push (const db::Box &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } + virtual void push (const db::Polygon &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } + +private: + Delivery m_delivery; + tl::GlobPattern m_glob_pat; + bool m_all; + bool m_pattern; + std::string m_pat; + std::pair m_text_annot_name_id; + const db::Layout *mp_layout; }; } -EdgePairs -Region::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +Edges +Region::texts_as_dots (const std::string &pat, bool pattern) const { - EdgePairs result; - - db::box_scanner scanner (m_report_progress, m_progress_desc); - scanner.reserve (size () + (other ? other->size () : 0)); - - ensure_valid_merged_polygons (); - size_t n = 0; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - scanner.insert (&*p, n); - n += 2; + const db::DeepRegion *dr = dynamic_cast (delegate ()); + if (dr) { + return texts_as_dots (pat, pattern, const_cast (*dr->deep_layer ().store ())); } - if (other) { - other->ensure_valid_merged_polygons (); - n = 1; - for (const_iterator p = other->begin_merged (); ! p.at_end (); ++p) { - scanner.insert (&*p, n); - n += 2; - } + std::pair si = begin_iter (); + if (! dr) { + // some optimization + si.first.shape_flags (si.first.shape_flags () & db::ShapeIterator::Texts); } - EdgeRelationFilter check (rel, d, metrics); - check.set_include_zero (other != 0); - check.set_whole_edges (whole_edges); - check.set_ignore_angle (ignore_angle); - check.set_min_projection (min_projection); - check.set_max_projection (max_projection); + std::auto_ptr res (new db::FlatEdges ()); + res->set_merged_semantics (false); - Edge2EdgeCheck edge_check (check, result, different_polygons, other != 0); - Poly2PolyCheck poly_check (edge_check); + fill_texts (si.first, pat, pattern, dot_delivery (), res.get (), si.second, dr); - do { - scanner.process (poly_check, d, db::box_convert ()); - } while (edge_check.prepare_next_pass ()); - - return result; + return Edges (res.release ()); } -EdgePairs -Region::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +Edges +Region::texts_as_dots (const std::string &pat, bool pattern, db::DeepShapeStore &store) const { - EdgePairs result; + const db::DeepRegion *dr = dynamic_cast (delegate ()); - EdgeRelationFilter check (rel, d, metrics); - check.set_whole_edges (whole_edges); - check.set_ignore_angle (ignore_angle); - check.set_min_projection (min_projection); - check.set_max_projection (max_projection); + std::pair si = begin_iter (); + if (! dr) { + // some optimization + si.first.shape_flags (si.first.shape_flags () & db::ShapeIterator::Texts); + } - Edge2EdgeCheck edge_check (check, result, false, false); - Poly2PolyCheck poly_check (edge_check); + if (! si.first.layout ()) { - do { - size_t n = 0; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - poly_check.finish (&*p, n); - n += 2; - } - } while (edge_check.prepare_next_pass ()); + // flat fallback if the source isn't a deep or original layer + std::auto_ptr res (new db::FlatEdges ()); + res->set_merged_semantics (false); - return result; + fill_texts (si.first, pat, pattern, dot_delivery (), res.get (), si.second, dr); + + return Edges (res.release ()); + + } + + text_shape_receiver > pipe = text_shape_receiver > (dot_delivery (), pat, pattern, dr); + if (dr && dr->deep_layer ().store () == &store) { + return Edges (new db::DeepEdges (store.create_copy (dr->deep_layer (), &pipe))); + } else { + return Edges (new db::DeepEdges (store.create_custom_layer (si.first, &pipe, si.second))); + } +} + +Region +Region::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl) const +{ + const db::DeepRegion *dr = dynamic_cast (delegate ()); + if (dr) { + return texts_as_boxes (pat, pattern, enl, const_cast (*dr->deep_layer ().store ())); + } + + std::pair si = begin_iter (); + if (! dr) { + // some optimization + si.first.shape_flags (si.first.shape_flags () & db::ShapeIterator::Texts); + } + + std::auto_ptr res (new db::FlatRegion ()); + res->set_merged_semantics (false); + + fill_texts (si.first, pat, pattern, box_delivery (enl), res.get (), si.second, dr); + + return Region (res.release ()); +} + +Region +Region::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl, db::DeepShapeStore &store) const +{ + const db::DeepRegion *dr = dynamic_cast (delegate ()); + + std::pair si = begin_iter (); + if (! dr) { + // some optimization + si.first.shape_flags (si.first.shape_flags () & db::ShapeIterator::Texts); + } + + if (! si.first.layout ()) { + + // flat fallback if the source isn't a deep or original layer + std::auto_ptr res (new db::FlatRegion ()); + res->set_merged_semantics (false); + + fill_texts (si.first, pat, pattern, box_delivery (enl), res.get (), si.second, dr); + + return Region (res.release ()); + + } + + text_shape_receiver > pipe = text_shape_receiver > (box_delivery (enl), pat, pattern, dr); + if (dr && dr->deep_layer ().store () == &store) { + return Region (new db::DeepRegion (store.create_copy (dr->deep_layer (), &pipe))); + } else { + return Region (new db::DeepRegion (store.create_custom_layer (si.first, &pipe, si.second))); + } } } diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 4ca5cae6b..30a322d88 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -25,281 +25,102 @@ #define HDR_dbRegion #include "dbCommon.h" - -#include "dbTypes.h" -#include "dbPolygon.h" -#include "dbPath.h" -#include "dbTrans.h" -#include "dbShape.h" -#include "dbShapes.h" -#include "dbShapes2.h" -#include "dbEdgePairRelations.h" -#include "dbShapeProcessor.h" -#include "dbEdges.h" +#include "dbRegionDelegate.h" #include "dbRecursiveShapeIterator.h" -#include "dbEdgePairs.h" -#include "tlString.h" +#include "dbPolygonGenerators.h" +#include "dbCellVariants.h" + #include "gsiObject.h" +#include + namespace db { -/** - * @brief A perimeter filter for use with Region::filter or Region::filtered - * - * This filter has two parameters: pmin and pmax. - * It will filter all polygons for which the perimeter is >= pmin and < pmax. - * There is an "invert" flag which allows to select all polygons not - * matching the criterion. - */ - -struct DB_PUBLIC RegionPerimeterFilter -{ - typedef db::coord_traits::perimeter_type perimeter_type; - - /** - * @brief Constructor - * - * @param amin The min perimeter (only polygons above this value are filtered) - * @param amax The maximum perimeter (only polygons with a perimeter below this value are filtered) - * @param inverse If set to true, only polygons not matching this criterion will be filtered - */ - RegionPerimeterFilter (perimeter_type pmin, perimeter_type pmax, bool inverse) - : m_pmin (pmin), m_pmax (pmax), m_inverse (inverse) - { - // .. nothing yet .. - } - - /** - * @brief Returns true if the polygon's perimeter matches the criterion - */ - bool operator() (const db::Polygon &poly) const - { - perimeter_type p = 0; - for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end () && p < m_pmax; ++e) { - p += (*e).length (); - } - - if (! m_inverse) { - return p >= m_pmin && p < m_pmax; - } else { - return ! (p >= m_pmin && p < m_pmax); - } - } - -private: - perimeter_type m_pmin, m_pmax; - bool m_inverse; -}; - -/** - * @brief An area filter for use with Region::filter or Region::filtered - * - * This filter has two parameters: amin and amax. - * It will filter all polygons for which the area is >= amin and < amax. - * There is an "invert" flag which allows to select all polygons not - * matching the criterion. - */ - -struct DB_PUBLIC RegionAreaFilter -{ - typedef db::Polygon::area_type area_type; - - /** - * @brief Constructor - * - * @param amin The min area (only polygons above this value are filtered) - * @param amax The maximum area (only polygons with an area below this value are filtered) - * @param inverse If set to true, only polygons not matching this criterion will be filtered - */ - RegionAreaFilter (area_type amin, area_type amax, bool inverse) - : m_amin (amin), m_amax (amax), m_inverse (inverse) - { - // .. nothing yet .. - } - - /** - * @brief Returns true if the polygon's area matches the criterion - */ - bool operator() (const db::Polygon &poly) const - { - area_type a = poly.area (); - if (! m_inverse) { - return a >= m_amin && a < m_amax; - } else { - return ! (a >= m_amin && a < m_amax); - } - } - -private: - area_type m_amin, m_amax; - bool m_inverse; -}; - -/** - * @brief A filter for rectilinear polygons - * - * This filter will select all polygons which are rectilinear. - */ - -struct DB_PUBLIC RectilinearFilter -{ - /** - * @brief Constructor - * @param inverse If set to true, only polygons not matching this criterion will be filtered - */ - RectilinearFilter (bool inverse) - : m_inverse (inverse) - { - // .. nothing yet .. - } - - /** - * @brief Returns true if the polygon's area matches the criterion - */ - bool operator() (const db::Polygon &poly) const - { - return poly.is_rectilinear () != m_inverse; - } - -private: - bool m_inverse; -}; - -/** - * @brief A rectangle filter - * - * This filter will select all polygons which are rectangles. - */ - -struct DB_PUBLIC RectangleFilter -{ - /** - * @brief Constructor - * @param inverse If set to true, only polygons not matching this criterion will be filtered - */ - RectangleFilter (bool inverse) - : m_inverse (inverse) - { - // .. nothing yet .. - } - - /** - * @brief Returns true if the polygon's area matches the criterion - */ - bool operator() (const db::Polygon &poly) const - { - return poly.is_box () != m_inverse; - } - -private: - bool m_inverse; -}; - -/** - * @brief A bounding box filter for use with Region::filter or Region::filtered - * - * This filter has two parameters: vmin and vmax. - * It will filter all polygons for which the selected bounding box parameter is >= vmin and < vmax. - * There is an "invert" flag which allows to select all polygons not - * matching the criterion. - * - * For bounding box parameters the following choices are available: - * - (BoxWidth) width - * - (BoxHeight) height - * - (BoxMaxDim) maximum of width and height - * - (BoxMinDim) minimum of width and height - * - (BoxAverageDim) average of width and height - */ - -struct DB_PUBLIC RegionBBoxFilter -{ - typedef db::Box::distance_type value_type; - - /** - * @brief The parameters available - */ - enum parameter_type { - BoxWidth, - BoxHeight, - BoxMaxDim, - BoxMinDim, - BoxAverageDim - }; - - /** - * @brief Constructor - * - * @param vmin The min value (only polygons with bounding box parameters above this value are filtered) - * @param vmax The max value (only polygons with bounding box parameters below this value are filtered) - * @param inverse If set to true, only polygons not matching this criterion will be filtered - */ - RegionBBoxFilter (value_type vmin, value_type vmax, bool inverse, parameter_type parameter) - : m_vmin (vmin), m_vmax (vmax), m_inverse (inverse), m_parameter (parameter) - { - // .. nothing yet .. - } - - /** - * @brief Returns true if the polygon's area matches the criterion - */ - bool operator() (const db::Polygon &poly) const - { - value_type v = 0; - db::Box box = poly.box (); - if (m_parameter == BoxWidth) { - v = box.width (); - } else if (m_parameter == BoxHeight) { - v = box.height (); - } else if (m_parameter == BoxMinDim) { - v = std::min (box.width (), box.height ()); - } else if (m_parameter == BoxMaxDim) { - v = std::max (box.width (), box.height ()); - } else if (m_parameter == BoxAverageDim) { - v = (box.width () + box.height ()) / 2; - } - if (! m_inverse) { - return v >= m_vmin && v < m_vmax; - } else { - return ! (v >= m_vmin && v < m_vmax); - } - } - -private: - value_type m_vmin, m_vmax; - bool m_inverse; - parameter_type m_parameter; -}; +class EdgeFilterBase; +class FlatRegion; +class EmptyRegion; +class DeepShapeStore; +class TransformationReducer; /** * @brief A region iterator * * The iterator delivers the polygons of the region */ - class DB_PUBLIC RegionIterator { public: - typedef db::Polygon value_type; - typedef const db::Polygon &reference; - typedef const db::Polygon *pointer; + typedef RegionIteratorDelegate::value_type value_type; + typedef const value_type &reference; + typedef const value_type *pointer; typedef std::forward_iterator_tag iterator_category; typedef void difference_type; + /** + * @brief Default constructor + */ + RegionIterator () + : mp_delegate (0) + { + // .. nothing yet .. + } + + /** + * @brief Constructor from a delegate + * The iterator will take ownership over the delegate + */ + RegionIterator (RegionIteratorDelegate *delegate) + : mp_delegate (delegate) + { + // .. nothing yet .. + } + + /** + * @brief Destructor + */ + ~RegionIterator () + { + delete mp_delegate; + mp_delegate = 0; + } + + /** + * @brief Copy constructor and assignment + */ + RegionIterator (const RegionIterator &other) + : mp_delegate (0) + { + operator= (other); + } + + /** + * @brief Assignment + */ + RegionIterator &operator= (const RegionIterator &other) + { + if (this != &other) { + delete mp_delegate; + mp_delegate = other.mp_delegate ? other.mp_delegate->clone () : 0; + } + return *this; + } + /** * @Returns true, if the iterator is at the end */ bool at_end () const { - return m_from == m_to && m_rec_iter.at_end (); + return mp_delegate == 0 || mp_delegate->at_end (); } /** * @brief Increment */ - RegionIterator &operator++ () + RegionIterator &operator++ () { - inc (); - set (); + if (mp_delegate) { + mp_delegate->increment (); + } return *this; } @@ -308,11 +129,9 @@ public: */ reference operator* () const { - if (m_rec_iter.at_end ()) { - return *m_from; - } else { - return m_polygon; - } + const value_type *value = operator-> (); + tl_assert (value != 0); + return *value; } /** @@ -320,70 +139,66 @@ public: */ pointer operator-> () const { - if (m_rec_iter.at_end ()) { - return &*m_from; + return mp_delegate ? mp_delegate->get () : 0; + } + +private: + RegionIteratorDelegate *mp_delegate; +}; + +/** + * @brief A helper class allowing delivery of addressable polygons + * + * In some applications (i.e. box scanner), polygons need to be taken + * by address. The region cannot always deliver adressable polygons. + * This class help providing this ability by keeping a temporary copy + * if required. + */ + +class DB_PUBLIC AddressablePolygonDelivery +{ +public: + AddressablePolygonDelivery () + : m_iter (), m_valid (false) + { + // .. nothing yet .. + } + + AddressablePolygonDelivery (const RegionIterator &iter, bool valid) + : m_iter (iter), m_valid (valid) + { + if (! m_valid && ! m_iter.at_end ()) { + m_heap.push_back (*m_iter); + } + } + + bool at_end () const + { + return m_iter.at_end (); + } + + AddressablePolygonDelivery &operator++ () + { + ++m_iter; + if (! m_valid && ! m_iter.at_end ()) { + m_heap.push_back (*m_iter); + } + return *this; + } + + const db::Polygon *operator-> () const + { + if (m_valid) { + return m_iter.operator-> (); } else { - return &m_polygon; + return &m_heap.back (); } } private: - friend class Region; - - typedef db::layer polygon_layer_type; - typedef polygon_layer_type::iterator iterator_type; - - db::RecursiveShapeIterator m_rec_iter; - db::ICplxTrans m_iter_trans; - db::Polygon m_polygon; - iterator_type m_from, m_to; - - /** - * @brief ctor from a recursive shape iterator - */ - RegionIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) - : m_rec_iter (iter), m_iter_trans (trans), m_from (), m_to () - { - // NOTE: the following initialization appears to be required on some compilers - // (specifically MacOS/clang) to ensure the proper initialization of the iterators - m_from = m_to; - set (); - } - - /** - * @brief ctor from a range of polygons inside a vector - */ - RegionIterator (iterator_type from, iterator_type to) - : m_from (from), m_to (to) - { - // no required yet: set (); - } - - /** - * @brief Establish the iterator at the current position - */ - void set () - { - while (! m_rec_iter.at_end () && ! (m_rec_iter.shape ().is_polygon () || m_rec_iter.shape ().is_path () || m_rec_iter.shape ().is_box ())) { - inc (); - } - if (! m_rec_iter.at_end ()) { - m_rec_iter.shape ().polygon (m_polygon); - m_polygon.transform (m_iter_trans * m_rec_iter.trans (), false); - } - } - - /** - * @brief Increment the iterator - */ - void inc () - { - if (! m_rec_iter.at_end ()) { - ++m_rec_iter; - } else { - ++m_from; - } - } + RegionIterator m_iter; + bool m_valid; + std::list m_heap; }; /** @@ -395,12 +210,11 @@ private: * Regions can have different states. Specifically a region can be merged (no overlapping * polygons are present, touching polygons are merged, self-intersections of polygons are * removed) or non-merged (polygons may overlap or polygons may be self-intersecting). In - * merged state, the wrap count at every point is either zero or 1, in non-merged state + * merged state, the wrap count at every point is either zero or 1, in non-merged state * it can be every value. * * Polygons inside the region may contain holes if the region is merged. */ - class DB_PUBLIC Region : public gsi::ObjectBase { @@ -411,47 +225,87 @@ public: typedef db::Vector vector_type; typedef db::Point point_type; typedef db::Box box_type; - typedef coord_traits::distance_type distance_type; - typedef coord_traits::perimeter_type perimeter_type; - typedef coord_traits::area_type area_type; + typedef coord_traits::distance_type distance_type; + typedef coord_traits::perimeter_type perimeter_type; + typedef coord_traits::area_type area_type; typedef RegionIterator const_iterator; - /** + /** * @brief Default constructor * * Creates an empty region. */ - Region () - : m_polygons (false), m_merged_polygons (false) + Region (); + + /** + * @brief Destructor + */ + ~Region (); + + /** + * @brief Constructor from a delegate + * + * The region will take ownership of the delegate. + */ + Region (RegionDelegate *delegate); + + /** + * @brief Copy constructor + */ + Region (const Region &other); + + /** + * @brief Assignment + */ + Region &operator= (const Region &other); + + /** + * @brief Constructor from a box + */ + explicit Region (const db::Box &s) + : mp_delegate (0) { - init (); + insert (s); } /** - * @brief Constructor from an object - * - * Creates a region representing a single instance of that object + * @brief Constructor from a polygon */ - template - Region (const Sh &s) - : m_polygons (false), m_merged_polygons (false) + explicit Region (const db::Polygon &s) + : mp_delegate (0) + { + insert (s); + } + + /** + * @brief Constructor from a simple polygon + */ + explicit Region (const db::SimplePolygon &s) + : mp_delegate (0) + { + insert (s); + } + + /** + * @brief Constructor from a path + */ + explicit Region (const db::Path &s) + : mp_delegate (0) { - init (); insert (s); } /** * @brief Sequence constructor * - * Creates a region from a sequence of objects. The objects can be boxes, + * Creates a region from a sequence of objects. The objects can be boxes, * polygons, paths or shapes. This version accepts iterators of the begin ... end * style. */ template - Region (const Iter &b, const Iter &e) - : m_polygons (false), m_merged_polygons (false) + explicit Region (const Iter &b, const Iter &e) + : mp_delegate (0) { - init (); reserve (e - b); for (Iter i = b; i != e; ++i) { insert (*i); @@ -461,31 +315,81 @@ public: /** * @brief Constructor from a RecursiveShapeIterator * - * Creates a region from a recursive shape iterator. This allows to feed a region + * Creates a region from a recursive shape iterator. This allows feeding a region * from a hierarchy of cells. */ - Region (const RecursiveShapeIterator &si); + explicit Region (const RecursiveShapeIterator &si); /** * @brief Constructor from a RecursiveShapeIterator with a transformation * - * Creates a region from a recursive shape iterator. This allows to feed a region + * Creates a region from a recursive shape iterator. This allows feeding a region * from a hierarchy of cells. The transformation is useful to scale to a specific * DBU for example. */ - Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true); + explicit Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true); + + /** + * @brief Constructor from a RecursiveShapeIterator providing a deep representation + * + * This version will create a hierarchical region. The DeepShapeStore needs to be provided + * during the lifetime of the region and acts as a heap for optimized data. + * + * "area_ratio" and "max_vertex_count" are optimization parameters for the + * shape splitting algorithm. + */ + explicit Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio = 3.0, size_t max_vertex_count = 16); + + /** + * @brief Constructor from a RecursiveShapeIterator providing a deep representation with transformation + */ + explicit Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics = true, double area_ratio = 3.0, size_t max_vertex_count = 16); + + /** + * @brief Gets the underlying delegate object + */ + RegionDelegate *delegate () const + { + return mp_delegate; + } + + /** + * @brief Sets the base verbosity + * + * Setting this value will make timing measurements appear at least at + * the given verbosity level and more detailed timing at the given level + * plus 10. The default level is 30. + */ + void set_base_verbosity (int vb) + { + mp_delegate->set_base_verbosity (vb); + } + + /** + * @brief Gets the base verbosity + */ + unsigned int base_verbosity () const + { + return mp_delegate->base_verbosity (); + } /** * @brief Enable progress reporting * * @param progress_text The description text of the progress object */ - void enable_progress (const std::string &progress_desc = std::string ()); + void enable_progress (const std::string &desc = std::string ()) + { + mp_delegate->enable_progress (desc); + } /** * @brief Disable progress reporting */ - void disable_progress (); + void disable_progress () + { + mp_delegate->disable_progress (); + } /** * @brief Iterator of the region @@ -495,52 +399,43 @@ public: */ const_iterator begin () const { - if (has_valid_polygons ()) { - return const_iterator (m_polygons.get_layer ().begin (), m_polygons.get_layer ().end ()); - } else { - return const_iterator (m_iter, m_iter_trans); - } + return RegionIterator (mp_delegate->begin ()); } /** - * @brief Returns the merged polygons if merge semantics applies + * @brief Returns the merged polygons if merge semantics applies * * If merge semantics is not enabled, this iterator delivers the individual polygons. */ - const_iterator begin_merged () const; + const_iterator begin_merged () const + { + return RegionIterator (mp_delegate->begin_merged ()); + } /** * @brief Delivers a RecursiveShapeIterator pointing to the polygons plus the necessary transformation */ - std::pair begin_iter () const; + std::pair begin_iter () const + { + return mp_delegate->begin_iter (); + } /** * @brief Delivers a RecursiveShapeIterator pointing to the merged polygons plus the necessary transformation */ - std::pair begin_merged_iter () const; + std::pair begin_merged_iter () const + { + return mp_delegate->begin_merged_iter (); + } /** - * @brief Insert a box into the region + * @brief Inserts the given shape (working object) into the region */ - void insert (const db::Box &box); + template + void insert (const Sh &shape); /** - * @brief Insert a path into the region - */ - void insert (const db::Path &path); - - /** - * @brief Insert a simple polygon into the region - */ - void insert (const db::SimplePolygon &polygon); - - /** - * @brief Insert a polygon into the region - */ - void insert (const db::Polygon &polygon); - - /** - * @brief Insert a shape into the region + * @brief Insert a shape reference into the region */ void insert (const db::Shape &shape); @@ -548,38 +443,33 @@ public: * @brief Insert a transformed shape into the region */ template - void insert (const db::Shape &shape, const T &trans) - { - if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - ensure_valid_polygons (); - db::Polygon poly; - shape.polygon (poly); - poly.transform (trans); - m_polygons.insert (poly); - m_is_merged = false; - invalidate_cache (); - } - } + void insert (const db::Shape &shape, const T &trans); /** * @brief Returns true if the region is empty */ bool empty () const { - return has_valid_polygons () && m_polygons.empty (); + return mp_delegate->empty (); } /** * @brief Returns the number of polygons in the region */ - size_t size () const; + size_t size () const + { + return mp_delegate->size (); + } /** * @brief Returns a string representing the region * * nmax specifies how many polygons are included (set to std::numeric_limits::max() for "all". */ - std::string to_string (size_t nmax = 10) const; + std::string to_string (size_t nmax = 10) const + { + return mp_delegate->to_string (nmax); + } /** * @brief Clear the region @@ -589,10 +479,7 @@ public: /** * @brief Reserve memory for the given number of polygons */ - void reserve (size_t n) - { - m_polygons.reserve (db::Polygon::tag (), n); - } + void reserve (size_t n); /** * @brief Sets the minimum-coherence flag @@ -600,15 +487,12 @@ public: * If minimum coherence is set, the merge operations (explicit merge with \merge or * implicit merge through merged_semantics) are performed using minimum coherence mode. * The coherence mode determines how kissing-corner situations are resolved. If - * minimum coherence is selected, they are resolved such that multiple polygons are + * minimum coherence is selected, they are resolved such that multiple polygons are * created which touch at a corner). */ void set_min_coherence (bool f) { - if (m_merge_min_coherence != f) { - m_merge_min_coherence = f; - invalidate_cache (); - } + mp_delegate->set_min_coherence (f); } /** @@ -616,45 +500,51 @@ public: */ bool min_coherence () const { - return m_merge_min_coherence; + return mp_delegate->min_coherence (); } /** * @brief Sets the merged-semantics flag * - * If merged semantics is enabled (the default), coherent polygons will be considered - * as single regions and artificial edges such as cut-lines will not be considered. + * If merged semantics is enabled (the default), coherent polygons will be considered + * as single regions and artificial edges such as cut-lines will not be considered. * Merged semantics thus is equivalent to considering coherent areas rather than * single polygons. */ - void set_merged_semantics (bool f); + void set_merged_semantics (bool f) + { + mp_delegate->set_merged_semantics (f); + } /** * @brief Gets the merged-semantics flag */ bool merged_semantics () const { - return m_merged_semantics; + return mp_delegate->merged_semantics (); } /** * @brief Enables or disables strict handling * - * Strict handling means to leave away some optimizations. Specifically the + * Strict handling means to leave away some optimizations. Specifically the * output of boolean operations will be merged even if one input is empty. - * Without strict handling, the operation will be optimized and output + * Without strict handling, the operation will be optimized and output * won't be merged. * * Strict handling is disabled by default. */ - void set_strict_handling (bool f); + void set_strict_handling (bool f) + { + mp_delegate->set_strict_handling (f); + } /** * @brief Gets a valid indicating whether strict handling is enabled */ bool strict_handling () const { - return m_strict_handling; + return mp_delegate->strict_handling (); } /** @@ -663,100 +553,145 @@ public: * If the region is not merged, this method may return false even * if the merged region would be a box. */ - bool is_box () const; + bool is_box () const + { + return mp_delegate->is_box (); + } /** - * @brief Returns true if the region is merged + * @brief Returns true if the region is merged */ bool is_merged () const { - return m_is_merged; + return mp_delegate->is_merged (); } /** * @brief Returns the area of the region * - * This method returns the area sum over all polygons. - * Merged semantics applies. In merged semantics, the area is the correct area covered by the + * This method returns the area sum over all polygons. + * Merged semantics applies. In merged semantics, the area is the correct area covered by the * polygons. Without merged semantics, overlapping parts are counted twice. * * If a box is given, the computation is restricted to that box. */ - area_type area (const db::Box &box = db::Box ()) const; + area_type area (const db::Box &box = db::Box ()) const + { + return mp_delegate->area (box); + } /** * @brief Returns the perimeter sum of the region * - * This method returns the perimeter sum over all polygons. - * Merged semantics applies. In merged semantics, the perimeter is the true perimeter. + * This method returns the perimeter sum over all polygons. + * Merged semantics applies. In merged semantics, the perimeter is the true perimeter. * Without merged semantics, inner edges contribute to the perimeter. * * If a box is given, the computation is restricted to that box. * Edges coincident with the box edges are counted only if the form outer edges at the box edge. */ - perimeter_type perimeter (const db::Box &box = db::Box ()) const; + perimeter_type perimeter (const db::Box &box = db::Box ()) const + { + return mp_delegate->perimeter (box); + } /** * @brief Returns the bounding box of the region */ Box bbox () const { - ensure_bbox_valid (); - return m_bbox; + return mp_delegate->bbox (); } /** - * @brief Filters the polygons + * @brief Filters the polygons * * This method will keep all polygons for which the filter returns true. * Merged semantics applies. In merged semantics, the filter will run over * all merged polygons. */ - template - Region &filter (F &filter) + Region &filter (const PolygonFilterBase &filter) { - polygon_iterator_type pw = m_polygons.get_layer ().begin (); - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - if (filter (*p)) { - if (pw == m_polygons.get_layer ().end ()) { - m_polygons.get_layer ().insert (*p); - pw = m_polygons.get_layer ().end (); - } else { - m_polygons.get_layer ().replace (pw++, *p); - } - } - } - m_polygons.get_layer ().erase (pw, m_polygons.get_layer ().end ()); - m_merged_polygons.clear (); - m_is_merged = m_merged_semantics; - m_iter = db::RecursiveShapeIterator (); + set_delegate (mp_delegate->filter_in_place (filter)); return *this; } /** * @brief Returns the filtered polygons * - * This method will return a new region with only those polygons which + * This method will return a new region with only those polygons which * conform to the filter criterion. */ - template - Region filtered (F &filter) const + Region filtered (const PolygonFilterBase &filter) const { - Region d; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - if (filter (*p)) { - d.insert (*p); - } - } - return d; + return Region (mp_delegate->filtered (filter)); + } + + /** + * @brief Processes the (merged) polygons + * + * This method will keep all polygons which the processor returns. + * The processing filter can apply modifications too. These modifications will be + * kept in the output region. + * + * Merged semantics applies. In merged semantics, the filter will run over + * all merged polygons. + */ + Region &process (const PolygonProcessorBase &filter) + { + set_delegate (mp_delegate->process_in_place (filter)); + return *this; + } + + /** + * @brief Returns the processed polygons + * + * This method will keep all polygons which the processor returns. + * The processing filter can apply modifications too. These modifications will be + * kept in the output region. + * + * Merged semantics applies. In merged semantics, the filter will run over + * all merged polygons. + * + * This method will return a new region with the modified and filtered polygons. + */ + Region processed (const PolygonProcessorBase &filter) const + { + return Region (mp_delegate->processed (filter)); + } + + /** + * @brief Processes the polygons into edges + * + * This method applies a processor returning edges for the polygons. + * + * Merged semantics applies. In merged semantics, the filter will run over + * all merged polygons. + */ + Edges processed (const PolygonToEdgeProcessorBase &filter) const + { + return Edges (mp_delegate->processed_to_edges (filter)); + } + + /** + * @brief Processes the polygons into edge pairs + * + * This method applies a processor returning edge pairs for the polygons. + * + * Merged semantics applies. In merged semantics, the filter will run over + * all merged polygons. + */ + EdgePairs processed (const PolygonToEdgePairProcessorBase &filter) const + { + return EdgePairs (mp_delegate->processed_to_edge_pairs (filter)); } /** * @brief Applies a width check and returns EdgePairs which correspond to violation markers * - * The width check will create a edge pairs if the width of the area between the + * The width check will create a edge pairs if the width of the area between the * edges is less than the specified threshold d. Without "whole_edges", the parts of - * the edges are returned which violate the condition. If "whole_edges" is true, the + * the edges are returned which violate the condition. If "whole_edges" is true, the * result will contain the complete edges participating in the result. * * The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected" @@ -764,9 +699,9 @@ public: * * ingore_angle allows specification of a maximum angle that connected edges can have to not participate * in the check. By choosing 90 degree, edges with angles of 90 degree and larger are not checked, - * but acute corners are for example. + * but acute corners are for example. * - * With min_projection and max_projection it is possible to specify how edges must be related + * With min_projection and max_projection it is possible to specify how edges must be related * to each other. If the length of the projection of either edge on the other is >= min_projection * or < max_projection, the edges are considered for the check. * @@ -776,7 +711,7 @@ public: */ EdgePairs width_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const { - return run_single_polygon_check (db::WidthRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + return EdgePairs (mp_delegate->width_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); } /** @@ -789,7 +724,7 @@ public: */ EdgePairs space_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const { - return run_check (db::SpaceRelation, false, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + return EdgePairs (mp_delegate->space_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); } /** @@ -802,7 +737,7 @@ public: */ EdgePairs isolated_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const { - return run_check (db::SpaceRelation, true, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + return EdgePairs (mp_delegate->isolated_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); } /** @@ -815,13 +750,13 @@ public: */ EdgePairs notch_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const { - return run_single_polygon_check (db::SpaceRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + return EdgePairs (mp_delegate->notch_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); } /** * @brief Applies a enclosed check and returns EdgePairs which correspond to violation markers * - * The check will return true, where this region is enclosing the polygons of the other + * The check will return true, where this region is enclosing the polygons of the other * region by less than the specified threshold d. * * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". @@ -832,13 +767,13 @@ public: */ EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const { - return run_check (db::OverlapRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + return EdgePairs (mp_delegate->enclosing_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); } /** * @brief Applies a overlap check and returns EdgePairs which correspond to violation markers * - * The check will return true, where this region overlaps the polygons of the other + * The check will return true, where this region overlaps the polygons of the other * region by less than the specified threshold d. * * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". @@ -849,13 +784,13 @@ public: */ EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const { - return run_check (db::WidthRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + return EdgePairs (mp_delegate->overlap_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); } /** * @brief Applies a separation check and returns EdgePairs which correspond to violation markers * - * The check will return true, where this region is separated by polygons of the other + * The check will return true, where this region is separated by polygons of the other * region by less than the specified threshold d. * * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". @@ -866,13 +801,13 @@ public: */ EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const { - return run_check (db::SpaceRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + return EdgePairs (mp_delegate->separation_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); } /** * @brief Applies a inside check and returns EdgePairs which correspond to violation markers * - * The check will return true, where this region is inside by less than the threshold d inside the polygons of the other + * The check will return true, where this region is inside by less than the threshold d inside the polygons of the other * region. * * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". @@ -883,7 +818,7 @@ public: */ EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const { - return run_check (db::InsideRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + return EdgePairs (mp_delegate->inside_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection)); } /** @@ -891,46 +826,29 @@ public: * * Merged semantics applies. In merged semantics, only full, outer edges are delivered. */ - Edges edges () const; + Edges edges () const + { + return Edges (mp_delegate->edges (0)); + } /** - * @brief Returns an edge set containing all edges of the polygons in this region + * @brief Returns an edge set containing all edges of the polygons in this region * - * This version allows to specify a filter by which the edges are filtered before they are + * This version allows to specify a filter by which the edges are filtered before they are * returned. * * Merged semantics applies. In merged semantics, only full, outer edges are delivered. */ - template - Edges edges (F &filter) const + Edges edges (const EdgeFilterBase &filter) const { - Edges edges; - for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { - for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { - if (filter (*e)) { - edges.insert (*e); - } - } - } - return edges; + return mp_delegate->edges (&filter); } /** * @brief Transform the region */ template - Region &transform (const T &trans) - { - if (! trans.is_unity ()) { - ensure_valid_polygons (); - for (polygon_iterator_type p = m_polygons.get_layer ().begin (); p != m_polygons.get_layer ().end (); ++p) { - m_polygons.get_layer ().replace (p, p->transformed (trans)); - } - m_iter_trans = db::ICplxTrans (trans) * m_iter_trans; - m_bbox_valid = false; - } - return *this; - } + Region &transform (const T &trans); /** * @brief Returns the transformed region @@ -949,39 +867,40 @@ public: * The method returns single-point edge pairs for each vertex found off-grid. * The grid can be specified differently in x and y direction. */ - EdgePairs grid_check (db::Coord gx, db::Coord gy) const; + EdgePairs grid_check (db::Coord gx, db::Coord gy) const + { + return EdgePairs (mp_delegate->grid_check (gx, gy)); + } /** * @brief Performs an angle check * - * The method returns edge pairs for each connected edges having - * an angle between min and max (exclusive max, in degree) or + * The method returns edge pairs for each connected edges having + * an angle between min and max (exclusive max, in degree) or * not between min and max (if inverse is true). */ - EdgePairs angle_check (double min, double max, bool inverse) const; + EdgePairs angle_check (double min, double max, bool inverse) const + { + return EdgePairs (mp_delegate->angle_check (min, max, inverse)); + } /** * @brief Grid-snaps the region * * Snaps the vertices of the polygons to the specified grid. - * different grids can be specified int x and y direction. + * different grids can be specified int x and y direction. */ void snap (db::Coord gx, db::Coord gy); /** * @brief Returns the snapped region */ - Region snapped (db::Coord gx, db::Coord gy) const - { - Region d (*this); - d.snap (gx, gy); - return d; - } + Region snapped (db::Coord gx, db::Coord gy) const; /** * @brief Performs a check for "strange" polygons * - * This check will return a region with all self-overlapping or + * This check will return a region with all self-overlapping or * non-orientable parts of polygons. * * Naturally this method will ignore the merged_semantics setting. @@ -991,7 +910,10 @@ public: /** * @brief Swap with the other region */ - void swap (db::Region &other); + void swap (db::Region &other) + { + std::swap (other.mp_delegate, mp_delegate); + } /** * @brief Merge the region @@ -1000,19 +922,21 @@ public: * It returns a reference to this region. * An out-of-place merge version is "merged". */ - Region &merge (); + Region &merge () + { + set_delegate (mp_delegate->merged_in_place ()); + return *this; + } - /* + /** * @brief Returns the merged region * - * This is the out-of-place merge. It returns a new region but does not modify + * This is the out-of-place merge. It returns a new region but does not modify * the region it is called on. An in-place version is "merge". */ Region merged () const { - Region d (*this); - d.merge (); - return d; + return Region (mp_delegate->merged ()); } /** @@ -1030,7 +954,11 @@ public: * @param min_wrapcount See the description above * @return A reference to this region */ - Region &merge (bool min_coherence, unsigned int min_wc = 0); + Region &merge (bool min_coherence, unsigned int min_wc = 0) + { + set_delegate (mp_delegate->merged_in_place (min_coherence, min_wc)); + return *this; + } /** * @brief Returns the merged region with options @@ -1039,9 +967,7 @@ public: */ Region merged (bool min_coherence, unsigned int min_wc = 0) const { - Region d (*this); - d.merge (min_coherence, min_wc); - return d; + return Region (mp_delegate->merged (min_coherence, min_wc)); } /** @@ -1051,7 +977,7 @@ public: * region is merged if this is not the case already. * * The result is a set of polygons which may be overlapping, but are not self- - * intersecting. + * intersecting. * * Merged semantics applies. * @@ -1059,10 +985,7 @@ public: * @param mode The sizing mode (see EdgeProcessor) for a description of the sizing mode which controls the miter distance. * @return A reference to self */ - Region &size (coord_type d, unsigned int mode = 2) - { - return size (d, d, mode); - } + Region &size (coord_type d, unsigned int mode = 2); /** * @brief Anisotropic sizing @@ -1086,12 +1009,7 @@ public: * * Merged semantics applies. */ - Region sized (coord_type d, unsigned int mode = 2) const - { - Region r (*this); - r.size (d, mode); - return r; - } + Region sized (coord_type d, unsigned int mode = 2) const; /** * @brief Returns the sized region @@ -1101,21 +1019,14 @@ public: * * Merged semantics applies. */ - Region sized (coord_type dx, coord_type dy, unsigned int mode = 2) const - { - Region r (*this); - r.size (dx, dy, mode); - return r; - } + Region sized (coord_type dx, coord_type dy, unsigned int mode = 2) const; /** * @brief Boolean AND operator */ Region operator& (const Region &other) const { - Region d (*this); - d &= other; - return d; + return Region (mp_delegate->and_with (other)); } /** @@ -1124,16 +1035,18 @@ public: * This method does not necessarily merge the region. To ensure the region * is merged, call merge afterwards. */ - Region &operator&= (const Region &other); + Region &operator&= (const Region &other) + { + set_delegate (mp_delegate->and_with (other)); + return *this; + } /** * @brief Boolean NOT operator */ Region operator- (const Region &other) const { - Region d (*this); - d -= other; - return d; + return Region (mp_delegate->not_with (other)); } /** @@ -1142,16 +1055,18 @@ public: * This method does not necessarily merge the region. To ensure the region * is merged, call merge afterwards. */ - Region &operator-= (const Region &other); + Region &operator-= (const Region &other) + { + set_delegate (mp_delegate->not_with (other)); + return *this; + } /** * @brief Boolean XOR operator */ Region operator^ (const Region &other) const { - Region d (*this); - d ^= other; - return d; + return Region (mp_delegate->xor_with (other)); } /** @@ -1160,7 +1075,11 @@ public: * This method does not necessarily merge the region. To ensure the region * is merged, call merge afterwards. */ - Region &operator^= (const Region &other); + Region &operator^= (const Region &other) + { + set_delegate (mp_delegate->xor_with (other)); + return *this; + } /** * @brief Boolean OR operator @@ -1169,15 +1088,17 @@ public: */ Region operator| (const Region &other) const { - Region d (*this); - d |= other; - return d; + return Region (mp_delegate->or_with (other)); } /** * @brief In-place boolean OR operator */ - Region &operator|= (const Region &other); + Region &operator|= (const Region &other) + { + set_delegate (mp_delegate->or_with (other)); + return *this; + } /** * @brief Joining of regions @@ -1186,15 +1107,17 @@ public: */ Region operator+ (const Region &other) const { - Region d (*this); - d += other; - return d; + return Region (mp_delegate->add (other)); } /** * @brief In-place region joining */ - Region &operator+= (const Region &other); + Region &operator+= (const Region &other) + { + set_delegate (mp_delegate->add_in_place (other)); + return *this; + } /** * @brief Selects all polygons of this region which are completly outside polygons from the other region @@ -1203,7 +1126,7 @@ public: */ Region &select_outside (const Region &other) { - select_interacting_generic (other, 1, false, false); + set_delegate (mp_delegate->selected_outside (other)); return *this; } @@ -1214,7 +1137,7 @@ public: */ Region &select_not_outside (const Region &other) { - select_interacting_generic (other, 1, false, true); + set_delegate (mp_delegate->selected_not_outside (other)); return *this; } @@ -1227,7 +1150,7 @@ public: */ Region selected_outside (const Region &other) const { - return selected_interacting_generic (other, 1, false, false); + return Region (mp_delegate->selected_outside (other)); } /** @@ -1239,7 +1162,7 @@ public: */ Region selected_not_outside (const Region &other) const { - return selected_interacting_generic (other, 1, false, true); + return Region (mp_delegate->selected_not_outside (other)); } /** @@ -1249,7 +1172,7 @@ public: */ Region &select_inside (const Region &other) { - select_interacting_generic (other, -1, false, false); + set_delegate (mp_delegate->selected_inside (other)); return *this; } @@ -1260,7 +1183,7 @@ public: */ Region &select_not_inside (const Region &other) { - select_interacting_generic (other, -1, false, true); + set_delegate (mp_delegate->selected_not_inside (other)); return *this; } @@ -1273,7 +1196,7 @@ public: */ Region selected_inside (const Region &other) const { - return selected_interacting_generic (other, -1, true, false); + return Region (mp_delegate->selected_inside (other)); } /** @@ -1285,7 +1208,7 @@ public: */ Region selected_not_inside (const Region &other) const { - return selected_interacting_generic (other, -1, true, true); + return Region (mp_delegate->selected_not_inside (other)); } /** @@ -1295,7 +1218,7 @@ public: */ Region &select_interacting (const Region &other) { - select_interacting_generic (other, 0, true, false); + set_delegate (mp_delegate->selected_interacting (other)); return *this; } @@ -1306,7 +1229,7 @@ public: */ Region &select_not_interacting (const Region &other) { - select_interacting_generic (other, 0, true, true); + set_delegate (mp_delegate->selected_not_interacting (other)); return *this; } @@ -1319,7 +1242,7 @@ public: */ Region selected_interacting (const Region &other) const { - return selected_interacting_generic (other, 0, true, false); + return Region (mp_delegate->selected_interacting (other)); } /** @@ -1331,7 +1254,7 @@ public: */ Region selected_not_interacting (const Region &other) const { - return selected_interacting_generic (other, 0, true, true); + return Region (mp_delegate->selected_not_interacting (other)); } /** @@ -1341,7 +1264,7 @@ public: */ Region &select_interacting (const Edges &other) { - select_interacting_generic (other, false); + set_delegate (mp_delegate->selected_interacting (other)); return *this; } @@ -1352,7 +1275,7 @@ public: */ Region &select_not_interacting (const Edges &other) { - select_interacting_generic (other, true); + set_delegate (mp_delegate->selected_not_interacting (other)); return *this; } @@ -1365,7 +1288,7 @@ public: */ Region selected_interacting (const Edges &other) const { - return selected_interacting_generic (other, false); + return Region (mp_delegate->selected_interacting (other)); } /** @@ -1377,7 +1300,7 @@ public: */ Region selected_not_interacting (const Edges &other) const { - return selected_interacting_generic (other, true); + return Region (mp_delegate->selected_not_interacting (other)); } /** @@ -1387,7 +1310,7 @@ public: */ Region &select_overlapping (const Region &other) { - select_interacting_generic (other, 0, false, false); + set_delegate (mp_delegate->selected_overlapping (other)); return *this; } @@ -1398,7 +1321,7 @@ public: */ Region &select_not_overlapping (const Region &other) { - select_interacting_generic (other, 0, false, true); + set_delegate (mp_delegate->selected_not_overlapping (other)); return *this; } @@ -1411,7 +1334,7 @@ public: */ Region selected_overlapping (const Region &other) const { - return selected_interacting_generic (other, 0, false, false); + return Region (mp_delegate->selected_overlapping (other)); } /** @@ -1423,40 +1346,43 @@ public: */ Region selected_not_overlapping (const Region &other) const { - return selected_interacting_generic (other, 0, false, true); + return Region (mp_delegate->selected_not_overlapping (other)); } /** - * @brief Returns the holes + * @brief Returns the holes * - * This method returns the holes of the polygons. + * This method returns the holes of the polygons. * * Merged semantics applies. */ Region holes () const; /** - * @brief Returns the hulls + * @brief Returns the hulls * * This method returns the hulls of the polygons. It does not merge the - * polygons before the hulls are derived. + * polygons before the hulls are derived. * * Merged semantics applies. */ Region hulls () const; /** - * @brief Returns all polygons which are in the other region + * @brief Returns all polygons which are in the other region * - * This method will return all polygons which are part of another region. + * This method will return all polygons which are part of another region. * The match is done exactly. - * The "invert" flag can be used to invert the sense, i.e. with + * The "invert" flag can be used to invert the sense, i.e. with * "invert" set to true, this method will return all polygons not * in the other region. * * Merged semantics applies. */ - Region in (const Region &other, bool invert = false) const; + Region in (const Region &other, bool invert = false) const + { + return Region (mp_delegate->in (other, invert)); + } /** * @brief Round corners (in-place) @@ -1465,17 +1391,18 @@ public: * @param router The outer radius in DBU units * @param n The number of points to use per circle */ - void round_corners (double rinner, double router, unsigned int n) - { - Region r = rounded_corners (rinner, router, n); - swap (r); - } + void round_corners (double rinner, double router, unsigned int n); /** * @brief Returns a new region with rounded corners (out of place) */ Region rounded_corners (double rinner, double router, unsigned int n) const; + /** + * @brief Smoothes the region (in-place) + */ + void smooth (coord_type d); + /** * @brief Returns the smoothed region * @@ -1484,35 +1411,68 @@ public: Region smoothed (coord_type d) const; /** - * @brief Smoothes the region (in-place) - */ - void smooth (coord_type d) - { - Region r = smoothed (d); - swap (r); - } - - /** - * @brief Returns the nth polygon + * @brief Returns the nth polygon * - * This method will force the polygons to be inside the polygon vector. - * If that happens, the method may be costly and will invalidate any iterator. - * The iterator should be used whenever possible. + * This operation is available only for flat regions - i.e. such for which + * "has_valid_polygons" is true. */ const db::Polygon *nth (size_t n) const { - ensure_valid_polygons (); - return n < m_polygons.size () ? &m_polygons.get_layer ().begin () [n] : 0; + return mp_delegate->nth (n); + } + + /** + * @brief Forces flattening of the region + * + * This method will turn any region into a flat shape collection. + */ + db::Region &flatten () + { + flat_region (); + return *this; } /** * @brief Returns true, if the region has valid polygons stored within itself + * + * If the region has valid polygons, it is permissable to use the polygon's addresses + * from the iterator. Furthermore, the random access operator nth() is available. */ bool has_valid_polygons () const { - // Note: we take a copy of the iterator since the at_end method may - // validate the iterator which will make it refer to a specifc configuration. - return db::RecursiveShapeIterator (m_iter).at_end (); + return mp_delegate->has_valid_polygons (); + } + + /** + * @brief Returns an addressable delivery for polygons + * + * This object allows accessing the polygons by address, even if they + * are not delivered from a container. The magic is a heap object + * inside the delivery object. Hence, the deliver object must persist + * as long as the addresses are required. + */ + AddressablePolygonDelivery addressable_polygons () const + { + return AddressablePolygonDelivery (begin (), has_valid_polygons ()); + } + + /** + * @brief Returns true, if the region has valid merged polygons stored within itself + * + * If the region has valid merged polygons, it is permissable to use the polygon's addresses + * from the merged polygon iterator. Furthermore, the random access operator nth() is available. + */ + bool has_valid_merged_polygons () const + { + return mp_delegate->has_valid_merged_polygons (); + } + + /** + * @brief Returns an addressable delivery for merged polygons + */ + AddressablePolygonDelivery addressable_merged_polygons () const + { + return AddressablePolygonDelivery (begin_merged (), has_valid_merged_polygons ()); } /** @@ -1520,73 +1480,84 @@ public: * * This method is intended for users who know what they are doing */ - const db::RecursiveShapeIterator &iter () const - { - return m_iter; - } - - /** - * @brief Ensures the region has valid polygons - * - * This method is const since it has const semantics. - */ - void ensure_valid_polygons () const; - - /** - * @brief Ensures the region has valid merged polygons - * - * It will make sure that begin_merged will deliver an - * iterator to a polygon with a unique memory location. - */ - void ensure_valid_merged_polygons () const; + const db::RecursiveShapeIterator &iter () const; /** * @brief Equality */ - bool operator== (const db::Region &other) const; + bool operator== (const db::Region &other) const + { + return mp_delegate->equals (other); + } /** * @brief Inequality */ bool operator!= (const db::Region &other) const { - return !operator== (other); + return ! mp_delegate->equals (other); } /** * @brief Less operator */ - bool operator< (const db::Region &other) const; + bool operator< (const db::Region &other) const + { + return mp_delegate->less (other); + } + + /** + * @brief Inserts the region into the given layout, cell and layer + * If the region is a hierarchical region, the hierarchy is copied into the + * layout's hierarchy. + */ + void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const + { + return mp_delegate->insert_into (layout, into_cell, into_layer); + } + + /** + * @brief Delivers texts as dots (degenerated edges) + * + * "pat" is a text selector. If "as_pattern" is true, this pattern will be + * treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text.. + */ + db::Edges texts_as_dots (const std::string &pat, bool as_pattern) const; + + /** + * @brief Delivers texts as dots (degenerated edges) in a deep edge collection + * + * "pat" is a text selector. If "as_pattern" is true, this pattern will be + * treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text.. + */ + db::Edges texts_as_dots (const std::string &pat, bool as_pattern, db::DeepShapeStore &store) const; + + /** + * @brief Delivers texts as boxes + * + * "pat" is a text selector. If "as_pattern" is true, this pattern will be + * treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text. + * "enl" is the half size of the box (the box is 2*enl wide and 2*enl high). + */ + db::Region texts_as_boxes (const std::string &pat, bool as_pattern, db::Coord enl) const; + + /** + * @brief Delivers texts as boxes in a deep region + * + * "pat" is a text selector. If "as_pattern" is true, this pattern will be + * treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text. + * "enl" is the half size of the box (the box is 2*enl wide and 2*enl high). + */ + db::Region texts_as_boxes (const std::string &pat, bool as_pattern, db::Coord enl, db::DeepShapeStore &store) const; private: - typedef db::layer polygon_layer_type; - typedef polygon_layer_type::iterator polygon_iterator_type; + friend class Edges; + friend class EdgePairs; - bool m_is_merged; - bool m_merged_semantics; - bool m_strict_handling; - bool m_merge_min_coherence; - mutable db::Shapes m_polygons; - mutable db::Shapes m_merged_polygons; - mutable db::Box m_bbox; - mutable bool m_bbox_valid; - mutable bool m_merged_polygons_valid; - mutable db::RecursiveShapeIterator m_iter; - db::ICplxTrans m_iter_trans; - bool m_report_progress; - std::string m_progress_desc; + RegionDelegate *mp_delegate; - void init (); - void invalidate_cache (); - void set_valid_polygons (); - void ensure_bbox_valid () const; - void ensure_merged_polygons_valid () const; - EdgePairs run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - EdgePairs run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; - void select_interacting_generic (const Region &other, int mode, bool touching, bool inverse); - Region selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const; - Region selected_interacting_generic (const Edges &other, bool inverse) const; - void select_interacting_generic (const Edges &other, bool inverse); + void set_delegate (RegionDelegate *delegate, bool keep_attributes = true); + FlatRegion *flat_region (); }; /** diff --git a/src/db/db/dbRegionDelegate.cc b/src/db/db/dbRegionDelegate.cc new file mode 100644 index 000000000..0e6328ce7 --- /dev/null +++ b/src/db/db/dbRegionDelegate.cc @@ -0,0 +1,98 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbRegionDelegate.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- + +RegionDelegate::RegionDelegate () +{ + m_base_verbosity = 30; + m_report_progress = false; + m_merged_semantics = true; + m_strict_handling = false; + m_merge_min_coherence = false; +} + +RegionDelegate::RegionDelegate (const RegionDelegate &other) +{ + operator= (other); +} + +RegionDelegate & +RegionDelegate::operator= (const RegionDelegate &other) +{ + if (this != &other) { + m_base_verbosity = other.m_base_verbosity; + m_report_progress = other.m_report_progress; + m_merged_semantics = other.m_merged_semantics; + m_strict_handling = other.m_strict_handling; + m_merge_min_coherence = other.m_merge_min_coherence; + } + return *this; +} + +RegionDelegate::~RegionDelegate () +{ + // .. nothing yet .. +} + +void RegionDelegate::enable_progress (const std::string &progress_desc) +{ + m_report_progress = true; + m_progress_desc = progress_desc; +} + +void RegionDelegate::disable_progress () +{ + m_report_progress = false; +} + +void RegionDelegate::set_base_verbosity (int vb) +{ + m_base_verbosity = vb; +} + +void RegionDelegate::set_min_coherence (bool f) +{ + m_merge_min_coherence = f; +} + +void RegionDelegate::set_merged_semantics (bool f) +{ + if (f != m_merged_semantics) { + m_merged_semantics = f; + merged_semantics_changed (); + } +} + +void RegionDelegate::set_strict_handling (bool f) +{ + m_strict_handling = f; +} + +} + diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h new file mode 100644 index 000000000..f5eb945d7 --- /dev/null +++ b/src/db/db/dbRegionDelegate.h @@ -0,0 +1,335 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbRegionDelegate +#define HDR_dbRegionDelegate + +#include "dbCommon.h" + +#include "dbPolygon.h" +#include "dbEdges.h" +#include "dbEdgePairs.h" +#include "dbEdgePairRelations.h" +#include "tlUniqueId.h" + +#include + +namespace db { + +class RecursiveShapeIterator; +class EdgeFilterBase; +class EdgesDelegate; +class EdgePairsDelegate; + +/** + * @brief A base class for polygon filters + */ +class DB_PUBLIC PolygonFilterBase +{ +public: + /** + * @brief Constructor + */ + PolygonFilterBase () { } + + virtual ~PolygonFilterBase () { } + + /** + * @brief Filters the polygon + * If this method returns true, the polygon is kept. Otherwise it's discarded. + */ + virtual bool selected (const db::Polygon &polygon) const = 0; + + /** + * @brief Returns the transformation reducer for building cell variants + * This method may return 0. In this case, not cell variants are built. + */ + virtual const TransformationReducer *vars () const = 0; + + /** + * @brief Returns true, if the filter wants raw (not merged) input + */ + virtual bool requires_raw_input () const = 0; + + /** + * @brief Returns true, if the filter wants to build variants + * If not true, the filter accepts shape propagation as variant resolution. + */ + virtual bool wants_variants () const = 0; +}; + +/** + * @brief A template base class for polygon processors + * + * A polygon processor can turn a polygon into something else. + */ +template +class DB_PUBLIC polygon_processor +{ +public: + /** + * @brief Constructor + */ + polygon_processor () { } + + /** + * @brief Destructor + */ + virtual ~polygon_processor () { } + + /** + * @brief Performs the actual processing + * This method will take the input polygon from "polygon" and puts the results into "res". + * "res" can be empty - in this case, the polygon will be skipped. + */ + virtual void process (const db::Polygon &polygon, std::vector &res) const = 0; + + /** + * @brief Returns the transformation reducer for building cell variants + * This method may return 0. In this case, not cell variants are built. + */ + virtual const TransformationReducer *vars () const = 0; + + /** + * @brief Returns true, if the result of this operation can be regarded "merged" always. + */ + virtual bool result_is_merged () const = 0; + + /** + * @brief Returns true, if the result of this operation must not be merged. + * This feature can be used, if the result represents "degenerated" objects such + * as point-like edges. These must not be merged. Otherwise they disappear. + */ + virtual bool result_must_not_be_merged () const = 0; + + /** + * @brief Returns true, if the processor wants raw (not merged) input + */ + virtual bool requires_raw_input () const = 0; + + /** + * @brief Returns true, if the processor wants to build variants + * If not true, the processor accepts shape propagation as variant resolution. + */ + virtual bool wants_variants () const = 0; +}; + +/** + * @brief A polygon-to-polygon processor base class + */ +class DB_PUBLIC PolygonProcessorBase + : public polygon_processor +{ + // .. nothing yet .. +}; + +/** + * @brief A polygon-to-edge processor base class + */ +class DB_PUBLIC PolygonToEdgeProcessorBase + : public polygon_processor +{ + // .. nothing yet .. +}; + +/** + * @brief A polygon-to-edge pair processor base class + */ +class DB_PUBLIC PolygonToEdgePairProcessorBase + : public polygon_processor +{ + // .. nothing yet .. +}; + +/** + * @brief The region iterator delegate + */ +class DB_PUBLIC RegionIteratorDelegate +{ +public: + RegionIteratorDelegate () { } + virtual ~RegionIteratorDelegate () { } + + typedef db::Polygon value_type; + + virtual bool at_end () const = 0; + virtual void increment () = 0; + virtual const value_type *get () const = 0; + virtual RegionIteratorDelegate *clone () const = 0; +}; + +/** + * @brief The delegate for the actual region implementation + */ +class DB_PUBLIC RegionDelegate + : public tl::UniqueId +{ +public: + typedef db::Coord coord_type; + typedef db::coord_traits coord_traits; + typedef db::Polygon polygon_type; + typedef db::Vector vector_type; + typedef db::Point point_type; + typedef db::Box box_type; + typedef coord_traits::distance_type distance_type; + typedef coord_traits::perimeter_type perimeter_type; + typedef coord_traits::area_type area_type; + + RegionDelegate (); + virtual ~RegionDelegate (); + + RegionDelegate (const RegionDelegate &other); + RegionDelegate &operator= (const RegionDelegate &other); + + virtual RegionDelegate *clone () const = 0; + + void set_base_verbosity (int vb); + int base_verbosity () const + { + return m_base_verbosity; + } + + void enable_progress (const std::string &progress_desc); + void disable_progress (); + + void set_min_coherence (bool f); + bool min_coherence () const + { + return m_merge_min_coherence; + } + + void set_merged_semantics (bool f); + bool merged_semantics () const + { + return m_merged_semantics; + } + + void set_strict_handling (bool f); + bool strict_handling () const + { + return m_strict_handling; + } + + virtual std::string to_string (size_t nmax) const = 0; + + virtual RegionIteratorDelegate *begin () const = 0; + virtual RegionIteratorDelegate *begin_merged () const = 0; + + virtual std::pair begin_iter () const = 0; + virtual std::pair begin_merged_iter () const = 0; + + virtual bool empty () const = 0; + virtual bool is_box () const = 0; + virtual bool is_merged () const = 0; + virtual size_t size () const = 0; + + virtual area_type area (const db::Box &box) const = 0; + virtual perimeter_type perimeter (const db::Box &box) const = 0; + virtual Box bbox () const = 0; + + virtual EdgePairsDelegate *width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairsDelegate *space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairsDelegate *isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairsDelegate *notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairsDelegate *enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairsDelegate *overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairsDelegate *separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairsDelegate *inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairsDelegate *grid_check (db::Coord gx, db::Coord gy) const = 0; + virtual EdgePairsDelegate *angle_check (double min, double max, bool inverse) const = 0; + + virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy) = 0; + virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy) = 0; + + virtual EdgesDelegate *edges (const EdgeFilterBase *filter) const = 0; + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) = 0; + virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const = 0; + virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter) = 0; + virtual RegionDelegate *processed (const PolygonProcessorBase &filter) const = 0; + virtual EdgesDelegate *processed_to_edges (const PolygonToEdgeProcessorBase &filter) const = 0; + virtual EdgePairsDelegate *processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &filter) const = 0; + + virtual RegionDelegate *merged_in_place () = 0; + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc) = 0; + virtual RegionDelegate *merged () const = 0; + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const = 0; + + virtual RegionDelegate *sized (coord_type d, unsigned int mode) const = 0; + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const = 0; + + virtual RegionDelegate *and_with (const Region &other) const = 0; + virtual RegionDelegate *not_with (const Region &other) const = 0; + virtual RegionDelegate *xor_with (const Region &other) const = 0; + virtual RegionDelegate *or_with (const Region &other) const = 0; + virtual RegionDelegate *add_in_place (const Region &other) = 0; + virtual RegionDelegate *add (const Region &other) const = 0; + + virtual RegionDelegate *selected_outside (const Region &other) const = 0; + virtual RegionDelegate *selected_not_outside (const Region &other) const = 0; + virtual RegionDelegate *selected_inside (const Region &other) const = 0; + virtual RegionDelegate *selected_not_inside (const Region &other) const = 0; + virtual RegionDelegate *selected_interacting (const Region &other) const = 0; + virtual RegionDelegate *selected_not_interacting (const Region &other) const = 0; + virtual RegionDelegate *selected_interacting (const Edges &other) const = 0; + virtual RegionDelegate *selected_not_interacting (const Edges &other) const = 0; + virtual RegionDelegate *selected_overlapping (const Region &other) const = 0; + virtual RegionDelegate *selected_not_overlapping (const Region &other) const = 0; + virtual RegionDelegate *in (const Region &other, bool invert) const = 0; + + virtual const db::Polygon *nth (size_t n) const = 0; + virtual bool has_valid_polygons () const = 0; + virtual bool has_valid_merged_polygons () const = 0; + + virtual const db::RecursiveShapeIterator *iter () const = 0; + + virtual bool equals (const Region &other) const = 0; + virtual bool less (const Region &other) const = 0; + + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const = 0; + +protected: + const std::string &progress_desc () const + { + return m_progress_desc; + } + + bool report_progress () const + { + return m_report_progress; + } + + virtual void merged_semantics_changed () { } + +private: + bool m_merged_semantics; + bool m_strict_handling; + bool m_merge_min_coherence; + bool m_report_progress; + std::string m_progress_desc; + int m_base_verbosity; +}; + +} + +#endif + diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc new file mode 100644 index 000000000..bc3e38458 --- /dev/null +++ b/src/db/db/dbRegionProcessors.cc @@ -0,0 +1,206 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbRegionProcessors.h" +#include "dbPolygon.h" +#include "dbPolygonGenerators.h" + +namespace db +{ + +// ----------------------------------------------------------------------------------- +// CornerDetectorCore implementation + +CornerDetectorCore::CornerDetectorCore (double angle_start, double angle_end) +{ + m_t_start = db::CplxTrans(1.0, angle_start, false, db::DVector ()); + m_t_end = db::CplxTrans(1.0, angle_end, false, db::DVector ()); + + m_big_angle = (angle_end - angle_start + db::epsilon) > 180.0; + m_all = (angle_end - angle_start - db::epsilon) > 360.0; +} + +void CornerDetectorCore::detect_corners (const db::Polygon &poly, const CornerPointDelivery &delivery) const +{ + size_t n = poly.holes () + 1; + for (size_t i = 0; i < n; ++i) { + + const db::Polygon::contour_type &ctr = poly.contour (int (i)); + size_t nn = ctr.size (); + if (nn > 2) { + + db::Point pp = ctr [nn - 2]; + db::Point pt = ctr [nn - 1]; + for (size_t j = 0; j < nn; ++j) { + + db::Point pn = ctr [j]; + + if (m_all) { + delivery.make_point (pt); + } else { + + db::Vector vin (pt - pp); + db::DVector vout (pn - pt); + + db::DVector v1 = m_t_start * vin; + db::DVector v2 = m_t_end * vin; + + bool vp1 = db::vprod_sign (v1, vout) >= 0; + bool vp2 = db::vprod_sign (v2, vout) <= 0; + + if (m_big_angle && (vp1 || vp2)) { + delivery.make_point (pt); + } else if (! m_big_angle && vp1 && vp2) { + if (db::sprod_sign (v1, vout) > 0 && db::sprod_sign (v2, vout) > 0) { + delivery.make_point (pt); + } + } + + } + + pp = pt; + pt = pn; + + } + + } + + } +} + +// ----------------------------------------------------------------------------------- +// Extents implementation + +void Extents::process (const db::Polygon &poly, std::vector &result) const +{ + db::Box box = poly.box ().enlarged (db::Vector (m_dx, m_dy)); + if (! box.empty ()) { + result.push_back (db::Polygon (box)); + } +} + +const TransformationReducer *Extents::vars () const +{ + if (m_dx == 0 && m_dy == 0) { + return 0; + } else if (m_dx == m_dy) { + return & m_isotropic_reducer; + } else { + return & m_anisotropic_reducer; + } +} + +// ----------------------------------------------------------------------------------- +// RelativeExtents implementation + +void RelativeExtents::process (const db::Polygon &poly, std::vector &result) const +{ + db::Box b = poly.box (); + db::Point p1 (b.left () + db::coord_traits::rounded (m_fx1 * b.width ()), + b.bottom () + db::coord_traits::rounded (m_fy1 * b.height ())); + db::Point p2 (b.left () + db::coord_traits::rounded (m_fx2 * b.width ()), + b.bottom () + db::coord_traits::rounded (m_fy2 * b.height ())); + db::Box box = db::Box (p1, p2).enlarged (db::Vector (m_dx, m_dy)); + if (! box.empty ()) { + result.push_back (db::Polygon (box)); + } +} + +const TransformationReducer *RelativeExtents::vars () const +{ + if (m_dx == 0 && m_dy == 0 && fabs (m_fx1) < db::epsilon && fabs (m_fy1) < db::epsilon && fabs (m_fx2) < db::epsilon && fabs (m_fy2) < db::epsilon) { + return 0; + } else if (m_dx == m_dy && fabs (m_fx1 - m_fy1) < db::epsilon && fabs (1.0 - (m_fx1 + m_fx2)) < db::epsilon && fabs (m_fx2 - m_fy2) < db::epsilon && fabs (1.0 - (m_fy1 + m_fy2)) < db::epsilon) { + return & m_isotropic_reducer; + } else { + return & m_anisotropic_reducer; + } +} + +// ----------------------------------------------------------------------------------- +// RelativeExtentsAsEdges implementation + +void RelativeExtentsAsEdges::process (const db::Polygon &poly, std::vector &result) const +{ + db::Box b = poly.box (); + db::Point p1 (b.left () + db::coord_traits::rounded (m_fx1 * b.width ()), + b.bottom () + db::coord_traits::rounded (m_fy1 * b.height ())); + db::Point p2 (b.left () + db::coord_traits::rounded (m_fx2 * b.width ()), + b.bottom () + db::coord_traits::rounded (m_fy2 * b.height ())); + result.push_back (db::Edge (p1, p2)); +} + +const TransformationReducer *RelativeExtentsAsEdges::vars () const +{ + return & m_anisotropic_reducer; +} + +bool RelativeExtentsAsEdges::result_must_not_be_merged () const +{ + // don't merge if the results will just be points + return (fabs (m_fx1 - m_fx2) < db::epsilon && fabs (m_fy1 - m_fy2) < db::epsilon); +} + +// ----------------------------------------------------------------------------------- +// ConvexDecomposition implementation + +void ConvexDecomposition::process (const db::Polygon &poly, std::vector &result) const +{ + db::SimplePolygonContainer sp; + db::decompose_convex (poly, m_mode, sp); + for (std::vector ::const_iterator i = sp.polygons ().begin (); i != sp.polygons ().end (); ++i) { + result.push_back (db::simple_polygon_to_polygon (*i)); + } +} + +// ----------------------------------------------------------------------------------- +// TrapezoidDecomposition implementation + +void TrapezoidDecomposition::process (const db::Polygon &poly, std::vector &result) const +{ + db::SimplePolygonContainer sp; + db::decompose_trapezoids (poly, m_mode, sp); + for (std::vector ::const_iterator i = sp.polygons ().begin (); i != sp.polygons ().end (); ++i) { + result.push_back (db::simple_polygon_to_polygon (*i)); + } +} + +// ----------------------------------------------------------------------------------- +// PolygonBreaker implementation + +void PolygonBreaker::process (const db::Polygon &poly, std::vector &result) const +{ + if ((m_max_vertex_count > 0 && poly.vertices () > m_max_vertex_count) || + (m_max_area_ratio > 0 && poly.area_ratio () > m_max_area_ratio)) { + + std::vector split_polygons; + db::split_polygon (poly, split_polygons); + for (std::vector::const_iterator p = split_polygons.begin (); p != split_polygons.end (); ++p) { + process (*p, result); + } + + } else { + result.push_back (poly); + } +} + +} diff --git a/src/db/db/dbRegionProcessors.h b/src/db/db/dbRegionProcessors.h new file mode 100644 index 000000000..05172a7f2 --- /dev/null +++ b/src/db/db/dbRegionProcessors.h @@ -0,0 +1,364 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbRegionProcessors +#define HDR_dbRegionProcessors + +#include "dbCommon.h" +#include "dbRegionDelegate.h" +#include "dbPolygonTools.h" + +namespace db +{ + +// ----------------------------------------------------------------------------------- +// Corner detection + +/** + * @brief An interface to accept corners + */ +class DB_PUBLIC CornerPointDelivery +{ +public: + virtual void make_point (const db::Point &pt) const = 0; +}; + +/** + * @brief An interface to accept corners and turns them into rectangles with 2*dim x 2*dim + */ +class DB_PUBLIC CornerRectDelivery + : public CornerPointDelivery +{ +public: + CornerRectDelivery (db::Coord dim, std::vector &result) + : m_d (dim, dim), mp_result (&result) + { } + + virtual void make_point (const db::Point &pt) const + { + mp_result->push_back (db::Polygon (db::Box (pt - m_d, pt + m_d))); + } + +private: + db::Vector m_d; + std::vector *mp_result; +}; + +/** + * @brief An interface to accept corners and turns them into degenerated edges (dots) + */ +class DB_PUBLIC CornerDotDelivery + : public CornerPointDelivery +{ +public: + CornerDotDelivery (std::vector &result) + : mp_result (&result) + { } + + virtual void make_point (const db::Point &pt) const + { + mp_result->push_back (db::Edge (pt, pt)); + } + +private: + db::Vector m_d; + std::vector *mp_result; +}; + +/** + * @brief A helper class implementing the core corner detection algorithm + */ +class DB_PUBLIC CornerDetectorCore +{ +public: + CornerDetectorCore (double angle_start, double angle_end); + virtual ~CornerDetectorCore () { } + + void detect_corners (const db::Polygon &poly, const CornerPointDelivery &delivery) const; + +private: + db::CplxTrans m_t_start, m_t_end; + bool m_big_angle, m_all; +}; + +/** + * @brief A corner detector delivering small retangles (2*dim x 2*dim) per detected corner + */ +class DB_PUBLIC CornersAsRectangles + : public db::PolygonProcessorBase, private CornerDetectorCore +{ +public: + CornersAsRectangles (double angle_start, double angle_end, db::Coord dim = 1) + : CornerDetectorCore (angle_start, angle_end), m_dim (dim) + { + // .. nothing yet .. + } + + void process (const db::Polygon &poly, std::vector &result) const + { + detect_corners (poly, CornerRectDelivery (m_dim, result)); + } + + virtual const TransformationReducer *vars () const { return &m_vars; } + virtual bool result_is_merged () const { return false; } // overlaps may happen + virtual bool result_must_not_be_merged () const { return false; } + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return false; } + +private: + db::Coord m_dim; + db::MagnificationReducer m_vars; +}; + +/** + * @brief A corner detector delivering degenerated edges (dots) for the corners + */ +class DB_PUBLIC CornersAsDots + : public db::PolygonToEdgeProcessorBase, private CornerDetectorCore +{ +public: + CornersAsDots (double angle_start, double angle_end) + : CornerDetectorCore (angle_start, angle_end) + { + // .. nothing yet .. + } + + void process (const db::Polygon &poly, std::vector &result) const + { + detect_corners (poly, CornerDotDelivery (result)); + } + + virtual const TransformationReducer *vars () const { return 0; } + virtual bool result_is_merged () const { return false; } + virtual bool result_must_not_be_merged () const { return true; } // to preserve dots + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return false; } +}; + +// ----------------------------------------------------------------------------------- +// Extents + +/** + * @brief A processor delivering the extents (bounding box) of the merged polygons + * This processor allows over- or undersizing of the resulting box by a given amount + */ +class DB_PUBLIC Extents + : public db::PolygonProcessorBase +{ +public: + Extents (db::Coord dx, db::Coord dy) + : m_dx (dx), m_dy (dy) + { + // .. nothing yet .. + } + + void process (const db::Polygon &poly, std::vector &result) const; + + virtual const TransformationReducer *vars () const; + virtual bool result_is_merged () const { return false; } + virtual bool result_must_not_be_merged () const { return false; } + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return true; } + +private: + db::Coord m_dx, m_dy; + db::MagnificationAndOrientationReducer m_anisotropic_reducer; + db::MagnificationReducer m_isotropic_reducer; +}; + +/** + * @brief A processor delivering the relative extents (bounding box) of the merged polygons + * This processor allows over- or undersizing of the resulting box by a given amount and delivery + * of a box relative to the original box. + */ +class DB_PUBLIC RelativeExtents + : public db::PolygonProcessorBase +{ +public: + RelativeExtents (double fx1, double fy1, double fx2, double fy2, db::Coord dx, db::Coord dy) + : m_fx1 (fx1), m_fy1 (fy1), m_fx2 (fx2), m_fy2 (fy2), m_dx (dx), m_dy (dy) + { + // .. nothing yet .. + } + + void process (const db::Polygon &poly, std::vector &result) const; + + virtual const TransformationReducer *vars () const; + virtual bool result_is_merged () const { return false; } + virtual bool result_must_not_be_merged () const { return false; } + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return false; } // variants are too common, so don't do this + +private: + double m_fx1, m_fy1, m_fx2, m_fy2; + db::Coord m_dx, m_dy; + db::MagnificationAndOrientationReducer m_anisotropic_reducer; + db::MagnificationReducer m_isotropic_reducer; +}; + +/** + * @brief A processor delivers one edge per merged polygon + * The edge runs from the relative coordinate fx1, fy1 (0: left/bottom, 1: right/top) to + * fx2, fy2. + * This processor allows over- or undersizing of the resulting box by a given amount and delivery + * of a box relative to the original box. + */ +class DB_PUBLIC RelativeExtentsAsEdges + : public db::PolygonToEdgeProcessorBase +{ +public: + RelativeExtentsAsEdges (double fx1, double fy1, double fx2, double fy2) + : m_fx1 (fx1), m_fy1 (fy1), m_fx2 (fx2), m_fy2 (fy2) + { + // .. nothing yet .. + } + + void process (const db::Polygon &poly, std::vector &result) const; + + virtual const TransformationReducer *vars () const; + virtual bool result_is_merged () const { return false; } + virtual bool result_must_not_be_merged () const; + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return false; } // variants are too common, so don't do this + +private: + double m_fx1, m_fy1, m_fx2, m_fy2; + db::MagnificationAndOrientationReducer m_anisotropic_reducer; +}; + +/** + * @brief A decomposition processor to deliver convex-only polygons + */ +class DB_PUBLIC ConvexDecomposition + : public db::PolygonProcessorBase +{ +public: + ConvexDecomposition (db::PreferredOrientation mode) + : m_mode (mode) + { + // .. nothing yet .. + } + + void process (const db::Polygon &poly, std::vector &result) const; + + virtual const TransformationReducer *vars () const { return &m_vars; } + virtual bool result_is_merged () const { return false; } + virtual bool result_must_not_be_merged () const { return true; } // would spoil the decomposition otherwise + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return true; } + +private: + db::PreferredOrientation m_mode; + db::OrientationReducer m_vars; +}; + +/** + * @brief A decomposition processor to deliver trapezoids + */ +class DB_PUBLIC TrapezoidDecomposition + : public db::PolygonProcessorBase +{ +public: + TrapezoidDecomposition (db::TrapezoidDecompositionMode mode) + : m_mode (mode) + { + // .. nothing yet .. + } + + void process (const db::Polygon &poly, std::vector &result) const; + + virtual const TransformationReducer *vars () const { return &m_vars; } + virtual bool result_is_merged () const { return false; } + virtual bool result_must_not_be_merged () const { return true; } // would spoil the decomposition otherwise + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return true; } + +private: + db::TrapezoidDecompositionMode m_mode; + db::OrientationReducer m_vars; +}; + +/** + * @brief A polygon breaker processor + * This processor reduces polygons with more than max_vertex_count vertices and + * an bbox-to-polygon area ratio bigger than max_area_ratio. + * A zero value for these parameters means "don't care". + */ +class DB_PUBLIC PolygonBreaker + : public db::PolygonProcessorBase +{ +public: + PolygonBreaker (size_t max_vertex_count, double max_area_ratio) + : m_max_vertex_count (max_vertex_count), m_max_area_ratio (max_area_ratio) + { + // .. nothing yet .. + } + + void process (const db::Polygon &poly, std::vector &result) const; + + virtual const TransformationReducer *vars () const { return 0; } + virtual bool result_is_merged () const { return false; } + virtual bool result_must_not_be_merged () const { return true; } // would spoil the decomposition otherwise + virtual bool requires_raw_input () const { return true; } // acts on original shapes + virtual bool wants_variants () const { return false; } + +private: + size_t m_max_vertex_count; + double m_max_area_ratio; +}; + +/** + * @brief Computes the Minkowsky sum between the polygons and the given object + * The object can be Edge, Polygon, Box and std::vector + */ +template +class DB_PUBLIC_TEMPLATE minkowsky_sum_computation + : public db::PolygonProcessorBase +{ +public: + minkowsky_sum_computation (const Object &q) + : m_q (q) + { + // .. nothing yet .. + } + + void process (const db::Polygon &poly, std::vector &result) const + { + result.push_back (db::minkowsky_sum (poly, m_q, false)); + } + + // TODO: could be less if the object is symmetric + virtual const TransformationReducer *vars () const { return &m_vars; } + virtual bool result_is_merged () const { return false; } + virtual bool result_must_not_be_merged () const { return false; } + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return true; } + +private: + Object m_q; + db::MagnificationAndOrientationReducer m_vars; +}; + +} + +#endif diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc new file mode 100644 index 000000000..eb1114e1a --- /dev/null +++ b/src/db/db/dbRegionUtils.cc @@ -0,0 +1,328 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbRegionUtils.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------- +// Edge2EdgeCheckBase implementation + +Edge2EdgeCheckBase::Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers) + : mp_check (&check), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons), + m_pass (0) +{ + m_distance = check.distance (); +} + +bool +Edge2EdgeCheckBase::prepare_next_pass () +{ + ++m_pass; + + if (m_pass == 1) { + + if (! m_ep.empty ()) { + m_ep_discarded.resize (m_ep.size (), false); + return true; + } + + } else if (m_pass == 2) { + + std::vector::const_iterator d = m_ep_discarded.begin (); + std::vector::const_iterator ep = m_ep.begin (); + while (ep != m_ep.end ()) { + tl_assert (d != m_ep_discarded.end ()); + if (! *d) { + put (*ep); + } + ++d; + ++ep; + } + + } + + return false; +} + +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 ((! m_different_polygons || p1 != p2) && (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0)) { + + // 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)); + + db::EdgePair ep; + if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { + + // found a violation: store inside the local buffer for now. In the second + // pass we will eliminate those which are shielded completely. + size_t n = m_ep.size (); + m_ep.push_back (ep); + m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n)); + m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n)); + + } + + } + + } else { + + // 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 n1, n2; + + for (unsigned int p = 0; p < 2; ++p) { + + std::pair k (*o1, p1); + for (std::multimap, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) { + n1.push_back (i->second); + } + + 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 nn; + std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn)); + + for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { + if (! m_ep_discarded [*i]) { + db::EdgePair ep = m_ep [*i].normalized (); + if (db::Edge (ep.first ().p1 (), ep.second ().p2 ()).intersect (*o2) && + db::Edge (ep.second ().p1 (), ep.first ().p2 ()).intersect (*o2)) { + m_ep_discarded [*i] = true; + } + } + } + + std::swap (o1, o2); + std::swap (p1, p2); + n1.swap (n2); + + } + + } + +} + +/** + * @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 + +Poly2PolyCheckBase::Poly2PolyCheckBase (Edge2EdgeCheckBase &output) + : mp_output (& output) +{ + // .. nothing yet .. +} + +void +Poly2PolyCheckBase::finish (const db::Polygon *o, size_t p) +{ + enter (*o, p); +} + +void +Poly2PolyCheckBase::enter (const db::Polygon &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 (o.vertices ()); + + m_edges.clear (); + m_edges.reserve (o.vertices ()); + + for (db::Polygon::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p); + } + + tl_assert (m_edges.size () == o.vertices ()); + + m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); + + } +} + +void +Poly2PolyCheckBase::add (const db::Polygon *o1, size_t p1, const db::Polygon *o2, size_t p2) +{ + enter (*o1, p1, *o2, p2); +} + +void +Poly2PolyCheckBase::enter (const db::Polygon &o1, size_t p1, const db::Polygon &o2, size_t p2) +{ + if ((! mp_output->different_polygons () || p1 != p2) && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) { + + m_scanner.clear (); + m_scanner.reserve (o1.vertices () + o2.vertices ()); + + m_edges.clear (); + m_edges.reserve (o1.vertices () + o2.vertices ()); + + for (db::Polygon::polygon_edge_iterator e = o1.begin_edge (); ! e.at_end (); ++e) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p1); + } + + for (db::Polygon::polygon_edge_iterator e = o2.begin_edge (); ! e.at_end (); ++e) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p2); + } + + tl_assert (m_edges.size () == o1.vertices () + o2.vertices ()); + + // 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 ()); + + mp_output->set_different_polygons (no_intra); + + } +} + +// ------------------------------------------------------------------------------------- +// RegionToEdgeInteractionFilterBase implementation + +RegionToEdgeInteractionFilterBase::RegionToEdgeInteractionFilterBase (bool inverse) + : m_inverse (inverse) +{ + // .. nothing yet .. +} + +void +RegionToEdgeInteractionFilterBase::preset (const db::Polygon *poly) +{ + m_seen.insert (poly); +} + +void +RegionToEdgeInteractionFilterBase::add (const db::Polygon *p, size_t, const db::Edge *e, size_t) +{ + if ((m_seen.find (p) == 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 (db::Polygon::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 (p); + } else { + m_seen.insert (p); + put (*p); + } + } + + } +} + +void +RegionToEdgeInteractionFilterBase::fill_output () +{ + for (std::set::const_iterator p = m_seen.begin (); p != m_seen.end (); ++p) { + put (**p); + } +} + +} + diff --git a/src/db/db/dbRegionUtils.h b/src/db/db/dbRegionUtils.h new file mode 100644 index 000000000..90f437b35 --- /dev/null +++ b/src/db/db/dbRegionUtils.h @@ -0,0 +1,529 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbRegionUtils +#define HDR_dbRegionUtils + +#include "dbCommon.h" +#include "dbRegion.h" +#include "dbCellVariants.h" +#include "dbBoxScanner.h" + +namespace db { + +/** + * @brief A perimeter filter for use with Region::filter or Region::filtered + * + * This filter has two parameters: pmin and pmax. + * It will filter all polygons for which the perimeter is >= pmin and < pmax. + * There is an "invert" flag which allows to select all polygons not + * matching the criterion. + */ + +struct DB_PUBLIC RegionPerimeterFilter + : public PolygonFilterBase +{ + typedef db::coord_traits::perimeter_type perimeter_type; + + /** + * @brief Constructor + * + * @param amin The min perimeter (only polygons above this value are filtered) + * @param amax The maximum perimeter (only polygons with a perimeter below this value are filtered) + * @param inverse If set to true, only polygons not matching this criterion will be filtered + */ + RegionPerimeterFilter (perimeter_type pmin, perimeter_type pmax, bool inverse) + : m_pmin (pmin), m_pmax (pmax), m_inverse (inverse) + { + // .. nothing yet .. + } + + /** + * @brief Returns true if the polygon's perimeter matches the criterion + */ + virtual bool selected (const db::Polygon &poly) const + { + perimeter_type p = 0; + for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end () && p < m_pmax; ++e) { + p += (*e).length (); + } + + if (! m_inverse) { + return p >= m_pmin && p < m_pmax; + } else { + return ! (p >= m_pmin && p < m_pmax); + } + } + + /** + * @brief This filter is isotropic + */ + virtual const TransformationReducer *vars () const + { + return &m_vars; + } + + /** + * @brief This filter prefers producing variants + */ + virtual bool wants_variants () const { return true; } + + /** + * @brief This filter wants merged input + */ + virtual bool requires_raw_input () const { return false; } + +private: + perimeter_type m_pmin, m_pmax; + bool m_inverse; + db::MagnificationReducer m_vars; +}; + +/** + * @brief An area filter for use with Region::filter or Region::filtered + * + * This filter has two parameters: amin and amax. + * It will filter all polygons for which the area is >= amin and < amax. + * There is an "invert" flag which allows to select all polygons not + * matching the criterion. + */ + +struct DB_PUBLIC RegionAreaFilter + : public PolygonFilterBase +{ + typedef db::Polygon::area_type area_type; + + /** + * @brief Constructor + * + * @param amin The min area (only polygons above this value are filtered) + * @param amax The maximum area (only polygons with an area below this value are filtered) + * @param inverse If set to true, only polygons not matching this criterion will be filtered + */ + RegionAreaFilter (area_type amin, area_type amax, bool inverse) + : m_amin (amin), m_amax (amax), m_inverse (inverse) + { + // .. nothing yet .. + } + + /** + * @brief Returns true if the polygon's area matches the criterion + */ + virtual bool selected (const db::Polygon &poly) const + { + area_type a = poly.area (); + if (! m_inverse) { + return a >= m_amin && a < m_amax; + } else { + return ! (a >= m_amin && a < m_amax); + } + } + + /** + * @brief This filter is isotropic + */ + virtual const TransformationReducer *vars () const + { + return &m_vars; + } + + /** + * @brief This filter prefers producing variants + */ + virtual bool wants_variants () const { return true; } + + /** + * @brief This filter wants merged input + */ + virtual bool requires_raw_input () const { return false; } + +private: + area_type m_amin, m_amax; + bool m_inverse; + db::MagnificationReducer m_vars; +}; + +/** + * @brief A filter for rectilinear polygons + * + * This filter will select all polygons which are rectilinear. + */ + +struct DB_PUBLIC RectilinearFilter + : public PolygonFilterBase +{ + /** + * @brief Constructor + * @param inverse If set to true, only polygons not matching this criterion will be filtered + */ + RectilinearFilter (bool inverse) + : m_inverse (inverse) + { + // .. nothing yet .. + } + + /** + * @brief Returns true if the polygon's area matches the criterion + */ + virtual bool selected (const db::Polygon &poly) const + { + return poly.is_rectilinear () != m_inverse; + } + + /** + * @brief This filter does not need variants + */ + virtual const TransformationReducer *vars () const + { + return 0; + } + + /** + * @brief This filter prefers producing variants + */ + virtual bool wants_variants () const { return true; } + + /** + * @brief This filter wants merged input + */ + virtual bool requires_raw_input () const { return false; } + +private: + bool m_inverse; +}; + +/** + * @brief A rectangle filter + * + * This filter will select all polygons which are rectangles. + */ + +struct DB_PUBLIC RectangleFilter + : public PolygonFilterBase +{ + /** + * @brief Constructor + * @param inverse If set to true, only polygons not matching this criterion will be filtered + */ + RectangleFilter (bool inverse) + : m_inverse (inverse) + { + // .. nothing yet .. + } + + /** + * @brief Returns true if the polygon's area matches the criterion + */ + virtual bool selected (const db::Polygon &poly) const + { + return poly.is_box () != m_inverse; + } + + /** + * @brief This filter does not need variants + */ + virtual const TransformationReducer *vars () const + { + return 0; + } + + /** + * @brief This filter prefers producing variants + */ + virtual bool wants_variants () const { return true; } + + /** + * @brief This filter wants merged input + */ + virtual bool requires_raw_input () const { return false; } + +private: + bool m_inverse; +}; + +/** + * @brief A bounding box filter for use with Region::filter or Region::filtered + * + * This filter has two parameters: vmin and vmax. + * It will filter all polygons for which the selected bounding box parameter is >= vmin and < vmax. + * There is an "invert" flag which allows to select all polygons not + * matching the criterion. + * + * For bounding box parameters the following choices are available: + * - (BoxWidth) width + * - (BoxHeight) height + * - (BoxMaxDim) maximum of width and height + * - (BoxMinDim) minimum of width and height + * - (BoxAverageDim) average of width and height + */ + +struct DB_PUBLIC RegionBBoxFilter + : public PolygonFilterBase +{ + typedef db::Box::distance_type value_type; + + /** + * @brief The parameters available + */ + enum parameter_type { + BoxWidth, + BoxHeight, + BoxMaxDim, + BoxMinDim, + BoxAverageDim + }; + + /** + * @brief Constructor + * + * @param vmin The min value (only polygons with bounding box parameters above this value are filtered) + * @param vmax The max value (only polygons with bounding box parameters below this value are filtered) + * @param inverse If set to true, only polygons not matching this criterion will be filtered + */ + RegionBBoxFilter (value_type vmin, value_type vmax, bool inverse, parameter_type parameter) + : m_vmin (vmin), m_vmax (vmax), m_inverse (inverse), m_parameter (parameter) + { + // .. nothing yet .. + } + + /** + * @brief Returns true if the polygon's area matches the criterion + */ + virtual bool selected (const db::Polygon &poly) const + { + value_type v = 0; + db::Box box = poly.box (); + if (m_parameter == BoxWidth) { + v = box.width (); + } else if (m_parameter == BoxHeight) { + v = box.height (); + } else if (m_parameter == BoxMinDim) { + v = std::min (box.width (), box.height ()); + } else if (m_parameter == BoxMaxDim) { + v = std::max (box.width (), box.height ()); + } else if (m_parameter == BoxAverageDim) { + v = (box.width () + box.height ()) / 2; + } + if (! m_inverse) { + return v >= m_vmin && v < m_vmax; + } else { + return ! (v >= m_vmin && v < m_vmax); + } + } + + /** + * @brief This filter is isotropic unless the parameter is width or height + */ + virtual const TransformationReducer *vars () const + { + if (m_parameter != BoxWidth && m_parameter != BoxHeight) { + return &m_isotropic_vars; + } else { + return &m_anisotropic_vars; + } + } + + /** + * @brief This filter prefers producing variants + */ + virtual bool wants_variants () const { return true; } + + /** + * @brief This filter wants merged input + */ + virtual bool requires_raw_input () const { return false; } + +private: + value_type m_vmin, m_vmax; + bool m_inverse; + parameter_type m_parameter; + db::MagnificationReducer m_isotropic_vars; + db::MagnificationAndOrientationReducer m_anisotropic_vars; +}; + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + */ +class DB_PUBLIC Edge2EdgeCheckBase + : public db::box_scanner_receiver +{ +public: + Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers); + + /** + * @brief Call this to initiate a new pass until the return value is false + */ + bool prepare_next_pass (); + + /** + * @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 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 Gets the distance value + */ + EdgeRelationFilter::distance_type distance () const; + +protected: + virtual void put (const db::EdgePair &edge) const = 0; + +private: + const EdgeRelationFilter *mp_check; + bool m_requires_different_layers; + bool m_different_polygons; + EdgeRelationFilter::distance_type m_distance; + std::vector m_ep; + std::multimap, size_t> m_e2ep; + std::vector m_ep_discarded; + unsigned int m_pass; +}; + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + */ +template +class DB_PUBLIC_TEMPLATE edge2edge_check + : public Edge2EdgeCheckBase +{ +public: + edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers) + : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers), mp_output (&output) + { + // .. nothing yet .. + } + +protected: + void put (const db::EdgePair &edge) const + { + mp_output->insert (edge); + } + +private: + Output *mp_output; +}; + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + */ +class DB_PUBLIC Poly2PolyCheckBase + : public db::box_scanner_receiver +{ +public: + Poly2PolyCheckBase (Edge2EdgeCheckBase &output); + + void finish (const db::Polygon *o, size_t p); + void enter (const db::Polygon &o, size_t p); + void add (const db::Polygon *o1, size_t p1, const db::Polygon *o2, size_t p2); + void enter (const db::Polygon &o1, size_t p1, const db::Polygon &o2, size_t p2); + +private: + db::Edge2EdgeCheckBase *mp_output; + db::box_scanner m_scanner; + std::vector m_edges; +}; + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + */ +template +class DB_PUBLIC_TEMPLATE poly2poly_check + : public Poly2PolyCheckBase +{ +public: + poly2poly_check (edge2edge_check &output) + : Poly2PolyCheckBase (output) + { + // .. nothing yet .. + } +}; + +/** + * @brief A helper class for the region to edge interaction functionality + */ +class DB_PUBLIC RegionToEdgeInteractionFilterBase + : public db::box_scanner_receiver2 +{ +public: + RegionToEdgeInteractionFilterBase (bool inverse); + + void preset (const db::Polygon *poly); + void add (const db::Polygon *p, size_t, const db::Edge *e, size_t); + void fill_output (); + +protected: + virtual void put (const db::Polygon &poly) const = 0; + +private: + std::set m_seen; + bool m_inverse; +}; + +/** + * @brief A helper class for the region to edge interaction functionality + */ +template +class DB_PUBLIC_TEMPLATE region_to_edge_interaction_filter + : public RegionToEdgeInteractionFilterBase +{ +public: + region_to_edge_interaction_filter (OutputContainer &output, bool inverse) + : RegionToEdgeInteractionFilterBase (inverse), mp_output (&output) + { + // .. nothing yet .. + } + +protected: + virtual void put (const db::Polygon &poly) const + { + mp_output->insert (poly); + } + +private: + OutputContainer *mp_output; +}; + +} // namespace db + +#endif + diff --git a/src/db/db/dbShape.cc b/src/db/db/dbShape.cc index 2e08f93d4..3b6a0bfee 100644 --- a/src/db/db/dbShape.cc +++ b/src/db/db/dbShape.cc @@ -52,6 +52,8 @@ db::properties_id_type Shape::prop_id () const return (**((psimple_polygon_ptr_array_iter_type *) m_generic.iter)).properties_id (); case Edge: return (**((pedge_iter_type *) m_generic.iter)).properties_id (); + case EdgePair: + return (**((pedge_pair_iter_type *) m_generic.iter)).properties_id (); case Path: return (**((ppath_iter_type *) m_generic.iter)).properties_id (); case PathRef: @@ -99,6 +101,8 @@ db::properties_id_type Shape::prop_id () const return m_generic.psimple_polygon_aref->properties_id (); case Edge: return m_generic.pedge->properties_id (); + case EdgePair: + return m_generic.pedge_pair->properties_id (); case Path: return m_generic.ppath->properties_id (); case PathRef: @@ -701,6 +705,8 @@ Shape::box_type Shape::bbox () const return basic_ptr (text_ptr_array_type::tag ())->bbox (db::box_convert ()); case Edge: return box_type (edge ().p1 (), edge ().p2 ()); + case EdgePair: + return edge_pair ().bbox (); case Path: return path ().box (); case PathRef: @@ -772,6 +778,9 @@ Shape::to_string () const case Edge: r = "edge " + edge ().to_string (); break; + case EdgePair: + r = "edge_pair " + edge_pair ().to_string (); + break; case Path: case PathRef: case PathPtrArrayMember: @@ -810,6 +819,4 @@ Shape::to_string () const return r; } - } - diff --git a/src/db/db/dbShape.h b/src/db/db/dbShape.h index 7895ecc42..1666ca297 100644 --- a/src/db/db/dbShape.h +++ b/src/db/db/dbShape.h @@ -32,6 +32,7 @@ #include "dbPolygon.h" #include "dbPath.h" #include "dbEdge.h" +#include "dbEdgePair.h" #include "dbText.h" #include "dbBox.h" #include "dbBoxConvert.h" @@ -616,6 +617,7 @@ public: typedef db::path_ref path_ptr_type; typedef db::array path_ptr_array_type; typedef db::edge edge_type; + typedef db::edge_pair edge_pair_type; typedef db::text text_type; typedef db::text_ref text_ref_type; typedef db::text_ref text_ptr_type; @@ -642,6 +644,7 @@ public: typedef tl::reuse_vector >::const_iterator path_ptr_iter_type; typedef tl::reuse_vector >::const_iterator path_ptr_array_iter_type; typedef tl::reuse_vector >::const_iterator edge_iter_type; + typedef tl::reuse_vector >::const_iterator edge_pair_iter_type; typedef tl::reuse_vector >::const_iterator text_iter_type; typedef tl::reuse_vector >::const_iterator text_ref_iter_type; typedef tl::reuse_vector >::const_iterator text_ptr_iter_type; @@ -665,6 +668,7 @@ public: typedef tl::reuse_vector > >::const_iterator ppath_ptr_iter_type; typedef tl::reuse_vector > >::const_iterator ppath_ptr_array_iter_type; typedef tl::reuse_vector > >::const_iterator pedge_iter_type; + typedef tl::reuse_vector > >::const_iterator pedge_pair_iter_type; typedef tl::reuse_vector > >::const_iterator ptext_iter_type; typedef tl::reuse_vector > >::const_iterator ptext_ref_iter_type; typedef tl::reuse_vector > >::const_iterator ptext_ptr_iter_type; @@ -690,6 +694,7 @@ public: SimplePolygonPtrArray, SimplePolygonPtrArrayMember, Edge, + EdgePair, Path, PathRef, PathPtrArray, @@ -1016,7 +1021,15 @@ public: m_type = Edge; } - /** + /** + * @brief Construct a shape proxy as a reference to a edge pair + */ + void init (edge_pair_type::tag) + { + m_type = EdgePair; + } + + /** * @brief Construct a shape proxy as a reference to a box */ void init (box_type::tag) @@ -1344,7 +1357,23 @@ public: } } - /** + /** + * @brief Return the actual object that this shape reference is pointing to + * + * This is a generalisation of the polygon (), etc. methods using a tag to identify the + * target object. + */ + const edge_pair_type *basic_ptr (edge_pair_type::tag) const + { + tl_assert (m_type == EdgePair); + if (m_stable) { + return m_with_props ? &**(((pedge_pair_iter_type *) m_generic.iter)) : &**(((edge_pair_iter_type *) m_generic.iter)); + } else { + return m_with_props ? m_generic.pedge_pair : m_generic.edge_pair; + } + } + + /** * @brief Return the actual object that this shape reference is pointing to * * This is a generalisation of the polygon (), etc. methods using a tag to identify the @@ -1636,7 +1665,24 @@ public: } } - /** + /** + * @brief Return the actual object that this shape reference is pointing to for objects with properties + * + * This is a generalisation of the polygon (), etc. methods using a tag to identify the + * target object. + */ + const db::object_with_properties *basic_ptr (db::object_with_properties::tag) const + { + tl_assert (m_type == EdgePair); + tl_assert (m_with_props); + if (m_stable) { + return &**(((pedge_pair_iter_type *) m_generic.iter)); + } else { + return m_generic.pedge_pair; + } + } + + /** * @brief Return the actual object that this shape reference is pointing to for objects with properties * * This is a generalisation of the polygon (), etc. methods using a tag to identify the @@ -1862,7 +1908,16 @@ public: return *(((edge_iter_type *) m_generic.iter)); } - /** + /** + * @brief Return the iterator (in stable reference mode) by tag + */ + edge_pair_iter_type basic_iter (edge_pair_type::tag) const + { + tl_assert (m_type == EdgePair && ! m_with_props); + return *(((edge_pair_iter_type *) m_generic.iter)); + } + + /** * @brief Return the iterator (in stable reference mode) by tag */ text_iter_type basic_iter (text_type::tag) const @@ -2024,7 +2079,16 @@ public: return *(((pedge_iter_type *) m_generic.iter)); } - /** + /** + * @brief Return the iterator (in stable reference mode) by tag for objects with properties + */ + pedge_pair_iter_type basic_iter (db::object_with_properties::tag) const + { + tl_assert (m_type == EdgePair && m_with_props); + return *(((pedge_pair_iter_type *) m_generic.iter)); + } + + /** * @brief Return the iterator (in stable reference mode) by tag for objects with properties */ ptext_iter_type basic_iter (db::object_with_properties::tag) const @@ -2288,7 +2352,50 @@ public: return edge (p); } - /** + /** + * @brief Return a reference to the edge pair if one is referenced + */ + const edge_pair_type &edge_pair () const + { + tl_assert (m_type == EdgePair); + return *basic_ptr (edge_pair_type::tag ()); + } + + /** + * @brief Test if the shape proxy points to a edge pair + */ + bool is_edge_pair () const + { + return (m_type == EdgePair); + } + + /** + * @brief Instantiate the edge pair object + * + * If an edge pair is referenced, this object is instantiated + * by this method. + * Returns true, if the conversion was successful. + */ + bool edge_pair (edge_pair_type &e) const + { + if (is_edge_pair ()) { + e = edge_pair (); + return true; + } else { + return false; + } + } + + /** + * @brief Alias for polymorphic expansion + * Returns true, if the conversion was successful. + */ + bool instantiate (edge_pair_type &p) const + { + return edge_pair (p); + } + + /** * @brief Return a reference to the text if one is referenced */ const text_type &text () const @@ -2567,6 +2674,7 @@ public: const text_ref_type *text_ref; const text_ptr_array_type *text_aref; const edge_type *edge; + const edge_pair_type *edge_pair; const path_type *path; const path_ref_type *path_ref; const path_ptr_array_type *path_aref; @@ -2586,6 +2694,7 @@ public: const db::object_with_properties *ptext_ref; const db::object_with_properties *ptext_aref; const db::object_with_properties *pedge; + const db::object_with_properties *pedge_pair; const db::object_with_properties *ppath; const db::object_with_properties *ppath_ref; const db::object_with_properties *ppath_aref; diff --git a/src/db/db/dbShapeIterator.cc b/src/db/db/dbShapeIterator.cc index 7dfb61ba9..2acf3f924 100644 --- a/src/db/db/dbShapeIterator.cc +++ b/src/db/db/dbShapeIterator.cc @@ -634,6 +634,9 @@ ShapeIterator::advance_generic (int mode) case Edge: if (advance_shape (mode)) return; break; + case EdgePair: + if (advance_shape (mode)) return; + break; case Path: if (advance_shape (mode)) return; break; @@ -758,6 +761,8 @@ ShapeIterator::quad_box_generic () const return (quad_box_by_shape (region_tag)); case Edge: return (quad_box_by_shape (region_tag)); + case EdgePair: + return (quad_box_by_shape (region_tag)); case Path: return (quad_box_by_shape (region_tag)); case PathRef: diff --git a/src/db/db/dbShapes.cc b/src/db/db/dbShapes.cc index 5e98d1e4f..4dd7d1341 100644 --- a/src/db/db/dbShapes.cc +++ b/src/db/db/dbShapes.cc @@ -295,6 +295,8 @@ Shapes::do_insert (const Shapes::shape_type &shape, const Shapes::unit_trans_typ return (insert_array_by_tag (shape_type::simple_polygon_ptr_array_type::tag (), shape, shape_repository (), pm)); case shape_type::Edge: return (insert_by_tag (shape_type::edge_type::tag (), shape, pm)); + case shape_type::EdgePair: + return (insert_by_tag (shape_type::edge_pair_type::tag (), shape, pm)); case shape_type::Path: return (insert_by_tag (shape_type::path_type::tag (), shape, pm)); case shape_type::PathRef: @@ -439,6 +441,16 @@ Shapes::do_insert (const Shapes::shape_type &shape, const Trans &t, tl::func_del return insert (db::object_with_properties (p, pm (shape.prop_id ()))); } } + case shape_type::EdgePair: + { + shape_type::edge_pair_type p (shape.edge_pair ()); + p.transform (t); + if (! shape.has_prop_id ()) { + return insert (p); + } else { + return insert (db::object_with_properties (p, pm (shape.prop_id ()))); + } + } case shape_type::Path: { shape_type::path_type p (shape.path ()); @@ -554,6 +566,8 @@ Shapes::find (const Shapes::shape_type &shape) const return find_shape_by_tag (shape_type::simple_polygon_ptr_array_type::tag (), shape); case shape_type::Edge: return find_shape_by_tag (shape_type::edge_type::tag (), shape); + case shape_type::EdgePair: + return find_shape_by_tag (shape_type::edge_pair_type::tag (), shape); case shape_type::Path: return find_shape_by_tag (shape_type::path_type::tag (), shape); case shape_type::PathRef: @@ -620,6 +634,9 @@ Shapes::replace_prop_id (const Shapes::shape_type &ref, db::properties_id_type p case shape_type::Edge: replace_prop_id (ref.basic_ptr (object_with_properties::tag ()), prop_id); break; + case shape_type::EdgePair: + replace_prop_id (ref.basic_ptr (object_with_properties::tag ()), prop_id); + break; case shape_type::Path: replace_prop_id (ref.basic_ptr (object_with_properties::tag ()), prop_id); break; @@ -682,6 +699,8 @@ Shapes::replace_prop_id (const Shapes::shape_type &ref, db::properties_id_type p return replace_prop_id_iter (shape_type::simple_polygon_ptr_array_type::tag (), ref.basic_iter (shape_type::simple_polygon_ptr_array_type::tag ()), prop_id); case shape_type::Edge: return replace_prop_id_iter (shape_type::edge_type::tag (), ref.basic_iter (shape_type::edge_type::tag ()), prop_id); + case shape_type::EdgePair: + return replace_prop_id_iter (shape_type::edge_pair_type::tag (), ref.basic_iter (shape_type::edge_pair_type::tag ()), prop_id); case shape_type::Path: return replace_prop_id_iter (shape_type::path_type::tag (), ref.basic_iter (shape_type::path_type::tag ()), prop_id); case shape_type::PathRef: @@ -759,6 +778,12 @@ Shapes::transform (const Shapes::shape_type &ref, const Trans &t) p.transform (t); return replace_member_with_props (shape_type::edge_type::tag (), ref, p); } + case shape_type::EdgePair: + { + shape_type::edge_pair_type p (ref.edge_pair ()); + p.transform (t); + return replace_member_with_props (shape_type::edge_pair_type::tag (), ref, p); + } case shape_type::Path: { shape_type::path_type p (ref.path ()); @@ -845,6 +870,8 @@ Shapes::replace (const Shapes::shape_type &ref, const Sh &sh) return replace_member_with_props (shape_type::simple_polygon_ptr_array_type::tag (), ref, sh); case shape_type::Edge: return replace_member_with_props (shape_type::edge_type::tag (), ref, sh); + case shape_type::EdgePair: + return replace_member_with_props (shape_type::edge_pair_type::tag (), ref, sh); case shape_type::Path: return replace_member_with_props (shape_type::path_type::tag (), ref, sh); case shape_type::PathRef: @@ -1212,6 +1239,7 @@ template DB_PUBLIC Shape Shapes::replace<>(const Shape &, const Polygon &); template DB_PUBLIC Shape Shapes::replace<>(const Shape &, const SimplePolygon &); template DB_PUBLIC Shape Shapes::replace<>(const Shape &, const Text &); template DB_PUBLIC Shape Shapes::replace<>(const Shape &, const Edge &); +template DB_PUBLIC Shape Shapes::replace<>(const Shape &, const EdgePair &); template DB_PUBLIC Shape Shapes::transform<> (const Shape &, const ICplxTrans &); template DB_PUBLIC Shape Shapes::transform<> (const Shape &, const Trans &); @@ -1239,6 +1267,8 @@ template class DB_PUBLIC layer_op, db::stable_layer_tag>; template class DB_PUBLIC layer_op; template class DB_PUBLIC layer_op, db::stable_layer_tag>; +template class DB_PUBLIC layer_op; +template class DB_PUBLIC layer_op, db::stable_layer_tag>; template class DB_PUBLIC layer_op; template class DB_PUBLIC layer_op, db::stable_layer_tag>; template class DB_PUBLIC layer_op; @@ -1275,6 +1305,8 @@ template class DB_PUBLIC layer_op, db::unstable_layer_tag>; template class DB_PUBLIC layer_op; template class DB_PUBLIC layer_op, db::unstable_layer_tag>; +template class DB_PUBLIC layer_op; +template class DB_PUBLIC layer_op, db::unstable_layer_tag>; template class DB_PUBLIC layer_op; template class DB_PUBLIC layer_op, db::unstable_layer_tag>; template class DB_PUBLIC layer_op; diff --git a/src/db/db/dbShapes.h b/src/db/db/dbShapes.h index e279bd651..bc5100f8f 100644 --- a/src/db/db/dbShapes.h +++ b/src/db/db/dbShapes.h @@ -83,6 +83,7 @@ public: typedef db::array path_ptr_array_type; typedef path_ptr_array_type::iterator path_ptr_array_iterator_type; typedef db::edge edge_type; + typedef db::edge_pair edge_pair_type; typedef db::text text_type; typedef db::text_ref text_ref_type; typedef db::text_ref text_ptr_type; @@ -126,18 +127,19 @@ public: SimplePolygonRef = 4, SimplePolygonPtrArray = 5, Edge = 6, - Path = 7, - PathRef = 8, - PathPtrArray = 9, - Box = 10, - BoxArray = 11, - ShortBox = 12, - ShortBoxArray = 13, - Text = 14, - TextRef = 15, - TextPtrArray = 16, - UserObject = 17, - Null = 18 // must be last! + EdgePair = 7, + Path = 8, + PathRef = 9, + PathPtrArray = 10, + Box = 11, + BoxArray = 12, + ShortBox = 13, + ShortBoxArray = 14, + Text = 15, + TextRef = 16, + TextPtrArray = 17, + UserObject = 18, + Null = 19 // must be last! }; enum flags_type @@ -151,7 +153,8 @@ public: | (1 << SimplePolygonRef) | (1 << SimplePolygonPtrArray), Edges = (1 << Edge), - Paths = (1 << Path) + EdgePairs = (1 << EdgePair), + Paths = (1 << Path) | (1 << PathRef) | (1 << PathPtrArray), Boxes = (1 << Box) @@ -352,14 +355,15 @@ private: char sz8 [sizeof (per_shape_iter_size )]; char sz9 [sizeof (per_shape_iter_size )]; char sz10 [sizeof (per_shape_iter_size )]; - char sz11 [sizeof (per_shape_iter_size )]; - char sz12 [sizeof (per_shape_iter_size )]; - char sz13 [sizeof (per_shape_iter_size )]; - char sz14 [sizeof (per_shape_iter_size )]; - char sz15 [sizeof (per_shape_iter_size )]; - char sz16 [sizeof (per_shape_iter_size )]; - char sz17 [sizeof (per_shape_iter_size )]; - char sz18 [sizeof (per_shape_iter_size )]; + char sz11 [sizeof (per_shape_iter_size )]; + char sz12 [sizeof (per_shape_iter_size )]; + char sz13 [sizeof (per_shape_iter_size )]; + char sz14 [sizeof (per_shape_iter_size )]; + char sz15 [sizeof (per_shape_iter_size )]; + char sz16 [sizeof (per_shape_iter_size )]; + char sz17 [sizeof (per_shape_iter_size )]; + char sz18 [sizeof (per_shape_iter_size )]; + char sz19 [sizeof (per_shape_iter_size )]; }; // this union is simply there to determine the maximum size required for all diff --git a/src/db/db/dbShapes2.cc b/src/db/db/dbShapes2.cc index f2e6b7e7d..e8b82e555 100644 --- a/src/db/db/dbShapes2.cc +++ b/src/db/db/dbShapes2.cc @@ -673,6 +673,12 @@ inline unsigned int iterator_type_mask (ShapeIterator::edge_type::tag) return 1 << ShapeIterator::Edge; } +/// @brief Internal: ShapeIterator masks per shape type +inline unsigned int iterator_type_mask (ShapeIterator::edge_pair_type::tag) +{ + return 1 << ShapeIterator::EdgePair; +} + /// @brief Internal: ShapeIterator masks per shape type inline unsigned int iterator_type_mask (ShapeIterator::path_type::tag) { @@ -917,6 +923,8 @@ template class layer_class template class layer_class, db::stable_layer_tag>; template class layer_class; template class layer_class, db::stable_layer_tag>; +template class layer_class; +template class layer_class, db::stable_layer_tag>; template class layer_class; template class layer_class, db::stable_layer_tag>; template class layer_class; @@ -953,6 +961,8 @@ template class layer_class, db::unstable_layer_tag>; template class layer_class; template class layer_class, db::unstable_layer_tag>; +template class layer_class; +template class layer_class, db::unstable_layer_tag>; template class layer_class; template class layer_class, db::unstable_layer_tag>; template class layer_class; diff --git a/src/db/db/dbShapes3.cc b/src/db/db/dbShapes3.cc index 68fbe3cc4..099bb3702 100644 --- a/src/db/db/dbShapes3.cc +++ b/src/db/db/dbShapes3.cc @@ -117,6 +117,8 @@ template DB_PUBLIC layer & template DB_PUBLIC layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> (); template DB_PUBLIC layer &Shapes::get_layer (); template DB_PUBLIC layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> (); +template DB_PUBLIC layer &Shapes::get_layer (); +template DB_PUBLIC layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> (); template DB_PUBLIC layer &Shapes::get_layer (); template DB_PUBLIC layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> (); template DB_PUBLIC layer &Shapes::get_layer (); @@ -153,6 +155,8 @@ template DB_PUBLIC layer template DB_PUBLIC layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> (); template DB_PUBLIC layer &Shapes::get_layer (); template DB_PUBLIC layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> (); +template DB_PUBLIC layer &Shapes::get_layer (); +template DB_PUBLIC layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> (); template DB_PUBLIC layer &Shapes::get_layer (); template DB_PUBLIC layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> (); template DB_PUBLIC layer &Shapes::get_layer (); @@ -190,6 +194,8 @@ template DB_PUBLIC const layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> () const; template DB_PUBLIC const layer &Shapes::get_layer () const; template DB_PUBLIC const layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> () const; +template DB_PUBLIC const layer &Shapes::get_layer () const; +template DB_PUBLIC const layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> () const; template DB_PUBLIC const layer &Shapes::get_layer () const; template DB_PUBLIC const layer, db::stable_layer_tag> &Shapes::get_layer, db::stable_layer_tag> () const; template DB_PUBLIC const layer &Shapes::get_layer () const; @@ -226,6 +232,8 @@ template DB_PUBLIC const layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> () const; template DB_PUBLIC const layer &Shapes::get_layer () const; template DB_PUBLIC const layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> () const; +template DB_PUBLIC const layer &Shapes::get_layer () const; +template DB_PUBLIC const layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> () const; template DB_PUBLIC const layer &Shapes::get_layer () const; template DB_PUBLIC const layer, db::unstable_layer_tag> &Shapes::get_layer, db::unstable_layer_tag> () const; template DB_PUBLIC const layer &Shapes::get_layer () const; @@ -282,6 +290,8 @@ Shapes::is_valid (const Shapes::shape_type &shape) const return is_valid_shape_by_tag (shape_type::simple_polygon_ptr_array_type::tag (), shape); case shape_type::Edge: return is_valid_shape_by_tag (shape_type::edge_type::tag (), shape); + case shape_type::EdgePair: + return is_valid_shape_by_tag (shape_type::edge_pair_type::tag (), shape); case shape_type::Path: return is_valid_shape_by_tag (shape_type::path_type::tag (), shape); case shape_type::PathRef: @@ -440,6 +450,9 @@ Shapes::erase_shape (const Shapes::shape_type &shape) case shape_type::Edge: erase_shape_by_tag (shape_type::edge_type::tag (), shape); break; + case shape_type::EdgePair: + erase_shape_by_tag (shape_type::edge_pair_type::tag (), shape); + break; case shape_type::Path: erase_shape_by_tag (shape_type::path_type::tag (), shape); break; @@ -532,6 +545,9 @@ Shapes::erase_shapes (const std::vector &shapes) case shape_type::Edge: erase_shapes_by_tag (shape_type::edge_type::tag (), s, snext); break; + case shape_type::EdgePair: + erase_shapes_by_tag (shape_type::edge_pair_type::tag (), s, snext); + break; case shape_type::Path: erase_shapes_by_tag (shape_type::path_type::tag (), s, snext); break; diff --git a/src/db/db/dbSubCircuit.cc b/src/db/db/dbSubCircuit.cc new file mode 100644 index 000000000..4db7dfa6c --- /dev/null +++ b/src/db/db/dbSubCircuit.cc @@ -0,0 +1,140 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbSubCircuit.h" +#include "dbCircuit.h" + +namespace db +{ + +// -------------------------------------------------------------------------------- +// SubCircuit class implementation + +SubCircuit::SubCircuit () + : m_id (0), mp_circuit (0) +{ + // .. nothing yet .. +} + +SubCircuit::~SubCircuit() +{ + for (std::vector::const_iterator p = m_pin_refs.begin (); p != m_pin_refs.end (); ++p) { + if (*p != Net::subcircuit_pin_iterator () && (*p)->net ()) { + (*p)->net ()->erase_subcircuit_pin (*p); + } + } +} + +SubCircuit::SubCircuit (Circuit *circuit, const std::string &name) + : m_circuit_ref (0), m_name (name), m_id (0), mp_circuit (0) +{ + set_circuit_ref (circuit); +} + +SubCircuit::SubCircuit (const SubCircuit &other) + : tl::Object (other), m_id (0), mp_circuit (0) +{ + operator= (other); +} + +SubCircuit &SubCircuit::operator= (const SubCircuit &other) +{ + if (this != &other) { + m_name = other.m_name; + m_trans = other.m_trans; + set_circuit_ref (const_cast (other.circuit_ref ())); + } + return *this; +} + +void SubCircuit::set_name (const std::string &n) +{ + m_name = n; + if (mp_circuit) { + mp_circuit->m_subcircuit_by_name.invalidate (); + } +} + +std::string SubCircuit::expanded_name () const +{ + if (name ().empty ()) { + return "$" + tl::to_string (id ()); + } else { + return name (); + } +} + +void SubCircuit::set_trans (const db::DCplxTrans &t) +{ + m_trans = t; +} + +void SubCircuit::set_pin_ref_for_pin (size_t pin_id, Net::subcircuit_pin_iterator iter) +{ + if (m_pin_refs.size () < pin_id + 1) { + m_pin_refs.resize (pin_id + 1, Net::subcircuit_pin_iterator ()); + } + m_pin_refs [pin_id] = iter; +} + +void SubCircuit::set_circuit_ref (Circuit *c) +{ + if (m_circuit_ref.get ()) { + m_circuit_ref->unregister_ref (this); + } + m_circuit_ref.reset (c); + if (m_circuit_ref.get ()) { + m_circuit_ref->register_ref (this); + } +} + +const Net *SubCircuit::net_for_pin (size_t pin_id) const +{ + if (pin_id < m_pin_refs.size ()) { + Net::subcircuit_pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::subcircuit_pin_iterator ()) { + return p->net (); + } + } + return 0; +} + +void SubCircuit::connect_pin (size_t pin_id, Net *net) +{ + if (net_for_pin (pin_id) == net) { + return; + } + + if (pin_id < m_pin_refs.size ()) { + Net::subcircuit_pin_iterator p = m_pin_refs [pin_id]; + if (p != Net::subcircuit_pin_iterator () && p->net ()) { + p->net ()->erase_subcircuit_pin (p); + } + m_pin_refs [pin_id] = Net::subcircuit_pin_iterator (); + } + + if (net) { + net->add_subcircuit_pin (NetSubcircuitPinRef (this, pin_id)); + } +} + +} diff --git a/src/db/db/dbSubCircuit.h b/src/db/db/dbSubCircuit.h new file mode 100644 index 000000000..6a25001c0 --- /dev/null +++ b/src/db/db/dbSubCircuit.h @@ -0,0 +1,222 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbSubCircuit +#define _HDR_dbSubCircuit + +#include "dbCommon.h" +#include "dbTrans.h" +#include "dbNet.h" + +#include "tlObject.h" + +#include +#include + +namespace db +{ + +class Circuit; + +/** + * @brief A subcircuit of a circuit + * + * This class essentially is a reference to another circuit + */ +class DB_PUBLIC SubCircuit + : public tl::Object +{ +public: + typedef tl::vector connected_net_list; + + /** + * @brief Default constructor + */ + SubCircuit (); + + /** + * @brief Copy constructor + */ + SubCircuit (const SubCircuit &other); + + /** + * @brief Assignment + */ + SubCircuit &operator= (const SubCircuit &other); + + /** + * @brief Creates a subcircuit reference to the given circuit + */ + SubCircuit (Circuit *circuit_ref, const std::string &name = std::string ()); + + /** + * @brief Destructor + */ + ~SubCircuit (); + + /** + * @brief Gets the subcircuit ID + * The ID is a unique integer which identifies the subcircuit. + * It can be used to retrieve the subcircuit from the circuit using Circuit::subcircuit_by_id. + * When assigned, the subcircuit ID is not 0. + */ + size_t id () const + { + return m_id; + } + + /** + * @brief Gets the circuit the subcircuit lives in (const version) + * This pointer is 0 if the subcircuit isn't added to a circuit + */ + const Circuit *circuit () const + { + return mp_circuit; + } + + /** + * @brief Gets the circuit the subcircuit lives in (non-const version) + * This pointer is 0 if the subcircuit isn't added to a circuit + */ + Circuit *circuit () + { + return mp_circuit; + } + + /** + * @brief Gets the circuit the reference points to (const version) + */ + const Circuit *circuit_ref () const + { + return m_circuit_ref.get (); + } + + /** + * @brief Gets the circuit the reference points to (non-const version) + */ + Circuit *circuit_ref () + { + return m_circuit_ref.get (); + } + + /** + * @brief Sets the name of the subcircuit + * + * The name is one way to identify the subcircuit. The transformation is + * another one. + */ + void set_name (const std::string &n); + + /** + * @brief Gets the name of the subcircuit + */ + const std::string &name () const + { + return m_name; + } + + /** + * @brief Gets a name which always non-empty + * This method will pick a name like "$" if the explicit name is empty. + */ + std::string expanded_name () const; + + /** + * @brief Sets the transformation describing the subcircuit + * + * The transformation is a natural description of a subcircuit + * (in contrast to the name) when deriving it from a layout. + */ + void set_trans (const db::DCplxTrans &trans); + + /** + * @brief Gets the transformation describing the subcircuit + */ + const db::DCplxTrans &trans () const + { + return m_trans; + } + + /** + * @brief Gets the net attached to a specific pin + * Returns 0 if no net is attached. + */ + const Net *net_for_pin (size_t pin_id) const; + + /** + * @brief Gets the net attached to a specific pin (non-const version) + * Returns 0 if no net is attached. + */ + Net *net_for_pin (size_t pin_id) + { + return const_cast (((const SubCircuit *) this)->net_for_pin (pin_id)); + } + + /** + * @brief Connects the given pin to the given net + * If the net is 0 the pin is disconnected. + * If non-null, a NetPinRef object will be inserted into the + * net and connected with the given pin. + */ + void connect_pin (size_t pin_id, Net *net); + +private: + friend class Circuit; + friend class Net; + + tl::weak_ptr m_circuit_ref; + std::string m_name; + db::DCplxTrans m_trans; + std::vector m_pin_refs; + size_t m_id; + Circuit *mp_circuit; + + /** + * @brief Sets the pin reference for a specific pin + */ + void set_pin_ref_for_pin (size_t ppin_id, Net::subcircuit_pin_iterator iter); + + /** + * @brief Sets the circuit reference + */ + void set_circuit_ref (Circuit *c); + + /** + * @brief Sets the circuit the subcircuit belongs to + */ + void set_circuit (Circuit *c) + { + mp_circuit = c; + } + + /** + * @brief Sets the device ID + */ + void set_id (size_t id) + { + m_id = id; + } +}; + +} + +#endif diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index 27b6f7af8..b8b1af7d5 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -50,15 +50,22 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: hash = (hash << 4) ^ (hash >> 4) ^ ((unsigned int) *cp); } + const db::Layout *subject = 0; + db::Layout layout2; + std::string tmp_file; db::SaveLayoutOptions options; if (norm == WriteGDS2) { tmp_file = _this->tmp_file (tl::sprintf ("tmp_%x.gds", hash)); options.set_format ("GDS2"); - } else { + } else if (norm == WriteOAS) { tmp_file = _this->tmp_file (tl::sprintf ("tmp_%x.oas", hash)); options.set_format ("OASIS"); + } else { + // write the temp file in the same format than the au file + tmp_file = _this->tmp_file (tl::sprintf ("tmp_%x." + tl::extension (au_file), hash)); + options.set_format_from_filename (tmp_file); } { @@ -67,10 +74,7 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: writer.write (const_cast (layout), stream); } - const db::Layout *subject = 0; - db::Layout layout2; - - if (norm != NoNormalization) { + if (norm == WriteGDS2 || norm == WriteOAS) { // read all layers from the original layout, so the layer table is the same for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { @@ -121,7 +125,12 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: db::Reader reader (stream); reader.read (layout_au, options); - equal = db::compare_layouts (*subject, layout_au, (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) | db::layout_diff::f_flatten_array_insts /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/, tolerance, 100 /*max diff lines*/); + equal = db::compare_layouts (*subject, layout_au, + (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) + | (norm == AsPolygons ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0) + | db::layout_diff::f_flatten_array_insts + /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/ + , tolerance, 100 /*max diff lines*/); if (equal && n > 0) { tl::info << tl::sprintf ("Found match on golden reference variant %s", fn); } diff --git a/src/db/db/dbTestSupport.h b/src/db/db/dbTestSupport.h index aceb15955..00fb38432 100644 --- a/src/db/db/dbTestSupport.h +++ b/src/db/db/dbTestSupport.h @@ -46,6 +46,7 @@ class LayerMap; enum NormalizationMode { NoNormalization, // no normalization - take the test subject as it is + AsPolygons, // paths and boxes are treated as polygons WriteGDS2, // normalize subject by writing to GDS2 and reading back WriteOAS // normalize subject by writing to OASIS and reading back }; diff --git a/src/db/db/dbTilingProcessor.h b/src/db/db/dbTilingProcessor.h index ff857044e..ed492c07c 100644 --- a/src/db/db/dbTilingProcessor.h +++ b/src/db/db/dbTilingProcessor.h @@ -341,7 +341,7 @@ void insert (X &inserter, const db::Edges &data, const db::Box &tile, bool clip) template void insert (X &inserter, const db::EdgePairs &data, const db::Box &tile, bool clip) { - for (db::EdgePairs::const_iterator o = data.begin (); o != data.end (); ++o) { + for (db::EdgePairs::const_iterator o = data.begin (); ! o.at_end (); ++o) { insert (inserter, *o, tile, clip); } } diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 0865ab1e7..92e13c342 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1358,7 +1358,6 @@ static std::vector copy_tree (db::Cell *cell, const db::Cel throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout"))); } - db::PropertyMapper pm (*target_layout, *source_layout); db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ()); db::CellMapping cm; @@ -1389,7 +1388,6 @@ static void copy_tree_shapes2 (db::Cell *cell, const db::Cell &source_cell, cons throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout"))); } - db::PropertyMapper pm (*target_layout, *source_layout); db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ()); db::LayerMapping lm; @@ -1415,7 +1413,6 @@ static void copy_tree_shapes3 (db::Cell *cell, const db::Cell &source_cell, cons throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout"))); } - db::PropertyMapper pm (*target_layout, *source_layout); db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ()); std::vector source_cells; diff --git a/src/db/db/gsiDeclDbDeepShapeStore.cc b/src/db/db/gsiDeclDbDeepShapeStore.cc new file mode 100644 index 000000000..471c6fe67 --- /dev/null +++ b/src/db/db/gsiDeclDbDeepShapeStore.cc @@ -0,0 +1,108 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "gsiDecl.h" +#include "dbDeepShapeStore.h" + +namespace gsi +{ + +Class decl_dbDeepShapeStore ("db", "DeepShapeStore", + gsi::method ("instance_count", &db::DeepShapeStore::instance_count, + "@hide\n" + ) + + gsi::method ("is_singular?", &db::DeepShapeStore::is_singular, + "@brief Gets a value indicating whether there is a single layout variant\n" + "\n" + "Specifically for network extraction, singular DSS objects are required. " + "Multiple layouts may be present if different sources of layouts have " + "been used. Such DSS objects are not usable for network extraction." + ) + + gsi::method ("threads=", &db::DeepShapeStore::set_threads, gsi::arg ("n"), + "@brief Sets the number of threads to allocate for the hierarchical processor\n" + ) + + gsi::method ("threads", &db::DeepShapeStore::threads, + "@brief Gets the number of threads.\n" + ) + + gsi::method ("max_vertex_count=", &db::DeepShapeStore::set_max_vertex_count, gsi::arg ("count"), + "@brief Sets the maximum vertex count default value\n" + "\n" + "This parameter is used to simplify complex polygons. It is used by\n" + "create_polygon_layer with the default parameters. It's also used by\n" + "boolean operations when they deliver their output.\n" + ) + + gsi::method ("max_vertex_count", &db::DeepShapeStore::max_vertex_count, + "@brief Gets the maximum vertex count.\n" + ) + + gsi::method ("max_area_ratio=", &db::DeepShapeStore::set_max_area_ratio, gsi::arg ("ratio"), + "@brief Sets the max. area ratio for bounding box vs. polygon area\n" + "\n" + "This parameter is used to simplify complex polygons. It is used by\n" + "create_polygon_layer with the default parameters. It's also used by\n" + "boolean operations when they deliver their output.\n" + ) + + gsi::method ("max_area_ratio", &db::DeepShapeStore::max_area_ratio, + "@brief Gets the max. area ratio.\n" + ) + + gsi::method ("text_property_name=", &db::DeepShapeStore::set_text_property_name, gsi::arg ("name"), + "@brief Sets the text property name.\n" + "\n" + "If set to a non-null variant, text strings are attached to the generated boxes\n" + "as properties with this particular name. This option has an effect only if the\n" + "text_enlargement property is not negative.\n" + "By default, the name is empty.\n" + ) + + gsi::method ("text_property_name", &db::DeepShapeStore::text_property_name, + "@brief Gets the text property name.\n" + ) + + gsi::method ("text_enlargement=", &db::DeepShapeStore::set_text_enlargement, gsi::arg ("value"), + "@brief Sets the text enlargement value\n" + "\n" + "If set to a non-negative value, text objects are converted to boxes with the\n" + "given enlargement (width = 2 * enlargement). The box centers are identical\n" + "to the original location of the text.\n" + "If this value is negative (the default), texts are ignored.\n" + ) + + gsi::method ("text_enlargement", &db::DeepShapeStore::text_enlargement, + "@brief Gets the text enlargement value.\n" + ), + "@brief An opaque layout heap for the deep region processor\n" + "\n" + "This class is used for keeping intermediate, hierarchical data for the " + "deep region processor. It is used in conjunction with the region " + "constructor to create a deep (hierarchical) region." + "\n" + "@code\n" + "layout = ... # a layout\n" + "layer = ... # a layer\n" + "cell = ... # a cell (initial cell for the deep region)\n" + "dss = RBA::DeepShapeStore::new\n" + "region = RBA::Region::new(cell.begin(layer), dss)\n" + "@/code\n" + "\n" + "The DeepShapeStore object also supplies some configuration options " + "for the operations acting on the deep regions. See for example \\threads=.\n" + "\n" + "This class has been introduced in version 0.26.\n" +); + +} diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index ecf1116f4..6bef742c6 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -26,6 +26,7 @@ #include "dbEdgePairs.h" #include "dbEdges.h" #include "dbRegion.h" +#include "dbDeepEdgePairs.h" namespace gsi { @@ -35,6 +36,45 @@ static db::EdgePairs *new_v () return new db::EdgePairs (); } +static db::EdgePairs *new_a (const std::vector &pairs) +{ + return new db::EdgePairs (pairs.begin (), pairs.end ()); +} + +static db::EdgePairs *new_ep (const db::EdgePair &pair) +{ + return new db::EdgePairs (pair); +} + +static db::EdgePairs *new_shapes (const db::Shapes &s) +{ + db::EdgePairs *r = new db::EdgePairs (); + for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::EdgePairs); !i.at_end (); ++i) { + r->insert (*i); + } + return r; +} + +static db::EdgePairs *new_si (const db::RecursiveShapeIterator &si) +{ + return new db::EdgePairs (si); +} + +static db::EdgePairs *new_si2 (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) +{ + return new db::EdgePairs (si, trans); +} + +static db::EdgePairs *new_sid (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss) +{ + return new db::EdgePairs (si, dss); +} + +static db::EdgePairs *new_si2d (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const db::ICplxTrans &trans) +{ + return new db::EdgePairs (si, dss, trans); +} + static std::string to_string0 (const db::EdgePairs *r) { return r->to_string (); @@ -45,15 +85,6 @@ static std::string to_string1 (const db::EdgePairs *r, size_t n) return r->to_string (n); } -static const db::EdgePair *nth (const db::EdgePairs *r, size_t n) -{ - if (n >= r->size ()) { - return 0; - } else { - return &r->begin ()[n]; - } -} - static db::EdgePairs &move_p (db::EdgePairs *r, const db::Vector &p) { r->transform (db::Disp (p)); @@ -94,7 +125,7 @@ static db::Region extents2 (const db::EdgePairs *r, db::Coord dx, db::Coord dy) { db::Region e; e.reserve (r->size ()); - for (db::EdgePairs::const_iterator i = r->begin (); i != r->end (); ++i) { + for (db::EdgePairs::const_iterator i = r->begin (); ! i.at_end (); ++i) { e.insert (i->bbox ().enlarged (db::Vector (dx, dy))); } return e; @@ -133,17 +164,146 @@ static db::Edges second_edges (const db::EdgePairs *ep) static void insert_e (db::EdgePairs *e, const db::EdgePairs &a) { - for (db::EdgePairs::const_iterator p = a.begin (); p != a.end (); ++p) { + for (db::EdgePairs::const_iterator p = a.begin (); ! p.at_end (); ++p) { e->insert (*p); } } +static bool is_deep (const db::EdgePairs *ep) +{ + return dynamic_cast (ep->delegate ()) != 0; +} + +static size_t id (const db::EdgePairs *ep) +{ + return tl::id_of (ep->delegate ()); +} + Class decl_EdgePairs ("db", "EdgePairs", constructor ("new", &new_v, "@brief Default constructor\n" "\n" "This constructor creates an empty edge pair collection.\n" ) + + constructor ("new", &new_a, + "@brief Constructor from an edge pair array\n" + "@args array\n" + "\n" + "This constructor creates an edge pair collection from an array of \\EdgePair objects.\n" + "\n" + "This constructor has been introduced in version 0.26." + ) + + constructor ("new", &new_ep, + "@brief Constructor from a single edge pair object\n" + "@args edge_pair\n" + "\n" + "This constructor creates an edge pair collection with a single edge pair.\n" + "\n" + "This constructor has been introduced in version 0.26." + ) + + constructor ("new", &new_shapes, + "@brief Shapes constructor\n" + "@args shapes\n" + "\n" + "This constructor creates an edge pair collection from a \\Shapes collection.\n" + "\n" + "This constructor has been introduced in version 0.26." + ) + + constructor ("new", &new_si, + "@brief Constructor from a hierarchical shape set\n" + "@args shape_iterator\n" + "\n" + "This constructor creates an edge pair collection from the shapes delivered by the given recursive shape iterator.\n" + "Only edge pairs are taken from the shape set and other shapes are ignored.\n" + "This method allows feeding the edge pair collection from a hierarchy of cells.\n" + "Edge pairs in layout objects are somewhat special as most formats don't support reading " + "or writing of edge pairs. Still they are useful objects and can be created and manipulated inside layouts.\n" + "\n" + "@code\n" + "layout = ... # a layout\n" + "cell = ... # the index of the initial cell\n" + "layer = ... # the index of the layer from where to take the shapes from\n" + "r = RBA::EdgePairs::new(layout.begin_shapes(cell, layer))\n" + "@/code\n" + "\n" + "This constructor has been introduced in version 0.26." + ) + + constructor ("new", &new_si2, + "@brief Constructor from a hierarchical shape set with a transformation\n" + "@args shape_iterator, trans\n" + "\n" + "This constructor creates an edge pair collection from the shapes delivered by the given recursive shape iterator.\n" + "Only edge pairs are taken from the shape set and other shapes are ignored.\n" + "The given transformation is applied to each edge pair taken.\n" + "This method allows feeding the edge pair collection from a hierarchy of cells.\n" + "The transformation is useful to scale to a specific database unit for example.\n" + "Edge pairs in layout objects are somewhat special as most formats don't support reading " + "or writing of edge pairs. Still they are useful objects and can be created and manipulated inside layouts.\n" + "\n" + "@code\n" + "layout = ... # a layout\n" + "cell = ... # the index of the initial cell\n" + "layer = ... # the index of the layer from where to take the shapes from\n" + "dbu = 0.1 # the target database unit\n" + "r = RBA::EdgePairs::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" + "@/code\n" + "\n" + "This constructor has been introduced in version 0.26." + ) + + constructor ("new", &new_sid, gsi::arg ("shape_iterator"), gsi::arg ("dss"), + "@brief Creates a hierarchical edge pair collection from an original layer\n" + "\n" + "This constructor creates an edge pair collection from the shapes delivered by the given recursive shape iterator.\n" + "This version will create a hierarchical edge pair collection which supports hierarchical operations.\n" + "Edge pairs in layout objects are somewhat special as most formats don't support reading " + "or writing of edge pairs. Still they are useful objects and can be created and manipulated inside layouts.\n" + "\n" + "@code\n" + "dss = RBA::DeepShapeStore::new\n" + "layout = ... # a layout\n" + "cell = ... # the index of the initial cell\n" + "layer = ... # the index of the layer from where to take the shapes from\n" + "r = RBA::EdgePairs::new(layout.begin_shapes(cell, layer))\n" + "@/code\n" + "\n" + "This constructor has been introduced in version 0.26." + ) + + constructor ("new", &new_si2d, gsi::arg ("shape_iterator"), gsi::arg ("dss"), gsi::arg ("trans"), + "@brief Creates a hierarchical edge pair collection from an original layer with a transformation\n" + "\n" + "This constructor creates an edge pair collection from the shapes delivered by the given recursive shape iterator.\n" + "This version will create a hierarchical edge pair collection which supports hierarchical operations.\n" + "The transformation is useful to scale to a specific database unit for example.\n" + "Edge pairs in layout objects are somewhat special as most formats don't support reading " + "or writing of edge pairs. Still they are useful objects and can be created and manipulated inside layouts.\n" + "\n" + "@code\n" + "dss = RBA::DeepShapeStore::new\n" + "layout = ... # a layout\n" + "cell = ... # the index of the initial cell\n" + "layer = ... # the index of the layer from where to take the shapes from\n" + "dbu = 0.1 # the target database unit\n" + "r = RBA::EdgePairs::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" + "@/code\n" + "\n" + "This constructor has been introduced in version 0.26." + ) + + method ("insert_into", &db::EdgePairs::insert_into, gsi::arg ("layout"), gsi::arg ("cell_index"), gsi::arg ("layer"), + "@brief Inserts this edge pairs into the given layout, below the given cell and into the given layer.\n" + "If the edge pair collection is a hierarchical one, a suitable hierarchy will be built below the top cell or " + "and existing hierarchy will be reused.\n" + "\n" + "This method has been introduced in version 0.26." + ) + + method ("insert_into_as_polygons", &db::EdgePairs::insert_into_as_polygons, gsi::arg ("layout"), gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("e"), + "@brief Inserts this edge pairs into the given layout, below the given cell and into the given layer.\n" + "If the edge pair collection is a hierarchical one, a suitable hierarchy will be built below the top cell or " + "and existing hierarchy will be reused.\n" + "\n" + "The edge pairs will be converted to polygons with the enlargement value given be 'e'.\n" + "\n" + "This method has been introduced in version 0.26." + ) + method ("insert", (void (db::EdgePairs::*) (const db::Edge &, const db::Edge &)) &db::EdgePairs::insert, "@brief Inserts an edge pair into the collection\n" "@args first, second\n" @@ -152,6 +312,16 @@ Class decl_EdgePairs ("db", "EdgePairs", "@brief Inserts an edge pair into the collection\n" "@args edge_pair\n" ) + + method_ext ("is_deep?", &is_deep, + "@brief Returns true if the edge pair collection is a deep (hierarchical) one\n" + "\n" + "This method has been added in version 0.26." + ) + + method_ext ("data_id", &id, + "@brief Returns the data ID (a unique identifier for the underlying data storage)\n" + "\n" + "This method has been added in version 0.26." + ) + method ("+", &db::EdgePairs::operator+, "@brief Returns the combined edge pair collection of self and the other one\n" "\n" @@ -339,14 +509,30 @@ Class decl_EdgePairs ("db", "EdgePairs", method ("size", &db::EdgePairs::size, "@brief Returns the number of edge pairs in this collection\n" ) + - gsi::iterator ("each", &db::EdgePairs::begin, &db::EdgePairs::end, + gsi::iterator ("each", &db::EdgePairs::begin, "@brief Returns each edge pair of the edge pair collection\n" ) + - method_ext ("[]", &nth, + method ("[]", &db::EdgePairs::nth, "@brief Returns the nth edge pair\n" "@args n\n" "\n" - "This method returns nil if the index is out of range.\n" + "This method returns nil if the index is out of range. It is available for flat edge pairs only - i.e. " + "those for which \\has_valid_edge_pairs? is true. Use \\flatten to explicitly flatten an edge pair collection.\n" + "\n" + "The \\each iterator is the more general approach to access the edge pairs." + ) + + method ("flatten", &db::EdgePairs::flatten, + "@brief Explicitly flattens an edge pair collection\n" + "\n" + "If the collection is already flat (i.e. \\has_valid_edge_pairs? returns true), this method will " + "not change the collection.\n" + "\n" + "This method has been introduced in version 0.26." + ) + + method ("has_valid_edge_pairs?", &db::EdgePairs::has_valid_edge_pairs, + "@brief Returns true if the edge pair collection is flat and individual edge pairs can be accessed randomly\n" + "\n" + "This method has been introduced in version 0.26." ) + method ("enable_progress", &db::EdgePairs::enable_progress, "@brief Enable progress reporting\n" diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 9a607c6f1..31a44b39e 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -23,8 +23,12 @@ #include "gsiDecl.h" +#include "dbDeepShapeStore.h" #include "dbEdges.h" +#include "dbEdgesUtils.h" +#include "dbDeepEdges.h" #include "dbRegion.h" +#include "dbOriginalLayerRegion.h" #include "dbLayoutUtils.h" namespace gsi @@ -82,6 +86,15 @@ static db::Edges *new_path (const db::Path &o) return new db::Edges (o); } +static db::Edges *new_shapes (const db::Shapes &s, bool as_edges) +{ + db::Edges *r = new db::Edges (); + for (db::Shapes::shape_iterator i = s.begin (as_edges ? db::ShapeIterator::All : db::ShapeIterator::Edges); !i.at_end (); ++i) { + r->insert (*i); + } + return r; +} + static db::Edges *new_si (const db::RecursiveShapeIterator &si, bool as_edges) { return new db::Edges (si, as_edges); @@ -92,6 +105,16 @@ static db::Edges *new_si2 (const db::RecursiveShapeIterator &si, const db::ICplx return new db::Edges (si, trans, as_edges); } +static db::Edges *new_sid (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, bool as_edges) +{ + return new db::Edges (si, dss, as_edges); +} + +static db::Edges *new_si2d (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const db::ICplxTrans &trans, bool as_edges) +{ + return new db::Edges (si, dss, trans, as_edges); +} + static db::Edges::distance_type length1 (const db::Edges *edges) { return edges->length (); @@ -362,65 +385,85 @@ static void insert_s (db::Edges *e, const db::Shapes &a) insert_st (e, a, db::UnitTrans ()); } +static bool is_deep (const db::Edges *e) +{ + return dynamic_cast (e->delegate ()) != 0; +} + +static db::Edges *new_texts_as_dots1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern) +{ + return new db::Edges (db::Region (si).texts_as_dots (pat, pattern)); +} + +static db::Edges *new_texts_as_dots2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const std::string &pat, bool pattern) +{ + return new db::Edges (db::Region (si).texts_as_dots (pat, pattern, dss)); +} + +static size_t id (const db::Edges *e) +{ + return tl::id_of (e->delegate ()); +} + Class dec_Edges ("db", "Edges", constructor ("new", &new_v, "@brief Default constructor\n" "\n" "This constructor creates an empty edge collection.\n" ) + - constructor ("new", &new_e, + constructor ("new", &new_e, gsi::arg ("edge"), "@brief Constructor from a single edge\n" - "@args edge\n" "\n" "This constructor creates an edge collection with a single edge.\n" ) + - constructor ("new", &new_a1, + constructor ("new", &new_a1, gsi::arg ("array"), "@brief Constructor from a polygon array\n" - "@args array\n" "\n" "This constructor creates a region from an array of polygons.\n" "The edges form the contours of the polygons.\n" ) + - constructor ("new", &new_a2, + constructor ("new", &new_a2, gsi::arg ("array"), "@brief Constructor from an egde array\n" - "@args array\n" "\n" "This constructor creates a region from an array of edges.\n" ) + - constructor ("new", &new_b, + constructor ("new", &new_b, gsi::arg ("box"), "@brief Box constructor\n" - "@args box\n" "\n" "This constructor creates an edge collection from a box.\n" "The edges form the contour of the box.\n" ) + - constructor ("new", &new_p, + constructor ("new", &new_p, gsi::arg ("polygon"), "@brief Polygon constructor\n" - "@args polygon\n" "\n" "This constructor creates an edge collection from a polygon.\n" "The edges form the contour of the polygon.\n" ) + - constructor ("new", &new_ps, + constructor ("new", &new_ps, gsi::arg ("polygon"), "@brief Simple polygon constructor\n" - "@args polygon\n" "\n" "This constructor creates an edge collection from a simple polygon.\n" "The edges form the contour of the polygon.\n" ) + - constructor ("new", &new_path, + constructor ("new", &new_path, gsi::arg ("path"), "@brief Path constructor\n" - "@args path\n" "\n" "This constructor creates an edge collection from a path.\n" "The edges form the contour of the path.\n" ) + - constructor ("new", &new_si, - "@brief Constructor from a hierarchical shape set\n" - "@args shape_iterator, as_edges\n" + constructor ("new", &new_shapes, gsi::arg ("shapes"), gsi::arg ("as_edges", true), + "@brief Constructor of a flat edge collection from a \\Shapes container\n" + "\n" + "If 'as_edges' is true, the shapes from the container will be converted to edges (i.e. polygon contours to edges). " + "Otherwise, only edges will be taken from the container.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + constructor ("new", &new_si, gsi::arg ("shape_iterator"), gsi::arg ("as_edges", true), + "@brief Constructor of a flat edge collection from a hierarchical shape set\n" "\n" "This constructor creates an edge collection from the shapes delivered by the given recursive shape iterator.\n" - "It feeds the shapes from a hierarchy of cells into the edge set.\n" + "It feeds the shapes from a hierarchy of cells into a flat edge set.\n" "\n" "Text objects are not inserted, because they cannot be converted to edges.\n" "Edge objects are inserted as such. If \"as_edges\" is true, \"solid\" objects (boxes, polygons, paths) are converted to edges which " @@ -433,12 +476,11 @@ Class dec_Edges ("db", "Edges", "r = RBA::Edges::new(layout.begin_shapes(cell, layer), false)\n" "@/code\n" ) + - constructor ("new", &new_si2, - "@brief Constructor from a hierarchical shape set with a transformation\n" - "@args shape_iterator, trans, as_edges\n" + constructor ("new", &new_si2, gsi::arg ("shape_iterator"), gsi::arg ("trans"), gsi::arg ("as_edges", true), + "@brief Constructor of a flat edge collection from a hierarchical shape set with a transformation\n" "\n" "This constructor creates an edge collection from the shapes delivered by the given recursive shape iterator.\n" - "It feeds the shapes from a hierarchy of cells into the edge set.\n" + "It feeds the shapes from a hierarchy of cells into a flat edge set.\n" "The transformation is useful to scale to a specific database unit for example.\n" "\n" "Text objects are not inserted, because they cannot be converted to edges.\n" @@ -453,16 +495,102 @@ Class dec_Edges ("db", "Edges", "r = RBA::Edges::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" "@/code\n" ) + - method_ext ("with_length", with_length1, + constructor ("new", &new_sid, gsi::arg ("shape_iterator"), gsi::arg ("dss"), gsi::arg ("as_edges", true), + "@brief Constructor of a hierarchical edge collection\n" + "\n" + "This constructor creates an edge collection from the shapes delivered by the given recursive shape iterator.\n" + "It feeds the shapes from a hierarchy of cells into the hierarchical edge set.\n" + "The edges remain within their original hierachy unless other operations require the edges to be moved in the hierarchy.\n" + "\n" + "Text objects are not inserted, because they cannot be converted to edges.\n" + "Edge objects are inserted as such. If \"as_edges\" is true, \"solid\" objects (boxes, polygons, paths) are converted to edges which " + "form the hull of these objects. If \"as_edges\" is false, solid objects are ignored.\n" + "\n" + "@code\n" + "dss = RBA::DeepShapeStore::new\n" + "layout = ... # a layout\n" + "cell = ... # the index of the initial cell\n" + "layer = ... # the index of the layer from where to take the shapes from\n" + "r = RBA::Edges::new(layout.begin_shapes(cell, layer), dss, false)\n" + "@/code\n" + ) + + constructor ("new", &new_si2d, gsi::arg ("shape_iterator"), gsi::arg ("dss"), gsi::arg ("trans"), gsi::arg ("as_edges", true), + "@brief Constructor of a hierarchical edge collection with a transformation\n" + "\n" + "This constructor creates an edge collection from the shapes delivered by the given recursive shape iterator.\n" + "It feeds the shapes from a hierarchy of cells into the hierarchical edge set.\n" + "The edges remain within their original hierachy unless other operations require the edges to be moved in the hierarchy.\n" + "The transformation is useful to scale to a specific database unit for example.\n" + "\n" + "Text objects are not inserted, because they cannot be converted to edges.\n" + "Edge objects are inserted as such. If \"as_edges\" is true, \"solid\" objects (boxes, polygons, paths) are converted to edges which " + "form the hull of these objects. If \"as_edges\" is false, solid objects are ignored.\n" + "\n" + "@code\n" + "dss = RBA::DeepShapeStore::new\n" + "layout = ... # a layout\n" + "cell = ... # the index of the initial cell\n" + "layer = ... # the index of the layer from where to take the shapes from\n" + "dbu = 0.1 # the target database unit\n" + "r = RBA::Edges::new(layout.begin_shapes(cell, layer), dss, RBA::ICplxTrans::new(layout.dbu / dbu), false)\n" + "@/code\n" + ) + + constructor ("new", &new_texts_as_dots1, gsi::arg("shape_iterator"), gsi::arg ("expr"), gsi::arg ("as_pattern", true), + "@brief Constructor from a text set\n" + "\n" + "@param shape_iterator The iterator from which to derive the texts\n" + "@param expr The selection string\n" + "@param as_pattern If true, the selection string is treated as a glob pattern. Otherwise the match is exact.\n" + "\n" + "This special constructor will create dot-like edges from the text objects delivered by the shape iterator. " + "Each text object will give a degenerated edge (a dot) that represents the text origin.\n" + "Texts can be selected by their strings - either through a glob pattern or by exact comparison with " + "the given string. The following options are available:\n" + "\n" + "@code\n" + "dots = RBA::Edges::new(iter, \"*\") # all texts\n" + "dots = RBA::Edges::new(iter, \"A*\") # all texts starting with an 'A'\n" + "dots = RBA::Edges::new(iter, \"A*\", false) # all texts exactly matchin 'A*'\n" + "@/code\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + constructor ("new", &new_texts_as_dots2, gsi::arg("shape_iterator"), gsi::arg ("dss"), gsi::arg ("expr"), gsi::arg ("as_pattern", true), + "@brief Constructor from a text set\n" + "\n" + "@param shape_iterator The iterator from which to derive the texts\n" + "@param dss The \\DeepShapeStore object that acts as a heap for hierarchical operations.\n" + "@param expr The selection string\n" + "@param as_pattern If true, the selection string is treated as a glob pattern. Otherwise the match is exact.\n" + "\n" + "This special constructor will create a deep edge set from the text objects delivered by the shape iterator. " + "Each text object will give a degenerated edge (a dot) that represents the text origin.\n" + "Texts can be selected by their strings - either through a glob pattern or by exact comparison with " + "the given string. The following options are available:\n" + "\n" + "@code\n" + "region = RBA::Region::new(iter, dss, \"*\") # all texts\n" + "region = RBA::Region::new(iter, dss, \"A*\") # all texts starting with an 'A'\n" + "region = RBA::Region::new(iter, dss, \"A*\", false) # all texts exactly matchin 'A*'\n" + "@/code\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + method ("insert_into", &db::Edges::insert_into, gsi::arg ("layout"), gsi::arg ("cell_index"), gsi::arg ("layer"), + "@brief Inserts this edge collection into the given layout, below the given cell and into the given layer.\n" + "If the edge collection is a hierarchical one, a suitable hierarchy will be built below the top cell or " + "and existing hierarchy will be reused.\n" + "\n" + "This method has been introduced in version 0.26." + ) + + method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"), "@brief Filter the edges by length\n" - "@args length, inverse\n" "Filters the edges in the edge collection by length. If \"inverse\" is false, only " "edges which have the given length are returned. If \"inverse\" is true, " "edges not having the given length are returned.\n" ) + - method_ext ("with_length", with_length2, + method_ext ("with_length", with_length2, gsi::arg ("min_length"), gsi::arg ("max_length"), gsi::arg ("inverse"), "@brief Filter the edges by length\n" - "@args min_length, max_length, inverse\n" "Filters the edges in the edge collection by length. If \"inverse\" is false, only " "edges which have a length larger or equal to \"min_length\" and less than \"max_length\" are " "returned. If \"inverse\" is true, " @@ -471,9 +599,8 @@ Class dec_Edges ("db", "Edges", "\n" "If you don't want to specify a lower or upper limit, pass nil to that parameter.\n" ) + - method_ext ("with_angle", with_angle1, + method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Filter the edges by orientation\n" - "@args length, inverse\n" "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " "edges which have the given angle to the x-axis are returned. If \"inverse\" is true, " "edges not having the given angle are returned.\n" @@ -484,92 +611,79 @@ Class dec_Edges ("db", "Edges", "horizontal = edges.with_orientation(0, true)\n" "@/code\n" ) + - method_ext ("with_angle", with_angle2, + method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), "@brief Filter the edges by orientation\n" - "@args min_angle, max_angle, inverse\n" "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " "edges which have an angle to the x-axis larger or equal to \"min_angle\" and less than \"max_angle\" are " "returned. If \"inverse\" is true, " "edges which do not conform to this criterion are returned." ) + - method ("insert", (void (db::Edges::*)(const db::Edge &)) &db::Edges::insert, + method ("insert", (void (db::Edges::*)(const db::Edge &)) &db::Edges::insert, gsi::arg ("edge"), "@brief Inserts an edge\n" - "@args edge\n" "\n" "Inserts the edge into the edge collection.\n" ) + - method ("insert", (void (db::Edges::*)(const db::Box &)) &db::Edges::insert, + method ("insert", (void (db::Edges::*)(const db::Box &)) &db::Edges::insert, gsi::arg ("box"), "@brief Inserts a box\n" - "@args box\n" "\n" "Inserts the edges that form the contour of the box into the edge collection.\n" ) + - method ("insert", (void (db::Edges::*)(const db::Polygon &)) &db::Edges::insert, + method ("insert", (void (db::Edges::*)(const db::Polygon &)) &db::Edges::insert, gsi::arg ("polygon"), "@brief Inserts a polygon\n" - "@args polygon\n" "\n" "Inserts the edges that form the contour of the polygon into the edge collection.\n" ) + - method ("insert", (void (db::Edges::*)(const db::SimplePolygon &)) &db::Edges::insert, + method ("insert", (void (db::Edges::*)(const db::SimplePolygon &)) &db::Edges::insert, gsi::arg ("polygon"), "@brief Inserts a simple polygon\n" - "@args polygon\n" "\n" "Inserts the edges that form the contour of the simple polygon into the edge collection.\n" ) + - method ("insert", (void (db::Edges::*)(const db::Path &)) &db::Edges::insert, + method ("insert", (void (db::Edges::*)(const db::Path &)) &db::Edges::insert, gsi::arg ("path"), "@brief Inserts a path\n" - "@args path\n" "\n" "Inserts the edges that form the contour of the path into the edge collection.\n" ) + - method_ext ("insert", &insert_e, + method_ext ("insert", &insert_e, gsi::arg ("edges"), "@brief Inserts all edges from the other edge collection into this one\n" - "@args edges\n" "This method has been introduced in version 0.25." ) + - method_ext ("insert", &insert_r, + method_ext ("insert", &insert_r, gsi::arg ("region"), "@brief Inserts a region\n" - "@args region\n" "Inserts the edges that form the contours of the polygons from the region into the edge collection.\n" "\n" "This method has been introduced in version 0.25." ) + - method_ext ("insert", &insert_s, + method_ext ("insert", &insert_s, gsi::arg ("shapes"), "@brief Inserts all edges from the shape collection into this edge collection\n" - "@args shapes\n" "This method takes each edge from the shape collection and " "insertes it into the region. \"Polygon-like\" objects are inserted as edges forming the contours of the polygons.\n" "Text objects are ignored.\n" "\n" "This method has been introduced in version 0.25." ) + - method_ext ("insert", &insert_st, + method_ext ("insert", &insert_st, gsi::arg ("shapes"), gsi::arg ("trans"), "@brief Inserts all edges from the shape collection into this edge collection (with transformation)\n" - "@args shapes\n" "This method acts as the version without transformation, but will apply the given " "transformation before inserting the edges.\n" "\n" "This method has been introduced in version 0.25." ) + - method_ext ("insert", &insert_st, + method_ext ("insert", &insert_st, gsi::arg ("shapes"), gsi::arg ("trans"), "@brief Inserts all edges from the shape collection into this edge collection with complex transformation\n" - "@args shapes\n" "This method acts as the version without transformation, but will apply the given " "complex transformation before inserting the edges.\n" "\n" "This method has been introduced in version 0.25." ) + - method_ext ("insert", &insert_si, + method_ext ("insert", &insert_si, gsi::arg ("shape_iterator"), "@brief Inserts all shapes delivered by the recursive shape iterator into this edge collection\n" - "@args shape_iterator\n" "\n" "For \"solid\" shapes (boxes, polygons, paths), this method inserts the edges that form the contour of the shape into the edge collection.\n" "Edge shapes are inserted as such.\n" "Text objects are not inserted, because they cannot be converted to polygons.\n" ) + - method_ext ("insert", &insert_si2, + method_ext ("insert", &insert_si2, gsi::arg ("shape_iterator"), gsi::arg ("trans"), "@brief Inserts all shapes delivered by the recursive shape iterator into this edge collection with a transformation\n" - "@args shape_iterator, trans\n" "\n" "For \"solid\" shapes (boxes, polygons, paths), this method inserts the edges that form the contour of the shape into the edge collection.\n" "Edge shapes are inserted as such.\n" @@ -577,13 +691,11 @@ Class dec_Edges ("db", "Edges", "This variant will apply the given transformation to the shapes. This is useful to scale the " "shapes to a specific database unit for example.\n" ) + - method_ext ("insert", &insert_a1, + method_ext ("insert", &insert_a1, gsi::arg ("polygons"), "@brief Inserts all polygons from the array into this edge collection\n" - "@args array\n" ) + - method_ext ("insert", &insert_a2, + method_ext ("insert", &insert_a2, gsi::arg ("edges"), "@brief Inserts all edges from the array into this edge collection\n" - "@args array\n" ) + method ("merge", (db::Edges &(db::Edges::*) ()) &db::Edges::merge, "@brief Merge the edges\n" @@ -603,30 +715,27 @@ Class dec_Edges ("db", "Edges", "Crossing edges are not merged.\n" "In contrast to \\merge, this method does not modify the edge collection but returns a merged copy.\n" ) + - method ("&", (db::Edges (db::Edges::*)(const db::Edges &) const) &db::Edges::operator&, + method ("&", (db::Edges (db::Edges::*)(const db::Edges &) const) &db::Edges::operator&, gsi::arg ("other"), "@brief Returns the boolean AND between self and the other edge collection\n" "\n" - "@args other\n" "@return The result of the boolean AND operation\n" "\n" "The boolean AND operation will return all parts of the edges in this collection which " "are coincident with parts of the edges in the other collection." "The result will be a merged edge collection.\n" ) + - method ("&=", (db::Edges &(db::Edges::*)(const db::Edges &)) &db::Edges::operator&=, + method ("&=", (db::Edges &(db::Edges::*)(const db::Edges &)) &db::Edges::operator&=, gsi::arg ("other"), "@brief Performs the boolean AND between self and the other edge collection\n" "\n" - "@args other\n" "@return The edge collection after modification (self)\n" "\n" "The boolean AND operation will return all parts of the edges in this collection which " "are coincident with parts of the edges in the other collection." "The result will be a merged edge collection.\n" ) + - method ("&", (db::Edges (db::Edges::*)(const db::Region &) const) &db::Edges::operator&, + method ("&", (db::Edges (db::Edges::*)(const db::Region &) const) &db::Edges::operator&, gsi::arg ("other"), "@brief Returns the parts of the edges inside the given region\n" "\n" - "@args other\n" "@return The edges inside the given region\n" "\n" "This operation returns the parts of the edges which are inside the given region.\n" @@ -636,10 +745,9 @@ Class dec_Edges ("db", "Edges", "\n" "This method has been introduced in version 0.24." ) + - method ("&=", (db::Edges &(db::Edges::*)(const db::Region &)) &db::Edges::operator&=, + method ("&=", (db::Edges &(db::Edges::*)(const db::Region &)) &db::Edges::operator&=, gsi::arg ("other"), "@brief Selects the parts of the edges inside the given region\n" "\n" - "@args other\n" "@return The edge collection after modification (self)\n" "\n" "This operation selects the parts of the edges which are inside the given region.\n" @@ -649,30 +757,27 @@ Class dec_Edges ("db", "Edges", "\n" "This method has been introduced in version 0.24." ) + - method ("-", (db::Edges (db::Edges::*)(const db::Edges &) const) &db::Edges::operator-, + method ("-", (db::Edges (db::Edges::*)(const db::Edges &) const) &db::Edges::operator-, gsi::arg ("other"), "@brief Returns the boolean NOT between self and the other edge collection\n" "\n" - "@args other\n" "@return The result of the boolean NOT operation\n" "\n" "The boolean NOT operation will return all parts of the edges in this collection which " "are not coincident with parts of the edges in the other collection." "The result will be a merged edge collection.\n" ) + - method ("-=", (db::Edges &(db::Edges::*)(const db::Edges &)) &db::Edges::operator-=, + method ("-=", (db::Edges &(db::Edges::*)(const db::Edges &)) &db::Edges::operator-=, gsi::arg ("other"), "@brief Performs the boolean NOT between self and the other edge collection\n" "\n" - "@args other\n" "@return The edge collection after modification (self)\n" "\n" "The boolean NOT operation will return all parts of the edges in this collection which " "are not coincident with parts of the edges in the other collection." "The result will be a merged edge collection.\n" ) + - method ("-", (db::Edges (db::Edges::*)(const db::Region &) const) &db::Edges::operator-, + method ("-", (db::Edges (db::Edges::*)(const db::Region &) const) &db::Edges::operator-, gsi::arg ("other"), "@brief Returns the parts of the edges outside the given region\n" "\n" - "@args other\n" "@return The edges outside the given region\n" "\n" "This operation returns the parts of the edges which are outside the given region.\n" @@ -682,10 +787,9 @@ Class dec_Edges ("db", "Edges", "\n" "This method has been introduced in version 0.24." ) + - method ("-=", (db::Edges &(db::Edges::*)(const db::Region &)) &db::Edges::operator-=, + method ("-=", (db::Edges &(db::Edges::*)(const db::Region &)) &db::Edges::operator-=, gsi::arg ("other"), "@brief Selects the parts of the edges outside the given region\n" "\n" - "@args other\n" "@return The edge collection after modification (self)\n" "\n" "This operation selects the parts of the edges which are outside the given region.\n" @@ -695,138 +799,123 @@ Class dec_Edges ("db", "Edges", "\n" "This method has been introduced in version 0.24." ) + - method ("^", &db::Edges::operator^, + method ("^", &db::Edges::operator^, gsi::arg ("other"), "@brief Returns the boolean XOR between self and the other edge collection\n" "\n" - "@args other\n" "@return The result of the boolean XOR operation\n" "\n" "The boolean XOR operation will return all parts of the edges in this and the other collection except " "the parts where both are coincident.\n" "The result will be a merged edge collection.\n" ) + - method ("^=", &db::Edges::operator^=, + method ("^=", &db::Edges::operator^=, gsi::arg ("other"), "@brief Performs the boolean XOR between self and the other edge collection\n" "\n" - "@args other\n" "@return The edge collection after modification (self)\n" "\n" "The boolean XOR operation will return all parts of the edges in this and the other collection except " "the parts where both are coincident.\n" "The result will be a merged edge collection.\n" ) + - method ("\\|", &db::Edges::operator|, + method ("\\|", &db::Edges::operator|, gsi::arg ("other"), "@brief Returns the boolean OR between self and the other edge set\n" "\n" - "@args other\n" "@return The resulting edge collection\n" "\n" "The boolean OR is implemented by merging the edges of both edge sets. To simply join the edge collections " "without merging, the + operator is more efficient." ) + - method ("\\|=", &db::Edges::operator|=, + method ("\\|=", &db::Edges::operator|=, gsi::arg ("other"), "@brief Performs the boolean OR between self and the other redge set\n" "\n" - "@args other\n" "@return The edge collection after modification (self)\n" "\n" "The boolean OR is implemented by merging the edges of both edge sets. To simply join the edge collections " "without merging, the + operator is more efficient." ) + - method ("+", &db::Edges::operator+, + method ("+", &db::Edges::operator+, gsi::arg ("other"), "@brief Returns the combined edge set of self and the other one\n" "\n" - "@args other\n" "@return The resulting edge set\n" "\n" "This operator adds the edges of the other edge set to self and returns a new combined edge set. " "This usually creates unmerged edge sets and edges may overlap. Use \\merge if you want to ensure the result edge set is merged.\n" ) + - method ("+=", &db::Edges::operator+=, + method ("+=", &db::Edges::operator+=, gsi::arg ("other"), "@brief Adds the edges of the other edge collection to self\n" "\n" - "@args other\n" "@return The edge set after modification (self)\n" "\n" "This operator adds the edges of the other edge set to self. " "This usually creates unmerged edge sets and edges may overlap. Use \\merge if you want to ensure the result edge set is merged.\n" ) + - method ("interacting", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_interacting, + method ("interacting", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_interacting, gsi::arg ("other"), "@brief Returns the edges of this edge collection which overlap or touch edges from the other edge collection\n" "\n" - "@args other\n" "@return A new edge collection containing the edges overlapping or touching edges from the other region\n" "\n" "This method does not merge the edges before they are selected. If you want to select coherent " "edges, make sure the edge collection is merged before this method is used.\n" ) + - method ("not_interacting", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_not_interacting, + method ("not_interacting", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_not_interacting, gsi::arg ("other"), "@brief Returns the edges of this edge collection which do not overlap or touch edges from the other edge collection\n" "\n" - "@args other\n" "@return A new edge collection containing the edges not overlapping or touching edges from the other region\n" "\n" "This method does not merge the edges before they are selected. If you want to select coherent " "edges, make sure the edge collection is merged before this method is used.\n" ) + - method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_interacting, + method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_interacting, gsi::arg ("other"), "@brief Selects the edges from this edge collection which overlap or touch edges from the other edge collection\n" "\n" - "@args other\n" "@return The edge collection after the edges have been selected (self)\n" "\n" "This method does not merge the edges before they are selected. If you want to select coherent " "edges, make sure the edge collection is merged before this method is used.\n" ) + - method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_not_interacting, + method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Edges &)) &db::Edges::select_not_interacting, gsi::arg ("other"), "@brief Selects the edges from this edge collection which do not overlap or touch edges from the other edge collection\n" "\n" - "@args other\n" "@return The edge collection after the edges have been selected (self)\n" "\n" "This method does not merge the edges before they are selected. If you want to select coherent " "edges, make sure the edge collection is merged before this method is used.\n" ) + - method ("interacting", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_interacting, + method ("interacting", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_interacting, gsi::arg ("other"), "@brief Returns the edges from this region which overlap or touch polygons from the region\n" "\n" - "@args other\n" "@return A new edge collection containing the edges overlapping or touching polygons from the region\n" "\n" "This method does not merge the edges before they are selected. If you want to select coherent " "edges, make sure the edge collection is merged before this method is used.\n" ) + - method ("not_interacting", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_not_interacting, + method ("not_interacting", (db::Edges (db::Edges::*) (const db::Region &) const) &db::Edges::selected_not_interacting, gsi::arg ("other"), "@brief Returns the edges from this region which do not overlap or touch polygons from the region\n" "\n" - "@args other\n" "@return A new edge collection containing the edges not overlapping or touching polygons from the region\n" "\n" "This method does not merge the edges before they are selected. If you want to select coherent " "edges, make sure the edge collection is merged before this method is used.\n" ) + - method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_interacting, + method ("select_interacting", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_interacting, gsi::arg ("other"), "@brief Selects the edges from this region which overlap or touch polygons from the region\n" "\n" - "@args other\n" "@return The edge collection after the edges have been selected (self)\n" "\n" "This method does not merge the edges before they are selected. If you want to select coherent " "edges, make sure the edge collection is merged before this method is used.\n" ) + - method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_not_interacting, + method ("select_not_interacting", (db::Edges &(db::Edges::*) (const db::Region &)) &db::Edges::select_not_interacting, gsi::arg ("other"), "@brief Selects the edges from this region which do not overlap or touch polygons from the region\n" "\n" - "@args other\n" "@return The edge collection after the edges have been selected (self)\n" "\n" "This method does not merge the edges before they are selected. If you want to select coherent " "edges, make sure the edge collection is merged before this method is used.\n" ) + - method ("inside_part", &db::Edges::inside_part, + method ("inside_part", &db::Edges::inside_part, gsi::arg ("other"), "@brief Returns the parts of the edges of this edge collection which are inside the polygons of the region\n" "\n" - "@args other\n" "@return A new edge collection containing the edge parts inside the region\n" "\n" "This operation returns the parts of the edges which are inside the given region.\n" @@ -836,10 +925,9 @@ Class dec_Edges ("db", "Edges", "\n" "This method has been introduced in version 0.24." ) + - method ("outside_part", &db::Edges::outside_part, + method ("outside_part", &db::Edges::outside_part, gsi::arg ("other"), "@brief Returns the parts of the edges of this edge collection which are outside the polygons of the region\n" "\n" - "@args other\n" "@return A new edge collection containing the edge parts outside the region\n" "\n" "This operation returns the parts of the edges which are not inside the given region.\n" @@ -849,10 +937,9 @@ Class dec_Edges ("db", "Edges", "\n" "This method has been introduced in version 0.24." ) + - method ("select_inside_part", &db::Edges::select_inside_part, + method ("select_inside_part", &db::Edges::select_inside_part, gsi::arg ("other"), "@brief Selects the parts of the edges from this edge collection which are inside the polygons of the given region\n" "\n" - "@args other\n" "@return The edge collection after the edges have been selected (self)\n" "\n" "This operation selects the parts of the edges which are inside the given region.\n" @@ -862,10 +949,9 @@ Class dec_Edges ("db", "Edges", "\n" "This method has been introduced in version 0.24." ) + - method ("select_outside_part", &db::Edges::select_outside_part, + method ("select_outside_part", &db::Edges::select_outside_part, gsi::arg ("other"), "@brief Selects the parts of the edges from this edge collection which are outside the polygons of the given region\n" "\n" - "@args other\n" "@return The edge collection after the edges have been selected (self)\n" "\n" "This operation selects the parts of the edges which are not inside the given region.\n" @@ -878,15 +964,13 @@ Class dec_Edges ("db", "Edges", method ("clear", &db::Edges::clear, "@brief Clears the edge collection\n" ) + - method ("swap", &db::Edges::swap, + method ("swap", &db::Edges::swap, gsi::arg ("other"), "@brief Swap the contents of this edge collection with the contents of another one\n" - "@args other\n" "This method is useful to avoid excessive memory allocation in some cases. " "For managed memory languages such as Ruby, those cases will be rare. " ) + - method_ext ("move", &move_p, + method_ext ("move", &move_p, gsi::arg ("v"), "@brief Moves the edge collection\n" - "@args v\n" "\n" "Moves the polygon by the given offset and returns the \n" "moved edge collection. The edge collection is overwritten.\n" @@ -897,9 +981,8 @@ Class dec_Edges ("db", "Edges", "\n" "Starting with version 0.25 the displacement type is a vector." ) + - method_ext ("move", &move_xy, + method_ext ("move", &move_xy, gsi::arg ("x"), gsi::arg ("y"), "@brief Moves the edge collection\n" - "@args x,y\n" "\n" "Moves the edge collection by the given offset and returns the \n" "moved edge collection. The edge collection is overwritten.\n" @@ -909,9 +992,8 @@ Class dec_Edges ("db", "Edges", "\n" "@return The moved edge collection (self).\n" ) + - method_ext ("moved", &moved_p, + method_ext ("moved", &moved_p, gsi::arg ("v"), "@brief Returns the moved edge collection (does not modify self)\n" - "@args v\n" "\n" "Moves the edge collection by the given offset and returns the \n" "moved edge collection. The edge collection is not modified.\n" @@ -922,9 +1004,8 @@ Class dec_Edges ("db", "Edges", "\n" "Starting with version 0.25 the displacement type is a vector." ) + - method_ext ("moved", &moved_xy, + method_ext ("moved", &moved_xy, gsi::arg ("x"), gsi::arg ("v"), "@brief Returns the moved edge collection (does not modify self)\n" - "@args x,y\n" "\n" "Moves the edge collection by the given offset and returns the \n" "moved edge collection. The edge collection is not modified.\n" @@ -934,9 +1015,8 @@ Class dec_Edges ("db", "Edges", "\n" "@return The moved edge collection.\n" ) + - method ("transformed", (db::Edges (db::Edges::*)(const db::Trans &) const) &db::Edges::transformed, + method ("transformed", (db::Edges (db::Edges::*)(const db::Trans &) const) &db::Edges::transformed, gsi::arg ("t"), "@brief Transform the edge collection\n" - "@args t\n" "\n" "Transforms the edge collection with the given transformation.\n" "Does not modify the edge collection but returns the transformed edge collection.\n" @@ -945,9 +1025,8 @@ Class dec_Edges ("db", "Edges", "\n" "@return The transformed edge collection.\n" ) + - method ("transformed|#transformed_icplx", (db::Edges (db::Edges::*)(const db::ICplxTrans &) const) &db::Edges::transformed, + method ("transformed|#transformed_icplx", (db::Edges (db::Edges::*)(const db::ICplxTrans &) const) &db::Edges::transformed, gsi::arg ("t"), "@brief Transform the edge collection with a complex transformation\n" - "@args t\n" "\n" "Transforms the edge collection with the given complex transformation.\n" "Does not modify the edge collection but returns the transformed edge collection.\n" @@ -956,9 +1035,8 @@ Class dec_Edges ("db", "Edges", "\n" "@return The transformed edge collection.\n" ) + - method ("transform", (db::Edges &(db::Edges::*)(const db::Trans &)) &db::Edges::transform, + method ("transform", (db::Edges &(db::Edges::*)(const db::Trans &)) &db::Edges::transform, gsi::arg ("t"), "@brief Transform the edge collection (modifies self)\n" - "@args t\n" "\n" "Transforms the edge collection with the given transformation.\n" "This version modifies the edge collection and returns a reference to self.\n" @@ -967,9 +1045,8 @@ Class dec_Edges ("db", "Edges", "\n" "@return The transformed edge collection.\n" ) + - method ("transform|#transform_icplx", (db::Edges &(db::Edges::*)(const db::ICplxTrans &)) &db::Edges::transform, + method ("transform|#transform_icplx", (db::Edges &(db::Edges::*)(const db::ICplxTrans &)) &db::Edges::transform, gsi::arg ("t"), "@brief Transform the edge collection with a complex transformation (modifies self)\n" - "@args t\n" "\n" "Transforms the edge collection with the given transformation.\n" "This version modifies the edge collection and returns a reference to self.\n" @@ -978,9 +1055,8 @@ Class dec_Edges ("db", "Edges", "\n" "@return The transformed edge collection.\n" ) + - method_ext ("width_check", &width1, + method_ext ("width_check", &width1, gsi::arg ("d"), "@brief Performs a width check between edges\n" - "@args other, d\n" "@param d The minimum width for which the edges are checked\n" "@param other The other edge collection against which to check\n" "To understand the overlap check for edges, one has to be familiar with the concept of the inside and outside " @@ -996,9 +1072,8 @@ Class dec_Edges ("db", "Edges", "A version of this method is available with more options (i.e. the option the deliver whole edges). " "Other checks with different edge relations are \\space_check, \\inside_check, \\overlap_check, \\separation_check and \\enclosing_check.\n" ) + - method_ext ("width_check", &width2, + method_ext ("width_check", &width2, gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs a width check with options\n" - "@args d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum width for which the edges are checked\n" "@param whole_edges If true, deliver the whole edges\n" "@param metrics Specify the metrics type\n" @@ -1026,9 +1101,8 @@ Class dec_Edges ("db", "Edges", "The projected length must be larger or equal to \"min_projection\" and less than \"max_projection\". " "If you don't want to specify one threshold, pass nil to the respective value.\n" ) + - method_ext ("space_check", &space1, + method_ext ("space_check", &space1, gsi::arg ("d"), "@brief Performs a space check between edges\n" - "@args d\n" "@param d The minimum distance for which the edges are checked\n" "To understand the space check for edges, one has to be familiar with the concept of the inside and outside " "interpretation of an edge. An edge is considered a boundary between \"inside\" and \"outside\" where \"inside\" " @@ -1043,9 +1117,8 @@ Class dec_Edges ("db", "Edges", "A version of this method is available with more options (i.e. the option the deliver whole edges). " "Other checks with different edge relations are \\width_check, \\inside_check, \\overlap_check, \\separation_check and \\enclosing_check.\n" ) + - method_ext ("space_check", &space2, + method_ext ("space_check", &space2, gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs a space check with options\n" - "@args d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum distance for which the edges are checked\n" "@param whole_edges If true, deliver the whole edges\n" "@param metrics Specify the metrics type\n" @@ -1073,9 +1146,8 @@ Class dec_Edges ("db", "Edges", "The projected length must be larger or equal to \"min_projection\" and less than \"max_projection\". " "If you don't want to specify one threshold, pass nil to the respective value.\n" ) + - method_ext ("inside_check", &inside1, + method_ext ("inside_check", &inside1, gsi::arg ("other"), gsi::arg ("d"), "@brief Performs an inside check between edges\n" - "@args other, d\n" "@param d The minimum distance for which the edges are checked\n" "@param other The other edge collection against which to check\n" "To understand the inside check for edges, one has to be familiar with the concept of the inside and outside " @@ -1091,9 +1163,8 @@ Class dec_Edges ("db", "Edges", "A version of this method is available with more options (i.e. the option the deliver whole edges). " "Other checks with different edge relations are \\width_check, \\space_check, \\overlap_check, \\separation_check and \\enclosing_check.\n" ) + - method_ext ("inside_check", &inside2, + method_ext ("inside_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs an inside check with options\n" - "@args other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum distance for which the edges are checked\n" "@param other The other edge collection against which to check\n" "@param whole_edges If true, deliver the whole edges\n" @@ -1122,9 +1193,8 @@ Class dec_Edges ("db", "Edges", "The projected length must be larger or equal to \"min_projection\" and less than \"max_projection\". " "If you don't want to specify one threshold, pass nil to the respective value.\n" ) + - method_ext ("enclosing_check", &enclosing1, + method_ext ("enclosing_check", &enclosing1, gsi::arg ("other"), gsi::arg ("d"), "@brief Performs an enclosing check between edges\n" - "@args other, d\n" "@param d The minimum distance for which the edges are checked\n" "@param other The other edge collection against which to check\n" "To understand the enclosing check for edges, one has to be familiar with the concept of the inside and outside " @@ -1140,9 +1210,8 @@ Class dec_Edges ("db", "Edges", "A version of this method is available with more options (i.e. the option the deliver whole edges). " "Other checks with different edge relations are \\width_check, \\space_check, \\overlap_check, \\separation_check and \\inside_check.\n" ) + - method_ext ("enclosing_check", &enclosing2, + method_ext ("enclosing_check", &enclosing2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs an enclosing check with options\n" - "@args other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum distance for which the edges are checked\n" "@param other The other edge collection against which to check\n" "@param whole_edges If true, deliver the whole edges\n" @@ -1171,9 +1240,8 @@ Class dec_Edges ("db", "Edges", "The projected length must be larger or equal to \"min_projection\" and less than \"max_projection\". " "If you don't want to specify one threshold, pass nil to the respective value.\n" ) + - method_ext ("overlap_check", &overlap1, + method_ext ("overlap_check", &overlap1, gsi::arg ("other"), gsi::arg ("d"), "@brief Performs an overlap check between edges\n" - "@args other, d\n" "@param d The minimum distance for which the edges are checked\n" "@param other The other edge collection against which to check\n" "Technically, the overlap check is a width check between edges from different collections. " @@ -1187,9 +1255,8 @@ Class dec_Edges ("db", "Edges", "A version of this method is available with more options (i.e. the option the deliver whole edges). " "Other checks with different edge relations are \\width_check, \\space_check, \\enclosing_check, \\separation_check and \\inside_check.\n" ) + - method_ext ("overlap_check", &overlap2, + method_ext ("overlap_check", &overlap2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs an overlap check with options\n" - "@args other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum distance for which the edges are checked\n" "@param other The other edge collection against which to check\n" "@param whole_edges If true, deliver the whole edges\n" @@ -1218,9 +1285,8 @@ Class dec_Edges ("db", "Edges", "The projected length must be larger or equal to \"min_projection\" and less than \"max_projection\". " "If you don't want to specify one threshold, pass nil to the respective value.\n" ) + - method_ext ("separation_check", &separation1, + method_ext ("separation_check", &separation1, gsi::arg ("other"), gsi::arg ("d"), "@brief Performs an separation check between edges\n" - "@args other, d\n" "@param d The minimum distance for which the edges are checked\n" "@param other The other edge collection against which to check\n" "Technically, the separation check is a space check between edges from different collections. " @@ -1234,9 +1300,8 @@ Class dec_Edges ("db", "Edges", "A version of this method is available with more options (i.e. the option the deliver whole edges). " "Other checks with different edge relations are \\width_check, \\space_check, \\enclosing_check, \\overlap_check and \\inside_check.\n" ) + - method_ext ("separation_check", &separation2, + method_ext ("separation_check", &separation2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs an overlap check with options\n" - "@args other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum distance for which the edges are checked\n" "@param other The other edge collection against which to check\n" "@param whole_edges If true, deliver the whole edges\n" @@ -1271,25 +1336,22 @@ Class dec_Edges ("db", "Edges", "The boxes will not be merged, so it is possible to determine overlaps " "of these boxes for example.\n" ) + - method_ext ("extents", &extents1, + method_ext ("extents", &extents1, gsi::arg ("d"), "@brief Returns a region with the enlarged bounding boxes of the edges\n" - "@args d\n" "This method will return a region consisting of the bounding boxes of the edges enlarged by the given distance d.\n" "The enlargement is specified per edge, i.e the width and height will be increased by 2*d.\n" "The boxes will not be merged, so it is possible to determine overlaps " "of these boxes for example.\n" ) + - method_ext ("extents", &extents2, + method_ext ("extents", &extents2, gsi::arg ("dx"), gsi::arg ("dy"), "@brief Returns a region with the enlarged bounding boxes of the edges\n" - "@args dx, dy\n" "This method will return a region consisting of the bounding boxes of the edges enlarged by the given distance dx in x direction and dy in y direction.\n" "The enlargement is specified per edge, i.e the width will be increased by 2*dx.\n" "The boxes will not be merged, so it is possible to determine overlaps " "of these boxes for example.\n" ) + - method_ext ("extended_in", &extended_in, + method_ext ("extended_in", &extended_in, gsi::arg ("e"), "@brief Returns a region with shapes representing the edges with the given width\n" - "@args e\n" "@param e The extension width\n" "@return A region containing the polygons representing these extended edges\n" "The edges are extended to the \"inside\" by the given distance \"e\". The distance will be applied to the right side " @@ -1298,9 +1360,8 @@ Class dec_Edges ("db", "Edges", "\n" "Other versions of this feature are \\extended_out and \\extended.\n" ) + - method_ext ("extended_out", &extended_out, + method_ext ("extended_out", &extended_out, gsi::arg ("e"), "@brief Returns a region with shapes representing the edges with the given width\n" - "@args e\n" "@param e The extension width\n" "@return A region containing the polygons representing these extended edges\n" "The edges are extended to the \"outside\" by the given distance \"e\". The distance will be applied to the left side " @@ -1309,9 +1370,8 @@ Class dec_Edges ("db", "Edges", "\n" "Other versions of this feature are \\extended_in and \\extended.\n" ) + - method_ext ("extended", &extended, + method_ext ("extended", &extended, gsi::arg ("b"), gsi::arg ("e"), gsi::arg ("o"), gsi::arg ("i"), gsi::arg ("join"), "@brief Returns a region with shapes representing the edges with the specified extensions\n" - "@args b, e, o, i, join\n" "@param b the parallel extension at the start point of the edge\n" "@param e the parallel extension at the end point of the edge\n" "@param o the perpendicular extension to the \"outside\" (left side as seen in the direction of the edge)\n" @@ -1327,9 +1387,8 @@ Class dec_Edges ("db", "Edges", "If join is true and edges form a closed loop, the b and e parameters are ignored and a rim polygon is created " "that forms the loop with the outside and inside extension given by o and i.\n" ) + - method ("start_segments", &db::Edges::start_segments, + method ("start_segments", &db::Edges::start_segments, gsi::arg ("length"), gsi::arg ("fraction"), "@brief Returns edges representing a part of the edge after the start point\n" - "@args length, fraction\n" "@return A new collection of edges representing the start part\n" "This method allows to specify the length of these segments in a twofold way: either as a fixed length or " "by specifying a fraction of the original length:\n" @@ -1345,9 +1404,8 @@ Class dec_Edges ("db", "Edges", "It is possible to specify 0 for both values. In this case, degenerated edges (points) are delivered which specify the " "start positions of the edges but can't participate in some functions.\n" ) + - method ("end_segments", &db::Edges::end_segments, + method ("end_segments", &db::Edges::end_segments, gsi::arg ("length"), gsi::arg ("fraction"), "@brief Returns edges representing a part of the edge before the end point\n" - "@args length, fraction\n" "@return A new collection of edges representing the end part\n" "This method allows to specify the length of these segments in a twofold way: either as a fixed length or " "by specifying a fraction of the original length:\n" @@ -1363,9 +1421,8 @@ Class dec_Edges ("db", "Edges", "It is possible to specify 0 for both values. In this case, degenerated edges (points) are delivered which specify the " "end positions of the edges but can't participate in some functions.\n" ) + - method ("centers", &db::Edges::centers, + method ("centers", &db::Edges::centers, gsi::arg ("length"), gsi::arg ("fraction"), "@brief Returns edges representing the center part of the edges\n" - "@args length, fraction\n" "@return A new collection of edges representing the part around the center\n" "This method allows to specify the length of these segments in a twofold way: either as a fixed length or " "by specifying a fraction of the original length:\n" @@ -1390,27 +1447,34 @@ Class dec_Edges ("db", "Edges", "\n" "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("length", &length2, + method_ext ("length", &length2, gsi::arg ("rect"), "@brief Returns the total length of all edges in the edge collection (restricted to a rectangle)\n" - "@args rect\n" "This version will compute the total length of all edges in the collection, restricting the computation to the given rectangle.\n" "Edges along the border are handled in a special way: they are counted when they are oriented with their inside " "side toward the rectangle (in other words: outside edges must coincide with the rectangle's border in order to be counted).\n" "\n" "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("members_of|#in", &in, + method_ext ("members_of|#in", &in, gsi::arg ("other"), "@brief Returns all edges which are members of the other edge collection\n" - "@args other\n" "This method returns all edges in self which can be found in the other edge collection as well with exactly the same " "geometry." ) + - method_ext ("not_members_of|#not_in", ¬_in, + method_ext ("not_members_of|#not_in", ¬_in, gsi::arg ("other"), "@brief Returns all edges which are not members of the other edge collection\n" - "@args other\n" "This method returns all edges in self which can not be found in the other edge collection with exactly the same " "geometry." ) + + method_ext ("is_deep?", &is_deep, + "@brief Returns true if the edge collection is a deep (hierarchical) one\n" + "\n" + "This method has been added in version 0.26." + ) + + method_ext ("data_id", &id, + "@brief Returns the data ID (a unique identifier for the underlying data storage)\n" + "\n" + "This method has been added in version 0.26." + ) + method ("is_merged?", &db::Edges::is_merged, "@brief Returns true if the edge collection is merged\n" "If the region is merged, coincident edges have been merged into single edges. You can ensure merged state " @@ -1432,25 +1496,39 @@ Class dec_Edges ("db", "Edges", "\n" "This method has been introduced in version 0.25." ) + - method ("[]", &db::Edges::nth, - "@brief Returns the nth edge of the edge collection\n" - "@args n\n" + method ("[]", &db::Edges::nth, gsi::arg ("n"), + "@brief Returns the nth edge of the collection\n" "\n" - "This method returns nil if the index is out of range.\n" + "This method returns nil if the index is out of range. It is available for flat edge collections only - i.e. " + "those for which \\has_valid_edges? is true. Use \\flatten to explicitly flatten an edge collection.\n" + "This method returns the raw edge (not merged edges, even if merged semantics is enabled).\n" + "\n" + "The \\each iterator is the more general approach to access the edges." + ) + + method ("flatten", &db::Edges::flatten, + "@brief Explicitly flattens an edge collection\n" + "\n" + "If the collection is already flat (i.e. \\has_valid_edges? returns true), this method will " + "not change it.\n" + "\n" + "This method has been introduced in version 0.26." + ) + + method ("has_valid_edges?", &db::Edges::has_valid_edges, + "@brief Returns true if the edge collection is flat and individual edges can be accessed randomly\n" + "\n" + "This method has been introduced in version 0.26." ) + method_ext ("to_s", &to_string0, "@brief Converts the edge collection to a string\n" "The length of the output is limited to 20 edges to avoid giant strings on large regions. " "For full output use \"to_s\" with a maximum count parameter.\n" ) + - method_ext ("to_s", &to_string1, + method_ext ("to_s", &to_string1, gsi::arg ("max_count"), "@brief Converts the edge collection to a string\n" - "@args max_count\n" "This version allows specification of the maximum number of edges contained in the string." ) + - method ("merged_semantics=", &db::Edges::set_merged_semantics, + method ("merged_semantics=", &db::Edges::set_merged_semantics, gsi::arg ("f"), "@brief Enable or disable merged semantics\n" - "@args f\n" "If merged semantics is enabled (the default), colinear, connected or overlapping edges will be considered\n" "as single edges.\n" ) + @@ -1458,9 +1536,8 @@ Class dec_Edges ("db", "Edges", "@brief Gets a flag indicating whether merged semantics is enabled\n" "See \\merged_semantics= for a description of this attribute.\n" ) + - method ("enable_progress", &db::Edges::enable_progress, + method ("enable_progress", &db::Edges::enable_progress, gsi::arg ("label"), "@brief Enable progress reporting\n" - "@args label\n" "After calling this method, the edge collection will report the progress through a progress bar while " "expensive operations are running.\n" "The label is a text which is put in front of the progress bar.\n" diff --git a/src/db/db/gsiDeclDbHierNetworkProcessor.cc b/src/db/db/gsiDeclDbHierNetworkProcessor.cc new file mode 100644 index 000000000..8aef6b9d0 --- /dev/null +++ b/src/db/db/gsiDeclDbHierNetworkProcessor.cc @@ -0,0 +1,66 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "gsiDecl.h" +#include "dbHierNetworkProcessor.h" + +namespace gsi +{ + +Class decl_dbConnectivity ("db", "Connectivity", + gsi::method ("connect", (void (db::Connectivity::*) (unsigned int)) &db::Connectivity::connect, gsi::arg ("layer"), + "@brief Specifies intra-layer connectivity.\n" + ) + + gsi::method ("connect", (void (db::Connectivity::*) (unsigned int, unsigned int)) &db::Connectivity::connect, gsi::arg ("layer_a"), gsi::arg ("layer_b"), + "@brief Specifies inter-layer connectivity.\n" + ) + + gsi::method ("connect_global", (size_t (db::Connectivity::*) (unsigned int, const std::string &)) &db::Connectivity::connect_global, gsi::arg ("layer"), gsi::arg ("global_net_name"), + "@brief Connects the given layer to the global net given by name.\n" + "Returns the ID of the global net." + ) + + gsi::method ("global_net_name", &db::Connectivity::global_net_name, gsi::arg ("global_net_id"), + "@brief Gets the name for a given global net ID.\n" + ) + + gsi::method ("global_net_id", &db::Connectivity::global_net_id, gsi::arg ("global_net_name"), + "@brief Gets the ID for a given global net name.\n" + ), + "@brief This class specifies connections between different layers.\n" + "Connections are build using \\connect. There are basically two flavours of connections: intra-layer and inter-layer.\n" + "\n" + "Intra-layer connections make nets begin propagated along different shapes on the same net. Without the " + "intra-layer connections, nets are not propagated over shape boundaries. As this is usually intended, intra-layer connections " + "should always be specified for each layer.\n" + "\n" + "Inter-layer connections connect shapes on different layers. Shapes which touch across layers will be connected if " + "their layers are specified as being connected through inter-layer \\connect.\n" + "\n" + "All layers are specified in terms of layer indexes. Layer indexes are layout layer indexes (see \\Layout class).\n" + "\n" + "The connectivity object also manages the global nets. Global nets are substrate for example " + "and they are propagated automatically from subcircuits to circuits. " + "Global nets are defined by name and are managed through IDs. To get the name for a given ID, use " + "\\global_net_name." + "\n" + "This class has been introduced in version 0.26.\n" +); + +} diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index a11e07a01..2a6d432a3 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -32,6 +32,7 @@ #include "dbLibraryManager.h" #include "dbPCellDeclaration.h" #include "dbHash.h" +#include "dbRegion.h" #include "tlStream.h" namespace gsi @@ -367,41 +368,24 @@ static std::vector multi_clip_into (db::Layout *l, db::cell return db::clip_layout(*l, *t, c, boxes, true); } -static unsigned int get_layer (db::Layout *l, const db::LayerProperties &lp) -{ - if (lp.is_null ()) { - // for a null layer info always create a layer - return l->insert_layer (); - } else { - // if we have a layer with the requested properties already, return this. - for (db::Layout::layer_iterator li = l->begin_layers (); li != l->end_layers (); ++li) { - if ((*li).second->log_equal (lp)) { - return (*li).first; - } - } - // otherwise create a new layer - return l->insert_layer (lp); - } -} - static unsigned int get_layer0 (db::Layout *l) { - return get_layer (l, db::LayerProperties ()); + return l->get_layer (db::LayerProperties ()); } static unsigned int get_layer1 (db::Layout *l, const std::string &name) { - return get_layer (l, db::LayerProperties (name)); + return l->get_layer (db::LayerProperties (name)); } static unsigned int get_layer2 (db::Layout *l, int ln, int dn) { - return get_layer (l, db::LayerProperties (ln, dn)); + return l->get_layer (db::LayerProperties (ln, dn)); } static unsigned int get_layer3 (db::Layout *l, int ln, int dn, const std::string &name) { - return get_layer (l, db::LayerProperties (ln, dn, name)); + return l->get_layer (db::LayerProperties (ln, dn, name)); } static tl::Variant find_layer (db::Layout *l, const db::LayerProperties &lp) @@ -1196,6 +1180,30 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.20.\n" ) + + gsi::method ("insert", (void (db::Layout::*) (db::cell_index_type, int, const db::Region &)) &db::Layout::insert, + gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), + "@brief Inserts a region into the given cell and layer\n" + "If the region is (conceptionally) a flat region, it will be inserted into the cell's shapes " + "list as a flat sequence of polygons.\n" + "If the region is a deep (hierarchical) region, it will create a subhierarchy below the given " + "cell and it's shapes will be put into the respective cells. Suitable subcells will be picked " + "for inserting the shapes. If a hierarchy already exists below the given cell, the algorithm will " + "try to reuse this hierarchy.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + gsi::method ("insert", (void (db::Layout::*) (db::cell_index_type, int, const db::Edges &)) &db::Layout::insert, + gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("edges"), + "@brief Inserts an edge collection into the given cell and layer\n" + "If the edge collection is (conceptionally) flat, it will be inserted into the cell's shapes " + "list as a flat sequence of edges.\n" + "If the edge collection is deep (hierarchical), it will create a subhierarchy below the given " + "cell and it's edges will be put into the respective cells. Suitable subcells will be picked " + "for inserting the edges. If a hierarchy already exists below the given cell, the algorithm will " + "try to reuse this hierarchy.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + gsi::method_ext ("flatten", &flatten, "@brief Flattens the given cell\n" "@args cell_index, levels, prune\n" @@ -1297,7 +1305,7 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.25.\n" ) + - gsi::method_ext ("layer", &get_layer, + gsi::method ("layer", &db::Layout::get_layer, "@brief Finds or creates a layer with the given properties\n" "@args info\n" "\n" diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc new file mode 100644 index 000000000..2ce0807eb --- /dev/null +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -0,0 +1,513 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "gsiDecl.h" +#include "dbLayoutToNetlist.h" +#include "dbLayoutToNetlistWriter.h" +#include "dbLayoutToNetlistReader.h" +#include "tlStream.h" +#include "tlVariant.h" + +namespace gsi +{ + +static db::LayoutToNetlist *make_l2n (const db::RecursiveShapeIterator &iter) +{ + return new db::LayoutToNetlist (iter); +} + +static db::LayoutToNetlist *make_l2n_default () +{ + return new db::LayoutToNetlist (); +} + +static db::LayoutToNetlist *make_l2n_from_existing_dss_with_layout (db::DeepShapeStore *dss, unsigned int layout_index) +{ + return new db::LayoutToNetlist (dss, layout_index); +} + +static db::LayoutToNetlist *make_l2n_from_existing_dss (db::DeepShapeStore *dss) +{ + return new db::LayoutToNetlist (dss); +} + +static db::LayoutToNetlist *make_l2n_flat (const std::string &topcell_name, double dbu) +{ + return new db::LayoutToNetlist (topcell_name, dbu); +} + +static db::Layout *l2n_internal_layout (db::LayoutToNetlist *l2n) +{ + // although this isn't very clean, we dare to do so as const references are pretty useless in script languages. + return const_cast (l2n->internal_layout ()); +} + +static db::Cell *l2n_internal_top_cell (db::LayoutToNetlist *l2n) +{ + // although this isn't very clean, we dare to do so as const references are pretty useless in script languages. + return const_cast (l2n->internal_top_cell ()); +} + +static void build_net (const db::LayoutToNetlist *l2n, const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) +{ + std::string p = circuit_cell_name_prefix.to_string (); + std::string dp = device_cell_name_prefix.to_string (); + l2n->build_net (net, target, target_cell, lmap, circuit_cell_name_prefix.is_nil () ? 0 : p.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); +} + +static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) +{ + std::string cp = circuit_cell_name_prefix.to_string (); + std::string np = net_cell_name_prefix.to_string (); + std::string dp = device_cell_name_prefix.to_string (); + l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); +} + +static void write_l2n (const db::LayoutToNetlist *l2n, const std::string &path, bool short_format) +{ + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, short_format); + writer.write (l2n); +} + +static void read_l2n (db::LayoutToNetlist *l2n, const std::string &path) +{ + tl::InputStream stream (path); + db::LayoutToNetlistStandardReader reader (stream); + reader.read (l2n); +} + +static std::vector l2n_layer_names (const db::LayoutToNetlist *l2n) +{ + std::vector ln; + for (db::LayoutToNetlist::layer_iterator l = l2n->begin_layers (); l != l2n->end_layers (); ++l) { + ln.push_back (l->second); + } + return ln; +} + +static db::Region antenna_check (db::LayoutToNetlist *l2n, const db::Region &poly, const db::Region &metal, double ratio, const std::vector &diodes) +{ + std::vector > diode_pairs; + + for (std::vector::const_iterator d = diodes.begin (); d != diodes.end (); ++d) { + + if (d->is_user ()) { + + diode_pairs.push_back (std::make_pair (& d->to_user (), 0.0)); + + } else if (d->is_list ()) { + + const std::vector &list = d->get_list (); + if (list.size () != 2) { + throw tl::Exception (tl::to_string (tr ("Diode layer specifications of 'antenna' method require list of diode layer/ratio pairs (e.g. '[ [ diode_layer, 10.0 ], ... ]')"))); + } + if (! list [0].is_user ()) { + throw tl::Exception (tl::to_string (tr ("Diode layer specifications of 'antenna' method require list of diode layer/ratio pairs (e.g. '[ [ diode_layer, 10.0 ], ... ]') - first element isn't a Region object"))); + } + if (! list [1].can_convert_to_double ()) { + throw tl::Exception (tl::to_string (tr ("Diode layer specifications of 'antenna' method require list of diode layer/ratio pairs (e.g. '[ [ diode_layer, 10.0 ], ... ]') - second element isn't a number"))); + } + + diode_pairs.push_back (std::make_pair (& list [0].to_user (), list [1].to_double ())); + + } + + } + + return l2n->antenna_check (poly, metal, ratio, diode_pairs); +} + +Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", + gsi::constructor ("new", &make_l2n, gsi::arg ("iter"), + "@brief Creates a new extractor connected to an original layout\n" + "This constructor will attach the extractor to an original layout through the " + "shape iterator.\n" + ) + + gsi::constructor ("new", &make_l2n_default, + "@brief Creates a new and empty extractor object\n" + "The main objective for this constructor is to create an object suitable for reading an annotated netlist.\n" + ) + + gsi::constructor ("new", &make_l2n_from_existing_dss, gsi::arg ("dss"), + "@brief Creates a new extractor object reusing an existing \\DeepShapeStore object\n" + "This constructor can be used if there is a DSS object already from which the " + "shapes can be taken. This version can only be used with \\register to " + "add layers (regions) inside the 'dss' object.\n" + "\n" + "The make_... methods will not create new layers as there is no particular place " + "defined where to create the layers." + ) + + gsi::constructor ("new", &make_l2n_from_existing_dss_with_layout, gsi::arg ("dss"), gsi::arg ("layout_index"), + "@brief Creates a new extractor object reusing an existing \\DeepShapeStore object\n" + "This constructor can be used if there is a DSS object already from which the " + "shapes can be taken. NOTE: in this case, the make_... functions will create " + "new layers inside this DSS. To register existing layers (regions) use \\register.\n" + ) + + gsi::constructor ("new", &make_l2n_flat, gsi::arg ("topcell_name"), gsi::arg ("dbu"), + "@brief Creates a new extractor object with a flat DSS\n" + "@param topcell_name The name of the top cell of the internal flat layout\n" + "@param dbu The database unit to use for the internal flat layout\n" + "\n" + "This constructor will create an extractor for flat extraction. Layers registered " + "with \\register will be flattened. New layers created with make_... will be flat " + "layers.\n" + "\n" + "The database unit is mandatory because the physical parameter extraction " + "for devices requires this unit for translation of layout to physical dimensions.\n" + ) + + gsi::method ("threads=", &db::LayoutToNetlist::set_threads, gsi::arg ("n"), + "@brief Sets the number of threads to use for operations which support multiple threads\n" + ) + + gsi::method ("threads", &db::LayoutToNetlist::threads, + "@brief Gets the number of threads to use for operations which support multiple threads\n" + ) + + gsi::method ("area_ratio=", &db::LayoutToNetlist::set_area_ratio, gsi::arg ("r"), + "@brief Sets the area_ratio parameter for the hierarchical network processor\n" + "This parameter controls splitting of large polygons in order to reduce the\n" + "error made by the bounding box approximation.\n" + ) + + gsi::method ("area_ratio", &db::LayoutToNetlist::area_ratio, + "@brief Gets the area_ratio parameter for the hierarchical network processor\n" + "See \\area_ratio= for details about this attribute." + ) + + gsi::method ("max_vertex_count=", &db::LayoutToNetlist::set_max_vertex_count, gsi::arg ("n"), + "@brief Sets the max_vertex_count parameter for the hierarchical network processor\n" + "This parameter controls splitting of large polygons in order to enhance performance\n" + "for very big polygons.\n" + ) + + gsi::method ("max_vertex_count", &db::LayoutToNetlist::max_vertex_count, + "See \\max_vertex_count= for details about this attribute." + ) + + gsi::method ("name", (std::string (db::LayoutToNetlist::*) (const db::Region ®ion) const) &db::LayoutToNetlist::name, gsi::arg ("l"), + "@brief Get the name of the given layer\n" + ) + + gsi::method ("name", (std::string (db::LayoutToNetlist::*) (unsigned int) const) &db::LayoutToNetlist::name, gsi::arg ("l"), + "@brief Get the name of the given layer (by index)\n" + ) + + gsi::method ("register", (void (db::LayoutToNetlist::*) (const db::Region ®ion, const std::string &)) &db::LayoutToNetlist::register_layer, gsi::arg ("l"), gsi::arg ("n"), + "@brief Names the given layer\n" + "'l' must be a hierarchical region derived with \\make_layer, \\make_text_layer or \\make_polygon_layer or " + "a region derived from those by boolean operations or other hierarchical operations.\n" + "\n" + "Naming a layer allows the system to indicate the layer in various contexts, i.e. " + "when writing the data to a file. Named layers are also persisted inside the LayoutToNetlist object. " + "They are not discarded when the Region object is destroyed. Only named layers can be put into " + "\\connect.\n" + ) + + gsi::method_ext ("layer_names", &l2n_layer_names, + "@brief Returns a list of names of the layer kept inside the LayoutToNetlist object." + ) + + gsi::factory ("layer_by_name", &db::LayoutToNetlist::layer_by_name, gsi::arg ("name"), + "@brief Gets a layer object for the given name.\n" + "The returned object is a copy which represents the named layer." + ) + + gsi::factory ("layer_by_index", &db::LayoutToNetlist::layer_by_index, gsi::arg ("index"), + "@brief Gets a layer object for the given index.\n" + "Only named layers can be retrieved with this method. " + "The returned object is a copy which represents the named layer." + ) + + gsi::method ("is_persisted?", &db::LayoutToNetlist::is_persisted, gsi::arg ("layer"), + "@brief Returns true, if the given layer is a persisted region.\n" + "Persisted layers are kept inside the LayoutToNetlist object and are not released " + "if their object is destroyed. Named layers are persisted, unnamed layers are not. " + "Only persisted, named layers can be put into \\connect." + ) + + gsi::factory ("make_layer", (db::Region *(db::LayoutToNetlist::*) (const std::string &)) &db::LayoutToNetlist::make_layer, gsi::arg ("name", std::string ()), + "@brief Creates a new, empty hierarchical region\n" + "\n" + "The name is optional. If given, the layer will already be named accordingly (see \\register).\n" + ) + + gsi::factory ("make_layer", (db::Region *(db::LayoutToNetlist::*) (unsigned int, const std::string &)) &db::LayoutToNetlist::make_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), + "@brief Creates a new hierarchical region representing an original layer\n" + "'layer_index' is the layer index of the desired layer in the original layout.\n" + "This variant produces polygons and takes texts for net name annotation.\n" + "A variant not taking texts is \\make_polygon_layer. A Variant only taking\n" + "texts is \\make_text_layer.\n" + "\n" + "The name is optional. If given, the layer will already be named accordingly (see \\register).\n" + ) + + gsi::factory ("make_text_layer", &db::LayoutToNetlist::make_text_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), + "@brief Creates a new region representing an original layer taking texts only\n" + "See \\make_layer for details.\n" + "\n" + "The name is optional. If given, the layer will already be named accordingly (see \\register).\n" + ) + + gsi::factory ("make_polygon_layer", &db::LayoutToNetlist::make_polygon_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), + "@brief Creates a new region representing an original layer taking polygons and texts\n" + "See \\make_layer for details.\n" + "\n" + "The name is optional. If given, the layer will already be named accordingly (see \\register).\n" + ) + + gsi::method ("extract_devices", &db::LayoutToNetlist::extract_devices, gsi::arg ("extractor"), gsi::arg ("layers"), + "@brief Extracts devices\n" + "See the class description for more details.\n" + "This method will run device extraction for the given extractor. The layer map is specific\n" + "for the extractor and uses the region objects derived with \\make_layer and it's variants.\n" + "\n" + "In addition, derived regions can be passed too. Certain limitations apply. It's safe to use\n" + "boolean operations for deriving layers. Other operations are applicable as long as they are\n" + "capable of delivering hierarchical layers.\n" + "\n" + "If errors occur, the device extractor will contain theses errors.\n" + ) + + gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("l"), + "@brief Defines an intra-layer connection for the given layer.\n" + "The layer is either an original layer created with \\make_layer and it's variants or\n" + "a derived layer. Certain limitations apply. It's safe to use\n" + "boolean operations for deriving layers. Other operations are applicable as long as they are\n" + "capable of delivering hierarchical layers.\n" + ) + + gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &, const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("a"), gsi::arg ("b"), + "@brief Defines an inter-layer connection for the given layers.\n" + "The conditions mentioned with intra-layer \\connect apply for this method too.\n" + ) + + gsi::method ("connect_global", (void (db::LayoutToNetlist::*) (const db::Region &, const std::string &)) &db::LayoutToNetlist::connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"), + "@brief Defines a connection of the given layer with a global net.\n" + "This method returns the ID of the global net. Use \\global_net_name to get " + "the name back from the ID." + ) + + gsi::method ("global_net_name", &db::LayoutToNetlist::global_net_name, gsi::arg ("global_net_id"), + "@brief Gets the global net name for the given global net ID." + ) + + gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, gsi::arg ("join_nets_by_label", true), + "@brief Runs the netlist extraction\n" + "If join_nets_by_label is true, nets on the same hierarchy level carrying the same label will be connected " + "implicitly even if there is no physical connection.\n" + "See the class description for more details.\n" + ) + + gsi::method_ext ("internal_layout", &l2n_internal_layout, + "@brief Gets the internal layout\n" + "Usually it should not be required to obtain the internal layout. If you need to do so, make sure not to modify the layout as\n" + "the functionality of the netlist extractor depends on it." + ) + + gsi::method_ext ("internal_top_cell", &l2n_internal_top_cell, + "@brief Gets the internal top cell\n" + "Usually it should not be required to obtain the internal cell. If you need to do so, make sure not to modify the cell as\n" + "the functionality of the netlist extractor depends on it." + ) + + gsi::method ("layer_of", &db::LayoutToNetlist::layer_of, gsi::arg ("l"), + "@brief Gets the internal layer for a given extraction layer\n" + "This method is required to derive the internal layer index - for example for\n" + "investigating the cluster tree.\n" + ) + + gsi::method ("cell_mapping_into", &db::LayoutToNetlist::cell_mapping_into, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("with_device_cells", false), + "@brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.\n" + "If 'with_device_cells' is true, cells will be produced for devices. These are cells not corresponding to circuits, so they are disabled normally.\n" + "Use this option, if you want to access device terminal shapes per device.\n" + "CAUTION: this function may create new cells in 'layout'.\n" + ) + + gsi::method ("const_cell_mapping_into", &db::LayoutToNetlist::const_cell_mapping_into, gsi::arg ("layout"), gsi::arg ("cell"), + "@brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.\n" + "This version will not create new cells in the target layout.\n" + "If the required cells do not exist there yet, flatting will happen.\n" + ) + + gsi::method ("netlist", &db::LayoutToNetlist::netlist, + "@brief gets the netlist extracted (0 if no extraction happened yet)\n" + ) + + gsi::factory ("shapes_of_net", (db::Region *(db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), + "@brief Returns all shapes of a specific net and layer.\n" + "If 'recursive'' is true, the returned region will contain the shapes of\n" + "all subcircuits too.\n" + ) + + gsi::method ("shapes_of_net", (void (db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool, db::Shapes &) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), gsi::arg ("to"), + "@brief Sends all shapes of a specific net and layer to the given Shapes container.\n" + "If 'recursive'' is true, the returned region will contain the shapes of\n" + "all subcircuits too.\n" + ) + + gsi::method_ext ("build_net", &build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), + "@brief Builds a net representation in the given layout and cell\n" + "\n" + "This method has two modes: recursive and top-level mode. In recursive mode,\n" + "it will create a proper hierarchy below the given target cell to hold all subcircuits the\n" + "net connects to. It will copy the net's parts from this subcircuits into these cells.\n" + "\n" + "In top-level mode, only the shapes from the net inside it's circuit are copied to\n" + "the given target cell. No other cells are created.\n" + "\n" + "Recursive mode is picked when a circuit cell name prefix is given. The new cells will be\n" + "named like circuit_cell_name_prefix + circuit name.\n" + "\n" + "If a device cell name prefix is given, device shapes will be output on device cells named\n" + "like device_cell_name_prefix + device name.\n" + "\n" + "@param target The target layout\n" + "@param target_cell The target cell\n" + "@param lmap Target layer indexes (keys) and net regions (values)\n" + "@param circuit_cell_name_prefix Chooses recursive mode if non-nil\n" + "@param device_cell_name_prefix If given, devices will be output as separate cells\n" + ) + + gsi::method_ext ("build_all_nets", &build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), + "@brief Builds a full hierarchical representation of the nets\n" + "\n" + "This method copies all nets into cells corresponding to the circuits. It uses the cmap\n" + "object to determine the target cell (create them with \\cell_mapping_into or \\const_cell_mapping_into.\n" + "If no mapping is requested, the specific circuit it skipped.\n" + "\n" + "The method has two net annotation modes:\n" + "\n" + "@ul\n" + "@li 'No annotation'' (net_cell_name_prefix == 0): the shapes will be put into the target cell simply @/li\n" + "@li Individual subcells per net (net_cell_name_prefix != 0): for each net, a subcell is created\n" + " and the net shapes will be put there (name of the subcell = net_cell_name_prefix + net name). @/li\n" + "@/ul\n" + "\n" + "In addition, net hierarchy is covered in two ways:\n" + "\n" + "@ul\n" + "@li No connection indicated (circuit_cell_name_prefix == 0: the net shapes are simply put into their\n" + " respective circuits. The connections are not indicated. @/li\n" + "@li Subnet hierarchy (circuit_cell_name_prefix != 0): for each root net, a full hierarchy is built\n" + " to accomodate the subnets (see build_net in recursive mode). @/li\n" + "@/ul\n" + "\n" + "If a device name prefix is given, device shapes will be output on device cells named\n" + "like device_name_prefix + device name.\n" + "\n" + "@param cmap The mapping of internal layout to target layout for the circuit mapping\n" + "@param target The target layout\n" + "@param lmap Target layer indexes (keys) and net regions (values)\n" + "@param net_cell_name_prefix See method description\n" + "@param circuit_cell_name_prefix See method description\n" + "@param device_cell_name_prefix If given, devices will be output as separate cells\n" + ) + + gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), + "@brief Finds the net by probing a specific location on the given layer\n" + "\n" + "This method will find a net looking at the given layer at the specific position.\n" + "It will traverse the hierarchy below if no shape in the requested layer is found\n" + "in the specified location. The function will report the topmost net from far above the\n" + "hierarchy of circuits as possible.\n" + "\n" + "If no net is found at all, 0 is returned.\n" + "\n" + "It is recommended to use \\probe on the netlist right after extraction.\n" + "Optimization functions such as \\Netlist#purge will remove parts of the net which means\n" + "shape to net probing may no longer work for these nets.\n" + "\n" + "This variant accepts a micrometer-unit location. The location is given in the\n" + "coordinate space of the initial cell.\n" + ) + + gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::Point &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), + "@brief Finds the net by probing a specific location on the given layer\n" + "See the description of the other \\probe_net variant.\n" + "This variant accepts a database-unit location. The location is given in the\n" + "coordinate space of the initial cell.\n" + ) + + gsi::method_ext ("write", &write_l2n, gsi::arg ("path"), gsi::arg ("short_format", false), + "@brief Writes the extracted netlist to a file.\n" + "This method employs the native format of KLayout.\n" + ) + + gsi::method_ext ("read", &read_l2n, gsi::arg ("path"), + "@brief Reads the extracted netlist from the file.\n" + "This method employs the native format of KLayout.\n" + ) + + gsi::method_ext ("antenna_check", &antenna_check, gsi::arg ("gate"), gsi::arg ("metal"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector (), "[]"), + "@brief Runs an antenna check on the extracted clusters\n" + "\n" + "The antenna check will traverse all clusters and run an antenna check\n" + "for all root clusters. The antenna ratio is defined by the total\n" + "area of all \"metal\" shapes divided by the total area of all \"gate\" shapes\n" + "on the cluster. Of all clusters where the antenna ratio is larger than\n" + "the limit ratio all metal shapes are copied to the output region as\n" + "error markers.\n" + "\n" + "The simple call is:\n" + "\n" + "@code\n" + "l2n = ... # a LayoutToNetlist object\n" + "l2n.extract_netlist\n" + "# check for antenna ratio 10.0 of metal vs. poly:\n" + "errors = l2n.antenna(poly, metal, 10.0)\n" + "@/code\n" + "\n" + "You can include diodes which rectify the antenna effect. " + "Provide recognition layers for theses diodes and include them " + "in the connections. Then specify the diode layers in the antenna call:\n" + "\n" + "@code\n" + "...\n" + "# include diode_layer1:\n" + "errors = l2n.antenna(poly, metal, 10.0, [ diode_layer1 ])\n" + "# include diode_layer1 and diode_layer2:" + "errors = l2n.antenna(poly, metal, 10.0, [ diode_layer1, diode_layer2 ])\n" + "@/code\n" + "\n" + "Diodes can be configured to partially reduce the antenna effect depending " + "on their area. This will make the diode_layer1 increase the ratio by 50.0 " + "per square micrometer area of the diode:\n" + "\n" + "@code\n" + "...\n" + "# diode_layer1 increases the ratio by 50 per sqaure micrometer area:\n" + "errors = l2n.antenna(poly, metal, 10.0 [ [ diode_layer, 50.0 ] ])\n" + "@/code\n" + ), + "@brief A generic framework for extracting netlists from layouts\n" + "\n" + "This class wraps various concepts from db::NetlistExtractor and db::NetlistDeviceExtractor\n" + "and more. It is supposed to provide a framework for extracting a netlist from a layout.\n" + "\n" + "The use model of this class consists of five steps which need to be executed in this order.\n" + "\n" + "@ul\n" + "@li Configuration: in this step, the LayoutToNetlist object is created and\n" + " if required, configured. Methods to be used in this step are \\threads=,\n" + " \\area_ratio= or \\max_vertex_count=. The constructor for the LayoutToNetlist\n" + " object receives a \\RecursiveShapeIterator object which basically supplies the\n" + " hierarchy and the layout taken as input.\n" + "@/li\n" + "@li Preparation\n" + " In this step, the device recognitions and extraction layers are drawn from\n" + " the framework. Derived can now be computed using boolean operations.\n" + " Methods to use in this step are \\make_layer and it's variants.\n" + " Layer preparation is not necessarily required to happen before all\n" + " other steps. Layers can be computed shortly before they are required.\n" + "@/li\n" + "@li Following the preparation, the devices can be extracted using \\extract_devices.\n" + " This method needs to be called for each device extractor required. Each time,\n" + " a device extractor needs to be given plus a map of device layers. The device\n" + " layers are device extractor specific. Either original or derived layers\n" + " may be specified here. Layer preparation may happen between calls to \\extract_devices.\n" + "@/li\n" + "@li Once the devices are derived, the netlist connectivity can be defined and the\n" + " netlist extracted. The connectivity is defined with \\connect and it's\n" + " flavours. The actual netlist extraction happens with \\extract_netlist.\n" + "@/li\n" + "@li After netlist extraction, the information is ready to be retrieved.\n" + " The produced netlist is available with \\netlist. The Shapes of a\n" + " specific net are available with \\shapes_of_net. \\probe_net allows\n" + " finding a net by probing a specific location.\n" + "@/li\n" + "@/ul\n" + "\n" + "You can also use the extractor with an existing \\DeepShapeStore object " + "or even flat data. In this case, preparation means importing existing regions " + "with the \\register method.\n" + "If you want to use the \\LayoutToNetlist object with flat data, use the " + "'LayoutToNetlist(topcell, dbu)' constructor. If you want to use it with " + "hierarchical data and an existing DeepShapeStore object, use the " + "'LayoutToNetlist(dss)' constructor.\n" + "\n" + "This class has been introduced in version 0.26." +); + +} diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc new file mode 100644 index 000000000..0b5b4d0d8 --- /dev/null +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -0,0 +1,1233 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "gsiDecl.h" +#include "dbNetlist.h" +#include "dbNetlistWriter.h" +#include "dbNetlistSpiceWriter.h" +#include "tlException.h" +#include "tlInternational.h" +#include "tlStream.h" + +namespace gsi +{ + +Class decl_dbPin ("db", "Pin", + gsi::method ("id", &db::Pin::id, + "@brief Gets the ID of the pin.\n" + ) + + gsi::method ("name", &db::Pin::name, + "@brief Gets the name of the pin.\n" + ), + "@brief A pin of a circuit.\n" + "Pin objects are used to describe the outgoing pins of " + "a circuit. To create a new pin of a circuit, use \\Circuit#create_pin.\n" + "\n" + "This class has been added in version 0.26." +); + +static void device_connect_terminal_by_name (db::Device *device, const std::string &terminal_name, db::Net *net) +{ + if (! device->device_class ()) { + throw tl::Exception (tl::to_string (tr ("Device does not have a device class"))); + } + size_t terminal_id = device->device_class ()->terminal_id_for_name (terminal_name); + device->connect_terminal (terminal_id, net); +} + +static void device_disconnect_terminal (db::Device *device, size_t terminal_id) +{ + device->connect_terminal (terminal_id, 0); +} + +static void device_disconnect_terminal_by_name (db::Device *device, const std::string &terminal_name) +{ + device_connect_terminal_by_name (device, terminal_name, 0); +} + +Class decl_dbDevice ("db", "Device", + gsi::method ("device_class", &db::Device::device_class, + "@brief Gets the device class the device belongs to.\n" + ) + + gsi::method ("device_abstract", &db::Device::device_abstract, + "@brief Gets the device abstract for this device instance.\n" + "See \\DeviceAbstract for more details.\n" + ) + + gsi::method ("circuit", (db::Circuit *(db::Device::*) ()) &db::Device::circuit, + "@brief Gets the circuit the device lives in." + ) + + gsi::method ("id", &db::Device::id, + "@brief Gets the device ID.\n" + "The ID is a unique integer which identifies the device.\n" + "It can be used to retrieve the device from the circuit using \\Circuit#device_by_id.\n" + "When assigned, the device ID is not 0.\n" + ) + + gsi::method ("name=", &db::Device::set_name, gsi::arg ("name"), + "@brief Sets the name of the device.\n" + "Device names are used to name a device inside a netlist file. " + "Device names should be unique within a circuit." + ) + + gsi::method ("name", &db::Device::name, + "@brief Gets the name of the device.\n" + ) + + gsi::method ("expanded_name", &db::Device::expanded_name, + "@brief Gets the expanded name of the device.\n" + "The expanded name takes the name of the device. If the name is empty, the numeric ID will be used to build a name. " + ) + + gsi::method ("net_for_terminal", (db::Net *(db::Device::*) (size_t)) &db::Device::net_for_terminal, gsi::arg ("terminal_id"), + "@brief Gets the net connected to the specified terminal.\n" + "If the terminal is not connected, nil is returned for the net." + ) + + gsi::method ("connect_terminal", &db::Device::connect_terminal, gsi::arg ("terminal_id"), gsi::arg ("net"), + "@brief Connects the given terminal to the specified net.\n" + ) + + gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal, gsi::arg ("terminal_id"), + "@brief Disconnects the given terminal from any net.\n" + "If the terminal has been connected to a global, this connection will be disconnected too." + ) + + gsi::method_ext ("connect_terminal", &device_connect_terminal_by_name, gsi::arg ("terminal_name"), gsi::arg ("net"), + "@brief Connects the given terminal to the specified net.\n" + "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised.\n" + "If the terminal has been connected to a global net, it will be disconnected from there." + ) + + gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal_by_name, gsi::arg ("terminal_name"), + "@brief Disconnects the given terminal from any net.\n" + "This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised." + ) + + gsi::method ("parameter", (double (db::Device::*) (size_t) const) &db::Device::parameter_value, gsi::arg ("param_id"), + "@brief Gets the parameter value for the given parameter ID." + ) + + gsi::method ("set_parameter", (void (db::Device::*) (size_t, double)) &db::Device::set_parameter_value, gsi::arg ("param_id"), gsi::arg ("value"), + "@brief Sets the parameter value for the given parameter ID." + ) + + gsi::method ("parameter", (double (db::Device::*) (const std::string &) const) &db::Device::parameter_value, gsi::arg ("param_name"), + "@brief Gets the parameter value for the given parameter name.\n" + "If the parameter name is not valid, an exception is thrown." + ) + + gsi::method ("set_parameter", (void (db::Device::*) (const std::string &, double)) &db::Device::set_parameter_value, gsi::arg ("param_name"), gsi::arg ("value"), + "@brief Sets the parameter value for the given parameter name.\n" + "If the parameter name is not valid, an exception is thrown." + ), + "@brief A device inside a circuit.\n" + "Device object represent atomic devices such as resistors, diodes or transistors. " + "The \\Device class represents a particular device with specific parameters. " + "The type of device is represented by a \\DeviceClass object. Device objects " + "live in \\Circuit objects, the device class objects live in the \\Netlist object.\n" + "\n" + "Devices connect to nets through terminals. Terminals are described by a terminal ID which is " + "essentially the zero-based index of the terminal. Terminal definitions can be " + "obtained from the device class using the \\DeviceClass#terminal_definitions method.\n" + "\n" + "Devices connect to nets through the \\Device#connect_terminal method. " + "Device terminals can be disconnected using \\Device#disconnect_terminal.\n" + "\n" + "Device objects are created inside a circuit with \\Circuit#create_device.\n" + "\n" + "This class has been added in version 0.26." +); + +Class decl_dbDeviceAbstract ("db", "DeviceAbstract", + gsi::method ("netlist", (db::Netlist *(db::DeviceAbstract::*) ()) &db::DeviceAbstract::netlist, + "@brief Gets the netlist the device abstract lives in." + ) + + gsi::method ("device_class", &db::DeviceAbstract::device_class, + "@brief Gets the device class of the device." + ) + + gsi::method ("name=", &db::DeviceAbstract::set_name, gsi::arg ("name"), + "@brief Sets the name of the device abstract.\n" + "Device names are used to name a device abstract inside a netlist file. " + "Device names should be unique within a netlist." + ) + + gsi::method ("name", &db::DeviceAbstract::name, + "@brief Gets the name of the device abstract.\n" + ) + + gsi::method ("cell_index", &db::DeviceAbstract::cell_index, + "@brief Gets the cell index of the device abstract.\n" + "This is the cell that represents the device." + ) + + gsi::method ("cluster_id_for_terminal", &db::DeviceAbstract::cluster_id_for_terminal, gsi::arg ("terminal_id"), + "@brief Gets the cluster ID for the given terminal.\n" + "The cluster ID links the terminal to geometrical shapes within the clusters of the cell (see \\cell_index)" + ), + "@brief A geometrical device abstract\n" + "This class represents the geometrical model for the device. It links into the extracted layout " + "to a cell which holds the terminal shapes for the device.\n" + "\n" + "This class has been added in version 0.26." +); + +static void subcircuit_connect_pin1 (db::SubCircuit *subcircuit, const db::Pin *pin, db::Net *net) +{ + if (pin) { + subcircuit->connect_pin (pin->id (), net); + } +} + +static void subcircuit_disconnect_pin (db::SubCircuit *subcircuit, size_t pin_id) +{ + subcircuit->connect_pin (pin_id, 0); +} + +static void subcircuit_disconnect_pin1 (db::SubCircuit *subcircuit, const db::Pin *pin) +{ + if (pin) { + subcircuit->connect_pin (pin->id (), 0); + } +} + +Class decl_dbSubCircuit ("db", "SubCircuit", + gsi::method ("circuit_ref", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit_ref, + "@brief Gets the circuit referenced by the subcircuit.\n" + ) + + gsi::method ("circuit", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit, + "@brief Gets the circuit the subcircuit lives in.\n" + "This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \\circuit_ref." + ) + + gsi::method ("id", &db::SubCircuit::id, + "@brief Gets the subcircuit ID.\n" + "The ID is a unique integer which identifies the subcircuit.\n" + "It can be used to retrieve the subcircuit from the circuit using \\Circuit#subcircuit_by_id.\n" + "When assigned, the subcircuit ID is not 0.\n" + ) + + gsi::method ("name=", &db::SubCircuit::set_name, gsi::arg ("name"), + "@brief Sets the name of the subcircuit.\n" + "SubCircuit names are used to name a subcircuits inside a netlist file. " + "SubCircuit names should be unique within a circuit." + ) + + gsi::method ("name", &db::SubCircuit::name, + "@brief Gets the name of the subcircuit.\n" + ) + + gsi::method ("expanded_name", &db::SubCircuit::expanded_name, + "@brief Gets the expanded name of the subcircuit.\n" + "The expanded name takes the name of the subcircuit. If the name is empty, the numeric ID will be used to build a name. " + ) + + gsi::method ("net_for_pin", (db::Net *(db::SubCircuit::*) (size_t)) &db::SubCircuit::net_for_pin, gsi::arg ("pin_id"), + "@brief Gets the net connected to the specified pin of the subcircuit.\n" + "If the pin is not connected, nil is returned for the net." + ) + + gsi::method ("connect_pin", &db::SubCircuit::connect_pin, gsi::arg ("pin_id"), gsi::arg ("net"), + "@brief Connects the given pin to the specified net.\n" + ) + + gsi::method_ext ("connect_pin", &gsi::subcircuit_connect_pin1, gsi::arg ("pin"), gsi::arg ("net"), + "@brief Connects the given pin to the specified net.\n" + "This version takes a \\Pin reference instead of a pin ID." + ) + + gsi::method_ext ("disconnect_pin", &gsi::subcircuit_disconnect_pin, gsi::arg ("pin_id"), + "@brief Disconnects the given pin from any net.\n" + ) + + gsi::method_ext ("disconnect_pin", &gsi::subcircuit_disconnect_pin1, gsi::arg ("pin"), + "@brief Disconnects the given pin from any net.\n" + "This version takes a \\Pin reference instead of a pin ID." + ), + "@brief A subcircuit inside a circuit.\n" + "Circuits may instantiate other circuits as subcircuits similar to cells " + "in layouts. Such an instance is a subcircuit. A subcircuit refers to a " + "circuit implementation (a \\Circuit object), and presents connections through " + "pins. The pins of a subcircuit can be connected to nets. The subcircuit pins " + "are identical to the outgoing pins of the circuit the subcircuit refers to.\n" + "\n" + "Subcircuits connect to nets through the \\SubCircuit#connect_pin method. " + "SubCircuit pins can be disconnected using \\SubCircuit#disconnect_pin.\n" + "\n" + "Subcircuit objects are created inside a circuit with \\Circuit#create_subcircuit.\n" + "\n" + "This class has been added in version 0.26." +); + +Class decl_dbNetTerminalRef ("db", "NetTerminalRef", + gsi::method ("terminal_id", &db::NetTerminalRef::terminal_id, + "@brief Gets the ID of the terminal of the device the connection is made to." + ) + + gsi::method ("device", (db::Device *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::device, + "@brief Gets the device reference.\n" + "Gets the device object that this connection is made to." + ) + + gsi::method ("net", (db::Net *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::net, + "@brief Gets the net this terminal reference is attached to" + ) + + gsi::method ("device_class", (db::DeviceClass *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::device_class, + "@brief Gets the class of the device which is addressed." + ) + + gsi::method ("terminal_def", (db::DeviceTerminalDefinition *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::terminal_def, + "@brief Gets the terminal definition of the terminal that is connected" + ), + "@brief A connection to a terminal of a device.\n" + "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" + "\n" + "This class has been added in version 0.26." +); + +Class decl_dbNetPinRef ("db", "NetPinRef", + gsi::method ("pin_id", &db::NetPinRef::pin_id, + "@brief Gets the ID of the pin the connection is made to." + ) + + gsi::method ("pin", &db::NetPinRef::pin, + "@brief Gets the \\Pin object of the pin the connection is made to." + ) + + gsi::method ("net", (db::Net *(db::NetPinRef::*) ()) &db::NetPinRef::net, + "@brief Gets the net this pin reference is attached to" + ), + "@brief A connection to an outgoing pin of the circuit.\n" + "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" + "\n" + "This class has been added in version 0.26." +); + +Class decl_dbNetSubcircuitPinRef ("db", "NetSubcircuitPinRef", + gsi::method ("pin_id", &db::NetSubcircuitPinRef::pin_id, + "@brief Gets the ID of the pin the connection is made to." + ) + + gsi::method ("pin", &db::NetSubcircuitPinRef::pin, + "@brief Gets the \\Pin object of the pin the connection is made to." + ) + + gsi::method ("subcircuit", (db::SubCircuit *(db::NetSubcircuitPinRef::*) ()) &db::NetSubcircuitPinRef::subcircuit, + "@brief Gets the subcircuit reference.\n" + "This attribute indicates the subcircuit the net attaches to. The " + "subcircuit lives in the same circuit than the net. " + ) + + gsi::method ("net", (db::Net *(db::NetSubcircuitPinRef::*) ()) &db::NetSubcircuitPinRef::net, + "@brief Gets the net this pin reference is attached to" + ), + "@brief A connection to a pin of a subcircuit.\n" + "This object is used inside a net (see \\Net) to describe the connections a net makes.\n" + "\n" + "This class has been added in version 0.26." +); + +Class decl_dbNet ("db", "Net", + gsi::method ("circuit", (db::Circuit *(db::Net::*) ()) &db::Net::circuit, + "@brief Gets the circuit the net lives in." + ) + + gsi::method ("clear", &db::Net::clear, + "@brief Clears the net." + ) + + gsi::method ("name=", &db::Net::set_name, gsi::arg ("name"), + "@brief Sets the name of the net.\n" + "The name of the net is used for nameing the net in schematic files for example. " + "The name of the net has to be unique." + ) + + gsi::method ("name", &db::Net::name, + "@brief Gets the name of the net.\n" + "See \\name= for details about the name." + ) + + gsi::method ("qname|to_s", &db::Net::qname, + "@brief Gets the qualified name.\n" + "The qualified name is like the expanded name, but the circuit's name is preceeded\n" + "(i.e. 'CIRCUIT:NET') if available.\n" + ) + + gsi::method ("expanded_name", &db::Net::expanded_name, + "@brief Gets the expanded name of the net.\n" + "The expanded name takes the name of the net. If the name is empty, the cluster ID will be used to build a name. " + ) + + gsi::method ("cluster_id=", &db::Net::set_cluster_id, gsi::arg ("id"), + "@brief Sets the cluster ID of the net.\n" + "The cluster ID connects the net with a layout cluster. It is set when " + "the net is extracted from a layout." + ) + + gsi::method ("cluster_id", &db::Net::cluster_id, + "@brief Gets the cluster ID of the net.\n" + "See \\cluster_id= for details about the cluster ID." + ) + + gsi::iterator ("each_pin", (db::Net::pin_iterator (db::Net::*) ()) &db::Net::begin_pins, (db::Net::pin_iterator (db::Net::*) ()) &db::Net::end_pins, + "@brief Iterates over all outgoing pins the net connects.\n" + "Pin connections are described by \\NetPinRef objects. Pin connections " + "are connections to outgoing pins of the circuit the net lives in." + ) + + gsi::iterator ("each_subcircuit_pin", (db::Net::subcircuit_pin_iterator (db::Net::*) ()) &db::Net::begin_subcircuit_pins, (db::Net::subcircuit_pin_iterator (db::Net::*) ()) &db::Net::end_subcircuit_pins, + "@brief Iterates over all subcircuit pins the net connects.\n" + "Subcircuit pin connections are described by \\NetSubcircuitPinRef objects. These are " + "connections to specific pins of subcircuits." + ) + + gsi::iterator ("each_terminal", (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::begin_terminals, (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::end_terminals, + "@brief Iterates over all terminals the net connects.\n" + "Terminals connect devices. Terminal connections are described by \\NetTerminalRef " + "objects." + ) + + gsi::method ("is_floating?", &db::Net::is_floating, + "@brief Returns true, if the net is floating.\n" + "Floating nets are those who don't have any or only a single connection (pin_count + terminal_count < 2)." + ) + + gsi::method ("is_internal?", &db::Net::is_internal, + "@brief Returns true, if the net is an internal net.\n" + "Internal nets are those which connect exactly two terminals and nothing else (pin_count = 0 and terminal_count == 2)." + ) + + gsi::method ("pin_count", &db::Net::pin_count, + "@brief Returns the number of outgoing pins connected by this net.\n" + ) + + gsi::method ("subcircuit_pin_count", &db::Net::subcircuit_pin_count, + "@brief Returns the number of subcircuit pins connected by this net.\n" + ) + + gsi::method ("terminal_count", &db::Net::terminal_count, + "@brief Returns the number of terminals connected by this net.\n" + ), + "@brief A single net.\n" + "A net connects multiple pins or terminals together. Pins are either " + "pin or subcircuits of outgoing pins of the circuit the net lives in. " + "Terminals are connections made to specific terminals of devices.\n" + "\n" + "Net objects are created inside a circuit with \\Circuit#create_net.\n" + "\n" + "To connect a net to an outgoing pin of a circuit, use \\Circuit#connect_pin, to " + "disconnect a net from an outgoing pin use \\Circuit#disconnect_pin. " + "To connect a net to a pin of a subcircuit, use \\SubCircuit#connect_pin, to " + "disconnect a net from a pin of a subcircuit, use \\SubCircuit#disconnect_pin. " + "To connect a net to a terminal of a device, use \\Device#connect_terminal, to " + "disconnect a net from a terminal of a device, use \\Device#disconnect_terminal.\n" + "\n" + "This class has been added in version 0.26." +); + +static db::DeviceTerminalDefinition *new_terminal_definition (const std::string &name, const std::string &description) +{ + return new db::DeviceTerminalDefinition (name, description); +} + +Class decl_dbDeviceTerminalDefinition ("db", "DeviceTerminalDefinition", + gsi::constructor ("new", &gsi::new_terminal_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), + "@brief Creates a new terminal definition." + ) + + gsi::method ("name", &db::DeviceTerminalDefinition::name, + "@brief Gets the name of the terminal." + ) + + gsi::method ("name=", &db::DeviceTerminalDefinition::set_name, gsi::arg ("name"), + "@brief Sets the name of the terminal." + ) + + gsi::method ("description", &db::DeviceTerminalDefinition::description, + "@brief Gets the description of the terminal." + ) + + gsi::method ("description=", &db::DeviceTerminalDefinition::set_description, gsi::arg ("description"), + "@brief Sets the description of the terminal." + ) + + gsi::method ("id", &db::DeviceTerminalDefinition::id, + "@brief Gets the ID of the terminal.\n" + "The ID of the terminal is used in some places to refer to a specific terminal (e.g. in " + "the \\NetTerminalRef object)." + ), + "@brief A terminal descriptor\n" + "This class is used inside the \\DeviceClass class to describe a terminal of the device.\n" + "\n" + "This class has been added in version 0.26." +); + +static db::DeviceParameterDefinition *new_parameter_definition (const std::string &name, const std::string &description, double default_value) +{ + return new db::DeviceParameterDefinition (name, description, default_value); +} + +Class decl_dbDeviceParameterDefinition ("db", "DeviceParameterDefinition", + gsi::constructor ("new", &gsi::new_parameter_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), gsi::arg ("default_value", 0.0), + "@brief Creates a new parameter definition." + ) + + gsi::method ("name", &db::DeviceParameterDefinition::name, + "@brief Gets the name of the parameter." + ) + + gsi::method ("name=", &db::DeviceParameterDefinition::set_name, gsi::arg ("name"), + "@brief Sets the name of the parameter." + ) + + gsi::method ("description", &db::DeviceParameterDefinition::description, + "@brief Gets the description of the parameter." + ) + + gsi::method ("description=", &db::DeviceParameterDefinition::set_description, gsi::arg ("description"), + "@brief Sets the description of the parameter." + ) + + gsi::method ("default_value", &db::DeviceParameterDefinition::default_value, + "@brief Gets the default value of the parameter." + ) + + gsi::method ("default_value=", &db::DeviceParameterDefinition::set_default_value, gsi::arg ("default_value"), + "@brief Sets the default value of the parameter.\n" + "The default value is used to initialize parameters of \\Device objects." + ) + + gsi::method ("id", &db::DeviceParameterDefinition::id, + "@brief Gets the ID of the parameter.\n" + "The ID of the parameter is used in some places to refer to a specific parameter (e.g. in " + "the \\NetParameterRef object)." + ), + "@brief A parameter descriptor\n" + "This class is used inside the \\DeviceClass class to describe a parameter of the device.\n" + "\n" + "This class has been added in version 0.26." +); + +static tl::id_type id_of_device_class (const db::DeviceClass *cls) +{ + return tl::id_of (cls); +} + +Class decl_dbDeviceClass ("db", "DeviceClass", + gsi::method ("name", &db::DeviceClass::name, + "@brief Gets the name of the device class." + ) + + gsi::method ("name=", &db::DeviceClass::set_name, gsi::arg ("name"), + "@brief Sets the name of the device class." + ) + + gsi::method ("description", &db::DeviceClass::description, + "@brief Gets the description text of the device class." + ) + + gsi::method ("description=", &db::DeviceClass::set_description, gsi::arg ("description"), + "@brief Sets the description of the device class." + ) + + gsi::method ("netlist", (db::Netlist *(db::DeviceClass::*) ()) &db::DeviceClass::netlist, + "@brief Gets the netlist the device class lives in." + ) + + gsi::method_ext ("id", &gsi::id_of_device_class, + "@brief Gets the unique ID of the device class\n" + "The ID is a unique integer that identifies the device class. Use the ID " + "to check for object identity - i.e. to determine whether two devices share the " + "same device class." + ) + + gsi::method ("terminal_definitions", &db::DeviceClass::terminal_definitions, + "@brief Gets the list of terminal definitions of the device.\n" + "See the \\DeviceTerminalDefinition class description for details." + ) + + gsi::method ("terminal_definition", &db::DeviceClass::terminal_definition, gsi::arg ("terminal_id"), + "@brief Gets the terminal definition object for a given ID.\n" + "Terminal definition IDs are used in some places to reference a specific terminal of a device. " + "This method obtains the corresponding definition object." + ) + + gsi::method ("parameter_definitions", &db::DeviceClass::parameter_definitions, + "@brief Gets the list of parameter definitions of the device.\n" + "See the \\DeviceParameterDefinition class description for details." + ) + + gsi::method ("parameter_definition", &db::DeviceClass::parameter_definition, gsi::arg ("parameter_id"), + "@brief Gets the parameter definition object for a given ID.\n" + "Parameter definition IDs are used in some places to reference a specific parameter of a device. " + "This method obtains the corresponding definition object." + ) + + gsi::method ("has_parameter?", &db::DeviceClass::has_parameter_with_name, gsi::arg ("name"), + "@brief Returns true, if the device class has a parameter with the given name.\n" + ) + + gsi::method ("parameter_id", &db::DeviceClass::parameter_id_for_name, gsi::arg ("name"), + "@brief Returns the parameter ID of the parameter with the given name.\n" + "An exception is thrown if there is no parameter with the given name. Use \\has_parameter to check " + "whether the name is a valid parameter name." + ) + + gsi::method ("has_terminal?", &db::DeviceClass::has_terminal_with_name, gsi::arg ("name"), + "@brief Returns true, if the device class has a terminal with the given name.\n" + ) + + gsi::method ("terminal_id", &db::DeviceClass::terminal_id_for_name, gsi::arg ("name"), + "@brief Returns the terminal ID of the terminal with the given name.\n" + "An exception is thrown if there is no terminal with the given name. Use \\has_terminal to check " + "whether the name is a valid terminal name." + ), + "@brief A class describing a specific type of device.\n" + "Device class objects live in the context of a \\Netlist object. After a " + "device class is created, it must be added to the netlist using \\Netlist#add. " + "The netlist will own the device class object. When the netlist is destroyed, the " + "device class object will become invalid.\n" + "\n" + "The \\DeviceClass class is the base class for other device classes.\n" + "\n" + "This class has been added in version 0.26." +); + +namespace { + +/** + * @brief A DeviceClass implementation that allows reimplementation of the virtual methods + * + * NOTE: cloning of the generic device class is not supported currently. Hence when the + * netlist is copied, the device class attributes will remain, but the functionality is lost. + */ +class GenericDeviceClass + : public db::DeviceClass +{ +public: + GenericDeviceClass () + : db::DeviceClass (), m_supports_parallel_combination (true), m_supports_serial_combination (true) + { + // .. nothing yet .. + } + + virtual bool combine_devices (db::Device *a, db::Device *b) const + { + if (cb_combine_devices.can_issue ()) { + return cb_combine_devices.issue (&db::DeviceClass::combine_devices, a, b); + } else { + return db::DeviceClass::combine_devices (a, b); + } + } + + virtual bool supports_parallel_combination () const + { + return m_supports_parallel_combination; + } + + virtual bool supports_serial_combination () const + { + return m_supports_serial_combination; + } + + void set_supports_parallel_combination (bool f) + { + m_supports_parallel_combination = f; + } + + void set_supports_serial_combination (bool f) + { + m_supports_serial_combination = f; + } + + gsi::Callback cb_combine_devices; + +private: + bool m_supports_parallel_combination; + bool m_supports_serial_combination; +}; + +} + +static void gdc_add_terminal_definition (GenericDeviceClass *cls, db::DeviceTerminalDefinition *terminal_def) +{ + if (terminal_def) { + *terminal_def = cls->add_terminal_definition (*terminal_def); + } +} + +static void gdc_add_parameter_definition (GenericDeviceClass *cls, db::DeviceParameterDefinition *parameter_def) +{ + if (parameter_def) { + *parameter_def = cls->add_parameter_definition (*parameter_def); + } +} + +Class decl_GenericDeviceClass (decl_dbDeviceClass, "db", "GenericDeviceClass", + gsi::method_ext ("add_terminal", &gsi::gdc_add_terminal_definition, gsi::arg ("terminal_def"), + "@brief Adds the given terminal definition to the device class\n" + "This method will define a new terminal. The new terminal is added at the end of existing terminals. " + "The terminal definition object passed as the argument is modified to contain the " + "new ID of the terminal.\n" + "\n" + "The terminal is copied into the device class. Modifying the terminal object later " + "does not have the effect of changing the terminal definition." + ) + + gsi::method ("clear_terminals", &GenericDeviceClass::clear_terminal_definitions, + "@brief Clears the list of terminals\n" + ) + + gsi::method_ext ("add_parameter", &gsi::gdc_add_parameter_definition, gsi::arg ("parameter_def"), + "@brief Adds the given parameter definition to the device class\n" + "This method will define a new parameter. The new parameter is added at the end of existing parameters. " + "The parameter definition object passed as the argument is modified to contain the " + "new ID of the parameter." + "\n" + "The parameter is copied into the device class. Modifying the parameter object later " + "does not have the effect of changing the parameter definition." + ) + + gsi::method ("clear_parameters", &GenericDeviceClass::clear_parameter_definitions, + "@brief Clears the list of parameters\n" + ) + + gsi::callback ("combine_devices", &GenericDeviceClass::combine_devices, &GenericDeviceClass::cb_combine_devices, gsi::arg ("a"), gsi::arg ("b"), + "@brief Combines two devices.\n" + "This method shall test, whether the two devices can be combined. Both devices " + "are guaranteed to share the same device class (self). " + "If they cannot be combined, this method shall do nothing and return false. " + "If they can be combined, this method shall reconnect the nets of the first " + "device and entirely disconnect the nets of the second device. " + "It shall combine the parameters of both devices into the first. " + "The second device will be deleted afterwards.\n" + ) + + gsi::method ("supports_parallel_combination=", &GenericDeviceClass::set_supports_parallel_combination, gsi::arg ("f"), + "@brief Specifies whether the device supports parallel device combination.\n" + "Parallel device combination means that all terminals of two combination candidates are connected to the same nets. " + "If the device does not support this combination mode, this predicate can be set to false. This will make the device " + "extractor skip the combination test in parallel mode and improve performance somewhat." + ) + + gsi::method ("supports_serial_combination=", &GenericDeviceClass::set_supports_serial_combination, gsi::arg ("f"), + "@brief Specifies whether the device supports serial device combination.\n" + "Serial device combination means that the devices are connected by internal nodes. " + "If the device does not support this combination mode, this predicate can be set to false. This will make the device " + "extractor skip the combination test in serial mode and improve performance somewhat." + ), + "@brief A generic device class\n" + "This class allows building generic device classes. Specificially, terminals can be defined " + "by adding terminal definitions. Terminal definitions should not be added dynamically. To create " + "your own device, instantiate the \\GenericDeviceClass object, set name and description and " + "specify the terminals. Then add this new device class to the \\Netlist object where it will live " + "and be used to define device instances (\\Device objects).\n" + "\n" + "In addition, parameters can be defined which correspond to values stored inside the " + "specific device instance (\\Device object)." + "\n" + "This class has been added in version 0.26." +); + +static db::Net *create_net (db::Circuit *c, const std::string &name) +{ + db::Net *n = new db::Net (); + c->add_net (n); + n->set_name (name); + return n; +} + +static db::Device *create_device1 (db::Circuit *c, db::DeviceClass *dc, const std::string &name) +{ + db::Device *d = new db::Device (dc, name); + c->add_device (d); + return d; +} + +static db::SubCircuit *create_subcircuit1 (db::Circuit *c, db::Circuit *cc, const std::string &name) +{ + db::SubCircuit *sc = new db::SubCircuit (cc, name); + c->add_subcircuit (sc); + return sc; +} + +static db::Net *circuit_net_for_pin (db::Circuit *c, const db::Pin *pin) +{ + return pin ? c->net_for_pin (pin->id ()) : 0; +} + +static void circuit_connect_pin1 (db::Circuit *c, const db::Pin *pin, db::Net *net) +{ + if (pin) { + c->connect_pin (pin->id (), net); + } +} + +static void circuit_disconnect_pin (db::Circuit *c, size_t pin_id) +{ + c->connect_pin (pin_id, 0); +} + +static void circuit_disconnect_pin1 (db::Circuit *c, const db::Pin *pin) +{ + if (pin) { + c->connect_pin (pin->id (), 0); + } +} + +Class decl_dbCircuit ("db", "Circuit", + gsi::method ("create_pin", &db::Circuit::add_pin, gsi::arg ("name"), + "@brief Creates a new \\Pin object inside the circuit\n" + "This object will describe a pin of the circuit. A circuit connects " + "to the outside through such a pin. The pin is added after all existing " + "pins. For more details see the \\Pin class." + ) + + gsi::iterator ("each_child", (db::Circuit::child_circuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_children, (db::Circuit::child_circuit_iterator (db::Circuit::*) ()) &db::Circuit::end_children, + "@brief Iterates over the child circuits of this circuit\n" + "Child circuits are the ones that are referenced from this circuit via subcircuits." + ) + + gsi::iterator ("each_parent", (db::Circuit::parent_circuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_parents, (db::Circuit::parent_circuit_iterator (db::Circuit::*) ()) &db::Circuit::end_parents, + "@brief Iterates over the parent circuits of this circuit\n" + "Child circuits are the ones that are referencing this circuit via subcircuits." + ) + + gsi::iterator ("each_ref", (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::begin_refs, (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::end_refs, + "@brief Iterates over the subcircuit objects referencing this circuit\n" + ) + + gsi::iterator ("each_pin", (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::begin_pins, (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::end_pins, + "@brief Iterates over the pins of the circuit" + ) + + gsi::method ("device_by_id", (db::Device *(db::Circuit::*) (size_t)) &db::Circuit::device_by_id, gsi::arg ("id"), + "@brief Gets the device object for a given ID.\n" + "If the ID is not a valid device ID, nil is returned." + ) + + gsi::method ("device_by_name", (db::Device *(db::Circuit::*) (const std::string &)) &db::Circuit::device_by_name, gsi::arg ("name"), + "@brief Gets the device object for a given name.\n" + "If the ID is not a valid device name, nil is returned." + ) + + gsi::method ("subcircuit_by_id", (db::SubCircuit *(db::Circuit::*) (size_t)) &db::Circuit::subcircuit_by_id, gsi::arg ("id"), + "@brief Gets the subcircuit object for a given ID.\n" + "If the ID is not a valid subcircuit ID, nil is returned." + ) + + gsi::method ("subcircuit_by_name", (db::SubCircuit *(db::Circuit::*) (const std::string &)) &db::Circuit::subcircuit_by_name, gsi::arg ("name"), + "@brief Gets the subcircuit object for a given name.\n" + "If the ID is not a valid subcircuit name, nil is returned." + ) + + gsi::method ("net_by_cluster_id", (db::Net *(db::Circuit::*) (size_t)) &db::Circuit::net_by_cluster_id, gsi::arg ("cluster_id"), + "@brief Gets the net object corresponding to a specific cluster ID\n" + "If the ID is not a valid pin cluster ID, nil is returned." + ) + + gsi::method ("net_by_name", (db::Net *(db::Circuit::*) (const std::string &)) &db::Circuit::net_by_name, gsi::arg ("name"), + "@brief Gets the net object for a given name.\n" + "If the ID is not a valid net name, nil is returned." + ) + + gsi::method ("pin_by_id", &db::Circuit::pin_by_id, gsi::arg ("id"), + "@brief Gets the \\Pin object corresponding to a specific ID\n" + "If the ID is not a valid pin ID, nil is returned." + ) + + gsi::method ("pin_by_name", &db::Circuit::pin_by_name, gsi::arg ("name"), + "@brief Gets the \\Pin object corresponding to a specific name\n" + "If the ID is not a valid pin name, nil is returned." + ) + + gsi::method ("pin_count", &db::Circuit::pin_count, + "@brief Gets the number of pins in the circuit" + ) + + gsi::method_ext ("create_net", &gsi::create_net, gsi::arg ("name", std::string ()), + "@brief Creates a new \\Net object inside the circuit\n" + "This object will describe a net of the circuit. The nets are basically " + "connections between the different components of the circuit (subcircuits, " + "devices and pins).\n" + "\n" + "A net needs to be filled with references to connect to specific objects. " + "See the \\Net class for more details." + ) + + gsi::method ("remove_net", &db::Circuit::remove_net, gsi::arg ("net"), + "@brief Removes the given net from the circuit\n" + ) + + gsi::iterator ("each_net", (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::begin_nets, (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::end_nets, + "@brief Iterates over the nets of the circuit" + ) + + gsi::method_ext ("create_device", &gsi::create_device1, gsi::arg ("device_class"), gsi::arg ("name", std::string ()), + "@brief Creates a new bound \\Device object inside the circuit\n" + "This object describes a device of the circuit. The device is already attached " + "to the device class. The name is optional and is used to identify the device in a " + "netlist file.\n" + "\n" + "For more details see the \\Device class." + ) + + gsi::method ("remove_device", &db::Circuit::remove_device, gsi::arg ("device"), + "@brief Removes the given device from the circuit\n" + ) + + gsi::iterator ("each_device", (db::Circuit::device_iterator (db::Circuit::*) ()) &db::Circuit::begin_devices, (db::Circuit::device_iterator (db::Circuit::*) ()) &db::Circuit::end_devices, + "@brief Iterates over the devices of the circuit" + ) + + gsi::method_ext ("create_subcircuit", &gsi::create_subcircuit1, gsi::arg ("circuit"), gsi::arg ("name", std::string ()), + "@brief Creates a new bound \\SubCircuit object inside the circuit\n" + "This object describes an instance of another circuit inside the circuit. The subcircuit is already attached " + "to the other circuit. The name is optional and is used to identify the subcircuit in a " + "netlist file.\n" + "\n" + "For more details see the \\SubCircuit class." + ) + + gsi::method ("remove_subcircuit", &db::Circuit::remove_subcircuit, gsi::arg ("subcircuit"), + "@brief Removes the given subcircuit from the circuit\n" + ) + + gsi::iterator ("each_subcircuit", (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_subcircuits, (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::end_subcircuits, + "@brief Iterates over the subcircuits of the circuit" + ) + + gsi::method ("netlist", (db::Netlist *(db::Circuit::*) ()) &db::Circuit::netlist, + "@brief Gets the netlist object the circuit lives in" + ) + + gsi::method ("name=", &db::Circuit::set_name, gsi::arg ("name"), + "@brief Sets the name of the circuit" + ) + + gsi::method ("name", &db::Circuit::name, + "@brief Gets the name of the circuit" + ) + + gsi::method ("cell_index=", &db::Circuit::set_cell_index, gsi::arg ("cell_index"), + "@brief Sets the cell index\n" + "The cell index relates a circuit with a cell from a layout. It's intended to " + "hold a cell index number if the netlist was extracted from a layout.\n" + ) + + gsi::method ("cell_index", &db::Circuit::cell_index, + "@brief Gets the cell index of the circuit\n" + "See \\cell_index= for details.\n" + ) + + gsi::method ("net_for_pin", (db::Net *(db::Circuit::*) (size_t)) &db::Circuit::net_for_pin, gsi::arg ("pin_id"), + "@brief Gets the net object attached to a specific pin.\n" + "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" + "This method returns nil if the pin is not connected or the pin ID is invalid." + ) + + gsi::method_ext ("net_for_pin", &gsi::circuit_net_for_pin, gsi::arg ("pin"), + "@brief Gets the net object attached to a specific pin.\n" + "This is the net object inside the circuit which attaches to the given outward-bound pin.\n" + "This method returns nil if the pin is not connected or the pin object is nil." + ) + + gsi::method ("connect_pin", &db::Circuit::connect_pin, gsi::arg ("pin_id"), gsi::arg ("net"), + "@brief Connects the given pin with the given net.\n" + "The net must be one inside the circuit. Any previous connected is resolved before this " + "connection is made. A pin can only be connected to one net at a time." + ) + + gsi::method_ext ("connect_pin", &gsi::circuit_connect_pin1, gsi::arg ("pin"), gsi::arg ("net"), + "@brief Connects the given pin with the given net.\n" + "The net and the pin must be objects from inside the circuit. Any previous connected is resolved before this " + "connection is made. A pin can only be connected to one net at a time." + ) + + gsi::method_ext ("disconnect_pin", &gsi::circuit_disconnect_pin, gsi::arg ("pin_id"), + "@brief Disconnects the given pin from any net.\n" + ) + + gsi::method_ext ("disconnect_pin", &gsi::circuit_disconnect_pin1, gsi::arg ("pin"), + "@brief Disconnects the given pin from any net.\n" + ) + + gsi::method ("clear", &db::Circuit::clear, + "@brief Clears the circuit\n" + "This method removes all objects and clears the other attributes." + ) + + gsi::method ("combine_devices", &db::Circuit::combine_devices, + "@brief Combines devices where possible\n" + "This method will combine devices that can be combined according " + "to their device classes 'combine_devices' method.\n" + "For example, serial or parallel resistors can be combined into " + "a single resistor.\n" + ) + + gsi::method ("purge_nets", &db::Circuit::purge_nets, + "@brief Purges floating nets.\n" + "Floating nets can be created as effect of reconnections of devices or pins. " + "This method will eliminate all nets that make less than two connections." + ), + "@brief Circuits are the basic building blocks of the netlist\n" + "A circuit has pins by which it can connect to the outside. Pins are " + "created using \\create_pin and are represented by the \\Pin class.\n" + "\n" + "Futhermore, a circuit manages the components of the netlist. " + "Components are devices (class \\Device) and subcircuits (class \\SubCircuit). " + "Devices are basic devices such as resistors or transistors. Subcircuits " + "are other circuits to which nets from this circuit connect. " + "Devices are created using the \\create_device method. Subcircuits are " + "created using the \\create_subcircuit method.\n" + "\n" + "Devices are connected through 'terminals', subcircuits are connected through " + "their pins. Terminals and pins are described by integer ID's in the context of " + "most methods.\n" + "\n" + "Finally, the circuit consists of the nets. Nets connect terminals of devices " + "and pins of subcircuits or the circuit itself. Nets are created using " + "\\create_net and are represented by objects of the \\Net class.\n" + "See there for more about nets.\n" + "\n" + "The Circuit object is only valid if the netlist object is alive. " + "Circuits must be added to a netlist using \\Netlist#add to become " + "part of the netlist.\n" + "\n" + "The Circuit class has been introduced in version 0.26." +); + +static void add_circuit (db::Netlist *nl, db::Circuit *c) +{ + tl_assert (c != 0); + c->keep (); + nl->add_circuit (c); +} + +static void add_device_class (db::Netlist *nl, db::DeviceClass *cl) +{ + tl_assert (cl != 0); + cl->keep (); + nl->add_device_class (cl); +} + +static void write_netlist (const db::Netlist *nl, const std::string &file, db::NetlistWriter *writer, const std::string &description) +{ + tl_assert (writer != 0); + tl::OutputStream os (file); + writer->write (os, *nl, description); +} + +Class decl_dbNetlist ("db", "Netlist", + gsi::method_ext ("add", &gsi::add_circuit, gsi::arg ("circuit"), + "@brief Adds the circuit to the netlist\n" + "This method will add the given circuit object to the netlist. " + "After the circuit has been added, it will be owned by the netlist." + ) + + gsi::method ("remove", &db::Netlist::remove_circuit, gsi::arg ("circuit"), + "@brief Removes the given circuit object from the netlist\n" + "After the object has been removed, it becomes invalid and cannot be used further." + ) + + gsi::method ("circuit_by_cell_index", (db::Circuit *(db::Netlist::*) (db::cell_index_type)) &db::Netlist::circuit_by_cell_index, gsi::arg ("cell_index"), + "@brief Gets the circuit object for a given cell index.\n" + "If the cell index is not valid or no circuit is registered with this index, nil is returned." + ) + + gsi::method ("circuit_by_name", (db::Circuit *(db::Netlist::*) (const std::string &)) &db::Netlist::circuit_by_name, gsi::arg ("name"), + "@brief Gets the circuit object for a given name.\n" + "If the ID is not a valid circuit name, nil is returned." + ) + + gsi::iterator ("each_circuit_top_down", (db::Netlist::top_down_circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_top_down, (db::Netlist::top_down_circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_top_down, + "@brief Iterates over the circuits top-down\n" + "Iterating top-down means the parent circuits come before the child circuits. " + "The first \\top_circuit_count circuits are top circuits - i.e. those which are not referenced by other circuits." + ) + + gsi::iterator ("each_circuit_bottom_up", (db::Netlist::bottom_up_circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_bottom_up, (db::Netlist::bottom_up_circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_bottom_up, + "@brief Iterates over the circuits bottom-up\n" + "Iterating bottom-up means the parent circuits come after the child circuits. " + "This is the basically the reverse order as delivered by \\each_circuit_top_down." + ) + + gsi::method ("top_circuit_count", &db::Netlist::top_circuit_count, + "@brief Gets the number of top circuits.\n" + "Top circuits are those which are not referenced by other circuits via subcircuits. " + "A well-formed netlist has a single top circuit." + ) + + gsi::iterator ("each_circuit", (db::Netlist::circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_circuits, (db::Netlist::circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_circuits, + "@brief Iterates over the circuits of the netlist" + ) + + gsi::method_ext ("add", &gsi::add_device_class, gsi::arg ("device_class"), + "@brief Adds the device class to the netlist\n" + "This method will add the given device class object to the netlist. " + "After the device class has been added, it will be owned by the netlist." + ) + + gsi::method ("remove", &db::Netlist::remove_device_class, gsi::arg ("device_class"), + "@brief Removes the given device class object from the netlist\n" + "After the object has been removed, it becomes invalid and cannot be used further. " + "Use this method with care as it may corrupt the internal structure of the netlist. " + "Only use this method when device refers to this device class." + ) + + gsi::iterator ("each_device_class", (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::begin_device_classes, (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::end_device_classes, + "@brief Iterates over the device classes of the netlist" + ) + + gsi::method ("to_s", &db::Netlist::to_string, + "@brief Converts the netlist to a string representation.\n" + "This method is intended for test purposes mainly." + ) + + gsi::method ("combine_devices", &db::Netlist::combine_devices, + "@brief Combines devices where possible\n" + "This method will combine devices that can be combined according " + "to their device classes 'combine_devices' method.\n" + "For example, serial or parallel resistors can be combined into " + "a single resistor.\n" + ) + + gsi::method ("make_top_level_pins", &db::Netlist::make_top_level_pins, + "@brief Creates pins for top-level circuits.\n" + "This method will turn all named nets of top-level circuits (such that are not " + "referenced by subcircuits) into pins. This method can be used before purge to " + "avoid that purge will remove nets which are directly connecting to subcircuits." + ) + + gsi::method ("purge", &db::Netlist::purge, + "@brief Purge unused nets, circuits and subcircuits.\n" + "This method will purge all nets which return \\floating == true. Circuits which don't have any " + "nets (or only floating ones) and removed. Their subcircuits are disconnected." + ) + + gsi::method ("purge_nets", &db::Netlist::purge_nets, + "@brief Purges floating nets.\n" + "Floating nets can be created as effect of reconnections of devices or pins. " + "This method will eliminate all nets that make less than two connections." + ) + + gsi::method_ext ("write", &write_netlist, gsi::arg ("file"), gsi::arg ("writer"), gsi::arg ("description", std::string ()), + "@brief Writes the netlist to the given file using the given writer object to format the file\n" + "See \\NetlistSpiceWriter for an example for a formatter. " + "The description is an arbitrary text which will be put into the file somewhere at the beginning." + ), + "@brief The netlist top-level class\n" + "A netlist is a hierarchical structure of circuits. At least one circuit is the " + "top-level circuit, other circuits may be referenced as subcircuits.\n" + "Circuits are created with \\create_circuit and are represented by objects of the \\Circuit class.\n" + "\n" + "Beside circuits, the netlist manages device classes. Device classes describe specific " + "types of devices. Device classes are represented by objects of the \\DeviceClass class " + "and are created using \\create_device_class.\n" + "\n" + "The netlist class has been introduced with version 0.26." +); + +/** + * @brief A SPICE writer delegate base class for reimplementation + */ +class NetlistSpiceWriterDelegateImpl + : public db::NetlistSpiceWriterDelegate, public gsi::ObjectBase +{ +public: + NetlistSpiceWriterDelegateImpl () + : db::NetlistSpiceWriterDelegate () + { + // .. nothing yet .. + } + + virtual void write_header () const + { + if (cb_write_header.can_issue ()) { + cb_write_header.issue (&db::NetlistSpiceWriterDelegate::write_header); + } else { + db::NetlistSpiceWriterDelegate::write_header (); + } + } + + virtual void write_device_intro (const db::DeviceClass &ccls) const + { + reimpl_write_device_intro (const_cast (ccls)); + } + + // NOTE: we pass non-const refs to Ruby/Python - everthing else is a bit of a nightmare. + // Still that's not really clean. Just say, the implementation promises not to change the objects. + void reimpl_write_device_intro (db::DeviceClass &cls) const + { + if (cb_write_device_intro.can_issue ()) { + cb_write_device_intro.issue (&NetlistSpiceWriterDelegateImpl::org_write_device_intro, const_cast (cls)); + } else { + org_write_device_intro (cls); + } + } + + void org_write_device_intro (db::DeviceClass &cls) const + { + db::NetlistSpiceWriterDelegate::write_device_intro (cls); + } + + virtual void write_device (const db::Device &cdev) const + { + reimpl_write_device (const_cast (cdev)); + } + + // NOTE: we pass non-const refs to Ruby/Python - everthing else is a bit of a nightmare. + // Still that's not really clean. Just say, the implementation promises not to change the objects. + void reimpl_write_device (db::Device &dev) const + { + if (cb_write_device.can_issue ()) { + cb_write_device.issue (&NetlistSpiceWriterDelegateImpl::org_write_device, dev); + } else { + org_write_device (dev); + } + } + + void org_write_device (db::Device &dev) const + { + db::NetlistSpiceWriterDelegate::write_device (dev); + } + + gsi::Callback cb_write_header; + gsi::Callback cb_write_device_intro; + gsi::Callback cb_write_device; +}; + +Class db_NetlistSpiceWriterDelegate ("db", "NetlistSpiceWriterDelegate", + gsi::callback ("write_header", &NetlistSpiceWriterDelegateImpl::write_header, &NetlistSpiceWriterDelegateImpl::cb_write_header, + "@brief Writes the text at the beginning of the SPICE netlist\n" + "Reimplement this method to insert your own text at the beginning of the file" + ) + + gsi::callback ("write_device_intro", &NetlistSpiceWriterDelegateImpl::reimpl_write_device_intro, &NetlistSpiceWriterDelegateImpl::cb_write_device_intro, gsi::arg ("device_class"), + "@brief Inserts a text for the given device class\n" + "Reimplement this method to insert your own text at the beginning of the file for the given device class" + ) + + gsi::callback ("write_device", &NetlistSpiceWriterDelegateImpl::reimpl_write_device, &NetlistSpiceWriterDelegateImpl::cb_write_device, gsi::arg ("device"), + "@brief Inserts a text for the given device\n" + "Reimplement this method to write the given device in the desired way" + ) + + gsi::method ("write_device", &NetlistSpiceWriterDelegateImpl::org_write_device, gsi::arg ("device"), + "@brief Calls the default implementation of the \\write_device method.\n" + "The default implementation will utilize the device class information to write native SPICE " + "elements for the devices." + ) + + gsi::method ("emit_comment", &NetlistSpiceWriterDelegateImpl::emit_comment, gsi::arg ("comment"), + "@brief Writes the given comment into the file" + ) + + gsi::method ("emit_line", &NetlistSpiceWriterDelegateImpl::emit_line, gsi::arg ("line"), + "@brief Writes the given line into the file" + ) + + gsi::method ("net_to_string", &NetlistSpiceWriterDelegateImpl::net_to_string, gsi::arg ("net"), + "@brief Gets the node ID for the given net\n" + "The node ID is a numeric string instead of the full name of the net. Numeric IDs are used within " + "SPICE netlist because they are usually shorter.\n" + ) + + gsi::method ("format_name", &NetlistSpiceWriterDelegateImpl::format_name, gsi::arg ("name"), + "@brief Formats the given name in a SPICE-compatible way" + ), + "@brief Provides a delegate for the SPICE writer for doing special formatting for devices\n" + "Supply a customized class to provide a specialized writing scheme for devices. " + "You need a customized class if you want to implement special devices or you want to use " + "subcircuits rather than the built-in devices.\n" + "\n" + "See \\NetlistSpiceWriter for more details.\n" + "\n" + "This class has been introduced in version 0.26." +); + +namespace { + +class NetlistSpiceWriterWithOwnership + : public db::NetlistSpiceWriter +{ +public: + NetlistSpiceWriterWithOwnership (NetlistSpiceWriterDelegateImpl *delegate) + : db::NetlistSpiceWriter (delegate), m_ownership (delegate) + { + if (delegate) { + delegate->keep (); + } + } + +private: + tl::shared_ptr m_ownership; +}; + +} + +db::NetlistSpiceWriter *new_spice_writer () +{ + return new db::NetlistSpiceWriter (); +} + +db::NetlistSpiceWriter *new_spice_writer2 (NetlistSpiceWriterDelegateImpl *delegate) +{ + return new NetlistSpiceWriterWithOwnership (delegate); +} + +Class db_NetlistWriter ("db", "NetlistWriter", + gsi::Methods (), + "@hide\n" +); + +Class db_NetlistSpiceWriter (db_NetlistWriter, "db", "NetlistSpiceWriter", + gsi::constructor ("new", &new_spice_writer, + "@brief Creates a new writer without delegate.\n" + ) + + gsi::constructor ("new", &new_spice_writer2, + "@brief Creates a new writer with a delegate.\n" + ), + "@brief Implements a netlist writer for the SPICE format.\n" + "Provide a delegate for customizing the way devices are written.\n" + "\n" + "Use the SPICE writer like this:\n" + "\n" + "@code\n" + "writer = RBA::NetlistSpiceWriter::new\n" + "netlist.write(path, writer)\n" + "@endcode\n" + "\n" + "You can give a custom description for the headline:\n" + "\n" + "@code\n" + "writer = RBA::NetlistSpiceWriter::new\n" + "netlist.write(path, writer, \"A custom description\")\n" + "@endcode\n" + "\n" + "To customize the output, you can use a device writer delegate.\n" + "The delegate is an object of a class derived from \\NetlistSpiceWriterDelegate which " + "reimplements several methods to customize the following parts:\n" + "\n" + "@ul\n" + "@li A global header (\\NetlistSpiceWriterDelegate#write_header): this method is called to print the part right after the headline @/li\n" + "@li A per-device class header (\\NetlistSpiceWriterDelegate#write_device_intro): this method is called for every device class and may print device-class specific headers (e.g. model definitions) @/li\n" + "@li Per-device output: this method (\\NetlistSpiceWriterDelegate#write_device): this method is called for every device and may print the device statement(s) in a specific way.\n" + "@/ul\n" + "\n" + "The delegate must use \\NetlistSpiceWriterDelegate#emit_line to print a line, \\NetlistSpiceWriterDelegate#emit_comment to print a comment etc.\n" + "For more method see \\NetlistSpiceWriterDelegate.\n" + "\n" + "A sample with a delegate is this:\n" + "\n" + "@code\n" + "class MyDelegate < RBA::NetlistSpiceWriterDelegate\n" + "\n" + " def write_header\n" + " emit_line(\"*** My special header\")\n" + " end\n" + "\n" + " def write_device_intro(cls)\n" + " emit_comment(\"My intro for class \" + cls.name)\n" + " end\n" + "\n" + " def write_device(dev)\n" + " if dev.device_class.name != \"MYDEVICE\"\n" + " emit_comment(\"Terminal #1: \" + net_to_string(dev.net_for_terminal(0)))\n" + " emit_comment(\"Terminal #2: \" + net_to_string(dev.net_for_terminal(1)))\n" + " super(dev)\n" + " emit_comment(\"After device \" + dev.expanded_name)\n" + " end\n" + "\n" + "end\n" + "\n" + "# write the netlist with delegate:\n" + "writer = RBA::NetlistSpiceWriter::new(MyDelegate::new)\n" + "netlist.write(path, writer)\n" + "@endcode\n" + "\n" + "This class has been introduced in version 0.26." +); + +} diff --git a/src/db/db/gsiDeclDbNetlistDeviceClasses.cc b/src/db/db/gsiDeclDbNetlistDeviceClasses.cc new file mode 100644 index 000000000..4bd11a670 --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistDeviceClasses.cc @@ -0,0 +1,202 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "gsiDecl.h" +#include "dbNetlistDeviceClasses.h" + +namespace gsi +{ + +extern Class decl_dbDeviceClass; + +Class decl_dbDeviceClassResistor (decl_dbDeviceClass, "db", "DeviceClassResistor", + gsi::constant ("TERMINAL_A", db::DeviceClassResistor::terminal_id_A, + "@brief A constant giving the terminal ID for terminal A" + ) + + gsi::constant ("TERMINAL_B", db::DeviceClassResistor::terminal_id_B, + "@brief A constant giving the terminal ID for terminal B" + ) + + gsi::constant ("PARAM_R", db::DeviceClassResistor::param_id_R, + "@brief A constant giving the parameter ID for parameter R" + ), + "@brief A device class for a resistor.\n" + "This class can be used to describe resistors. Resistors are defined by their combination behavior and " + "the basic parameter 'R' which is the resistance in Ohm.\n" + "\n" + "A resistor has two terminals, A and B.\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_dbDeviceClassCapacitor (decl_dbDeviceClass, "db", "DeviceClassCapacitor", + gsi::constant ("TERMINAL_A", db::DeviceClassCapacitor::terminal_id_A, + "@brief A constant giving the terminal ID for terminal A" + ) + + gsi::constant ("TERMINAL_B", db::DeviceClassCapacitor::terminal_id_B, + "@brief A constant giving the terminal ID for terminal B" + ) + + gsi::constant ("PARAM_C", db::DeviceClassCapacitor::param_id_C, + "@brief A constant giving the parameter ID for parameter C" + ), + "@brief A device class for a capacitor.\n" + "This class can be used to describe capacitors. Capacitors are defined by their combination behavior and " + "the basic parameter 'C' which is the capacitance in Farad.\n" + "\n" + "A capacitor has two terminals, A and B.\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_dbDeviceClassInductor (decl_dbDeviceClass, "db", "DeviceClassInductor", + gsi::constant ("TERMINAL_A", db::DeviceClassInductor::terminal_id_A, + "@brief A constant giving the terminal ID for terminal A" + ) + + gsi::constant ("TERMINAL_B", db::DeviceClassInductor::terminal_id_B, + "@brief A constant giving the terminal ID for terminal B" + ) + + gsi::constant ("PARAM_L", db::DeviceClassInductor::param_id_L, + "@brief A constant giving the parameter ID for parameter L" + ), + "@brief A device class for an inductor.\n" + "This class can be used to describe inductors. Inductors are defined by their combination behavior and " + "the basic parameter 'L' which is the inductance in Henry.\n" + "\n" + "An inductor has two terminals, A and B.\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_dbDeviceClassDiode (decl_dbDeviceClass, "db", "DeviceClassDiode", + gsi::constant ("TERMINAL_A", db::DeviceClassDiode::terminal_id_A, + "@brief A constant giving the terminal ID for terminal A" + ) + + gsi::constant ("TERMINAL_C", db::DeviceClassDiode::terminal_id_C, + "@brief A constant giving the terminal ID for terminal C" + ) + + gsi::constant ("PARAM_A", db::DeviceClassDiode::param_id_A, + "@brief A constant giving the parameter ID for parameter A" + ), + "@brief A device class for a diode.\n" + "This class can be used to describe diodes. Diodes are defined by their combination behavior and " + "the basic parameter 'A' which is their area in square micrometers.\n" + "\n" + "Diodes only combine when parallel and in the same direction. In this case, their areas are added." + "\n" + "An inductor has two terminals, A (anode) and C (cathode).\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_dbDeviceClassMOS3Transistor (decl_dbDeviceClass, "db", "DeviceClassMOS3Transistor", + gsi::constant ("TERMINAL_S", db::DeviceClassMOS3Transistor::terminal_id_S, + "@brief A constant giving the terminal ID for terminal S" + ) + + gsi::constant ("TERMINAL_D", db::DeviceClassMOS3Transistor::terminal_id_D, + "@brief A constant giving the terminal ID for terminal D" + ) + + gsi::constant ("TERMINAL_G", db::DeviceClassMOS3Transistor::terminal_id_G, + "@brief A constant giving the terminal ID for terminal G" + ) + + gsi::constant ("PARAM_L", db::DeviceClassMOS3Transistor::param_id_L, + "@brief A constant giving the parameter ID for parameter L" + ) + + gsi::constant ("PARAM_W", db::DeviceClassMOS3Transistor::param_id_W, + "@brief A constant giving the parameter ID for parameter W" + ) + + gsi::constant ("PARAM_AS", db::DeviceClassMOS3Transistor::param_id_AS, + "@brief A constant giving the parameter ID for parameter AS" + ) + + gsi::constant ("PARAM_AD", db::DeviceClassMOS3Transistor::param_id_AD, + "@brief A constant giving the parameter ID for parameter AD" + ) + + gsi::constant ("PARAM_PS", db::DeviceClassMOS3Transistor::param_id_PS, + "@brief A constant giving the parameter ID for parameter PS" + ) + + gsi::constant ("PARAM_PD", db::DeviceClassMOS3Transistor::param_id_PD, + "@brief A constant giving the parameter ID for parameter PD" + ), + "@brief A device class for a 3-terminal MOS transistor.\n" + "This class can be used to describe MOS transistors without a bulk terminal. " + "A device class for a MOS transistor with a bulk terminal is \\DeviceClassMOS4Transistor. " + "MOS transistors are defined by their combination behavior and the basic parameters.\n" + "\n" + "The parameters are L, W, AS, AD, PS and PD for the gate length and width in micrometers, source and drain area " + "in square micrometers and the source and drain perimeter in micrometers.\n" + "\n" + "The terminals are S, G and D for source, gate and drain.\n" + "\n" + "MOS transistors combine in parallel mode, when both gate lengths are identical and " + "their gates are connected (source and drain can be swapped). In this case, their widths and source and drain " + "areas are added.\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_dbDeviceClassMOS4Transistor (decl_dbDeviceClass, "db", "DeviceClassMOS4Transistor", + gsi::constant ("TERMINAL_S", db::DeviceClassMOS4Transistor::terminal_id_S, + "@brief A constant giving the terminal ID for terminal S" + ) + + gsi::constant ("TERMINAL_D", db::DeviceClassMOS4Transistor::terminal_id_D, + "@brief A constant giving the terminal ID for terminal D" + ) + + gsi::constant ("TERMINAL_G", db::DeviceClassMOS4Transistor::terminal_id_G, + "@brief A constant giving the terminal ID for terminal G" + ) + + gsi::constant ("TERMINAL_B", db::DeviceClassMOS4Transistor::terminal_id_B, + "@brief A constant giving the terminal ID for terminal B" + ) + + gsi::constant ("PARAM_L", db::DeviceClassMOS4Transistor::param_id_L, + "@brief A constant giving the parameter ID for parameter L" + ) + + gsi::constant ("PARAM_W", db::DeviceClassMOS4Transistor::param_id_W, + "@brief A constant giving the parameter ID for parameter W" + ) + + gsi::constant ("PARAM_AS", db::DeviceClassMOS4Transistor::param_id_AS, + "@brief A constant giving the parameter ID for parameter AS" + ) + + gsi::constant ("PARAM_AD", db::DeviceClassMOS4Transistor::param_id_AD, + "@brief A constant giving the parameter ID for parameter AD" + ) + + gsi::constant ("PARAM_PS", db::DeviceClassMOS4Transistor::param_id_PS, + "@brief A constant giving the parameter ID for parameter PS" + ) + + gsi::constant ("PARAM_PD", db::DeviceClassMOS4Transistor::param_id_PD, + "@brief A constant giving the parameter ID for parameter PD" + ), + "@brief A device class for a 4-terminal MOS transistor.\n" + "This class can be used to describe MOS transistors with a bulk terminal. " + "A device class for a MOS transistor without a bulk terminal is \\DeviceClassMOS3Transistor. " + "MOS transistors are defined by their combination behavior and the basic parameters.\n" + "\n" + "The parameters are L, W, AS, AD, PS and PD for the gate length and width in micrometers, source and drain area " + "in square micrometers and the source and drain perimeter in micrometers.\n" + "\n" + "The terminals are S, G, D and B for source, gate, drain and bulk.\n" + "\n" + "MOS transistors combine in parallel mode, when both gate lengths are identical and " + "their gates and bulk terminals are connected (source and drain can be swapped). In this case, their widths and source and drain " + "areas are added.\n" + "\n" + "This class has been introduced in version 0.26." +); + +} diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc new file mode 100644 index 000000000..911e40aed --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc @@ -0,0 +1,441 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "gsiDecl.h" +#include "dbNetlistDeviceExtractor.h" +#include "dbNetlistDeviceExtractorClasses.h" + +namespace { + +/** + * @brief A NetlistDeviceExtractor implementation that allows reimplementation of the virtual methods + */ +class GenericDeviceExtractor + : public db::NetlistDeviceExtractor +{ +public: + GenericDeviceExtractor () + : db::NetlistDeviceExtractor (std::string ()) + { + // .. nothing yet .. + } + + void register_device_class (db::DeviceClass *device_class) + { + // the class is owned by the extractor + device_class->keep (); + db::NetlistDeviceExtractor::register_device_class (device_class); + } + + void setup_fb () + { + return db::NetlistDeviceExtractor::setup (); + } + + virtual void setup () + { + if (cb_setup.can_issue ()) { + cb_setup.issue (&GenericDeviceExtractor::setup_fb); + } else { + db::NetlistDeviceExtractor::setup (); + } + } + + db::Connectivity get_connectivity_fb (const db::Layout &layout, const std::vector &layers) const + { + return db::NetlistDeviceExtractor::get_connectivity (layout, layers); + } + + virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector &layers) const + { + if (cb_get_connectivity.can_issue ()) { + return cb_get_connectivity.issue &> (&GenericDeviceExtractor::get_connectivity_fb, layout, layers); + } else { + return db::NetlistDeviceExtractor::get_connectivity (layout, layers); + } + } + + void extract_devices_fb (const std::vector &layer_geometry) + { + return db::NetlistDeviceExtractor::extract_devices (layer_geometry); + } + + virtual void extract_devices (const std::vector &layer_geometry) + { + if (cb_extract_devices.can_issue ()) { + cb_extract_devices.issue &> (&GenericDeviceExtractor::extract_devices_fb, layer_geometry); + } else { + db::NetlistDeviceExtractor::extract_devices (layer_geometry); + } + } + + gsi::Callback cb_setup; + gsi::Callback cb_get_connectivity; + gsi::Callback cb_extract_devices; +}; + +} + +namespace tl +{ + +template<> struct type_traits : public tl::type_traits +{ + // mark "NetlistDeviceExtractor" as having a default ctor and no copy ctor + typedef tl::false_tag has_copy_constructor; + typedef tl::true_tag has_default_constructor; +}; + +} + +namespace gsi +{ + +Class decl_dbNetlistDeviceExtractorError ("db", "NetlistDeviceExtractorError", + gsi::method ("message", &db::NetlistDeviceExtractorError::message, + "@brief Gets the message text.\n" + ) + + gsi::method ("message=", &db::NetlistDeviceExtractorError::set_message, gsi::arg ("message"), + "@brief Sets the message text.\n" + ) + + gsi::method ("cell_name", &db::NetlistDeviceExtractorError::cell_name, + "@brief Gets the cell name.\n" + "See \\cell_name= for details about this attribute." + ) + + gsi::method ("cell_name=", &db::NetlistDeviceExtractorError::set_cell_name, gsi::arg ("cell_name"), + "@brief Sets the cell name.\n" + "The cell name is the name of the layout cell which was treated. This is " + "also the name of the circuit the device should have appeared in (it may be dropped because of this error). " + "If netlist hierarchy manipulation happens however, the circuit may not exist " + "any longer or may be renamed." + ) + + gsi::method ("geometry", &db::NetlistDeviceExtractorError::geometry, + "@brief Gets the geometry.\n" + "See \\geometry= for more details." + ) + + gsi::method ("geometry=", &db::NetlistDeviceExtractorError::set_geometry, gsi::arg ("polygon"), + "@brief Sets the geometry.\n" + "The geometry is optional. If given, a marker will be shown when selecting this error." + ) + + gsi::method ("category_name", &db::NetlistDeviceExtractorError::category_name, + "@brief Gets the category name.\n" + "See \\category_name= for more details." + ) + + gsi::method ("category_name=", &db::NetlistDeviceExtractorError::set_category_name, gsi::arg ("name"), + "@brief Sets the category name.\n" + "The category name is optional. If given, it specifies a formal category name. Errors with the same " + "category name are shown in that category. If in addition a category description is specified " + "(see \\category_description), this description will be displayed as the title of." + ) + + gsi::method ("category_description", &db::NetlistDeviceExtractorError::category_description, + "@brief Gets the category description.\n" + "See \\category_name= for details about categories." + ) + + gsi::method ("category_description=", &db::NetlistDeviceExtractorError::set_category_description, gsi::arg ("description"), + "@brief Sets the category description.\n" + "See \\category_name= for details about categories." + ), + "@brief An error that occured during device extraction\n" + "The device extractor will keep errors that occured during extraction of the devices. " + "It does not by using this error class.\n" + "\n" + "An error is basically described by the cell/circuit it occures in and the message. " + "In addition, a geometry may be attached forming a marker that can be shown when the error is selected. " + "The geometry is given as a \\DPolygon object. If no geometry is specified, this polygon is empty.\n" + "\n" + "For categorization of the errors, a category name and description may be specified. If given, the " + "errors will be shown in the specified category. The category description is optional.\n" + "\n" + "This class has been introduced in version 0.26." +); + +static const std::string &ld_name (const db::NetlistDeviceExtractorLayerDefinition *ld) +{ + return ld->name; +} + +static const std::string &ld_description (const db::NetlistDeviceExtractorLayerDefinition *ld) +{ + return ld->description; +} + +static size_t ld_index (const db::NetlistDeviceExtractorLayerDefinition *ld) +{ + return ld->index; +} + +Class decl_dbNetlistDeviceExtractorLayerDefinition ("db", "NetlistDeviceExtractorLayerDefinition", + gsi::method_ext ("name", &ld_name, + "@brief Gets the name of the layer.\n" + ) + + gsi::method_ext ("description", &ld_description, + "@brief Gets the description of the layer.\n" + ) + + gsi::method_ext ("index", &ld_index, + "@brief Gets the index of the layer.\n" + ), + "@brief Describes a layer used in the device extraction\n" + "This read-only structure is used to describe a layer in the device extraction.\n" + "Every device has specific layers used in the device extraction process.\n" + "Layer definitions can be retrieved using \\NetlistDeviceExtractor#each_layer.\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_dbNetlistDeviceExtractor ("db", "DeviceExtractorBase", + gsi::method ("name", &db::NetlistDeviceExtractor::name, + "@brief Gets the name of the device extractor and the device class." + ) + + gsi::iterator ("each_layer_definition", &db::NetlistDeviceExtractor::begin_layer_definitions, &db::NetlistDeviceExtractor::end_layer_definitions, + "@brief Iterates over all layer definitions." + ) + + gsi::iterator ("each_error", &db::NetlistDeviceExtractor::begin_errors, &db::NetlistDeviceExtractor::end_errors, + "@brief Iterates over all errors collected in the device extractor." + ), + "@brief The base class for all device extractors.\n" + "This is an abstract base class for device extractors. See \\GenericDeviceExtractor for a generic " + "class which you can reimplement to supply your own customized device extractor. " + "In many cases using one of the preconfigured specific device extractors may be useful already and " + "it's not required to implement a custom one. For an example about a preconfigured device extractor see " + "\\DeviceExtractorMOS3Transistor.\n" + "\n" + "This class cannot and should not be instantiated explicitly. Use one of the subclasses instead.\n" + "\n" + "This class has been introduced in version 0.26." +); + +Class decl_GenericDeviceExtractor (decl_dbNetlistDeviceExtractor, "db", "GenericDeviceExtractor", + gsi::method ("name=", &GenericDeviceExtractor::set_name, + "@brief Sets the name of the device extractor and the device class." + ) + + gsi::callback ("setup", &GenericDeviceExtractor::setup, &GenericDeviceExtractor::cb_setup, + "@brief Sets up the extractor.\n" + "This method is supposed to set up the device extractor. This involves three basic steps:\n" + "defining the name, the device classe and setting up the device layers.\n" + "\n" + "Use \\name= to give the extractor and it's device class a name.\n" + "Use \\register_device_class to register the device class you need.\n" + "Defined the layers by calling \\define_layer once or several times.\n" + ) + + gsi::callback ("get_connectivity", &GenericDeviceExtractor::get_connectivity, &GenericDeviceExtractor::cb_get_connectivity, + gsi::arg ("layout"), gsi::arg ("layers"), + "@brief Gets the connectivity object used to extract the device geometry.\n" + "This method shall raise an error, if the input layer are not properly defined (e.g.\n" + "too few etc.)\n" + "\n" + "The 'layers' argument specifies the actual layer layouts for the logical device layers (see \\define_layer). " + "The list of layers corresponds to the number of layers defined. Use the layer indexes from this list " + "to build the connectivity with \\Connectivity#connect." + ) + + gsi::callback ("extract_devices", &GenericDeviceExtractor::extract_devices, &GenericDeviceExtractor::cb_extract_devices, + gsi::arg ("layer_geometry"), + "@brief Extracts the devices from the given shape cluster.\n" + "\n" + "The shape cluster is a set of geometries belonging together in terms of the\n" + "connectivity defined by \"get_connectivity\". The cluster might cover multiple devices,\n" + "so the implementation needs to consider this case. The geometries are already merged.\n" + "\n" + "The implementation of this method shall use \"create_device\" to create new\n" + "devices based on the geometry found. It shall use \"define_terminal\" to define\n" + "terminals by which the nets extracted in the network extraction step connect\n" + "to the new devices.\n" + ) + + gsi::method ("register_device_class", &GenericDeviceExtractor::register_device_class, gsi::arg ("device_class"), + "@brief Registers a device class.\n" + "The device class object will become owned by the netlist and must not be deleted by\n" + "the caller. The name of the device class will be changed to the name given to\n" + "the device extractor.\n" + "This method shall be used inside the implementation of \\setup to register\n" + "the device classes.\n" + ) + + gsi::method ("define_layer", &GenericDeviceExtractor::define_layer, gsi::arg ("name"), gsi::arg ("description"), + "@brief Defines a layer.\n" + "Each call will define one more layer for the device extraction.\n" + "This method shall be used inside the implementation of \\setup to define\n" + "the device layers. The actual geometries are later available to \\extract_devices\n" + "in the order the layers are defined.\n" + ) + + gsi::method ("create_device", &GenericDeviceExtractor::create_device, + "@brief Creates a device.\n" + "The device object returned can be configured by the caller, e.g. set parameters.\n" + "It will be owned by the netlist and must not be deleted by the caller.\n" + ) + + gsi::method ("define_terminal", (void (GenericDeviceExtractor::*) (db::Device *, size_t, size_t, const db::Polygon &)) &GenericDeviceExtractor::define_terminal, + gsi::arg ("device"), gsi::arg ("terminal_id"), gsi::arg ("layer_index"), gsi::arg ("shape"), + "@brief Defines a device terminal.\n" + "This method will define a terminal to the given device and the given terminal ID. \n" + "The terminal will be placed on the layer given by \"layer_index\". The layer index \n" + "is the index of the layer during layer definition. The first layer is 0, the second layer 1 etc.\n" + "\n" + "This version produces a terminal with a shape given by the polygon. Note that the polygon is\n" + "specified in database units.\n" + ) + + gsi::method ("define_terminal", (void (GenericDeviceExtractor::*) (db::Device *, size_t, size_t, const db::Box &)) &GenericDeviceExtractor::define_terminal, + gsi::arg ("device"), gsi::arg ("terminal_id"), gsi::arg ("layer_index"), gsi::arg ("shape"), + "@brief Defines a device terminal.\n" + "This method will define a terminal to the given device and the given terminal ID. \n" + "The terminal will be placed on the layer given by \"layer_index\". The layer index \n" + "is the index of the layer during layer definition. The first layer is 0, the second layer 1 etc.\n" + "\n" + "This version produces a terminal with a shape given by the box. Note that the box is\n" + "specified in database units.\n" + ) + + gsi::method ("define_terminal", (void (GenericDeviceExtractor::*) (db::Device *, size_t, size_t, const db::Point &)) &GenericDeviceExtractor::define_terminal, + gsi::arg ("device"), gsi::arg ("terminal_id"), gsi::arg ("layer_index"), gsi::arg ("point"), + "@brief Defines a device terminal.\n" + "This method will define a terminal to the given device and the given terminal ID. \n" + "The terminal will be placed on the layer given by \"layer_index\". The layer index \n" + "is the index of the layer during layer definition. The first layer is 0, the second layer 1 etc.\n" + "\n" + "This version produces a point-like terminal. Note that the point is\n" + "specified in database units.\n" + ) + + gsi::method ("dbu", &GenericDeviceExtractor::dbu, + "@brief Gets the database unit\n" + ) + + gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &)) &GenericDeviceExtractor::error, + gsi::arg ("message"), + "@brief Issues an error with the given message\n" + ) + + gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const db::DPolygon &)) &GenericDeviceExtractor::error, + gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues an error with the given message and micrometer-units polygon geometry\n" + ) + + gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const db::Polygon &)) &GenericDeviceExtractor::error, + gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues an error with the given message and databse-unit polygon geometry\n" + ) + + gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &)) &GenericDeviceExtractor::error, + gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), + "@brief Issues an error with the given category name and description, message\n" + ) + + gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &, const db::DPolygon &)) &GenericDeviceExtractor::error, + gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues an error with the given category name and description, message and micrometer-units polygon geometry\n" + ) + + gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &, const db::Polygon &)) &GenericDeviceExtractor::error, + gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues an error with the given category name and description, message and databse-unit polygon geometry\n" + ), + "@brief The basic class for implementing custom device extractors.\n" + "\n" + "This class serves as a base class for implementing customized device extractors. " + "This class does not provide any extraction functionality, so you have to " + "implement every detail.\n" + "\n" + "Device extraction requires a few definitions. The definitions are made in the reimplementation of the \\setup\n" + "method. Required definitions to be made are:\n" + "\n" + "@ul\n" + " @li The name of the extractor. This will also be the name of the device class produced by the extractor. " + " The name is set using \\name=. @/li\n" + " @li The device class of the devices to produce. The device class is registered using \\register_device_class. @/li\n" + " @li The layers used for the device extraction. These are input layers for the extraction as well as " + " output layers for defining the terminals. Terminals are the poins at which the nets connect to the devices.\n" + " Layers are defined using \\define_layer. Initially, layers are abstract definitions with a name and a description.\n" + " Concrete layers will be given when defining the connectivitiy. @/li\n" + "@/ul\n" + "\n" + "When the device extraction is started, the device extraction algorithm will first ask the device extractor " + "for the 'connectivity'. This is not a connectivity in a sense of electrical connections. The connectivity defines are " + "logical compound that makes up the device. 'Connected' shapes are collected and presented to the device extractor.\n" + "The connectivity is obtained by calling \\get_connectivity. This method must be " + "implemented to produce the connectivity.\n" + "\n" + "Finally, the individual devices need to be extracted. Each cluster of connected shapes is presented to the " + "device extractor. A cluster may include more than one device. It's the device extractor's responsibilty to " + "extract the devices from this cluster and deliver the devices through \\create_device. In addition, terminals " + "have to be defined, so the net extractor can connect to the devices. Terminal definitions are made through " + "\\define_terminal. The device extraction is implemented in the \\extract_devices method.\n" + "\n" + "If errors occur during device extraction, the \\error method may be used to issue such errors. Errors " + "reported this way are kept in the error log.\n" + "\n" + "This class has been introduced in version 0.26." +); + +db::NetlistDeviceExtractorMOS3Transistor *make_mos3_extractor (const std::string &name) +{ + return new db::NetlistDeviceExtractorMOS3Transistor (name); +} + +Class decl_NetlistDeviceExtractorMOS3Transistor (decl_dbNetlistDeviceExtractor, "db", "DeviceExtractorMOS3Transistor", + gsi::constructor ("new", &make_mos3_extractor, gsi::arg ("name"), + "@brief Creates a new device extractor with the given name." + ), + "@brief A device extractor for a three-terminal MOS transistor\n" + "\n" + "This class supplies the generic extractor for a MOS device.\n" + "The device is defined by two basic input layers: the diffusion area\n" + "(source and drain) and the gate area. It requires a third layer\n" + "(poly) to put the gate terminals on. The separation between poly\n" + "and allows separating the device recognition layer (gate) from the\n" + "conductive layer.\n" + "\n" + "The device class produced by this extractor is \\DeviceClassMOS3Transistor.\n" + "The extractor extracts the four parameters of this class: L, W, AS and AD.\n" + "\n" + "The diffusion area is distributed on the number of gates connecting to\n" + "the particular source or drain area.\n" + "\n" + "This class is a closed one and methods cannot be reimplemented. To reimplement " + "specific methods, see \\DeviceExtractor.\n" + "\n" + "This class has been introduced in version 0.26." +); + +db::NetlistDeviceExtractorMOS4Transistor *make_mos4_extractor (const std::string &name) +{ + return new db::NetlistDeviceExtractorMOS4Transistor (name); +} + +Class decl_NetlistDeviceExtractorMOS4Transistor (decl_dbNetlistDeviceExtractor, "db", "DeviceExtractorMOS4Transistor", + gsi::constructor ("new", &make_mos4_extractor, gsi::arg ("name"), + "@brief Creates a new device extractor with the given name." + ), + "@brief A device extractor for a four-terminal MOS transistor\n" + "\n" + "This class supplies the generic extractor for a MOS device.\n" + "The device is defined by two basic input layers: the diffusion area\n" + "(source and drain) and the gate area. It requires a third layer\n" + "(poly) to put the gate terminals on and a forth layer to put the bulk\n" + "terminal an. The separation between poly\n" + "and allows separating the device recognition layer (gate) from the\n" + "conductive layer.\n" + "\n" + "The bulk terminal layer can be an empty layer representing the substrate.\n" + "In this use mode the bulk terminal shapes will be produced there. This\n" + "layer then needs to be connected to a global net to establish the net.\n" + "\n" + "The device class produced by this extractor is \\DeviceClassMOS4Transistor.\n" + "The extractor extracts the four parameters of this class: L, W, AS and AD.\n" + "\n" + "The diffusion area is distributed on the number of gates connecting to\n" + "the particular source or drain area.\n" + "\n" + "This class is a closed one and methods cannot be reimplemented. To reimplement " + "specific methods, see \\DeviceExtractor.\n" + "\n" + "This class has been introduced in version 0.26." +); + +} diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc index cd063a325..d57da88da 100644 --- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc +++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc @@ -409,7 +409,7 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "\n" "This method has been introduced in version 0.23.\n" ) + - gsi::method ("shape_flags=", &db::RecursiveShapeIterator::shape_flags, + gsi::method ("shape_flags=", (void (db::RecursiveShapeIterator::*)(unsigned int)) &db::RecursiveShapeIterator::shape_flags, "@brief Specifies the shape selection flags\n" "@args flags\n" "\n" @@ -453,7 +453,7 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS gsi::method ("cell_index", &db::RecursiveShapeIterator::cell_index, "@brief Gets the current cell's index \n" ) + - gsi::method ("next", &db::RecursiveShapeIterator::next, + gsi::method ("next", (void (db::RecursiveShapeIterator::*) ()) &db::RecursiveShapeIterator::next, "@brief Increment the iterator\n" "This moves the iterator to the next shape inside the search scope." ) + diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 545641b42..965fc3ce1 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -23,9 +23,15 @@ #include "gsiDecl.h" #include "dbRegion.h" +#include "dbRegionUtils.h" +#include "dbDeepRegion.h" +#include "dbOriginalLayerRegion.h" #include "dbPolygonTools.h" #include "dbLayoutUtils.h" #include "dbShapes.h" +#include "dbDeepShapeStore.h" +#include "dbRegion.h" +#include "dbRegionProcessors.h" #include "tlGlobPattern.h" #include @@ -72,140 +78,44 @@ static db::Region *new_shapes (const db::Shapes &s) return r; } -struct DotDelivery +static db::Region *new_texts_as_boxes1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern, db::Coord enl) { - typedef db::Edges container_type; - - DotDelivery () : container () - { - container.reset (new container_type ()); - container->set_merged_semantics (false); - } - - void insert (const db::Point &pt) - { - container->insert (db::Edge (pt, pt)); - } - - std::auto_ptr container; -}; - -struct BoxDelivery -{ - typedef db::Region container_type; - - BoxDelivery () : container () - { - container.reset (new container_type ()); - } - - void insert (const db::Point &pt) - { - container->insert (db::Box (pt - db::Vector (1, 1), pt + db::Vector (1, 1))); - } - - std::auto_ptr container; -}; - -template -static typename Delivery::container_type *new_texts (const db::RecursiveShapeIterator &si_in, const std::string &pat, bool pattern) -{ - db::RecursiveShapeIterator si (si_in); - si.shape_flags (db::ShapeIterator::Texts); - - tl::GlobPattern glob_pat; - bool all = false; - if (pattern) { - if (pat == "*") { - all = true; - } else { - glob_pat = tl::GlobPattern (pat); - } - } - - Delivery delivery; - - while (! si.at_end ()) { - if (si.shape ().is_text () && - (all || (pattern && glob_pat.match (si.shape ().text_string ())) || (!pattern && si.shape ().text_string () == pat))) { - db::Text t; - si.shape ().text (t); - t.transform (si.trans ()); - delivery.insert (t.box ().center ()); - } - si.next (); - } - - return delivery.container.release (); + return new db::Region (db::Region (si).texts_as_boxes (pat, pattern, enl)); } -template -static typename Delivery::container_type *texts (const db::Region *r, const std::string &pat, bool pattern) +static db::Region *new_texts_as_boxes2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const std::string &pat, bool pattern, db::Coord enl) { - return new_texts (r->iter (), pat, pattern); + return new db::Region (db::Region (si).texts_as_boxes (pat, pattern, enl, dss)); } -template -static typename Delivery::container_type *corners (const db::Region *r, double angle_start, double angle_end) +static db::Edges *texts_as_dots1 (const db::Region *r, const std::string &pat, bool pattern) { - db::CplxTrans t_start (1.0, angle_start, false, db::DVector ()); - db::CplxTrans t_end (1.0, angle_end, false, db::DVector ()); + return new db::Edges (r->texts_as_dots (pat, pattern)); +} - bool big_angle = (angle_end - angle_start + db::epsilon) > 180.0; - bool all = (angle_end - angle_start - db::epsilon) > 360.0; +static db::Edges *texts_as_dots2 (const db::Region *r, db::DeepShapeStore &dss, const std::string &pat, bool pattern) +{ + return new db::Edges (r->texts_as_dots (pat, pattern, dss)); +} - Delivery delivery; +static db::Region *texts_as_boxes1 (const db::Region *r, const std::string &pat, bool pattern, db::Coord enl) +{ + return new db::Region (r->texts_as_boxes (pat, pattern, enl)); +} - for (db::Region::const_iterator p = r->begin_merged (); ! p.at_end (); ++p) { +static db::Region *texts_as_boxes2 (const db::Region *r, db::DeepShapeStore &dss, const std::string &pat, bool pattern, db::Coord enl) +{ + return new db::Region (r->texts_as_boxes (pat, pattern, enl, dss)); +} - size_t n = p->holes () + 1; - for (size_t i = 0; i < n; ++i) { +static db::Edges corners_to_dots (const db::Region *r, double angle_start, double angle_end) +{ + return r->processed (db::CornersAsDots (angle_start, angle_end)); +} - const db::Polygon::contour_type &ctr = p->contour (int (i)); - size_t nn = ctr.size (); - if (nn > 2) { - - db::Point pp = ctr [nn - 2]; - db::Point pt = ctr [nn - 1]; - for (size_t j = 0; j < nn; ++j) { - - db::Point pn = ctr [j]; - - if (all) { - delivery.insert (pt); - } else { - - db::Vector vin (pt - pp); - db::DVector vout (pn - pt); - - db::DVector v1 = t_start * vin; - db::DVector v2 = t_end * vin; - - bool vp1 = db::vprod_sign (v1, vout) >= 0; - bool vp2 = db::vprod_sign (v2, vout) <= 0; - - if (big_angle && (vp1 || vp2)) { - delivery.insert (pt); - } else if (! big_angle && vp1 && vp2) { - if (db::sprod_sign (v1, vout) > 0 && db::sprod_sign (v2, vout) > 0) { - delivery.insert (pt); - } - } - - } - - pp = pt; - pt = pn; - - } - - } - - } - - } - - return delivery.container.release (); +static db::Region corners_to_boxes (const db::Region *r, double angle_start, double angle_end, db::Coord dim) +{ + return r->processed (db::CornersAsRectangles (angle_start, angle_end, dim)); } static db::Region *new_si (const db::RecursiveShapeIterator &si) @@ -213,11 +123,21 @@ static db::Region *new_si (const db::RecursiveShapeIterator &si) return new db::Region (si); } +static db::Region *new_sid (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) +{ + return new db::Region (si, dss, area_ratio, max_vertex_count); +} + static db::Region *new_si2 (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) { return new db::Region (si, trans); } +static db::Region *new_sid2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const db::ICplxTrans &trans, double area_ratio, size_t max_vertex_count) +{ + return new db::Region (si, dss, trans, true, area_ratio, max_vertex_count); +} + static std::string to_string0 (const db::Region *r) { return r->to_string (); @@ -295,38 +215,22 @@ static void insert_si2 (db::Region *r, db::RecursiveShapeIterator si, db::ICplxT static db::Region minkowsky_sum_pe (const db::Region *r, const db::Edge &e) { - db::Region o; - for (db::Region::const_iterator p = r->begin_merged (); ! p.at_end (); ++p) { - o.insert (db::minkowsky_sum (*p, e, false)); - } - return o; + return r->processed (db::minkowsky_sum_computation (e)); } static db::Region minkowsky_sum_pp (const db::Region *r, const db::Polygon &q) { - db::Region o; - for (db::Region::const_iterator p = r->begin_merged (); ! p.at_end (); ++p) { - o.insert (db::minkowsky_sum (*p, q, false)); - } - return o; + return r->processed (db::minkowsky_sum_computation (q)); } static db::Region minkowsky_sum_pb (const db::Region *r, const db::Box &q) { - db::Region o; - for (db::Region::const_iterator p = r->begin_merged (); ! p.at_end (); ++p) { - o.insert (db::minkowsky_sum (*p, q, false)); - } - return o; + return r->processed (db::minkowsky_sum_computation (q)); } static db::Region minkowsky_sum_pc (const db::Region *r, const std::vector &q) { - db::Region o; - for (db::Region::const_iterator p = r->begin_merged (); ! p.at_end (); ++p) { - o.insert (db::minkowsky_sum (*p, q, false)); - } - return o; + return r->processed (db::minkowsky_sum_computation > (q)); } static db::Region &move_p (db::Region *r, const db::Vector &p) @@ -353,12 +257,7 @@ static db::Region moved_xy (const db::Region *r, db::Coord x, db::Coord y) static db::Region extents2 (const db::Region *r, db::Coord dx, db::Coord dy) { - db::Region e; - e.reserve (r->size ()); - for (db::Region::const_iterator i = r->begin_merged (); ! i.at_end (); ++i) { - e.insert (i->box ().enlarged (db::Vector (dx, dy))); - } - return e; + return r->processed (db::Extents (dx, dy)); } static db::Region extents1 (const db::Region *r, db::Coord d) @@ -373,33 +272,12 @@ static db::Region extents0 (const db::Region *r) static db::Region extent_refs (const db::Region *r, double fx1, double fy1, double fx2, double fy2, db::Coord dx, db::Coord dy) { - db::Region e; - e.reserve (r->size ()); - for (db::Region::const_iterator i = r->begin_merged (); ! i.at_end (); ++i) { - db::Box b = i->box (); - db::Point p1 (b.left () + db::coord_traits::rounded (fx1 * b.width ()), - b.bottom () + db::coord_traits::rounded (fy1 * b.height ())); - db::Point p2 (b.left () + db::coord_traits::rounded (fx2 * b.width ()), - b.bottom () + db::coord_traits::rounded (fy2 * b.height ())); - e.insert (db::Box (p1, p2).enlarged (db::Vector (dx, dy))); - } - return e; + return r->processed (db::RelativeExtents (fx1, fy1, fx2, fy2, dx, dy)); } static db::Edges extent_refs_edges (const db::Region *r, double fx1, double fy1, double fx2, double fy2) { - db::Edges e; - e.set_merged_semantics (false); - e.reserve (r->size ()); - for (db::Region::const_iterator i = r->begin_merged (); ! i.at_end (); ++i) { - db::Box b = i->box (); - db::Point p1 (b.left () + db::coord_traits::rounded (fx1 * b.width ()), - b.bottom () + db::coord_traits::rounded (fy1 * b.height ())); - db::Point p2 (b.left () + db::coord_traits::rounded (fx2 * b.width ()), - b.bottom () + db::coord_traits::rounded (fy2 * b.height ())); - e.insert (db::Edge (p1, p2)); - } - return e; + return r->processed (db::RelativeExtentsAsEdges (fx1, fy1, fx2, fy2)); } static db::Region with_perimeter1 (const db::Region *r, db::Region::perimeter_type perimeter, bool inverse) @@ -518,6 +396,11 @@ static db::Region non_rectilinear (const db::Region *r) return r->filtered (f); } +static void break_polygons (db::Region *r, size_t max_vertex_count, double max_area_ratio) +{ + r->process (db::PolygonBreaker (max_vertex_count, max_area_ratio)); +} + static db::Region &size_ext (db::Region *r, db::Coord d) { r->size (d); @@ -708,6 +591,16 @@ static Container *decompose_trapezoids (const db::Region *r, int mode) return shapes.release (); } +static bool is_deep (const db::Region *region) +{ + return dynamic_cast (region->delegate ()) != 0; +} + +static size_t id (const db::Region *r) +{ + return tl::id_of (r->delegate ()); +} + // provided by gsiDeclDbPolygon.cc: int td_simple (); int po_any (); @@ -756,9 +649,8 @@ Class decl_Region ("db", "Region", "\n" "This constructor has been introduced in version 0.25." ) + - constructor ("new", &new_si, + constructor ("new", &new_si, gsi::arg ("shape_iterator"), "@brief Constructor from a hierarchical shape set\n" - "@args shape_iterator\n" "\n" "This constructor creates a region from the shapes delivered by the given recursive shape iterator.\n" "Text objects and edges are not inserted, because they cannot be converted to polygons.\n" @@ -771,9 +663,8 @@ Class decl_Region ("db", "Region", "r = RBA::Region::new(layout.begin_shapes(cell, layer))\n" "@/code\n" ) + - constructor ("new", &new_si2, + constructor ("new", &new_si2, gsi::arg ("shape_iterator"), gsi::arg ("trans"), "@brief Constructor from a hierarchical shape set with a transformation\n" - "@args shape_iterator, trans\n" "\n" "This constructor creates a region from the shapes delivered by the given recursive shape iterator.\n" "Text objects and edges are not inserted, because they cannot be converted to polygons.\n" @@ -789,15 +680,49 @@ Class decl_Region ("db", "Region", "r = RBA::Region::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" "@/code\n" ) + - constructor ("new", &new_texts, gsi::arg("shape_iterator"), gsi::arg ("expr"), gsi::arg ("as_pattern", true), + constructor ("new", &new_sid, gsi::arg ("shape_iterator"), gsi::arg ("deep_shape_store"), gsi::arg ("area_ratio", 0.0), gsi::arg ("max_vertex_count", size_t (0)), + "@brief Constructor for a deep region from a hierarchical shape set\n" + "\n" + "This constructor creates a hierarchical region. Use a \\DeepShapeStore object to " + "supply the hierarchical heap. See \\DeepShapeStore for more details.\n" + "\n" + "'area_ratio' and 'max_vertex' supply two optimization parameters which control how " + "big polygons are split to reduce the region's polygon complexity.\n" + "\n" + "@param shape_iterator The recursive shape iterator which delivers the hierarchy to take\n" + "@param deep_shape_store The hierarchical heap (see there)\n" + "@param area_ratio The maximum ratio of bounding box to polygon area before polygons are split\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + constructor ("new", &new_sid2, gsi::arg ("shape_iterator"), gsi::arg ("deep_shape_store"), gsi::arg ("trans"), gsi::arg ("area_ratio", 0.0), gsi::arg ("max_vertex_count", size_t (0)), + "@brief Constructor for a deep region from a hierarchical shape set\n" + "\n" + "This constructor creates a hierarchical region. Use a \\DeepShapeStore object to " + "supply the hierarchical heap. See \\DeepShapeStore for more details.\n" + "\n" + "'area_ratio' and 'max_vertex' supply two optimization parameters which control how " + "big polygons are split to reduce the region's polygon complexity.\n" + "\n" + "The transformation is useful to scale to a specific database unit for example.\n" + "\n" + "@param shape_iterator The recursive shape iterator which delivers the hierarchy to take\n" + "@param deep_shape_store The hierarchical heap (see there)\n" + "@param area_ratio The maximum ratio of bounding box to polygon area before polygons are split\n" + "@param trans The transformation to apply when storing the layout data\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + constructor ("new", &new_texts_as_boxes1, gsi::arg("shape_iterator"), gsi::arg ("expr"), gsi::arg ("as_pattern", true), gsi::arg ("enl", 1), "@brief Constructor from a text set\n" "\n" "@param shape_iterator The iterator from which to derive the texts\n" "@param expr The selection string\n" "@param as_pattern If true, the selection string is treated as a glob pattern. Otherwise the match is exact.\n" + "@param enl The per-side enlargement of the box to mark the text (1 gives a 2x2 DBU box)" "\n" "This special constructor will create a region from the text objects delivered by the shape iterator. " - "Each text object will deliver a small (non-empty) box that represents the text origin.\n" + "Each text object will give a small (non-empty) box that represents the text origin.\n" "Texts can be selected by their strings - either through a glob pattern or by exact comparison with " "the given string. The following options are available:\n" "\n" @@ -807,13 +732,50 @@ Class decl_Region ("db", "Region", "region = RBA::Region::new(iter, \"A*\", false) # all texts exactly matchin 'A*'\n" "@/code\n" "\n" - "This method has been introduced in version 0.25." + "This method has been introduced in version 0.25. The enlargement parameter has been added in version 0.26.\n" ) + - factory_ext ("texts", &texts, gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true), + constructor ("new", &new_texts_as_boxes2, gsi::arg("shape_iterator"), gsi::arg ("dss"), gsi::arg ("expr"), gsi::arg ("as_pattern", true), gsi::arg ("enl", 1), + "@brief Constructor from a text set\n" + "\n" + "@param shape_iterator The iterator from which to derive the texts\n" + "@param dss The \\DeepShapeStore object that acts as a heap for hierarchical operations.\n" + "@param expr The selection string\n" + "@param as_pattern If true, the selection string is treated as a glob pattern. Otherwise the match is exact.\n" + "@param enl The per-side enlargement of the box to mark the text (1 gives a 2x2 DBU box)" + "\n" + "This special constructor will create a deep region from the text objects delivered by the shape iterator. " + "Each text object will give a small (non-empty) box that represents the text origin.\n" + "Texts can be selected by their strings - either through a glob pattern or by exact comparison with " + "the given string. The following options are available:\n" + "\n" + "@code\n" + "region = RBA::Region::new(iter, dss, \"*\") # all texts\n" + "region = RBA::Region::new(iter, dss, \"A*\") # all texts starting with an 'A'\n" + "region = RBA::Region::new(iter, dss, \"A*\", false) # all texts exactly matchin 'A*'\n" + "@/code\n" + "\n" + "This variant has been introduced in version 0.26.\n" + ) + + method ("insert_into", &db::Region::insert_into, gsi::arg ("layout"), gsi::arg ("cell_index"), gsi::arg ("layer"), + "@brief Inserts this region into the given layout, below the given cell and into the given layer.\n" + "If the region is a hierarchical one, a suitable hierarchy will be built below the top cell or " + "and existing hierarchy will be reused.\n" + "\n" + "This method has been introduced in version 0.26." + ) + + factory_ext ("texts", &texts_as_boxes1, gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true), gsi::arg ("enl", 1), "@hide\n" "This method is provided for DRC implementation only." ) + - factory_ext ("texts_dots", &texts, gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true), + factory_ext ("texts", &texts_as_boxes2, gsi::arg ("dss"), gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true), gsi::arg ("enl", 1), + "@hide\n" + "This method is provided for DRC implementation only." + ) + + factory_ext ("texts_dots", &texts_as_dots1, gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true), + "@hide\n" + "This method is provided for DRC implementation only." + ) + + factory_ext ("texts_dots", &texts_as_dots2, gsi::arg ("dss"), gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true), "@hide\n" "This method is provided for DRC implementation only." ) + @@ -1159,11 +1121,13 @@ Class decl_Region ("db", "Region", "@hide\n" "This method is provided for DRC implementation.\n" ) + - factory_ext ("corners", &corners, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), + method_ext ("corners", &corners_to_boxes, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("dim", 1), "@brief This method will select all corners whose attached edges satisfy the angle condition.\n" "\n" "The angle values specify a range of angles: all corners whose attached edges form an angle " - "between angle_start and angle_end will be reported as small (2x2 DBU) boxes. The angle is measured " + "between angle_start and angle_end will be reported boxes with 2*dim x 2*dim dimension. The default dimension is 2x2 DBU.\n" + "\n" + "The angle is measured " "between the incoming and the outcoming edge in mathematical sense: a positive value is a turn left " "while a negative value is a turn right. Since polygon contours are oriented clockwise, positive " "angles will report concave corners while negative ones report convex ones.\n" @@ -1172,7 +1136,7 @@ Class decl_Region ("db", "Region", "\n" "This function has been introduced in version 0.25.\n" ) + - method_ext ("corners_dots", &corners, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), + method_ext ("corners_dots", &corners_to_dots, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), "@brief This method will select all corners whose attached edges satisfy the angle condition.\n" "\n" "This method is similar to \\corners, but delivers an \\Edges collection with dot-like edges for each corner.\n" @@ -1744,9 +1708,23 @@ Class decl_Region ("db", "Region", "This method returns all polygons in self which are not rectilinear." "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("minkowsky_sum", &minkowsky_sum_pe, + method_ext ("break", &break_polygons, gsi::arg ("max_vertex_count"), gsi::arg ("max_area_ratio", 0.0), + "@brief Breaks the polygons of the region into smaller ones\n" + "\n" + "There are two criteria for splitting a polygon: a polygon is split into parts with less then " + "'max_vertex_count' points and an bounding box-to-polygon area ratio less than 'max_area_ratio'. " + "The area ratio is supposed to render polygons whose bounding box is a better approximation. " + "This applies for example to 'L' shape polygons.\n" + "\n" + "Using a value of 0 for either limit means that the respective limit isn't checked. " + "Breaking happens by cutting the polygons into parts at 'good' locations. The " + "algorithm does not have a specific goal to minimize the number of parts for example. " + "The only goal is to achieve parts within the given limits.\n" + "\n" + "This method has been introduced in version 0.26." + ) + + method_ext ("minkowsky_sum", &minkowsky_sum_pe, gsi::arg ("e"), "@brief Compute the Minkowsky sum of the region and an edge\n" - "@args e\n" "\n" "@param e The edge.\n" "\n" @@ -1759,9 +1737,8 @@ Class decl_Region ("db", "Region", "The resulting polygons are not merged. In order to remove overlaps, use the \\merge or \\merged method." "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("minkowsky_sum", &minkowsky_sum_pp, + method_ext ("minkowsky_sum", &minkowsky_sum_pp, gsi::arg ("p"), "@brief Compute the Minkowsky sum of the region and a polygon\n" - "@args p\n" "\n" "@param p The first argument.\n" "\n" @@ -1773,9 +1750,8 @@ Class decl_Region ("db", "Region", "The resulting polygons are not merged. In order to remove overlaps, use the \\merge or \\merged method." "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("minkowsky_sum", &minkowsky_sum_pb, + method_ext ("minkowsky_sum", &minkowsky_sum_pb, gsi::arg ("b"), "@brief Compute the Minkowsky sum of the region and a box\n" - "@args b\n" "\n" "@param b The box.\n" "\n" @@ -1787,9 +1763,8 @@ Class decl_Region ("db", "Region", "The resulting polygons are not merged. In order to remove overlaps, use the \\merge or \\merged method." "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("minkowsky_sum", &minkowsky_sum_pc, + method_ext ("minkowsky_sum", &minkowsky_sum_pc, gsi::arg ("b"), "@brief Compute the Minkowsky sum of the region and a contour of points (a trace)\n" - "@args b\n" "\n" "@param b The contour (a series of points forming the trace).\n" "\n" @@ -1802,9 +1777,8 @@ Class decl_Region ("db", "Region", "The resulting polygons are not merged. In order to remove overlaps, use the \\merge or \\merged method." "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("move", &move_p, + method_ext ("move", &move_p, gsi::arg ("v"), "@brief Moves the region\n" - "@args v\n" "\n" "Moves the polygon by the given offset and returns the \n" "moved region. The region is overwritten.\n" @@ -1815,9 +1789,8 @@ Class decl_Region ("db", "Region", "\n" "@return The moved region (self).\n" ) + - method_ext ("move", &move_xy, + method_ext ("move", &move_xy, gsi::arg ("x"), gsi::arg ("y"), "@brief Moves the region\n" - "@args x,y\n" "\n" "Moves the region by the given offset and returns the \n" "moved region. The region is overwritten.\n" @@ -1827,9 +1800,8 @@ Class decl_Region ("db", "Region", "\n" "@return The moved region (self).\n" ) + - method_ext ("moved", &moved_p, + method_ext ("moved", &moved_p, gsi::arg ("v"), "@brief Returns the moved region (does not modify self)\n" - "@args p\n" "\n" "Moves the region by the given offset and returns the \n" "moved region. The region is not modified.\n" @@ -1840,9 +1812,8 @@ Class decl_Region ("db", "Region", "\n" "@return The moved region.\n" ) + - method_ext ("moved", &moved_xy, + method_ext ("moved", &moved_xy, gsi::arg ("x"), gsi::arg ("y"), "@brief Returns the moved region (does not modify self)\n" - "@args x,y\n" "\n" "Moves the region by the given offset and returns the \n" "moved region. The region is not modified.\n" @@ -1852,9 +1823,8 @@ Class decl_Region ("db", "Region", "\n" "@return The moved region.\n" ) + - method ("transform", (db::Region &(db::Region::*)(const db::Trans &)) &db::Region::transform, + method ("transform", (db::Region &(db::Region::*)(const db::Trans &)) &db::Region::transform, gsi::arg ("t"), "@brief Transform the region (modifies self)\n" - "@args t\n" "\n" "Transforms the region with the given transformation.\n" "This version modifies the region and returns a reference to self.\n" @@ -1863,9 +1833,8 @@ Class decl_Region ("db", "Region", "\n" "@return The transformed region.\n" ) + - method ("transform|#transform_icplx", (db::Region &(db::Region::*)(const db::ICplxTrans &)) &db::Region::transform, + method ("transform|#transform_icplx", (db::Region &(db::Region::*)(const db::ICplxTrans &)) &db::Region::transform, gsi::arg ("t"), "@brief Transform the region with a complex transformation (modifies self)\n" - "@args t\n" "\n" "Transforms the region with the given transformation.\n" "This version modifies the region and returns a reference to self.\n" @@ -1874,9 +1843,8 @@ Class decl_Region ("db", "Region", "\n" "@return The transformed region.\n" ) + - method ("transformed", (db::Region (db::Region::*)(const db::Trans &) const) &db::Region::transformed, + method ("transformed", (db::Region (db::Region::*)(const db::Trans &) const) &db::Region::transformed, gsi::arg ("t"), "@brief Transform the region\n" - "@args t\n" "\n" "Transforms the region with the given transformation.\n" "Does not modify the region but returns the transformed region.\n" @@ -1885,9 +1853,8 @@ Class decl_Region ("db", "Region", "\n" "@return The transformed region.\n" ) + - method ("transformed|#transformed_icplx", (db::Region (db::Region::*)(const db::ICplxTrans &) const) &db::Region::transformed, + method ("transformed|#transformed_icplx", (db::Region (db::Region::*)(const db::ICplxTrans &) const) &db::Region::transformed, gsi::arg ("t"), "@brief Transform the region with a complex transformation\n" - "@args t\n" "\n" "Transforms the region with the given complex transformation.\n" "Does not modify the region but returns the transformed region.\n" @@ -1896,9 +1863,8 @@ Class decl_Region ("db", "Region", "\n" "@return The transformed region.\n" ) + - method_ext ("width_check", &width1, + method_ext ("width_check", &width1, gsi::arg ("d"), "@brief Performs a width check\n" - "@args d\n" "@param d The minimum width for which the polygons are checked\n" "Performs a width check against the minimum width \"d\". For locations where a polygon has a " "width less than the given value, an error marker is produced. Error markers form a " @@ -1908,9 +1874,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("width_check", &width2, + method_ext ("width_check", &width2, gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs a width check with options\n" - "@args d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum width for which the polygons are checked\n" "@param whole_edges If true, deliver the whole edges\n" "@param metrics Specify the metrics type\n" @@ -1940,9 +1905,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("space_check", &space1, + method_ext ("space_check", &space1, gsi::arg ("d"), "@brief Performs a space check\n" - "@args d\n" "@param d The minimum space for which the polygons are checked\n" "Performs a space check against the minimum space \"d\". For locations where a polygon has a " "space less than the given value to either itself (a notch) or to other polygons, an error marker is produced. Error markers form a " @@ -1955,9 +1919,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("space_check", &space2, + method_ext ("space_check", &space2, gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs a space check with options\n" - "@args d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum space for which the polygons are checked\n" "@param whole_edges If true, deliver the whole edges\n" "@param metrics Specify the metrics type\n" @@ -1987,9 +1950,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("notch_check", ¬ch1, + method_ext ("notch_check", ¬ch1, gsi::arg ("d"), "@brief Performs a space check between edges of the same polygon\n" - "@args d\n" "@param d The minimum space for which the polygons are checked\n" "Performs a space check against the minimum space \"d\". For locations where a polygon has a " "space less than the given value to either itself (a notch) or to other polygons, an error marker is produced. Error markers form a " @@ -2004,9 +1966,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("notch_check", ¬ch2, + method_ext ("notch_check", ¬ch2, gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs a space check between edges of the same polygon with options\n" - "@args d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum space for which the polygons are checked\n" "@param whole_edges If true, deliver the whole edges\n" "@param metrics Specify the metrics type\n" @@ -2036,9 +1997,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("isolated_check", &isolated1, + method_ext ("isolated_check", &isolated1, gsi::arg ("d"), "@brief Performs a space check between edges of different polygons\n" - "@args d\n" "@param d The minimum space for which the polygons are checked\n" "Performs a space check against the minimum space \"d\". For locations where a polygon has a " "space less than the given value to other polygons (not itself), an error marker is produced. Error markers form a " @@ -2053,9 +2013,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("isolated_check", &isolated2, + method_ext ("isolated_check", &isolated2, gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs a space check between edges of different polygons with options\n" - "@args d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum space for which the polygons are checked\n" "@param whole_edges If true, deliver the whole edges\n" "@param metrics Specify the metrics type\n" @@ -2085,9 +2044,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("inside_check", &inside1, + method_ext ("inside_check", &inside1, gsi::arg ("other"), gsi::arg ("d"), "@brief Performs a check whether polygons of this region are inside polygons of the other region by some amount\n" - "@args other, d\n" "@param d The minimum overlap for which the polygons are checked\n" "@param other The other region against which to check\n" "Returns edge pairs for all locations where edges of polygons of this region are inside polygons of the other region " @@ -2097,9 +2055,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("inside_check", &inside2, + method_ext ("inside_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs an inside check with options\n" - "@args other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum distance for which the polygons are checked\n" "@param other The other region against which to check\n" "@param whole_edges If true, deliver the whole edges\n" @@ -2130,9 +2087,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("overlap_check", &overlap1, + method_ext ("overlap_check", &overlap1, gsi::arg ("other"), gsi::arg ("d"), "@brief Performs a check whether polygons of this region overlap polygons of the other region by some amount\n" - "@args other, d\n" "@param d The minimum overlap for which the polygons are checked\n" "@param other The other region against which to check\n" "Returns edge pairs for all locations where edges of polygons of this region overlap polygons of the other region " @@ -2140,9 +2096,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("overlap_check", &overlap2, + method_ext ("overlap_check", &overlap2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs an overlap check with options\n" - "@args other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum overlap for which the polygons are checked\n" "@param other The other region against which to check\n" "@param whole_edges If true, deliver the whole edges\n" @@ -2173,9 +2128,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("enclosing_check", &enclosing1, + method_ext ("enclosing_check", &enclosing1, gsi::arg ("other"), gsi::arg ("d"), "@brief Performs a check whether polygons of this region enclose polygons of the other region by some amount\n" - "@args other, d\n" "@param d The minimum overlap for which the polygons are checked\n" "@param other The other region against which to check\n" "Returns edge pairs for all locations where edges of polygons of this region are enclosing polygons of the other region " @@ -2183,9 +2137,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("enclosing_check", &enclosing2, + method_ext ("enclosing_check", &enclosing2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs an enclosing check with options\n" - "@args other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum enclosing distance for which the polygons are checked\n" "@param other The other region against which to check\n" "@param whole_edges If true, deliver the whole edges\n" @@ -2216,9 +2169,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("separation_check", &separation1, + method_ext ("separation_check", &separation1, gsi::arg ("other"), gsi::arg ("d"), "@brief Performs a check whether polygons of this region are separated from polygons of the other region by some amount\n" - "@args other, d\n" "@param d The minimum separation for which the polygons are checked\n" "@param other The other region against which to check\n" "Returns edge pairs for all locations where edges of polygons of this region are separated by polygons of the other region " @@ -2226,9 +2178,8 @@ Class decl_Region ("db", "Region", "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= of merged semantics)\n" ) + - method_ext ("separation_check", &separation2, + method_ext ("separation_check", &separation2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges"), gsi::arg ("metrics"), gsi::arg ("ignore_angle"), gsi::arg ("min_projection"), gsi::arg ("max_projection"), "@brief Performs a separation check with options\n" - "@args other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection\n" "@param d The minimum separation for which the polygons are checked\n" "@param other The other region against which to check\n" "@param whole_edges If true, deliver the whole edges\n" @@ -2265,9 +2216,8 @@ Class decl_Region ("db", "Region", "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" "If merged semantics is not enabled, overlapping areas are counted twice.\n" ) + - method_ext ("area", &area2, + method_ext ("area", &area2, gsi::arg ("rect"), "@brief The area of the region (restricted to a rectangle)\n" - "@args rect\n" "This version will compute the area of the shapes, restricting the computation to the given rectangle.\n" "\n" "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" @@ -2279,9 +2229,8 @@ Class decl_Region ("db", "Region", "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" "If merged semantics is not enabled, internal edges are counted as well.\n" ) + - method_ext ("perimeter", &perimeter2, + method_ext ("perimeter", &perimeter2, gsi::arg ("rect"), "@brief The total perimeter of the polygons (restricted to a rectangle)\n" - "@args rect\n" "This version will compute the perimeter of the polygons, restricting the computation to the given rectangle.\n" "Edges along the border are handled in a special way: they are counted when they are oriented with their inside " "side toward the rectangle (in other words: outside edges must coincide with the rectangle's border in order to be counted).\n" @@ -2293,6 +2242,16 @@ Class decl_Region ("db", "Region", "@brief Return the bounding box of the region\n" "The bounding box is the box enclosing all points of all polygons.\n" ) + + method_ext ("is_deep?", &is_deep, + "@brief Returns true if the region is a deep (hierarchical) one\n" + "\n" + "This method has been added in version 0.26." + ) + + method_ext ("data_id", &id, + "@brief Returns the data ID (a unique identifier for the underlying data storage)\n" + "\n" + "This method has been added in version 0.26." + ) + method ("is_merged?", &db::Region::is_merged, "@brief Returns true if the region is merged\n" "If the region is merged, polygons will not touch or overlap. You can ensure merged state " @@ -2301,10 +2260,11 @@ Class decl_Region ("db", "Region", method ("is_empty?", &db::Region::empty, "@brief Returns true if the region is empty\n" ) + - method ("size", (size_t (db::Region::*) () const) &db::Region::size, + method ("size|count", (size_t (db::Region::*) () const) &db::Region::size, "@brief Returns the number of polygons in the region\n" "\n" "This returns the number of raw polygons (not merged polygons if merged semantics is enabled).\n" + "The 'count' alias has been provided in version 0.26 to avoid ambiguitiy with the 'size' method which applies a geometrical bias." ) + iterator ("each", &db::Region::begin, "@brief Returns each polygon of the region\n" @@ -2316,29 +2276,41 @@ Class decl_Region ("db", "Region", "\n" "This returns the raw polygons if merged semantics is disabled or the merged ones if merged semantics is enabled.\n" ) + - method ("[]", &db::Region::nth, + method ("[]", &db::Region::nth, gsi::arg ("n"), "@brief Returns the nth polygon of the region\n" - "@args n\n" "\n" - "This method returns nil if the index is out of range.\n" - "This returns the raw polygon (not merged polygons if merged semantics is enabled).\n" + "This method returns nil if the index is out of range. It is available for flat regions only - i.e. " + "those for which \\has_valid_polygons? is true. Use \\flatten to explicitly flatten a region.\n" + "This method returns the raw polygon (not merged polygons, even if merged semantics is enabled).\n" "\n" - "Using this method may be costly in terms of memory since it will load the polygons into an array if they have been " - "stored in an hierarchical layout before. It is recommended to use the \\each iterator instead if possible." + "The \\each iterator is the more general approach to access the polygons." + ) + + method ("flatten", &db::Region::flatten, + "@brief Explicitly flattens a region\n" + "\n" + "If the region is already flat (i.e. \\has_valid_polygons? returns true), this method will " + "not change it.\n" + "\n" + "Returns 'self', so this method can be used in a dot concatenation.\n" + "\n" + "This method has been introduced in version 0.26." + ) + + method ("has_valid_polygons?", &db::Region::has_valid_polygons, + "@brief Returns true if the region is flat and individual polygons can be accessed randomly\n" + "\n" + "This method has been introduced in version 0.26." ) + method_ext ("to_s", &to_string0, "@brief Converts the region to a string\n" "The length of the output is limited to 20 polygons to avoid giant strings on large regions. " "For full output use \"to_s\" with a maximum count parameter.\n" ) + - method_ext ("to_s", &to_string1, + method_ext ("to_s", &to_string1, gsi::arg ("max_count"), "@brief Converts the region to a string\n" - "@args max_count\n" "This version allows specification of the maximum number of polygons contained in the string." ) + - method ("enable_progress", &db::Region::enable_progress, + method ("enable_progress", &db::Region::enable_progress, gsi::arg ("label"), "@brief Enable progress reporting\n" - "@args label\n" "After calling this method, the region will report the progress through a progress bar while " "expensive operations are running.\n" "The label is a text which is put in front of the progress bar.\n" @@ -2348,6 +2320,20 @@ Class decl_Region ("db", "Region", "@brief Disable progress reporting\n" "Calling this method will disable progress reporting. See \\enable_progress.\n" ) + + method ("base_verbosity=", &db::Region::set_base_verbosity, gsi::arg ("verbosity"), + "@brief Sets the minimum verbosity for timing reports\n" + "Timing reports will be given only if the verbosity is larger than this value. " + "Detailed reports will be given when the verbosity is more than this value plus 10.\n" + "In binary operations, the base verbosity of the first argument is considered.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + method ("base_verbosity", &db::Region::base_verbosity, + "@brief Gets the minimum verbosity for timing reports\n" + "See \\base_verbosity= for details.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + method ("Euclidian", &euclidian_metrics, "@brief Specifies Euclidian metrics for the check functions\n" "This value can be used for the metrics parameter in the check functions, i.e. \\width_check. " diff --git a/src/db/db/gsiDeclDbShape.cc b/src/db/db/gsiDeclDbShape.cc index 22238f7bf..eb9aa128f 100644 --- a/src/db/db/gsiDeclDbShape.cc +++ b/src/db/db/gsiDeclDbShape.cc @@ -689,6 +689,26 @@ static tl::Variant get_dedge (const db::Shape *s) } } +static tl::Variant get_edge_pair (const db::Shape *s) +{ + db::Shape::edge_pair_type p; + if (s->edge_pair (p)) { + return tl::Variant (p); + } else { + return tl::Variant (); + } +} + +static tl::Variant get_dedge_pair (const db::Shape *s) +{ + db::Shape::edge_pair_type p; + if (s->edge_pair (p)) { + return tl::Variant (db::CplxTrans (shape_dbu (s)) * p); + } else { + return tl::Variant (); + } +} + static tl::Variant get_text (const db::Shape *s) { db::Shape::text_type p; @@ -1087,6 +1107,7 @@ static int t_simplePolygonRef () { return db::Shape::SimplePolygonRef static int t_simplePolygonPtrArray () { return db::Shape::SimplePolygonPtrArray; } static int t_simplePolygonPtrArrayMember () { return db::Shape::SimplePolygonPtrArrayMember; } static int t_edge () { return db::Shape::Edge; } +static int t_edge_pair () { return db::Shape::EdgePair; } static int t_path () { return db::Shape::Path; } static int t_pathRef () { return db::Shape::PathRef; } static int t_pathPtrArray () { return db::Shape::PathPtrArray; } @@ -1218,7 +1239,7 @@ Class decl_Shape ("db", "Shape", "\n" "This method has been introduced in version 0.25." ) + - gsi::method_ext ("edge=", &set_shape, + gsi::method_ext ("edge=", &set_shape, gsi::arg("edge"), "@brief Replaces the shape by the given edge\n" "@args box\n" "This method replaces the shape by the given edge. This method can only be called " @@ -1235,6 +1256,23 @@ Class decl_Shape ("db", "Shape", "\n" "This method has been introduced in version 0.25." ) + + gsi::method_ext ("edge_pair=", &set_shape, gsi::arg("edge_pair"), + "@brief Replaces the shape by the given edge pair\n" + "@args box\n" + "This method replaces the shape by the given edge pair. This method can only be called " + "for editable layouts. It does not change the user properties of the shape.\n" + "Calling this method will invalidate any iterators. It should not be called inside a " + "loop iterating over shapes.\n" + "\n" + "This method has been introduced in version 0.26." + ) + + gsi::method_ext ("edge_pair=|dedge_pair=", &set_dshape, gsi::arg("edge_pair"), + "@brief Replaces the shape by the given edge pair (in micrometer units)\n" + "This method replaces the shape by the given edge pair, like \\edge_pair= with a \\EdgePair argument does. " + "This version translates the edge pair from micrometer units to database units internally.\n" + "\n" + "This method has been introduced in version 0.26." + ) + gsi::method_ext ("delete_property", &delete_property, "@brief Deletes the user property with the given key\n" "@args key\n" @@ -1713,12 +1751,29 @@ Class decl_Shape ("db", "Shape", "Starting with version 0.23, this method returns nil, if the shape does not represent an edge." ) + gsi::method_ext ("dedge", &get_dedge, - "@brief Returns the path object as a \\DEdge object in micrometer units\n" + "@brief Returns the edge object as a \\DEdge object in micrometer units\n" "See \\edge for a description of this method. This method returns the edge after translation to " "micrometer units.\n" "\n" "This method has been added in version 0.25.\n" ) + + gsi::method ("is_edge_pair?", &db::Shape::is_edge_pair, + "@brief Returns true, if the object is an edge pair\n" + "\n" + "This method has been introduced in version 0.26." + ) + + gsi::method_ext ("edge_pair", &get_edge_pair, + "@brief Returns the edge pair object\n" + "\n" + "This method has been introduced in version 0.26." + ) + + gsi::method_ext ("dedge_pair", &get_dedge_pair, + "@brief Returns the edge pair object as a \\DEdgePair object in micrometer units\n" + "See \\edge_pair for a description of this method. This method returns the edge pair after translation to " + "micrometer units.\n" + "\n" + "This method has been added in version 0.26.\n" + ) + gsi::method ("is_text?", &db::Shape::is_text, "@brief Returns true, if the object is a text\n" ) + @@ -1729,7 +1784,7 @@ Class decl_Shape ("db", "Shape", ) + gsi::method_ext ("dtext", &get_dtext, "@brief Returns the path object as a \\DText object in micrometer units\n" - "See \\edge for a description of this method. This method returns the text after translation to " + "See \\text for a description of this method. This method returns the text after translation to " "micrometer units.\n" "\n" "This method has been added in version 0.25.\n" @@ -2026,6 +2081,7 @@ Class decl_Shape ("db", "Shape", gsi::method ("TSimplePolygonPtrArray|#t_simple_polygon_ptr_array", &t_simplePolygonPtrArray) + gsi::method ("TSimplePolygonPtrArrayMember|#t_simple_polygon_ptr_array_member", &t_simplePolygonPtrArrayMember) + gsi::method ("TEdge|#t_edge", &t_edge) + + gsi::method ("TEdgePair|#t_edge_pair", &t_edge_pair) + gsi::method ("TPath|#t_path", &t_path) + gsi::method ("TPathRef|#t_path_ref", &t_pathRef) + gsi::method ("TPathPtrArray|#t_path_ptr_array", &t_pathPtrArray) + diff --git a/src/db/db/gsiDeclDbShapes.cc b/src/db/db/gsiDeclDbShapes.cc index 19b7661be..f422ab20d 100644 --- a/src/db/db/gsiDeclDbShapes.cc +++ b/src/db/db/gsiDeclDbShapes.cc @@ -340,7 +340,7 @@ static void insert_edges_with_dtrans (db::Shapes *sh, const db::Edges &r, const static void insert_edge_pairs_as_polygons (db::Shapes *sh, const db::EdgePairs &r, db::Coord e) { - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->normalized ().to_simple_polygon (e)); } } @@ -348,14 +348,14 @@ static void insert_edge_pairs_as_polygons (db::Shapes *sh, const db::EdgePairs & static void insert_edge_pairs_as_polygons_d (db::Shapes *sh, const db::EdgePairs &r, db::DCoord de) { db::Coord e = db::coord_traits::rounded (de / shapes_dbu (sh)); - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->normalized ().to_simple_polygon (e)); } } static void insert_edge_pairs_as_polygons_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans, db::Coord e) { - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->normalized ().to_simple_polygon (e).transformed (trans)); } } @@ -365,14 +365,14 @@ static void insert_edge_pairs_as_polygons_with_dtrans (db::Shapes *sh, const db: db::Coord e = db::coord_traits::rounded (de / shapes_dbu (sh)); db::CplxTrans dbu_trans (shapes_dbu (sh)); db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->normalized ().to_simple_polygon (e).transformed (itrans)); } } static void insert_edge_pairs_as_edges (db::Shapes *sh, const db::EdgePairs &r) { - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->first ()); sh->insert (s->second ()); } @@ -380,7 +380,7 @@ static void insert_edge_pairs_as_edges (db::Shapes *sh, const db::EdgePairs &r) static void insert_edge_pairs_as_edges_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans) { - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->first ().transformed (trans)); sh->insert (s->second ().transformed (trans)); } @@ -390,18 +390,42 @@ static void insert_edge_pairs_as_edges_with_dtrans (db::Shapes *sh, const db::Ed { db::CplxTrans dbu_trans (shapes_dbu (sh)); db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; - for (db::EdgePairs::const_iterator s = r.begin (); s != r.end (); ++s) { + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { sh->insert (s->first ().transformed (itrans)); sh->insert (s->second ().transformed (itrans)); } } +static void insert_edge_pairs (db::Shapes *sh, const db::EdgePairs &r) +{ + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { + sh->insert (*s); + } +} + +static void insert_edge_pairs_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans) +{ + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { + sh->insert (s->transformed (trans)); + } +} + +static void insert_edge_pairs_with_dtrans (db::Shapes *sh, const db::EdgePairs &r, const db::DCplxTrans &trans) +{ + db::CplxTrans dbu_trans (shapes_dbu (sh)); + db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; + for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { + sh->insert (s->transformed (itrans)); + } +} + static unsigned int s_all () { return db::ShapeIterator::All; } static unsigned int s_all_with_properties () { return db::ShapeIterator::AllWithProperties; } static unsigned int s_properties () { return db::ShapeIterator::Properties; } static unsigned int s_polygons () { return db::ShapeIterator::Polygons; } static unsigned int s_boxes () { return db::ShapeIterator::Boxes; } static unsigned int s_edges () { return db::ShapeIterator::Edges; } +static unsigned int s_edge_pairs () { return db::ShapeIterator::EdgePairs; } static unsigned int s_paths () { return db::ShapeIterator::Paths; } static unsigned int s_texts () { return db::ShapeIterator::Texts; } static unsigned int s_user_objects () { return db::ShapeIterator::UserObjects; } @@ -553,6 +577,34 @@ Class decl_Shapes ("db", "Shapes", "\n" "This method has been introduced in version 0.25.\n" ) + + gsi::method_ext ("insert", &insert_edge_pairs, gsi::arg ("edge_pairs"), + "@brief Inserts the edges from the edge pair collection into this shape container\n" + "@param edges The edge pairs to insert\n" + "\n" + "This method inserts all edge pairs from the edge pair collection into this shape container.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + gsi::method_ext ("insert", &insert_edge_pairs_with_trans, gsi::arg ("edge_pairs"), gsi::arg ("trans"), + "@brief Inserts the edge pairs from the edge pair collection into this shape container with a transformation\n" + "@param edges The edge pairs to insert\n" + "@param trans The transformation to apply\n" + "\n" + "This method inserts all edge pairs from the edge pair collection into this shape container.\n" + "Before an edge pair is inserted, the given transformation is applied.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + gsi::method_ext ("insert", &insert_edge_pairs_with_dtrans, gsi::arg ("edge_pairs"), gsi::arg ("trans"), + "@brief Inserts the edge pairs from the edge pair collection into this shape container with a transformation (given in micrometer units)\n" + "@param edges The edge pairs to insert\n" + "@param trans The transformation to apply (displacement in micrometer units)\n" + "\n" + "This method inserts all edge pairs from the edge collection into this shape container.\n" + "Before an edge pair is inserted, the given transformation is applied.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + gsi::method_ext ("insert_as_polygons", &insert_edge_pairs_as_polygons, gsi::arg ("edge_pairs"), gsi::arg ("e"), "@brief Inserts the edge pairs from the edge pair collection as polygons into this shape container\n" "@param edge_pairs The edge pairs to insert\n" @@ -731,9 +783,8 @@ Class decl_Shapes ("db", "Shapes", "\n" "This variant has been introduced in version 0.25." ) + - gsi::method_ext ("replace", &replace, + gsi::method_ext ("replace", &replace, gsi::arg ("shape"), gsi::arg ("edge"), "@brief Replaces the given shape with an edge object\n" - "@args shape,edge\n" "\n" "This method has been introduced with version 0.16. It replaces the given shape with the " "object specified. It does not change the property Id. To change the property Id, " @@ -743,7 +794,7 @@ Class decl_Shapes ("db", "Shapes", "This method is permitted in editable mode only." ) + gsi::method_ext ("replace", &dreplace, gsi::arg ("shape"), gsi::arg ("edge"), - "@brief Replaces the given shape with a edge given in micrometer units\n" + "@brief Replaces the given shape with an edge given in micrometer units\n" "@return A reference to the new shape (a \\Shape object)\n" "\n" "This method behaves like the \\replace version with an \\Edge argument, except that it will " @@ -751,6 +802,27 @@ Class decl_Shapes ("db", "Shapes", "\n" "This variant has been introduced in version 0.25." ) + + gsi::method_ext ("replace", &replace, gsi::arg ("shape"), gsi::arg ("edge_pair"), + "@brief Replaces the given shape with an edge pair object\n" + "\n" + "It replaces the given shape with the " + "object specified. It does not change the property Id. To change the property Id, " + "use the \\replace_prop_id method. To replace a shape and discard the property Id, erase the " + "shape and insert a new shape." + "\n" + "This method is permitted in editable mode only.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + gsi::method_ext ("replace", &dreplace, gsi::arg ("shape"), gsi::arg ("edge_pair"), + "@brief Replaces the given shape with an edge pair given in micrometer units\n" + "@return A reference to the new shape (a \\Shape object)\n" + "\n" + "This method behaves like the \\replace version with an \\EdgePair argument, except that it will " + "internally translate the edge pair from micrometer to database units.\n" + "\n" + "This variant has been introduced in version 0.26.\n" + ) + gsi::method_ext ("replace", &replace, "@brief Replaces the given shape with a text object\n" "@return A reference to the new shape (a \\Shape object)\n" @@ -844,9 +916,8 @@ Class decl_Shapes ("db", "Shapes", "\n" "This variant has been introduced in version 0.25." ) + - gsi::method_ext ("insert|#insert_edge", &insert, - "@brief Inserts a edge into the shapes list\n" - "@args edge\n" + gsi::method_ext ("insert|#insert_edge", &insert, gsi::arg ("edge"), + "@brief Inserts an edge into the shapes list\n" "\n" "Starting with version 0.16, this method returns a reference to the newly created shape\n" ) + @@ -858,6 +929,19 @@ Class decl_Shapes ("db", "Shapes", "\n" "This variant has been introduced in version 0.25." ) + + gsi::method_ext ("insert", &insert, gsi::arg ("edge_pair"), + "@brief Inserts an edge pair into the shapes list\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + gsi::method_ext ("insert", &dinsert, gsi::arg ("edge_pair"), + "@brief Inserts a micrometer-unit edge pair into the shapes list\n" + "@return A reference to the new shape (a \\Shape object)\n" + "This method behaves like the \\insert version with a \\EdgePair argument, except that it will " + "internally translate the edge pair from micrometer to database units.\n" + "\n" + "This variant has been introduced in version 0.26." + ) + gsi::method_ext ("insert|#insert_text", &insert, "@brief Inserts a text into the shapes list\n" "@return A reference to the new shape (a \\Shape object)\n" @@ -937,14 +1021,13 @@ Class decl_Shapes ("db", "Shapes", "\n" "This variant has been introduced in version 0.25." ) + - gsi::method_ext ("insert|#insert_edge_with_properties", &insert_with_properties, - "@brief Inserts a edge with properties into the shapes list\n" - "@args edge, property_id\n" + gsi::method_ext ("insert|#insert_edge_with_properties", &insert_with_properties, gsi::arg ("edge"), gsi::arg ("property_id"), + "@brief Inserts an edge with properties into the shapes list\n" "@return A reference to the new shape (a \\Shape object)\n" "The property Id must be obtained from the \\Layout object's property_id method which " "associates a property set with a property Id." "\n" - "Starting with version 0.16, this method returns a reference to the newly created shape\n" + "Starting with version 0.16, this method returns a reference to the newly created shape.\n" ) + gsi::method_ext ("insert", &dinsert_with_properties, gsi::arg ("edge"), gsi::arg ("property_id"), "@brief Inserts a micrometer-unit edge with properties into the shapes list\n" @@ -954,6 +1037,22 @@ Class decl_Shapes ("db", "Shapes", "\n" "This variant has been introduced in version 0.25." ) + + gsi::method_ext ("insert", &insert_with_properties, gsi::arg ("edge_pair"), gsi::arg ("property_id"), + "@brief Inserts an edge pair with properties into the shapes list\n" + "@return A reference to the new shape (a \\Shape object)\n" + "The property Id must be obtained from the \\Layout object's property_id method which " + "associates a property set with a property Id." + "\n" + "This method has been introduced in version 0.26.\n" + ) + + gsi::method_ext ("insert", &dinsert_with_properties, gsi::arg ("edge_pair"), gsi::arg ("property_id"), + "@brief Inserts a micrometer-unit edge pair with properties into the shapes list\n" + "@return A reference to the new shape (a \\Shape object)\n" + "This method behaves like the \\insert version with a \\EdgePair argument and a property ID, except that it will " + "internally translate the edge pair from micrometer to database units.\n" + "\n" + "This variant has been introduced in version 0.26." + ) + gsi::method_ext ("insert|#insert_text_with_properties", &insert_with_properties, "@brief Inserts a text with properties into the shapes list\n" "@args text, property_id\n" @@ -1136,6 +1235,9 @@ Class decl_Shapes ("db", "Shapes", gsi::method ("SEdges|#s_edges", &s_edges, "@brief Indicates that edges shall be retrieved" ) + + gsi::method ("SEdgePairs|#s_edge_pairs", &s_edge_pairs, + "@brief Indicates that edge pairs shall be retrieved" + ) + gsi::method ("SPaths|#s_paths", &s_paths, "@brief Indicates that paths shall be retrieved" ) + @@ -1154,7 +1256,7 @@ Class decl_Shapes ("db", "Shapes", "@brief A collection of shapes\n" "\n" "A shapes collection is a collection of geometrical objects, such as " - "polygons, boxes, paths, edges or text objects.\n" + "polygons, boxes, paths, edges, edge pairs or text objects.\n" "\n" "Shapes objects are the basic containers for geometrical objects of a cell. Inside a cell, there is " "one Shapes object per layer.\n" diff --git a/src/db/unit_tests/dbBoxScanner.cc b/src/db/unit_tests/dbBoxScanner.cc index ad793cd63..8d396e3b7 100644 --- a/src/db/unit_tests/dbBoxScanner.cc +++ b/src/db/unit_tests/dbBoxScanner.cc @@ -35,6 +35,8 @@ struct BoxScannerTestRecorder str += "<" + tl::to_string (p) + ">"; } + bool stop () const { return false; } + void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) { str += "(" + tl::to_string (p1) + "-" + tl::to_string (p2) + ")"; @@ -43,10 +45,32 @@ struct BoxScannerTestRecorder std::string str; }; +struct BoxScannerTestRecorderStopping +{ + BoxScannerTestRecorderStopping () : do_stop (false) { } + + void finish (const db::Box * /*box*/, size_t p) { + str += "<" + tl::to_string (p) + ">"; + } + + bool stop () const { return do_stop; } + + void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) + { + str += "(" + tl::to_string (p1) + "-" + tl::to_string (p2) + ")"; + do_stop = true; + } + + std::string str; + bool do_stop; +}; + struct BoxScannerTestRecorder2 { void finish (const db::Box *, size_t) { } + bool stop () const { return false; } + void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) { interactions.insert (std::make_pair (p1, p2)); @@ -56,6 +80,65 @@ struct BoxScannerTestRecorder2 std::set > interactions; }; +struct BoxScannerTestRecorderTwo +{ + void finish1 (const db::Box * /*box*/, size_t p) { + str += "<" + tl::to_string (p) + ">"; + } + + void finish2 (const db::SimplePolygon * /*poly*/, int p) { + str += "<" + tl::to_string (p) + ">"; + } + + bool stop () const { return false; } + + void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) + { + str += "(" + tl::to_string (p1) + "-" + tl::to_string (p2) + ")"; + } + + std::string str; +}; + +struct BoxScannerTestRecorderTwoStopping +{ + BoxScannerTestRecorderTwoStopping () : do_stop (false) { } + + void finish1 (const db::Box * /*box*/, size_t p) { + str += "<" + tl::to_string (p) + ">"; + } + + void finish2 (const db::SimplePolygon * /*poly*/, int p) { + str += "<" + tl::to_string (p) + ">"; + } + + bool stop () const { return do_stop; } + + void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) + { + str += "(" + tl::to_string (p1) + "-" + tl::to_string (p2) + ")"; + do_stop = true; + } + + std::string str; + bool do_stop; +}; + +struct BoxScannerTestRecorder2Two +{ + void finish1 (const db::Box *, size_t) { } + void finish2 (const db::SimplePolygon *, int) { } + + bool stop () const { return false; } + + void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) + { + interactions.insert (std::make_pair (p1, p2)); + } + + std::set > interactions; +}; + TEST(1) { db::box_scanner bs; @@ -75,8 +158,12 @@ TEST(1) bs.set_fill_factor (0.0); db::box_convert bc; bs.set_scanner_threshold (0); - bs.process (tr, 1, bc); + EXPECT_EQ (bs.process (tr, 1, bc), true); EXPECT_EQ (tr.str, "(4-2)(5-2)(5-4)(3-2)(3-4)(5-3)<2><5><4><3>(1-0)<0><1>"); + + BoxScannerTestRecorderStopping trstop; + EXPECT_EQ (bs.process (trstop, 1, bc), false); + EXPECT_EQ (trstop.str, "(4-2)"); } TEST(1a) @@ -196,6 +283,28 @@ TEST(1f) EXPECT_EQ (tr.str, ""); } +TEST(1g) +{ + // empty elements + db::box_scanner bs; + + std::vector bb; + bb.push_back (db::Box (0, 0, 101, 100)); + bb.push_back (db::Box (200, 0, 300, 100)); + bb.push_back (db::Box ()); + bb.push_back (db::Box (100, 0, 200, 100)); + bb.push_back (db::Box ()); + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bs.insert (&*b, b - bb.begin ()); + } + + BoxScannerTestRecorder tr; + bs.set_fill_factor (0.0); + db::box_convert bc; + bs.process (tr, 0, bc); + EXPECT_EQ (tr.str, "<2><4>(0-3)<0><1><3>"); +} + void run_test2 (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true) { std::vector bb; @@ -787,3 +896,255 @@ TEST(100) } + +TEST(two_1) +{ + db::box_scanner2 bs; + + std::vector bb; + std::vector bb2; + bb.push_back (db::Box (0, 210, 200, 310)); + bb.push_back (db::Box (10, 220, 210, 320)); + bb.push_back (db::Box (0, 0, 100, 100)); + bb.push_back (db::Box (50, 50, 150, 150)); + bb.push_back (db::Box (10, 10, 110, 110)); + bb.push_back (db::Box (100, 10, 200, 110)); + + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bb2.push_back (db::SimplePolygon (*b)); + } + + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bs.insert1 (&*b, b - bb.begin ()); + } + for (std::vector::const_iterator b = bb2.begin (); b != bb2.end (); ++b) { + bs.insert2 (&*b, int (b - bb2.begin ()) + 10); + } + + BoxScannerTestRecorderTwo tr; + bs.set_fill_factor (0.0); + db::box_convert bc1; + db::box_convert bc2; + bs.set_scanner_threshold (0); + bs.process (tr, 1, bc1, bc2); + EXPECT_EQ (tr.str, "(2-12)(2-14)(4-12)(4-14)(2-15)(4-15)(5-12)(5-14)(5-15)(2-13)(4-13)(3-12)(3-14)(3-13)(3-15)(5-13)(0-10)<2><5><4><3><12><15><14><13>(0-11)(1-10)(1-11)<0><1><10><11>"); +} + +TEST(two_1a) +{ + db::box_scanner2 bs; + + std::vector bb; + bb.push_back (db::Box (0, 210, 200, 310)); + //bb.push_back (db::Box (10, 220, 210, 320)); + //bb.push_back (db::Box (0, 0, 100, 100)); + bb.push_back (db::Box (50, 50, 150, 150)); + bb.push_back (db::Box (10, 10, 110, 110)); + //bb.push_back (db::Box (100, 10, 200, 110)); + + std::vector bb2; + //bb2.push_back (db::SimplePolygon (db::Box (0, 210, 200, 310))); + bb2.push_back (db::SimplePolygon (db::Box (10, 220, 210, 320))); + bb2.push_back (db::SimplePolygon (db::Box (0, 0, 100, 100))); + //bb2.push_back (db::SimplePolygon (db::Box (50, 50, 150, 150))); + //bb2.push_back (db::SimplePolygon (db::Box (10, 10, 110, 110))); + bb2.push_back (db::SimplePolygon (db::Box (100, 10, 200, 110))); + + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bs.insert1 (&*b, b - bb.begin ()); + } + for (std::vector::const_iterator b = bb2.begin (); b != bb2.end (); ++b) { + bs.insert2 (&*b, int (b - bb2.begin ()) + 10); + } + + BoxScannerTestRecorderTwo tr; + bs.set_fill_factor (0.0); + db::box_convert bc1; + db::box_convert bc2; + bs.set_scanner_threshold (0); + bs.process (tr, 1, bc1, bc2); + EXPECT_EQ (tr.str, "(2-11)(2-12)(1-11)(1-12)<1><2><11><12>(0-10)<0><10>"); +} + +TEST(two_1b) +{ + db::box_scanner2 bs; + + std::vector bb; + //bb.push_back (db::Box (0, 210, 200, 310)); + bb.push_back (db::Box (10, 220, 210, 320)); + bb.push_back (db::Box (0, 0, 100, 100)); + //bb.push_back (db::Box (50, 50, 150, 150)); + //bb.push_back (db::Box (10, 10, 110, 110)); + bb.push_back (db::Box (100, 10, 200, 110)); + + std::vector bb2; + bb2.push_back (db::SimplePolygon (db::Box (0, 210, 200, 310))); + //bb2.push_back (db::SimplePolygon (db::Box (10, 220, 210, 320))); + //bb2.push_back (db::SimplePolygon (db::Box (0, 0, 100, 100))); + bb2.push_back (db::SimplePolygon (db::Box (50, 50, 150, 150))); + bb2.push_back (db::SimplePolygon (db::Box (10, 10, 110, 110))); + //bb2.push_back (db::SimplePolygon (db::Box (100, 10, 200, 110))); + + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bs.insert1 (&*b, b - bb.begin ()); + } + for (std::vector::const_iterator b = bb2.begin (); b != bb2.end (); ++b) { + bs.insert2 (&*b, int (b - bb2.begin ()) + 10); + } + + BoxScannerTestRecorderTwo tr; + bs.set_fill_factor (0.0); + db::box_convert bc1; + db::box_convert bc2; + bs.set_scanner_threshold (0); + EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true); + EXPECT_EQ (tr.str, "(1-12)(2-12)(1-11)(2-11)<1><2><11><12>(0-10)<0><10>"); + + + BoxScannerTestRecorderTwoStopping trstop; + EXPECT_EQ (bs.process (trstop, 1, bc1, bc2), false); + EXPECT_EQ (trstop.str, "(1-12)"); +} + +TEST(two_1c) +{ + // some empty elements + db::box_scanner2 bs; + + std::vector bb; + bb.push_back (db::Box ()); + bb.push_back (db::Box (0, 0, 100, 100)); + bb.push_back (db::Box (100, 10, 200, 110)); + + std::vector bb2; + bb2.push_back (db::SimplePolygon (db::Box ())); + bb2.push_back (db::SimplePolygon (db::Box (50, 50, 150, 150))); + bb2.push_back (db::SimplePolygon (db::Box (10, 10, 110, 110))); + + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bs.insert1 (&*b, b - bb.begin ()); + } + for (std::vector::const_iterator b = bb2.begin (); b != bb2.end (); ++b) { + bs.insert2 (&*b, int (b - bb2.begin ()) + 10); + } + + BoxScannerTestRecorderTwo tr; + bs.set_fill_factor (0.0); + db::box_convert bc1; + db::box_convert bc2; + bs.set_scanner_threshold (0); + EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true); + EXPECT_EQ (tr.str, "<0><10>(1-12)(2-12)(1-11)(2-11)<1><2><12><11>"); +} + +void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true) +{ + std::vector bb; + for (size_t i = 0; i < n; ++i) { + db::Coord x = rand () % spread; + db::Coord y = rand () % spread; + bb.push_back (db::Box (x, y, x + 100, y + 100)); + // std::cout << "Box 1" << bb.back ().to_string () << std::endl; + } + + std::vector bb2; + for (size_t i = 0; i < n; ++i) { + db::Coord x = rand () % spread; + db::Coord y = rand () % spread; + bb2.push_back (db::SimplePolygon (db::Box (x, y, x + 100, y + 100))); + // std::cout << "Polygon 2" << bb2.back ().to_string () << std::endl; + } + + db::box_scanner2 bs; + for (std::vector::const_iterator b = bb.begin (); b != bb.end (); ++b) { + bs.insert1 (&*b, b - bb.begin ()); + } + for (std::vector::const_iterator b2 = bb2.begin (); b2 != bb2.end (); ++b2) { + bs.insert2 (&*b2, int (b2 - bb2.begin ())); + } + + BoxScannerTestRecorder2Two tr; + bs.set_fill_factor (ff); + db::box_convert bc1; + db::box_convert bc2; + { + tl::SelfTimer timer ("box-scanner"); + bs.set_scanner_threshold (0); + bs.process (tr, touch ? 1 : 0, bc1, bc2); + } + + std::set > interactions; + { + tl::SelfTimer timer ("brute-force"); + for (size_t i = 0; i < bb.size (); ++i) { + for (size_t j = 0; j < bb2.size (); ++j) { + if ((touch && bb[i].touches (bb2[j].box ())) || (!touch && bb[i].overlaps (bb2[j].box ()))) { + interactions.insert (std::make_pair (i, int (j))); + } + } + } + } + + if (interactions != tr.interactions) { + tl::info << "Interactions 1-2 in 'brute force' but not in 'box-scanner':"; + for (std::set >::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { + if (tr.interactions.find (*i) == tr.interactions.end ()) { + tl::info << " " << i->first << "-" << i->second; + } + } + tl::info << "Interactions 1-2 in 'box-scanner' but not in 'brute force':"; + for (std::set >::const_iterator i = tr.interactions.begin (); i != tr.interactions.end (); ++i) { + if (interactions.find (*i) == interactions.end ()) { + tl::info << " " << i->first << "-" << i->second; + } + } + } + EXPECT_EQ (interactions == tr.interactions, true); + +} + +TEST(two_2a) +{ + run_test2_two(_this, 10, 0.0, 1000); +} + +TEST(two_2b) +{ + run_test2_two(_this, 10, 0.0, 100); +} + +TEST(two_2c) +{ + run_test2_two(_this, 10, 0.0, 10); +} + +TEST(two_2d) +{ + run_test2_two(_this, 1000, 0.0, 1000); +} + +TEST(two_2e) +{ + run_test2_two(_this, 1000, 2, 1000); +} + +TEST(two_2f) +{ + run_test2_two(_this, 1000, 2, 1000, false); +} + +TEST(two_2g) +{ + run_test2_two(_this, 1000, 2, 500); +} + +TEST(two_2h) +{ + run_test2_two(_this, 1000, 2, 100); +} + +TEST(two_2i) +{ + run_test2_two(_this, 10000, 2, 10000); +} diff --git a/src/db/unit_tests/dbCellMapping.cc b/src/db/unit_tests/dbCellMapping.cc index 9b97122bc..f493f6116 100644 --- a/src/db/unit_tests/dbCellMapping.cc +++ b/src/db/unit_tests/dbCellMapping.cc @@ -427,3 +427,29 @@ TEST(5) EXPECT_EQ (l2s (hh), "a0top#0:cell_index=4 r90 0,0 array=(0,10,10,0 5x1),cell_index=5 r90 0,0 array=(0,10,10,0 5x2),cell_index=1 r0 10,0,cell_index=6 r90 0,0 array=(0,20,20,0 5x2),cell_index=7 r90 0,0 array=(0,20,20,0 5x2);a1#1:cell_index=2 r0 0,0,cell_index=2 r0 0,20,cell_index=3 r0 0,40;a2#2:cell_index=3 r0 0,0,cell_index=3 r0 0,10;a3#3:cell_index=4 r90 0,0;a4#4:;a5#5:;a4$1#6:;a5$1#7:"); } +// Resolution of array references +TEST(6) +{ + std::auto_ptr g (new db::Layout ()); + db::Cell &a0 (g->cell (g->add_cell ("a0"))); + db::Cell *a4p, *a5p; + a4p = &(g->cell (g->add_cell ("a4"))); + a5p = &(g->cell (g->add_cell ("a5"))); + db::Cell &a4 (*a4p); + db::Cell &a5 (*a5p); + + a0.insert (db::CellInstArray (db::CellInst (a4.cell_index ()), db::Trans (1/*r90*/), g->array_repository (), db::Vector(0, 10), db::Vector(10, 0), 5, 2)); + a0.insert (db::CellInstArray (db::CellInst (a5.cell_index ()), db::Trans (1/*r90*/), g->array_repository (), db::Vector(0, 10), db::Vector(10, 0), 5, 2)); + + db::Layout h; + db::Cell &b0 (h.cell (h.add_cell ("a0top"))); + + db::CellMapping cm; + cm.create_single_mapping_full (h, b0.cell_index (), *g, a0.cell_index ()); + EXPECT_EQ (m2s (cm, *g, h), "a0->a0top;a4->a4;a5->a5"); + + g.reset (0); + + EXPECT_EQ (l2s (h), "a0top#0:cell_index=1 r90 0,0 array=(0,10,10,0 5x2),cell_index=2 r90 0,0 array=(0,10,10,0 5x2);a4#1:;a5#2:"); +} + diff --git a/src/db/unit_tests/dbCellVariantsTests.cc b/src/db/unit_tests/dbCellVariantsTests.cc new file mode 100644 index 000000000..f43cd7f1e --- /dev/null +++ b/src/db/unit_tests/dbCellVariantsTests.cc @@ -0,0 +1,438 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbCellVariants.h" +#include "dbTestSupport.h" +#include "dbReader.h" +#include "tlUnitTest.h" +#include "tlStream.h" + +std::string var2str (const std::map &vars) +{ + std::string res; + for (std::map::const_iterator i = vars.begin (); i != vars.end (); ++i) { + if (! res.empty ()) { + res += ";"; + } + res += i->first.to_string (); + res += "["; + res += tl::to_string (i->second); + res += "]"; + } + return res; +} + +std::string vm2str (const db::Layout &ly, const std::map > &vm) +{ + std::string res; + for (std::map >::const_iterator i = vm.begin (); i != vm.end (); ++i) { + if (! res.empty ()) { + res += ";"; + } + res += ly.cell_name (i->first); + res += ":"; + for (std::map::const_iterator j = i->second.begin (); j != i->second.end (); ++j) { + if (j != i->second.begin ()) { + res += ","; + } + res += ly.cell_name (j->second); + res += "["; + res += j->first.to_string (); + res += "]"; + } + } + return res; +} + +std::string inst2str (const db::Layout &ly, const db::Cell &cell) +{ + std::string res; + for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) { + for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end (); ++ia) { + db::ICplxTrans rt = i->complex_trans (*ia); + if (! res.empty ()) { + res += ";"; + } + res += ly.cell_name (i->cell_index ()); + res += ":"; + res += rt.to_string (); + } + } + return res; +} + +TEST(1_Trivial) +{ + db::Layout ly; + db::Cell &a = ly.cell (ly.add_cell ("A")); + db::Cell &b = ly.cell (ly.add_cell ("B")); + db::Cell &c = ly.cell (ly.add_cell ("C")); + db::Cell &d = ly.cell (ly.add_cell ("D")); + + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (0, false, db::Vector (1, 10)))); + + db::OrientationReducer red; + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), ""); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); + + std::map > vm; + vb.separate_variants (ly, a, &vm); + EXPECT_EQ (vm.empty (), true); + EXPECT_EQ (vm2str (ly, vm), ""); +} + +TEST(2_TwoVariants) +{ + db::Layout ly; + db::Cell &a = ly.cell (ly.add_cell ("A")); + db::Cell &b = ly.cell (ly.add_cell ("B")); + db::Cell &c = ly.cell (ly.add_cell ("C")); + db::Cell &d = ly.cell (ly.add_cell ("D")); + + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (0, false, db::Vector (1, 10)))); + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (0, true, db::Vector (1, 100)))); + + db::OrientationReducer red; + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "m0 *1 0,0[1];r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), ""); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B:m0 *1 1,100"); + + std::map > vm; + vb.separate_variants (ly, a, &vm); + EXPECT_EQ (vm2str (ly, vm), "B:B[m0 *1 0,0],B$VAR1[r0 *1 0,0]"); + EXPECT_EQ (inst2str (ly, a), "B$VAR1:r0 *1 1,10;B:m0 *1 1,100"); +} + +TEST(3_TwoLevels) +{ + db::Layout ly; + db::Cell &a = ly.cell (ly.add_cell ("A")); + db::Cell &b = ly.cell (ly.add_cell ("B")); + db::Cell &c = ly.cell (ly.add_cell ("C")); + db::Cell &d = ly.cell (ly.add_cell ("D")); + + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (0, false, db::Vector (1, 10)))); + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (1, false, db::Vector (1, 100)))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans (0, false, db::Vector (2, 10)))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans (0, true, db::Vector (2, 100)))); + + db::OrientationReducer red; + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0[1];r90 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "m0 *1 0,0[1];r0 *1 0,0[1];m45 *1 0,0[1];r90 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B:r90 *1 1,100"); + EXPECT_EQ (inst2str (ly, b), "C:r0 *1 2,10;C:m0 *1 2,100"); + + std::map > vm; + vb.separate_variants (ly, a, &vm); + EXPECT_EQ (vm2str (ly, vm), "B:B[r0 *1 0,0],B$VAR1[r90 *1 0,0];C:C[m0 *1 0,0],C$VAR1[r0 *1 0,0],C$VAR2[m45 *1 0,0],C$VAR3[r90 *1 0,0]"); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B$VAR1:r90 *1 1,100"); + EXPECT_EQ (inst2str (ly, b), "C$VAR1:r0 *1 2,10;C:m0 *1 2,100"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("B$VAR1").second)), "C$VAR3:r0 *1 2,10;C$VAR2:m0 *1 2,100"); +} + +TEST(4_ThreeLevels) +{ + db::Layout ly; + db::Cell &a = ly.cell (ly.add_cell ("A")); + db::Cell &b = ly.cell (ly.add_cell ("B")); + db::Cell &c = ly.cell (ly.add_cell ("C")); + db::Cell &d = ly.cell (ly.add_cell ("D")); + + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (0, false, db::Vector (1, 10)))); + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (1, false, db::Vector (1, 100)))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans (0, false, db::Vector (2, 10)))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans (0, true, db::Vector (2, 100)))); + c.insert (db::CellInstArray (db::CellInst (d.cell_index ()), db::Trans (1, true, db::Vector (0, 0)))); + + db::OrientationReducer red; + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0[1];r90 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "m0 *1 0,0[1];r0 *1 0,0[1];m45 *1 0,0[1];r90 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), "r270 *1 0,0[1];m90 *1 0,0[1];r0 *1 0,0[1];m45 *1 0,0[1]"); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B:r90 *1 1,100"); + EXPECT_EQ (inst2str (ly, b), "C:r0 *1 2,10;C:m0 *1 2,100"); + EXPECT_EQ (inst2str (ly, c), "D:m45 *1 0,0"); + + std::map > vm; + vb.separate_variants (ly, a, &vm); + EXPECT_EQ (vm2str (ly, vm), "B:B[r0 *1 0,0],B$VAR1[r90 *1 0,0];C:C[m0 *1 0,0],C$VAR1[r0 *1 0,0],C$VAR2[m45 *1 0,0],C$VAR3[r90 *1 0,0];D:D[r270 *1 0,0],D$VAR1[m90 *1 0,0],D$VAR2[r0 *1 0,0],D$VAR3[m45 *1 0,0]"); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B$VAR1:r90 *1 1,100"); + EXPECT_EQ (inst2str (ly, b), "C$VAR1:r0 *1 2,10;C:m0 *1 2,100"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("B$VAR1").second)), "C$VAR3:r0 *1 2,10;C$VAR2:m0 *1 2,100"); + EXPECT_EQ (inst2str (ly, c), "D:m45 *1 0,0"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("C$VAR1").second)), "D$VAR3:m45 *1 0,0"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("C$VAR2").second)), "D$VAR2:m45 *1 0,0"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("C$VAR3").second)), "D$VAR1:m45 *1 0,0"); +} + +TEST(5_ComplexTrans) +{ + db::Layout ly; + db::Cell &a = ly.cell (ly.add_cell ("A")); + db::Cell &b = ly.cell (ly.add_cell ("B")); + db::Cell &c = ly.cell (ly.add_cell ("C")); + db::Cell &d = ly.cell (ly.add_cell ("D")); + + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::ICplxTrans (db::Trans (0, false, db::Vector (1, 10))))); + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::ICplxTrans (db::Trans (1, false, db::Vector (1, 100))))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (db::Trans (0, false, db::Vector (2, 10))))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (db::Trans (0, true, db::Vector (2, 100))))); + + db::OrientationReducer red; + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0[1];r90 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "m0 *1 0,0[1];r0 *1 0,0[1];m45 *1 0,0[1];r90 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); +} + +TEST(6_Arrays) +{ + + db::Layout ly; + db::Cell &a = ly.cell (ly.add_cell ("A")); + db::Cell &b = ly.cell (ly.add_cell ("B")); + db::Cell &c = ly.cell (ly.add_cell ("C")); + db::Cell &d = ly.cell (ly.add_cell ("D")); + + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (0, false, db::Vector (1, 10)), db::Vector (0, 100), db::Vector (100, 0), 10, 10)); + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (1, false, db::Vector (1, 100)))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans (0, false, db::Vector (2, 10)), db::Vector (0, 101), db::Vector (101, 0), 10, 10)); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans (0, true, db::Vector (2, 100)))); + + db::OrientationReducer red; + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0[100];r90 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "m0 *1 0,0[100];r0 *1 0,0[10000];m45 *1 0,0[1];r90 *1 0,0[100]"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); +} + +TEST(7_ScalingVariants) +{ + db::Layout ly; + db::Cell &a = ly.cell (ly.add_cell ("A")); + db::Cell &b = ly.cell (ly.add_cell ("B")); + db::Cell &c = ly.cell (ly.add_cell ("C")); + db::Cell &d = ly.cell (ly.add_cell ("D")); + + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::ICplxTrans (1.5, 0, false, db::Vector (1, 10)), db::Vector (0, 100), db::Vector (100, 0), 10, 10)); + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::ICplxTrans (1.0, 90.0, false, db::Vector (1, 100)))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (2.0, 0, false, db::Vector (2, 10)), db::Vector (0, 101), db::Vector (101, 0), 10, 10)); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (1.0, 0, true, db::Vector (2, 100)))); + + db::MagnificationReducer red; + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0[1];r0 *1.5 0,0[100]"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "r0 *1 0,0[1];r0 *1.5 0,0[100];r0 *2 0,0[100];r0 *3 0,0[10000]"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); +} + +TEST(8_GridVariants) +{ + db::Layout ly; + db::Cell &a = ly.cell (ly.add_cell ("A")); + db::Cell &b = ly.cell (ly.add_cell ("B")); + db::Cell &c = ly.cell (ly.add_cell ("C")); + db::Cell &d = ly.cell (ly.add_cell ("D")); + + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::ICplxTrans (1.0, 0, false, db::Vector (1, 10)), db::Vector (0, 101), db::Vector (102, 0), 2, 2)); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (1.0, 0, false, db::Vector (2, 3)))); + + db::GridReducer red (10); + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 1,0[1];r0 *1 3,0[1];r0 *1 1,1[1];r0 *1 3,1[1]"); + + // placements are: + // b in a: r0 *1 x=1,1+102 y=10,10+101 + // c in b: r0 *1 x=2,y=3 + // expanded placements: + // c in a: r0 *2 x=1,1+102 y=10,10+101 x r0 *1 x=2,y=3 + // = (3,13),(105,13),(3,114),(105,114) + // expanded placements mod 10: + // c in a: r0 *2 x=1,1+102 y=10,10+101 x r0 *1 x=2,y=3 + // = (3,3),(5,3),(3,4),(5,4) + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "r0 *1 3,3[1];r0 *1 5,3[1];r0 *1 3,4[1];r0 *1 5,4[1]"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B:r0 *1 1,111;B:r0 *1 103,10;B:r0 *1 103,111"); + EXPECT_EQ (inst2str (ly, b), "C:r0 *1 2,3"); + EXPECT_EQ (inst2str (ly, c), ""); + + std::map > vm; + vb.separate_variants (ly, a, &vm); + EXPECT_EQ (vm2str (ly, vm), "B:B[r0 *1 1,0],B$VAR1[r0 *1 3,0],B$VAR2[r0 *1 1,1],B$VAR3[r0 *1 3,1];C:C[r0 *1 3,3],C$VAR1[r0 *1 5,3],C$VAR2[r0 *1 3,4],C$VAR3[r0 *1 5,4]"); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B$VAR2:r0 *1 1,111;B$VAR1:r0 *1 103,10;B$VAR3:r0 *1 103,111"); + EXPECT_EQ (inst2str (ly, b), "C:r0 *1 2,3"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("B$VAR1").second)), "C$VAR1:r0 *1 2,3"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("B$VAR2").second)), "C$VAR2:r0 *1 2,3"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("B$VAR3").second)), "C$VAR3:r0 *1 2,3"); + EXPECT_EQ (inst2str (ly, c), ""); +} + +TEST(9_ComplexGridVariants) +{ + db::Layout ly; + db::Cell &a = ly.cell (ly.add_cell ("A")); + db::Cell &b = ly.cell (ly.add_cell ("B")); + db::Cell &c = ly.cell (ly.add_cell ("C")); + db::Cell &d = ly.cell (ly.add_cell ("D")); + + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::ICplxTrans (2.0, 0, false, db::Vector (1, 10)), db::Vector (0, 101), db::Vector (102, 0), 2, 2)); + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::ICplxTrans (1.0, 90.0, false, db::Vector (1, 100)))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (2.0, 0, false, db::Vector (2, 10)), db::Vector (0, 103), db::Vector (105, 0), 2, 2)); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (1.0, 0, true, db::Vector (2, 100)))); + + db::GridReducer red (10); + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *2 1,0[1];r90 *1 1,0[1];r0 *2 3,0[1];r0 *2 1,1[1];r0 *2 3,1[1]"); + + // placements are: + // b in a: r0 *2 x=1,1+102 y=10,10+101 + // r90 *1 x=1,y=100 + // c in b: r0 *2 x=2,2+105 y=10,10+103 + // m0 *1 x=2,y=100 + // expanded placements: + // c in a: r0 *2 x=1,1+102 y=10,10+101 x r0 *2 x=2,2+105 y=10,10+103 + // = (5,30),(215,30),(5,236),(215,236) + // (107,30),(317,30),(107,236),(317,236) + // (5,131),(215,131),(5,337),(215,337) + // (107,131),(317,131),(107,337),(317,337) + // r0 *2 x=1,1+102 y=10,10+101 x m0 *1 x=2,y=100 + // (5,210),(5,311),(107,210),(107,311) + // r90 *1 x=1,y=100 x r0 *2 x=2,2+105 y=10,10+103 + // (-9,102),(-9,207),(-112,102),(-112,207) + // r90 *1 x=1,y=100 x m0 *1 x=2,y=100 + // (-99,102) + // expanded placements mod 10: + // c in a: r0 *2 x=1,1+102 y=10,10+101 x r0 *2 x=2,2+105 y=10,10+103 + // = (5,0),(5,0),(5,6),(5,6) + // (7,0),(7,0),(7,6),(7,6) + // (5,1),(5,1),(5,7),(5,7) + // (7,1),(7,1),(7,7),(7,7) + // r0 *2 x=1,1+102 y=10,10+101 x m0 *1 x=2,y=100 + // (5,0),(5,1),(7,0),(7,1) + // r90 *1 x=1,y=100 x r0 *2 x=2,2+105 y=10,10+103 + // (1,2),(1,7),(8,2),(8,7) + // r90 *1 x=1,y=100 x m0 *1 x=2,y=100 + // (1,2) + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "m0 *2 5,0[1];r0 *4 5,0[2];m0 *2 7,0[1];r0 *4 7,0[2];m0 *2 5,1[1];r0 *4 5,1[2];" + "m0 *2 7,1[1];r0 *4 7,1[2];m45 *1 1,2[1];r90 *2 1,2[1];r90 *2 8,2[1];" + "r0 *4 5,6[2];r0 *4 7,6[2];r90 *2 1,7[1];r0 *4 5,7[2];r0 *4 7,7[2];r90 *2 8,7[1]"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); +} + +TEST(100_OrientationVariantsWithLayout) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::OrientationReducer red; + db::cell_variants_collector vb (red); + vb.collect (ly, top_cell); + vb.separate_variants (ly, top_cell); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/cell_variants_au1.gds"); +} + +TEST(101_Propagation) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/cell_variants_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + std::map > to_commit; + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.insert_layer (db::LayerProperties (2, 0)); + + db::cell_variants_collector vb; + vb.collect (ly, top_cell); + + for (db::Layout::const_iterator c = ly.begin (); c != ly.end (); ++c) { + + const std::map &vv = vb.variants (c->cell_index ()); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + + db::Shapes &out = to_commit [c->cell_index ()][v->first]; + for (db::Shapes::shape_iterator s = c->shapes (l1).begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + db::Box b = s->bbox ().transformed (v->first); + b.enlarge (db::Vector (-100, 0)); + out.insert (b.transformed (v->first.inverted ())); + } + + } + + } + + vb.commit_shapes (ly, top_cell, l2, to_commit); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/cell_variants_au2.gds"); +} diff --git a/src/db/unit_tests/dbDeepEdgePairsTests.cc b/src/db/unit_tests/dbDeepEdgePairsTests.cc new file mode 100644 index 000000000..58845695a --- /dev/null +++ b/src/db/unit_tests/dbDeepEdgePairsTests.cc @@ -0,0 +1,105 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbHierarchyBuilder.h" +#include "dbReader.h" +#include "dbTestSupport.h" +#include "dbEdgePairs.h" +#include "dbDeepShapeStore.h" +#include "dbRegion.h" +#include "dbEdges.h" +#include "tlUnitTest.h" +#include "tlStream.h" + +TEST(1_Basics) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + // turn boxes into edge pairs to produce a test case + for (db::Layout::layer_iterator l = ly.begin_layers (); l != ly.end_layers (); ++l) { + for (db::Layout::iterator c = ly.begin (); c != ly.end (); ++c) { + db::Shapes out (ly.is_editable ()); + db::Shapes &in = c->shapes ((*l).first); + for (db::Shapes::shape_iterator s = in.begin (db::ShapeIterator::All); !s.at_end (); ++s) { + if (s->is_box ()) { + db::Box b = s->box (); + db::EdgePair ep (db::Edge (b.p1 (), b.upper_left ()), db::Edge (b.p2 (), b.lower_right ())); + out.insert (ep); + } + } + in.swap (out); + } + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l100 = ly.get_layer (db::LayerProperties (100, 0)); + + db::EdgePairs ep2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::EdgePairs ep3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::EdgePairs ep100 (db::RecursiveShapeIterator (ly, top_cell, l100), dss); + + EXPECT_EQ (ep100.empty (), true); + EXPECT_EQ (ep2.empty (), false); + EXPECT_EQ (ep2.bbox ().to_string (), "(-1050,-475;24810,3275)"); + EXPECT_EQ (ep2.size (), size_t (40)); + EXPECT_EQ (ep2.to_string ().substr (0, 42), "(-1050,-475;-1050,475)/(250,475;250,-475);"); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + db::Region polygons; + ep2.polygons (polygons); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), polygons); + + polygons.clear (); + ep3.polygons (polygons); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), polygons); + + db::Edges edges, first_edges, second_edges; + ep2.edges (edges); + ep2.first_edges (first_edges); + ep2.second_edges (second_edges); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), edges); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), first_edges); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), second_edges); + + // NOTE: insert ep2 as layer 14/0 from a copy - this tests the ability to copy-construct an EP + db::EdgePairs ep2_copy (ep2); + ep2_copy.insert_into_as_polygons (&target, target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), 1); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edge_pairs_au1.gds"); +} diff --git a/src/db/unit_tests/dbDeepEdgesTests.cc b/src/db/unit_tests/dbDeepEdgesTests.cc new file mode 100644 index 000000000..9fcfed52a --- /dev/null +++ b/src/db/unit_tests/dbDeepEdgesTests.cc @@ -0,0 +1,437 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbHierarchyBuilder.h" +#include "dbReader.h" +#include "dbTestSupport.h" +#include "dbEdges.h" +#include "dbRegion.h" +#include "dbEdgesUtils.h" +#include "dbDeepShapeStore.h" +#include "tlUnitTest.h" +#include "tlStream.h" + +TEST(1) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + + db::DeepShapeStore dss; + db::Layout target; + + // deliberately using vector to force reallocation ... + std::vector edges; + std::vector target_layers; + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1); + target_layers.push_back (target.insert_layer (*(*li).second)); + + edges.push_back (db::Edges (iter, dss)); + + EXPECT_EQ (edges.back ().size (), db::Edges (iter).size ()); + EXPECT_EQ (edges.back ().bbox (), db::Edges (iter).bbox ()); + + } + + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + for (std::vector::const_iterator r = edges.begin (); r != edges.end (); ++r) { + target.insert (target_top_cell_index, target_layers [r - edges.begin ()], *r); + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au1.gds"); +} + +TEST(2_MergeEdges) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Edges r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + r2.merge (); + db::Edges r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Edges r3_merged (r3.merged ()); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r3_merged); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au2.gds"); +} + +TEST(3_Edge2EdgeBooleans) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r2and3 = r2 & r3; + + db::Edges e3 = r3.edges (); + db::Edges e2and3 = r2and3.edges (); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), e3 & e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), e3 - e2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), e3 ^ e2and3); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au3.gds"); +} + +TEST(4_Edge2PolygonBooleans) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r2and3 = r2 & r3; + + db::Edges e3 = r3.edges (); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), r3); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e3 & r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e3 & r2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), e3 - r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), e3 - r2and3); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), e3.inside_part (r2)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), e3.inside_part (r2and3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), e3.outside_part (r2)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), e3.outside_part (r2and3)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au4.gds"); +} + +TEST(5_Filters) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Edges e2 = r2.edges (); + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + + db::EdgeLengthFilter elf1 (0, 40000, false); + db::EdgeLengthFilter elf2 (0, 30000, true); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e2.filtered (elf1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2.filtered (elf2)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au5a.gds"); + } + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + + db::EdgeOrientationFilter eof1 (0, 1, false); + db::EdgeOrientationFilter eof2 (0, 1, true); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e2.filtered (eof1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2.filtered (eof2)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au5b.gds"); + } +} + +TEST(6_Extended) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Edges e2 = r2.edges (); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + + db::EdgeLengthFilter elf1 (0, 40000, false); + db::Edges e2f = e2.filtered (elf1); + + db::Region e2e1; + e2.extended (e2e1, 100, 200, 300, 50); + db::Region e2e2; + e2f.extended (e2e2, 0, 0, 300, 0); + db::Region e2e3; + e2.extended (e2e3, 100, 200, 300, 50, true); + db::Region e2e4; + e2f.extended (e2e4, 0, 0, 300, 0, true); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e2e1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2e2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), e2e3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), e2e4); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au6.gds"); +} + +TEST(7_Partial) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Edges e2 = r2.edges (); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + + db::EdgeLengthFilter elf1 (0, 40000, false); + db::Edges e2f = e2.filtered (elf1); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e2.start_segments (1000, 0.0)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2.start_segments (0, 0.2)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), e2f.start_segments (1000, 0.0)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), e2f.start_segments (0, 0.2)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), e2.end_segments (1000, 0.0)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), e2.end_segments (0, 0.2)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), e2f.end_segments (1000, 0.0)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), e2f.end_segments (0, 0.2)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), e2.centers (1000, 0.0)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), e2.centers (0, 0.2)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), e2f.centers (1000, 0.0)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (33, 0)), e2f.centers (0, 0.2)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au7.gds"); +} + +TEST(8_SelectInteracting) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Edges e2 = r2.edges (); + db::Edges e3 = r3.edges (); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), r3); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e2.selected_interacting (e3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2.selected_not_interacting (e3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), e3.selected_interacting (e2)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), e3.selected_not_interacting (e2)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), e2.selected_interacting (r3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), e2.selected_not_interacting (r3)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), e3.selected_interacting (r2)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), e3.selected_not_interacting (r2)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au8.gds"); +} + +TEST(9_DRCChecks) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + unsigned int l4 = ly.get_layer (db::LayerProperties (4, 0)); + + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6), dss); + db::Region r4 (db::RecursiveShapeIterator (ly, top_cell, l4), dss); + + db::Edges e3 = r3.edges (); + db::Edges e4 = r4.edges (); + db::Edges e6 = r6.edges (); + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (4, 0)), r4); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (6, 0)), r6); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e3.space_check (500, false, db::Projection, 90, 0)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e3.space_check (500, true, db::Projection, 90, 300)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), e3.separation_check (e4, 200, false, db::Projection, 90, 0)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), e6.enclosing_check (e4, 100, true, db::Projection, 90, 0)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au9.gds"); + } +} + diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc new file mode 100644 index 000000000..ed2607462 --- /dev/null +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -0,0 +1,1408 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbHierarchyBuilder.h" +#include "dbReader.h" +#include "dbTestSupport.h" +#include "dbRegion.h" +#include "dbEdges.h" +#include "dbRegionUtils.h" +#include "dbRegionProcessors.h" +#include "dbEdgesUtils.h" +#include "dbDeepShapeStore.h" +#include "dbOriginalLayerRegion.h" +#include "tlUnitTest.h" +#include "tlStream.h" + +TEST(1) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + + db::DeepShapeStore dss; + db::Layout target; + + // deliberately using vector to force reallocation ... + std::vector regions; + std::vector target_layers; + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1); + target_layers.push_back (target.insert_layer (*(*li).second)); + + regions.push_back (db::Region (iter, dss)); + + EXPECT_EQ (regions.back ().size (), db::Region (iter).size ()); + EXPECT_EQ (regions.back ().bbox (), db::Region (iter).bbox ()); + EXPECT_EQ (regions.back ().is_merged (), false); + + } + + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + for (std::vector::const_iterator r = regions.begin (); r != regions.end (); ++r) { + target.insert (target_top_cell_index, target_layers [r - regions.begin ()], *r); + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au1.gds"); + + // some operations + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + db::Region r2 (db::RecursiveShapeIterator (ly, ly.cell (top_cell_index), l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, ly.cell (top_cell_index), l3), dss); + + EXPECT_EQ (r2.is_merged (), false); + r2.merge (); + EXPECT_EQ (r2.is_merged (), true); + r2 += r3; + EXPECT_EQ (r2.is_merged (), false); + EXPECT_EQ (r2.merged ().is_merged (), true); + EXPECT_EQ (r2.is_merged (), false); + r2.merge (); + EXPECT_EQ (r2.is_merged (), true); + r2.flatten (); + EXPECT_EQ (r2.is_merged (), true); + r2.insert (db::Box (0, 0, 1000, 2000)); + EXPECT_EQ (r2.is_merged (), false); +} + +TEST(2) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + + db::DeepShapeStore dss; + db::Layout target; + + // deliberately using vector to force reallocation ... + std::vector > regions; + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + unsigned int tl = target.insert_layer (*(*li).second); + + db::RecursiveShapeIterator iter1 (ly, ly.cell (top_cell_index), li1, db::Box (2000, -1000, 6000, 4000)); + regions.push_back (std::make_pair (db::Region (iter1, dss), tl)); + + db::RecursiveShapeIterator iter2 (ly, ly.cell (top_cell_index), li1, db::Box (14000, 0, 20000, 3000)); + regions.push_back (std::make_pair (db::Region (iter2, dss), tl)); + + } + + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + for (std::vector >::const_iterator r = regions.begin (); r != regions.end (); ++r) { + target.insert (target_top_cell_index, r->second, r->first); + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au2.gds"); +} + +TEST(3_BoolAndNot) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l42 = ly.get_layer (db::LayerProperties (42, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r42 (db::RecursiveShapeIterator (ly, top_cell, l42), dss); + db::Region box (db::Box (2000, -1000, 6000, 4000)); + + db::Region r2minus3 = r2 - r3; + db::Region r2minusbox = r2 - box; + db::Region r2minus42 = r2 - r42; + db::Region rboxminus3 = box - r3; + db::Region r42minus3 = r42 - r3; + db::Region r42minus42 = r42 - r42; + + db::Region r2and3 = r2 & r3; + db::Region r2andbox = r2 & box; + db::Region r2and42 = r2 & r42; + db::Region rboxand3 = box & r3; + db::Region r42and3 = r42 & r3; + db::Region r42and42 = r42 & r42; + + EXPECT_EQ (r2and3.is_merged (), false); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2minus3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2minusbox); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2minus42); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), rboxminus3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r42minus3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r42minus42); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r2and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r2andbox); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r2and42); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), rboxand3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), r42and3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), r42and42); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au3.gds"); +} + +TEST(4_Add) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l42 = ly.get_layer (db::LayerProperties (42, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r42 (db::RecursiveShapeIterator (ly, top_cell, l42), dss); + db::Region box (db::Box (2000, -1000, 6000, 4000)); + db::Region r2box (db::RecursiveShapeIterator (ly, top_cell, l2, box), dss); + db::Region r3box (db::RecursiveShapeIterator (ly, top_cell, l3, box), dss); + + // intra-layout + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2 + r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r42 + r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2 + r42); + + db::Region rnew2 = r2; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), rnew2); + rnew2 += r3; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), rnew2); + rnew2 += r42; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), rnew2); + + db::Region rnew42 = r42; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), rnew42); + rnew42 += r2; + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), rnew42); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au4a.gds"); + } + + // inter-layout + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2box + r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2 + r3box); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2box + r3box); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), box + r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r2 + box); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au4b.gds"); + } +} + +TEST(5_BoolXOR) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l42 = ly.get_layer (db::LayerProperties (42, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r42 (db::RecursiveShapeIterator (ly, top_cell, l42), dss); + db::Region box (db::Box (2000, -1000, 6000, 4000)); + + db::Region r2xor3 = r2 ^ r3; + db::Region r2xorbox = r2 ^ box; + db::Region r2xor42 = r2 ^ r42; + db::Region rboxxor3 = box ^ r3; + db::Region r42xor3 = r42 ^ r3; + db::Region r42xor42 = r42 ^ r42; + + EXPECT_EQ (r2xor3.is_merged (), false); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2xor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2xorbox); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2xor42); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), rboxxor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r42xor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r42xor42); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au5.gds"); +} + +TEST(6_Reduction) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_max_vertex_count (4); + dss.set_threads (0); + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l42 = ly.get_layer (db::LayerProperties (42, 0)); + unsigned int lbox = ly.insert_layer (); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r42 (db::RecursiveShapeIterator (ly, top_cell, l42), dss); + + top_cell.shapes (lbox).insert (db::Box (2000, -1000, 6000, 4000)); + db::Region box (db::RecursiveShapeIterator (ly, top_cell, lbox), dss); + + db::Region r2xor3 = r2 ^ r3; + db::Region r2xorbox = r2 ^ box; + db::Region r2xor42 = r2 ^ r42; + db::Region rboxxor3 = box ^ r3; + db::Region r42xor3 = r42 ^ r3; + db::Region r42xor42 = r42 ^ r42; + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2xor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2xorbox); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2xor42); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), rboxxor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r42xor3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r42xor42); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au6.gds"); +} + +TEST(7_Merge) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_max_vertex_count (4); + dss.set_threads (0); + + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6), dss); + + db::Region r6_merged = r6.merged (); + db::Region r6_merged_minwc = r6.merged (false, 1); + + db::Region r6_minwc = r6; + r6_minwc.merge (false, 1); + + r6.merge (); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r6); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r6_minwc); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r6_merged); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r6_merged_minwc); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au7.gds"); +} + +TEST(8_AreaAndPerimeter) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_max_vertex_count (4); + dss.set_threads (0); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + EXPECT_EQ (r1.area (), db::coord_traits::area_type (9722000000)); + EXPECT_EQ (r1.perimeter (), db::coord_traits::perimeter_type (1360000)); + + EXPECT_EQ (r1.area (r1.bbox ()), db::coord_traits::area_type (9722000000)); + EXPECT_EQ (r1.perimeter (r1.bbox ()), db::coord_traits::perimeter_type (1360000)); + + EXPECT_EQ (r1.area (db::Box (40000, -90000, 50000, -80000)), db::coord_traits::area_type (100000000)); + EXPECT_EQ (r1.perimeter (db::Box (40000, -90000, 50000, -80000)), db::coord_traits::perimeter_type (0)); + EXPECT_EQ (r1.area (db::Box (-40000, -90000, -50000, -80000)), db::coord_traits::area_type (0)); +} + +TEST(9_SizingSimple) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_max_vertex_count (4); + dss.set_threads (0); + + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6), dss); + db::Region r6_sized = r6.sized (-50); + EXPECT_EQ (r6_sized.is_merged (), true); + db::Region r6_sized_aniso = r6.sized (-20, -100); + EXPECT_EQ (r6_sized_aniso.is_merged (), true); + db::Region r6_sized_plus = r6.sized (50); + EXPECT_EQ (r6_sized_plus.is_merged (), false); + db::Region r6_sized_aniso_plus = r6.sized (20, 100); + EXPECT_EQ (r6_sized_aniso_plus.is_merged (), false); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r6); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r6_sized); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r6_sized_aniso); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r6_sized_plus); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r6_sized_aniso_plus); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au9a.gds"); +} + +TEST(9_SizingWithScaleVariants) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_max_vertex_count (4); + dss.set_threads (0); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + db::Region r1_sized = r1.sized (-2000); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1_sized); + + // copy another layer - this challenges the ability to map to multiple variants + + unsigned int l1b = ly.get_layer (db::LayerProperties (1, 0)); + db::Region r1b (db::RecursiveShapeIterator (ly, top_cell, l1b), dss); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1b.merged ()); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au9b.gds"); +} + +TEST(9_SizingWithScaleAndXYVariants) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_max_vertex_count (4); + dss.set_threads (0); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + db::Region r1_sized = r1.sized (-2000); + db::Region r1_sized_aniso = r1.sized (-1000, -2000); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1_sized); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r1_sized_aniso); + + // copy another layer - this challenges the ability to map to multiple variants + + unsigned int l1b = ly.get_layer (db::LayerProperties (1, 0)); + db::Region r1b (db::RecursiveShapeIterator (ly, top_cell, l1b), dss); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1b.merged ()); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au9c.gds"); + + // merge back to original - this challenges the ability to map back the variants + + ly.insert (top_cell_index, ly.get_layer (db::LayerProperties (11, 0)), r1_sized); + ly.insert (top_cell_index, ly.get_layer (db::LayerProperties (12, 0)), r1_sized_aniso); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/deep_region_au9d.gds"); +} + +TEST(9_SizingWithBoolean) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_max_vertex_count (4); + dss.set_threads (0); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + db::Region r1_sized = r1.sized (2000); + r1_sized -= r1; + db::Region r1_sized_aniso = r1.sized (1000, 2000); + r1_sized_aniso -= r1; + EXPECT_EQ (r1_sized_aniso.is_merged (), false); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1_sized); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r1_sized_aniso); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au9e.gds"); +} + +TEST(10_HullsAndHoles) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_max_vertex_count (4); + dss.set_threads (0); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + db::Region r1_sized = r1.sized (2000); + r1_sized -= r1; + + db::Region hulls = r1_sized.hulls (); + db::Region holes = r1_sized.holes (); + EXPECT_EQ (hulls.is_merged (), true); + EXPECT_EQ (holes.is_merged (), true); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1_sized); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), hulls); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), holes); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au10.gds"); +} + +TEST(11_RoundAndSmoothed) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_max_vertex_count (4); + dss.set_threads (0); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + db::Region r1_sized = r1.sized (2000); + r1_sized -= r1; + + db::Region rounded = r1_sized.rounded_corners (3000, 5000, 100); + db::Region smoothed = rounded.smoothed (100); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1_sized); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), rounded); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), smoothed); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au11.gds"); +} + +TEST(12_GridSnap) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r3snapped = r3.snapped (50, 50); + EXPECT_EQ (r3snapped.is_merged (), false); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r3snapped); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au12.gds"); +} + +TEST(13_Edges) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Edges r3edges = r3.edges (); + EXPECT_EQ (r3edges.is_merged (), true); + + db::EdgeLengthFilter f (0, 500, true); + db::Edges r3edges_filtered = r3.edges (f); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r3edges); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r3edges_filtered); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au13.gds"); +} + +TEST(14_Interacting) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6), dss); + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2.selected_interacting (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2.selected_not_interacting (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2.selected_inside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r2.selected_not_inside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r2.selected_outside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r2.selected_not_outside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (16, 0)), r2.selected_overlapping (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (17, 0)), r2.selected_not_overlapping (r1)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r6.selected_interacting (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r6.selected_not_interacting (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r6.selected_inside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), r6.selected_not_inside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), r6.selected_outside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), r6.selected_not_outside (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 0)), r6.selected_overlapping (r1)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (27, 0)), r6.selected_not_overlapping (r1)); + + EXPECT_EQ (r2.selected_interacting (r1).is_merged (), true); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au14a.gds"); + } + + db::Edges r1e = r1.edges (); + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r6); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1e); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r6.selected_interacting (r1e)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r6.selected_not_interacting (r1e)); + + EXPECT_EQ (r6.selected_interacting (r1e).is_merged (), true); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au14b.gds"); + } +} + +TEST(15_Filtered) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + db::RegionAreaFilter af1 (0, 1000000000, false); + db::Region af1_filtered = r1.filtered (af1); + db::RegionAreaFilter af1inv (0, 1000000000, true); + db::Region af1_else = r1.filtered (af1inv); + EXPECT_EQ (af1_filtered.is_merged (), true); + EXPECT_EQ (af1_else.is_merged (), true); + + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), af1_filtered); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), af1_else); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au15a.gds"); + } + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::RegionBBoxFilter bwf (0, 50000, false, db::RegionBBoxFilter::BoxWidth); + db::RegionBBoxFilter bhf (0, 50000, false, db::RegionBBoxFilter::BoxHeight); + db::Region r2_bwf = r2.filtered (bwf); + db::Region r2_bhf = r2.filtered (bhf); + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2_bwf); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2_bhf); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au15b.gds"); + } +} + +TEST(16_MergeWithMinWC) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + db::Region r1_merged_wc0 = r1.merged (true, 0); + db::Region r1_merged_wc1 = r1.merged (true, 1); + db::Region r1_merged_wc2 = r1.merged (true, 2); + EXPECT_EQ (r1_merged_wc0.is_merged (), true); + EXPECT_EQ (r1_merged_wc1.is_merged (), true); + EXPECT_EQ (r1_merged_wc2.is_merged (), true); + + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1_merged_wc0); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1_merged_wc1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r1_merged_wc2); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au16.gds"); + } +} + +TEST(17_SinglePolygonChecks) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + unsigned int l4 = ly.get_layer (db::LayerProperties (4, 0)); + + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6), dss); + db::Region r4 (db::RecursiveShapeIterator (ly, top_cell, l4), dss); + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (4, 0)), r4); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (6, 0)), r6); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r3.width_check (260, false, db::Euclidian, 90, 0)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r3.width_check (260, true, db::Projection, 90, 2000)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r6.notch_check (1300, false, db::Euclidian, 90, 0)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au17.gds"); + } +} + +TEST(18_MultiPolygonChecks) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + unsigned int l4 = ly.get_layer (db::LayerProperties (4, 0)); + + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6), dss); + db::Region r4 (db::RecursiveShapeIterator (ly, top_cell, l4), dss); + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (4, 0)), r4); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (6, 0)), r6); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r3.space_check (500, false, db::Projection, 90, 0)); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r3.space_check (500, true, db::Projection, 90, 300)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r3.separation_check (r4, 200, false, db::Projection, 90, 0)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), r6.enclosing_check (r4, 100, true, db::Projection, 90, 0)); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au18.gds"); + } +} + +TEST(19_GridCheck) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r3_gc1; + r3.grid_check (25, 25).polygons (r3_gc1, 100); + db::Region r3_gc2; + r3.grid_check (40, 40).polygons (r3_gc2, 100); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r3_gc1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r3_gc2); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au19.gds"); +} + +TEST(20_AngleCheck) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/angle_check_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + db::EdgePairs ep1_ac1 = r1.angle_check (0, 91, true); + db::EdgePairs ep1_ac2 = r1.angle_check (0, 45, false); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), ep1_ac1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), ep1_ac2); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au20.gds"); +} + +TEST(21_Processors) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.processed (db::CornersAsDots (-180.0, 180.0))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.processed (db::CornersAsDots (0.0, 180.0))); + db::Region ext; + r1.processed (db::CornersAsDots (0.0, 180.0)).extended (ext, 1000, 1000, 2000, 2000); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), ext); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r1.processed (db::CornersAsRectangles (-180.0, 180.0, 2000))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r1.processed (db::CornersAsRectangles (0.0, 180.0, 2000))); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r1.processed (db::Extents (0, 0))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.processed (db::Extents (1000, 2000))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r1.processed (db::RelativeExtents (0, 0, 1.0, 1.0, 0, 0))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), r1.processed (db::RelativeExtents (0.25, 0.4, 0.75, 0.6, 1000, 2000))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), r1.processed (db::RelativeExtentsAsEdges (0, 0, 1.0, 1.0))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), r1.processed (db::RelativeExtentsAsEdges (0.5, 0.5, 0.5, 0.5))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 0)), r1.processed (db::RelativeExtentsAsEdges (0.25, 0.4, 0.75, 0.6))); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (30, 0)), r1.processed (db::minkowsky_sum_computation (db::Box (-1000, -2000, 3000, 4000)))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (31, 0)), r1.processed (db::minkowsky_sum_computation (db::Edge (-1000, 0, 3000, 0)))); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (40, 0)), r1.processed (db::TrapezoidDecomposition (db::TD_htrapezoids))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (41, 0)), r1.processed (db::ConvexDecomposition (db::PO_vertical))); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (42, 0)), r1.processed (db::ConvexDecomposition (db::PO_horizontal))); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au21.gds"); +} + +TEST(22_TwoLayoutsWithDifferentDBU) +{ + db::Layout ly1; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly1); + } + + db::cell_index_type top_cell_index1 = *ly1.begin_top_down (); + db::Cell &top_cell1 = ly1.cell (top_cell_index1); + + db::Layout ly2; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_area_peri_l1_dbu2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly2); + } + + db::cell_index_type top_cell_index2 = *ly2.begin_top_down (); + db::Cell &top_cell2 = ly2.cell (top_cell_index2); + + db::DeepShapeStore dss; + + unsigned int l11 = ly1.get_layer (db::LayerProperties (1, 0)); + db::Region r11 (db::RecursiveShapeIterator (ly1, top_cell1, l11), dss); + + unsigned int l12 = ly2.get_layer (db::LayerProperties (2, 0)); + db::Region r12 (db::RecursiveShapeIterator (ly2, top_cell2, l12), dss, db::ICplxTrans (ly2.dbu () / ly1.dbu ())); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly1.cell_name (top_cell_index1)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r11.sized (1000) ^ r12); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au22.gds"); +} + +TEST(23_Texts) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 1)); + unsigned int l8 = ly.get_layer (db::LayerProperties (8, 1)); + + db::Region r6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100, dss)); + db::Region r6dots; + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true, dss)).extended (r6dots, 20, 20, 20, 20); + db::Region r8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100, dss)); + db::Region r8dots; + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true, dss)).extended (r8dots, 20, 20, 20, 20); + + db::Region rf6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100)); + db::Region rf6dots; + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true)).extended (rf6dots, 20, 20, 20, 20); + db::Region rf8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100)); + db::Region rf8dots; + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true)).extended (rf8dots, 20, 20, 20, 20); + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r6boxes); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r6dots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), rf6boxes); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), rf6dots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r8boxes); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r8dots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), rf8boxes); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), rf8dots); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au23.gds"); + } +} + +TEST(24_TextsFromDeep) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_text_enlargement (1); + dss.set_text_property_name (tl::Variant ("textstring")); + + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 1)); + unsigned int l8 = ly.get_layer (db::LayerProperties (8, 1)); + + db::Region r6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_boxes ("*", true, 100, dss)); + db::Region r6dots; + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_dots ("*", true, dss)).extended (r6dots, 20, 20, 20, 20); + db::Region r8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_boxes ("VDD", false, 100, dss)); + db::Region r8dots; + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_dots ("V*", true, dss)).extended (r8dots, 20, 20, 20, 20); + + db::Region rf6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_boxes ("*", true, 100)); + db::Region rf6dots; + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_dots ("*", true)).extended (rf6dots, 20, 20, 20, 20); + db::Region rf8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_boxes ("VDD", false, 100)); + db::Region rf8dots; + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_dots ("V*", true)).extended (rf8dots, 20, 20, 20, 20); + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r6boxes); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r6dots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), rf6boxes); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), rf6dots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r8boxes); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r8dots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), rf8boxes); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), rf8dots); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au24.gds"); + } +} + +TEST(100_Integration) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/vexriscv_clocked_r.oas.gz"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_max_vertex_count (4); + dss.set_threads (0); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l4 = ly.get_layer (db::LayerProperties (4, 0)); + unsigned int l5 = ly.get_layer (db::LayerProperties (5, 0)); + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 0)); + unsigned int l7 = ly.get_layer (db::LayerProperties (7, 0)); + unsigned int l10 = ly.get_layer (db::LayerProperties (10, 0)); + unsigned int l11 = ly.get_layer (db::LayerProperties (11, 0)); + unsigned int l14 = ly.get_layer (db::LayerProperties (14, 0)); + unsigned int l16 = ly.get_layer (db::LayerProperties (16, 0)); + unsigned int l18 = ly.get_layer (db::LayerProperties (18, 0)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r4 (db::RecursiveShapeIterator (ly, top_cell, l4), dss); + db::Region r5 (db::RecursiveShapeIterator (ly, top_cell, l5), dss); + db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6), dss); + db::Region r7 (db::RecursiveShapeIterator (ly, top_cell, l7), dss); + db::Region r10 (db::RecursiveShapeIterator (ly, top_cell, l10), dss); + db::Region r11 (db::RecursiveShapeIterator (ly, top_cell, l11), dss); + db::Region r14 (db::RecursiveShapeIterator (ly, top_cell, l14), dss); + db::Region r16 (db::RecursiveShapeIterator (ly, top_cell, l16), dss); + db::Region r18 (db::RecursiveShapeIterator (ly, top_cell, l18), dss); + + db::Region psd = r4 - r7; + db::Region nsd = r3 - r7; + db::Region pgate = r4 & r7; + db::Region ngate = r3 & r7; + db::Region poly_cont = r10 & r7; + db::Region diff_cont = r10 - r7; + + r1.merge (); + r3.merge (); + r4.merge (); + r5.merge (); + r6.merge (); + r7.merge (); + r10.merge (); + r11.merge (); + r14.merge (); + r16.merge (); + r18.merge (); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (3, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (4, 0)), r4); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (5, 0)), r5); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (6, 0)), r6); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (7, 0)), r7); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r10); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r11); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r14); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (16, 0)), r16); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (18, 0)), r18); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (100, 0)), psd); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (101, 0)), nsd); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (102, 0)), pgate); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (103, 0)), ngate); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (104, 0)), poly_cont); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (105, 0)), diff_cont); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au100.gds"); +} + +TEST(101_DeepFlatCollaboration) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r2_flat (db::RecursiveShapeIterator (ly, top_cell, l2)); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r3_flat (db::RecursiveShapeIterator (ly, top_cell, l3)); + + db::Region r2fminus3 = r2_flat - r3; + db::Region r2minus3f = r2 - r3_flat; + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2fminus3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2minus3f); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au101.gds"); +} + diff --git a/src/db/unit_tests/dbDeepShapeStoreTests.cc b/src/db/unit_tests/dbDeepShapeStoreTests.cc new file mode 100644 index 000000000..9ef242efe --- /dev/null +++ b/src/db/unit_tests/dbDeepShapeStoreTests.cc @@ -0,0 +1,210 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbDeepShapeStore.h" +#include "dbRegion.h" +#include "dbDeepRegion.h" +#include "tlUnitTest.h" +#include "tlStream.h" + +TEST(1) +{ + db::DeepShapeStore store; + db::Layout layout; + + unsigned int l1 = layout.insert_layer (); + unsigned int l2 = layout.insert_layer (); + db::cell_index_type c1 = layout.add_cell ("C1"); + db::cell_index_type c2 = layout.add_cell ("C2"); + + EXPECT_EQ (store.layouts (), (unsigned int) 0); + + db::DeepLayer dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); + db::DeepLayer dl2 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l2)); + + EXPECT_EQ (dl1.layer (), l1); + EXPECT_EQ (dl2.layer (), l2); + EXPECT_EQ (&dl1.layout (), &dl2.layout ()); + EXPECT_EQ (store.layouts (), (unsigned int) 1); + + db::DeepLayer dl3 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c2), l1)); + EXPECT_EQ (dl3.layer (), l1); + EXPECT_NE (&dl1.layout (), &dl3.layout ()); + EXPECT_EQ (store.layouts (), (unsigned int) 2); + + db::DeepLayer dl4 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1, db::Box (0, 1, 2, 3))); + db::DeepLayer dl5 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l2, db::Box (0, 1, 2, 3))); + EXPECT_EQ (dl4.layer (), l1); + EXPECT_EQ (dl5.layer (), l1); // not l2, because it's a new layout + EXPECT_EQ (store.layouts (), (unsigned int) 4); + + db::DeepLayer dl6 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1, db::Box (0, 1, 2, 3))); + EXPECT_EQ (dl6.layer (), l2); // a new layer (a copy) + EXPECT_EQ (&dl6.layout (), &dl4.layout ()); + EXPECT_EQ (store.layouts (), (unsigned int) 4); +} + +static size_t shapes_in_top (const db::Layout &layout, unsigned int layer) +{ + const db::Cell &top = layout.cell (*layout.begin_top_down ()); + return top.shapes (layer).size (); +} + +TEST(2_RefCounting) +{ + db::DeepShapeStore store; + db::Layout layout; + + unsigned int l1 = layout.insert_layer (); + unsigned int l2 = layout.insert_layer (); + db::cell_index_type c1 = layout.add_cell ("C1"); + db::cell_index_type c2 = layout.add_cell ("C2"); + layout.cell (c1).shapes (l1).insert (db::Box (0, 1, 2, 3)); + layout.cell (c1).shapes (l2).insert (db::Box (0, 1, 2, 3)); + + EXPECT_EQ (store.layouts (), (unsigned int) 0); + + db::DeepLayer dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); + db::DeepLayer dl2 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l2)); + db::DeepLayer dl3 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c2), l1)); + db::DeepLayer dl4 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1, db::Box (0, 1, 2, 3))); + db::DeepLayer dl5 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l2, db::Box (0, 1, 2, 3))); + db::DeepLayer dl6 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1, db::Box (0, 1, 2, 3))); + + EXPECT_EQ (store.layouts (), (unsigned int) 4); + + unsigned int lyi1 = dl1.layout_index (); + unsigned int lyi2 = dl2.layout_index (); + unsigned int lyi3 = dl3.layout_index (); + unsigned int lyi4 = dl4.layout_index (); + unsigned int lyi5 = dl5.layout_index (); + unsigned int lyi6 = dl6.layout_index (); + + EXPECT_EQ (lyi1, lyi2); + EXPECT_NE (lyi3, lyi2); + EXPECT_NE (lyi5, lyi4); + EXPECT_NE (lyi5, lyi3); + EXPECT_EQ (lyi6, lyi4); + + EXPECT_EQ (dl1.layer (), l1); + EXPECT_EQ (dl2.layer (), l2); + EXPECT_EQ (dl4.layer (), l1); + EXPECT_EQ (dl6.layer (), l2); + + // dl1 and dl2 share the same layout, but not the same layer + // dl4 and dl6 share the same layout, but not the same layer + + EXPECT_EQ (store.is_valid_layout_index (lyi6), true); + EXPECT_EQ (store.is_valid_layout_index (lyi5), true); + EXPECT_EQ (store.is_valid_layout_index (lyi3), true); + EXPECT_EQ (store.is_valid_layout_index (lyi1), true); + + EXPECT_EQ (shapes_in_top (store.const_layout (lyi6), l2), size_t (1)); + dl6 = db::DeepLayer (); + EXPECT_EQ (shapes_in_top (store.const_layout (lyi6), l2), size_t (0)); + + EXPECT_EQ (shapes_in_top (store.const_layout (lyi6), l1), size_t (1)); + db::DeepLayer dl4a = dl4; + dl4 = db::DeepLayer (); + EXPECT_EQ (shapes_in_top (store.const_layout (lyi6), l1), size_t (1)); + dl4a = db::DeepLayer (); + EXPECT_EQ (store.is_valid_layout_index (lyi6), false); + + dl3 = db::DeepLayer (); + EXPECT_EQ (store.is_valid_layout_index (lyi3), false); + + { + db::DeepLayer dl5a = dl5; + db::DeepLayer dl5b = dl5a; + dl5 = db::DeepLayer (); + EXPECT_EQ (store.is_valid_layout_index (lyi5), true); + } + EXPECT_EQ (store.is_valid_layout_index (lyi5), false); + + EXPECT_EQ (shapes_in_top (store.const_layout (lyi1), l1), size_t (1)); + EXPECT_EQ (shapes_in_top (store.const_layout (lyi1), l2), size_t (1)); + + dl1 = db::DeepLayer (); + EXPECT_EQ (shapes_in_top (store.const_layout (lyi1), l1), size_t (0)); + EXPECT_EQ (shapes_in_top (store.const_layout (lyi1), l2), size_t (1)); + + dl2 = db::DeepLayer (); + EXPECT_EQ (store.is_valid_layout_index (lyi1), false); +} + +TEST(3_TextTreatment) +{ + db::DeepShapeStore store; + db::Layout layout; + + unsigned int l1 = layout.insert_layer (); + db::cell_index_type c1 = layout.add_cell ("C1"); + layout.cell (c1).shapes (l1).insert (db::Text ("TEXT", db::Trans (db::Vector (1000, 2000)))); + + db::DeepLayer dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); + EXPECT_EQ (store.layouts (), (unsigned int) 1); + + EXPECT_EQ (dl1.initial_cell ().shapes (dl1.layer ()).empty (), true); + + store.set_text_enlargement (1); + dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); + EXPECT_EQ (store.layouts (), (unsigned int) 1); + + EXPECT_EQ (dl1.initial_cell ().shapes (dl1.layer ()).size (), size_t (1)); + EXPECT_EQ (dl1.initial_cell ().shapes (dl1.layer ()).begin (db::ShapeIterator::All)->to_string (), "polygon (999,1999;999,2001;1001,2001;1001,1999)"); + + store.set_text_property_name (tl::Variant ("text")); + dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); + EXPECT_EQ (store.layouts (), (unsigned int) 1); + + EXPECT_EQ (dl1.initial_cell ().shapes (dl1.layer ()).size (), size_t (1)); + EXPECT_EQ (dl1.initial_cell ().shapes (dl1.layer ()).begin (db::ShapeIterator::All)->to_string (), "polygon (999,1999;999,2001;1001,2001;1001,1999) prop_id=1"); + + const db::Layout *dss_layout = &store.const_layout (0); + db::PropertiesRepository::properties_set ps = dss_layout->properties_repository ().properties (1); + EXPECT_EQ (ps.size (), size_t (1)); + EXPECT_EQ (dss_layout->properties_repository ().prop_name (ps.begin ()->first).to_string (), "text"); + EXPECT_EQ (ps.begin ()->second.to_string (), "TEXT"); +} + +TEST(4_FlatAndEmptyInput) +{ + db::DeepShapeStore dss ("TOP", 0.01); + EXPECT_EQ (dss.layout ().dbu (), 0.01); + + db::Region r1; + r1.insert (db::Box (0, 0, 1000, 1000)); + + db::Region r2; + r2.insert (db::Box (100, 100, 900, 900)); + + db::Region r3; + + db::Region dr1 (new db::DeepRegion (dss.create_from_flat (r1, true))); + db::Region dr2 (new db::DeepRegion (dss.create_from_flat (r2, true))); + db::Region dr3 (new db::DeepRegion (dss.create_from_flat (r3, true))); + + EXPECT_EQ ((dr1 - dr2).to_string (), "(0,0;0,900;100,900;100,100;900,100;900,900;0,900;0,1000;1000,1000;1000,0)"); + EXPECT_EQ ((dr1 - dr3).to_string (), "(0,0;0,1000;1000,1000;1000,0)"); +} + diff --git a/src/db/unit_tests/dbEdge.cc b/src/db/unit_tests/dbEdge.cc index 940e7a474..319fd1f5f 100644 --- a/src/db/unit_tests/dbEdge.cc +++ b/src/db/unit_tests/dbEdge.cc @@ -29,6 +29,7 @@ TEST(1) { db::Edge e (0, 0, 100, 200); + db::Edge ee; db::Edge empty; EXPECT_EQ (empty.is_degenerate (), true); @@ -53,6 +54,12 @@ TEST(1) EXPECT_EQ (e.to_string (), "(0,0;100,200)"); EXPECT_EQ (e.swapped_points ().to_string (), "(100,200;0,0)"); EXPECT_EQ (e.to_string (), "(0,0;100,200)"); + EXPECT_EQ (e.transformed (db::Trans (1)).to_string (), "(0,0;-200,100)"); + EXPECT_EQ (e.transformed (db::Trans (5)).to_string (), "(200,100;0,0)"); // mirroring transformations swap points + ee = e; + ee.transform (db::Trans (5)); + EXPECT_EQ (ee.to_string (), "(200,100;0,0)"); + EXPECT_EQ (e.swapped_points ().to_string (), "(100,200;0,0)"); e.swap_points (); EXPECT_EQ (e.to_string (), "(100,200;0,0)"); @@ -66,7 +73,6 @@ TEST(1) EXPECT_EQ (e.shifted (2).to_string (), "(0,4;10,12)"); EXPECT_EQ (e.shifted (1).to_string (), "(0,3;10,11)"); - db::Edge ee; ee = e; ee.shift (2); EXPECT_EQ (ee.to_string (), "(0,4;10,12)"); diff --git a/src/db/unit_tests/dbEdgePairs.cc b/src/db/unit_tests/dbEdgePairs.cc index b26aeab25..5204360c7 100644 --- a/src/db/unit_tests/dbEdgePairs.cc +++ b/src/db/unit_tests/dbEdgePairs.cc @@ -106,11 +106,20 @@ TEST(2) } struct EPTestFilter + : public db::EdgePairFilterBase { - bool operator() (const db::EdgePair &ep) + bool selected (const db::EdgePair &ep) const { return ep.first ().double_length () < 50; } + + const db::TransformationReducer *vars () const + { + return &m_vars; + } + +private: + db::MagnificationReducer m_vars; }; TEST(3) @@ -125,3 +134,18 @@ TEST(3) EXPECT_EQ (ep.to_string (), ""); } +TEST(4) +{ + db::EdgePairs ep; + ep.insert (db::EdgePair (db::Edge (db::Point (10, 20), db::Point (50, 50)), db::Edge (db::Point (-10, -20), db::Point (90, 80)))); + ep.insert (db::EdgePair (db::Edge (db::Point (10, 20), db::Point (110, 120)), db::Edge (db::Point (90, 80), db::Point (-10, -20)))); + + db::Layout ly; + unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0)); + db::cell_index_type top_cell = ly.add_cell ("TOP"); + + ep.insert_into_as_polygons (&ly, top_cell, l1, 1); + + db::Region r (db::RecursiveShapeIterator (ly, ly.cell (top_cell), l1)); + EXPECT_EQ (r.to_string (), "(-10,-21;9,20;50,51;91,80);(-10,-21;9,20;110,121;91,80)"); +} diff --git a/src/db/unit_tests/dbEdges.cc b/src/db/unit_tests/dbEdges.cc index 12e3daf27..23e7fd371 100644 --- a/src/db/unit_tests/dbEdges.cc +++ b/src/db/unit_tests/dbEdges.cc @@ -24,6 +24,7 @@ #include "tlUnitTest.h" #include "dbEdges.h" +#include "dbEdgesUtils.h" #include "dbPolygonTools.h" #include "dbRegion.h" @@ -53,7 +54,7 @@ TEST(1) EXPECT_EQ (r.bbox ().to_string (), "(0,0;100,200)"); EXPECT_EQ (r.transformed (db::Trans (db::Vector (1, 2))).bbox ().to_string (), "(1,2;101,202)"); EXPECT_EQ (r.empty (), false); - EXPECT_EQ (r.is_merged (), false); + EXPECT_EQ (r.is_merged (), true); EXPECT_EQ (r.begin ().at_end (), false); db::Edges r1 = r; @@ -638,7 +639,7 @@ TEST(20) EXPECT_EQ (r1.to_string (), "(120,20;120,40);(120,40;140,40);(140,40;140,20);(140,20;120,20);(160,80;160,140);(220,80;160,80)"); EXPECT_EQ (r1.has_valid_edges (), false); EXPECT_EQ (r1.length (), db::Edges::length_type (200)); - EXPECT_EQ (r1.has_valid_edges (), true); // length, since merging, will implicitly convert to valid edges + EXPECT_EQ (r1.has_valid_edges (), false); EXPECT_EQ (r1.bbox ().to_string (), "(120,20;220,140)"); EXPECT_EQ (r1.size (), size_t (6)); EXPECT_EQ (r1.empty (), false); @@ -649,7 +650,7 @@ TEST(20) EXPECT_EQ (rr.to_string (), "(120,20;120,40);(120,40;140,40);(140,40;140,20);(140,20;120,20)"); db::Edges r2 = r1; - EXPECT_EQ (r2.has_valid_edges (), true); + EXPECT_EQ (r2.has_valid_edges (), false); EXPECT_EQ (r2.length (), db::Edges::length_type (200)); EXPECT_EQ (r2.bbox ().to_string (), "(120,20;220,140)"); EXPECT_EQ (r2.size (), size_t (6)); diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc new file mode 100644 index 000000000..22575f670 --- /dev/null +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -0,0 +1,1272 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlUnitTest.h" +#include "dbHierNetworkProcessor.h" +#include "dbTestSupport.h" +#include "dbShapeRepository.h" +#include "dbPolygon.h" +#include "dbPath.h" +#include "dbText.h" +#include "dbLayout.h" +#include "dbStream.h" +#include "dbCommonReader.h" + +static std::string l2s (db::Connectivity::layer_iterator b, db::Connectivity::layer_iterator e) +{ + std::string s; + for (db::Connectivity::layer_iterator i = b; i != e; ++i) { + if (! s.empty ()) { + s += ","; + } + s += tl::to_string (*i); + } + return s; +} + +static std::string gn2s (db::Connectivity::global_nets_iterator b, db::Connectivity::global_nets_iterator e) +{ + std::string s; + for (db::Connectivity::global_nets_iterator i = b; i != e; ++i) { + if (! s.empty ()) { + s += ","; + } + s += tl::to_string (*i); + } + return s; +} + +TEST(1_Connectivity) +{ + db::Connectivity conn; + + EXPECT_EQ (l2s (conn.begin_layers (), conn.end_layers ()), ""); + + conn.connect (0); + EXPECT_EQ (l2s (conn.begin_layers (), conn.end_layers ()), "0"); + EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0"); + EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), ""); + + conn.connect (0, 1); + EXPECT_EQ (l2s (conn.begin_layers (), conn.end_layers ()), "0,1"); + EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1"); + EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0"); + + conn.connect (1); + EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0,1"); + + conn.connect (0, 2); + conn.connect (2); + + EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1,2"); + EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0,1"); + EXPECT_EQ (l2s (conn.begin_connected (2), conn.end_connected (2)), "0,2"); + + EXPECT_EQ (conn.connect_global (0, "GLOBAL"), size_t (0)); + EXPECT_EQ (gn2s (conn.begin_global_connections (2), conn.end_global_connections (2)), ""); + EXPECT_EQ (gn2s (conn.begin_global_connections (0), conn.end_global_connections (0)), "0"); + EXPECT_EQ (conn.connect_global (2, "GLOBAL2"), size_t (1)); + EXPECT_EQ (gn2s (conn.begin_global_connections (2), conn.end_global_connections (2)), "1"); + EXPECT_EQ (conn.connect_global (0, "GLOBAL2"), size_t (1)); + EXPECT_EQ (gn2s (conn.begin_global_connections (0), conn.end_global_connections (0)), "0,1"); + + EXPECT_EQ (conn.global_net_name (0), "GLOBAL"); + EXPECT_EQ (conn.global_net_name (1), "GLOBAL2"); + + db::Connectivity conn2 = conn; + + EXPECT_EQ (l2s (conn2.begin_connected (0), conn2.end_connected (0)), "0,1,2"); + EXPECT_EQ (l2s (conn2.begin_connected (1), conn2.end_connected (1)), "0,1"); + EXPECT_EQ (l2s (conn2.begin_connected (2), conn2.end_connected (2)), "0,2"); + + EXPECT_EQ (gn2s (conn2.begin_global_connections (0), conn2.end_global_connections (0)), "0,1"); + EXPECT_EQ (conn2.global_net_name (0), "GLOBAL"); + EXPECT_EQ (conn2.global_net_name (1), "GLOBAL2"); +} + +TEST(2_ShapeInteractions) +{ + db::Connectivity conn; + + conn.connect (0); + conn.connect (1); + conn.connect (0, 1); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + db::GenericRepository repo; + db::PolygonRef ref1 (poly, repo); + db::ICplxTrans t2 (db::Trans (db::Vector (0, 10))); + db::PolygonRef ref2 (poly.transformed (t2), repo); + db::ICplxTrans t3 (db::Trans (db::Vector (0, 2000))); + db::PolygonRef ref3 (poly.transformed (t3), repo); + + EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2), true); // t2*ref1 == ref2 + EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3), false); // t3*ref1 == ref3 + EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2), false); +} + +TEST(2_ShapeInteractionsRealPolygon) +{ + db::Connectivity conn; + + conn.connect (0); + conn.connect (1); + conn.connect (0, 1); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;500,1000;500,1500;1000,1500;1000,0)", poly); + db::GenericRepository repo; + db::PolygonRef ref1 (poly, repo); + db::ICplxTrans t2 (db::Trans (db::Vector (0, 10))); + db::PolygonRef ref2 (poly.transformed (t2), repo); + db::ICplxTrans t3 (db::Trans (db::Vector (0, 2000))); + db::PolygonRef ref3 (poly.transformed (t3), repo); + db::ICplxTrans t4 (db::Trans (db::Vector (0, 1500))); + db::PolygonRef ref4 (poly.transformed (t4), repo); + + EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2), true); // t2*ref1 == ref2 + EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref4, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t4), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2), false); +} + +TEST(10_LocalClusterBasic) +{ + db::GenericRepository repo; + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + + db::local_cluster cluster; + EXPECT_EQ (cluster.bbox ().to_string (), "()"); + EXPECT_EQ (cluster.id (), size_t (0)); + + cluster.add (db::PolygonRef (poly, repo), 0); + cluster.add_attr (1); + EXPECT_EQ (cluster.bbox ().to_string (), "(0,0;1000,1000)"); + + db::local_cluster cluster2; + cluster2.add (db::PolygonRef (poly, repo).transformed (db::Trans (db::Vector (10, 20))), 1); + cluster2.add_attr (2); + + cluster.join_with (cluster2); + EXPECT_EQ (cluster.bbox ().to_string (), "(0,0;1010,1020)"); + + EXPECT_EQ (cluster.begin_attr () == cluster.end_attr (), false); + db::local_cluster::attr_iterator a = cluster.begin_attr (); + EXPECT_EQ (*a++, 1u); + EXPECT_EQ (*a++, 2u); + EXPECT_EQ (a == cluster.end_attr (), true); +} + +TEST(11_LocalClusterInteractBasic) +{ + db::GenericRepository repo; + + db::Connectivity conn; + conn.connect (0); + conn.connect (1); + conn.connect (2); + conn.connect (0, 1); + conn.connect (0, 2); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + + db::local_cluster cluster; + db::local_cluster cluster2; + + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + + cluster.add (db::PolygonRef (poly, repo), 0); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + + cluster2.add (db::PolygonRef (poly, repo), 0); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (10, 20))), conn), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1000))), conn), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1001))), conn), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 2000))), conn), false); + + cluster.clear (); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); +} + +TEST(11_LocalClusterInteractDifferentLayers) +{ + db::GenericRepository repo; + + db::Connectivity conn; + conn.connect (0); + conn.connect (1); + conn.connect (2); + conn.connect (0, 1); + conn.connect (0, 2); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + + db::local_cluster cluster; + db::local_cluster cluster2; + + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + + cluster.add (db::PolygonRef (poly, repo), 0); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + + cluster2.add (db::PolygonRef (poly, repo), 1); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (10, 20))), conn), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1000))), conn), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1001))), conn), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 2000))), conn), false); + + cluster.clear (); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + cluster.add (db::PolygonRef (poly, repo), 2); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); // not connected + + cluster.clear (); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + cluster.add (db::PolygonRef (poly, repo), 1); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), true); +} + +static std::string obj2string (const db::PolygonRef &ref) +{ + return ref.obj ().transformed (ref.trans ()).to_string (); +} + +static std::string obj2string (const db::Edge &ref) +{ + return ref.to_string (); +} + +template +static std::string local_cluster_to_string (const db::local_cluster &cluster, const db::Connectivity &conn) +{ + std::string res; + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + for (typename db::local_cluster::shape_iterator s = cluster.begin (*l); ! s.at_end (); ++s) { + if (! res.empty ()) { + res += ";"; + } + res += "[" + tl::to_string (*l) + "]" + obj2string (*s); + } + } + for (typename db::local_cluster::attr_iterator a = cluster.begin_attr (); a != cluster.end_attr (); ++a) { + res += "%" + tl::to_string (*a); + } + for (typename db::local_cluster::global_nets_iterator g = cluster.begin_global_nets (); g != cluster.end_global_nets (); ++g) { + res += "+" + conn.global_net_name (*g); + } + return res; +} + +template +static std::string local_clusters_to_string (const db::local_clusters &clusters, const db::Connectivity &conn) +{ + std::string s; + for (typename db::local_clusters::const_iterator c = clusters.begin (); c != clusters.end (); ++c) { + if (! s.empty ()) { + s += "\n"; + } + s += "#" + tl::to_string (c->id ()) + ":" + local_cluster_to_string (*c, conn); + } + return s; +} + +TEST(12_LocalClusterSplitByAreaRatio) +{ + db::GenericRepository repo; + db::Connectivity conn; + conn.connect (0); + conn.connect (1); + conn.connect (2); + + db::local_cluster cluster (17); + cluster.add (db::PolygonRef (db::Polygon (db::Box (0, 0, 20, 20)), repo), 0); + cluster.add (db::PolygonRef (db::Polygon (db::Box (0, 0, 1000, 20)), repo), 0); + cluster.add (db::PolygonRef (db::Polygon (db::Box (1000, 0, 1020, 1000)), repo), 1); + cluster.add (db::PolygonRef (db::Polygon (db::Box (0, 1000, 1000, 1020)), repo), 2); + + std::list > out; + std::back_insert_iterator > > iout = std::back_inserter (out); + size_t n = cluster.split (10.0, iout); + + EXPECT_EQ (n, size_t (3)); + EXPECT_EQ (out.size (), size_t (3)); + + std::list >::const_iterator i = out.begin (); + EXPECT_EQ (local_cluster_to_string (*i, conn), "[0](0,0;0,20;20,20;20,0);[0](0,0;0,20;1000,20;1000,0)"); + EXPECT_EQ (i->id (), size_t (17)); + ++i; + EXPECT_EQ (local_cluster_to_string (*i, conn), "[1](1000,0;1000,1000;1020,1000;1020,0)"); + EXPECT_EQ (i->id (), size_t (17)); + ++i; + EXPECT_EQ (local_cluster_to_string (*i, conn), "[2](0,1000;0,1020;1000,1020;1000,1000)"); + EXPECT_EQ (i->id (), size_t (17)); +} + +TEST(20_LocalClustersBasic) +{ + db::Layout layout; + db::Cell &cell = layout.cell (layout.add_cell ("TOP")); + db::GenericRepository &repo = layout.shape_repository (); + + db::Connectivity conn; + conn.connect (0); + conn.connect (1); + conn.connect (2); + conn.connect (0, 1); + conn.connect (0, 2); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + + cell.shapes (0).insert (db::PolygonRef (poly, repo)); + + db::local_clusters clusters; + EXPECT_EQ (local_clusters_to_string (clusters, conn), ""); + + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), "#1:[0](0,0;0,1000;1000,1000;1000,0)"); + + // one more shape + cell.shapes (0).insert (db::PolygonRef (poly.transformed (db::Trans (db::Vector (10, 20))), repo)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)"); + + // one more shape creating a new cluster + cell.shapes (2).insert (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1100))), repo)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)\n" + "#2:[2](0,1100;0,2100;1000,2100;1000,1100)" + ); + + // one more shape connecting these + cell.shapes (2).insert (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1000))), repo)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20);[2](0,1000;0,2000;1000,2000;1000,1000);[2](0,1100;0,2100;1000,2100;1000,1100)" + ); + + // one more shape opening a new cluster + cell.shapes (1).insert (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1100))), repo)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20);[2](0,1000;0,2000;1000,2000;1000,1000);[2](0,1100;0,2100;1000,2100;1000,1100)\n" + "#2:[1](0,1100;0,2100;1000,2100;1000,1100)" + ); +} + +TEST(21_LocalClustersBasicWithAttributes) +{ + db::Layout layout; + db::Cell &cell = layout.cell (layout.add_cell ("TOP")); + db::GenericRepository &repo = layout.shape_repository (); + + db::Connectivity conn; + conn.connect (0); + conn.connect (1); + conn.connect (2); + conn.connect (0, 1); + conn.connect (0, 2); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + + cell.shapes (0).insert (db::PolygonRef (poly, repo)); + + db::local_clusters clusters; + EXPECT_EQ (local_clusters_to_string (clusters, conn), ""); + + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), "#1:[0](0,0;0,1000;1000,1000;1000,0)"); + + // one more shape + cell.shapes (0).insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (db::Trans (db::Vector (10, 20))), repo), 1)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)%1"); + + // one more shape creating a new cluster + cell.shapes (2).insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1100))), repo), 2)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)%1\n" + "#2:[2](0,1100;0,2100;1000,2100;1000,1100)%2" + ); + + // one more shape connecting these + cell.shapes (2).insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1000))), repo), 3)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20);[2](0,1000;0,2000;1000,2000;1000,1000);[2](0,1100;0,2100;1000,2100;1000,1100)%1%2%3" + ); + + // one more shape opening a new cluster + cell.shapes (1).insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1100))), repo), 4)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20);[2](0,1000;0,2000;1000,2000;1000,1000);[2](0,1100;0,2100;1000,2100;1000,1100)%1%2%3\n" + "#2:[1](0,1100;0,2100;1000,2100;1000,1100)%4" + ); +} + +TEST(22_LocalClustersWithGlobal) +{ + db::Layout layout; + db::Cell &cell = layout.cell (layout.add_cell ("TOP")); + db::GenericRepository &repo = layout.shape_repository (); + + db::Connectivity conn; + conn.connect (0); + conn.connect (1); + conn.connect (2); + conn.connect (0, 1); + conn.connect (0, 2); + + db::Polygon poly; + tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); + + cell.shapes (0).insert (db::PolygonRef (poly, repo)); + + db::local_clusters clusters; + EXPECT_EQ (local_clusters_to_string (clusters, conn), ""); + + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), "#1:[0](0,0;0,1000;1000,1000;1000,0)"); + + // one more shape + cell.shapes (0).insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (db::Trans (db::Vector (10, 20))), repo), 1)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)%1"); + + // one more shape creating a new cluster + cell.shapes (2).insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (db::Trans (db::Vector (0, 1100))), repo), 2)); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)%1\n" + "#2:[2](0,1100;0,2100;1000,2100;1000,1100)%2" + ); + + conn.connect_global (0, "GLOBAL"); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)%1+GLOBAL\n" + "#2:[2](0,1100;0,2100;1000,2100;1000,1100)%2" + ); + + conn.connect_global (2, "GLOBAL2"); + + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20)%1+GLOBAL\n" + "#2:[2](0,1100;0,2100;1000,2100;1000,1100)%2+GLOBAL2" + ); + + conn.connect_global (0, "GLOBAL2"); + + // now, GLOBAL2 will connect these clusters + clusters.clear (); + clusters.build_clusters (cell, db::ShapeIterator::Polygons, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,1000;1000,1000;1000,0);[0](10,20;10,1020;1010,1020;1010,20);[2](0,1100;0,2100;1000,2100;1000,1100)%1%2+GLOBAL+GLOBAL2" + ); +} + +TEST(23_LocalClustersWithEdges) +{ + db::Layout layout; + db::Cell &cell = layout.cell (layout.add_cell ("TOP")); + + db::Edge edge; + + tl::from_string ("(0,0;0,500)", edge); + cell.shapes (0).insert (edge); + + tl::from_string ("(0,500;0,1000)", edge); + cell.shapes (0).insert (edge); + + tl::from_string ("(0,1000;2000,1000)", edge); + cell.shapes (0).insert (edge); + + tl::from_string ("(2000,1000;2000,500)", edge); + cell.shapes (0).insert (edge); + + tl::from_string ("(2000,500;1000,250)", edge); + cell.shapes (0).insert (edge); + + tl::from_string ("(1500,375;0,0)", edge); + cell.shapes (0).insert (edge); + + { + // edge clusters are for intra-layer mainly + db::Connectivity conn; + conn.connect (0); + + db::local_clusters clusters; + clusters.build_clusters (cell, db::ShapeIterator::Edges, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,0;0,500);[0](0,500;0,1000)\n" + "#2:[0](2000,500;1000,250);[0](1500,375;0,0)\n" + "#3:[0](0,1000;2000,1000)\n" + "#4:[0](2000,1000;2000,500)" + ); + } + + { + // edge clusters are for intra-layer mainly + db::Connectivity conn (db::Connectivity::EdgesConnectByPoints); + conn.connect (0); + + db::local_clusters clusters; + clusters.build_clusters (cell, db::ShapeIterator::Edges, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), "#1:[0](0,0;0,500);[0](0,500;0,1000);[0](1500,375;0,0);[0](0,1000;2000,1000);[0](2000,1000;2000,500);[0](2000,500;1000,250)"); + } +} + +TEST(30_LocalConnectedClusters) +{ + db::Layout layout; + db::cell_index_type ci1 = layout.add_cell ("C1"); + db::cell_index_type ci2 = layout.add_cell ("C2"); + db::cell_index_type ci3 = layout.add_cell ("C3"); + + db::Instance i1 = layout.cell (ci1).insert (db::CellInstArray (db::CellInst (ci2), db::Trans ())); + db::Instance i2 = layout.cell (ci2).insert (db::CellInstArray (db::CellInst (ci3), db::Trans ())); + + db::connected_clusters cc; + + db::connected_clusters::connections_type x; + db::connected_clusters::connections_type::const_iterator ix; + + x = cc.connections_for_cluster (1); + EXPECT_EQ (x.size (), size_t (0)); + x = cc.connections_for_cluster (2); + EXPECT_EQ (x.size (), size_t (0)); + + // after this: + // [#1] -> i1:#1 + // -> i2:#2 + cc.add_connection (1, db::ClusterInstance (1, db::InstElement (i1))); + cc.add_connection (1, db::ClusterInstance (2, db::InstElement (i2))); + + x = cc.connections_for_cluster (1); + EXPECT_EQ (x.size (), size_t (2)); + x = cc.connections_for_cluster (2); + EXPECT_EQ (x.size (), size_t (0)); + + // after this: + // [#1] -> i1:#1 + // -> i2:#2 + // [#2] -> i2:#1 + cc.add_connection (2, db::ClusterInstance (1, db::InstElement (i2))); + x = cc.connections_for_cluster (2); + EXPECT_EQ (x.size (), size_t (1)); + + cc.join_cluster_with (1, 2); + x = cc.connections_for_cluster (1); + EXPECT_EQ (x.size (), size_t (3)); + ix = x.begin (); + EXPECT_EQ (ix->id (), size_t (1)); + EXPECT_EQ (*ix == db::ClusterInstance (ix->id (), i1.cell_index (), i1.complex_trans (), i1.prop_id ()), true); + ++ix; + EXPECT_EQ (ix->id (), size_t (2)); + EXPECT_EQ (*ix == db::ClusterInstance (ix->id (), i2.cell_index (), i2.complex_trans (), i2.prop_id ()), true); + ++ix; + EXPECT_EQ (ix->id (), size_t (1)); + EXPECT_EQ (*ix == db::ClusterInstance (ix->id (), i2.cell_index (), i2.complex_trans (), i2.prop_id ()), true); + + x = cc.connections_for_cluster (2); + EXPECT_EQ (x.size (), size_t (0)); + + // after this: + // [#1] -> i1:#1 + // -> i2:#2 + // [#2] -> i2:#1 + // -> i1:#3 + cc.add_connection (2, db::ClusterInstance (3, db::InstElement (i1))); + + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (3, db::InstElement (i1))), size_t (2)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, db::InstElement (i1))), size_t (0)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, db::InstElement (i2))), size_t (1)); + + // after this: + // [#1] -> i1:#1 + // -> i2:#2 + // -> i2:#1 + // -> i1:#3 + cc.join_cluster_with (1, 2); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (3, db::InstElement (i1))), size_t (1)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (1, db::InstElement (i1))), size_t (1)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, db::InstElement (i1))), size_t (0)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, db::InstElement (i2))), size_t (1)); + + x = cc.connections_for_cluster (1); + EXPECT_EQ (x.size (), size_t (4)); + x = cc.connections_for_cluster (2); + EXPECT_EQ (x.size (), size_t (0)); +} + +static db::PolygonRef make_box (db::Layout &ly, const db::Box &box) +{ + return db::PolygonRef (db::Polygon (box), ly.shape_repository ()); +} + +TEST(40_HierClustersBasic) +{ + db::hier_clusters hc; + + db::Layout ly; + unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0)); + + db::Cell &top = ly.cell (ly.add_cell ("TOP")); + top.shapes (l1).insert (make_box (ly, db::Box (0, 0, 1000, 1000))); + + db::Cell &c1 = ly.cell (ly.add_cell ("C1")); + c1.shapes (l1).insert (make_box (ly, db::Box (0, 0, 2000, 500))); + top.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans ())); + + db::Cell &c2 = ly.cell (ly.add_cell ("C2")); + c2.shapes (l1).insert (make_box (ly, db::Box (0, 0, 500, 2000))); + c2.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans ())); + top.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), db::Trans ())); + + db::Connectivity conn; + conn.connect (l1, l1); + + hc.build (ly, top, db::ShapeIterator::Polygons, conn); + + int n, nc; + const db::connected_clusters *cluster; + + // 1 cluster in TOP with 2 connections + n = 0; + cluster = &hc.clusters_per_cell (top.cell_index ()); + for (db::connected_clusters::const_iterator i = cluster->begin (); i != cluster->end (); ++i) { + ++n; + } + EXPECT_EQ (n, 1); + EXPECT_EQ (cluster->bbox ().to_string (), "(0,0;1000,1000)") + nc = 0; + for (db::connected_clusters::connections_iterator i = cluster->begin_connections (); i != cluster->end_connections (); ++i) { + nc += int (i->second.size ()); + } + EXPECT_EQ (nc, 2); + + // 1 cluster in C1 without connection + n = 0; + cluster = &hc.clusters_per_cell (c1.cell_index ()); + for (db::connected_clusters::const_iterator i = cluster->begin (); i != cluster->end (); ++i) { + ++n; + } + EXPECT_EQ (n, 1); + EXPECT_EQ (cluster->bbox ().to_string (), "(0,0;2000,500)") + nc = 0; + for (db::connected_clusters::connections_iterator i = cluster->begin_connections (); i != cluster->end_connections (); ++i) { + nc += int (i->second.size ()); + } + EXPECT_EQ (nc, 0); + + // 1 cluster in C2 with one connection + n = 0; + cluster = &hc.clusters_per_cell (c2.cell_index ()); + for (db::connected_clusters::const_iterator i = cluster->begin (); i != cluster->end (); ++i) { + ++n; + } + EXPECT_EQ (n, 1); + EXPECT_EQ (cluster->bbox ().to_string (), "(0,0;500,2000)") + nc = 0; + for (db::connected_clusters::connections_iterator i = cluster->begin_connections (); i != cluster->end_connections (); ++i) { + nc += int (i->second.size ()); + } + EXPECT_EQ (nc, 1); +} + +static std::string path2string (const db::Layout &ly, db::cell_index_type ci, const std::vector &path) +{ + std::string res = ly.cell_name (ci); + for (std::vector::const_iterator p = path.begin (); p != path.end (); ++p) { + res += "/"; + res += ly.cell_name (p->inst_cell_index ()); + } + return res; +} + +static std::string rcsiter2string (const db::Layout &ly, db::cell_index_type ci, db::recursive_cluster_shape_iterator si, db::cell_index_type ci2skip = std::numeric_limits::max ()) +{ + std::string res; + while (! si.at_end ()) { + if (si.cell_index () == ci2skip) { + si.skip_cell (); + continue; + } + db::Polygon poly = si->obj (); + poly.transform (si->trans ()); + poly.transform (si.trans ()); + if (! res.empty ()) { + res += ";"; + } + res += path2string (ly, ci, si.inst_path ()); + res += ":"; + res += poly.to_string (); + ++si; + } + return res; +} + +static std::string rciter2string (const db::Layout &ly, db::cell_index_type ci, db::recursive_cluster_iterator si) +{ + std::string res; + while (! si.at_end ()) { + if (! res.empty ()) { + res += ";"; + } + res += path2string (ly, ci, si.inst_path ()); + ++si; + } + return res; +} + +TEST(41_HierClustersRecursiveClusterShapeIterator) +{ + db::hier_clusters hc; + + db::Layout ly; + unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0)); + + db::Cell &top = ly.cell (ly.add_cell ("TOP")); + top.shapes (l1).insert (make_box (ly, db::Box (0, 0, 1000, 1000))); + + db::Cell &c1 = ly.cell (ly.add_cell ("C1")); + c1.shapes (l1).insert (make_box (ly, db::Box (0, 0, 2000, 500))); + top.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 10)))); + + db::Cell &c2 = ly.cell (ly.add_cell ("C2")); + c2.shapes (l1).insert (make_box (ly, db::Box (0, 0, 500, 2000))); + c2.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 20)))); + top.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), db::Trans (db::Vector (0, 30)))); + + db::Connectivity conn; + conn.connect (l1, l1); + + hc.build (ly, top, db::ShapeIterator::Polygons, conn); + + std::string res; + int n = 0; + db::connected_clusters *cluster = &hc.clusters_per_cell (top.cell_index ()); + for (db::connected_clusters::const_iterator i = cluster->begin (); i != cluster->end (); ++i) { + res = rcsiter2string (ly, top.cell_index (), db::recursive_cluster_shape_iterator (hc, l1, top.cell_index (), i->id ())); + ++n; + } + EXPECT_EQ (n, 1); + EXPECT_EQ (res, "TOP:(0,0;0,1000;1000,1000;1000,0);TOP/C1:(0,10;0,510;2000,510;2000,10);TOP/C2:(0,30;0,2030;500,2030;500,30);TOP/C2/C1:(0,50;0,550;2000,550;2000,50)"); + + res.clear (); + n = 0; + cluster = &hc.clusters_per_cell (top.cell_index ()); + for (db::connected_clusters::const_iterator i = cluster->begin (); i != cluster->end (); ++i) { + res = rcsiter2string (ly, top.cell_index (), db::recursive_cluster_shape_iterator (hc, l1, top.cell_index (), i->id ()), c1.cell_index ()); + ++n; + } + EXPECT_EQ (n, 1); + EXPECT_EQ (res, "TOP:(0,0;0,1000;1000,1000;1000,0);TOP/C2:(0,30;0,2030;500,2030;500,30)"); +} + +TEST(41_HierClustersRecursiveClusterIterator) +{ + db::hier_clusters hc; + + db::Layout ly; + unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0)); + + db::Cell &top = ly.cell (ly.add_cell ("TOP")); + top.shapes (l1).insert (make_box (ly, db::Box (0, 0, 1000, 1000))); + + db::Cell &c1 = ly.cell (ly.add_cell ("C1")); + c1.shapes (l1).insert (make_box (ly, db::Box (0, 0, 2000, 500))); + top.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 10)))); + + db::Cell &c2 = ly.cell (ly.add_cell ("C2")); + c2.shapes (l1).insert (make_box (ly, db::Box (0, 0, 500, 2000))); + c2.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 20)))); + top.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), db::Trans (db::Vector (0, 30)))); + + db::Connectivity conn; + conn.connect (l1, l1); + + hc.build (ly, top, db::ShapeIterator::Polygons, conn); + + std::string res; + int n = 0; + db::connected_clusters *cluster = &hc.clusters_per_cell (top.cell_index ()); + for (db::connected_clusters::const_iterator i = cluster->begin (); i != cluster->end (); ++i) { + res = rciter2string (ly, top.cell_index (), db::recursive_cluster_iterator (hc, top.cell_index (), i->id ())); + ++n; + } + EXPECT_EQ (n, 1); + EXPECT_EQ (res, "TOP;TOP/C1;TOP/C2;TOP/C2/C1"); +} + +static void normalize_layer (db::Layout &layout, std::vector &strings, unsigned int &layer) +{ + unsigned int new_layer = layout.insert_layer (); + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + const db::Shapes &s = c->shapes (layer); + for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::Texts | db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); !i.at_end (); ++i) { + if (! i->is_text ()) { + db::Polygon poly; + i->polygon (poly); + c->shapes (new_layer).insert (db::PolygonRef (poly, layout.shape_repository ())); + } else { + db::Polygon poly (i->bbox ()); + unsigned int attr_id = (unsigned int) strings.size () + 1; + strings.push_back (i->text_string ()); + c->shapes (new_layer).insert (db::PolygonRefWithProperties (db::PolygonRef (poly, layout.shape_repository ()), attr_id)); + } + } + } + + layer = new_layer; +} + +static void copy_cluster_shapes (const std::string *&attrs, db::Shapes &out, db::cell_index_type ci, const db::hier_clusters &hc, db::local_cluster::id_type cluster_id, const db::ICplxTrans &trans, const db::Connectivity &conn) +{ + // use property #1 to code the cell name + // use property #2 to code the attrs string for the first shape + + db::PropertiesRepository &pr = out.layout ()->properties_repository (); + + db::properties_id_type cell_pid = 0, cell_and_attr_pid = 0; + + db::property_names_id_type pn_id = pr.prop_name_id (tl::Variant (1)); + db::PropertiesRepository::properties_set pm; + pm.insert (std::make_pair (pn_id, tl::Variant (out.layout ()->cell_name (ci)))); + cell_pid = pr.properties_id (pm); + + if (attrs && ! attrs->empty ()) { + db::property_names_id_type pn2_id = pr.prop_name_id (tl::Variant (2)); + pm.insert (std::make_pair (pn2_id, tl::Variant (*attrs))); + cell_and_attr_pid = pr.properties_id (pm); + } + + const db::connected_clusters &clusters = hc.clusters_per_cell (ci); + const db::local_cluster &lc = clusters.cluster_by_id (cluster_id); + + // copy the shapes from this cell + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { + db::Polygon poly = s->obj ().transformed (trans * db::ICplxTrans (s->trans ())); + out.insert (db::PolygonWithProperties (poly, cell_and_attr_pid > 0 ? cell_and_attr_pid : cell_pid)); + cell_and_attr_pid = 0; + attrs = 0; // used + } + } + + out.layout ()->cell_name (ci); + + // copy the shapes from the child cells too + typedef db::connected_clusters::connections_type connections_type; + const connections_type &connections = clusters.connections_for_cluster (cluster_id); + for (connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { + + db::ICplxTrans t = trans * i->inst_trans (); + + db::cell_index_type cci = i->inst_cell_index (); + copy_cluster_shapes (attrs, out, cci, hc, i->id (), t, conn); + + } +} + +static void run_hc_test (tl::TestBase *_this, const std::string &file, const std::string &au_file) +{ + db::Layout ly; + unsigned int l1 = 0, l2 = 0, l3 = 0, l4 = 0, l5 = 0, l6 = 0; + + { + db::LayerProperties p; + db::LayerMap lmap; + + p.layer = 1; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l1 = ly.insert_layer ()); + ly.set_properties (l1, p); + + p.layer = 2; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l2 = ly.insert_layer ()); + ly.set_properties (l2, p); + + p.layer = 3; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l3 = ly.insert_layer ()); + ly.set_properties (l3, p); + + p.layer = 4; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l4 = ly.insert_layer ()); + ly.set_properties (l4, p); + + p.layer = 5; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l5 = ly.insert_layer ()); + ly.set_properties (l5, p); + + p.layer = 6; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l6 = ly.insert_layer ()); + ly.set_properties (l6, p); + + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/"; + fn += file; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + std::vector strings; + normalize_layer (ly, strings, l1); + normalize_layer (ly, strings, l2); + normalize_layer (ly, strings, l3); + normalize_layer (ly, strings, l4); + normalize_layer (ly, strings, l5); + normalize_layer (ly, strings, l6); + + // connect 1 to 1, 1 to 2 and 1 to 3, but *not* 2 to 3 + db::Connectivity conn; + conn.connect (l1, l1); + conn.connect (l2, l2); + conn.connect (l3, l3); + conn.connect (l1, l2); + conn.connect (l1, l3); + conn.connect (l1, l4); + conn.connect (l1, l5); + conn.connect (l1, l6); + + conn.connect_global (l4, "BULK"); + conn.connect_global (l5, "BULK2"); + conn.connect_global (l6, "BULK"); + conn.connect_global (l6, "BULK2"); + + db::hier_clusters hc; + hc.build (ly, ly.cell (*ly.begin_top_down ()), db::ShapeIterator::Polygons, conn); + + std::vector > net_layers; + + for (db::Layout::top_down_const_iterator td = ly.begin_top_down (); td != ly.end_top_down (); ++td) { + + const db::connected_clusters &clusters = hc.clusters_per_cell (*td); + for (db::connected_clusters::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { + + if (! clusters.is_root (*c)) { + continue; + } + + // collect strings + std::string attrs; + for (db::recursive_cluster_iterator rc (hc, *td, *c); ! rc.at_end (); ++rc) { + const db::local_cluster &rcc = hc.clusters_per_cell (rc.cell_index ()).cluster_by_id (rc.cluster_id ()); + for (db::local_cluster::attr_iterator a = rcc.begin_attr (); a != rcc.end_attr (); ++a) { + if (! attrs.empty ()) { + attrs += "/"; + } + attrs += std::string (ly.cell_name (rc.cell_index ())) + ":" + strings[*a - 1]; + } + } + + net_layers.push_back (std::make_pair (0, ly.insert_layer ())); + + unsigned int lout = net_layers.back ().second; + + db::Shapes &out = ly.cell (*td).shapes (lout); + const std::string *attrs_str = &attrs; + copy_cluster_shapes (attrs_str, out, *td, hc, *c, db::ICplxTrans (), conn); + + db::Polygon::area_type area = 0; + for (db::Shapes::shape_iterator s = out.begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + area += s->area (); + } + net_layers.back ().first = area; + + } + + } + + // sort layers by area so we have a consistent numbering + std::sort (net_layers.begin (), net_layers.end ()); + std::reverse (net_layers.begin (), net_layers.end ()); + + int ln = 1000; + for (std::vector >::const_iterator l = net_layers.begin (); l != net_layers.end (); ++l) { + ly.set_properties (l->second, db::LayerProperties (ln, 0)); + ++ln; + } + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/" + au_file); +} + +static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::string &file, const std::string &au_file) +{ + db::Layout ly; + unsigned int l1 = 0, l2 = 0, l3 = 0, l4 = 0, l5 = 0, l6 = 0; + + { + db::LayerProperties p; + db::LayerMap lmap; + + p.layer = 1; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l1 = ly.insert_layer ()); + ly.set_properties (l1, p); + + p.layer = 2; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l2 = ly.insert_layer ()); + ly.set_properties (l2, p); + + p.layer = 3; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l3 = ly.insert_layer ()); + ly.set_properties (l3, p); + + p.layer = 4; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l4 = ly.insert_layer ()); + ly.set_properties (l4, p); + + p.layer = 5; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l5 = ly.insert_layer ()); + ly.set_properties (l5, p); + + p.layer = 6; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l6 = ly.insert_layer ()); + ly.set_properties (l6, p); + + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/"; + fn += file; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + std::vector strings; + normalize_layer (ly, strings, l1); + normalize_layer (ly, strings, l2); + normalize_layer (ly, strings, l3); + normalize_layer (ly, strings, l4); + normalize_layer (ly, strings, l5); + normalize_layer (ly, strings, l6); + + // connect 1 to 1, 1 to 2 and 1 to 3, but *not* 2 to 3 + db::Connectivity conn; + conn.connect (l1, l1); + conn.connect (l2, l2); + conn.connect (l3, l3); + conn.connect (l1, l2); + conn.connect (l1, l3); + conn.connect (l1, l4); + conn.connect (l1, l5); + conn.connect (l1, l6); + + conn.connect_global (l4, "BULK"); + conn.connect_global (l5, "BULK2"); + conn.connect_global (l6, "BULK"); + conn.connect_global (l6, "BULK2"); + + db::hier_clusters hc; + hc.build (ly, ly.cell (*ly.begin_top_down ()), db::ShapeIterator::Polygons, conn); + + std::map lm; + lm[l1] = ly.insert_layer (db::LayerProperties (101, 0)); + lm[l2] = ly.insert_layer (db::LayerProperties (102, 0)); + lm[l3] = ly.insert_layer (db::LayerProperties (103, 0)); + lm[l4] = ly.insert_layer (db::LayerProperties (104, 0)); + lm[l5] = ly.insert_layer (db::LayerProperties (105, 0)); + lm[l6] = ly.insert_layer (db::LayerProperties (106, 0)); + hc.return_to_hierarchy (ly, lm); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/" + au_file); +} + +TEST(101_HierClusters) +{ + run_hc_test (_this, "hc_test_l1.gds", "hc_test_au1.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l1.gds", "hc_test_au1b.gds"); +} + +TEST(102_HierClusters) +{ + run_hc_test (_this, "hc_test_l2.gds", "hc_test_au2.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l2.gds", "hc_test_au2b.gds"); +} + +TEST(103_HierClusters) +{ + run_hc_test (_this, "hc_test_l3.gds", "hc_test_au3.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l3.gds", "hc_test_au3b.gds"); +} + +TEST(104_HierClusters) +{ + run_hc_test (_this, "hc_test_l4.gds", "hc_test_au4.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l4.gds", "hc_test_au4b.gds"); +} + +TEST(105_HierClusters) +{ + run_hc_test (_this, "hc_test_l5.gds", "hc_test_au5.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l5.gds", "hc_test_au5b.gds"); +} + +TEST(106_HierClusters) +{ + run_hc_test (_this, "hc_test_l6.gds", "hc_test_au6.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l6.gds", "hc_test_au6b.gds"); +} + +TEST(107_HierClusters) +{ + run_hc_test (_this, "hc_test_l7.gds", "hc_test_au7.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l7.gds", "hc_test_au7b.gds"); +} + +TEST(108_HierClusters) +{ + run_hc_test (_this, "hc_test_l8.gds", "hc_test_au8.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l8.gds", "hc_test_au8b.gds"); +} + +TEST(109_HierClusters) +{ + run_hc_test (_this, "hc_test_l9.gds", "hc_test_au9.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l9.gds", "hc_test_au9b.gds"); +} + +TEST(110_HierClusters) +{ + run_hc_test (_this, "hc_test_l10.gds", "hc_test_au10.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l10.gds", "hc_test_au10b.gds"); +} + +TEST(111_HierClusters) +{ + run_hc_test (_this, "hc_test_l11.gds", "hc_test_au11.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l11.gds", "hc_test_au11b.gds"); +} + +TEST(112_HierClusters) +{ + run_hc_test (_this, "hc_test_l12.gds", "hc_test_au12.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l12.gds", "hc_test_au12b.gds"); +} + +TEST(113_HierClusters) +{ + run_hc_test (_this, "hc_test_l13.gds", "hc_test_au13.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l13.gds", "hc_test_au13b.gds"); +} + +TEST(114_HierClusters) +{ + run_hc_test (_this, "hc_test_l14.gds", "hc_test_au14.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l14.gds", "hc_test_au14b.gds"); +} + +TEST(115_HierClusters) +{ + run_hc_test (_this, "hc_test_l15.gds", "hc_test_au15.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l15.gds", "hc_test_au15b.gds"); +} + +TEST(116_HierClusters) +{ + run_hc_test (_this, "hc_test_l16.gds", "hc_test_au16.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l16.gds", "hc_test_au16b.gds"); +} + +TEST(117_HierClusters) +{ + run_hc_test (_this, "hc_test_l17.gds", "hc_test_au17.gds"); + run_hc_test_with_backannotation (_this, "hc_test_l17.gds", "hc_test_au17b.gds"); +} + diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc new file mode 100644 index 000000000..e5b2724c8 --- /dev/null +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -0,0 +1,1097 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlUnitTest.h" +#include "tlStream.h" +#include "dbHierProcessor.h" +#include "dbTestSupport.h" +#include "dbReader.h" +#include "dbCommonReader.h" + +static std::string testdata (const std::string &fn) +{ + return tl::testsrc () + "/testdata/algo/" + fn; +} + +enum TestMode +{ + TMAnd = 0, + TMNot = 1, + TMAndSwapped = 2, + TMNotSwapped = 3, + TMSelfOverlap = 4 +}; + +/** + * @brief A new processor class which and/nots with a sized version of the intruder + */ +class BoolAndOrNotWithSizedLocalOperation + : public db::BoolAndOrNotLocalOperation +{ +public: + BoolAndOrNotWithSizedLocalOperation (bool is_and, db::Coord dist) + : BoolAndOrNotLocalOperation (is_and), m_dist (dist) + { + // .. nothing yet .. + } + + virtual void compute_local (db::Layout *layout, const db::shape_interactions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const + { + db::shape_interactions sized_interactions = interactions; + for (db::shape_interactions::iterator i = sized_interactions.begin (); i != sized_interactions.end (); ++i) { + for (db::shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + const db::PolygonRef &ref = interactions.intruder_shape (*j); + db::Polygon poly = ref.obj ().transformed (ref.trans ()); + poly.size (m_dist, m_dist); + sized_interactions.add_intruder_shape (*j, db::PolygonRef (poly, layout->shape_repository ())); + } + } + BoolAndOrNotLocalOperation::compute_local (layout, sized_interactions, result, max_vertex_count, area_ratio); + } + + db::Coord dist () const + { + return m_dist; + } + +private: + db::Coord m_dist; +}; + +/** + * @brief A new processor class which and/nots with a sized version of the intruder + */ +class SelfOverlapWithSizedLocalOperation + : public db::SelfOverlapMergeLocalOperation +{ +public: + SelfOverlapWithSizedLocalOperation (unsigned int wc, db::Coord dist) + : SelfOverlapMergeLocalOperation (wc), m_dist (dist) + { + // .. nothing yet .. + } + + virtual void compute_local (db::Layout *layout, const db::shape_interactions &interactions, std::unordered_set &result, size_t max_vertex_count, double area_ratio) const + { + db::shape_interactions sized_interactions = interactions; + for (db::shape_interactions::iterator i = sized_interactions.begin (); i != sized_interactions.end (); ++i) { + + const db::PolygonRef &ref = interactions.subject_shape (i->first); + db::Polygon poly = ref.obj ().transformed (ref.trans ()); + poly.size (m_dist / 2, m_dist / 2); + sized_interactions.add_subject_shape (i->first, db::PolygonRef (poly, layout->shape_repository ())); + + for (db::shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + const db::PolygonRef &ref = interactions.intruder_shape (*j); + db::Polygon poly = ref.obj ().transformed (ref.trans ()); + poly.size (m_dist / 2, m_dist / 2); + sized_interactions.add_intruder_shape (*j, db::PolygonRef (poly, layout->shape_repository ())); + } + + } + + SelfOverlapMergeLocalOperation::compute_local (layout, sized_interactions, result, max_vertex_count, area_ratio); + } + + db::Coord dist () const + { + return m_dist; + } + +private: + db::Coord m_dist; +}; + +/** + * @brief Turns a layer into polygons and polygon references + * The hierarchical processor needs polygon references and can't work on polygons directly. + */ +static void normalize_layer (db::Layout &layout, unsigned int layer) +{ + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + db::Shapes s (layout.is_editable ()); + s.swap (c->shapes (layer)); + for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); !i.at_end (); ++i) { + db::Polygon poly; + i->polygon (poly); + c->shapes (layer).insert (db::PolygonRef (poly, layout.shape_repository ())); + } + } +} + + +static std::string contexts_to_s (db::Layout *layout, db::local_processor_contexts &contexts) +{ + std::string res; + + for (db::Layout::top_down_const_iterator i = layout->begin_top_down (); i != layout->end_top_down(); ++i) { + db::local_processor_contexts::iterator cc = contexts.context_map ().find (&layout->cell (*i)); + if (cc != contexts.context_map ().end ()) { + int index = 1; + for (db::local_processor_cell_contexts::iterator j = cc->second.begin (); j != cc->second.end (); ++j) { + res += tl::sprintf ("%s[%d] %d insts, %d shapes (%d times)\n", layout->cell_name (*i), index, int (j->first.first.size ()), int (j->first.second.size ()), int (j->second.size ())); + index += 1; + } + } + } + + return res; +} + +static void run_test_bool_gen (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc, bool single, db::Coord dist, unsigned int nthreads = 0) +{ + db::Layout layout_org; + + unsigned int l1 = 0, l2 = 0, lout = 0; + db::LayerMap lmap; + bool swap = (mode == TMAndSwapped || mode == TMNotSwapped); + + { + tl::InputStream stream (testdata (file)); + db::Reader reader (stream); + + db::LayerProperties p; + + p.layer = swap ? 2 : 1; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l1 = layout_org.insert_layer ()); + layout_org.set_properties (l1, p); + + p.layer = swap ? 1 : 2; + p.datatype = 0; + if (mode == TMSelfOverlap) { + lmap.map (db::LDPair (p.layer, p.datatype), l2 = l1); + } else { + lmap.map (db::LDPair (p.layer, p.datatype), l2 = layout_org.insert_layer ()); + layout_org.set_properties (l2, p); + } + + p.layer = out_layer_num; + p.datatype = 0; + lmap.map (db::LDPair (out_layer_num, 0), lout = layout_org.insert_layer ()); + layout_org.set_properties (lout, p); + + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + reader.read (layout_org, options); + } + + layout_org.clear_layer (lout); + normalize_layer (layout_org, l1); + if (l1 != l2) { + normalize_layer (layout_org, l2); + } + + db::local_operation *lop = 0; + db::BoolAndOrNotLocalOperation bool_op (mode == TMAnd || mode == TMAndSwapped); + db::SelfOverlapMergeLocalOperation self_intersect_op (2); + BoolAndOrNotWithSizedLocalOperation sized_bool_op (mode == TMAnd || mode == TMAndSwapped, dist); + SelfOverlapWithSizedLocalOperation sized_self_intersect_op (2, dist); + if (mode == TMSelfOverlap) { + if (dist > 0) { + lop = &sized_self_intersect_op; + } else { + lop = &self_intersect_op; + } + } else { + if (dist > 0) { + lop = &sized_bool_op; + } else { + lop = &bool_op; + } + } + + if (single) { + + db::local_processor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ())); + proc.set_threads (nthreads); + proc.set_area_ratio (3.0); + proc.set_max_vertex_count (16); + + if (! context_doc) { + proc.run (lop, l1, l2, lout); + } else { + db::local_processor_contexts contexts; + proc.compute_contexts (contexts, lop, l1, l2); + *context_doc = contexts_to_s (&layout_org, contexts); + proc.compute_results (contexts, lop, lout); + } + + } else { + + db::Layout layout_org2 = layout_org; + + db::local_processor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ()), &layout_org2, &layout_org2.cell (*layout_org2.begin_top_down ())); + proc.set_threads (nthreads); + proc.set_area_ratio (3.0); + proc.set_max_vertex_count (16); + + if (! context_doc) { + proc.run (lop, l1, l2, lout); + } else { + db::local_processor_contexts contexts; + proc.compute_contexts (contexts, lop, l1, l2); + *context_doc = contexts_to_s (&layout_org, contexts); + proc.compute_results (contexts, lop, lout); + } + + } + + db::compare_layouts (_this, layout_org, testdata (file), lmap, false /*skip other layers*/, db::AsPolygons); +} + +static void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) +{ + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, true, 0, nthreads); +} + +static void run_test_bool2 (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) +{ + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, false, 0, nthreads); +} + +static void run_test_bool_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) +{ + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, true, dist, nthreads); +} + +static void run_test_bool2_with_size (tl::TestBase *_this, const char *file, TestMode mode, db::Coord dist, int out_layer_num, std::string *context_doc = 0, unsigned int nthreads = 0) +{ + run_test_bool_gen (_this, file, mode, out_layer_num, context_doc, false, dist, nthreads); +} + +TEST(BasicAnd1) +{ + // Simple flat AND + run_test_bool (_this, "hlp1.oas", TMAnd, 100); +} + +TEST(BasicAnd1SingleThread) +{ + // Simple flat AND + run_test_bool (_this, "hlp1.oas", TMAnd, 100, 0, 1); +} + +TEST(BasicAnd1FourThreads) +{ + // Simple flat AND + run_test_bool (_this, "hlp1.oas", TMAnd, 100, 0, 4); +} + +TEST(BasicNot1) +{ + // Simple flat AND + run_test_bool (_this, "hlp1.oas", TMNot, 101); +} + +TEST(BasicNot1SingleThread) +{ + // Simple flat NOT + run_test_bool (_this, "hlp1.oas", TMNot, 101, 0, 1); +} + +TEST(BasicNot1FourThreads) +{ + // Simple flat NOT + run_test_bool (_this, "hlp1.oas", TMNot, 101, 0, 4); +} + +TEST(BasicAnd2) +{ + // Up/down and down/up interactions, AND + run_test_bool (_this, "hlp2.oas", TMAnd, 100); +} + +TEST(BasicAnd2SingleThread) +{ + // Up/down and down/up interactions, AND + run_test_bool (_this, "hlp2.oas", TMAnd, 100, 0, 1); +} + +TEST(BasicAnd2FourThreads) +{ + // Up/down and down/up interactions, AND + run_test_bool (_this, "hlp2.oas", TMAnd, 100, 0, 4); +} + +TEST(BasicNot2) +{ + // Up/down and down/up interactions, NOT + run_test_bool (_this, "hlp2.oas", TMNot, 101); +} + +TEST(BasicNot2SingleThread) +{ + // Up/down and down/up interactions, NOT + run_test_bool (_this, "hlp2.oas", TMNot, 101, 0, 1); +} + +TEST(BasicNot2FourThreads) +{ + // Up/down and down/up interactions, NOT + run_test_bool (_this, "hlp2.oas", TMNot, 101, 0, 4); +} + +TEST(BasicAnd3) +{ + // Variant building, AND + run_test_bool (_this, "hlp3.oas", TMAnd, 100); +} + +TEST(BasicAnd3SingleThread) +{ + // Variant building, AND + run_test_bool (_this, "hlp3.oas", TMAnd, 100, 0, 1); +} + +TEST(BasicAnd3FourThreads) +{ + // Variant building, AND + run_test_bool (_this, "hlp3.oas", TMAnd, 100, 0, 4); +} + +TEST(BasicNot3) +{ + // Variant building, NOT + run_test_bool (_this, "hlp3.oas", TMNot, 101); +} + +TEST(BasicNot3SingleThread) +{ + // Variant building, NOT + run_test_bool (_this, "hlp3.oas", TMNot, 101, 0, 1); +} + +TEST(BasicNot3FourThreads) +{ + // Variant building, NOT + run_test_bool (_this, "hlp3.oas", TMNot, 101, 0, 4); +} + +TEST(BasicAnd4) +{ + // Sibling interactions, variant building, AND + run_test_bool (_this, "hlp4.oas", TMAnd, 100); +} + +TEST(BasicNot4) +{ + // Sibling interactions, variant building, NOT + run_test_bool (_this, "hlp4.oas", TMNot, 101); +} + +TEST(BasicAnd5) +{ + // Variant building with intermediate hierarchy, AND + run_test_bool (_this, "hlp5.oas", TMAnd, 100); +} + +TEST(BasicNot5) +{ + // Variant building with intermediate hierarchy, NOT + run_test_bool (_this, "hlp5.oas", TMNot, 101); +} + +TEST(BasicAnd6) +{ + // Extreme variants (copy, vanishing), AND + run_test_bool (_this, "hlp6.oas", TMAnd, 100); +} + +TEST(BasicNot6) +{ + // Extreme variants (copy, vanishing), NOT + run_test_bool (_this, "hlp6.oas", TMNot, 101); +} + +TEST(BasicAnd7) +{ + // Context replication - direct and indirect, AND + run_test_bool (_this, "hlp7.oas", TMAnd, 100); +} + +TEST(BasicNot7) +{ + // Context replication - direct and indirect, NOT + run_test_bool (_this, "hlp7.oas", TMNot, 101); +} + +TEST(BasicAnd8) +{ + // Mixed sibling-parent contexts, AND + run_test_bool (_this, "hlp8.oas", TMAnd, 100); +} + +TEST(BasicNot8) +{ + // Mixed sibling-parent contexts, NOT + run_test_bool (_this, "hlp8.oas", TMNot, 101); +} + +TEST(BasicAnd9) +{ + // Top-level ring structure, AND + std::string doc; + run_test_bool (_this, "hlp9.oas", TMAnd, 100, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + ); +} + +TEST(BasicNot9) +{ + // Top-level ring structure, NOT + std::string doc; + run_test_bool (_this, "hlp9.oas", TMNot, 101, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + ); +} + +TEST(BasicAnd10) +{ + // Array instances, AND + run_test_bool (_this, "hlp10.oas", TMAnd, 100); +} + +TEST(BasicNot10) +{ + // Array instances, NOT + run_test_bool (_this, "hlp10.oas", TMNot, 101); +} + +TEST(BasicAndWithSize1) +{ + // Simple flat AND + run_test_bool_with_size (_this, "hlp1.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize1) +{ + // Simple flat NOT + run_test_bool_with_size (_this, "hlp1.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize2) +{ + // Up/down and down/up interactions, AND + run_test_bool_with_size (_this, "hlp2.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize2) +{ + // Up/down and down/up interactions, NOT + run_test_bool_with_size (_this, "hlp2.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize3) +{ + // Variant building, AND + run_test_bool_with_size (_this, "hlp3.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize3) +{ + // Variant building, NOT + run_test_bool_with_size (_this, "hlp3.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize4) +{ + // Sibling interactions, variant building, AND + run_test_bool_with_size (_this, "hlp4.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize4) +{ + // Sibling interactions, variant building, NOT + run_test_bool_with_size (_this, "hlp4.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize5) +{ + // Variant building with intermediate hierarchy, AND + run_test_bool_with_size (_this, "hlp5.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize5) +{ + // Variant building with intermediate hierarchy, NOT + run_test_bool_with_size (_this, "hlp5.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize6) +{ + // Extreme variants (copy, vanishing), AND + run_test_bool_with_size (_this, "hlp6.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize6) +{ + // Extreme variants (copy, vanishing), NOT + run_test_bool_with_size (_this, "hlp6.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize7) +{ + // Context replication - direct and indirect, AND + run_test_bool_with_size (_this, "hlp7.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize7) +{ + // Context replication - direct and indirect, NOT + run_test_bool_with_size (_this, "hlp7.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize8) +{ + // Mixed sibling-parent contexts, AND + run_test_bool_with_size (_this, "hlp8.oas", TMAnd, 1500, 102); +} + +TEST(BasicNotWithSize8) +{ + // Mixed sibling-parent contexts, NOT + run_test_bool_with_size (_this, "hlp8.oas", TMNot, 1500, 103); +} + +TEST(BasicAndWithSize9) +{ + // Top-level ring structure, AND + std::string doc; + run_test_bool_with_size (_this, "hlp9.oas", TMAnd, 1500, 102, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + ); +} + +TEST(BasicNotWithSize9) +{ + // Top-level ring structure, NOT + std::string doc; + run_test_bool_with_size (_this, "hlp9.oas", TMNot, 1500, 103, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + ); +} + +TEST(BasicAndWithSize10) +{ + // Array instances, AND + run_test_bool_with_size (_this, "hlp10.oas", TMAnd, 150, 102); +} + +TEST(BasicNotWithSize10) +{ + // Array instances, NOT + run_test_bool_with_size (_this, "hlp10.oas", TMNot, 150, 103); +} + +TEST(BasicNotWithSize11) +{ + // Up/down and down/up interactions, NOT + run_test_bool_with_size (_this, "hlp11.oas", TMNot, 1500, 103); +} + +TEST(BasicNotWithSizeSwappedLayers11) +{ + // Up/down and down/up interactions, NOT + run_test_bool_with_size (_this, "hlp11.oas", TMNotSwapped, 1500, 104); +} + +TEST(TwoInputsAnd1) +{ + // Simple flat AND + run_test_bool2 (_this, "hlp1.oas", TMAnd, 100); +} + +TEST(TwoInputsNot1) +{ + // Simple flat NOT + run_test_bool2 (_this, "hlp1.oas", TMNot, 101); +} + +TEST(TwoInputsAnd2) +{ + // Up/down and down/up interactions, AND + run_test_bool2 (_this, "hlp2.oas", TMAnd, 100); +} + +TEST(TwoInputsNot2) +{ + // Up/down and down/up interactions, NOT + run_test_bool2 (_this, "hlp2.oas", TMNot, 101); +} + +TEST(TwoInputsAnd3) +{ + // Variant building, AND + run_test_bool2 (_this, "hlp3.oas", TMAnd, 100); +} + +TEST(TwoInputsNot3) +{ + // Variant building, NOT + run_test_bool2 (_this, "hlp3.oas", TMNot, 101); +} + +TEST(TwoInputsAnd4) +{ + // Sibling interactions, variant building, AND + run_test_bool2 (_this, "hlp4.oas", TMAnd, 100); +} + +TEST(TwoInputsNot4) +{ + // Sibling interactions, variant building, NOT + run_test_bool2 (_this, "hlp4.oas", TMNot, 101); +} + +TEST(TwoInputsAnd5) +{ + // Variant building with intermediate hierarchy, AND + run_test_bool2 (_this, "hlp5.oas", TMAnd, 100); +} + +TEST(TwoInputsNot5) +{ + // Variant building with intermediate hierarchy, NOT + run_test_bool2 (_this, "hlp5.oas", TMNot, 101); +} + +TEST(TwoInputsAnd6) +{ + // Extreme variants (copy, vanishing), AND + run_test_bool2 (_this, "hlp6.oas", TMAnd, 120); +} + +TEST(TwoInputsNot6) +{ + // Extreme variants (copy, vanishing), NOT + run_test_bool2 (_this, "hlp6.oas", TMNot, 121); +} + +TEST(TwoInputsAnd7) +{ + // Context replication - direct and indirect, AND + run_test_bool2 (_this, "hlp7.oas", TMAnd, 100); +} + +TEST(TwoInputsNot7) +{ + // Context replication - direct and indirect, NOT + run_test_bool2 (_this, "hlp7.oas", TMNot, 101); +} + +TEST(TwoInputsAnd8) +{ + // Mixed sibling-parent contexts, AND + run_test_bool2 (_this, "hlp8.oas", TMAnd, 100); +} + +TEST(TwoInputsNot8) +{ + // Mixed sibling-parent contexts, NOT + run_test_bool2 (_this, "hlp8.oas", TMNot, 101); +} + +TEST(TwoInputsAnd9) +{ + // Top-level ring structure, AND + std::string doc; + run_test_bool2 (_this, "hlp9.oas", TMAnd, 100, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 1 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + ); +} + +TEST(TwoInputsNot9) +{ + // Top-level ring structure, NOT + std::string doc; + run_test_bool2 (_this, "hlp9.oas", TMNot, 101, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 1 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + ); +} + +TEST(TwoInputsAnd10) +{ + // Array instances, AND + run_test_bool2 (_this, "hlp10.oas", TMAnd, 100); +} + +TEST(TwoInputsNot10) +{ + // Array instances, NOT + run_test_bool2 (_this, "hlp10.oas", TMNot, 101); +} + +TEST(TwoInputsAndWithSize1) +{ + // Simple flat AND + run_test_bool2_with_size (_this, "hlp1.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize1) +{ + // Simple flat NOT + run_test_bool2_with_size (_this, "hlp1.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize2) +{ + // Up/down and down/up interactions, AND + run_test_bool2_with_size (_this, "hlp2.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize2) +{ + // Up/down and down/up interactions, NOT + run_test_bool2_with_size (_this, "hlp2.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize3) +{ + // Variant building, AND + run_test_bool2_with_size (_this, "hlp3.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize3) +{ + // Variant building, NOT + run_test_bool2_with_size (_this, "hlp3.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize4) +{ + // Sibling interactions, variant building, AND + run_test_bool2_with_size (_this, "hlp4.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize4) +{ + // Sibling interactions, variant building, NOT + run_test_bool2_with_size (_this, "hlp4.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize5) +{ + // Variant building with intermediate hierarchy, AND + run_test_bool2_with_size (_this, "hlp5.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize5) +{ + // Variant building with intermediate hierarchy, NOT + run_test_bool2_with_size (_this, "hlp5.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize6) +{ + // Extreme variants (copy, vanishing), AND + run_test_bool2_with_size (_this, "hlp6.oas", TMAnd, 1500, 122); +} + +TEST(TwoInputsNotWithSize6) +{ + // Extreme variants (copy, vanishing), NOT + run_test_bool2_with_size (_this, "hlp6.oas", TMNot, 1500, 123); +} + +TEST(TwoInputsAndWithSize7) +{ + // Context replication - direct and indirect, AND + run_test_bool2_with_size (_this, "hlp7.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize7) +{ + // Context replication - direct and indirect, NOT + run_test_bool2_with_size (_this, "hlp7.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize8) +{ + // Mixed sibling-parent contexts, AND + run_test_bool2_with_size (_this, "hlp8.oas", TMAnd, 1500, 102); +} + +TEST(TwoInputsNotWithSize8) +{ + // Mixed sibling-parent contexts, NOT + run_test_bool2_with_size (_this, "hlp8.oas", TMNot, 1500, 103); +} + +TEST(TwoInputsAndWithSize9) +{ + // Top-level ring structure, AND + std::string doc; + run_test_bool2_with_size (_this, "hlp9.oas", TMAnd, 1500, 102, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 1 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + ); +} + +TEST(TwoInputsNotWithSize9) +{ + // Top-level ring structure, NOT + std::string doc; + run_test_bool2_with_size (_this, "hlp9.oas", TMNot, 1500, 103, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 1 insts, 0 shapes (1 times)\n" + "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + ); +} + +TEST(TwoInputsAndWithSize10) +{ + // Array instances, AND + run_test_bool2_with_size (_this, "hlp10.oas", TMAnd, 150, 102); +} + +TEST(TwoInputsNotWithSize10) +{ + // Array instances, NOT + run_test_bool2_with_size (_this, "hlp10.oas", TMNot, 150, 103); +} + +TEST(BasicSelfOverlap1) +{ + // Simple flat Self overlap + run_test_bool (_this, "hlp1.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap2) +{ + // Up/down and down/up interactions, Self overlap + run_test_bool (_this, "hlp2.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap3) +{ + // Variant building, Self overlap + run_test_bool (_this, "hlp3.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap4) +{ + // Sibling interactions, variant building, Self overlap + run_test_bool (_this, "hlp4.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap5) +{ + // Variant building with intermediate hierarchy, Self overlap + run_test_bool (_this, "hlp5.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap6) +{ + // Extreme variants (copy, vanishing), Self overlap + run_test_bool (_this, "hlp6.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap7) +{ + // Context replication - direct and indirect, Self overlap + run_test_bool (_this, "hlp7.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap8) +{ + // Mixed sibling-parent contexts, Self overlap + run_test_bool (_this, "hlp8.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlap9) +{ + // Top-level ring structure, Self overlap + std::string doc; + run_test_bool (_this, "hlp9.oas", TMSelfOverlap, 110, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 0 insts, 1 shapes (1 times)\n" + "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + ); +} + +TEST(BasicSelfOverlap10) +{ + // Array instances, Self overlap + run_test_bool (_this, "hlp10.oas", TMSelfOverlap, 110); +} + +TEST(BasicSelfOverlapWithSize1) +{ + // Simple flat Self overlap + run_test_bool_with_size (_this, "hlp1.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize2) +{ + // Up/down and down/up interactions, Self overlap + run_test_bool_with_size (_this, "hlp2.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize3) +{ + // Variant building, Self overlap + run_test_bool_with_size (_this, "hlp3.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize4) +{ + // Sibling interactions, variant building, Self overlap + run_test_bool_with_size (_this, "hlp4.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize5) +{ + // Variant building with intermediate hierarchy, Self overlap + run_test_bool_with_size (_this, "hlp5.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize6) +{ + // Extreme variants (copy, vanishing), Self overlap + run_test_bool_with_size (_this, "hlp6.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize7) +{ + // Context replication - direct and indirect, Self overlap + run_test_bool_with_size (_this, "hlp7.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize8) +{ + // Mixed sibling-parent contexts, Self overlap + run_test_bool_with_size (_this, "hlp8.oas", TMSelfOverlap, 1500, 111); +} + +TEST(BasicSelfOverlapWithSize9) +{ + // Top-level ring structure, Self overlap + std::string doc; + run_test_bool_with_size (_this, "hlp9.oas", TMSelfOverlap, 1500, 111, &doc); + EXPECT_EQ (doc, + // This means: the interaction test is strong enough, so it does not see interactions between the + // ring and the cells embedded inside the ring. So there is only one cell context. Some shapes + // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than + // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. + "TOP[1] 0 insts, 0 shapes (1 times)\n" + "RING[1] 0 insts, 1 shapes (1 times)\n" + "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + ); +} + +TEST(BasicSelfOverlapWithSize10) +{ + // Array instances, Self overlap + run_test_bool_with_size (_this, "hlp10.oas", TMSelfOverlap, 150, 111); +} + +TEST(TopWithBelow1) +{ + run_test_bool (_this, "hlp12.oas", TMNot, 100); +} + +TEST(TopWithBelow2) +{ + run_test_bool (_this, "hlp12.oas", TMNotSwapped, 101); +} + +TEST(BasicHierarchyVariantsAnd) +{ + run_test_bool (_this, "hlp13.oas", TMAnd, 100); +} + +TEST(BasicHierarchyVariantsNot) +{ + run_test_bool (_this, "hlp13.oas", TMNot, 101); +} + +TEST(BasicHierarchyVariantsAnd2) +{ + run_test_bool (_this, "hlp14.oas", TMAnd, 100); +} + +TEST(BasicHierarchyVariantsNot2) +{ + run_test_bool (_this, "hlp14.oas", TMNot, 101); +} + diff --git a/src/db/unit_tests/dbHierarchyBuilderTests.cc b/src/db/unit_tests/dbHierarchyBuilderTests.cc new file mode 100644 index 000000000..9bc86b860 --- /dev/null +++ b/src/db/unit_tests/dbHierarchyBuilderTests.cc @@ -0,0 +1,695 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbHierarchyBuilder.h" +#include "dbReader.h" +#include "dbTestSupport.h" +#include "dbRegion.h" +#include "tlUnitTest.h" +#include "tlStream.h" + +TEST(1) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::HierarchyBuilder builder (&target); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1); + + iter.push (&builder); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au1.gds"); +} + +TEST(1_WithEmptyLayer) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + + db::Layout target; + db::HierarchyBuilder builder (&target); + + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), std::set ()); + iter.push (&builder); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1); + + iter.push (&builder); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au1.gds"); +} + +TEST(2_WithoutClip) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::HierarchyBuilder builder (&target); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, -2000, 18500, 6000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2a.gds"); +} + +TEST(2_WithClip) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ClippingHierarchyBuilderShapeReceiver clip; + db::HierarchyBuilder builder (&target, db::ICplxTrans (), &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, -2000, 18500, 6000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2b.gds"); +} + +TEST(2_WithClipAndSimplification) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ReducingHierarchyBuilderShapeReceiver red(0, 1.2, 4); + db::ClippingHierarchyBuilderShapeReceiver clip(&red); + db::HierarchyBuilder builder (&target, db::ICplxTrans (), &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, -2000, 18500, 6000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2c.gds"); +} + +TEST(2_WithClipAndRefGeneration) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::PolygonReferenceHierarchyBuilderShapeReceiver ref(&target); + db::ClippingHierarchyBuilderShapeReceiver clip(&ref); + db::HierarchyBuilder builder (&target, db::ICplxTrans (), &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, -2000, 18500, 6000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2d.gds"); +} + +TEST(2_WithEmptyResult) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::PolygonReferenceHierarchyBuilderShapeReceiver ref(&target); + db::ClippingHierarchyBuilderShapeReceiver clip(&ref); + db::HierarchyBuilder builder (&target, db::ICplxTrans (), &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, 10000, 18500, 15000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2e.gds"); +} + +TEST(2_WithClipAndSimplificationAndEmptyLayer) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ReducingHierarchyBuilderShapeReceiver red(0, 1.2, 4); + db::ClippingHierarchyBuilderShapeReceiver clip(&red); + db::HierarchyBuilder builder (&target, db::ICplxTrans (), &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + db::Box clip_box (5000, -2000, 18500, 6000); + + builder.set_target_layer (target.insert_layer (db::LayerProperties (100, 0))); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), std::set (), clip_box); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, clip_box); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2f.gds"); +} + +TEST(3_ComplexRegionWithClip) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ClippingHierarchyBuilderShapeReceiver clip; + db::HierarchyBuilder builder (&target, db::ICplxTrans (), &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Region reg; + reg.insert (db::Box (5000, 13000, 18500, 20000)); + reg.insert (db::Box (11000, 20000, 18500, 36000)); + reg.merge (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, reg); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au3a.gds"); +} + +TEST(4_ComplexRegionAndLayoutWithClip) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l3.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ClippingHierarchyBuilderShapeReceiver clip; + db::HierarchyBuilder builder (&target, db::ICplxTrans (), &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Region reg; + reg.insert (db::Box (5000, 13000, 18500, 20000)); + reg.insert (db::Box (11000, 20000, 18500, 36000)); + reg.merge (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, reg); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au4a.gds"); +} + +TEST(5_CompareRecursiveShapeIterators) +{ + db::Layout ly; + db::cell_index_type ci = ly.add_cell ("TOP"); + db::cell_index_type ci1 = ly.add_cell ("TOPA"); + + db::Layout ly2; + db::cell_index_type ci2 = ly2.add_cell ("TOP"); + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0); + db::RecursiveShapeIterator iter2 (ly2, ly2.cell (ci2), 0); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci1), 0); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 1); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2), 0); + } + + { + std::vector ll1; + ll1.push_back (100); + ll1.push_back (101); + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), ll1); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 1); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2), 0); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0); + iter1.max_depth (1); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0); + iter2.max_depth (1); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2), 0); + + iter2.max_depth (2); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2), 0); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 1, db::Box (0, 1000, 2000, 3000)); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + std::vector ll1; + ll1.push_back (100); + ll1.push_back (101); + + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), ll1, db::Box (0, 1000, 2000, 3000)); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3001)); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::Region r1; + r1.insert (db::Box (0, 1000, 2000, 3000)); + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, r1); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2), 0); + } + + { + db::Region r1; + r1.insert (db::Box (0, 1000, 2000, 3000)); + r1.insert (db::Box (0, 4000, 2000, 6000)); + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, r1); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0, db::Box (0, 1000, 2000, 3000)); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } + + { + db::Region r1; + r1.insert (db::Box (0, 1000, 2000, 3000)); + r1.insert (db::Box (0, 4000, 2000, 6000)); + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, r1); + db::Region r2; + r2.insert (db::Box (0, 1000, 2000, 3000)); + r2.insert (db::Box (0, 4000, 2000, 6000)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0, r2); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2), 0); + } + + { + db::Region r1; + r1.insert (db::Box (0, 1000, 2000, 3000)); + r1.insert (db::Box (0, 4000, 2000, 6000)); + db::RecursiveShapeIterator iter1 (ly, ly.cell (ci), 0, r1); + db::Region r2; + r2.insert (db::Box (0, 1000, 2000, 3000)); + r2.insert (db::Box (0, 4000, 2000, 6001)); + db::RecursiveShapeIterator iter2 (ly, ly.cell (ci), 0, r2); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != 0, true); + EXPECT_EQ (db::compare_iterators_with_respect_to_target_hierarchy (iter1, iter2) != db::compare_iterators_with_respect_to_target_hierarchy (iter2, iter1), true); + } +} + +TEST(6_DisjunctLayersPerHierarchyBranch) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l4.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::HierarchyBuilder builder (&target); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1); + + iter.push (&builder); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au_l4.gds"); +} + +TEST(7_DetachFromOriginalLayout) +{ + // using OASIS means we create a lot of references to array + // and shape repo - we check here whether these references get + // translated or resolved in the hierarchy builder. + std::auto_ptr ly (new db::Layout (false)); + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l5.oas.gz"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (*ly); + } + + db::Layout target; + db::HierarchyBuilder builder (&target); + + for (db::Layout::layer_iterator li = ly->begin_layers (); li != ly->end_layers (); ++li) { + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly->begin_top_down (); + db::RecursiveShapeIterator iter (*ly, ly->cell (top_cell_index), li1); + + iter.push (&builder); + + } + + // make sure there is no connection to original layout + ly.reset (0); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au_l5.gds"); +} + +TEST(8a_SimpleWithTrans) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::HierarchyBuilder builder (&target, db::ICplxTrans (2.0, 45.0, false, db::Vector ())); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1); + + iter.push (&builder); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au8a.gds"); +} + +TEST(8b_ComplexRegionWithTransformation) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ClippingHierarchyBuilderShapeReceiver clip; + db::HierarchyBuilder builder (&target, db::ICplxTrans (2.0, 45.0, false, db::Vector ()), &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Region reg; + reg.insert (db::Box (5000, 13000, 18500, 20000)); + reg.insert (db::Box (11000, 20000, 18500, 36000)); + reg.merge (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, reg); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au8b.gds"); +} + diff --git a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc new file mode 100644 index 000000000..c7dc80031 --- /dev/null +++ b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc @@ -0,0 +1,190 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbLayoutToNetlist.h" +#include "dbLayoutToNetlistReader.h" +#include "dbLayoutToNetlistWriter.h" +#include "dbStream.h" +#include "dbCommonReader.h" +#include "dbNetlistDeviceExtractorClasses.h" +#include "dbTestSupport.h" + +#include "tlUnitTest.h" +#include "tlStream.h" +#include "tlFileUtils.h" + +TEST(1_ReaderBasic) +{ + db::LayoutToNetlist l2n; + + std::string in_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au.txt"); + tl::InputStream is_in (in_path); + + db::LayoutToNetlistStandardReader reader (is_in); + reader.read (&l2n); + + // verify against the input + + std::string path = tmp_file ("tmp_l2nreader_1.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, false); + writer.write (&l2n); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } + + // test build_all_nets from read l2n + + { + db::Layout ly2; + ly2.dbu (l2n.internal_layout ()->dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_by_name ("psd"); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_by_name ("nsd"); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_by_name ("poly"); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_by_name ("diff_cont"); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_by_name ("poly_cont"); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_by_name ("metal1"); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1"); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2"); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", 0, "DEVICE_"); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "l2n_reader_au.gds"); + + db::compare_layouts (_this, ly2, au); + } +} + +TEST(1b_ReaderBasicShort) +{ + db::LayoutToNetlist l2n; + + std::string in_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_s.txt"); + tl::InputStream is_in (in_path); + + db::LayoutToNetlistStandardReader reader (is_in); + reader.read (&l2n); + + // verify against the input + + std::string path = tmp_file ("tmp_l2nreader_2.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, true); + writer.write (&l2n); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_s.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } + +} + +TEST(2_ReaderWithGlobalNets) +{ + db::LayoutToNetlist l2n; + + std::string in_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_2.txt"); + tl::InputStream is_in (in_path); + + db::LayoutToNetlistStandardReader reader (is_in); + reader.read (&l2n); + + // verify against the input + + std::string path = tmp_file ("tmp_l2nreader_2.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, false); + writer.write (&l2n); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_2.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } + + // test build_all_nets from read l2n + + { + db::Layout ly2; + ly2.dbu (l2n.internal_layout ()->dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_by_name ("psd"); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_by_name ("nsd"); + lmap [ly2.insert_layer (db::LayerProperties (12, 0))] = l2n.layer_by_name ("rbulk"); + lmap [ly2.insert_layer (db::LayerProperties (13, 0))] = l2n.layer_by_name ("ptie"); + lmap [ly2.insert_layer (db::LayerProperties (14, 0))] = l2n.layer_by_name ("ntie"); + lmap [ly2.insert_layer (db::LayerProperties (1, 0)) ] = l2n.layer_by_name ("nwell"); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_by_name ("poly"); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_by_name ("diff_cont"); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_by_name ("poly_cont"); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_by_name ("metal1"); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1"); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2"); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_", "DEVICE_"); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "l2n_reader_au_2.gds"); + + db::compare_layouts (_this, ly2, au); + } +} + diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc new file mode 100644 index 000000000..730d31ae0 --- /dev/null +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -0,0 +1,2450 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbNetlistDeviceExtractorClasses.h" +#include "dbLayoutToNetlist.h" +#include "dbStream.h" +#include "dbDeepRegion.h" +#include "dbDeepShapeStore.h" +#include "dbReader.h" +#include "dbWriter.h" +#include "dbCommonReader.h" +#include "dbTestSupport.h" + +#include "tlUnitTest.h" +#include "tlString.h" +#include "tlFileUtils.h" + +#include +#include + +static std::string qnet_name (const db::Net *net) +{ + return net ? net->qname () : "(null)"; +} + +static void dump_nets_to_layout (const db::LayoutToNetlist &l2n, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) +{ + const db::Netlist &nl = *l2n.netlist (); + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + + db::Cell &cell = ly.cell (cmap.cell_mapping (c->cell_index ())); + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + + db::cell_index_type nci = std::numeric_limits::max (); + + for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { + + std::auto_ptr shapes (l2n.shapes_of_net (*n, *m->first, false)); + if (shapes->empty ()) { + continue; + } + + if (nci == std::numeric_limits::max ()) { + std::string nn = "NET_" + c->name () + "_" + n->expanded_name (); + nci = ly.add_cell (nn.c_str ()); + cell.insert (db::CellInstArray (db::CellInst (nci), db::Trans ())); + } + + shapes->insert_into (&ly, nci, m->second); + + } + + } + + } +} + +static void dump_recursive_nets_to_layout (const db::LayoutToNetlist &l2n, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) +{ + const db::Netlist &nl = *l2n.netlist (); + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + + db::Cell &cell = ly.cell (cmap.cell_mapping (c->cell_index ())); + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + + // only handle nets without outgoing pins - these are local + if (n->pin_count () > 0) { + continue; + } + + bool any = false; + for (std::map::const_iterator m = lmap.begin (); m != lmap.end () && !any; ++m) { + any = !db::recursive_cluster_shape_iterator (l2n.net_clusters (), l2n.layer_of (*m->first), c->cell_index (), n->cluster_id ()).at_end (); + } + + if (!any) { + continue; + } + + db::cell_index_type nci = std::numeric_limits::max (); + + if (nci == std::numeric_limits::max ()) { + std::string nn = "RNET_" + c->name () + "_" + n->expanded_name (); + nci = ly.add_cell (nn.c_str ()); + cell.insert (db::CellInstArray (db::CellInst (nci), db::Trans ())); + } + + for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { + l2n.shapes_of_net (*n, *m->first, true, ly.cell (nci).shapes (m->second)); + } + + } + + } +} + +static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) +{ + unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); + lmap.map (ly.get_properties (lid), lid); + return lid; +} + +TEST(0_Basic) +{ + db::LayoutToNetlist l2n; + + std::auto_ptr reg (l2n.make_layer ("l1")); + EXPECT_EQ (l2n.is_persisted (*reg), true); + EXPECT_EQ (l2n.name (*reg), "l1"); + EXPECT_EQ (l2n.layer_of (*reg), 0u); + EXPECT_EQ (l2n.internal_layout ()->is_valid_layer (0), true); + reg.reset (0); + EXPECT_EQ (l2n.internal_layout ()->is_valid_layer (0), true); + EXPECT_EQ (l2n.name (0u), "l1"); + + EXPECT_EQ (l2n.layer_by_index (1) == 0, true); + EXPECT_EQ (l2n.layer_by_name ("l2") == 0, true); + + std::auto_ptr reg_copy (l2n.layer_by_name ("l1")); + EXPECT_EQ (reg_copy.get () != 0, true); + EXPECT_EQ (l2n.name (*reg_copy), "l1"); + EXPECT_EQ (l2n.layer_of (*reg_copy), 0u); + reg_copy.reset (l2n.layer_by_index (0)); + EXPECT_EQ (reg_copy.get () != 0, true); + EXPECT_EQ (l2n.name (*reg_copy), "l1"); + EXPECT_EQ (l2n.layer_of (*reg_copy), 0u); + reg_copy.reset (0); + + std::auto_ptr reg2 (l2n.make_layer ()); + EXPECT_EQ (l2n.name (1u), ""); + EXPECT_EQ (l2n.name (*reg2), ""); + EXPECT_EQ (l2n.layer_of (*reg2), 1u); + EXPECT_EQ (l2n.internal_layout ()->is_valid_layer (1), true); + reg2.reset (0); + EXPECT_EQ (l2n.internal_layout ()->is_valid_layer (1), false); + + std::auto_ptr reg3 (l2n.make_layer ("l3")); + EXPECT_EQ (l2n.name (*reg3), "l3"); + EXPECT_EQ (l2n.layer_of (*reg3), 1u); + + std::string s; + for (db::LayoutToNetlist::layer_iterator l = l2n.begin_layers (); l != l2n.end_layers (); ++l) { + s += tl::to_string (l->first) + ":" + l->second + ";"; + } + EXPECT_EQ (s, "0:l1;1:l3;"); +} + +TEST(1_BasicExtraction) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); + + // derived regions + + db::Region rpactive = *ractive & *rnwell; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = *ractive - *rnwell; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (nmos_ex, dl); + + // return the computed layers into the original layout and write it for debugging purposes + // NOTE: this will include the device layers too + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + unsigned int lpoly = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> Poly with gate terminal + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rpoly->insert_into (&ly, tc.cell_index (), lpoly); + + // net extraction + + l2n.register_layer (rpsd, "psd"); + l2n.register_layer (rnsd, "nsd"); + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + // debug layers produced for nets + // 202/0 -> Active + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + std::map dump_map; + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = l2n.cell_mapping_into (ly, tc, true /*with device cells*/); + dump_nets_to_layout (l2n, ly, dump_map, cm); + + dump_map.clear (); + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); + + dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_with_rec_nets.gds"); + + db::compare_layouts (_this, ly, au); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO ():\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" + " XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD)\n" + " XINV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD)\n" + " XINV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD)\n" + " XINV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD)\n" + " XINV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD)\n" + " XINV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD)\n" + " XINV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD)\n" + " XINV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD)\n" + " XINV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" + " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" + " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" + " XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // do some probing before purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I39"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "RINGO:$I2"); + + // test build_all_nets + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, 0, 0, 0); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_rebuild_ff.gds"); + + db::compare_layouts (_this, ly2, au); + } + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", 0, 0); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_rebuild_nf.gds"); + + db::compare_layouts (_this, ly2, au); + } + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, 0, "CIRCUIT_", 0); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_rebuild_fr.gds"); + + db::compare_layouts (_this, ly2, au); + } + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_", "DEVICE_"); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_rebuild_nr.gds"); + + db::compare_layouts (_this, ly2, au); + } + + // doesn't do anything here, but we test that this does not destroy anything: + l2n.netlist ()->combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD):\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" + " XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD)\n" + " XINV2 $3 (IN=$I19,$2=(null),OUT=$I1,$4=VSS,$5=VDD)\n" + " XINV2 $4 (IN=$I1,$2=(null),OUT=$I2,$4=VSS,$5=VDD)\n" + " XINV2 $5 (IN=$I2,$2=(null),OUT=$I3,$4=VSS,$5=VDD)\n" + " XINV2 $6 (IN=$I3,$2=(null),OUT=$I4,$4=VSS,$5=VDD)\n" + " XINV2 $7 (IN=$I4,$2=(null),OUT=$I5,$4=VSS,$5=VDD)\n" + " XINV2 $8 (IN=$I5,$2=(null),OUT=$I6,$4=VSS,$5=VDD)\n" + " XINV2 $9 (IN=$I6,$2=(null),OUT=$I7,$4=VSS,$5=VDD)\n" + " XINV2 $10 (IN=$I7,$2=(null),OUT=$I8,$4=VSS,$5=VDD)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + ); + + // do some probing after purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + // the transistor which supplies this probe target has been optimized away by "purge". + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "(null)"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2:$2"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "RINGO:$I2"); +} + +TEST(2_Probing) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l2.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); + + // derived regions + + db::Region rpactive = *ractive & *rnwell; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = *ractive - *rnwell; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (nmos_ex, dl); + + // net extraction + + l2n.register_layer (rpsd, "psd"); + l2n.register_layer (rnsd, "nsd"); + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + // debug layers produced for nets + // 202/0 -> Active + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + std::map dump_map; + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = l2n.cell_mapping_into (ly, tc); + dump_nets_to_layout (l2n, ly, dump_map, cm); + + dump_map.clear (); + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); + + dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO ():\n" + " XINV2PAIR $1 ($1=FB,$2=VDD,$3=VSS,$4=$I3,$5=OSC)\n" + " XINV2PAIR $2 ($1=$I18,$2=VDD,$3=VSS,$4=FB,$5=$I9)\n" + " XINV2PAIR $3 ($1=$I19,$2=VDD,$3=VSS,$4=$I9,$5=$I1)\n" + " XINV2PAIR $4 ($1=$I20,$2=VDD,$3=VSS,$4=$I1,$5=$I2)\n" + " XINV2PAIR $5 ($1=$I21,$2=VDD,$3=VSS,$4=$I2,$5=$I3)\n" + "Circuit INV2PAIR ($1=$I7,$2=$I5,$3=$I4,$4=$I2,$5=$I1):\n" + " XINV2 $1 (IN=$I3,$2=$I7,OUT=$I1,$4=$I4,$5=$I5)\n" + " XINV2 $2 (IN=$I2,$2=$I6,OUT=$I3,$4=$I4,$5=$I5)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" + " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" + " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" + " XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au2_with_rec_nets.gds"); + + db::compare_layouts (_this, ly, au); + + // do some probing before purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I18"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I3"); + + // doesn't do anything here, but we test that this does not destroy anything: + l2n.netlist ()->combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD):\n" + " XINV2PAIR $1 ($1=FB,$2=VDD,$3=VSS,$4=$I3,$5=OSC)\n" + " XINV2PAIR $2 ($1=(null),$2=VDD,$3=VSS,$4=FB,$5=$I9)\n" + " XINV2PAIR $3 ($1=(null),$2=VDD,$3=VSS,$4=$I9,$5=$I1)\n" + " XINV2PAIR $4 ($1=(null),$2=VDD,$3=VSS,$4=$I1,$5=$I2)\n" + " XINV2PAIR $5 ($1=(null),$2=VDD,$3=VSS,$4=$I2,$5=$I3)\n" + "Circuit INV2PAIR ($1=$I7,$2=$I5,$3=$I4,$4=$I2,$5=$I1):\n" + " XINV2 $1 (IN=$I3,$2=$I7,OUT=$I1,$4=$I4,$5=$I5)\n" + " XINV2 $2 (IN=$I2,$2=(null),OUT=$I3,$4=$I4,$5=$I5)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + ); + + // do some probing after purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + // the transistor which supplies this probe target has been optimized away by "purge". + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "(null)"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2PAIR:$I7"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I3"); +} + +TEST(3_GlobalNetConnections) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int pplus = define_layer (ly, lmap, 10); + unsigned int nplus = define_layer (ly, lmap, 11); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l3.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpplus (l2n.make_layer (pplus, "pplus")); + std::auto_ptr rnplus (l2n.make_layer (nplus, "nplus")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); + + // derived regions + + db::Region ractive_in_nwell = *ractive & *rnwell; + db::Region rpactive = ractive_in_nwell & *rpplus; + db::Region rntie = ractive_in_nwell & *rnplus; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region ractive_outside_nwell = *ractive - *rnwell; + db::Region rnactive = ractive_outside_nwell & *rnplus; + db::Region rptie = ractive_outside_nwell & *rpplus; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion + unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie + unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rpsd.insert_into (&ly, tc.cell_index (), lptie); + rnsd.insert_into (&ly, tc.cell_index (), lntie); + + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (nmos_ex, dl); + + // net extraction + + l2n.register_layer (rpsd, "psd"); + l2n.register_layer (rnsd, "nsd"); + l2n.register_layer (rptie, "ptie"); + l2n.register_layer (rntie, "ntie"); + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rdiff_cont, rptie); + l2n.connect (*rdiff_cont, rntie); + l2n.connect (*rnwell, rntie); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + l2n.connect_global (rptie, "BULK"); + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + // debug layers produced for nets + // 201/0 -> Well + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + // 212/0 -> N tie + // 213/0 -> P tie + std::map dump_map; + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = l2n.cell_mapping_into (ly, tc); + dump_nets_to_layout (l2n, ly, dump_map, cm); + + dump_map.clear (); + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); + + dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO ():\n" + " XINV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD)\n" + " XINV2PAIR $2 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD)\n" + " XINV2PAIR $3 (BULK='BULK,VSS',$2=$I23,$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD)\n" + " XINV2PAIR $4 (BULK='BULK,VSS',$2=$I24,$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD)\n" + " XINV2PAIR $5 (BULK='BULK,VSS',$2=$I25,$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD)\n" + "Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I3,$3=$I7,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" + " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" + "Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" + " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " XTRANS $1 ($1=$3,$2=VSS,$3=IN)\n" + " XTRANS $2 ($1=$3,$2=VDD,$3=IN)\n" + " XTRANS $3 ($1=VDD,$2=OUT,$3=$3)\n" + " XTRANS $4 ($1=VSS,$2=OUT,$3=$3)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au3_with_rec_nets.gds"); + + db::compare_layouts (_this, ly, au); + + // do some probing before purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:BULK,VSS"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I22"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4"); + + // doesn't do anything here, but we test that this does not destroy anything: + l2n.netlist ()->combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS'):\n" + " XINV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD)\n" + " XINV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD)\n" + " XINV2PAIR $3 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD)\n" + " XINV2PAIR $4 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD)\n" + " XINV2PAIR $5 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD)\n" + "Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I3,$3=(null),OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" + " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" + "Circuit INV2 ($1=(null),IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=(null)):\n" + " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + ); + + // do some probing after purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + // the transistor which supplies this probe target has been optimized away by "purge". + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "(null)"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2PAIR:$I8"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4"); +} + +TEST(4_GlobalNetDeviceExtraction) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int pplus = define_layer (ly, lmap, 10); + unsigned int nplus = define_layer (ly, lmap, 11); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l3.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rbulk (l2n.make_layer (ly.insert_layer (), "bulk")); + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpplus (l2n.make_layer (pplus, "pplus")); + std::auto_ptr rnplus (l2n.make_layer (nplus, "nplus")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); + + // derived regions + + db::Region ractive_in_nwell = *ractive & *rnwell; + db::Region rpactive = ractive_in_nwell & *rpplus; + db::Region rntie = ractive_in_nwell & *rnplus; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region ractive_outside_nwell = *ractive - *rnwell; + db::Region rnactive = ractive_outside_nwell & *rnplus; + db::Region rptie = ractive_outside_nwell & *rpplus; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion + unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie + unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rpsd.insert_into (&ly, tc.cell_index (), lptie); + rnsd.insert_into (&ly, tc.cell_index (), lntie); + + db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (nmos_ex, dl); + + // net extraction + + l2n.register_layer (rpsd, "psd"); + l2n.register_layer (rnsd, "nsd"); + l2n.register_layer (rptie, "ptie"); + l2n.register_layer (rntie, "ntie"); + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rdiff_cont, rptie); + l2n.connect (*rdiff_cont, rntie); + l2n.connect (*rnwell, rntie); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + l2n.connect_global (rptie, "BULK"); + l2n.connect_global (*rbulk, "BULK"); + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + // debug layers produced for nets + // 201/0 -> Well + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + // 212/0 -> N tie + // 213/0 -> P tie + std::map dump_map; + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0)); + dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (214, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = l2n.cell_mapping_into (ly, tc); + dump_nets_to_layout (l2n, ly, dump_map, cm); + + dump_map.clear (); + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0)); + dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (314, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); + + dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO ():\n" + " XINV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD)\n" + " XINV2PAIR $2 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD)\n" + " XINV2PAIR $3 (BULK='BULK,VSS',$2=$I23,$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD)\n" + " XINV2PAIR $4 (BULK='BULK,VSS',$2=$I24,$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD)\n" + " XINV2PAIR $5 (BULK='BULK,VSS',$2=$I25,$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD)\n" + "Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I3,$3=$I7,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" + " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" + "Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" + " DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " XTRANS $1 ($1=$3,$2=VSS,$3=IN)\n" + " XTRANS $2 ($1=$3,$2=VDD,$3=IN)\n" + " XTRANS $3 ($1=VDD,$2=OUT,$3=$3)\n" + " XTRANS $4 ($1=VSS,$2=OUT,$3=$3)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au4_with_rec_nets.gds"); + + db::compare_layouts (_this, ly, au); + + // do some probing before purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:BULK,VSS"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I22"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4"); + + // doesn't do anything here, but we test that this does not destroy anything: + l2n.netlist ()->combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS'):\n" + " XINV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD)\n" + " XINV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD)\n" + " XINV2PAIR $3 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD)\n" + " XINV2PAIR $4 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD)\n" + " XINV2PAIR $5 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD)\n" + "Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I3,$3=(null),OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" + " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" + "Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" + " DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + ); + + // do some probing after purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + // the transistor which supplies this probe target has been optimized away by "purge". + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "(null)"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2PAIR:$I8"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4"); +} + +TEST(5_DeviceExtractionWithDeviceCombination) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int pplus = define_layer (ly, lmap, 10); + unsigned int nplus = define_layer (ly, lmap, 11); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l5.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rbulk (l2n.make_layer ("bulk")); + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpplus (l2n.make_layer (pplus, "pplus")); + std::auto_ptr rnplus (l2n.make_layer (nplus, "nplus")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); + + // derived regions + + db::Region ractive_in_nwell = *ractive & *rnwell; + db::Region rpactive = ractive_in_nwell & *rpplus; + db::Region rntie = ractive_in_nwell & *rnplus; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region ractive_outside_nwell = *ractive - *rnwell; + db::Region rnactive = ractive_outside_nwell & *rnplus; + db::Region rptie = ractive_outside_nwell & *rpplus; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion + unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie + unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rpsd.insert_into (&ly, tc.cell_index (), lptie); + rnsd.insert_into (&ly, tc.cell_index (), lntie); + + db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (nmos_ex, dl); + + // net extraction + + l2n.register_layer (rpsd, "psd"); + l2n.register_layer (rnsd, "nsd"); + l2n.register_layer (rptie, "ptie"); + l2n.register_layer (rntie, "ntie"); + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rdiff_cont, rptie); + l2n.connect (*rdiff_cont, rntie); + l2n.connect (*rnwell, rntie); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + l2n.connect_global (rptie, "BULK"); + l2n.connect_global (*rbulk, "BULK"); + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + // debug layers produced for nets + // 201/0 -> Well + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + // 212/0 -> N tie + // 213/0 -> P tie + std::map dump_map; + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0)); + dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (214, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = l2n.cell_mapping_into (ly, tc); + dump_nets_to_layout (l2n, ly, dump_map, cm); + + dump_map.clear (); + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0)); + dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (314, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); + + dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO ():\n" + " XINV2PAIR $1 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=FB,$5=$I7,$6=OSC,$7=VDD)\n" + " XINV2PAIR $2 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I22,$5=FB,$6=$I13,$7=VDD)\n" + " XINV2PAIR $3 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I23,$5=$I13,$6=$I5,$7=VDD)\n" + " XINV2PAIR $4 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I24,$5=$I5,$6=$I6,$7=VDD)\n" + " XINV2PAIR $5 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I25,$5=$I6,$6=$I7,$7=VDD)\n" + "Circuit INV2PAIR (BULK=BULK,$2=$I6,$3=$I5,$4=$I4,$5=$I3,$6=$I2,$7=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I3,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" + " XINV2 $2 ($1=$I1,IN=$I4,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" + "Circuit INV2 ($1=$1,IN=IN,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" + " DPMOS $1 (S=OUT,G=IN,D=VDD,B=$1) [L=0.25,W=1.75,AS=0.91875,AD=0.48125,PS=4.55,PD=2.3]\n" + " DPMOS $2 (S=VDD,G=IN,D=OUT,B=$1) [L=0.25,W=1.75,AS=0.48125,AD=0.91875,PS=2.3,PD=4.55]\n" + " DNMOS $3 (S=OUT,G=IN,D=VSS,B=BULK) [L=0.25,W=1.75,AS=0.91875,AD=0.48125,PS=4.55,PD=2.3]\n" + " DNMOS $4 (S=VSS,G=IN,D=OUT,B=BULK) [L=0.25,W=1.75,AS=0.48125,AD=0.91875,PS=2.3,PD=4.55]\n" + " XTRANS $1 ($1=OUT,$2=VSS,$3=IN)\n" + " XTRANS $2 ($1=OUT,$2=VDD,$3=IN)\n" + " XTRANS $3 ($1=OUT,$2=VSS,$3=IN)\n" + " XTRANS $4 ($1=OUT,$2=VDD,$3=IN)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au5_with_rec_nets.gds"); + + db::compare_layouts (_this, ly, au); + + // do some probing before purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:BULK,VSS"); + + // doesn't do anything here, but we test that this does not destroy anything: + l2n.netlist ()->combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS'):\n" + " XINV2PAIR $1 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=FB,$5=$I7,$6=OSC,$7=VDD)\n" + " XINV2PAIR $2 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=FB,$6=$I13,$7=VDD)\n" + " XINV2PAIR $3 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I13,$6=$I5,$7=VDD)\n" + " XINV2PAIR $4 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I5,$6=$I6,$7=VDD)\n" + " XINV2PAIR $5 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I6,$6=$I7,$7=VDD)\n" + "Circuit INV2PAIR (BULK=BULK,$2=$I6,$3=$I5,$4=$I4,$5=$I3,$6=$I2,$7=$I1):\n" + " XINV2 $1 ($1=$I1,IN=$I3,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" + " XINV2 $2 ($1=$I1,IN=$I4,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" + "Circuit INV2 ($1=$1,IN=IN,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" + " DPMOS $1 (S=OUT,G=IN,D=VDD,B=$1) [L=0.25,W=3.5,AS=1.4,AD=1.4,PS=6.85,PD=6.85]\n" + " DNMOS $3 (S=OUT,G=IN,D=VSS,B=BULK) [L=0.25,W=3.5,AS=1.4,AD=1.4,PS=6.85,PD=6.85]\n" + ); + + // do some probing after purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC"); + // the transistor which supplies this probe target has been optimized away by "purge". + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "(null)"); +} + +TEST(6_MoreDeviceTypes) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int thickgox = define_layer (ly, lmap, 3); + unsigned int pplus = define_layer (ly, lmap, 4); + unsigned int nplus = define_layer (ly, lmap, 5); + unsigned int poly = define_layer (ly, lmap, 6); + unsigned int poly_lbl = define_layer (ly, lmap, 7); + unsigned int cont = define_layer (ly, lmap, 8); + unsigned int metal1 = define_layer (ly, lmap, 9); + unsigned int metal1_lbl = define_layer (ly, lmap, 10); + unsigned int via1 = define_layer (ly, lmap, 11); + unsigned int metal2 = define_layer (ly, lmap, 12); + unsigned int metal2_lbl = define_layer (ly, lmap, 13); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l6.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rbulk (l2n.make_layer ("bulk")); + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr rthickgox (l2n.make_layer (thickgox, "thickgox")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpplus (l2n.make_layer (pplus, "pplus")); + std::auto_ptr rnplus (l2n.make_layer (nplus, "nplus")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rcont (l2n.make_polygon_layer (cont, "cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); + + // derived regions + + db::Region ractive_in_nwell = *ractive & *rnwell; + db::Region rpactive = ractive_in_nwell - *rnplus; + db::Region rntie = ractive_in_nwell & *rnplus; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + db::Region rhv_pgate = rpgate & *rthickgox; + db::Region rlv_pgate = rpgate - rhv_pgate; + db::Region rhv_psd = rpsd & *rthickgox; + db::Region rlv_psd = rpsd - *rthickgox; + + l2n.register_layer(rntie, "ntie"); + l2n.register_layer(rpsd, "psd"); + + db::Region ractive_outside_nwell = *ractive - *rnwell; + db::Region rnactive = ractive_outside_nwell - *rpplus; + db::Region rptie = ractive_outside_nwell & *rpplus; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + db::Region rhv_ngate = rngate & *rthickgox; + db::Region rlv_ngate = rngate - rhv_ngate; + db::Region rhv_nsd = rnsd & *rthickgox; + db::Region rlv_nsd = rnsd - *rthickgox; + + l2n.register_layer(rptie, "ptie"); + l2n.register_layer(rnsd, "nsd"); + + db::NetlistDeviceExtractorMOS4Transistor hvpmos_ex ("HVPMOS"); + db::NetlistDeviceExtractorMOS4Transistor hvnmos_ex ("HVNMOS"); + db::NetlistDeviceExtractorMOS4Transistor lvpmos_ex ("LVPMOS"); + db::NetlistDeviceExtractorMOS4Transistor lvnmos_ex ("LVNMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rhv_pgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (hvpmos_ex, dl); + + dl["SD"] = &rpsd; + dl["G"] = &rlv_pgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (lvpmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rhv_ngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (hvnmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rlv_ngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (lvnmos_ex, dl); + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rcont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (*rcont, rntie); + l2n.connect (*rcont, rptie); + l2n.connect (*rnwell, rntie); + l2n.connect (rpsd, *rcont); + l2n.connect (rnsd, *rcont); + l2n.connect (*rpoly, *rcont); + l2n.connect (*rcont, *rmetal1); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + l2n.connect_global (rptie, "BULK"); + l2n.connect_global (*rbulk, "BULK"); + + l2n.extract_netlist (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit TOP ():\n" + " DHVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) [L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4]\n" + " DHVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) [L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8]\n" + " DLVPMOS $3 (S=$10,G=A,D=$6,B=$9) [L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5]\n" + " DHVNMOS $4 (S=Z,G=$6,D=VSS,B=BULK) [L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6]\n" + " DHVNMOS $5 (S=VSS,G=A,D=$5,B=BULK) [L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2]\n" + " DLVNMOS $6 (S=VSS,G=A,D=$6,B=BULK) [L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89]\n" + ); +} + +TEST(7_MoreByEmptyDeviceTypes) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int thickgox = define_layer (ly, lmap, 1003); // does not exist + unsigned int pplus = define_layer (ly, lmap, 4); + unsigned int nplus = define_layer (ly, lmap, 5); + unsigned int poly = define_layer (ly, lmap, 6); + unsigned int poly_lbl = define_layer (ly, lmap, 7); + unsigned int cont = define_layer (ly, lmap, 8); + unsigned int metal1 = define_layer (ly, lmap, 9); + unsigned int metal1_lbl = define_layer (ly, lmap, 10); + unsigned int via1 = define_layer (ly, lmap, 11); + unsigned int metal2 = define_layer (ly, lmap, 12); + unsigned int metal2_lbl = define_layer (ly, lmap, 13); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l6.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rbulk (l2n.make_layer ("bulk")); + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr rthickgox (l2n.make_layer (thickgox, "thickgox")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpplus (l2n.make_layer (pplus, "pplus")); + std::auto_ptr rnplus (l2n.make_layer (nplus, "nplus")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rcont (l2n.make_polygon_layer (cont, "cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); + + // derived regions + + db::Region ractive_in_nwell = *ractive & *rnwell; + db::Region rpactive = ractive_in_nwell - *rnplus; + db::Region rntie = ractive_in_nwell & *rnplus; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + db::Region rhv_pgate = rpgate & *rthickgox; + db::Region rlv_pgate = rpgate - rhv_pgate; + db::Region rhv_psd = rpsd & *rthickgox; + db::Region rlv_psd = rpsd - *rthickgox; + + l2n.register_layer(rntie, "ntie"); + l2n.register_layer(rpsd, "psd"); + + db::Region ractive_outside_nwell = *ractive - *rnwell; + db::Region rnactive = ractive_outside_nwell - *rpplus; + db::Region rptie = ractive_outside_nwell & *rpplus; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + db::Region rhv_ngate = rngate & *rthickgox; + db::Region rlv_ngate = rngate - rhv_ngate; + db::Region rhv_nsd = rnsd & *rthickgox; + db::Region rlv_nsd = rnsd - *rthickgox; + + l2n.register_layer(rptie, "ptie"); + l2n.register_layer(rnsd, "nsd"); + + db::NetlistDeviceExtractorMOS4Transistor hvpmos_ex ("HVPMOS"); + db::NetlistDeviceExtractorMOS4Transistor hvnmos_ex ("HVNMOS"); + db::NetlistDeviceExtractorMOS4Transistor lvpmos_ex ("LVPMOS"); + db::NetlistDeviceExtractorMOS4Transistor lvnmos_ex ("LVNMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rhv_pgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (hvpmos_ex, dl); + + dl["SD"] = &rpsd; + dl["G"] = &rlv_pgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (lvpmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rhv_ngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (hvnmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rlv_ngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (lvnmos_ex, dl); + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rcont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (*rcont, rntie); + l2n.connect (*rcont, rptie); + l2n.connect (*rnwell, rntie); + l2n.connect (rpsd, *rcont); + l2n.connect (rnsd, *rcont); + l2n.connect (*rpoly, *rcont); + l2n.connect (*rcont, *rmetal1); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + l2n.connect_global (rptie, "BULK"); + l2n.connect_global (*rbulk, "BULK"); + + l2n.extract_netlist (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit TOP ():\n" + " DLVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) [L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4]\n" + " DLVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) [L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8]\n" + " DLVPMOS $3 (S=$10,G=A,D=$6,B=$9) [L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5]\n" + " DLVNMOS $4 (S=VSS,G=A,D=$6,B=BULK) [L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89]\n" + " DLVNMOS $5 (S=Z,G=$6,D=VSS,B=BULK) [L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6]\n" + " DLVNMOS $6 (S=VSS,G=A,D=$5,B=BULK) [L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2]\n" + ); +} + +TEST(8_FlatExtraction) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int thickgox = define_layer (ly, lmap, 3); + unsigned int pplus = define_layer (ly, lmap, 4); + unsigned int nplus = define_layer (ly, lmap, 5); + unsigned int poly = define_layer (ly, lmap, 6); + unsigned int poly_lbl = define_layer (ly, lmap, 7); + unsigned int cont = define_layer (ly, lmap, 8); + unsigned int metal1 = define_layer (ly, lmap, 9); + unsigned int metal1_lbl = define_layer (ly, lmap, 10); + unsigned int via1 = define_layer (ly, lmap, 11); + unsigned int metal2 = define_layer (ly, lmap, 12); + unsigned int metal2_lbl = define_layer (ly, lmap, 13); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l6.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + db::LayoutToNetlist l2n (ly.cell_name (tc.cell_index ()), ly.dbu ()); + + std::auto_ptr rbulk (new db::Region ()); + std::auto_ptr rnwell (new db::Region (db::RecursiveShapeIterator (ly, tc, nwell))); + std::auto_ptr rthickgox (new db::Region (db::RecursiveShapeIterator (ly, tc, thickgox))); + std::auto_ptr ractive (new db::Region (db::RecursiveShapeIterator (ly, tc, active))); + std::auto_ptr rpplus (new db::Region (db::RecursiveShapeIterator (ly, tc, pplus))); + std::auto_ptr rnplus (new db::Region (db::RecursiveShapeIterator (ly, tc, nplus))); + std::auto_ptr rpoly (new db::Region (db::RecursiveShapeIterator (ly, tc, poly))); + std::auto_ptr rpoly_lbl (new db::Region (db::RecursiveShapeIterator (ly, tc, poly_lbl))); + std::auto_ptr rcont (new db::Region (db::RecursiveShapeIterator (ly, tc, cont))); + std::auto_ptr rmetal1 (new db::Region (db::RecursiveShapeIterator (ly, tc, metal1))); + std::auto_ptr rmetal1_lbl (new db::Region (db::RecursiveShapeIterator (ly, tc, metal1_lbl))); + std::auto_ptr rvia1 (new db::Region (db::RecursiveShapeIterator (ly, tc, via1))); + std::auto_ptr rmetal2 (new db::Region (db::RecursiveShapeIterator (ly, tc, metal2))); + std::auto_ptr rmetal2_lbl (new db::Region (db::RecursiveShapeIterator (ly, tc, metal2_lbl))); + + l2n.register_layer (*rbulk, "bulk"); + l2n.register_layer (*rnwell, "nwell"); + l2n.register_layer (*rthickgox, "thickgox"); + l2n.register_layer (*ractive, "active"); + l2n.register_layer (*rpplus, "pplus"); + l2n.register_layer (*rnplus, "nplus"); + l2n.register_layer (*rpoly, "poly"); + l2n.register_layer (*rpoly_lbl, "poly_lbl"); + l2n.register_layer (*rcont, "cont"); + l2n.register_layer (*rmetal1, "metal1"); + l2n.register_layer (*rmetal1_lbl, "metal1_lbl"); + l2n.register_layer (*rvia1, "via1"); + l2n.register_layer (*rmetal2, "metal2"); + l2n.register_layer (*rmetal2_lbl, "metal2_lbl"); + + // derived regions + + db::Region ractive_in_nwell = *ractive & *rnwell; + db::Region rpactive = ractive_in_nwell - *rnplus; + db::Region rntie = ractive_in_nwell & *rnplus; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + db::Region rhv_pgate = rpgate & *rthickgox; + db::Region rlv_pgate = rpgate - rhv_pgate; + db::Region rhv_psd = rpsd & *rthickgox; + db::Region rlv_psd = rpsd - *rthickgox; + + l2n.register_layer (rntie, "ntie"); + l2n.register_layer (rpsd, "psd"); + // required to provide deep layers for flat ones for the extractor: + l2n.register_layer (rhv_pgate, "hv_pgate"); + l2n.register_layer (rlv_pgate, "lv_pgate"); + + db::Region ractive_outside_nwell = *ractive - *rnwell; + db::Region rnactive = ractive_outside_nwell - *rpplus; + db::Region rptie = ractive_outside_nwell & *rpplus; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + db::Region rhv_ngate = rngate & *rthickgox; + db::Region rlv_ngate = rngate - rhv_ngate; + db::Region rhv_nsd = rnsd & *rthickgox; + db::Region rlv_nsd = rnsd - *rthickgox; + + l2n.register_layer (rptie, "ptie"); + l2n.register_layer (rnsd, "nsd"); + // required to provide deep layers for flat ones for the extractor: + l2n.register_layer (rhv_ngate, "hv_ngate"); + l2n.register_layer (rlv_ngate, "lv_ngate"); + + db::NetlistDeviceExtractorMOS4Transistor hvpmos_ex ("HVPMOS"); + db::NetlistDeviceExtractorMOS4Transistor hvnmos_ex ("HVNMOS"); + db::NetlistDeviceExtractorMOS4Transistor lvpmos_ex ("LVPMOS"); + db::NetlistDeviceExtractorMOS4Transistor lvnmos_ex ("LVNMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rhv_pgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (hvpmos_ex, dl); + + dl["SD"] = &rpsd; + dl["G"] = &rlv_pgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (lvpmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rhv_ngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (hvnmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rlv_ngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (lvnmos_ex, dl); + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rcont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (*rcont, rntie); + l2n.connect (*rcont, rptie); + l2n.connect (*rnwell, rntie); + l2n.connect (rpsd, *rcont); + l2n.connect (rnsd, *rcont); + l2n.connect (*rpoly, *rcont); + l2n.connect (*rcont, *rmetal1); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + l2n.connect_global (rptie, "BULK"); + l2n.connect_global (*rbulk, "BULK"); + + l2n.extract_netlist (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit TOP ():\n" + " DHVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) [L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4]\n" + " DHVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) [L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8]\n" + " DLVPMOS $3 (S=$10,G=A,D=$6,B=$9) [L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5]\n" + " DHVNMOS $4 (S=Z,G=$6,D=VSS,B=BULK) [L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6]\n" + " DHVNMOS $5 (S=VSS,G=A,D=$5,B=BULK) [L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2]\n" + " DLVNMOS $6 (S=VSS,G=A,D=$6,B=BULK) [L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89]\n" + ); +} + +TEST(9_FlatExtractionWithExternalDSS) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int thickgox = define_layer (ly, lmap, 103); // does not exist + unsigned int pplus = define_layer (ly, lmap, 4); + unsigned int nplus = define_layer (ly, lmap, 5); + unsigned int poly = define_layer (ly, lmap, 6); + unsigned int poly_lbl = define_layer (ly, lmap, 7); + unsigned int cont = define_layer (ly, lmap, 8); + unsigned int metal1 = define_layer (ly, lmap, 9); + unsigned int metal1_lbl = define_layer (ly, lmap, 10); + unsigned int via1 = define_layer (ly, lmap, 11); + unsigned int metal2 = define_layer (ly, lmap, 12); + unsigned int metal2_lbl = define_layer (ly, lmap, 13); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l6.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + // NOTE: we use a DSS from a LayoutToNetlist object - this one is initialized properly + // with the text representation settings. + db::LayoutToNetlist l2n_master; + db::DeepShapeStore &dss = l2n_master.dss (); + + db::LayoutToNetlist l2n (&dss); + + std::auto_ptr rbulk (new db::Region ()); + std::auto_ptr rnwell (new db::Region (db::RecursiveShapeIterator (ly, tc, nwell), dss)); + std::auto_ptr rthickgox (new db::Region (db::RecursiveShapeIterator (ly, tc, thickgox), dss)); + std::auto_ptr ractive (new db::Region (db::RecursiveShapeIterator (ly, tc, active), dss)); + std::auto_ptr rpplus (new db::Region (db::RecursiveShapeIterator (ly, tc, pplus), dss)); + std::auto_ptr rnplus (new db::Region (db::RecursiveShapeIterator (ly, tc, nplus), dss)); + std::auto_ptr rpoly (new db::Region (db::RecursiveShapeIterator (ly, tc, poly), dss)); + std::auto_ptr rpoly_lbl (new db::Region (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss)); + std::auto_ptr rcont (new db::Region (db::RecursiveShapeIterator (ly, tc, cont), dss)); + std::auto_ptr rmetal1 (new db::Region (db::RecursiveShapeIterator (ly, tc, metal1), dss)); + std::auto_ptr rmetal1_lbl (new db::Region (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss)); + std::auto_ptr rvia1 (new db::Region (db::RecursiveShapeIterator (ly, tc, via1), dss)); + std::auto_ptr rmetal2 (new db::Region (db::RecursiveShapeIterator (ly, tc, metal2), dss)); + std::auto_ptr rmetal2_lbl (new db::Region (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss)); + + l2n.register_layer (*rbulk, "bulk"); + l2n.register_layer (*rnwell, "nwell"); + l2n.register_layer (*rthickgox, "thickgox"); + l2n.register_layer (*ractive, "active"); + l2n.register_layer (*rpplus, "pplus"); + l2n.register_layer (*rnplus, "nplus"); + l2n.register_layer (*rpoly, "poly"); + l2n.register_layer (*rpoly_lbl, "poly_lbl"); + l2n.register_layer (*rcont, "cont"); + l2n.register_layer (*rmetal1, "metal1"); + l2n.register_layer (*rmetal1_lbl, "metal1_lbl"); + l2n.register_layer (*rvia1, "via1"); + l2n.register_layer (*rmetal2, "metal2"); + l2n.register_layer (*rmetal2_lbl, "metal2_lbl"); + + // derived regions + + db::Region ractive_in_nwell = *ractive & *rnwell; + db::Region rpactive = ractive_in_nwell - *rnplus; + db::Region rntie = ractive_in_nwell & *rnplus; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + db::Region rhv_pgate = rpgate & *rthickgox; + db::Region rlv_pgate = rpgate - rhv_pgate; + db::Region rhv_psd = rpsd & *rthickgox; + db::Region rlv_psd = rpsd - *rthickgox; + + l2n.register_layer (rntie, "ntie"); + l2n.register_layer (rpsd, "psd"); + // required to provide deep layers for flat ones for the extractor: + l2n.register_layer (rhv_pgate, "hv_pgate"); + l2n.register_layer (rlv_pgate, "lv_pgate"); + + db::Region ractive_outside_nwell = *ractive - *rnwell; + db::Region rnactive = ractive_outside_nwell - *rpplus; + db::Region rptie = ractive_outside_nwell & *rpplus; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + db::Region rhv_ngate = rngate & *rthickgox; + db::Region rlv_ngate = rngate - rhv_ngate; + db::Region rhv_nsd = rnsd & *rthickgox; + db::Region rlv_nsd = rnsd - *rthickgox; + + l2n.register_layer (rptie, "ptie"); + l2n.register_layer (rnsd, "nsd"); + // required to provide deep layers for flat ones for the extractor: + l2n.register_layer (rhv_ngate, "hv_ngate"); + l2n.register_layer (rlv_ngate, "lv_ngate"); + + db::NetlistDeviceExtractorMOS4Transistor hvpmos_ex ("HVPMOS"); + db::NetlistDeviceExtractorMOS4Transistor hvnmos_ex ("HVNMOS"); + db::NetlistDeviceExtractorMOS4Transistor lvpmos_ex ("LVPMOS"); + db::NetlistDeviceExtractorMOS4Transistor lvnmos_ex ("LVNMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rhv_pgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (hvpmos_ex, dl); + + dl["SD"] = &rpsd; + dl["G"] = &rlv_pgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (lvpmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rhv_ngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (hvnmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rlv_ngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (lvnmos_ex, dl); + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rcont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (*rcont, rntie); + l2n.connect (*rcont, rptie); + l2n.connect (*rnwell, rntie); + l2n.connect (rpsd, *rcont); + l2n.connect (rnsd, *rcont); + l2n.connect (*rpoly, *rcont); + l2n.connect (*rcont, *rmetal1); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + l2n.connect_global (rptie, "BULK"); + l2n.connect_global (*rbulk, "BULK"); + + l2n.extract_netlist (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit TOP ():\n" + " DLVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) [L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4]\n" + " DLVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) [L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8]\n" + " DLVPMOS $3 (S=$10,G=A,D=$6,B=$9) [L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5]\n" + " DLVNMOS $4 (S=VSS,G=A,D=$6,B=BULK) [L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89]\n" + " DLVNMOS $5 (S=Z,G=$6,D=VSS,B=BULK) [L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6]\n" + " DLVNMOS $6 (S=VSS,G=A,D=$5,B=BULK) [L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2]\n" + ); +} + +TEST(10_Antenna) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int poly = define_layer (ly, lmap, 6); + unsigned int cont = define_layer (ly, lmap, 8); + unsigned int metal1 = define_layer (ly, lmap, 9); + unsigned int via1 = define_layer (ly, lmap, 11); + unsigned int metal2 = define_layer (ly, lmap, 12); + unsigned int diode = define_layer (ly, lmap, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "antenna_l1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + db::DeepShapeStore dss; + + std::auto_ptr rdiode (new db::Region (db::RecursiveShapeIterator (ly, tc, diode), dss)); + std::auto_ptr rpoly (new db::Region (db::RecursiveShapeIterator (ly, tc, poly), dss)); + std::auto_ptr rcont (new db::Region (db::RecursiveShapeIterator (ly, tc, cont), dss)); + std::auto_ptr rmetal1 (new db::Region (db::RecursiveShapeIterator (ly, tc, metal1), dss)); + std::auto_ptr rvia1 (new db::Region (db::RecursiveShapeIterator (ly, tc, via1), dss)); + std::auto_ptr rmetal2 (new db::Region (db::RecursiveShapeIterator (ly, tc, metal2), dss)); + + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOPTOP")); + + rdiode->insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (1, 0))); + rpoly->insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (6, 0))); + rcont->insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (8, 0))); + rmetal1->insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (9, 0))); + rvia1->insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (11, 0))); + rmetal2->insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (12, 0))); + + { + db::LayoutToNetlist l2n (&dss); + + l2n.register_layer (*rpoly, "poly"); + l2n.register_layer (*rcont, "cont"); + l2n.register_layer (*rmetal1, "metal1"); + l2n.register_layer (*rvia1, "via1"); + l2n.register_layer (*rmetal2, "metal2"); + + // Intra-layer + l2n.connect (*rpoly); + l2n.connect (*rcont); + l2n.connect (*rmetal1); + /* not yet: + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + */ + // Inter-layer + l2n.connect (*rpoly, *rcont); + l2n.connect (*rcont, *rmetal1); + /* not yet: + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + */ + + l2n.extract_netlist (); + + db::Region a1_3 = l2n.antenna_check (*rpoly, *rmetal1, 3); + db::Region a1_10 = l2n.antenna_check (*rpoly, *rmetal1, 10); + db::Region a1_30 = l2n.antenna_check (*rpoly, *rmetal1, 30); + + a1_3.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (100, 0))); + a1_10.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (101, 0))); + a1_30.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (102, 0))); + } + + { + db::LayoutToNetlist l2n (&dss); + + l2n.register_layer (*rpoly, "poly"); + l2n.register_layer (*rcont, "cont"); + l2n.register_layer (*rmetal1, "metal1"); + l2n.register_layer (*rvia1, "via1"); + l2n.register_layer (*rmetal2, "metal2"); + + // Intra-layer + l2n.connect (*rpoly); + l2n.connect (*rcont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + // Inter-layer + l2n.connect (*rpoly, *rcont); + l2n.connect (*rcont, *rmetal1); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + + l2n.extract_netlist (); + + db::Region a2_5 = l2n.antenna_check (*rpoly, *rmetal2, 5); + db::Region a2_10 = l2n.antenna_check (*rpoly, *rmetal2, 10); + db::Region a2_17 = l2n.antenna_check (*rpoly, *rmetal2, 17); + + a2_5.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (200, 0))); + a2_10.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (201, 0))); + a2_17.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (202, 0))); + } + + { + db::LayoutToNetlist l2n (&dss); + + l2n.register_layer (*rdiode, "diode"); + l2n.register_layer (*rpoly, "poly"); + l2n.register_layer (*rcont, "cont"); + l2n.register_layer (*rmetal1, "metal1"); + + // Intra-layer + l2n.connect (*rdiode); + l2n.connect (*rpoly); + l2n.connect (*rcont); + l2n.connect (*rmetal1); + // Inter-layer + l2n.connect (*rdiode, *rcont); + l2n.connect (*rpoly, *rcont); + l2n.connect (*rcont, *rmetal1); + + l2n.extract_netlist (); + + std::vector > diodes; + // 8.0 means: increase r by 8.0 for each um^2 of diode attached to a net + diodes.push_back (std::make_pair (rdiode.get (), 8.0)); + + db::Region a3_3 = l2n.antenna_check (*rpoly, *rmetal1, 3, diodes); + db::Region a3_10 = l2n.antenna_check (*rpoly, *rmetal1, 10, diodes); + db::Region a3_30 = l2n.antenna_check (*rpoly, *rmetal1, 30, diodes); + + a3_3.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (300, 0))); + a3_10.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (301, 0))); + a3_30.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (302, 0))); + } + + { + db::LayoutToNetlist l2n (&dss); + + l2n.register_layer (*rdiode, "diode"); + l2n.register_layer (*rpoly, "poly"); + l2n.register_layer (*rcont, "cont"); + l2n.register_layer (*rmetal1, "metal1"); + + // Intra-layer + l2n.connect (*rdiode); + l2n.connect (*rpoly); + l2n.connect (*rcont); + l2n.connect (*rmetal1); + // Inter-layer + l2n.connect (*rdiode, *rcont); + l2n.connect (*rpoly, *rcont); + l2n.connect (*rcont, *rmetal1); + + l2n.extract_netlist (); + + std::vector > diodes; + // 0.0 means: skip all nets where there is a rdiode attached + diodes.push_back (std::make_pair (rdiode.get (), 0.0)); + + db::Region a4_3 = l2n.antenna_check (*rpoly, *rmetal1, 3, diodes); + db::Region a4_10 = l2n.antenna_check (*rpoly, *rmetal1, 10, diodes); + db::Region a4_30 = l2n.antenna_check (*rpoly, *rmetal1, 30, diodes); + + a4_3.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (400, 0))); + a4_10.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (401, 0))); + a4_30.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (402, 0))); + } + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "antenna_au1.gds"); + + db::compare_layouts (_this, ly2, au); +} + diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc new file mode 100644 index 000000000..426e69980 --- /dev/null +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -0,0 +1,464 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbLayoutToNetlist.h" +#include "dbLayoutToNetlistWriter.h" +#include "dbStream.h" +#include "dbCommonReader.h" +#include "dbNetlistDeviceExtractorClasses.h" +#include "dbTestSupport.h" + +#include "tlUnitTest.h" +#include "tlStream.h" +#include "tlFileUtils.h" + +static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) +{ + unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); + lmap.map (ly.get_properties (lid), lid); + return lid; +} + +TEST(1_WriterBasic) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); + + // derived regions + + db::Region rpactive = *ractive & *rnwell; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + l2n.register_layer (rpactive, "pactive"); + l2n.register_layer (rpgate, "pgate"); + l2n.register_layer (rpsd, "psd"); + + db::Region rnactive = *ractive - *rnwell; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + l2n.register_layer (rnactive, "nactive"); + l2n.register_layer (rngate, "ngate"); + l2n.register_layer (rnsd, "nsd"); + + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (nmos_ex, dl); + + // return the computed layers into the original layout and write it for debugging purposes + // NOTE: this will include the device layers too + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + unsigned int lpoly = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> Poly with gate terminal + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rpoly->insert_into (&ly, tc.cell_index (), lpoly); + + // net extraction + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + std::string path = tmp_file ("tmp_l2nwriter_1.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, false); + writer.write (&l2n); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au.txt"); + + { + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } + } + + path = tmp_file ("tmp_l2nwriter_1s.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, true); + writer.write (&l2n); + } + + au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_s.txt"); + + { + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } + } + + // test build_all_nets (verify reference for reader) + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", 0, "DEVICE_"); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "l2n_writer_au.gds"); + + db::compare_layouts (_this, ly2, au); + } +} + +TEST(2_WriterWithGlobalNets) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int pplus = define_layer (ly, lmap, 10); + unsigned int nplus = define_layer (ly, lmap, 11); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l3.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rbulk (l2n.make_layer (ly.insert_layer (), "rbulk")); + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpplus (l2n.make_layer (pplus, "pplus")); + std::auto_ptr rnplus (l2n.make_layer (nplus, "nplus")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); + + // derived regions + + db::Region ractive_in_nwell = *ractive & *rnwell; + db::Region rpactive = ractive_in_nwell & *rpplus; + db::Region rntie = ractive_in_nwell & *rnplus; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + l2n.register_layer (rpactive, "pactive"); + l2n.register_layer (rntie, "ntie"); + l2n.register_layer (rpgate, "pgate"); + l2n.register_layer (rpsd, "psd"); + + db::Region ractive_outside_nwell = *ractive - *rnwell; + db::Region rnactive = ractive_outside_nwell & *rnplus; + db::Region rptie = ractive_outside_nwell & *rpplus; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + l2n.register_layer (rnactive, "nactive"); + l2n.register_layer (rptie, "ptie"); + l2n.register_layer (rngate, "ngate"); + l2n.register_layer (rnsd, "nsd"); + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion + unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie + unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rpsd.insert_into (&ly, tc.cell_index (), lptie); + rnsd.insert_into (&ly, tc.cell_index (), lntie); + + db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (nmos_ex, dl); + + // net extraction + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rdiff_cont, rptie); + l2n.connect (*rdiff_cont, rntie); + l2n.connect (*rnwell, rntie); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + l2n.connect_global (rptie, "BULK"); + l2n.connect_global (*rbulk, "BULK"); + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + std::string path = tmp_file ("tmp_l2nwriter_2.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, false); + writer.write (&l2n); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_2.txt"); + + { + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } + } + + path = tmp_file ("tmp_l2nwriter_2s.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, true); + writer.write (&l2n); + } + + au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_2s.txt"); + + { + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } + } + + // test build_all_nets as reference for the reader + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (12, 0))] = rbulk.get (); + lmap [ly2.insert_layer (db::LayerProperties (13, 0))] = &rptie; + lmap [ly2.insert_layer (db::LayerProperties (14, 0))] = &rntie; + lmap [ly2.insert_layer (db::LayerProperties (1, 0)) ] = rnwell.get (); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_", "DEVICE_"); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "l2n_writer_au_2.gds"); + + db::compare_layouts (_this, ly2, au); + } +} diff --git a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc new file mode 100644 index 000000000..095541605 --- /dev/null +++ b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc @@ -0,0 +1,1556 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbNetlistDeviceClasses.h" +#include "dbNetlist.h" +#include "tlUnitTest.h" + +#include +#include + +TEST(1_SerialResistors) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n2,B=n3) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n1,B=n3) [R=4]\n" + ); +} + +TEST(2_SerialResistors1Swapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n3,B=n2) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n1,B=n3) [R=4]\n" + ); +} + +TEST(3_SerialResistors1OtherSwapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n2,B=n1) [R=1]\n" + " D r2 (A=n2,B=n3) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n3,B=n1) [R=4]\n" + ); +} + +TEST(4_SerialResistors2Swapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n2,B=n1) [R=1]\n" + " D r2 (A=n3,B=n2) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n3,B=n1) [R=4]\n" + ); +} + +TEST(5_SerialResistorsNoCombination) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_c.id (), n2); // prevents combination + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3,C=n2):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n2,B=n3) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3,C=n2):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n2,B=n3) [R=3]\n" + ); +} + +TEST(6_ParallelResistors) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n1,B=n2) [R=2]\n" + " D r2 (A=n1,B=n2) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n1,B=n2) [R=1.2]\n" + ); +} + +TEST(7_ParallelResistors1Swapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n2,B=n1) [R=2]\n" + " D r2 (A=n1,B=n2) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n2,B=n1) [R=1.2]\n" + ); +} + +TEST(8_ParallelResistors1OtherSwapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n1,B=n2) [R=2]\n" + " D r2 (A=n2,B=n1) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n1,B=n2) [R=1.2]\n" + ); +} + +TEST(9_ParallelResistors2Swapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n2,B=n1) [R=2]\n" + " D r2 (A=n2,B=n1) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n2,B=n1) [R=1.2]\n" + ); +} + +TEST(10_ComplexRegistorCombination) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + /** + * (n2) + * +--[ r1=1.0 ]--+--[ r2=1.0 ]--+ + * | | + * --x (n1) (n3) x--[ r4=0.8 ]--+-- + * | | (n4) + * +----------[ r3=3.0 ]---------+ + */ + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r3 = new db::Device (res, "r3"); + r3->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + db::Device *r4 = new db::Device (res, "r4"); + r4->set_parameter_value (db::DeviceClassResistor::param_id_R, 0.8); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (r1); + circuit->add_device (r2); + circuit->add_device (r3); + circuit->add_device (r4); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + r3->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + r3->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + r4->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3); + + db::Net *n4 = new db::Net ("n4"); + circuit->add_net (n4); + circuit->connect_pin (pin_b.id (), n4); + r4->connect_terminal (db::DeviceClassResistor::terminal_id_B, n4); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n4):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n2,B=n3) [R=1]\n" + " D r3 (A=n1,B=n3) [R=3]\n" + " D r4 (A=n3,B=n4) [R=0.8]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n4):\n" + " D r4 (A=n1,B=n4) [R=2]\n" + ); +} + +TEST(11_SerialInductors) +{ + db::DeviceClassInductor *ind = new db::DeviceClassInductor (); + + db::Netlist nl; + nl.add_device_class (ind); + + db::Device *l1 = new db::Device (ind, "l1"); + l1->set_parameter_value (db::DeviceClassInductor::param_id_L, 1.0); + db::Device *l2 = new db::Device (ind, "l2"); + l2->set_parameter_value (db::DeviceClassInductor::param_id_L, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (l1); + circuit->add_device (l2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + l1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + l1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + l2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + l2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D l1 (A=n1,B=n2) [L=1]\n" + " D l2 (A=n2,B=n3) [L=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D l1 (A=n1,B=n3) [L=4]\n" + ); +} + +TEST(12_ParallelInductors) +{ + db::DeviceClassInductor *ind = new db::DeviceClassInductor (); + + db::Netlist nl; + nl.add_device_class (ind); + + db::Device *l1 = new db::Device (ind, "l1"); + l1->set_parameter_value (db::DeviceClassInductor::param_id_L, 2.0); + db::Device *l2 = new db::Device (ind, "l2"); + l2->set_parameter_value (db::DeviceClassInductor::param_id_L, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (l1); + circuit->add_device (l2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + l1->connect_terminal (db::DeviceClassInductor::terminal_id_A, n1); + l2->connect_terminal (db::DeviceClassInductor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + l1->connect_terminal (db::DeviceClassInductor::terminal_id_B, n2); + l2->connect_terminal (db::DeviceClassInductor::terminal_id_B, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D l1 (A=n1,B=n2) [L=2]\n" + " D l2 (A=n1,B=n2) [L=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D l1 (A=n1,B=n2) [L=1.2]\n" + ); +} + +TEST(13_SerialCapacitors) +{ + db::DeviceClassCapacitor *cap = new db::DeviceClassCapacitor (); + + db::Netlist nl; + nl.add_device_class (cap); + + db::Device *c1 = new db::Device (cap, "c1"); + c1->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 2.0); + db::Device *c2 = new db::Device (cap, "c2"); + c2->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (c1); + circuit->add_device (c2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n2); + c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D c1 (A=n1,B=n2) [C=2]\n" + " D c2 (A=n2,B=n3) [C=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D c1 (A=n1,B=n3) [C=1.2]\n" + ); +} + +TEST(14_ParallelCapacitors) +{ + db::DeviceClassCapacitor *cap = new db::DeviceClassCapacitor (); + + db::Netlist nl; + nl.add_device_class (cap); + + db::Device *c1 = new db::Device (cap, "c1"); + c1->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 1.0); + db::Device *c2 = new db::Device (cap, "c2"); + c2->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (c1); + circuit->add_device (c2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_A, n1); + c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n2); + c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D c1 (A=n1,B=n2) [C=1]\n" + " D c2 (A=n1,B=n2) [C=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D c1 (A=n1,B=n2) [C=4]\n" + ); +} + +TEST(15_SerialDiodes) +{ + db::DeviceClassDiode *diode = new db::DeviceClassDiode (); + + db::Netlist nl; + nl.add_device_class (diode); + + db::Device *d1 = new db::Device (diode, "d1"); + d1->set_parameter_value (db::DeviceClassDiode::param_id_A, 2.0); + db::Device *d2 = new db::Device (diode, "d2"); + d2->set_parameter_value (db::DeviceClassDiode::param_id_A, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassDiode::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + d1->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); + d2->connect_terminal (db::DeviceClassDiode::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + d2->connect_terminal (db::DeviceClassDiode::terminal_id_C, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D d1 (A=n1,C=n2) [A=2]\n" + " D d2 (A=n2,C=n3) [A=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + // serial diodes are not combined! + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D d1 (A=n1,C=n2) [A=2]\n" + " D d2 (A=n2,C=n3) [A=3]\n" + ); +} + +TEST(16_ParallelDiodes) +{ + db::DeviceClassDiode *diode = new db::DeviceClassDiode (); + + db::Netlist nl; + nl.add_device_class (diode); + + db::Device *d1 = new db::Device (diode, "d1"); + d1->set_parameter_value (db::DeviceClassDiode::param_id_A, 1.0); + db::Device *d2 = new db::Device (diode, "d2"); + d2->set_parameter_value (db::DeviceClassDiode::param_id_A, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassDiode::terminal_id_A, n1); + d2->connect_terminal (db::DeviceClassDiode::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); + d2->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D d1 (A=n1,C=n2) [A=1]\n" + " D d2 (A=n1,C=n2) [A=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D d1 (A=n1,C=n2) [A=4]\n" + ); +} + +TEST(17_AntiParallelDiodes) +{ + db::DeviceClassDiode *diode = new db::DeviceClassDiode (); + + db::Netlist nl; + nl.add_device_class (diode); + + db::Device *d1 = new db::Device (diode, "d1"); + d1->set_parameter_value (db::DeviceClassDiode::param_id_A, 1.0); + db::Device *d2 = new db::Device (diode, "d2"); + d2->set_parameter_value (db::DeviceClassDiode::param_id_A, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassDiode::terminal_id_A, n1); + d2->connect_terminal (db::DeviceClassDiode::terminal_id_C, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); + d2->connect_terminal (db::DeviceClassDiode::terminal_id_A, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D d1 (A=n1,C=n2) [A=1]\n" + " D d2 (A=n2,C=n1) [A=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + // anti-parallel diodes are not combined + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D d1 (A=n1,C=n2) [A=1]\n" + " D d2 (A=n2,C=n1) [A=3]\n" + ); +} + +TEST(20_ParallelMOS3Transistors) +{ + db::DeviceClassMOS3Transistor *cls = new db::DeviceClassMOS3Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=3,AS=5,AD=7,PS=25,PD=27]\n" + ); +} + +TEST(21_AntiParallelMOS3Transistors) +{ + db::DeviceClassMOS3Transistor *cls = new db::DeviceClassMOS3Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n2,G=n3,D=n1) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=3,AS=5,AD=7,PS=25,PD=27]\n" + ); +} + +TEST(22_ParallelMOS3TransistorsDisconnectedGates) +{ + db::DeviceClassMOS3Transistor *cls = new db::DeviceClassMOS3Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c1 = circuit->add_pin ("C1"); + db::Pin pin_c2 = circuit->add_pin ("C2"); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c1.id (), n3); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + + db::Net *n4 = new db::Net ("n4"); + circuit->add_net (n4); + circuit->connect_pin (pin_c2.id (), n4); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n4); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C1=n3,C2=n4):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n4,D=n2) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); + + nl.combine_devices (); + nl.purge (); + + // because of the disconnected gates, devices will no be joined: + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C1=n3,C2=n4):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n4,D=n2) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); +} + +TEST(23_ParallelMOS3TransistorsDifferentLength) +{ + db::DeviceClassMOS3Transistor *cls = new db::DeviceClassMOS3Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.75); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2) [L=0.75,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); + + nl.combine_devices (); + nl.purge (); + + // because of different length, the devices will not be combined: + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3):\n" + " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2) [L=0.75,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); +} + +TEST(30_ParallelMOS4Transistors) +{ + db::DeviceClassMOS4Transistor *cls = new db::DeviceClassMOS4Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); + db::Pin pin_d = circuit->add_pin ("D"); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + + db::Net *n0 = new db::Net ("n0"); + circuit->add_net (n0); + circuit->connect_pin (pin_d.id (), n0); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D=n0):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D=n0):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=3,AS=5,AD=7,PS=25,PD=27]\n" + ); +} + +TEST(31_AntiParallelMOS4Transistors) +{ + db::DeviceClassMOS4Transistor *cls = new db::DeviceClassMOS4Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); + db::Pin pin_d = circuit->add_pin ("D"); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + + db::Net *n0 = new db::Net ("n0"); + circuit->add_net (n0); + circuit->connect_pin (pin_d.id (), n0); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D=n0):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n2,G=n3,D=n1,B=n0) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D=n0):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=3,AS=5,AD=7,PS=25,PD=27]\n" + ); +} + +TEST(32_ParallelMOS4TransistorsDisconnectedGates) +{ + db::DeviceClassMOS4Transistor *cls = new db::DeviceClassMOS4Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c1 = circuit->add_pin ("C1"); + db::Pin pin_c2 = circuit->add_pin ("C2"); + db::Pin pin_d = circuit->add_pin ("D"); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + + db::Net *n3a = new db::Net ("n3a"); + circuit->add_net (n3a); + circuit->connect_pin (pin_c1.id (), n3a); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3a); + + db::Net *n3b = new db::Net ("n3b"); + circuit->add_net (n3b); + circuit->connect_pin (pin_c2.id (), n3b); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3b); + + db::Net *n0 = new db::Net ("n0"); + circuit->add_net (n0); + circuit->connect_pin (pin_d.id (), n0); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C1=n3a,C2=n3b,D=n0):\n" + " D d1 (S=n1,G=n3a,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3b,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); + + nl.combine_devices (); + nl.purge (); + + // not combined because gate is different: + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C1=n3a,C2=n3b,D=n0):\n" + " D d1 (S=n1,G=n3a,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3b,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); +} + +TEST(33_ParallelMOS4TransistorsDisconnectedBulk) +{ + db::DeviceClassMOS4Transistor *cls = new db::DeviceClassMOS4Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); + db::Pin pin_d1 = circuit->add_pin ("D1"); + db::Pin pin_d2 = circuit->add_pin ("D2"); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + + db::Net *n0a = new db::Net ("n0a"); + circuit->add_net (n0a); + circuit->connect_pin (pin_d1.id (), n0a); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0a); + + db::Net *n0b = new db::Net ("n0b"); + circuit->add_net (n0b); + circuit->connect_pin (pin_d2.id (), n0b); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0b); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D1=n0a,D2=n0b):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0a) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0b) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); + + // not combined because bulk is different: + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D1=n0a,D2=n0b):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0a) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0b) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); +} + +TEST(34_ParallelMOS4TransistorsDifferentLength) +{ + db::DeviceClassMOS4Transistor *cls = new db::DeviceClassMOS4Transistor (); + + db::Netlist nl; + nl.add_device_class (cls); + + db::Device *d1 = new db::Device (cls, "d1"); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.5); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 1.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 2.0); + d1->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 3.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 12.0); + d1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 13.0); + db::Device *d2 = new db::Device (cls, "d2"); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_L, 0.75); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_W, 2.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AS, 3.0); + d2->set_parameter_value (db::DeviceClassMOS4Transistor::param_id_AD, 4.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 13.0); + d2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 14.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_c = circuit->add_pin ("C"); + db::Pin pin_d = circuit->add_pin ("D"); + + circuit->add_device (d1); + circuit->add_device (d2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + circuit->connect_pin (pin_c.id (), n3); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, n3); + + db::Net *n0 = new db::Net ("n0"); + circuit->add_net (n0); + circuit->connect_pin (pin_d.id (), n0); + d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D=n0):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.75,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); + + nl.combine_devices (); + nl.purge (); + + // not combined because length is different: + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2,C=n3,D=n0):\n" + " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" + " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.75,W=2,AS=3,AD=4,PS=13,PD=14]\n" + ); +} + diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc new file mode 100644 index 000000000..3cba2aaf5 --- /dev/null +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -0,0 +1,85 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbNetlistDeviceExtractor.h" + +#include "tlUnitTest.h" + +TEST(1_NetlistDeviceExtractorErrorBasic) +{ + db::NetlistDeviceExtractorError error; + + EXPECT_EQ (error.message (), ""); + error.set_message ("x"); + EXPECT_EQ (error.message (), "x"); + error.set_category_name ("cat"); + EXPECT_EQ (error.category_name (), "cat"); + error.set_category_description ("cdesc"); + EXPECT_EQ (error.category_description (), "cdesc"); + error.set_cell_name ("cell"); + EXPECT_EQ (error.cell_name (), "cell"); + error.set_geometry (db::DPolygon (db::DBox (0, 1, 2, 3))); + EXPECT_EQ (error.geometry ().to_string (), "(0,1;0,3;2,3;2,1)"); + + error = db::NetlistDeviceExtractorError ("cell2", "msg2"); + EXPECT_EQ (error.cell_name (), "cell2"); + EXPECT_EQ (error.message (), "msg2"); + EXPECT_EQ (error.category_name (), ""); + EXPECT_EQ (error.category_description (), ""); + EXPECT_EQ (error.geometry ().to_string (), "()"); +} + +namespace { + class DummyDeviceExtractor + : public db::NetlistDeviceExtractor + { + public: + DummyDeviceExtractor () + : db::NetlistDeviceExtractor (std::string ("DUMMY")) + { + error ("msg1"); + error ("msg2", db::DPolygon (db::DBox (0, 1, 2, 3))); + error ("cat1", "desc1", "msg1"); + error ("cat1", "desc1", "msg3", db::DPolygon (db::DBox (10, 11, 12, 13))); + } + }; +} + +static std::string error2string (const db::NetlistDeviceExtractorError &e) +{ + return e.cell_name() + ":" + e.category_name () + ":" + e.category_description () + ":" + + e.geometry ().to_string () + ":" + e.message (); +} + +TEST(2_NetlistDeviceExtractorErrors) +{ + DummyDeviceExtractor dummy_ex; + + EXPECT_EQ (dummy_ex.has_errors (), true); + + std::vector errors (dummy_ex.begin_errors (), dummy_ex.end_errors ()); + EXPECT_EQ (int (errors.size ()), 4); + EXPECT_EQ (error2string (errors [0]), ":::():msg1"); + EXPECT_EQ (error2string (errors [1]), ":::(0,1;0,3;2,3;2,1):msg2"); + EXPECT_EQ (error2string (errors [2]), ":cat1:desc1:():msg1"); + EXPECT_EQ (error2string (errors [3]), ":cat1:desc1:(10,11;10,13;12,13;12,11):msg3"); +} diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc new file mode 100644 index 000000000..4713081aa --- /dev/null +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -0,0 +1,791 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbNetlistDeviceExtractorClasses.h" +#include "dbNetlistExtractor.h" +#include "dbNetlistDeviceClasses.h" +#include "dbLayout.h" +#include "dbDeepShapeStore.h" +#include "dbRegion.h" +#include "dbStream.h" +#include "dbDeepRegion.h" +#include "dbDeepShapeStore.h" +#include "dbReader.h" +#include "dbWriter.h" +#include "dbCommonReader.h" +#include "dbTestSupport.h" +#include "dbCellMapping.h" + +#include "tlUnitTest.h" +#include "tlString.h" +#include "tlFileUtils.h" + +#include +#include + +static unsigned int layer_of (const db::Region ®ion) +{ + const db::DeepRegion *dr = dynamic_cast (region.delegate ()); + tl_assert (dr != 0); + return dr->deep_layer ().layer (); +} + +static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) +{ + unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); + lmap.map (ly.get_properties (lid), lid); + return lid; +} + +static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters &clusters, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) +{ + std::set device_cells_seen; + + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + + db::Cell &cell = ly.cell (cmap.cell_mapping (c->cell_index ())); + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + + const db::local_cluster &lc = clusters.clusters_per_cell (c->cell_index ()).cluster_by_id (n->cluster_id ()); + + bool any_shapes = false; + for (std::map::const_iterator m = lmap.begin (); m != lmap.end () && !any_shapes; ++m) { + any_shapes = ! lc.begin (m->first).at_end (); + } + + if (any_shapes) { + + std::string nn = "NET_" + c->name () + "_" + n->expanded_name (); + db::Cell &net_cell = ly.cell (ly.add_cell (nn.c_str ())); + cell.insert (db::CellInstArray (db::CellInst (net_cell.cell_index ()), db::Trans ())); + + for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { + db::Shapes &target = net_cell.shapes (m->second); + for (db::local_cluster::shape_iterator s = lc.begin (m->first); !s.at_end (); ++s) { + target.insert (*s); + } + } + + } + + } + + for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { + + db::cell_index_type dci = d->device_abstract ()->cell_index (); + + if (device_cells_seen.find (dci) != device_cells_seen.end ()) { + continue; + } + + db::Cell &device_cell = ly.cell (cmap.cell_mapping (dci)); + device_cells_seen.insert (dci); + + std::string ps; + const std::vector &pd = d->device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (! ps.empty ()) { + ps += ","; + } + ps += i->name () + "=" + tl::to_string (d->parameter_value (i->id ())); + } + + const std::vector &td = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + + const db::local_cluster &dc = clusters.clusters_per_cell (dci).cluster_by_id (d->device_abstract ()->cluster_id_for_terminal (t->id ())); + + for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { + db::Shapes &target = device_cell.shapes (m->second); + for (db::local_cluster::shape_iterator s = dc.begin (m->first); !s.at_end (); ++s) { + target.insert (*s); + } + } + + } + + } + + } +} + +TEST(1_DeviceAndNetExtraction) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + db::DeepShapeStore dss; + dss.set_text_enlargement (1); + dss.set_text_property_name (tl::Variant ("LABEL")); + + // original layers + db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss); + db::Region ractive (db::RecursiveShapeIterator (ly, tc, active), dss); + db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss); + db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss); + db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss); + db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss); + db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss); + db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss); + db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss); + db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss); + db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss); + + // derived regions + + db::Region rpactive = ractive & rnwell; + db::Region rpgate = rpactive & rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = ractive - rnwell; + db::Region rngate = rnactive & rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + + // perform the extraction + + db::Netlist nl; + db::hier_clusters cl; + + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + pmos_ex.extract (dss, 0, dl, nl, cl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + nmos_ex.extract (dss, 0, dl, nl, cl); + + // perform the net extraction + + db::NetlistExtractor net_ex; + + db::Connectivity conn; + // Intra-layer + conn.connect (rpsd); + conn.connect (rnsd); + conn.connect (rpoly); + conn.connect (rdiff_cont); + conn.connect (rpoly_cont); + conn.connect (rmetal1); + conn.connect (rvia1); + conn.connect (rmetal2); + // Inter-layer + conn.connect (rpsd, rdiff_cont); + conn.connect (rnsd, rdiff_cont); + conn.connect (rpoly, rpoly_cont); + conn.connect (rpoly_cont, rmetal1); + conn.connect (rdiff_cont, rmetal1); + conn.connect (rmetal1, rvia1); + conn.connect (rvia1, rmetal2); + conn.connect (rpoly, rpoly_lbl); // attaches labels + conn.connect (rmetal1, rmetal1_lbl); // attaches labels + conn.connect (rmetal2, rmetal2_lbl); // attaches labels + + // extract the nets + + net_ex.extract_nets (dss, 0, conn, nl, cl); + + // debug layers produced for nets + // 202/0 -> Active + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + std::map dump_map; + dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [layer_of (rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ()); + dump_nets_to_layout (nl, cl, ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (nl.to_string (), + "Circuit RINGO ():\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" + " XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD)\n" + " XINV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD)\n" + " XINV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD)\n" + " XINV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD)\n" + " XINV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD)\n" + " XINV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD)\n" + " XINV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD)\n" + " XINV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD)\n" + " XINV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" + " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" + " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" + " XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // doesn't do anything here, but we test that this does not destroy anything: + nl.combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + nl.make_top_level_pins (); + nl.purge (); + + // compare netlist as string + EXPECT_EQ (nl.to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD):\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" + " XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD)\n" + " XINV2 $3 (IN=$I19,$2=(null),OUT=$I1,$4=VSS,$5=VDD)\n" + " XINV2 $4 (IN=$I1,$2=(null),OUT=$I2,$4=VSS,$5=VDD)\n" + " XINV2 $5 (IN=$I2,$2=(null),OUT=$I3,$4=VSS,$5=VDD)\n" + " XINV2 $6 (IN=$I3,$2=(null),OUT=$I4,$4=VSS,$5=VDD)\n" + " XINV2 $7 (IN=$I4,$2=(null),OUT=$I5,$4=VSS,$5=VDD)\n" + " XINV2 $8 (IN=$I5,$2=(null),OUT=$I6,$4=VSS,$5=VDD)\n" + " XINV2 $9 (IN=$I6,$2=(null),OUT=$I7,$4=VSS,$5=VDD)\n" + " XINV2 $10 (IN=$I7,$2=(null),OUT=$I8,$4=VSS,$5=VDD)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1.gds"); + + db::compare_layouts (_this, ly, au); +} + +TEST(2_DeviceAndNetExtractionFlat) +{ + db::Layout ly (true); + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + ly.flatten (ly.cell (*ly.begin_top_down ()), -1, true); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + db::DeepShapeStore dss; + dss.set_text_enlargement (1); + dss.set_text_property_name (tl::Variant ("LABEL")); + + // original layers + db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss); + db::Region ractive (db::RecursiveShapeIterator (ly, tc, active), dss); + db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss); + db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss); + db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss); + db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss); + db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss); + db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss); + db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss); + db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss); + db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss); + + // derived regions + + db::Region rpactive = ractive & rnwell; + db::Region rpgate = rpactive & rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = ractive - rnwell; + db::Region rngate = rnactive & rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + + // perform the extraction + + db::Netlist nl; + db::hier_clusters cl; + + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + pmos_ex.extract (dss, 0, dl, nl, cl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + nmos_ex.extract (dss, 0, dl, nl, cl); + + // perform the net extraction + + db::NetlistExtractor net_ex; + + db::Connectivity conn; + // Intra-layer + conn.connect (rpsd); + conn.connect (rnsd); + conn.connect (rpoly); + conn.connect (rdiff_cont); + conn.connect (rpoly_cont); + conn.connect (rmetal1); + conn.connect (rvia1); + conn.connect (rmetal2); + // Inter-layer + conn.connect (rpsd, rdiff_cont); + conn.connect (rnsd, rdiff_cont); + conn.connect (rpoly, rpoly_cont); + conn.connect (rpoly_cont, rmetal1); + conn.connect (rdiff_cont, rmetal1); + conn.connect (rmetal1, rvia1); + conn.connect (rvia1, rmetal2); + conn.connect (rpoly, rpoly_lbl); // attaches labels + conn.connect (rmetal1, rmetal1_lbl); // attaches labels + conn.connect (rmetal2, rmetal2_lbl); // attaches labels + + // extract the nets + + // don't use "join_nets_by_label" because the flattened texts will spoil everything + net_ex.extract_nets (dss, 0, conn, nl, cl, false); + + // debug layers produced for nets + // 202/0 -> Active + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + std::map dump_map; + dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [layer_of (rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ()); + dump_nets_to_layout (nl, cl, ly, dump_map, cm); + + // compare netlist as string + // NOTE: some of the nets are called IN,OUT but are different ones. They + // happen to be the same because they share the same label. + EXPECT_EQ (nl.to_string (), + "Circuit RINGO ():\n" + " DPMOS $1 (S=$16,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=VDD,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $3 (S=$14,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $4 (S=VDD,G=$14,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $5 (S=$12,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $6 (S=VDD,G=$12,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $7 (S='IN,FB',G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $8 (S=VDD,G='IN,FB',D='OUT,OSC') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $9 (S=$4,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $10 (S=VDD,G=$4,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $11 (S=$8,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $12 (S=VDD,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $13 (S=$2,G='IN,FB',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $14 (S=VDD,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $15 (S=$6,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $16 (S=VDD,G=$6,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $17 (S=$18,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $18 (S=VDD,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DPMOS $19 (S=$10,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $20 (S=VDD,G=$10,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $21 (S='IN,FB',G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $22 (S=VSS,G='IN,FB',D='OUT,OSC') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $23 (S=$18,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $24 (S=VSS,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $25 (S=$14,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $26 (S=VSS,G=$14,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $27 (S=$12,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $28 (S=VSS,G=$12,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $29 (S=$4,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $30 (S=VSS,G=$4,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $31 (S=$2,G='IN,FB',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $32 (S=VSS,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $33 (S=$8,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $34 (S=VSS,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $35 (S=$6,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $36 (S=VSS,G=$6,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $37 (S=$16,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $38 (S=VSS,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $39 (S=$10,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $40 (S=VSS,G=$10,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_flat.gds"); + + db::compare_layouts (_this, ly, au); +} + +static bool +all_net_names_unique (const db::Circuit &c) +{ + std::set names; + for (db::Circuit::const_net_iterator n = c.begin_nets (); n != c.end_nets (); ++n) { + if (! n->name ().empty ()) { + if (names.find (n->name ()) != names.end ()) { + return false; + } else { + names.insert (n->name ()); + } + } + } + return true; +} + +static bool +all_net_names_unique (const db::Netlist &nl) +{ + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + if (! all_net_names_unique (*c)) { + return false; + } + } + return true; +} + +TEST(3_DeviceAndNetExtractionWithImplicitConnections) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1_implicit_nets.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + db::DeepShapeStore dss; + dss.set_text_enlargement (1); + dss.set_text_property_name (tl::Variant ("LABEL")); + + // original layers + db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss); + db::Region ractive (db::RecursiveShapeIterator (ly, tc, active), dss); + db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss); + db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss); + db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss); + db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss); + db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss); + db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss); + db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss); + db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss); + db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss); + + // derived regions + + db::Region rpactive = ractive & rnwell; + db::Region rpgate = rpactive & rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = ractive - rnwell; + db::Region rngate = rnactive & rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + + // perform the extraction + + db::Netlist nl; + db::hier_clusters cl; + + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + pmos_ex.extract (dss, 0, dl, nl, cl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + nmos_ex.extract (dss, 0, dl, nl, cl); + + // perform the net extraction + + db::NetlistExtractor net_ex; + + db::Connectivity conn; + // Intra-layer + conn.connect (rpsd); + conn.connect (rnsd); + conn.connect (rpoly); + conn.connect (rdiff_cont); + conn.connect (rpoly_cont); + conn.connect (rmetal1); + conn.connect (rvia1); + conn.connect (rmetal2); + // Inter-layer + conn.connect (rpsd, rdiff_cont); + conn.connect (rnsd, rdiff_cont); + conn.connect (rpoly, rpoly_cont); + conn.connect (rpoly_cont, rmetal1); + conn.connect (rdiff_cont, rmetal1); + conn.connect (rmetal1, rvia1); + conn.connect (rvia1, rmetal2); + conn.connect (rpoly, rpoly_lbl); // attaches labels + conn.connect (rmetal1, rmetal1_lbl); // attaches labels + conn.connect (rmetal2, rmetal2_lbl); // attaches labels + + // extract the nets + + net_ex.extract_nets (dss, 0, conn, nl, cl); + + EXPECT_EQ (all_net_names_unique (nl), true); + + // debug layers produced for nets + // 202/0 -> Active + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + std::map dump_map; + dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [layer_of (rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ()); + dump_nets_to_layout (nl, cl, ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (nl.to_string (), + "Circuit RINGO ():\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $3 (IN=NEXT,$2=$I43,OUT=$I5,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $4 (IN=$I3,$2=$I42,OUT=NEXT,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $5 (IN=$I5,$2=$I44,OUT=$I6,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $6 (IN=$I6,$2=$I45,OUT=$I7,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $7 (IN=$I7,$2=$I46,OUT=$I8,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $8 (IN=$I19,$2=$I39,OUT=$I1,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $9 (IN=$I1,$2=$I40,OUT=$I2,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $10 (IN=$I2,$2=$I41,OUT=$I3,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" + " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" + " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" + " XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // doesn't do anything here, but we test that this does not destroy anything: + nl.combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + nl.make_top_level_pins (); + nl.purge (); + + // compare netlist as string + EXPECT_EQ (nl.to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,NEXT=NEXT,'VSSZ,VSS'='VSSZ,VSS','VDDZ,VDD'='VDDZ,VDD'):\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $3 (IN=NEXT,$2=(null),OUT=$I5,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $4 (IN=$I3,$2=(null),OUT=NEXT,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $5 (IN=$I5,$2=(null),OUT=$I6,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $6 (IN=$I6,$2=(null),OUT=$I7,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $7 (IN=$I7,$2=(null),OUT=$I8,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $8 (IN=$I19,$2=(null),OUT=$I1,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $9 (IN=$I1,$2=(null),OUT=$I2,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + " XINV2 $10 (IN=$I2,$2=(null),OUT=$I3,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_implicit_nets.gds"); + + db::compare_layouts (_this, ly, au); +} + diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc new file mode 100644 index 000000000..4d85928cd --- /dev/null +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -0,0 +1,1081 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbNetlist.h" + +#include "tlUnitTest.h" +#include "tlString.h" + +#include + +// ---------------------------------------------------------------------------------------- + +static std::string pd2string (const db::DeviceTerminalDefinition &pd) +{ + return pd.name () + "(" + pd.description () + ") #" + tl::to_string (pd.id ()); +} + +static std::string pd2string (const db::DeviceParameterDefinition &pd) +{ + return pd.name () + "(" + pd.description () + ")=" + tl::to_string (pd.default_value ()) + " #" + tl::to_string (pd.id ()); +} + +static std::string pins2string (const db::Circuit &c) +{ + std::string res; + for (db::Circuit::const_pin_iterator i = c.begin_pins (); i != c.end_pins (); ++i) { + if (!res.empty ()) { + res += ","; + } + res += i->name (); + res += "#" + tl::to_string (i->id ()); + } + return res; +} + +static std::string net2string (const db::Net &n) +{ + std::string res; + for (db::Net::const_terminal_iterator i = n.begin_terminals (); i != n.end_terminals (); ++i) { + if (! res.empty ()) { + res += ","; + } + res += i->device () ? i->device ()->name () : "(null)"; + res += ":"; + res += i->terminal_def () ? i->terminal_def ()->name () : "(null)"; + } + for (db::Net::const_pin_iterator i = n.begin_pins (); i != n.end_pins (); ++i) { + if (! res.empty ()) { + res += ","; + } + res += "+"; + res += i->pin () ? i->pin ()->name () : "(null)"; + } + for (db::Net::const_subcircuit_pin_iterator i = n.begin_subcircuit_pins (); i != n.end_subcircuit_pins (); ++i) { + if (! res.empty ()) { + res += ","; + } + res += i->subcircuit ()->circuit_ref () ? i->subcircuit ()->circuit_ref ()->name () : "(null)"; + res += ":"; + res += i->pin () ? i->pin ()->name () : "(null)"; + } + return res; +} + +static std::string nets2string (const db::Circuit &c) +{ + std::string res; + for (db::Circuit::const_net_iterator n = c.begin_nets (); n != c.end_nets (); ++n) { + res += net2string (*n); + res += "\n"; + } + return res; +} + +// dual form of netlist +static std::string netlist2 (const db::Circuit &c) +{ + std::string res; + + std::string pins; + for (db::Circuit::const_pin_iterator p = c.begin_pins (); p != c.end_pins (); ++p) { + if (! pins.empty ()) { + pins += ","; + } + const db::Net *net = c.net_for_pin (p->id ()); + pins += p->name (); + pins += "="; + pins += net ? net->name () : std::string ("(null)"); + } + + res += c.name () + ":" + pins + "\n"; + + for (db::Circuit::const_device_iterator d = c.begin_devices (); d != c.end_devices (); ++d) { + if (! d->device_class ()) { + continue; + } + pins.clear (); + for (size_t i = 0; i < d->device_class ()->terminal_definitions ().size (); ++i) { + if (! pins.empty ()) { + pins += ","; + } + const db::Net *net = d->net_for_terminal (i); + pins += d->device_class ()->terminal_definitions () [i].name (); + pins += "="; + pins += net ? net->name () : std::string ("(null)"); + } + res += " D" + d->name (); + if (d->device_abstract ()) { + res += "/" + d->device_abstract ()->name (); + } + res += ":" + pins + "\n"; + } + + for (db::Circuit::const_subcircuit_iterator s = c.begin_subcircuits (); s != c.end_subcircuits (); ++s) { + if (! s->circuit_ref ()) { + continue; + } + pins.clear (); + for (size_t i = 0; i < s->circuit_ref ()->pin_count (); ++i) { + if (! pins.empty ()) { + pins += ","; + } + pins += s->circuit_ref ()->pin_by_id (i)->name (); + pins += "="; + const db::Net *net = s->net_for_pin (i); + pins += net ? net->name () : std::string ("(null)"); + } + res += " X" + s->name () + ":" + pins + "\n"; + } + + return res; +} + +static std::string nl2string (const db::Netlist &nl) +{ + std::string res; + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + res += "[" + c->name () + "]\n"; + res += nets2string (*c); + } + return res; +} + +// dual form of netlist +static std::string netlist2 (const db::Netlist &nl) +{ + std::string res; + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + res += netlist2 (*c); + } + return res; +} + +static std::string refs2string (const db::Circuit *c) +{ + std::string res; + for (db::Circuit::const_refs_iterator r = c->begin_refs (); r != c->end_refs (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += r->name (); + } + return res; +} + +static std::string children2string (const db::Circuit *c) +{ + std::string res; + for (db::Circuit::const_child_circuit_iterator r = c->begin_children (); r != c->end_children (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += (*r)->name (); + } + return res; +} + +static std::string parents2string (const db::Circuit *c) +{ + std::string res; + for (db::Circuit::const_parent_circuit_iterator r = c->begin_parents (); r != c->end_parents (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += (*r)->name (); + } + return res; +} + +static std::string td2string (const db::Netlist *nl) +{ + std::string res; + for (db::Netlist::const_top_down_circuit_iterator r = nl->begin_top_down (); r != nl->end_top_down (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += (*r)->name (); + } + return res; +} + +static std::string bu2string (const db::Netlist *nl) +{ + std::string res; + for (db::Netlist::const_bottom_up_circuit_iterator r = nl->begin_bottom_up (); r != nl->end_bottom_up (); ++r) { + if (!res.empty ()) { + res += ","; + } + res += (*r)->name (); + } + return res; +} + +// ---------------------------------------------------------------------------------------- + +TEST(1_DeviceTerminalDefinition) +{ + db::DeviceTerminalDefinition pd; + + EXPECT_EQ (pd2string (pd), "() #0"); + pd.set_name ("name"); + pd.set_description ("nothing yet"); + EXPECT_EQ (pd2string (pd), "name(nothing yet) #0"); + + db::DeviceTerminalDefinition pd2; + pd2 = pd; + EXPECT_EQ (pd2string (pd2), "name(nothing yet) #0"); + pd2.set_name ("name2"); + pd2.set_description ("now it has something"); + EXPECT_EQ (pd2string (pd2), "name2(now it has something) #0"); + + db::DeviceClass dc; + dc.add_terminal_definition (pd); + dc.add_terminal_definition (pd2); + EXPECT_EQ (pd2string (dc.terminal_definitions ()[0]), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (dc.terminal_definitions ()[1]), "name2(now it has something) #1"); + + dc.clear_terminal_definitions (); + EXPECT_EQ (dc.terminal_definitions ().empty (), true); + + db::DeviceParameterDefinition ppd ("P1", "Parameter 1", 1.0); + dc.add_parameter_definition (ppd); + + db::DeviceParameterDefinition ppd2 ("P2", "Parameter 2"); + dc.add_parameter_definition (ppd2); + + EXPECT_EQ (pd2string (dc.parameter_definitions ()[0]), "P1(Parameter 1)=1 #0"); + EXPECT_EQ (pd2string (dc.parameter_definitions ()[1]), "P2(Parameter 2)=0 #1"); + + dc.clear_parameter_definitions (); + EXPECT_EQ (dc.parameter_definitions ().empty (), true); +} + +TEST(2_DeviceClass) +{ + db::DeviceTerminalDefinition pd; + pd.set_name ("name"); + pd.set_description ("nothing yet"); + + db::DeviceTerminalDefinition pd2; + pd2.set_name ("name2"); + pd2.set_description ("now it has something"); + + db::DeviceClass dc; + dc.set_name ("devname"); + dc.set_description ("devdesc"); + EXPECT_EQ (dc.name (), "devname"); + EXPECT_EQ (dc.description (), "devdesc"); + dc.add_terminal_definition (pd); + dc.add_terminal_definition (pd2); + EXPECT_EQ (dc.terminal_definitions ().size (), size_t (2)); + EXPECT_EQ (pd2string (dc.terminal_definitions ()[0]), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (dc.terminal_definitions ()[1]), "name2(now it has something) #1"); + + EXPECT_EQ (pd2string (*dc.terminal_definition (dc.terminal_definitions ()[0].id ())), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (*dc.terminal_definition (dc.terminal_definitions ()[1].id ())), "name2(now it has something) #1"); + EXPECT_EQ (dc.terminal_definition (3), 0); + + db::DeviceClass dc2 = dc; + EXPECT_EQ (dc2.name (), "devname"); + EXPECT_EQ (dc2.description (), "devdesc"); + EXPECT_EQ (dc2.terminal_definitions ().size (), size_t (2)); + EXPECT_EQ (pd2string (*dc2.terminal_definition (dc2.terminal_definitions ()[0].id ())), "name(nothing yet) #0"); + EXPECT_EQ (pd2string (*dc2.terminal_definition (dc2.terminal_definitions ()[1].id ())), "name2(now it has something) #1"); + EXPECT_EQ (dc2.terminal_definition (3), 0); +} + +TEST(3_CircuitBasic) +{ + db::Circuit c; + c.set_name ("name"); + EXPECT_EQ (c.name (), "name"); + + db::Pin p1 = c.add_pin ("p1"); + db::Pin p2 = c.add_pin ("p2"); + EXPECT_EQ (pins2string (c), "p1#0,p2#1"); + + EXPECT_EQ (c.pin_by_id (0)->name (), "p1"); + EXPECT_EQ (c.pin_by_id (1)->name (), "p2"); + EXPECT_EQ (c.pin_by_id (2), 0); + EXPECT_EQ (c.pin_by_name ("p1")->name (), "p1"); + EXPECT_EQ (c.pin_by_name ("doesnt_exist") == 0, true); + EXPECT_EQ (c.pin_by_name ("p2")->name (), "p2"); + + db::Circuit c2 = c; + EXPECT_EQ (c2.name (), "name"); + EXPECT_EQ (pins2string (c), "p1#0,p2#1"); + + EXPECT_EQ (c2.pin_by_id (0)->name (), "p1"); + EXPECT_EQ (c2.pin_by_id (1)->name (), "p2"); + EXPECT_EQ (c2.pin_by_id (2), 0); +} + +TEST(4_CircuitDevices) +{ + db::DeviceClass dc1; + dc1.set_name ("dc1"); + dc1.add_terminal_definition (db::DeviceTerminalDefinition ("S", "Source")); + dc1.add_terminal_definition (db::DeviceTerminalDefinition ("G", "Gate")); + dc1.add_terminal_definition (db::DeviceTerminalDefinition ("D", "Drain")); + dc1.add_parameter_definition (db::DeviceParameterDefinition ("U", "", 1.0)); + dc1.add_parameter_definition (db::DeviceParameterDefinition ("V", "", 2.0)); + + db::DeviceClass dc2; + dc2.set_name ("dc2"); + dc2.add_terminal_definition (db::DeviceTerminalDefinition ("A", "")); + dc2.add_terminal_definition (db::DeviceTerminalDefinition ("B", "")); + dc2.add_parameter_definition (db::DeviceParameterDefinition ("U", "", 2.0)); + dc2.add_parameter_definition (db::DeviceParameterDefinition ("V", "", 1.0)); + + std::auto_ptr c (new db::Circuit ()); + c->set_name ("c"); + + EXPECT_EQ (netlist2 (*c), + "c:\n" + ); + + db::Device *dd = new db::Device (&dc1, "dd"); + db::Device *d1 = new db::Device (&dc1, "d1"); + db::Device *d2a = new db::Device (&dc2, "d2a"); + db::Device *d2b = new db::Device (&dc2, "d2x"); + + EXPECT_EQ (d1->circuit () == 0, true); + + c->add_device (d1); + EXPECT_EQ (d1->circuit () == c.get (), true); + EXPECT_EQ (d1->id (), size_t (1)); + EXPECT_EQ (c->device_by_id (d1->id ()) == d1, true); + EXPECT_EQ (c->device_by_name (d1->name ()) == d1, true); + + c->add_device (dd); + EXPECT_EQ (dd->id (), size_t (2)); + EXPECT_EQ (c->device_by_id (dd->id ()) == dd, true); + EXPECT_EQ (c->device_by_name (dd->name ()) == dd, true); + + c->add_device (d2a); + EXPECT_EQ (d2a->id (), size_t (3)); + EXPECT_EQ (c->device_by_id (d2a->id ()) == d2a, true); + EXPECT_EQ (c->device_by_name (d2a->name ()) == d2a, true); + + c->add_device (d2b); + EXPECT_EQ (d2b->id (), size_t (4)); + EXPECT_EQ (c->device_by_id (d2b->id ()) == d2b, true); + EXPECT_EQ (c->device_by_name (d2b->name ()) == d2b, true); + + d2b->set_name ("d2b"); + EXPECT_EQ (c->device_by_id (d2b->id ()) == d2b, true); + EXPECT_EQ (c->device_by_name (d2b->name ()) == d2b, true); + EXPECT_EQ (c->device_by_name ("d2x") == 0, true); + + c->remove_device (dd); + dd = 0; + EXPECT_EQ (c->device_by_id (d2a->id ()) == d2a, true); + EXPECT_EQ (c->device_by_id (2) == 0, true); + EXPECT_EQ (c->device_by_name (d2a->name ()) == d2a, true); + EXPECT_EQ (c->device_by_name ("doesnt_exist") == 0, true); + + EXPECT_EQ (d1->parameter_value (0), 1.0); + EXPECT_EQ (d1->parameter_value (1), 2.0); + EXPECT_EQ (d2a->parameter_value (0), 2.0); + EXPECT_EQ (d2a->parameter_value (1), 1.0); + d1->set_parameter_value (1, 1.5); + EXPECT_EQ (d1->parameter_value (0), 1.0); + EXPECT_EQ (d1->parameter_value (1), 1.5); + d1->set_parameter_value (0, 0.5); + EXPECT_EQ (d1->parameter_value (0), 0.5); + EXPECT_EQ (d1->parameter_value (1), 1.5); + + d2a->set_parameter_value (0, -1.0); + EXPECT_EQ (d2a->parameter_value (0), -1.0); + EXPECT_EQ (d2a->parameter_value (1), 1.0); + + EXPECT_EQ (netlist2 (*c), + "c:\n" + " Dd1:S=(null),G=(null),D=(null)\n" + " Dd2a:A=(null),B=(null)\n" + " Dd2b:A=(null),B=(null)\n" + ); + + db::Net *n1 = new db::Net (); + n1->set_name ("n1"); + n1->set_cluster_id (41); + EXPECT_EQ (n1->circuit (), 0); + c->add_net (n1); + n1->add_terminal (db::NetTerminalRef (d1, 0)); + n1->add_terminal (db::NetTerminalRef (d2a, 0)); + EXPECT_EQ (n1->circuit (), c.get ()); + EXPECT_EQ (c->net_by_cluster_id (17) == 0, true); + EXPECT_EQ (c->net_by_cluster_id (41) == n1, true); + EXPECT_EQ (c->net_by_name ("doesnt_exist") == 0, true); + EXPECT_EQ (c->net_by_name ("n1") == n1, true); + + db::Net *n2 = new db::Net (); + n2->set_name ("n2x"); + n2->set_cluster_id (17); + c->add_net (n2); + n2->add_terminal (db::NetTerminalRef (d1, 1)); + n2->add_terminal (db::NetTerminalRef (d2a, 1)); + n2->add_terminal (db::NetTerminalRef (d2b, 0)); + EXPECT_EQ (c->net_by_cluster_id (17) == n2, true); + EXPECT_EQ (c->net_by_name ("n2x") == n2, true); + + n2->set_name ("n2"); + n2->set_cluster_id (42); + EXPECT_EQ (c->net_by_cluster_id (17) == 0, true); + EXPECT_EQ (c->net_by_name ("n2x") == 0, true); + EXPECT_EQ (c->net_by_cluster_id (42) == n2, true); + EXPECT_EQ (c->net_by_name ("n2") == n2, true); + + EXPECT_EQ (netlist2 (*c), + "c:\n" + " Dd1:S=n1,G=n2,D=(null)\n" + " Dd2a:A=n1,B=n2\n" + " Dd2b:A=n2,B=(null)\n" + ); + + db::Net *n3 = new db::Net (); + n3->set_name ("n3"); + c->add_net (n3); + n3->add_terminal (db::NetTerminalRef (d1, 2)); + n3->add_terminal (db::NetTerminalRef (d2b, 1)); + + EXPECT_EQ (nets2string (*c), + "d1:S,d2a:A\n" + "d1:G,d2a:B,d2b:A\n" + "d1:D,d2b:B\n" + ); + + EXPECT_EQ (netlist2 (*c), + "c:\n" + " Dd1:S=n1,G=n2,D=n3\n" + " Dd2a:A=n1,B=n2\n" + " Dd2b:A=n2,B=n3\n" + ); + + db::Circuit cc = *c; + c.reset (0); + EXPECT_EQ (cc.begin_nets ()->circuit (), &cc); + + EXPECT_EQ (nets2string (cc), + "d1:S,d2a:A\n" + "d1:G,d2a:B,d2b:A\n" + "d1:D,d2b:B\n" + ); + + EXPECT_EQ (netlist2 (cc), + "c:\n" + " Dd1:S=n1,G=n2,D=n3\n" + " Dd2a:A=n1,B=n2\n" + " Dd2b:A=n2,B=n3\n" + ); +} + +TEST(4_NetlistSubcircuits) +{ + std::auto_ptr nl (new db::Netlist ()); + + db::DeviceClass *dc = new db::DeviceClass (); + dc->set_name ("dc2"); + dc->add_terminal_definition (db::DeviceTerminalDefinition ("A", "")); + dc->add_terminal_definition (db::DeviceTerminalDefinition ("B", "")); + nl->add_device_class (dc); + + db::DeviceAbstract *dm = new db::DeviceAbstract (); + dm->set_device_class (dc); + EXPECT_EQ (dm->device_class () == dc, true); + dm->set_name ("dm2"); + dm->set_cell_index (42); + dm->set_cluster_id_for_terminal (0, 17); + nl->add_device_abstract (dm); + + db::Circuit *c1 = new db::Circuit (); + c1->set_cell_index (17); + EXPECT_EQ (c1->netlist (), 0); + c1->set_name ("c1"); + c1->add_pin ("c1p1"); + c1->add_pin ("c1p2"); + nl->add_circuit (c1); + EXPECT_EQ (c1->netlist (), nl.get ()); + EXPECT_EQ (nl->circuit_by_name ("c1") == c1, true); + EXPECT_EQ (nl->circuit_by_name ("doesnt_exist") == 0, true); + EXPECT_EQ (nl->circuit_by_cell_index (17) == c1, true); + EXPECT_EQ (nl->circuit_by_cell_index (42) == 0, true); + + db::Circuit *c2 = new db::Circuit (); + c2->set_name ("c2x"); + c2->add_pin ("c2p1"); + c2->add_pin ("c2p2"); + c2->set_cell_index (41); + nl->add_circuit (c2); + EXPECT_EQ (nl->circuit_by_name ("c2x") == c2, true); + EXPECT_EQ (nl->circuit_by_cell_index (41) == c2, true); + + c2->set_name ("c2"); + EXPECT_EQ (nl->circuit_by_name ("c2x") == 0, true); + EXPECT_EQ (nl->circuit_by_name ("c2") == c2, true); + c2->set_cell_index (42); + EXPECT_EQ (nl->circuit_by_cell_index (41) == 0, true); + EXPECT_EQ (nl->circuit_by_cell_index (42) == c2, true); + + db::Device *d = new db::Device (dc, dm, "D"); + c2->add_device (d); + EXPECT_EQ (d->device_abstract ()->name (), "dm2"); + + EXPECT_EQ (refs2string (c2), ""); + db::SubCircuit *sc1 = new db::SubCircuit (c2); + sc1->set_name ("sc1"); + EXPECT_EQ (refs2string (c2), "sc1"); + EXPECT_EQ (sc1->circuit () == 0, true); + c1->add_subcircuit (sc1); + EXPECT_EQ (sc1->circuit () == c1, true); + EXPECT_EQ (sc1->id (), size_t (1)); + EXPECT_EQ (c1->subcircuit_by_id (sc1->id ()) == sc1, true); + EXPECT_EQ (c1->subcircuit_by_id (2) == 0, true); + EXPECT_EQ (c1->subcircuit_by_name (sc1->name ()) == sc1, true); + EXPECT_EQ (c1->subcircuit_by_name ("doesnt_exist") == 0, true); + + db::SubCircuit *sc2 = new db::SubCircuit (c2); + sc2->set_name ("scx"); + EXPECT_EQ (refs2string (c2), "sc1,scx"); + c1->add_subcircuit (sc2); + EXPECT_EQ (sc2->id (), size_t (2)); + EXPECT_EQ (c1->subcircuit_by_id (sc2->id ()) == sc2, true); + EXPECT_EQ (c1->subcircuit_by_name (sc2->name ()) == sc2, true); + + sc2->set_name ("sc2"); + EXPECT_EQ (refs2string (c2), "sc1,sc2"); + EXPECT_EQ (c1->subcircuit_by_id (sc2->id ()) == sc2, true); + EXPECT_EQ (c1->subcircuit_by_name (sc2->name ()) == sc2, true); + EXPECT_EQ (c1->subcircuit_by_name ("scx") == 0, true); + + db::Net *n2a = new db::Net (); + c2->add_net (n2a); + n2a->set_name ("n2a"); + n2a->add_pin (db::NetPinRef (0)); + n2a->add_terminal (db::NetTerminalRef (d, 0)); + + db::Net *n2b = new db::Net (); + c2->add_net (n2b); + n2b->set_name ("n2b"); + n2b->add_terminal (db::NetTerminalRef (d, 1)); + n2b->add_pin (db::NetPinRef (1)); + + db::Net *n1a = new db::Net (); + c1->add_net (n1a); + n1a->set_name ("n1a"); + n1a->add_pin (db::NetPinRef (0)); + n1a->add_subcircuit_pin (db::NetSubcircuitPinRef (sc1, 0)); + + db::Net *n1b = new db::Net (); + c1->add_net (n1b); + n1b->set_name ("n1b"); + n1b->add_subcircuit_pin (db::NetSubcircuitPinRef (sc1, 1)); + n1b->add_subcircuit_pin (db::NetSubcircuitPinRef (sc2, 0)); + + db::Net *n1c = new db::Net (); + c1->add_net (n1c); + n1c->set_name ("n1c"); + n1c->add_subcircuit_pin (db::NetSubcircuitPinRef (sc2, 1)); + n1c->add_pin (db::NetPinRef (1)); + + EXPECT_EQ (nl2string (*nl), + "[c1]\n" + "+c1p1,c2:c2p1\n" + "c2:c2p2,c2:c2p1\n" + "+c1p2,c2:c2p2\n" + "[c2]\n" + "D:A,+c2p1\n" + "D:B,+c2p2\n" + ); + + EXPECT_EQ (netlist2 (*nl), + "c1:c1p1=n1a,c1p2=n1c\n" + " Xsc1:c2p1=n1a,c2p2=n1b\n" + " Xsc2:c2p1=n1b,c2p2=n1c\n" + "c2:c2p1=n2a,c2p2=n2b\n" + " DD/dm2:A=n2a,B=n2b\n" + ); + + // check netlist + for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) { + for (db::Circuit::net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + for (db::Net::terminal_iterator i = n->begin_terminals (); i != n->end_terminals (); ++i) { + EXPECT_EQ (i->net (), n.operator-> ()); + } + for (db::Net::pin_iterator i = n->begin_pins (); i != n->end_pins (); ++i) { + EXPECT_EQ (i->net (), n.operator-> ()); + } + } + } + + db::Netlist nl2 = *nl; + nl.reset (0); + + EXPECT_EQ (nl2.begin_circuits ()->netlist (), &nl2); + + EXPECT_EQ (nl2string (nl2), + "[c1]\n" + "+c1p1,c2:c2p1\n" + "c2:c2p2,c2:c2p1\n" + "+c1p2,c2:c2p2\n" + "[c2]\n" + "D:A,+c2p1\n" + "D:B,+c2p2\n" + ); + + EXPECT_EQ (netlist2 (nl2), + "c1:c1p1=n1a,c1p2=n1c\n" + " Xsc1:c2p1=n1a,c2p2=n1b\n" + " Xsc2:c2p1=n1b,c2p2=n1c\n" + "c2:c2p1=n2a,c2p2=n2b\n" + " DD/dm2:A=n2a,B=n2b\n" + ); + + // check netlist + for (db::Netlist::circuit_iterator c = nl2.begin_circuits (); c != nl2.end_circuits (); ++c) { + for (db::Circuit::net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + for (db::Net::terminal_iterator i = n->begin_terminals (); i != n->end_terminals (); ++i) { + EXPECT_EQ (i->net (), n.operator-> ()); + } + for (db::Net::pin_iterator i = n->begin_pins (); i != n->end_pins (); ++i) { + EXPECT_EQ (i->net (), n.operator-> ()); + } + } + } +} + +TEST(5_SubCircuit) +{ + db::SubCircuit sc, sc2; + + sc.set_name ("sc"); + EXPECT_EQ (sc.name (), "sc"); + sc.set_trans (db::DCplxTrans (2.5)); + EXPECT_EQ (sc.trans ().to_string (), "r0 *2.5 0,0"); + + sc2 = sc; + EXPECT_EQ (sc2.name (), "sc"); + EXPECT_EQ (sc2.trans ().to_string (), "r0 *2.5 0,0"); +} + +TEST(6_Net) +{ + db::Net n, n2; + + n.set_name ("n"); + EXPECT_EQ (n.name (), "n"); + n.set_cluster_id (17); + EXPECT_EQ (int (n.cluster_id ()), 17); + + n2 = n; + EXPECT_EQ (n2.name (), "n"); + EXPECT_EQ (int (n2.cluster_id ()), 17); + EXPECT_EQ (n2.expanded_name (), "n"); + n2.set_name (""); + EXPECT_EQ (n2.expanded_name (), "$17"); + n2.set_cluster_id (std::numeric_limits::max () - 2); + EXPECT_EQ (n2.expanded_name (), "$I3"); + + n.clear (); + EXPECT_EQ (n.name (), ""); + EXPECT_EQ (int (n.cluster_id ()), 0); + + EXPECT_EQ (n.pin_count (), size_t (0)); + EXPECT_EQ (n.terminal_count (), size_t (0)); + EXPECT_EQ (n.is_floating (), true); + EXPECT_EQ (n.is_internal (), false); +} + +TEST(7_NetTerminalsEditing) +{ + db::Circuit c; + db::DeviceClass dc; + dc.add_terminal_definition (db::DeviceTerminalDefinition ("A", "")); + dc.add_terminal_definition (db::DeviceTerminalDefinition ("B", "")); + + db::Device *d1 = new db::Device (&dc, "D1"); + c.add_device (d1); + db::Device *d2 = new db::Device (&dc, "D2"); + c.add_device (d2); + + db::Net *n1 = new db::Net (); + n1->set_name ("n1"); + c.add_net (n1); + + db::Net *n2 = new db::Net (); + n2->set_name ("n2"); + c.add_net (n2); + + d1->connect_terminal (0, n1); + d1->connect_terminal (1, n2); + + EXPECT_EQ (n1->terminal_count (), size_t (1)); + EXPECT_EQ (n1->pin_count (), size_t (0)); + EXPECT_EQ (n1->is_floating (), true); + EXPECT_EQ (n1->is_internal (), false); + + d2->connect_terminal (1, n1); + d2->connect_terminal (0, n2); + + EXPECT_EQ (n1->terminal_count (), size_t (2)); + EXPECT_EQ (n1->pin_count (), size_t (0)); + EXPECT_EQ (n1->is_floating (), false); + EXPECT_EQ (n1->is_internal (), true); + + EXPECT_EQ (d1->net_for_terminal (0), n1); + EXPECT_EQ (d1->net_for_terminal (1), n2); + EXPECT_EQ (d2->net_for_terminal (0), n2); + EXPECT_EQ (d2->net_for_terminal (1), n1); + + EXPECT_EQ (net2string (*n1), "D1:A,D2:B"); + EXPECT_EQ (net2string (*n2), "D1:B,D2:A"); + + d1->connect_terminal (0, n2); + d1->connect_terminal (1, n1); + + EXPECT_EQ (d1->net_for_terminal (0), n2); + EXPECT_EQ (d1->net_for_terminal (1), n1); + + EXPECT_EQ (net2string (*n1), "D2:B,D1:B"); + EXPECT_EQ (net2string (*n2), "D2:A,D1:A"); + + d1->connect_terminal (0, 0); + EXPECT_EQ (d1->net_for_terminal (0), 0); + + EXPECT_EQ (net2string (*n1), "D2:B,D1:B"); + EXPECT_EQ (net2string (*n2), "D2:A"); + + delete d1; + d1 = 0; + + EXPECT_EQ (c.begin_devices ()->name (), "D2"); + EXPECT_EQ (++c.begin_devices () == c.end_devices (), true); + + EXPECT_EQ (net2string (*n1), "D2:B"); + EXPECT_EQ (net2string (*n2), "D2:A"); + + delete n1; + n1 = 0; + + EXPECT_EQ (c.begin_nets ()->name (), "n2"); + EXPECT_EQ (++c.begin_nets () == c.end_nets (), true); + + EXPECT_EQ (net2string (*n2), "D2:A"); + + EXPECT_EQ (d2->net_for_terminal (0), n2); + EXPECT_EQ (d2->net_for_terminal (1), 0); +} + +TEST(8_NetSubCircuitsEditing) +{ + db::Circuit c; + c.set_name ("c"); + c.add_pin ("X"); + c.add_pin ("Y"); + + db::Circuit cc1; + cc1.set_name ("sc1"); + cc1.add_pin ("A"); + cc1.add_pin ("B"); + + db::Circuit cc2; + cc2.set_name ("sc2"); + cc2.add_pin ("A"); + cc2.add_pin ("B"); + + db::SubCircuit *sc1 = new db::SubCircuit (&cc1, "sc1"); + c.add_subcircuit (sc1); + + db::SubCircuit *sc2 = new db::SubCircuit (&cc2, "sc2"); + c.add_subcircuit (sc2); + + db::Net *n1 = new db::Net (); + n1->set_name ("n1"); + c.add_net (n1); + + db::Net *n2 = new db::Net (); + n2->set_name ("n2"); + c.add_net (n2); + + EXPECT_EQ (n1->pin_count (), size_t (0)); + c.connect_pin (0, n1); + + EXPECT_EQ (n1->terminal_count (), size_t (0)); + EXPECT_EQ (n1->pin_count (), size_t (1)); + EXPECT_EQ (n1->is_floating (), true); + EXPECT_EQ (n1->is_internal (), false); + EXPECT_NE (n1->pin_count (), size_t (0)); + + EXPECT_EQ (c.net_for_pin (0), n1); + EXPECT_EQ (c.net_for_pin (1), 0); + + sc1->connect_pin (0, n1); + sc1->connect_pin (1, n2); + EXPECT_EQ (n2->pin_count (), size_t (0)); + + EXPECT_EQ (n1->terminal_count (), size_t (0)); + EXPECT_EQ (n1->pin_count (), size_t (1)); + EXPECT_EQ (n1->subcircuit_pin_count (), size_t (1)); + EXPECT_EQ (n1->is_floating (), false); + EXPECT_EQ (n1->is_internal (), false); + + sc2->connect_pin (1, n1); + sc2->connect_pin (0, n2); + + EXPECT_EQ (sc1->net_for_pin (0), n1); + EXPECT_EQ (sc1->net_for_pin (1), n2); + EXPECT_EQ (sc2->net_for_pin (0), n2); + EXPECT_EQ (sc2->net_for_pin (1), n1); + + EXPECT_EQ (net2string (*n1), "+X,sc1:A,sc2:B"); + EXPECT_EQ (net2string (*n2), "sc1:B,sc2:A"); + + c.connect_pin (0, 0); + EXPECT_EQ (c.net_for_pin (0), 0); + + EXPECT_EQ (net2string (*n1), "sc1:A,sc2:B"); + EXPECT_EQ (net2string (*n2), "sc1:B,sc2:A"); + + sc1->connect_pin (0, n2); + sc1->connect_pin (1, n1); + + EXPECT_EQ (sc1->net_for_pin (0), n2); + EXPECT_EQ (sc1->net_for_pin (1), n1); + + EXPECT_EQ (net2string (*n1), "sc2:B,sc1:B"); + EXPECT_EQ (net2string (*n2), "sc2:A,sc1:A"); + + sc1->connect_pin (0, 0); + EXPECT_EQ (sc1->net_for_pin (0), 0); + + EXPECT_EQ (net2string (*n1), "sc2:B,sc1:B"); + EXPECT_EQ (net2string (*n2), "sc2:A"); + + delete sc1; + sc1 = 0; + + EXPECT_EQ (c.begin_subcircuits ()->name (), "sc2"); + EXPECT_EQ (++c.begin_subcircuits () == c.end_subcircuits (), true); + + EXPECT_EQ (net2string (*n1), "sc2:B"); + EXPECT_EQ (net2string (*n2), "sc2:A"); + + c.connect_pin (1, n1); + EXPECT_EQ (net2string (*n1), "+Y,sc2:B"); + EXPECT_EQ (c.net_for_pin (1), n1); + + delete n1; + n1 = 0; + + EXPECT_EQ (c.net_for_pin (1), 0); + + EXPECT_EQ (c.begin_nets ()->name (), "n2"); + EXPECT_EQ (++c.begin_nets () == c.end_nets (), true); + + EXPECT_EQ (net2string (*n2), "sc2:A"); + + EXPECT_EQ (sc2->net_for_pin (0), n2); + EXPECT_EQ (sc2->net_for_pin (1), 0); +} + +TEST(9_NetTerminalRefBasics) +{ + db::Device d1, d2; + + EXPECT_EQ (db::NetTerminalRef (&d1, 0) == db::NetTerminalRef (&d1, 0), true); + EXPECT_EQ (db::NetTerminalRef (&d1, 0) == db::NetTerminalRef (&d1, 1), false); + EXPECT_EQ (db::NetTerminalRef (&d1, 0) == db::NetTerminalRef (&d2, 0), false); + + EXPECT_EQ (db::NetTerminalRef (&d1, 0) < db::NetTerminalRef (&d1, 0), false); + EXPECT_EQ (db::NetTerminalRef (&d1, 0) < db::NetTerminalRef (&d1, 1), true); + EXPECT_EQ (db::NetTerminalRef (&d1, 1) < db::NetTerminalRef (&d1, 0), false); + EXPECT_NE ((db::NetTerminalRef (&d1, 0) < db::NetTerminalRef (&d2, 0)), (db::NetTerminalRef (&d2, 0) < db::NetTerminalRef (&d1, 0))); +} + +TEST(10_NetPinRefBasics) +{ + db::SubCircuit d1, d2; + + EXPECT_EQ (db::NetSubcircuitPinRef (&d1, 0) == db::NetSubcircuitPinRef (&d1, 0), true); + EXPECT_EQ (db::NetSubcircuitPinRef (&d1, 0) == db::NetSubcircuitPinRef (&d1, 1), false); + EXPECT_EQ (db::NetSubcircuitPinRef (&d1, 0) == db::NetSubcircuitPinRef (&d2, 0), false); + + EXPECT_EQ (db::NetSubcircuitPinRef (&d1, 0) < db::NetSubcircuitPinRef (&d1, 0), false); + EXPECT_EQ (db::NetSubcircuitPinRef (&d1, 0) < db::NetSubcircuitPinRef (&d1, 1), true); + EXPECT_EQ (db::NetSubcircuitPinRef (&d1, 1) < db::NetSubcircuitPinRef (&d1, 0), false); + EXPECT_NE ((db::NetSubcircuitPinRef (&d1, 0) < db::NetSubcircuitPinRef (&d2, 0)), (db::NetSubcircuitPinRef (&d2, 0) < db::NetSubcircuitPinRef (&d1, 0))); +} + +TEST(11_NetlistCircuitRefs) +{ + std::auto_ptr nl (new db::Netlist ()); + + db::Circuit *c1 = new db::Circuit (); + c1->set_name ("c1"); + nl->add_circuit (c1); + + db::Circuit *c2 = new db::Circuit (); + c2->set_name ("c2"); + nl->add_circuit (c2); + + db::SubCircuit *sc1 = new db::SubCircuit (c2); + sc1->set_name ("sc1"); + EXPECT_EQ (refs2string (c2), "sc1"); + c1->add_subcircuit (sc1); + + db::SubCircuit *sc2 = new db::SubCircuit (c2); + sc2->set_name ("sc2"); + EXPECT_EQ (refs2string (c2), "sc1,sc2"); + c1->add_subcircuit (sc2); + + *sc2 = db::SubCircuit (); + EXPECT_EQ (refs2string (c2), "sc1"); + + db::SubCircuit sc3 (c2); + sc3.set_name ("sc3"); + *sc2 = sc3; + sc2->set_name ("sc2"); + EXPECT_EQ (refs2string (c2), "sc1,sc3,sc2"); + + sc3 = db::SubCircuit (); + EXPECT_EQ (refs2string (c2), "sc1,sc2"); +} + +TEST(12_NetlistTopology) +{ + std::auto_ptr nl (new db::Netlist ()); + EXPECT_EQ (nl->top_circuit_count (), size_t (0)); + + db::Circuit *c1 = new db::Circuit (); + c1->set_name ("c1"); + nl->add_circuit (c1); + EXPECT_EQ (nl->top_circuit_count (), size_t (1)); + EXPECT_EQ (td2string (nl.get ()), "c1"); + EXPECT_EQ (bu2string (nl.get ()), "c1"); + + db::Circuit *c2 = new db::Circuit (); + c2->set_name ("c2"); + nl->add_circuit (c2); + EXPECT_EQ (nl->top_circuit_count (), size_t (2)); + EXPECT_EQ (td2string (nl.get ()), "c1,c2"); + EXPECT_EQ (bu2string (nl.get ()), "c2,c1"); + + std::auto_ptr locker (new db::NetlistLocker (nl.get ())); + + db::Circuit *c3 = new db::Circuit (); + c3->set_name ("c3"); + nl->add_circuit (c3); + + // because we locked, it did not get updated: + EXPECT_EQ (nl->top_circuit_count (), size_t (2)); + EXPECT_EQ (td2string (nl.get ()), "c1,c2"); + EXPECT_EQ (bu2string (nl.get ()), "c2,c1"); + locker.reset (0); + + // after removing the lock, it's updated + EXPECT_EQ (nl->top_circuit_count (), size_t (3)); + EXPECT_EQ (td2string (nl.get ()), "c1,c2,c3"); + EXPECT_EQ (bu2string (nl.get ()), "c3,c2,c1"); + + db::SubCircuit *sc1 = new db::SubCircuit (c2); + sc1->set_name ("sc1"); + c1->add_subcircuit (sc1); + EXPECT_EQ (children2string (c1), "c2"); + EXPECT_EQ (parents2string (c2), "c1"); + EXPECT_EQ (nl->top_circuit_count (), size_t (2)); + EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2"); + EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1"); + + db::SubCircuit *sc2 = new db::SubCircuit (c2); + sc2->set_name ("sc2"); + c1->add_subcircuit (sc2); + EXPECT_EQ (children2string (c1), "c2"); + EXPECT_EQ (parents2string (c2), "c1"); + EXPECT_EQ (nl->top_circuit_count (), size_t (2)); + EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2"); + EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1"); + + db::SubCircuit *sc3 = new db::SubCircuit (c3); + sc3->set_name ("sc3"); + c1->add_subcircuit (sc3); + EXPECT_EQ (children2string (c1), "c2,c3"); + EXPECT_EQ (children2string (c2), ""); + EXPECT_EQ (children2string (c3), ""); + EXPECT_EQ (parents2string (c2), "c1"); + EXPECT_EQ (parents2string (c3), "c1"); + EXPECT_EQ (nl->top_circuit_count (), size_t (1)); + EXPECT_EQ (td2string (nl.get ()), "c1,c2,c3"); + EXPECT_EQ (bu2string (nl.get ()), "c3,c2,c1"); + + db::SubCircuit *sc4 = new db::SubCircuit (*sc2); + sc4->set_name ("sc4"); + c3->add_subcircuit (sc4); + + EXPECT_EQ (children2string (c1), "c2,c3"); + EXPECT_EQ (children2string (c2), ""); + EXPECT_EQ (children2string (c3), "c2"); + EXPECT_EQ (parents2string (c2), "c1,c3"); + EXPECT_EQ (parents2string (c3), "c1"); + EXPECT_EQ (nl->top_circuit_count (), size_t (1)); + EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2"); + EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1"); +} + +TEST(13_DeviceAbstract) +{ + db::Netlist nl; + + db::DeviceAbstract *dm = new db::DeviceAbstract (0, "name"); + nl.add_device_abstract (dm); + EXPECT_EQ (dm->netlist () == &nl, true); + + EXPECT_EQ (dm->device_class () == 0, true); + EXPECT_EQ (dm->name (), "name"); + EXPECT_EQ (nl.device_abstract_by_name ("name") == dm, true); + EXPECT_EQ (nl.device_abstract_by_name ("name2") == 0, true); + EXPECT_EQ (nl.device_abstract_by_name ("does_not_exist") == 0, true); + dm->set_name ("name2"); + EXPECT_EQ (dm->name (), "name2"); + EXPECT_EQ (nl.device_abstract_by_name ("name") == 0, true); + EXPECT_EQ (nl.device_abstract_by_name ("name2") == dm, true); + EXPECT_EQ (nl.device_abstract_by_name ("does_not_exist") == 0, true); + + dm->set_cluster_id_for_terminal (1, 17); + dm->set_cluster_id_for_terminal (0, 42); + EXPECT_EQ (dm->cluster_id_for_terminal (0), size_t (42)); + EXPECT_EQ (dm->cluster_id_for_terminal (1), size_t (17)); + + dm->set_cell_index (5); + EXPECT_EQ (nl.device_abstract_by_cell_index (5) == dm, true); + EXPECT_EQ (nl.device_abstract_by_cell_index (17) == 0, true); + EXPECT_EQ (dm->cell_index (), db::cell_index_type (5)); + + EXPECT_EQ (nl.begin_device_abstracts () == nl.end_device_abstracts (), false); + EXPECT_EQ (nl.begin_device_abstracts ()->name (), "name2"); + + nl.remove_device_abstract (dm); + + EXPECT_EQ (nl.begin_device_abstracts () == nl.end_device_abstracts (), true); +} + diff --git a/src/db/unit_tests/dbNetlistWriterTests.cc b/src/db/unit_tests/dbNetlistWriterTests.cc new file mode 100644 index 000000000..2132169a4 --- /dev/null +++ b/src/db/unit_tests/dbNetlistWriterTests.cc @@ -0,0 +1,931 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbNetlistWriter.h" +#include "dbNetlistSpiceWriter.h" +#include "dbNetlist.h" +#include "dbNetlistDeviceClasses.h" + +#include "tlUnitTest.h" +#include "tlStream.h" +#include "tlFileUtils.h" + +TEST(1_WriterResistorDevices) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + + db::Device *rdev1 = new db::Device (rcls); + rdev1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.7); + db::Device *rdev2 = new db::Device (rcls); + rdev2->set_parameter_value (db::DeviceClassResistor::param_id_R, 42e-6); + circuit1->add_device (rdev1); + circuit1->add_device (rdev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + + rdev1->connect_terminal (rdev1->device_class ()->terminal_id_for_name ("A"), n1); + rdev1->connect_terminal (rdev1->device_class ()->terminal_id_for_name ("B"), n3); + rdev2->connect_terminal (rdev2->device_class ()->terminal_id_for_name ("A"), n3); + rdev2->connect_terminal (rdev2->device_class ()->terminal_id_for_name ("B"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter1.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter1_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(2_WriterCapacitorDevices) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + + db::Device *cdev1 = new db::Device (ccls); + cdev1->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 1.7e-12); + db::Device *cdev2 = new db::Device (ccls); + cdev2->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 42e-15); + circuit1->add_device (cdev1); + circuit1->add_device (cdev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + + cdev1->connect_terminal (cdev1->device_class ()->terminal_id_for_name ("A"), n1); + cdev1->connect_terminal (cdev1->device_class ()->terminal_id_for_name ("B"), n3); + cdev2->connect_terminal (cdev2->device_class ()->terminal_id_for_name ("A"), n3); + cdev2->connect_terminal (cdev2->device_class ()->terminal_id_for_name ("B"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter2.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter2_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(3_WriterInductorDevices) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + + db::Device *ldev1 = new db::Device (lcls); + ldev1->set_parameter_value (db::DeviceClassInductor::param_id_L, 1.7e-10); + db::Device *ldev2 = new db::Device (lcls); + ldev2->set_parameter_value (db::DeviceClassInductor::param_id_L, 42e-9); + circuit1->add_device (ldev1); + circuit1->add_device (ldev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + + ldev1->connect_terminal (ldev1->device_class ()->terminal_id_for_name ("A"), n1); + ldev1->connect_terminal (ldev1->device_class ()->terminal_id_for_name ("B"), n3); + ldev2->connect_terminal (ldev2->device_class ()->terminal_id_for_name ("A"), n3); + ldev2->connect_terminal (ldev2->device_class ()->terminal_id_for_name ("B"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter3.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter3_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(4_WriterDiodeDevices) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + + db::Device *ddev1 = new db::Device (dcls); + ddev1->set_parameter_value (db::DeviceClassDiode::param_id_A, 1.7e-10); + db::Device *ddev2 = new db::Device (dcls); + ddev2->set_parameter_value (db::DeviceClassDiode::param_id_A, 42e-9); + circuit1->add_device (ddev1); + circuit1->add_device (ddev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("A"), n1); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("C"), n3); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("A"), n3); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("C"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter4.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter4_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(5_WriterMOS3Devices) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3, *n4; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + n4 = new db::Net (); + n4->set_name ("n4"); + circuit1->add_net (n4); + + db::Device *ddev1 = new db::Device (m3cls); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.25); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.18); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.2); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.75); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.2); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.75); + db::Device *ddev2 = new db::Device (m3cls); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 1.4); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.25); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.3); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.85); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.3); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.85); + circuit1->add_device (ddev1); + circuit1->add_device (ddev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + size_t pid3 = circuit1->add_pin ("p3").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + circuit1->connect_pin (pid3, n4); + + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("S"), n1); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("D"), n3); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("S"), n3); + ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("D"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter5.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter5_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(6_WriterMOS4Devices) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3, *n4, *n5; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + n4 = new db::Net (); + n4->set_name ("n4"); + circuit1->add_net (n4); + n5 = new db::Net (); + n5->set_name ("n5"); + circuit1->add_net (n5); + + db::Device *ddev1 = new db::Device (m4cls); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.25); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.18); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.2); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.75); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.2); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.75); + db::Device *ddev2 = new db::Device (m4cls); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 1.4); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.25); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.3); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.85); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.3); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.85); + circuit1->add_device (ddev1); + circuit1->add_device (ddev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + size_t pid3 = circuit1->add_pin ("p3").id (); + size_t pid4 = circuit1->add_pin ("p4").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + circuit1->connect_pin (pid3, n4); + circuit1->connect_pin (pid4, n5); + + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("S"), n1); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("D"), n3); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("S"), n3); + ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("D"), n2); + ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter6.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter6_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(7_WriterAnyDevices) +{ + db::Netlist nl; + + db::DeviceClass *cls = new db::DeviceClass (); + cls->add_terminal_definition (db::DeviceTerminalDefinition ("A", "a")); + cls->add_terminal_definition (db::DeviceTerminalDefinition ("B", "b")); + cls->add_parameter_definition (db::DeviceParameterDefinition ("U", "u")); + cls->add_parameter_definition (db::DeviceParameterDefinition ("V", "v")); + cls->set_name ("XCLS"); + + nl.add_device_class (cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + + db::Device *ddev1 = new db::Device (cls); + ddev1->set_parameter_value (0, -17); + ddev1->set_parameter_value (1, 42); + db::Device *ddev2 = new db::Device (cls); + ddev2->set_parameter_value (0, 17); + ddev2->set_parameter_value (1, -42); + circuit1->add_device (ddev1); + circuit1->add_device (ddev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("A"), n1); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n3); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("A"), n3); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("B"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter7.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter7_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(8_WriterSubcircuits) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + { + db::Net *n1, *n2, *n3, *n4, *n5; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + n4 = new db::Net (); + n4->set_name ("n4"); + circuit1->add_net (n4); + n5 = new db::Net (); + n5->set_name ("n5"); + circuit1->add_net (n5); + + db::Device *ddev1 = new db::Device (m4cls); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.25); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.18); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.2); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.75); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.2); + ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.75); + db::Device *ddev2 = new db::Device (m4cls); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 1.4); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.25); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.3); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.85); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.3); + ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.85); + circuit1->add_device (ddev1); + circuit1->add_device (ddev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + size_t pid3 = circuit1->add_pin ("p3").id (); + size_t pid4 = circuit1->add_pin ("p4").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + circuit1->connect_pin (pid3, n4); + circuit1->connect_pin (pid4, n5); + + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("S"), n1); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("D"), n3); + ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("S"), n3); + ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4); + ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("D"), n2); + ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5); + } + + db::Circuit *circuit2 = new db::Circuit (); + circuit2->set_name ("C2"); + nl.add_circuit (circuit2); + + { + db::Net *n1, *n2, *n3, *n4, *n5; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit2->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit2->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit2->add_net (n3); + n4 = new db::Net (); + n4->set_name ("n4"); + circuit2->add_net (n4); + n5 = new db::Net (); + n5->set_name ("n5"); + circuit2->add_net (n5); + + db::SubCircuit *sc1 = new db::SubCircuit (circuit1, "SC1"); + circuit2->add_subcircuit (sc1); + sc1->connect_pin (0, n1); + sc1->connect_pin (1, n3); + sc1->connect_pin (2, n4); + sc1->connect_pin (3, n3); + + db::SubCircuit *sc2 = new db::SubCircuit (circuit1, "SC2"); + circuit2->add_subcircuit (sc2); + sc2->connect_pin (0, n3); + sc2->connect_pin (1, n2); + sc2->connect_pin (2, n4); + sc2->connect_pin (3, n3); + + size_t pid1 = circuit2->add_pin ("p1").id (); + size_t pid2 = circuit2->add_pin ("p2").id (); + size_t pid3 = circuit2->add_pin ("p3").id (); + + circuit2->connect_pin (pid1, n1); + circuit2->connect_pin (pid2, n2); + circuit2->connect_pin (pid3, n4); + } + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter8.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter8_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +TEST(10_WriterLongLines) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + rcls->set_name ("RCLS"); + nl.add_device_class (rcls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1withaverylongextensionthatgoesbeyondmultiplelinesunlessipasteeverythingtogetherwhichmakesithardtoreadbutexactlythatisthereasonwhyiwriteitthisway"); + nl.add_circuit (circuit1); + + db::Net *n0 = new db::Net (); + n0->set_name ("n0"); + circuit1->add_net (n0); + + size_t pid0 = circuit1->add_pin ("p0").id (); + circuit1->connect_pin (pid0, n0); + + for (int i = 0; i < 100; ++i) { + + db::Net *n = new db::Net (); + n->set_name ("n" + tl::to_string (i + 1)); + circuit1->add_net (n); + + size_t pid = circuit1->add_pin ("p" + tl::to_string (i + 1)).id (); + circuit1->connect_pin (pid, n); + + db::Device *ddev = new db::Device (rcls); + circuit1->add_device (ddev); + ddev->connect_terminal (db::DeviceClassResistor::terminal_id_A, n0); + ddev->connect_terminal (db::DeviceClassResistor::terminal_id_B, n); + + } + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter10.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter10_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + +namespace { + +class MyDelegate + : public db::NetlistSpiceWriterDelegate +{ +public: + MyDelegate () + : db::NetlistSpiceWriterDelegate () + { } + + void write_header () const + { + emit_line ("*** My special header"); + } + + void write_device_intro (const db::DeviceClass &cls) const + { + emit_line ("*** My intro for class " + cls.name ()); + } + + void write_device (const db::Device &dev) const + { + emit_line ("*** Before device " + dev.expanded_name ()); + db::NetlistSpiceWriterDelegate::write_device (dev); + emit_line ("*** After device " + dev.expanded_name ()); + } +}; + +} + +TEST(20_Delegate) +{ + db::Netlist nl; + + db::DeviceClass *rcls = new db::DeviceClassResistor (); + db::DeviceClass *ccls = new db::DeviceClassCapacitor (); + db::DeviceClass *lcls = new db::DeviceClassInductor (); + db::DeviceClass *dcls = new db::DeviceClassDiode (); + db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor (); + db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor (); + + rcls->set_name ("RCLS"); + lcls->set_name ("LCLS"); + ccls->set_name ("CCLS"); + dcls->set_name ("DCLS"); + m3cls->set_name ("M3CLS"); + m4cls->set_name ("M4CLS"); + + nl.add_device_class (rcls); + nl.add_device_class (lcls); + nl.add_device_class (ccls); + nl.add_device_class (dcls); + nl.add_device_class (m3cls); + nl.add_device_class (m4cls); + + db::Circuit *circuit1 = new db::Circuit (); + circuit1->set_name ("C1"); + nl.add_circuit (circuit1); + + db::Net *n1, *n2, *n3; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit1->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit1->add_net (n2); + n3 = new db::Net (); + n3->set_name ("n3"); + circuit1->add_net (n3); + + db::Device *rdev1 = new db::Device (rcls); + rdev1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.7); + db::Device *rdev2 = new db::Device (rcls); + rdev2->set_parameter_value (db::DeviceClassResistor::param_id_R, 42e-6); + circuit1->add_device (rdev1); + circuit1->add_device (rdev2); + + size_t pid1 = circuit1->add_pin ("p1").id (); + size_t pid2 = circuit1->add_pin ("p2").id (); + + circuit1->connect_pin (pid1, n1); + circuit1->connect_pin (pid2, n2); + + rdev1->connect_terminal (rdev1->device_class ()->terminal_id_for_name ("A"), n1); + rdev1->connect_terminal (rdev1->device_class ()->terminal_id_for_name ("B"), n3); + rdev2->connect_terminal (rdev2->device_class ()->terminal_id_for_name ("A"), n3); + rdev2->connect_terminal (rdev2->device_class ()->terminal_id_for_name ("B"), n2); + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter20.txt"); + { + tl::OutputStream stream (path); + MyDelegate delegate; + db::NetlistSpiceWriter writer (&delegate); + writer.write (stream, nl, "written by unit test"); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter20_au.txt"); + + tl::InputStream is (path); + tl::InputStream is_au (au_path); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (path), + tl::absolute_file_path (au_path))); + } +} + diff --git a/src/db/unit_tests/dbRecursiveShapeIterator.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc similarity index 67% rename from src/db/unit_tests/dbRecursiveShapeIterator.cc rename to src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index bc6925e73..bbf1f171c 100644 --- a/src/db/unit_tests/dbRecursiveShapeIterator.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -428,6 +428,12 @@ TEST(1a) x = collect(i5, g); EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + i5.set_layer (1); + x = collect_with_copy(i5, g); + EXPECT_EQ (x, "[$3](101,1;1101,1101)"); + x = collect(i5, g); + EXPECT_EQ (x, "[$3](101,1;1101,1101)"); + std::set ll; db::RecursiveShapeIterator i5a (g, c0, ll, db::Box::world ()); @@ -454,6 +460,14 @@ TEST(1a) EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$3](101,1;1101,1101)*1/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); x = collect(i5cc, g, true); EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$3](101,1;1101,1101)*1/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); + + std::vector ll_new; + ll_new.push_back (0); + i5c.set_layers (ll_new); + x = collect_with_copy(i5c, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); + x = collect(i5c, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); } TEST(1b) @@ -709,6 +723,21 @@ static db::Layout boxes2layout (const std::set &boxes) return l; } +class FlatPusher + : public db::RecursiveShapeReceiver +{ +public: + FlatPusher (std::set *boxes) : mp_boxes (boxes) { } + + void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + mp_boxes->insert (trans * shape.bbox ()); + } + +private: + std::set *mp_boxes; +}; + TEST(4) { // Big fun @@ -751,6 +780,16 @@ TEST(4) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator (g, c0, 0, search_box, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + db::Box search_box2 (500, 500, 1000, 1000); selected_boxes.clear (); @@ -772,6 +811,16 @@ TEST(4) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator (g, c0, 0, reg, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); } TEST(5) @@ -819,6 +868,16 @@ TEST(5) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator (g, c0, 0, search_box, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + db::Box search_box2 (500, 500, 1000, 1000); selected_boxes.clear (); @@ -840,4 +899,408 @@ TEST(5) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator (g, c0, 0, reg, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); +} + +class LoggingReceiver + : public db::RecursiveShapeReceiver +{ +public: + LoggingReceiver () { } + + const std::string &text () const { return m_text; } + + virtual void begin (const db::RecursiveShapeIterator * /*iter*/) { m_text += "begin\n"; } + virtual void end (const db::RecursiveShapeIterator * /*iter*/) { m_text += "end\n"; } + + virtual void enter_cell (const db::RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + m_text += std::string ("enter_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; + } + + virtual void leave_cell (const db::RecursiveShapeIterator *iter, const db::Cell *cell) + { + m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; + } + + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + { + m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ()); + if (all) { + m_text += ",all"; + } + m_text += ")\n"; + return NI_all; + } + + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + { + m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (trans); + if (all) { + m_text += ",all"; + } + m_text += ")\n"; + return true; + } + + virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + m_text += "shape(" + shape.to_string () + "," + tl::to_string (trans) + ")\n"; + } + +private: + std::string m_text; +}; + +class ReceiverRejectingACellInstanceArray + : public LoggingReceiver +{ +public: + ReceiverRejectingACellInstanceArray (db::cell_index_type rejected) : m_rejected (rejected) { } + + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all) + { + LoggingReceiver::new_inst (iter, inst, region, complex_region, all); + return inst.object ().cell_index () != m_rejected ? NI_all : NI_skip; + } + +private: + db::cell_index_type m_rejected; +}; + +class ReceiverRejectingACellInstanceArrayExceptOne + : public LoggingReceiver +{ +public: + ReceiverRejectingACellInstanceArrayExceptOne (db::cell_index_type rejected) : m_rejected (rejected) { } + + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all) + { + LoggingReceiver::new_inst (iter, inst, region, complex_region, all); + return inst.object ().cell_index () != m_rejected ? NI_all : NI_single; + } + +private: + db::cell_index_type m_rejected; +}; + +class ReceiverRejectingACellInstance + : public LoggingReceiver +{ +public: + ReceiverRejectingACellInstance (db::cell_index_type rejected, const db::ICplxTrans &trans_rejected) : m_rejected (rejected), m_trans_rejected (trans_rejected) { } + + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) + { + LoggingReceiver::new_inst_member (iter, inst, trans, region, complex_region, all); + return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected; + } + +private: + db::cell_index_type m_rejected; + db::ICplxTrans m_trans_rejected; +}; + +// Push mode with cells +TEST(10) +{ + db::Manager m; + db::Layout g (&m); + g.insert_layer(0); + + db::Cell &c0 (g.cell (g.add_cell ())); + db::Cell &c1 (g.cell (g.add_cell ())); + db::Cell &c2 (g.cell (g.add_cell ())); + + db::Box b (1000, -500, 2000, 500); + c2.shapes (0).insert (b); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt, db::Vector (0, 6000), db::Vector (6000, 0), 2, 2)); + c1.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), tt, db::Vector (0, 2000), db::Vector (3000, 1000), 2, 2)); + + LoggingReceiver lr1; + db::RecursiveShapeIterator i1 (g, c0, 0); + i1.push (&lr1); + + EXPECT_EQ (lr1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,6000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,8000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,7000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,9000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,0)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,6000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,8000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,7000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,9000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); + + ReceiverRejectingACellInstanceArray rr1 (c2.cell_index ()); + db::RecursiveShapeIterator ir1 (g, c0, 0); + ir1.push (&rr1); + + EXPECT_EQ (rr1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "leave_cell($2)\n" + "end\n" + ); + + ReceiverRejectingACellInstanceArrayExceptOne rs1 (c2.cell_index ()); + db::RecursiveShapeIterator is1 (g, c0, 0); + is1.push (&rs1); + + EXPECT_EQ (rs1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,6000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,0)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,6000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); + + ReceiverRejectingACellInstance rri1 (c2.cell_index (), db::ICplxTrans ()); + db::RecursiveShapeIterator iri1 (g, c0, 0); + iri1.push (&rri1); + + EXPECT_EQ (rri1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" // -> skipped + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,8000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,7000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,9000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,8000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,7000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,9000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); + + ReceiverRejectingACellInstanceArray rr2 (c1.cell_index ()); + db::RecursiveShapeIterator ir2 (g, c0, 0); + ir2.push (&rr2); + + EXPECT_EQ (rr2.text (), + "begin\n" + "new_inst($2,all)\n" + "end\n" + ); + + LoggingReceiver lr2; + db::RecursiveShapeIterator i2 (g, c0, 0, db::Box (0, 0, 5000, 5000)); + i2.push (&lr2); + + EXPECT_EQ (lr2.text (), + "begin\n" + "new_inst($2)\n" + "new_inst_member($2,r0 *1 0,0)\n" + "enter_cell($2)\n" + "new_inst($3)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); } diff --git a/src/db/unit_tests/dbRegion.cc b/src/db/unit_tests/dbRegion.cc index 55f61df92..6fb883ae1 100644 --- a/src/db/unit_tests/dbRegion.cc +++ b/src/db/unit_tests/dbRegion.cc @@ -24,6 +24,9 @@ #include "tlUnitTest.h" #include "dbRegion.h" +#include "dbRegionUtils.h" +#include "dbRegionProcessors.h" +#include "dbEdgesUtils.h" #include "dbBoxScanner.h" #include @@ -52,10 +55,19 @@ TEST(1) EXPECT_EQ (r.transformed (db::Trans (db::Vector (1, 2))).to_string (), "(1,2;1,202;101,202;101,2)"); EXPECT_EQ (r.bbox ().to_string (), "(0,0;100,200)"); EXPECT_EQ (r.empty (), false); - EXPECT_EQ (r.is_merged (), false); + EXPECT_EQ (r.is_merged (), true); EXPECT_EQ (r.is_box (), true); EXPECT_EQ (r.begin ().at_end (), false); + db::Region rr = r; + rr.insert (db::Box (db::Point (10, 10), db::Point (110, 30))); + EXPECT_EQ (rr.bbox ().to_string (), "(0,0;110,200)"); + EXPECT_EQ (rr.to_string (), "(0,0;0,200;100,200;100,0);(10,10;10,30;110,30;110,10)"); + EXPECT_EQ (rr.empty (), false); + EXPECT_EQ (rr.is_merged (), false); + EXPECT_EQ (rr.is_box (), false); + EXPECT_EQ (rr.begin ().at_end (), false); + db::Region r1 = r; db::Region r2; EXPECT_EQ (r1.to_string (), "(0,0;0,200;100,200;100,0)"); @@ -1329,6 +1341,38 @@ TEST(30c) EXPECT_EQ (r.to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); } +TEST(100_Processors) +{ + db::Region r; + r.insert (db::Box (db::Point (0, 0), db::Point (100, 200))); + r.insert (db::Box (db::Point (0, 300), db::Point (200, 400))); + r.insert (db::Box (db::Point (0, 300), db::Point (200, 400))); + r.insert (db::Box (db::Point (100, 300), db::Point (200, 500))); + + EXPECT_EQ (r.processed (db::CornersAsDots (-180.0, 180.0)).to_string (), "(100,0;100,0);(0,0;0,0);(0,200;0,200);(100,200;100,200);(200,300;200,300);(0,300;0,300);(0,400;0,400);(100,400;100,400);(100,500;100,500);(200,500;200,500)"); + EXPECT_EQ (r.processed (db::CornersAsDots (0.0, 180.0)).to_string (), "(100,400;100,400)"); + db::Region ext; + r.processed (db::CornersAsDots (0.0, 180.0)).extended (ext, 10, 10, 20, 20); + EXPECT_EQ (ext.to_string (), "(90,380;90,420;110,420;110,380)"); + EXPECT_EQ (r.processed (db::CornersAsRectangles (-180.0, 180.0, 2)).to_string (), "(98,-2;98,2;102,2;102,-2);(-2,-2;-2,2;2,2;2,-2);(-2,198;-2,202;2,202;2,198);(98,198;98,202;102,202;102,198);(198,298;198,302;202,302;202,298);(-2,298;-2,302;2,302;2,298);(-2,398;-2,402;2,402;2,398);(98,398;98,402;102,402;102,398);(98,498;98,502;102,502;102,498);(198,498;198,502;202,502;202,498)"); + EXPECT_EQ (r.processed (db::CornersAsRectangles (0.0, 180.0, 2)).to_string (), "(98,398;98,402;102,402;102,398)"); + + EXPECT_EQ (r.processed (db::Extents (0, 0)).to_string (), "(0,0;0,200;100,200;100,0);(0,300;0,500;200,500;200,300)"); + EXPECT_EQ (r.processed (db::Extents (10, 20)).to_string (), "(-10,-20;-10,220;110,220;110,-20);(-10,280;-10,520;210,520;210,280)"); + EXPECT_EQ (r.processed (db::RelativeExtents (0, 0, 1.0, 1.0, 0, 0)).to_string (), "(0,0;0,200;100,200;100,0);(0,300;0,500;200,500;200,300)"); + EXPECT_EQ (r.processed (db::RelativeExtents (0.25, 0.4, 0.75, 0.6, 10, 20)).to_string (), "(15,60;15,140;85,140;85,60);(40,360;40,440;160,440;160,360)"); + EXPECT_EQ (r.processed (db::RelativeExtentsAsEdges (0, 0, 1.0, 1.0)).to_string (), "(0,0;100,200);(0,300;200,500)"); + EXPECT_EQ (r.processed (db::RelativeExtentsAsEdges (0.5, 0.5, 0.5, 0.5)).to_string (), "(50,100;50,100);(100,400;100,400)"); + EXPECT_EQ (r.processed (db::RelativeExtentsAsEdges (0.25, 0.4, 0.75, 0.6)).to_string (), "(25,80;75,120);(50,380;150,420)"); + + EXPECT_EQ (r.processed (db::minkowsky_sum_computation (db::Box (-10, -20, 30, 40))).to_string (), "(-10,-20;-10,240;130,240;130,-20);(-10,280;-10,440;90,440;90,540;230,540;230,280)"); + EXPECT_EQ (r.processed (db::minkowsky_sum_computation (db::Edge (-10, 0, 30, 0))).to_string (), "(-10,0;-10,200;130,200;130,0);(-10,300;-10,400;90,400;90,500;230,500;230,300)"); + + EXPECT_EQ (r.processed (db::TrapezoidDecomposition (db::TD_htrapezoids)).to_string (), "(0,0;0,200;100,200;100,0);(100,300;100,500;200,500;200,300);(0,300;0,400;100,400;100,300)"); + EXPECT_EQ (r.processed (db::ConvexDecomposition (db::PO_vertical)).to_string (), "(0,0;0,200;100,200;100,0);(100,300;100,500;200,500;200,300);(0,300;0,400;100,400;100,300)"); + EXPECT_EQ (r.processed (db::ConvexDecomposition (db::PO_horizontal)).to_string (), "(0,0;0,200;100,200;100,0);(100,400;100,500;200,500;200,400);(0,300;0,400;200,400;200,300)"); +} + TEST(issue_228) { db::Region r; diff --git a/src/db/unit_tests/dbShapes.cc b/src/db/unit_tests/dbShapes.cc index 0673d15e5..5d16390d2 100644 --- a/src/db/unit_tests/dbShapes.cc +++ b/src/db/unit_tests/dbShapes.cc @@ -172,6 +172,16 @@ std::string shapes_to_string_norm (tl::TestBase *_this, const db::Shapes &shapes r += "path " + p.to_string (); EXPECT_EQ (p.box ().to_string (), shape->bbox ().to_string ()); EXPECT_EQ (p.area (), shape->area ()); + } else if (shape->is_edge ()) { + db::Shape::edge_type p; + shape->edge (p); + r += "edge " + p.to_string (); + EXPECT_EQ (p.bbox ().to_string (), shape->bbox ().to_string ()); + } else if (shape->is_edge_pair ()) { + db::Shape::edge_pair_type p; + shape->edge_pair (p); + r += "edge_pair " + p.to_string (); + EXPECT_EQ (p.bbox ().to_string (), shape->bbox ().to_string ()); } else if (shape->is_text ()) { db::Shape::text_type p; shape->text (p); @@ -3263,6 +3273,42 @@ TEST(22) EXPECT_EQ (shapes.find (*s).to_string (), "null"); } +// Edge pairs +TEST(23) +{ + db::Manager m; + db::Shapes s (&m, 0, db::default_editable_mode ()); + db::Box b_empty; + + s.update_bbox (); + EXPECT_EQ (s.bbox (), b_empty); + + db::EdgePair ep (db::Edge (-100, -200, 0, 0), db::Edge (0, -100, 100, 100)); + s.insert (ep); + s.update_bbox (); + EXPECT_EQ (s.bbox (), db::Box (-100, -200, 100, 100)); + + db::ShapeIterator si = s.begin (db::ShapeIterator::EdgePairs); + EXPECT_EQ (!si.at_end (), true); + EXPECT_EQ (si->edge_pair ().to_string (), "(-100,-200;0,0)/(0,-100;100,100)"); + EXPECT_EQ (si->is_edge_pair (), true); + + db::EdgePair ep2; + si->instantiate (ep2); + EXPECT_EQ (ep2.to_string (), "(-100,-200;0,0)/(0,-100;100,100)"); + + ++si; + EXPECT_EQ (si.at_end (), true); + + db::Shapes s2 = s; + EXPECT_EQ (shapes_to_string_norm (_this, s2), "edge_pair (-100,-200;0,0)/(0,-100;100,100) #0\n"); + + s2.clear (); + s2.insert (db::EdgePairWithProperties (db::EdgePair (db::Edge (0, 0, 1, 1), db::Edge (10, 10, 11, 11)), 17)); + + EXPECT_EQ (shapes_to_string_norm (_this, s2), "edge_pair (0,0;1,1)/(10,10;11,11) #17\n"); +} + // Bug #107 TEST(100) { diff --git a/src/db/unit_tests/dbTilingProcessor.cc b/src/db/unit_tests/dbTilingProcessor.cc index cae09c1ba..ae47fb25a 100644 --- a/src/db/unit_tests/dbTilingProcessor.cc +++ b/src/db/unit_tests/dbTilingProcessor.cc @@ -22,6 +22,7 @@ #include "tlUnitTest.h" +#include "tlThreads.h" #include "dbTilingProcessor.h" #include "dbTextWriter.h" @@ -29,10 +30,9 @@ #include "gsiDecl.h" #include "dbWriter.h" #include "dbSaveLayoutOptions.h" +#include "dbShapeProcessor.h" #include -#include -#include unsigned int get_rand() { @@ -237,7 +237,7 @@ TEST(3) tp.input ("i2", ir2.begin_iter ().first, ir2.begin_iter ().second); EXPECT_EQ (ir2.has_valid_polygons (), false); db::Region ir3 (db::RecursiveShapeIterator (ly, ly.cell (top), l3)); - ir3.ensure_valid_polygons (); + ir3.flatten (); tp.input ("i3", ir3.begin_iter ().first, ir3.begin_iter ().second); EXPECT_EQ (ir3.has_valid_polygons (), true); tp.output ("o1", ly, top, o1); @@ -402,8 +402,8 @@ public: void add (double x) const { - static QMutex lock; - QMutexLocker locker (&lock); + static tl::Mutex lock; + tl::MutexLocker locker (&lock); *mp_sum += x; *mp_n += 1; } diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index c1a034f6a..87fbdd2a0 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -39,7 +39,6 @@ SOURCES = \ dbPolygon.cc \ dbPolygonTools.cc \ dbPropertiesRepository.cc \ - dbRecursiveShapeIterator.cc \ dbRegion.cc \ dbShapeArray.cc \ dbShape.cc \ @@ -53,7 +52,24 @@ SOURCES = \ dbWriterTools.cc \ dbVariableWidthPath.cc \ dbLoadLayoutOptionsTests.cc \ - dbSaveLayoutOptionsTests.cc + dbSaveLayoutOptionsTests.cc \ + dbHierarchyBuilderTests.cc \ + dbRecursiveShapeIteratorTests.cc \ + dbHierProcessorTests.cc \ + dbDeepRegionTests.cc \ + dbDeepShapeStoreTests.cc \ + dbHierNetworkProcessorTests.cc \ + dbNetlistTests.cc \ + dbNetlistExtractorTests.cc \ + dbNetlistDeviceExtractorTests.cc \ + dbNetlistDeviceClassesTests.cc \ + dbLayoutToNetlistTests.cc \ + dbLayoutToNetlistWriterTests.cc \ + dbLayoutToNetlistReaderTests.cc \ + dbNetlistWriterTests.cc \ + dbCellVariantsTests.cc \ + dbDeepEdgesTests.cc \ + dbDeepEdgePairsTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/drc/drc/built-in-macros/drc.lym b/src/drc/drc/built-in-macros/drc.lym index 5bdb3de1b..9de7b23dd 100644 --- a/src/drc/drc/built-in-macros/drc.lym +++ b/src/drc/drc/built-in-macros/drc.lym @@ -1360,7 +1360,7 @@ CODE # It is an alias for the "&" operator. # # This method is available for polygon and edge layers. - # If the first operand is an edge layer and the second is an edge layer, the + # If the first operand is an edge layer and the second is a polygon layer, the # result will be the edges of the first operand which are inside or on the # borders of the polygons of the second operand. # @@ -2280,6 +2280,15 @@ CODE @data.is_a?(RBA::EdgePairs) end + # %DRC% + # @name is_deep? + # @brief Returns true, if the layer is a deep (hierarchical) layer + # @synopsis layer.is_deep? + + def is_deep? + @data.respond_to?(:is_deep?) && @data.is_deep? + end + # %DRC% # @name area # @brief Returns the total area of the polygons in the region @@ -2347,6 +2356,20 @@ CODE @engine._cmd(@data, :length) * @engine.dbu.to_f end + # %DRC% + # @name flatten + # @brief Flattens the layer + # @synopsis layer.flatten + # + # If the layer already is a flat one, this method does nothing. + # If the layer is a hierarchical layer (an original layer or + # a derived layer in deep mode), this method will convert it + # to a flat collection of polygons, edges or edge pairs. + + def flatten + @engine._cmd(@data, :flatten) + end + # %DRC% # @name is_merged? # @brief Returns true, if the polygons of the layer are merged @@ -3136,6 +3159,26 @@ CODE @data end + def requires_region(f) + @data.is_a?(RBA::Region) || raise("#{f}: Requires a polygon layer") + end + + def requires_edge_pairs(f) + @data.is_a?(RBA::EdgePairs) || raise("#{f}: Requires a edge pair layer") + end + + def requires_edges(f) + @data.is_a?(RBA::Edges) || raise("#{f}: Requires an edge layer") + end + + def requires_edges_or_region(f) + @data.is_a?(RBA::Edges) || @data.is_a?(RBA::Region) || raise("#{f}: Requires an edge or polygon layer") + end + + def requires_same_type(other, f) + @data.class == other.data.class || raise("#{f}: Requires input of the same kind") + end + private def insert_object_into(container, object, dbu_trans) @@ -3185,28 +3228,6 @@ CODE end end - protected - - def requires_region(f) - @data.is_a?(RBA::Region) || raise("#{f}: Requires a polygon layer") - end - - def requires_edge_pairs(f) - @data.is_a?(RBA::EdgePairs) || raise("#{f}: Requires a edge pair layer") - end - - def requires_edges(f) - @data.is_a?(RBA::Edges) || raise("#{f}: Requires an edge layer") - end - - def requires_edges_or_region(f) - @data.is_a?(RBA::Edges) || @data.is_a?(RBA::Region) || raise("#{f}: Requires an edge or polygon layer") - end - - def requires_same_type(other, f) - @data.class == other.data.class || raise("#{f}: Requires input of the same kind") - end - end # A layout source representative object. @@ -3467,10 +3488,23 @@ CODE # @li @tt METAL (17/0) @/tt: A layer named "METAL" or layer 17, datatype 0 (for GDS, which does # not have names)@/li # @/ul + # + # Layers created with "input" contain both texts and polygons. There is a subtle + # difference between flat and deep mode: in flat mode, texts are not visible in polygon + # operations. In deep mode, texts appear as small 2x2 DBU rectangles. In flat mode, + # some operations such as clipping are not fully supported for texts. Also, texts will + # vanish in most polygon operations such as booleans etc. + # + # Texts can later be selected on the layer returned by "input" with the \Layer#texts method. + # + # If you don't want to see texts, use \polygons to create an input layer with polygon data + # only. If you only want to see texts, use \labels to create an input layer with texts only. + # + # Use the global version of "input" without a source object to address the default source. def input(*args) layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, false)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll)) end # %DRC% @@ -3480,26 +3514,49 @@ CODE # @synopsis source.labels(layer, datatype) # @synopsis source.labels(layer_into) # @synopsis source.labels(filter, ...) - # Creates a layer with the labels from the given layer of the source. - # The layer can be specified by layer and optionally datatype, by a RBA::LayerInfo - # object or by a sequence of filters. - # Filters are expressions describing ranges - # of layers and/or datatype numbers or layer names. Multiple filters - # can be given and all layers matching at least one of these filter - # expressions are joined to render the label collection. See "input" for - # more details about the input layer specification. - # - # Label layers currently can only be passed to an output layer. - # Processing of labels is not supported. See "texts" for a way to filter - # texts and use the text locations in geometrical operations. # - # @code - # labels(1, 0).output(100, 0) - # @/code + # Creates a layer with the labels from the given layer of the source. + # + # This method is identical to \input, but takes only texts from the given input + # layer. + # + # Use the global version of "labels" without a source object to address the default source. def labels(*args) layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, true)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts)) + end + + # %DRC% + # @name polygons + # @brief Gets the polygon shapes (or shapes that can be converted polygons) from an input layer + # @synopsis source.polygons(layer) + # @synopsis source.polygons(layer, datatype) + # @synopsis source.polygons(layer_into) + # @synopsis source.polygons(filter, ...) + # + # Creates a layer with the polygon shapes from the given layer of the source. + # With "polygon shapes" we mean all kind of shapes that can be converted to polygons. + # Those are boxes, paths and real polygons. + # + # This method is identical to \input with respect to the options supported. + # + # Use the global version of "polygons" without a source object to address the default source. + + def polygons(*args) + layers = parse_input_layers(*args) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs)) + end + + # %DRC% + # @name make_layer + # @brief Creates an empty polygon layer based on the hierarchy of the layout + # @synopsis make_layer + # This method delivers a new empty original layer. + + def make_layer + layers = [] + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll)) end # %DRC% @@ -3572,6 +3629,341 @@ CODE end + # The netter object + + # %DRC% + # @scope + # @name Netter + # @brief DRC Reference: Netter object + # The Netter object provides services related to network extraction + # from a layout. The relevant methods of this object are available + # as global functions too where they act on a default incarnation + # of the netter. Usually it's not required to instantiate a Netter + # object, but it serves as a container for this functionality. + # + # An individual netter object can be created, if the netter results + # need to be kept for multiple extractions. If you really need + # a Netter object, use the global \netter function: + # + # @code + # # create a new Netter object: + # nx = netter + # nx.connect(poly, contact) + # ... + # @/code + # + # Network formation: + # + # A basic Service the Netter object provides is the formation of + # connected networks of conductive shapes. To do so, the Netter + # must be given a connection specification. This happens by calling + # "connect" with two polygon layers. The Netter will then regard all + # overlaps of shapes on these layers as connections between the + # respective materials. Networks are the basis for netlist extraction, + # network geometry deduction and the antenna check. + # + # Connections can be cleared with "clear_connections". If not, + # connections add atop of the already defined ones. Here is an + # example for the antenna check: + # + # @code + # # build connction of poly+gate to metal1 + # connect(gate, poly) + # connect(poly, contact) + # connect(contact, metal1) + # + # # runs an antenna check for metal1 with a ratio of 50 + # m1_antenna_errors = antenna_check(gate, metal1, 50.0) + # + # # add connections to metal2 + # connect(metal1, via1) + # connect(via1, metal2) + # + # # runs an antenna check for metal2 with a ratio of 70.0 + # m2_antenna_errors = antenna_check(gate, metal2, 70.0) + # + # # this will remove all connections made + # clear_connections + # ... + # @/code + # + # Further functionality of the Netter object: + # + # More methods will be added in the future to support network-related features. + + class DRCNetter + + def initialize(engine) + @engine = engine + clear_connections + end + + # %DRC% + # @name connect + # @brief Specifies a connection between two layers + # @synopsis connect(a, b) + # a and b must be polygon layers. After calling this function, the + # Netter regards all overlapping or touching shapes on these layers + # to form an electrical connection between the materials formed by + # these layers. This also implies intra-layer connections: shapes + # on these layers touching or overlapping other shapes on these + # layers will form bigger, electrically connected areas. + # + # Multiple connect calls must be made to form larger connectivity + # stacks across multiple layers. Such stacks may include forks and + # joins. + # + # Connections are accumulated. The connections defined so far + # can be cleared with \clear_connections. + + def connect(a, b) + a.is_a?(DRC::DRCLayer) || raise("First argument of Netter#connect must be a layer") + b.is_a?(DRC::DRCLayer) || raise("Second argument of Netter#connect must be a layer") + a.requires_region("Netter#connect (first argument)") + b.requires_region("Netter#connect (second argument)") + [ a, b ].each { |l| @layers[l.data.data_id] = l.data } + @connections << [ a, b ].collect { |l| l.data.data_id } + modified + end + + # %DRC% + # @name connect_global + # @brief Connects a layer with a global net + # @synopsis connect_global(l, name) + # Connects the shapes from the given layer l to a global net with the given name. + # Global nets are common to all cells. Global nets automatically connect to parent + # cells throughs implied pins. An example is the substrate (bulk) net which connects + # to shapes belonging to tie-down diodes. + + def connect_global(l, name) + l.is_a?(DRC::DRCLayer) || raise("Layer argument of Netter#connect_global must be a layer") + l.requires_region("Netter#connect_global (layer argument)") + @layers[l.data.data_id] = l.data + @global_connections << [ l.data.data_id, name.to_s ] + end + + # %DRC% + # @name extract_devices + # @brief Extracts devices based on the given extractor class, name and device layer selection + # @synopsis extract_devices(extractor, layer_hash) + # Runs the device extraction for given device extractor class. + # + # The device extractor is either an instance of one of the predefined extractor + # classes (e.g. RBA::DeviceExtractorMOS4Transistor) or a custom class. It provides the + # algorithms for deriving the device parameters from the device geometry. It needs + # several device recognition layers which are passed in the layer hash. + # + # Each device class (e.g. n-MOS/p-MOS or high Vt/low Vt) needs it's own instance + # of device extractor. The device extractor beside the algorithm and specific + # extraction settings defines the name of the device to be built. + # + # The layer hash is a map of device type specific functional names (key) and + # polygon layers (value). Here is an example: + # + # @code + # deep + # + # nwell = input(1, 0) + # active = input(2, 0) + # poly = input(3, 0) + # bulk = make_layer # renders an empty layer used for putting the terminals on + # + # nactive = active - nwell # active area of NMOS + # nsd = nactive - poly # source/drain area + # gate = nactive & poly # gate area + # + # mos4_ex = RBA::DeviceExtractorMOS4Transistor::new("NMOS4") + # extract_devices(mos4_ex, { :SD => nsd, :G => gate, :P => poly, :W => bulk }) + # @/code + + def extract_devices(devex, layer_selection) + + devex.is_a?(RBA::DeviceExtractorBase) || raise("First argument of Netter#extract_devices must be a device extractor instance") + layer_selection.is_a?(Hash) || raise("Second argument of Netter#extract_devices must be a hash") + + ls = {} + layer_selection.each do |n,l| + l.requires_region("Netter#extract_devices (#{n} layer)") + @layers[l.data.data_id] = l.data + ls[n.to_s] = l.data + end + + @devices_to_extract << [ devex, ls ] + modified + + end + + # %DRC% + # @name clear_connections + # @brief Clears all connections stored so far + # @synopsis clear_connections + # See \connect for more details. + + def clear_connections + @devices_to_extract = [] + @connections = [] + @global_connections = [] + @layers = {} + modified + end + + # %DRC% + # @brief Performs an antenna check + # @name antenna_check + # @synopsis antenna_check(gate, metal, ratio, [ diode_specs ... ]) + # + # The antenna check is used to avoid plasma induced damage. Physically, + # the damage happes if during the manufacturing of a metal layer with + # plasma etching charge accumulates on the metal islands. On reaching a + # certain threshold, this charge may discarge over gate oxide attached of + # devices attached to such metal areas hence damaging it. + # + # Antenna checks are performed by collecting all connected nets up to + # a certain metal layer and then computing the area of all metal shapes + # and all connected gates of a certain kind (e.g. thin and thick oxide gates). + # The ratio of metal area divided by the gate area must not exceed a certain + # threshold. + # + # A simple antenna check is this: + # + # @code + # poly = ... # poly layer + # diff = ... # diffusion layer + # contact = ... # contact layer + # metal1 = ... # metal layer + # + # # compute gate area + # gate = poly & diff + # + # # note that gate and poly have to be included - gate is + # # a subset of poly, but forms the sensitive area + # connect(gate, poly) + # connect(poly, contact) + # connect(contact, metal1) + # errors = antenna_check(gate, metal1, 50.0) + # @/code + # + # Plasma induced damage can be rectified by including diodes + # which create a safe current path for discharging the metal + # islands. Such diodes can be identified with a recognition layer + # (usually the diffusion area of a certain kind). You can include + # such diode recognition layers in the antenna check. If a connection + # is detected to a diode, the respective network is skipped: + # + # @code + # ... + # diode = ... # diode recognition layer + # + # connect(diode, contact) + # errors = antenna_check(gate, metal1, 50.0, diode) + # @/code + # + # You can also make diode connections decreases the + # sensitivity of the antenna check depending on the size + # of the diode. The following specification makes + # diode connections increase the ratio threshold by + # 10 per square micrometer of diode area: + # + # @code + # ... + # diode = ... # diode recognition layer + # + # connect(diode, contact) + # # each square micrometer of diode area connected to a network + # # will add 10 to the ratio: + # errors = antenna_check(gate, metal1, 50.0, [ diode, 10.0 ]) + # @/code + # + # Multiple diode specifications are allowed. Just add them + # to the antenna_check call. + # + # The error shapes produced by the antenna check are copies + # of the metal shapes on the metal layers of each network + # violating the antenna rule. + + def antenna_check(gate, metal, ratio, *diodes) + + gate.is_a?(DRC::DRCLayer) || raise("gate argument of Netter#antenna_check must be a layer") + gate.requires_region("Netter#antenna_check (gate argument)") + + metal.is_a?(DRC::DRCLayer) || raise("metal argument of Netter#antenna_check must be a layer") + metal.requires_region("Netter#antenna_check (metal argument)") + + if !ratio.is_a?(1.class) && !ratio.is_a?(Float) + raise("ratio argument Netter#antenna_check is not a number") + end + + dl = diodes.collect do |d| + if d.is_a?(Array) + d.size == 2 || raise("diode specification pair expects two elements") + d[0].requires_region("Netter#antenna_check (diode layer)") + [ d[0].data, d[1].to_f ] + else + d.requires_region("Netter#antenna_check (diode layer)") + [ d.data, 0.0 ] + end + end + + @l2n || make_l2n + DRC::DRCLayer::new(@engine, @engine._cmd(@l2n, :antenna_check, gate.data, metal.data, ratio, dl)) + + end + + # %DRC% + # @name l2n_data + # @brief Gets the internal RBA::LayoutToNetlist object + # @synopsis l2n_data + # The RBA::LayoutToNetlist object provides access to the internal details of + # the netter object. + + def l2n_data + @l2n || make_l2n + @l2n + end + + def _finish + clear_connections + # cleans up the L2N object + modified + end + + private + + def modified + @l2n && @l2n._destroy + @l2n = nil + end + + def make_l2n + + if @engine._dss + # TODO: check whether all layers are deep and come from the dss and layout index, + # then use this layout index. This will remove the need for this check: + @engine._dss.is_singular? || raise("The DRC script features more than one or no layout source - network extraction cannot be performed in such configurations") + @l2n = RBA::LayoutToNetlist::new(@engine._dss) + else + layout = @engine.source.layout + @l2n = RBA::LayoutToNetlist::new(layout.top_cell.name, layout.dbu) + end + + @layers.each { |id,l| @l2n.register(l, "l" + id.to_s) } + + @devices_to_extract.each do |devex,ls| + @engine._cmd(@l2n, :extract_devices, devex, ls) + end + + @layers.each { |id,l| @l2n.connect(l) } + @connections.each { |a,b| @l2n.connect(@layers[a], @layers[b]) } + @global_connections.each { |l,n| @l2n.connect_global(@layers[l], n) } + + # run extraction in a timed environment + @engine._cmd(@l2n, :extract_netlist) + @l2n + + end + + end + # The DRC engine # %DRC% @@ -3596,13 +3988,16 @@ CODE @output_layout = nil @output_rdb = nil @output_rdb_file = nil - @output_rdb_cell_id = nil + @output_rdb_cell = nil @used_output_layers = {} @output_layers = [] @vnum = 1 @layout_sources = {} @lnum = 1 @log_file = nil + @dss = nil + @deep = false + @netter = nil @verbose = false @@ -3821,11 +4216,14 @@ CODE # # In tiling mode, the memory requirements are usually smaller (depending on the # choice of the tile size) and multi-CPU support is enabled (see \threads). - # To disable tiling mode use \flat. + # To disable tiling mode use \flat or \deep. + # + # Tiling mode will disable deep mode (see \deep). def tiles(tx, ty = nil) @tx = tx.to_f @ty = (ty || tx).to_f + @deep = false end # %DRC% @@ -3837,6 +4235,38 @@ CODE @tx != nil end + # %DRC% + # @name deep + # @brief Enters deep (hierarchical) mode + # @synopsis deep + # + # In deep mode, the operations will be performed in a hierarchical fashion. + # Sometimes this reduces the time and memory required for an operation, but this + # will also add some overhead for the hierarchical analysis. + # + # "deepness" is a property of layers. Layers created with "input" while in + # deep mode carry hierarchy. Operations involving such layers at the only + # or the first argument are carried out in hierarchical mode. + # + # Hierarchical mode has some more implications, like "merged_semantics" being + # implied always. Sometimes cell variants will be created. + # + # Deep mode can be cancelled with \tiles or \flat. + + def deep + @deep = true + @tx = @ty = nil + end + + # %DRC% + # @name is_deep? + # @brief Returns true, if in deep mode + # @synopsis is_deep? + + def is_deep? + @deep + end + # %DRC% # @name tile_borders # @brief Specifies a minimum tile border @@ -3878,6 +4308,7 @@ CODE def flat @tx = @ty = nil + @deep = false end # %DRC% @@ -3892,12 +4323,33 @@ CODE @tt = n.to_i end + # %DRC% + # @name make_layer + # @brief Creates an empty polygon layer based on the hierarchical scheme selected + # @synopsis make_layer + # The intention of this method is to provide an empty polygon layer based on the + # hierarchical scheme selected. This will create a new layer with the hierarchy + # of the current layout in deep mode and a flat layer in flat mode. + # This method is similar to \polygon_layer, but the latter does not create + # a hierarchical layer. Hence the layer created by \make_layer is suitable + # for use in device extraction for example, while the one + # delivered by \polygon_layer is not. + # + # On the other hand, a layer created by the \make_layer method is not intended to be + # filled with \Layer#insert. + + def make_layer + layout.make_layer + end + # %DRC% # @name polygon_layer # @brief Creates an empty polygon layer # @synopsis polygon_layer # The intention of that method is to create an empty layer which can be # filled with polygon-like objects using \Layer#insert. + # A similar method which creates a hierarchical layer in deep mode is + # \make_layer. This other layer is better suited for use with device extraction. def polygon_layer DRCLayer::new(self, RBA::Region::new) @@ -4131,7 +4583,7 @@ CODE cn || raise("No cell name specified - either the source was not specified before 'report' or there is no default source. In the latter case, specify a cell name as the third parameter of 'report'") - @output_rdb_cell_id = @output_rdb.create_cell(cn).rdb_id + @output_rdb_cell = @output_rdb.create_cell(cn) @output_rdb.generator = $0 @output_rdb.top_cell_name = cn @output_rdb.description = description @@ -4153,15 +4605,15 @@ CODE if @output_rdb - cell_id = nil + cell = nil @output_rdb.each_cell do |c| if c.name == cellname - cell_id = c.rdb_id + cell = c end end - cell_id ||= @output_rdb.create_cell(cellname).rdb_id - @output_rdb_cell_id = cell_id + cell ||= @output_rdb.create_cell(cellname) + @output_rdb_cell = cell else @@ -4313,12 +4765,24 @@ CODE # @name input # @brief Fetches the shapes from the specified input from the default source # @synopsis input(args) - # See \Source#input for a description of that function. + # See \Source#input for a description of that function. This method will fetch + # polygons and labels. See \polygons and \labels for more specific versions of + # this method. def input(*args) layout.input(*args) end + # %DRC% + # @name polygons + # @brief Fetches the polygons (or shapes that can be converted to polygons) from the specified input from the default source + # @synopsis polygons(args) + # See \Source#polygons for a description of that function. + + def polygons(*args) + layout.polygons(*args) + end + # %DRC% # @name labels # @brief Gets the labels (text) from an original layer @@ -4430,6 +4894,60 @@ CODE CODE end + # %DRC% + # @name netter + # @brief Creates a new netter object + # @synopsis netter + # See \Netter for more details + + def netter + DRC::DRCNetter::new + end + + # %DRC% + # @name connect + # @brief Specifies a connection between two layers + # @synopsis connect(a, b) + # See \Netter#connect for a description of that function. + + # %DRC% + # @name connect_global + # @brief Specifies a connection to a global net + # @synopsis connect_global(l, name) + # See \Netter#connect_global for a description of that function. + + # %DRC% + # @name clear_connections + # @brief Clears all connections stored so far + # @synopsis clear_connections + # See \Netter#clear_connections for a description of that function + + # %DRC% + # @name antenna_check + # @brief Performs an antenna check + # @synopsis antenna_check(gate, metal, ratio, [ diode_specs ... ]) + # See \Netter#antenna_check for a description of that function + + # %DRC% + # @name l2n_data + # @brief Gets the internal RBA::LayoutToNetlist object for the default \Netter + # @synopsis l2n_data + # See \Netter#l2n_data for a description of that function + + # %DRC% + # @name extract_devices + # @brief Extracts devices for a given device extractor and device layer selection + # @synopsis extract_devices(extractor, layer_hash) + # See \Netter#extract_devices for a description of that function + + %w(connect connect_global clear_connections antenna_check l2n_data extract_devices).each do |f| + eval <<"CODE" + def #{f}(*args) + _netter.#{f}(*args) + end +CODE + end + def src_line cc = caller.find do |c| c !~ /drc.lym:/ && c !~ /\(eval\)/ @@ -4656,8 +5174,13 @@ CODE @output_layout_file = nil @output_cell = nil @output_rdb_file = nil + @output_rdb_cell = nil @output_rdb = nil @output_rdb_index = nil + + # clean up temp data + @dss && @dss._destroy + @netter && @netter._finish if final && @log_file @log_file.close @@ -4665,7 +5188,15 @@ CODE end end - + + def _dss + @dss + end + + def _netter + @netter ||= DRC::DRCNetter::new(self) + end + private def _make_string(v) @@ -4676,10 +5207,12 @@ CODE end end - def _input(layout, cell_index, layers, sel, box, clip, overlapping, labels_only) + def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags) - if layers.empty? + if layers.empty? && ! @deep + r = RBA::Region::new + else if box @@ -4687,9 +5220,7 @@ CODE else iter = RBA::RecursiveShapeIterator::new(layout, layout.cell(cell_index), layers) end - if labels_only - iter.shape_flags = RBA::Shapes::STexts - end + iter.shape_flags = shape_flags sel.each do |s| if s == "-" @@ -4707,25 +5238,21 @@ CODE end end - if labels_only - - # for labels layers, the iterator is the data (no region) - r = iter - + sf = layout.dbu / self.dbu + if @deep + @dss ||= RBA::DeepShapeStore::new + # TODO: align with LayoutToNetlist by using a "master" L2N + # object which keeps the DSS. + @dss.text_property_name = "LABEL" + @dss.text_enlargement = 1 + r = RBA::Region::new(iter, @dss, RBA::ICplxTrans::new(sf.to_f)) else + r = RBA::Region::new(iter, RBA::ICplxTrans::new(sf.to_f)) + end - sf = layout.dbu / self.dbu - if (sf - 1.0).abs > 1e-6 - r = RBA::Region::new(iter, RBA::ICplxTrans::new(sf.to_f)) - else - r = RBA::Region::new(iter) - end - - # clip if a box is specified - if box && clip - r &= RBA::Region::new(box) - end - + # clip if a box is specified + if box && clip + r &= RBA::Region::new(box) end end @@ -4749,11 +5276,7 @@ CODE cat = @output_rdb.create_category(args[0].to_s) args[1] && cat.description = args[1] - if data.is_a?(RBA::RecursiveShapeIterator) - @output_rdb.create_items(@output_rdb_cell_id, cat.rdb_id, data) - else - @output_rdb.create_items(@output_rdb_cell_id, cat.rdb_id, RBA::CplxTrans::new(self.dbu), data) - end + cat.scan_collection(@output_rdb_cell, RBA::CplxTrans::new(self.dbu), data) else @@ -4793,6 +5316,9 @@ CODE li = output.insert_layer(info) end + # make sure the output has the right database unit + output.dbu = self.dbu + tmp = li begin @@ -4807,14 +5333,11 @@ CODE @used_output_layers[li] = true end - # make sure the output has the right database unit - output.dbu = self.dbu - # insert the data into the output layer if data.is_a?(RBA::EdgePairs) - output_cell.shapes(tmp).insert_as_polygons(data, 1) + data.insert_into_as_polygons(output, output_cell.cell_index, tmp, 1) else - output_cell.shapes(tmp).insert(data) + data.insert_into(output, output_cell.cell_index, tmp) end # make the temp layer the output layer diff --git a/src/drc/unit_tests/drcBasicTests.cc b/src/drc/unit_tests/drcBasicTests.cc index a5978b6d6..98401cb0c 100644 --- a/src/drc/unit_tests/drcBasicTests.cc +++ b/src/drc/unit_tests/drcBasicTests.cc @@ -30,7 +30,7 @@ TEST(1) std::string input = tl::testsrc (); input += "/testdata/drc/drctest.gds"; std::string au = tl::testsrc (); - au += "/testdata/drc/drcBasicTests_au.oas"; + au += "/testdata/drc/drcBasicTests_au.gds"; std::string output = this->tmp_file ("tmp.gds"); diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index b3d225ce8..885495d8c 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -24,6 +24,7 @@ #include "dbReader.h" #include "dbTestSupport.h" #include "lymMacro.h" +#include "tlFileUtils.h" TEST(1) { @@ -31,7 +32,7 @@ TEST(1) rs += "/testdata/drc/drcSimpleTests_1.drc"; std::string au = tl::testsrc (); - au += "/testdata/drc/drcSimpleTests_au1.oas"; + au += "/testdata/drc/drcSimpleTests_au1.gds"; std::string output = this->tmp_file ("tmp.gds"); @@ -71,7 +72,7 @@ TEST(2) input += "/testdata/drc/drctest.gds"; std::string au = tl::testsrc (); - au += "/testdata/drc/drcSimpleTests_au2.oas"; + au += "/testdata/drc/drcSimpleTests_au2.gds"; std::string output = this->tmp_file ("tmp.gds"); @@ -102,7 +103,7 @@ TEST(2) db::compare_layouts (_this, layout, au, db::NoNormalization); } -TEST(3) +TEST(3_Flat) { std::string rs = tl::testsrc (); rs += "/testdata/drc/drcSimpleTests_3.drc"; @@ -111,7 +112,7 @@ TEST(3) input += "/testdata/drc/drctest.gds"; std::string au = tl::testsrc (); - au += "/testdata/drc/drcSimpleTests_au3.oas"; + au += "/testdata/drc/drcSimpleTests_au3.gds"; std::string output = this->tmp_file ("tmp.gds"); @@ -141,3 +142,383 @@ TEST(3) db::compare_layouts (_this, layout, au, db::NoNormalization); } + +TEST(4_Hierarchical) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_4.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/drctest.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au4.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (_this, layout, au, db::NoNormalization); +} + +TEST(5_FlatAntenna) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_5.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/antenna_l1.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au5.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (_this, layout, au, db::NoNormalization); +} + +TEST(6_HierarchicalAntenna) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_6.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/antenna_l1.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au6.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (_this, layout, au, db::NoNormalization); +} + +TEST(7_AntennaWithDiodes) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_7.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/antenna_l1.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au7.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (_this, layout, au, db::NoNormalization); +} + +TEST(8_TextsAndPolygons) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_8.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/texts.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au8.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (_this, layout, au, db::NoNormalization); +} + +TEST(9_NetlistExtraction) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_9.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/ringo.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au9a.cir"; + + std::string au_simplified = tl::testsrc (); + au_simplified += "/testdata/drc/drcSimpleTests_au9b.cir"; + + std::string output = this->tmp_file ("tmp.cir"); + std::string output_simplified = this->tmp_file ("tmp_simplified.cir"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + "$drc_test_target_simplified = '%s'\n" + , input, output, output_simplified) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + + // verify + + { + tl::InputStream is (output); + tl::InputStream is_au (au); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (output), + tl::absolute_file_path (au))); + } + } + + { + tl::InputStream is (output_simplified); + tl::InputStream is_au (au_simplified); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed (simplified netlist) - see\n actual: %s\n golden: %s", + tl::absolute_file_path (output_simplified), + tl::absolute_file_path (au_simplified))); + } + } +} + +TEST(10_NetlistExtractionFlat) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_10.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/ringo.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au10a.cir"; + + std::string au_simplified = tl::testsrc (); + au_simplified += "/testdata/drc/drcSimpleTests_au10b.cir"; + + std::string output = this->tmp_file ("tmp.cir"); + std::string output_simplified = this->tmp_file ("tmp_simplified.cir"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + "$drc_test_target_simplified = '%s'\n" + , input, output, output_simplified) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + + // verify + + { + tl::InputStream is (output); + tl::InputStream is_au (au); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (output), + tl::absolute_file_path (au))); + } + } + + { + tl::InputStream is (output_simplified); + tl::InputStream is_au (au_simplified); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed (simplified netlist) - see\n actual: %s\n golden: %s", + tl::absolute_file_path (output_simplified), + tl::absolute_file_path (au_simplified))); + } + } +} + +TEST(11_CustomDevices) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_11.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/vdiv.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au11a.cir"; + + std::string au_simplified = tl::testsrc (); + au_simplified += "/testdata/drc/drcSimpleTests_au11b.cir"; + + std::string output = this->tmp_file ("tmp.cir"); + std::string output_simplified = this->tmp_file ("tmp_simplified.cir"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + "$drc_test_target_simplified = '%s'\n" + , input, output, output_simplified) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + + // verify + + { + tl::InputStream is (output); + tl::InputStream is_au (au); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", + tl::absolute_file_path (output), + tl::absolute_file_path (au))); + } + } + + { + tl::InputStream is (output_simplified); + tl::InputStream is_au (au_simplified); + + if (is.read_all () != is_au.read_all ()) { + _this->raise (tl::sprintf ("Compare failed (simplified netlist) - see\n actual: %s\n golden: %s", + tl::absolute_file_path (output_simplified), + tl::absolute_file_path (au_simplified))); + } + } +} diff --git a/src/drc/unit_tests/drcSuiteTests.cc b/src/drc/unit_tests/drcSuiteTests.cc index 94e043626..b134a853d 100644 --- a/src/drc/unit_tests/drcSuiteTests.cc +++ b/src/drc/unit_tests/drcSuiteTests.cc @@ -72,25 +72,36 @@ void runtest (tl::TestBase *_this, int mode) db::compare_layouts (_this, layout, au, db::WriteOAS); } -TEST(1) +TEST(1_Flat) { runtest (_this, 1); } -TEST(2) +TEST(2_BigFlat) { test_is_long_runner (); runtest (_this, 2); } -TEST(3) +TEST(3_Tiled) { test_is_long_runner (); runtest (_this, 3); } -TEST(4) +TEST(4_BigTiled) { test_is_long_runner (); runtest (_this, 4); } + +TEST(5_Hier) +{ + runtest (_this, 5); +} + +TEST(6_BigHier) +{ + test_is_long_runner (); + runtest (_this, 6); +} diff --git a/src/gsi/gsi/gsiDeclTl.cc b/src/gsi/gsi/gsiDeclTl.cc index c9b3aebb8..7c4dc3a5b 100644 --- a/src/gsi/gsi/gsiDeclTl.cc +++ b/src/gsi/gsi/gsiDeclTl.cc @@ -152,7 +152,7 @@ namespace gsi static std::string timer_to_s (const tl::Timer *timer) { - return tl::sprintf ("%.12gs (user), %.12gs (kernel)", timer->sec_user (), timer->sec_sys ()); + return tl::sprintf ("%.12gs (sys), %.12gs (user), %.12gs (wall)", timer->sec_sys (), timer->sec_user (), timer->sec_wall ()); } Class decl_Timer ("tl", "Timer", @@ -162,7 +162,11 @@ Class decl_Timer ("tl", "Timer", gsi::method ("sys", &tl::Timer::sec_sys, "@brief Returns the elapsed CPU time in kernel mode from start to stop in seconds\n" ) + - gsi::method_ext ("to_s", &timer_to_s, + gsi::method ("wall", &tl::Timer::sec_wall, + "@brief Returns the elapsed real time from start to stop in seconds\n" + "This method has been introduced in version 0.26." + ) + + gsi::method_ext ("to_s", &timer_to_s, "@brief Produces a string with the currently elapsed times\n" ) + gsi::method ("start", &tl::Timer::start, diff --git a/src/gsi/gsi/gsiMethods.h b/src/gsi/gsi/gsiMethods.h index d29d70f25..9d214ef6f 100644 --- a/src/gsi/gsi/gsiMethods.h +++ b/src/gsi/gsi/gsiMethods.h @@ -855,6 +855,49 @@ constant (const std::string &name, R (*m) (), const std::string &doc = std::stri return Methods (new ConstantGetter (name, m, doc)); } +/** + * @brief A helper class to create a constant (a static method with "const" attribute, not taking any arguments) + * This version creates a constant getter from a real constant value. + */ +template +class ConstantValueGetter + : public StaticMethodBase +{ +public: + ConstantValueGetter (const std::string &name, const R &v, const std::string &doc) + : StaticMethodBase (name, doc, true), m_v (v) + { + } + + void initialize () + { + this->clear (); + // Note: a constant must not return a reference to an existing object, hence "set_return_new": + this->template set_return_new (); + } + + virtual MethodBase *clone () const + { + return new ConstantValueGetter (*this); + } + + virtual void call (void *, SerialArgs &, SerialArgs &ret) const + { + mark_called (); + ret.write (m_v); + } + +private: + R m_v; +}; + +template +Methods +constant (const std::string &name, const R &v, const std::string &doc = std::string ()) +{ + return Methods (new ConstantValueGetter (name, v, doc)); +} + // 0 argument #define _COUNT 0 diff --git a/src/gsi/unit_tests/gsiExpression.cc b/src/gsi/unit_tests/gsiExpression.cc index 5cff80f1d..8ed0b2238 100644 --- a/src/gsi/unit_tests/gsiExpression.cc +++ b/src/gsi/unit_tests/gsiExpression.cc @@ -235,7 +235,7 @@ TEST(2) v = e.parse ("var b=B.new; b.bx(-1)").execute (); EXPECT_EQ (v.to_string (), std::string ("xz")); /* - @@@ No detailed type analysis for ambiguity resolution so far: + TODO: No detailed type analysis for ambiguity resolution so far: v = e.parse ("var b=B.new; b.bx('hello', 1)").execute (); EXPECT_EQ (v.to_string (), std::string ("20.5")); */ @@ -297,10 +297,8 @@ TEST(2) v = e.parse ("b.amember_ref.a5(177)").execute (); EXPECT_EQ (v.to_string (), std::string ("nil")); - // @@@ v = e.parse ("b.amember_or_nil(true)").execute (); EXPECT_EQ (v.to_string (), std::string ("A: 177")); - // @@@ bool error = false; try { diff --git a/src/klayout.pri b/src/klayout.pri index cc76f9e3a..c09f225ce 100644 --- a/src/klayout.pri +++ b/src/klayout.pri @@ -97,6 +97,12 @@ equals(HAVE_RUBY, "1") { } } +equals(HAVE_CRONOLOGY, "1") { + DEFINES += HAVE_CRONOLOGY + LIBS += $$CRONOLOGY_LIB + INCLUDEPATH += $$CRONOLOGY_INCLUDE +} + msvc { QMAKE_CXXFLAGS += \ @@ -111,6 +117,11 @@ msvc { } else { + CONFIG(gcov) { + QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage + QMAKE_LFLAGS += --coverage + } + QMAKE_CXXFLAGS_WARN_ON += \ -pedantic \ -Woverloaded-virtual \ diff --git a/src/lay/lay/doc/about/drc_ref.xml b/src/lay/lay/doc/about/drc_ref.xml index d78407e3e..a4a9cf55f 100644 --- a/src/lay/lay/doc/about/drc_ref.xml +++ b/src/lay/lay/doc/about/drc_ref.xml @@ -1,7 +1,7 @@ - + @@ -9,6 +9,7 @@ + diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml index 145019e06..37e7e3297 100644 --- a/src/lay/lay/doc/about/drc_ref_global.xml +++ b/src/lay/lay/doc/about/drc_ref_global.xml @@ -1,7 +1,7 @@ - + @@ -13,6 +13,15 @@ Most of them are convenience functions that basically act on some default object or provide function-like alternatives for the methods.

+

"antenna_check" - Performs an antenna check

+ +

Usage:

+
    +
  • antenna_check(gate, metal, ratio, [ diode_specs ... ])
  • +
+

+See Netter#antenna_check for a description of that function +

"box" - Creates a box object

Usage:

@@ -42,6 +51,15 @@ cell("MACRO") l1 = input(1, 0)

+

"clear_connections" - Clears all connections stored so far

+ +

Usage:

+
    +
  • clear_connections
  • +
+

+See Netter#clear_connections for a description of that function +

"clip" - Specifies clipped input on the default source

Usage:

@@ -60,6 +78,24 @@ clip(0.mm, 0.mm, 0.5.mm, 0.6.mm) l1 = input(1, 0)

+

"connect" - Specifies a connection between two layers

+ +

Usage:

+
    +
  • connect(a, b)
  • +
+

+See Netter#connect for a description of that function. +

+

"connect_global" - Specifies a connection to a global net

+ +

Usage:

+
    +
  • connect_global(l, name)
  • +
+

+See Netter#connect_global for a description of that function. +

"dbu" - Gets or sets the database unit to use

Usage:

@@ -79,6 +115,26 @@ for two layouts (i.e. take the largest common denominator). When the database unit is set, it must be set at the beginning of the script and before any operation that uses it.

+

"deep" - Enters deep (hierarchical) mode

+ +

Usage:

+
    +
  • deep
  • +
+

+In deep mode, the operations will be performed in a hierarchical fashion. +Sometimes this reduces the time and memory required for an operation, but this +will also add some overhead for the hierarchical analysis. +

+"deepness" is a property of layers. Layers created with "input" while in +deep mode carry hierarchy. Operations involving such layers at the only +or the first argument are carried out in hierarchical mode. +

+Hierarchical mode has some more implications, like "merged_semantics" being +implied always. Sometimes cell variants will be created. +

+Deep mode can be cancelled with tiles or flat. +

"edge" - Creates an edge object

Usage:

@@ -117,6 +173,15 @@ Similar to
log, but the message is printed formatted as an er

See Source#extent for a description of that function.

+

"extract_devices" - Extracts devices for a given device extractor and device layer selection

+ +

Usage:

+
    +
  • extract_devices(extractor, layer_hash)
  • +
+

+See Netter#extract_devices for a description of that function +

"flat" - Disables tiling mode

Usage:

@@ -144,14 +209,31 @@ In non-verbose more, nothing is printed.
  • input(args)
  • -See Source#input for a description of that function. +See Source#input for a description of that function. This method will fetch +polygons and labels. See polygons and labels for more specific versions of +this method.

    +

    "is_deep?" - Returns true, if in deep mode

    + +

    Usage:

    +
      +
    • is_deep?
    • +

    "is_tiled?" - Returns true, if in tiled mode

    Usage:

    • is_tiled?
    +

    "l2n_data" - Gets the internal LayoutToNetlist object for the default Netter

    + +

    Usage:

    +
      +
    • l2n_data
    • +
    +

    +See Netter#l2n_data for a description of that function +

    "labels" - Gets the labels (text) from an original layer

    Usage:

    @@ -228,6 +310,33 @@ verbose mode is enabled. After using that method, the log output is sent to the given file instead of the logger window or the terminal.

    +

    "make_layer" - Creates an empty polygon layer based on the hierarchical scheme selected

    + +

    Usage:

    +
      +
    • make_layer
    • +
    +

    +The intention of this method is to provide an empty polygon layer based on the +hierarchical scheme selected. This will create a new layer with the hierarchy +of the current layout in deep mode and a flat layer in flat mode. +This method is similar to polygon_layer, but the latter does not create +a hierarchical layer. Hence the layer created by make_layer is suitable +for use in device extraction for example, while the one +delivered by polygon_layer is not. +

    +On the other hand, a layer created by the make_layer method is not intended to be +filled with Layer#insert. +

    +

    "netter" - Creates a new netter object

    + +

    Usage:

    +
      +
    • netter
    • +
    +

    +See Netter for more details +

    "no_borders" - Reset the tile borders

    Usage:

    @@ -301,6 +410,17 @@ This function creates a polygon object. The arguments are the same than for the

    The intention of that method is to create an empty layer which can be filled with polygon-like objects using Layer#insert. +A similar method which creates a hierarchical layer in deep mode is +make_layer. This other layer is better suited for use with device extraction. +

    +

    "polygons" - Fetches the polygons (or shapes that can be converted to polygons) from the specified input from the default source

    + +

    Usage:

    +
      +
    • polygons(args)
    • +
    +

    +See Source#polygons for a description of that function.

    "report" - Specifies a report database for output

    @@ -459,7 +579,9 @@ predict.

    In tiling mode, the memory requirements are usually smaller (depending on the choice of the tile size) and multi-CPU support is enabled (see threads). -To disable tiling mode use flat. +To disable tiling mode use flat or deep. +

    +Tiling mode will disable deep mode (see deep).

    "verbose" - Sets or resets verbose mode

    diff --git a/src/lay/lay/doc/about/drc_ref_layer.xml b/src/lay/lay/doc/about/drc_ref_layer.xml index 9d3ad68de..e6639416a 100644 --- a/src/lay/lay/doc/about/drc_ref_layer.xml +++ b/src/lay/lay/doc/about/drc_ref_layer.xml @@ -1,7 +1,7 @@ - + @@ -225,7 +225,7 @@ deliver objects that can be converted into polygons. Such objects are of class <

    This method produces markers on the corners of the polygons. An angle criterion can be given which selects corners based on the angle of the connecting edges. Positive angles indicate a left turn -while negative angles indicate a right turn. Since polygons are oriented clockwise, postive angles +while negative angles indicate a right turn. Since polygons are oriented clockwise, positive angles indicate concave corners while negative ones indicate convex corners.

    The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default. @@ -584,6 +584,18 @@ The following images show the effect of the extents method: Applies to edge pair collections only. Returns the first edges of the edge pairs in the collection.

    +

    "flatten" - Flattens the layer

    + +

    Usage:

    +
      +
    • layer.flatten
    • +
    +

    +If the layer already is a flat one, this method does nothing. +If the layer is a hierarchical layer (an original layer or +a derived layer in deep mode), this method will convert it +to a flat collection of polygons, edges or edge pairs. +

    "holes" - Selects all polygon holes from the input

    Usage:

    @@ -759,6 +771,12 @@ is composed of multiple pieces, this method will not return true.

    See clean for a discussion of the clean state.

    +

    "is_deep?" - Returns true, if the layer is a deep (hierarchical) layer

    + +

    Usage:

    +
      +
    • layer.is_deep?
    • +

    "is_empty?" - Returns true, if the layer is empty

    Usage:

    diff --git a/src/lay/lay/doc/about/drc_ref_netter.xml b/src/lay/lay/doc/about/drc_ref_netter.xml new file mode 100644 index 000000000..334ca9c51 --- /dev/null +++ b/src/lay/lay/doc/about/drc_ref_netter.xml @@ -0,0 +1,233 @@ + + + + + + + +DRC Reference: Netter object + +

    +The Netter object provides services related to network extraction +from a layout. The relevant methods of this object are available +as global functions too where they act on a default incarnation +of the netter. Usually it's not required to instantiate a Netter +object, but it serves as a container for this functionality. +

    +An individual netter object can be created, if the netter results +need to be kept for multiple extractions. If you really need +a Netter object, use the global netter function: +

    +

    +# create a new Netter object:
    +nx = netter
    +nx.connect(poly, contact)
    +...
    +
    +

    +Network formation: +

    +A basic Service the Netter object provides is the formation of +connected networks of conductive shapes. To do so, the Netter +must be given a connection specification. This happens by calling +"connect" with two polygon layers. The Netter will then regard all +overlaps of shapes on these layers as connections between the +respective materials. Networks are the basis for netlist extraction, +network geometry deduction and the antenna check. +

    +Connections can be cleared with "clear_connections". If not, +connections add atop of the already defined ones. Here is an +example for the antenna check: +

    +

    +# build connction of poly+gate to metal1
    +connect(gate, poly)
    +connect(poly, contact)
    +connect(contact, metal1)
    +
    +# runs an antenna check for metal1 with a ratio of 50
    +m1_antenna_errors = antenna_check(gate, metal1, 50.0)
    +
    +# add connections to metal2
    +connect(metal1, via1)
    +connect(via1, metal2)
    +
    +# runs an antenna check for metal2 with a ratio of 70.0
    +m2_antenna_errors = antenna_check(gate, metal2, 70.0)
    +
    +# this will remove all connections made
    +clear_connections
    +...
    +
    +

    +Further functionality of the Netter object: +

    +More methods will be added in the future to support network-related features. +

    + +

    "antenna_check" - Performs an antenna check

    + +

    Usage:

    +
      +
    • antenna_check(gate, metal, ratio, [ diode_specs ... ])
    • +
    +

    +The antenna check is used to avoid plasma induced damage. Physically, +the damage happes if during the manufacturing of a metal layer with +plasma etching charge accumulates on the metal islands. On reaching a +certain threshold, this charge may discarge over gate oxide attached of +devices attached to such metal areas hence damaging it. +

    +Antenna checks are performed by collecting all connected nets up to +a certain metal layer and then computing the area of all metal shapes +and all connected gates of a certain kind (e.g. thin and thick oxide gates). +The ratio of metal area divided by the gate area must not exceed a certain +threshold. +

    +A simple antenna check is this: +

    +

    +poly = ... # poly layer
    +diff = ... # diffusion layer
    +contact = ... # contact layer
    +metal1 = ... # metal layer
    +
    +# compute gate area
    +gate = poly & diff
    +
    +# note that gate and poly have to be included - gate is
    +# a subset of poly, but forms the sensitive area
    +connect(gate, poly)
    +connect(poly, contact)
    +connect(contact, metal1)
    +errors = antenna_check(gate, metal1, 50.0)
    +
    +

    +Plasma induced damage can be rectified by including diodes +which create a safe current path for discharging the metal +islands. Such diodes can be identified with a recognition layer +(usually the diffusion area of a certain kind). You can include +such diode recognition layers in the antenna check. If a connection +is detected to a diode, the respective network is skipped: +

    +

    +...
    +diode = ... # diode recognition layer
    +
    +connect(diode, contact)
    +errors = antenna_check(gate, metal1, 50.0, diode)
    +
    +

    +You can also make diode connections decreases the +sensitivity of the antenna check depending on the size +of the diode. The following specification makes +diode connections increase the ratio threshold by +10 per square micrometer of diode area: +

    +

    +...
    +diode = ... # diode recognition layer
    +
    +connect(diode, contact)
    +# each square micrometer of diode area connected to a network
    +# will add 10 to the ratio:
    +errors = antenna_check(gate, metal1, 50.0, [ diode, 10.0 ])
    +
    +

    +Multiple diode specifications are allowed. Just add them +to the antenna_check call. +

    +The error shapes produced by the antenna check are copies +of the metal shapes on the metal layers of each network +violating the antenna rule. +

    +

    "clear_connections" - Clears all connections stored so far

    + +

    Usage:

    +
      +
    • clear_connections
    • +
    +

    +See connect for more details. +

    +

    "connect" - Specifies a connection between two layers

    + +

    Usage:

    +
      +
    • connect(a, b)
    • +
    +

    +a and b must be polygon layers. After calling this function, the +Netter regards all overlapping or touching shapes on these layers +to form an electrical connection between the materials formed by +these layers. This also implies intra-layer connections: shapes +on these layers touching or overlapping other shapes on these +layers will form bigger, electrically connected areas. +

    +Multiple connect calls must be made to form larger connectivity +stacks across multiple layers. Such stacks may include forks and +joins. +

    +Connections are accumulated. The connections defined so far +can be cleared with clear_connections. +

    +

    "connect_global" - Connects a layer with a global net

    + +

    Usage:

    +
      +
    • connect_global(l, name)
    • +
    +

    +Connects the shapes from the given layer l to a global net with the given name. +Global nets are common to all cells. Global nets automatically connect to parent +cells throughs implied pins. An example is the substrate (bulk) net which connects +to shapes belonging to tie-down diodes. +

    +

    "extract_devices" - Extracts devices based on the given extractor class, name and device layer selection

    + +

    Usage:

    +
      +
    • extract_devices(extractor, layer_hash)
    • +
    +

    +Runs the device extraction for given device extractor class. +

    +The device extractor is either an instance of one of the predefined extractor +classes (e.g. DeviceExtractorMOS4Transistor) or a custom class. It provides the +algorithms for deriving the device parameters from the device geometry. It needs +several device recognition layers which are passed in the layer hash. +

    +Each device class (e.g. n-MOS/p-MOS or high Vt/low Vt) needs it's own instance +of device extractor. The device extractor beside the algorithm and specific +extraction settings defines the name of the device to be built. +

    +The layer hash is a map of device type specific functional names (key) and +polygon layers (value). Here is an example: +

    +

    +deep
    +
    +nwell   = input(1, 0)
    +active  = input(2, 0)
    +poly    = input(3, 0)
    +bulk    = make_layer   # renders an empty layer used for putting the terminals on
    +
    +nactive = active - nwell      # active area of NMOS
    +nsd     = nactive - poly      # source/drain area
    +gate    = nactive & poly  # gate area
    +
    +mos4_ex = DeviceExtractorMOS4Transistor::new("NMOS4")
    +extract_devices(mos4_ex, { :SD => nsd, :G => gate, :P => poly, :W => bulk })
    +
    +

    +

    "l2n_data" - Gets the internal LayoutToNetlist object

    + +

    Usage:

    +
      +
    • l2n_data
    • +
    +

    +The LayoutToNetlist object provides access to the internal details of +the netter object. +

    +
    diff --git a/src/lay/lay/doc/about/drc_ref_source.xml b/src/lay/lay/doc/about/drc_ref_source.xml index fd0266638..81a640cff 100644 --- a/src/lay/lay/doc/about/drc_ref_source.xml +++ b/src/lay/lay/doc/about/drc_ref_source.xml @@ -1,7 +1,7 @@ - + @@ -94,6 +94,19 @@ Some filter expressions are:
  • METAL (17/0) : A layer named "METAL" or layer 17, datatype 0 (for GDS, which does not have names)
  • +

    +Layers created with "input" contain both texts and polygons. There is a subtle +difference between flat and deep mode: in flat mode, texts are not visible in polygon +operations. In deep mode, texts appear as small 2x2 DBU rectangles. In flat mode, +some operations such as clipping are not fully supported for texts. Also, texts will +vanish in most polygon operations such as booleans etc. +

    +Texts can later be selected on the layer returned by "input" with the Layer#texts method. +

    +If you don't want to see texts, use polygons to create an input layer with polygon data +only. If you only want to see texts, use labels to create an input layer with texts only. +

    +Use the global version of "input" without a source object to address the default source.

    "labels" - Gets the labels (texts) from an input layer

    @@ -106,21 +119,11 @@ not have names)

    Creates a layer with the labels from the given layer of the source. -The layer can be specified by layer and optionally datatype, by a LayerInfo -object or by a sequence of filters. -Filters are expressions describing ranges -of layers and/or datatype numbers or layer names. Multiple filters -can be given and all layers matching at least one of these filter -expressions are joined to render the label collection. See "input" for -more details about the input layer specification.

    -Label layers currently can only be passed to an output layer. -Processing of labels is not supported. See "texts" for a way to filter -texts and use the text locations in geometrical operations. +This method is identical to input, but takes only texts from the given input +layer.

    -

    -labels(1, 0).output(100, 0)
    -
    +Use the global version of "label" without a source object to address the default source.

    "layers" - Gets the layers the source contains

    @@ -152,6 +155,15 @@ layers.each { |l| (input(l) & clip_box).output(l) }
    • layout
    +

    "make_layer" - Creates an empty polygon layer based on the hierarchy of the layout

    + +

    Usage:

    +
      +
    • make_layer
    • +
    +

    +This method delivers a new empty original layer. +

    "overlapping" - Specifies input selected from a region in overlapping mode

    Usage:

    @@ -168,6 +180,24 @@ the specified rectangle.
    touching is a similar method which delivers shapes touching the search region with their bounding box (without the requirement to overlap)

    +

    "polygons" - Gets the polygon shapes (or shapes that can be converted polygons) from an input layer

    + +

    Usage:

    +
      +
    • source.polygons(layer)
    • +
    • source.polygons(layer, datatype)
    • +
    • source.polygons(layer_into)
    • +
    • source.polygons(filter, ...)
    • +
    +

    +Creates a layer with the polygon shapes from the given layer of the source. +With "polygon shapes" we mean all kind of shapes that can be converted to polygons. +Those are boxes, paths and real polygons. +

    +This method is identical to input with respect to the options supported. +

    +Use the global version of "polygons" without a source object to address the default source. +

    "select" - Adds cell name expressions to the cell filters

    Usage:

    diff --git a/src/lay/lay/doc/images/drc_and1.png b/src/lay/lay/doc/images/drc_and1.png index 9ea749e22..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_and1.png and b/src/lay/lay/doc/images/drc_and1.png differ diff --git a/src/lay/lay/doc/images/drc_and2.png b/src/lay/lay/doc/images/drc_and2.png index 87bd69131..57f0e81fd 100644 Binary files a/src/lay/lay/doc/images/drc_and2.png and b/src/lay/lay/doc/images/drc_and2.png differ diff --git a/src/lay/lay/doc/images/drc_and3.png b/src/lay/lay/doc/images/drc_and3.png index ccb06bbc0..57f0e81fd 100644 Binary files a/src/lay/lay/doc/images/drc_and3.png and b/src/lay/lay/doc/images/drc_and3.png differ diff --git a/src/lay/lay/doc/images/drc_centers1.png b/src/lay/lay/doc/images/drc_centers1.png index 4237e55f9..59d0ff14c 100644 Binary files a/src/lay/lay/doc/images/drc_centers1.png and b/src/lay/lay/doc/images/drc_centers1.png differ diff --git a/src/lay/lay/doc/images/drc_centers2.png b/src/lay/lay/doc/images/drc_centers2.png index 07d6244de..59d0ff14c 100644 Binary files a/src/lay/lay/doc/images/drc_centers2.png and b/src/lay/lay/doc/images/drc_centers2.png differ diff --git a/src/lay/lay/doc/images/drc_corners1.png b/src/lay/lay/doc/images/drc_corners1.png index 52a4e8f4d..f94788dcd 100644 Binary files a/src/lay/lay/doc/images/drc_corners1.png and b/src/lay/lay/doc/images/drc_corners1.png differ diff --git a/src/lay/lay/doc/images/drc_corners2.png b/src/lay/lay/doc/images/drc_corners2.png index bad8c9dcb..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_corners2.png and b/src/lay/lay/doc/images/drc_corners2.png differ diff --git a/src/lay/lay/doc/images/drc_corners3.png b/src/lay/lay/doc/images/drc_corners3.png index 061cdf486..f94788dcd 100644 Binary files a/src/lay/lay/doc/images/drc_corners3.png and b/src/lay/lay/doc/images/drc_corners3.png differ diff --git a/src/lay/lay/doc/images/drc_enc1.png b/src/lay/lay/doc/images/drc_enc1.png index cbf117d95..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_enc1.png and b/src/lay/lay/doc/images/drc_enc1.png differ diff --git a/src/lay/lay/doc/images/drc_enc2.png b/src/lay/lay/doc/images/drc_enc2.png index 7b00902c5..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_enc2.png and b/src/lay/lay/doc/images/drc_enc2.png differ diff --git a/src/lay/lay/doc/images/drc_end_segments1.png b/src/lay/lay/doc/images/drc_end_segments1.png index bfac5785d..59d0ff14c 100644 Binary files a/src/lay/lay/doc/images/drc_end_segments1.png and b/src/lay/lay/doc/images/drc_end_segments1.png differ diff --git a/src/lay/lay/doc/images/drc_end_segments2.png b/src/lay/lay/doc/images/drc_end_segments2.png index f3e0abb21..59d0ff14c 100644 Binary files a/src/lay/lay/doc/images/drc_end_segments2.png and b/src/lay/lay/doc/images/drc_end_segments2.png differ diff --git a/src/lay/lay/doc/images/drc_extended1.png b/src/lay/lay/doc/images/drc_extended1.png index c919d576f..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_extended1.png and b/src/lay/lay/doc/images/drc_extended1.png differ diff --git a/src/lay/lay/doc/images/drc_extended2.png b/src/lay/lay/doc/images/drc_extended2.png index 1bb227e9a..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_extended2.png and b/src/lay/lay/doc/images/drc_extended2.png differ diff --git a/src/lay/lay/doc/images/drc_extended3.png b/src/lay/lay/doc/images/drc_extended3.png index 6f3983891..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_extended3.png and b/src/lay/lay/doc/images/drc_extended3.png differ diff --git a/src/lay/lay/doc/images/drc_extended4.png b/src/lay/lay/doc/images/drc_extended4.png index bfe992ca1..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_extended4.png and b/src/lay/lay/doc/images/drc_extended4.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs1.png b/src/lay/lay/doc/images/drc_extent_refs1.png index 072bdc417..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs1.png and b/src/lay/lay/doc/images/drc_extent_refs1.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs10.png b/src/lay/lay/doc/images/drc_extent_refs10.png index 517188045..dfd44ddff 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs10.png and b/src/lay/lay/doc/images/drc_extent_refs10.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs11.png b/src/lay/lay/doc/images/drc_extent_refs11.png index 1fd5f3fed..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs11.png and b/src/lay/lay/doc/images/drc_extent_refs11.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs12.png b/src/lay/lay/doc/images/drc_extent_refs12.png index 18b60de9e..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs12.png and b/src/lay/lay/doc/images/drc_extent_refs12.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs13.png b/src/lay/lay/doc/images/drc_extent_refs13.png index 20009cf24..61bb4f1fc 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs13.png and b/src/lay/lay/doc/images/drc_extent_refs13.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs20.png b/src/lay/lay/doc/images/drc_extent_refs20.png index a96e78264..29c074218 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs20.png and b/src/lay/lay/doc/images/drc_extent_refs20.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs21.png b/src/lay/lay/doc/images/drc_extent_refs21.png index 98fb1e73b..29c074218 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs21.png and b/src/lay/lay/doc/images/drc_extent_refs21.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs22.png b/src/lay/lay/doc/images/drc_extent_refs22.png index f91892d44..203a4aa22 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs22.png and b/src/lay/lay/doc/images/drc_extent_refs22.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs23.png b/src/lay/lay/doc/images/drc_extent_refs23.png index 13dfdf121..d1e2dc0e6 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs23.png and b/src/lay/lay/doc/images/drc_extent_refs23.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs24.png b/src/lay/lay/doc/images/drc_extent_refs24.png index e6432eee9..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs24.png and b/src/lay/lay/doc/images/drc_extent_refs24.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs25.png b/src/lay/lay/doc/images/drc_extent_refs25.png index e5222b8f1..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs25.png and b/src/lay/lay/doc/images/drc_extent_refs25.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs26.png b/src/lay/lay/doc/images/drc_extent_refs26.png index dbe8ed39a..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs26.png and b/src/lay/lay/doc/images/drc_extent_refs26.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs27.png b/src/lay/lay/doc/images/drc_extent_refs27.png index 6d7a1a4fe..d1e2dc0e6 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs27.png and b/src/lay/lay/doc/images/drc_extent_refs27.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs30.png b/src/lay/lay/doc/images/drc_extent_refs30.png index 2890ce143..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs30.png and b/src/lay/lay/doc/images/drc_extent_refs30.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs31.png b/src/lay/lay/doc/images/drc_extent_refs31.png index e3c48d877..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs31.png and b/src/lay/lay/doc/images/drc_extent_refs31.png differ diff --git a/src/lay/lay/doc/images/drc_extents1.png b/src/lay/lay/doc/images/drc_extents1.png index fba7447aa..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_extents1.png and b/src/lay/lay/doc/images/drc_extents1.png differ diff --git a/src/lay/lay/doc/images/drc_extents2.png b/src/lay/lay/doc/images/drc_extents2.png index e42d9e5d3..203a4aa22 100644 Binary files a/src/lay/lay/doc/images/drc_extents2.png and b/src/lay/lay/doc/images/drc_extents2.png differ diff --git a/src/lay/lay/doc/images/drc_holes.png b/src/lay/lay/doc/images/drc_holes.png index e3f08ddff..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_holes.png and b/src/lay/lay/doc/images/drc_holes.png differ diff --git a/src/lay/lay/doc/images/drc_hulls.png b/src/lay/lay/doc/images/drc_hulls.png index cb2e7d726..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_hulls.png and b/src/lay/lay/doc/images/drc_hulls.png differ diff --git a/src/lay/lay/doc/images/drc_in.png b/src/lay/lay/doc/images/drc_in.png index 958166227..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_in.png and b/src/lay/lay/doc/images/drc_in.png differ diff --git a/src/lay/lay/doc/images/drc_inside.png b/src/lay/lay/doc/images/drc_inside.png index f7066b7ae..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_inside.png and b/src/lay/lay/doc/images/drc_inside.png differ diff --git a/src/lay/lay/doc/images/drc_inside_part.png b/src/lay/lay/doc/images/drc_inside_part.png index dcd45b3bc..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_inside_part.png and b/src/lay/lay/doc/images/drc_inside_part.png differ diff --git a/src/lay/lay/doc/images/drc_interacting.png b/src/lay/lay/doc/images/drc_interacting.png index 78e8bc476..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_interacting.png and b/src/lay/lay/doc/images/drc_interacting.png differ diff --git a/src/lay/lay/doc/images/drc_join1.png b/src/lay/lay/doc/images/drc_join1.png index f2a722e8d..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_join1.png and b/src/lay/lay/doc/images/drc_join1.png differ diff --git a/src/lay/lay/doc/images/drc_join2.png b/src/lay/lay/doc/images/drc_join2.png index 6f25d45dc..48f1b93fd 100644 Binary files a/src/lay/lay/doc/images/drc_join2.png and b/src/lay/lay/doc/images/drc_join2.png differ diff --git a/src/lay/lay/doc/images/drc_merged1.png b/src/lay/lay/doc/images/drc_merged1.png index 1005e3d30..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_merged1.png and b/src/lay/lay/doc/images/drc_merged1.png differ diff --git a/src/lay/lay/doc/images/drc_merged2.png b/src/lay/lay/doc/images/drc_merged2.png index e60fb3060..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_merged2.png and b/src/lay/lay/doc/images/drc_merged2.png differ diff --git a/src/lay/lay/doc/images/drc_merged3.png b/src/lay/lay/doc/images/drc_merged3.png index 5e2590ecb..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_merged3.png and b/src/lay/lay/doc/images/drc_merged3.png differ diff --git a/src/lay/lay/doc/images/drc_merged4.png b/src/lay/lay/doc/images/drc_merged4.png index 8a0207a0c..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_merged4.png and b/src/lay/lay/doc/images/drc_merged4.png differ diff --git a/src/lay/lay/doc/images/drc_middle1.png b/src/lay/lay/doc/images/drc_middle1.png index 7ac5afbd7..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_middle1.png and b/src/lay/lay/doc/images/drc_middle1.png differ diff --git a/src/lay/lay/doc/images/drc_moved1.png b/src/lay/lay/doc/images/drc_moved1.png index 7536a9088..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_moved1.png and b/src/lay/lay/doc/images/drc_moved1.png differ diff --git a/src/lay/lay/doc/images/drc_not1.png b/src/lay/lay/doc/images/drc_not1.png index 3f6bd28d1..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_not1.png and b/src/lay/lay/doc/images/drc_not1.png differ diff --git a/src/lay/lay/doc/images/drc_not2.png b/src/lay/lay/doc/images/drc_not2.png index ae43ae1a5..57f0e81fd 100644 Binary files a/src/lay/lay/doc/images/drc_not2.png and b/src/lay/lay/doc/images/drc_not2.png differ diff --git a/src/lay/lay/doc/images/drc_not3.png b/src/lay/lay/doc/images/drc_not3.png index 6b4ef3e90..57f0e81fd 100644 Binary files a/src/lay/lay/doc/images/drc_not3.png and b/src/lay/lay/doc/images/drc_not3.png differ diff --git a/src/lay/lay/doc/images/drc_not_in.png b/src/lay/lay/doc/images/drc_not_in.png index d1829a7be..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_not_in.png and b/src/lay/lay/doc/images/drc_not_in.png differ diff --git a/src/lay/lay/doc/images/drc_not_inside.png b/src/lay/lay/doc/images/drc_not_inside.png index ed735fdf2..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_not_inside.png and b/src/lay/lay/doc/images/drc_not_inside.png differ diff --git a/src/lay/lay/doc/images/drc_not_interacting.png b/src/lay/lay/doc/images/drc_not_interacting.png index 97ee23413..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_not_interacting.png and b/src/lay/lay/doc/images/drc_not_interacting.png differ diff --git a/src/lay/lay/doc/images/drc_not_outside.png b/src/lay/lay/doc/images/drc_not_outside.png index d46555108..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_not_outside.png and b/src/lay/lay/doc/images/drc_not_outside.png differ diff --git a/src/lay/lay/doc/images/drc_not_overlapping.png b/src/lay/lay/doc/images/drc_not_overlapping.png index 95b70cf05..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_not_overlapping.png and b/src/lay/lay/doc/images/drc_not_overlapping.png differ diff --git a/src/lay/lay/doc/images/drc_or1.png b/src/lay/lay/doc/images/drc_or1.png index 4a3f4fe30..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_or1.png and b/src/lay/lay/doc/images/drc_or1.png differ diff --git a/src/lay/lay/doc/images/drc_or2.png b/src/lay/lay/doc/images/drc_or2.png index 58d0456dc..48f1b93fd 100644 Binary files a/src/lay/lay/doc/images/drc_or2.png and b/src/lay/lay/doc/images/drc_or2.png differ diff --git a/src/lay/lay/doc/images/drc_outside.png b/src/lay/lay/doc/images/drc_outside.png index 15aa2a81e..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_outside.png and b/src/lay/lay/doc/images/drc_outside.png differ diff --git a/src/lay/lay/doc/images/drc_outside_part.png b/src/lay/lay/doc/images/drc_outside_part.png index efe5a56a2..57f0e81fd 100644 Binary files a/src/lay/lay/doc/images/drc_outside_part.png and b/src/lay/lay/doc/images/drc_outside_part.png differ diff --git a/src/lay/lay/doc/images/drc_overlap1.png b/src/lay/lay/doc/images/drc_overlap1.png index dcb26b5ae..db427d86f 100644 Binary files a/src/lay/lay/doc/images/drc_overlap1.png and b/src/lay/lay/doc/images/drc_overlap1.png differ diff --git a/src/lay/lay/doc/images/drc_overlap2.png b/src/lay/lay/doc/images/drc_overlap2.png index 337826a7e..db427d86f 100644 Binary files a/src/lay/lay/doc/images/drc_overlap2.png and b/src/lay/lay/doc/images/drc_overlap2.png differ diff --git a/src/lay/lay/doc/images/drc_overlapping.png b/src/lay/lay/doc/images/drc_overlapping.png index c645a190f..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_overlapping.png and b/src/lay/lay/doc/images/drc_overlapping.png differ diff --git a/src/lay/lay/doc/images/drc_raw1.png b/src/lay/lay/doc/images/drc_raw1.png index cf881ed93..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_raw1.png and b/src/lay/lay/doc/images/drc_raw1.png differ diff --git a/src/lay/lay/doc/images/drc_raw2.png b/src/lay/lay/doc/images/drc_raw2.png index d0730e934..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_raw2.png and b/src/lay/lay/doc/images/drc_raw2.png differ diff --git a/src/lay/lay/doc/images/drc_raw3.png b/src/lay/lay/doc/images/drc_raw3.png index ba85651f4..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_raw3.png and b/src/lay/lay/doc/images/drc_raw3.png differ diff --git a/src/lay/lay/doc/images/drc_rotated1.png b/src/lay/lay/doc/images/drc_rotated1.png index ab74db467..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_rotated1.png and b/src/lay/lay/doc/images/drc_rotated1.png differ diff --git a/src/lay/lay/doc/images/drc_rounded_corners.png b/src/lay/lay/doc/images/drc_rounded_corners.png index 02e694e19..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_rounded_corners.png and b/src/lay/lay/doc/images/drc_rounded_corners.png differ diff --git a/src/lay/lay/doc/images/drc_scaled1.png b/src/lay/lay/doc/images/drc_scaled1.png index 5e9030c59..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_scaled1.png and b/src/lay/lay/doc/images/drc_scaled1.png differ diff --git a/src/lay/lay/doc/images/drc_separation1.png b/src/lay/lay/doc/images/drc_separation1.png index b958deeb1..db427d86f 100644 Binary files a/src/lay/lay/doc/images/drc_separation1.png and b/src/lay/lay/doc/images/drc_separation1.png differ diff --git a/src/lay/lay/doc/images/drc_sized1.png b/src/lay/lay/doc/images/drc_sized1.png index ec34fef66..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_sized1.png and b/src/lay/lay/doc/images/drc_sized1.png differ diff --git a/src/lay/lay/doc/images/drc_sized2.png b/src/lay/lay/doc/images/drc_sized2.png index 2949de1b1..936c7167e 100644 Binary files a/src/lay/lay/doc/images/drc_sized2.png and b/src/lay/lay/doc/images/drc_sized2.png differ diff --git a/src/lay/lay/doc/images/drc_sized3.png b/src/lay/lay/doc/images/drc_sized3.png index 61a4b8ca7..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_sized3.png and b/src/lay/lay/doc/images/drc_sized3.png differ diff --git a/src/lay/lay/doc/images/drc_sized4.png b/src/lay/lay/doc/images/drc_sized4.png index e96e99747..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_sized4.png and b/src/lay/lay/doc/images/drc_sized4.png differ diff --git a/src/lay/lay/doc/images/drc_sized5.png b/src/lay/lay/doc/images/drc_sized5.png index 89d97d50e..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_sized5.png and b/src/lay/lay/doc/images/drc_sized5.png differ diff --git a/src/lay/lay/doc/images/drc_sized6.png b/src/lay/lay/doc/images/drc_sized6.png index f045511c0..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_sized6.png and b/src/lay/lay/doc/images/drc_sized6.png differ diff --git a/src/lay/lay/doc/images/drc_space1.png b/src/lay/lay/doc/images/drc_space1.png index 8f91261fb..db427d86f 100644 Binary files a/src/lay/lay/doc/images/drc_space1.png and b/src/lay/lay/doc/images/drc_space1.png differ diff --git a/src/lay/lay/doc/images/drc_space2.png b/src/lay/lay/doc/images/drc_space2.png index df7dd0b40..db427d86f 100644 Binary files a/src/lay/lay/doc/images/drc_space2.png and b/src/lay/lay/doc/images/drc_space2.png differ diff --git a/src/lay/lay/doc/images/drc_space3.png b/src/lay/lay/doc/images/drc_space3.png index eabee35ee..db427d86f 100644 Binary files a/src/lay/lay/doc/images/drc_space3.png and b/src/lay/lay/doc/images/drc_space3.png differ diff --git a/src/lay/lay/doc/images/drc_start_segments1.png b/src/lay/lay/doc/images/drc_start_segments1.png index 3644f360d..59d0ff14c 100644 Binary files a/src/lay/lay/doc/images/drc_start_segments1.png and b/src/lay/lay/doc/images/drc_start_segments1.png differ diff --git a/src/lay/lay/doc/images/drc_start_segments2.png b/src/lay/lay/doc/images/drc_start_segments2.png index 800ec51dc..59d0ff14c 100644 Binary files a/src/lay/lay/doc/images/drc_start_segments2.png and b/src/lay/lay/doc/images/drc_start_segments2.png differ diff --git a/src/lay/lay/doc/images/drc_transformed1.png b/src/lay/lay/doc/images/drc_transformed1.png index ee33932d0..7060e1797 100644 Binary files a/src/lay/lay/doc/images/drc_transformed1.png and b/src/lay/lay/doc/images/drc_transformed1.png differ diff --git a/src/lay/lay/doc/images/drc_width1.png b/src/lay/lay/doc/images/drc_width1.png index b14ce9ddf..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_width1.png and b/src/lay/lay/doc/images/drc_width1.png differ diff --git a/src/lay/lay/doc/images/drc_width2.png b/src/lay/lay/doc/images/drc_width2.png index c9503e182..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_width2.png and b/src/lay/lay/doc/images/drc_width2.png differ diff --git a/src/lay/lay/doc/images/drc_width3.png b/src/lay/lay/doc/images/drc_width3.png index 23287c77d..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_width3.png and b/src/lay/lay/doc/images/drc_width3.png differ diff --git a/src/lay/lay/doc/images/drc_width4.png b/src/lay/lay/doc/images/drc_width4.png index 1e706274f..261fe8010 100644 Binary files a/src/lay/lay/doc/images/drc_width4.png and b/src/lay/lay/doc/images/drc_width4.png differ diff --git a/src/lay/lay/doc/images/drc_with_angle1.png b/src/lay/lay/doc/images/drc_with_angle1.png index 22dbcf75e..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_with_angle1.png and b/src/lay/lay/doc/images/drc_with_angle1.png differ diff --git a/src/lay/lay/doc/images/drc_with_angle2.png b/src/lay/lay/doc/images/drc_with_angle2.png index 8ccd9ec0c..48f1b93fd 100644 Binary files a/src/lay/lay/doc/images/drc_with_angle2.png and b/src/lay/lay/doc/images/drc_with_angle2.png differ diff --git a/src/lay/lay/doc/images/drc_with_angle3.png b/src/lay/lay/doc/images/drc_with_angle3.png index 26ed55013..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_with_angle3.png and b/src/lay/lay/doc/images/drc_with_angle3.png differ diff --git a/src/lay/lay/doc/images/drc_with_angle4.png b/src/lay/lay/doc/images/drc_with_angle4.png index 9d938f550..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_with_angle4.png and b/src/lay/lay/doc/images/drc_with_angle4.png differ diff --git a/src/lay/lay/doc/images/drc_xor1.png b/src/lay/lay/doc/images/drc_xor1.png index 16191d3ca..f3ba86b26 100644 Binary files a/src/lay/lay/doc/images/drc_xor1.png and b/src/lay/lay/doc/images/drc_xor1.png differ diff --git a/src/lay/lay/doc/images/drc_xor2.png b/src/lay/lay/doc/images/drc_xor2.png index 2dd68bc8c..48f1b93fd 100644 Binary files a/src/lay/lay/doc/images/drc_xor2.png and b/src/lay/lay/doc/images/drc_xor2.png differ diff --git a/src/lay/lay/layHelpResources.qrc b/src/lay/lay/layHelpResources.qrc index 33ed75312..8e74b2904 100644 --- a/src/lay/lay/layHelpResources.qrc +++ b/src/lay/lay/layHelpResources.qrc @@ -134,6 +134,7 @@ doc/about/drc_ref_layer.xml doc/about/drc_ref_source.xml doc/about/drc_ref_global.xml + doc/about/drc_ref_netter.xml doc/about/packages.xml diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 3a0c96d9c..43e81307e 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -379,24 +379,10 @@ TextProgressDelegate::TextProgressDelegate (MainWindow *mw, int verbosity) // .. nothing yet .. } -void TextProgressDelegate::set_progress_can_cancel (bool f) +void TextProgressDelegate::update_progress (tl::Progress *progress) { - if (!mp_mw->set_progress_can_cancel (f)) { - lay::TextProgress::set_progress_can_cancel (f); - } -} - -void TextProgressDelegate::set_progress_text (const std::string &text) -{ - if (!mp_mw->set_progress_text (text)) { - lay::TextProgress::set_progress_text (text); - } -} - -void TextProgressDelegate::set_progress_value (double v, const std::string &value) -{ - if (!mp_mw->set_progress_value (v, value)) { - lay::TextProgress::set_progress_value (v, value); + if (!mp_mw->update_progress (progress)) { + lay::TextProgress::update_progress (progress); } } @@ -4642,42 +4628,27 @@ MainWindow::progress_get_widget () const } bool -MainWindow::set_progress_can_cancel (bool f) +MainWindow::update_progress (tl::Progress *progress) { - if (mp_progress_dialog) { - mp_progress_dialog->set_can_cancel (f); - return true; - } else if (isVisible () && mp_progress_widget) { - mp_progress_widget->set_can_cancel (f); - return true; - } else { - return false; - } -} + bool can_cancel = progress->can_cancel (); + std::string text = progress->desc (); + std::string value = progress->formatted_value (); + double v = progress->value (); -bool -MainWindow::set_progress_text (const std::string &text) -{ if (mp_progress_dialog) { + + mp_progress_dialog->set_can_cancel (can_cancel); mp_progress_dialog->set_text (text); - return true; - } else if (isVisible () && mp_progress_widget) { - mp_progress_widget->set_text (text); - return true; - } else { - return false; - } -} - -bool -MainWindow::set_progress_value (double v, const std::string &value) -{ - if (mp_progress_dialog) { mp_progress_dialog->set_value (v, value); return true; + } else if (isVisible () && mp_progress_widget) { + + mp_progress_widget->set_can_cancel (can_cancel); + mp_progress_widget->set_text (text); mp_progress_widget->set_value (v, value); return true; + } else { return false; } diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 5880b2d52..2d18a1d01 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -105,9 +105,7 @@ class TextProgressDelegate public: TextProgressDelegate (MainWindow *mw, int verbosity); - virtual void set_progress_can_cancel (bool f); - virtual void set_progress_text (const std::string &text); - virtual void set_progress_value (double v, const std::string &value); + virtual void update_progress (tl::Progress *progress); virtual void show_progress_bar (bool show); virtual bool progress_wants_widget () const; virtual void progress_add_widget (QWidget *widget); @@ -381,17 +379,7 @@ public: /** * @brief Implementation of the lay::ProgressBar interface: set the flag indicating whether we can cancel the operation */ - bool set_progress_can_cancel(bool f); - - /** - * @brief Implementation of the lay::ProgressBar interface: set the text to display - */ - bool set_progress_text (const std::string &text); - - /** - * @brief Implementation of the lay::ProgressBar interface: set the value to display - */ - bool set_progress_value (double v, const std::string &value); + bool update_progress (tl::Progress *progress); /** * @brief Implementation of the lay::ProgressBar interface: Returns a value indicating whether a progress widget is wanted diff --git a/src/lay/lay/layProgress.cc b/src/lay/lay/layProgress.cc index 0e3dcae7b..5303692ea 100644 --- a/src/lay/lay/layProgress.cc +++ b/src/lay/lay/layProgress.cc @@ -92,7 +92,7 @@ ProgressReporter::register_object (tl::Progress *progress) QApplication::instance ()->installEventFilter (this); } - mp_objects.push_back (progress); // this keeps the outmost one visible. push_front would make the latest one visible. + mp_objects.push_back (*progress); // this keeps the outmost one visible. push_front would make the latest one visible. // mp_objects.push_front (progress); if (m_start_time == tl::Clock () && ! m_pw_visible) { @@ -110,36 +110,27 @@ ProgressReporter::register_object (tl::Progress *progress) void ProgressReporter::unregister_object (tl::Progress *progress) { - for (std::list ::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) { - - if (*k == progress) { - - mp_objects.erase (k); - - // close or refresh window - if (mp_objects.empty ()) { - if (m_pw_visible) { - set_visible (false); - } - m_start_time = tl::Clock (); - } - - update_and_yield (); - break; + progress->unlink (); + // close or refresh window + if (mp_objects.empty ()) { + if (m_pw_visible) { + set_visible (false); } - + m_start_time = tl::Clock (); } + update_and_yield (); + if (mp_objects.empty ()) { QApplication::instance ()->removeEventFilter (this); } } void -ProgressReporter::trigger (tl::Progress *progress) +ProgressReporter::trigger (tl::Progress * /*progress*/) { - if (! mp_objects.empty () && mp_objects.front () == progress) { + if (! mp_objects.empty ()) { // make dialog visible after some time has passed if (! m_pw_visible && (tl::Clock::current () - m_start_time).seconds () > 1.0) { set_visible (true); @@ -164,8 +155,8 @@ ProgressReporter::yield (tl::Progress * /*progress*/) void ProgressReporter::signal_break () { - for (std::list ::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) { - (*k)->signal_break (); + for (tl::list::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) { + k->signal_break (); } } @@ -174,13 +165,10 @@ ProgressReporter::update_and_yield () { if (m_pw_visible && ! mp_objects.empty ()) { if (mp_pb) { - // not supported yet: mp_pb->progress_widget ()->set_title ((*mp_objects.begin ())->title ()); - mp_pb->set_progress_can_cancel (mp_objects.front ()->can_cancel ()); - mp_pb->set_progress_text (mp_objects.front ()->desc ()); - mp_pb->set_progress_value (mp_objects.front ()->value (), mp_objects.front ()->formatted_value ()); + mp_pb->update_progress (mp_objects.first ()); QWidget *w = mp_pb->progress_get_widget (); if (w) { - mp_objects.front ()->render_progress (w); + mp_objects.first ()->render_progress (w); } } process_events (); // Qt4 seems to need this @@ -214,8 +202,8 @@ ProgressReporter::set_visible (bool vis) if (mp_pb) { if (!vis) { mp_pb->progress_remove_widget (); - } else if (mp_pb->progress_wants_widget () && mp_objects.front ()) { - mp_pb->progress_add_widget (mp_objects.front ()->progress_widget ()); + } else if (mp_pb->progress_wants_widget () && mp_objects.first ()) { + mp_pb->progress_add_widget (mp_objects.first ()->progress_widget ()); } } diff --git a/src/lay/lay/layProgress.h b/src/lay/lay/layProgress.h index 34cda7a8f..c7a6361f7 100644 --- a/src/lay/lay/layProgress.h +++ b/src/lay/lay/layProgress.h @@ -47,9 +47,7 @@ class LAY_PUBLIC ProgressBar public: virtual ~ProgressBar () { } - virtual void set_progress_can_cancel (bool f) = 0; - virtual void set_progress_text (const std::string &text) = 0; - virtual void set_progress_value (double v, const std::string &value) = 0; + virtual void update_progress (tl::Progress *progress) = 0; virtual bool progress_wants_widget () const { return false; } virtual void progress_add_widget (QWidget * /*widget*/) { } virtual void progress_remove_widget () { } @@ -79,7 +77,7 @@ public: } private: - std::list mp_objects; + tl::list mp_objects; tl::Clock m_start_time; lay::ProgressBar *mp_pb; bool m_pw_visible; diff --git a/src/lay/lay/layTextProgress.cc b/src/lay/lay/layTextProgress.cc index 4185dd010..1f9539f8b 100644 --- a/src/lay/lay/layTextProgress.cc +++ b/src/lay/lay/layTextProgress.cc @@ -33,21 +33,19 @@ TextProgress::TextProgress (int verbosity) // .. nothing yet .. } -void TextProgress::set_progress_can_cancel (bool /*f*/) -{ - // .. nothing yet .. -} - -void TextProgress::set_progress_text (const std::string &text) +void TextProgress::update_progress (tl::Progress *progress) { + std::string text = progress->desc (); if (m_progress_text != text && tl::verbosity () >= m_verbosity) { tl::info << text << " .."; m_progress_text = text; } -} -void TextProgress::set_progress_value (double /*value*/, const std::string &value) -{ + std::string value = progress->formatted_value (); + for (tl::Progress *p = progress->next (); p != 0; p = p->next ()) { + value += " " + p->formatted_value (); + } + if (m_progress_value != value && tl::verbosity () >= m_verbosity) { tl::info << ".. " << value; m_progress_value = value; diff --git a/src/lay/lay/layTextProgress.h b/src/lay/lay/layTextProgress.h index 26032368c..c1d5cf12f 100644 --- a/src/lay/lay/layTextProgress.h +++ b/src/lay/lay/layTextProgress.h @@ -46,9 +46,7 @@ public: */ TextProgress (int verbosity); - virtual void set_progress_can_cancel (bool f); - virtual void set_progress_text (const std::string &text); - virtual void set_progress_value (double v, const std::string &value); + virtual void update_progress (tl::Progress *progress); virtual void show_progress_bar (bool show); private: diff --git a/src/laybasic/laybasic/layDialogs.h b/src/laybasic/laybasic/layDialogs.h index 243923fa7..04ad110b1 100644 --- a/src/laybasic/laybasic/layDialogs.h +++ b/src/laybasic/laybasic/layDialogs.h @@ -370,9 +370,10 @@ public: bool exec_dialog (int &mode_x, int &mode_y, bool &visible_only, bool &adjust_calls); -private: +private slots: void button_clicked (); +private: Ui::AlignCellOptionsDialog *mp_ui; }; diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index 96f58c136..6f8d53f4f 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -296,7 +296,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S int layer = l->second.layer; int datatype = l->second.datatype; - db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); + db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::EdgePairs | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); while (! shape.at_end ()) { progress_checkpoint (); @@ -307,6 +307,9 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S write_polygon (layer, datatype, sf, *shape, multi_xy, max_vertex_count, layout, shape->prop_id ()); } else if (shape->is_edge ()) { write_edge (layer, datatype, sf, *shape, layout, shape->prop_id ()); + } else if (shape->is_edge_pair ()) { + write_edge (layer, datatype, sf, shape->edge_pair ().first (), layout, shape->prop_id ()); + write_edge (layer, datatype, sf, shape->edge_pair ().second (), layout, shape->prop_id ()); } else if (shape->is_path ()) { if (no_zero_length_paths && (shape->path_length () - shape->path_extensions ().first - shape->path_extensions ().second) == 0) { // eliminate the zero-width path @@ -580,8 +583,12 @@ GDS2WriterBase::write_path (int layer, int datatype, double sf, const db::Shape void GDS2WriterBase::write_edge (int layer, int datatype, double sf, const db::Shape &shape, const db::Layout &layout, db::properties_id_type prop_id) { - db::Edge e (shape.edge ()); + write_edge (layer, datatype, sf, shape.edge (), layout, prop_id); +} +void +GDS2WriterBase::write_edge (int layer, int datatype, double sf, const db::Edge &e, const db::Layout &layout, db::properties_id_type prop_id) +{ write_record_size (4); write_record (sPATH); diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h index d0c5dd6cf..ef8249b48 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h @@ -134,6 +134,11 @@ protected: */ void write_edge (int layer, int datatype, double sf, const db::Shape &shape, const db::Layout &layout, db::properties_id_type prop_id); + /** + * @brief Writes an edge + */ + void write_edge (int layer, int datatype, double sf, const db::Edge &edge, const db::Layout &layout, db::properties_id_type prop_id); + /** * @brief Write a shape as path */ diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc index c2e649f79..a55ffcc23 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.cc @@ -338,13 +338,13 @@ NetTracerSymbolInfo::parse (tl::Extractor &ex) // ----------------------------------------------------------------------------------- // Net implementation -Net::Net () +NetTracerNet::NetTracerNet () : m_dbu (0.001), m_incomplete (true), m_trace_path (false) { // .. nothing yet .. } -Net::Net (const NetTracer &tracer, const db::ICplxTrans &trans, const db::Layout &layout, db::cell_index_type cell_index, const std::string &layout_filename, const std::string &layout_name, const NetTracerData &data) +NetTracerNet::NetTracerNet (const NetTracer &tracer, const db::ICplxTrans &trans, const db::Layout &layout, db::cell_index_type cell_index, const std::string &layout_filename, const std::string &layout_name, const NetTracerData &data) : m_name (tracer.name ()), m_incomplete (tracer.incomplete ()), m_trace_path (false) { m_dbu = layout.dbu (); @@ -406,7 +406,7 @@ Net::Net (const NetTracer &tracer, const db::ICplxTrans &trans, const db::Layout } std::vector -Net::export_net (db::Layout &layout, db::Cell &export_cell) +NetTracerNet::export_net (db::Layout &layout, db::Cell &export_cell) { std::vector new_layers; std::map layer_map; @@ -446,7 +446,7 @@ Net::export_net (db::Layout &layout, db::Cell &export_cell) } const std::string & -Net::cell_name (db::cell_index_type cell_index) const +NetTracerNet::cell_name (db::cell_index_type cell_index) const { std::map ::const_iterator cn = m_cell_names.find (cell_index); if (cn != m_cell_names.end ()) { @@ -458,7 +458,7 @@ Net::cell_name (db::cell_index_type cell_index) const } db::LayerProperties -Net::representative_layer_for (unsigned int log_layer) const +NetTracerNet::representative_layer_for (unsigned int log_layer) const { std::map >::const_iterator l = m_layers.find (log_layer); if (l != m_layers.end ()) { @@ -469,7 +469,7 @@ Net::representative_layer_for (unsigned int log_layer) const } db::LayerProperties -Net::layer_for (unsigned int log_layer) const +NetTracerNet::layer_for (unsigned int log_layer) const { std::map >::const_iterator l = m_layers.find (log_layer); if (l != m_layers.end ()) { @@ -480,7 +480,7 @@ Net::layer_for (unsigned int log_layer) const } void -Net::define_layer (unsigned int l, const db::LayerProperties &lp, const db::LayerProperties &lp_representative) +NetTracerNet::define_layer (unsigned int l, const db::LayerProperties &lp, const db::LayerProperties &lp_representative) { m_layers.insert (std::make_pair (l, std::make_pair (lp, lp_representative))); } diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h index 50b588636..03b32127c 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracerIO.h @@ -157,7 +157,7 @@ private: std::string m_expression; }; -class DB_PLUGIN_PUBLIC Net +class DB_PLUGIN_PUBLIC NetTracerNet { public: typedef std::vector ::const_iterator iterator; @@ -165,12 +165,12 @@ public: /** * @brief Default constructor */ - Net (); + NetTracerNet (); /** * @brief Constructor */ - Net (const db::NetTracer &tracer, const db::ICplxTrans &trans, const db::Layout &layout, db::cell_index_type cell_index, const std::string &layout_filename, const std::string &layout_name, const db::NetTracerData &data); + NetTracerNet (const db::NetTracer &tracer, const db::ICplxTrans &trans, const db::Layout &layout, db::cell_index_type cell_index, const std::string &layout_filename, const std::string &layout_name, const db::NetTracerData &data); /** * @brief Iterate the shapes (begin) diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc index 033bf2f59..c43ec76f1 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc @@ -103,7 +103,7 @@ NetTracerDialog::~NetTracerDialog () void NetTracerDialog::clear_nets () { - for (std::vector ::iterator n = mp_nets.begin (); n != mp_nets.end (); ++n) { + for (std::vector ::iterator n = mp_nets.begin (); n != mp_nets.end (); ++n) { delete *n; } mp_nets.clear (); @@ -178,7 +178,7 @@ NetTracerDialog::mouse_click_event (const db::DPoint &p, unsigned int buttons, b stop_search_box = db::DBox (m_mouse_first_point, m_mouse_first_point).enlarged (db::DVector (l, l)); } - db::Net *net = do_trace (start_search_box, stop_search_box, trace_path); + db::NetTracerNet *net = do_trace (start_search_box, stop_search_box, trace_path); if (net) { // create a new net taking the shapes from the tracer @@ -220,7 +220,7 @@ NetTracerDialog::redo_trace_clicked () { BEGIN_PROTECTED - std::set selected_nets; + std::set selected_nets; QList selected_items = net_list->selectedItems (); for (QList::const_iterator item = selected_items.begin (); item != selected_items.end (); ++item) { @@ -230,18 +230,18 @@ BEGIN_PROTECTED } } - std::vector nets; + std::vector nets; nets.swap (mp_nets); m_net_index = 1; std::vector new_selection; - for (std::vector ::const_iterator n = nets.begin (); n != nets.end (); ++n) { + for (std::vector ::const_iterator n = nets.begin (); n != nets.end (); ++n) { try { - db::Net *net = do_trace ((*n)->start_search_box (), (*n)->stop_search_box (), (*n)->trace_path_flag ()); + db::NetTracerNet *net = do_trace ((*n)->start_search_box (), (*n)->stop_search_box (), (*n)->trace_path_flag ()); if (net) { // create a new net taking the shapes from the tracer @@ -281,7 +281,7 @@ BEGIN_PROTECTED END_PROTECTED } -db::Net * +db::NetTracerNet * NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &stop_search_box, bool trace_path) { unsigned int start_layer = 0; @@ -401,7 +401,7 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto } else { // create a new net taking the shapes from the tracer - db::Net *net = new db::Net (net_tracer, db::ICplxTrans (cv.context_trans ()), cv->layout (), cv.cell_index (), cv->filename (), cv->name (), tracer_data); + db::NetTracerNet *net = new db::NetTracerNet (net_tracer, db::ICplxTrans (cv.context_trans ()), cv->layout (), cv.cell_index (), cv->filename (), cv->name (), tracer_data); net->set_start_search_box (start_search_box); net->set_stop_search_box (stop_search_box); net->set_trace_path_flag (trace_path); @@ -712,7 +712,7 @@ NetTracerDialog::update_info () std::map::perimeter_type> statinfo_perimeter; size_t tot_shapes = 0; - for (db::Net::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { + for (db::NetTracerNet::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { if (tot_shapes++ >= max_shapes) { incomplete = true; @@ -917,7 +917,7 @@ NetTracerDialog::update_info () bool incomplete = false; std::set labels; - for (db::Net::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { + for (db::NetTracerNet::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { if (net_shape->shape ().is_text ()) { @@ -962,7 +962,7 @@ NetTracerDialog::update_info () incomplete = false; std::set cells; - for (db::Net::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { + for (db::NetTracerNet::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { if (cells.size () >= max_cells) { incomplete = true; @@ -1238,7 +1238,7 @@ BEGIN_PROTECTED w.start_element ("net"); - const db::Net *net = mp_nets[item_index]; + const db::NetTracerNet *net = mp_nets[item_index]; w.start_element ("name"); w.cdata (net->name ()); @@ -1262,7 +1262,7 @@ BEGIN_PROTECTED w.start_element ("shapes"); - for (db::Net::iterator net_shape = net->begin (); net_shape != net->end (); ++net_shape) { + for (db::NetTracerNet::iterator net_shape = net->begin (); net_shape != net->end (); ++net_shape) { w.start_element ("element"); @@ -1443,7 +1443,7 @@ NetTracerDialog::adjust_view () db::DBox cv_bbox; // Create markers for the shapes - for (db::Net::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { + for (db::NetTracerNet::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end (); ++net_shape) { // Find the actual layer by looking up the layer properties .. std::map ::const_iterator ll = llmap.find (net_shape->layer ()); @@ -1534,7 +1534,7 @@ NetTracerDialog::update_highlights () QColor net_color = mp_nets [item_index]->color (); // Create markers for the shapes - for (db::Net::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end () && n_marker < m_max_marker_count; ++net_shape) { + for (db::NetTracerNet::iterator net_shape = mp_nets [item_index]->begin (); net_shape != mp_nets [item_index]->end () && n_marker < m_max_marker_count; ++net_shape) { // Find the actual layer by looking up the layer properties .. std::map ::const_iterator ll = llmap.find (net_shape->layer ()); diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h index ff43e88fc..12cbcd2ed 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h @@ -38,7 +38,7 @@ namespace db { - class Net; + class NetTracerNet; } namespace lay @@ -82,7 +82,7 @@ protected slots: void redo_trace_clicked (); private: - std::vector mp_nets; + std::vector mp_nets; std::vector mp_markers; unsigned int m_cv_index; int m_net_index; @@ -113,7 +113,7 @@ private: void update_info (); void layer_list_changed (int index); void release_mouse (); - db::Net *do_trace (const db::DBox &start_search_box, const db::DBox &stop_search_box, bool trace_path); + db::NetTracerNet *do_trace (const db::DBox &start_search_box, const db::DBox &stop_search_box, bool trace_path); }; } diff --git a/src/plugins/tools/net_tracer/unit_tests/dbNetTracer.cc b/src/plugins/tools/net_tracer/unit_tests/dbNetTracer.cc index 512448ad2..179f38584 100644 --- a/src/plugins/tools/net_tracer/unit_tests/dbNetTracer.cc +++ b/src/plugins/tools/net_tracer/unit_tests/dbNetTracer.cc @@ -76,18 +76,18 @@ static db::NetTracerShape find_shape (const db::Layout &layout, const db::Cell & } #endif -static db::Net trace (db::NetTracer &tracer, const db::Layout &layout, const db::Cell &cell, const db::NetTracerTechnologyComponent &tc, unsigned int l_start, const db::Point &p_start) +static db::NetTracerNet trace (db::NetTracer &tracer, const db::Layout &layout, const db::Cell &cell, const db::NetTracerTechnologyComponent &tc, unsigned int l_start, const db::Point &p_start) { db::NetTracerData tracer_data = tc.get_tracer_data (layout); tracer.trace (layout, cell, p_start, l_start, tracer_data); - return db::Net (tracer, db::ICplxTrans (), layout, cell.cell_index (), std::string (), std::string (), tracer_data); + return db::NetTracerNet (tracer, db::ICplxTrans (), layout, cell.cell_index (), std::string (), std::string (), tracer_data); } -static db::Net trace (db::NetTracer &tracer, const db::Layout &layout, const db::Cell &cell, const db::NetTracerTechnologyComponent &tc, unsigned int l_start, const db::Point &p_start, unsigned int l_stop, const db::Point &p_stop) +static db::NetTracerNet trace (db::NetTracer &tracer, const db::Layout &layout, const db::Cell &cell, const db::NetTracerTechnologyComponent &tc, unsigned int l_start, const db::Point &p_start, unsigned int l_stop, const db::Point &p_stop) { db::NetTracerData tracer_data = tc.get_tracer_data (layout); tracer.trace (layout, cell, p_start, l_start, p_stop, l_stop, tracer_data); - return db::Net (tracer, db::ICplxTrans (), layout, cell.cell_index (), std::string (), std::string (), tracer_data); + return db::NetTracerNet (tracer, db::ICplxTrans (), layout, cell.cell_index (), std::string (), std::string (), tracer_data); } void run_test (tl::TestBase *_this, const std::string &file, const db::NetTracerTechnologyComponent &tc, const db::LayerProperties &lp_start, const db::Point &p_start, const std::string &file_au, const char *net_name = 0) @@ -107,7 +107,7 @@ void run_test (tl::TestBase *_this, const std::string &file, const db::NetTracer const db::Cell &cell = layout_org.cell (*layout_org.begin_top_down ()); db::NetTracer tracer; - db::Net net = trace (tracer, layout_org, cell, tc, layer_for (layout_org, lp_start), p_start); + db::NetTracerNet net = trace (tracer, layout_org, cell, tc, layer_for (layout_org, lp_start), p_start); if (net_name) { EXPECT_EQ (net.name (), std::string (net_name)); @@ -141,7 +141,7 @@ void run_test2 (tl::TestBase *_this, const std::string &file, const db::NetTrace const db::Cell &cell = layout_org.cell (*layout_org.begin_top_down ()); db::NetTracer tracer; - db::Net net = trace (tracer, layout_org, cell, tc, layer_for (layout_org, lp_start), p_start, layer_for (layout_org, lp_stop), p_stop); + db::NetTracerNet net = trace (tracer, layout_org, cell, tc, layer_for (layout_org, lp_start), p_start, layer_for (layout_org, lp_stop), p_stop); if (net_name) { EXPECT_EQ (net.name (), std::string (net_name)); diff --git a/src/pyastub/pya.cc b/src/pyastub/pya.cc index 99b65d1be..f9c239668 100644 --- a/src/pyastub/pya.cc +++ b/src/pyastub/pya.cc @@ -22,13 +22,14 @@ #include "pya.h" +#include "tlInternational.h" namespace pya { static void fail (const char *file, int line) { - throw tl::ScriptError (tl::to_string (QObject::tr ("Python support not compiled in")).c_str (), file, line, "missing_feature", std::vector ()); + throw tl::ScriptError (tl::to_string (tr ("Python support not compiled in")).c_str (), file, line, "missing_feature", std::vector ()); } static PythonInterpreter *sp_pya_interpreter = 0; diff --git a/src/rba/unit_tests/rba.cc b/src/rba/unit_tests/rba.cc index 9952f464a..5c6d4175e 100644 --- a/src/rba/unit_tests/rba.cc +++ b/src/rba/unit_tests/rba.cc @@ -108,7 +108,11 @@ RUBYTEST (dbLayout, "dbLayout.rb") RUBYTEST (dbLayoutTest, "dbLayoutTest.rb") RUBYTEST (dbLayoutDiff, "dbLayoutDiff.rb") RUBYTEST (dbLayoutQuery, "dbLayoutQuery.rb") +RUBYTEST (dbLayoutToNetlist, "dbLayoutToNetlist.rb") RUBYTEST (dbMatrix, "dbMatrix.rb") +RUBYTEST (dbNetlist, "dbNetlist.rb") +RUBYTEST (dbNetlistDeviceClasses, "dbNetlistDeviceClasses.rb") +RUBYTEST (dbNetlistWriterTests, "dbNetlistWriterTests.rb") RUBYTEST (dbPathTest, "dbPathTest.rb") RUBYTEST (dbPCells, "dbPCells.rb") RUBYTEST (dbPointTest, "dbPointTest.rb") diff --git a/src/rdb/rdb/gsiDeclRdb.cc b/src/rdb/rdb/gsiDeclRdb.cc index e79c978c5..16cee465b 100644 --- a/src/rdb/rdb/gsiDeclRdb.cc +++ b/src/rdb/rdb/gsiDeclRdb.cc @@ -267,9 +267,27 @@ static void scan_layer3 (rdb::Category *cat, const db::Layout &layout, unsigned rdb::scan_layer (cat, layout, layer, from_cell, levels); } -static void scan_shapes (rdb::Category *cat, const db::RecursiveShapeIterator &iter) +static void scan_shapes (rdb::Category *cat, const db::RecursiveShapeIterator &iter, bool flat) { - rdb::scan_layer (cat, iter); + rdb::scan_layer (cat, iter, flat); +} + +static void scan_region (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::Region ®ion, bool flat) +{ + std::pair it = region.begin_iter (); + rdb::scan_layer (cat, cell, trans * it.second, it.first, flat); +} + +static void scan_edges (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::Edges &edges, bool flat) +{ + std::pair it = edges.begin_iter (); + rdb::scan_layer (cat, cell, trans * it.second, it.first, flat); +} + +static void scan_edge_pairs (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::EdgePairs &edge_pairs, bool flat) +{ + std::pair it = edge_pairs.begin_iter (); + rdb::scan_layer (cat, cell, trans * it.second, it.first, flat); } Class decl_RdbCategory ("rdb", "RdbCategory", @@ -289,13 +307,41 @@ Class decl_RdbCategory ("rdb", "RdbCategory", "\n" "This method has been introduced in version 0.23." ) + - gsi::method_ext ("scan_shapes", &scan_shapes, + gsi::method_ext ("scan_shapes", &scan_shapes, gsi::arg ("iter"), gsi::arg ("flat", false), "@brief Scans the polygon or edge shapes from the shape iterator into the category\n" - "@args iter\n" "Creates RDB items for each polygon or edge shape read from the iterator and puts them into this category.\n" "A similar, but lower-level method is \\ReportDatabase#create_items with a \\RecursiveShapeIterator argument.\n" + "In contrast to \\ReportDatabase#create_items, 'scan_shapes' can also produce hierarchical databases " + "if the \\flat argument is false. In this case, the hierarchy the recursive shape iterator traverses is " + "copied into the report database using sample references.\n" "\n" - "This method has been introduced in version 0.23.\n" + "This method has been introduced in version 0.23. The flat mode argument has been added in version 0.26.\n" + ) + + gsi::method_ext ("scan_collection", &scan_region, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("region"), gsi::arg ("flat", false), + "@brief Turns the given region into a hierarchical or flat report database\n" + "The exact behavior depends on the nature of the region. If the region is a hierarchical (original or deep) region " + "and the 'flat' argument is false, this method will produce a hierarchical report database in the given category. " + "The 'cell_id' parameter is ignored in this case. Sample references will be produced to supply " + "minimal instantiation information.\n" + "\n" + "If the region is a flat one or the 'flat' argument is true, the region's polygons will be produced as " + "report database items in this category and in the cell given by 'cell_id'.\n" + "\n" + "The transformation argument needs to supply the dbu-to-micron transformation.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + gsi::method_ext ("scan_collection", &scan_edges, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("edges"), gsi::arg ("flat", false), + "@brief Turns the given edge collection into a hierarchical or flat report database\n" + "This a another flavour of \\scan_collection accepting an edge collection.\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + gsi::method_ext ("scan_collection", &scan_edge_pairs, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("edge_pairs"), gsi::arg ("flat", false), + "@brief Turns the given edge pair collection into a hierarchical or flat report database\n" + "This a another flavour of \\scan_collection accepting an edge pair collection.\n" + "\n" + "This method has been introduced in version 0.26.\n" ) + gsi::method_ext ("scan_layer", &scan_layer1, "@brief Scans a layer from a layout into this category\n" @@ -966,98 +1012,19 @@ void database_set_tag_description (rdb::Database *db, rdb::id_type tag, const st db->set_tag_description (tag, d); } -void create_items_from_iterator (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::RecursiveShapeIterator &iter) -{ - tl_assert (iter.layout ()); - double dbu = iter.layout ()->dbu (); - - for (db::RecursiveShapeIterator i = iter; !i.at_end (); ++i) { - std::auto_ptr value (rdb::ValueBase::create_from_shape (*i, db::CplxTrans (dbu) * i.trans ())); - if (value.get ()) { - rdb::Item *item = db->create_item (cell_id, cat_id); - item->values ().add (value.release ()); - } - } -} - -void create_items_from_shapes (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shapes &shapes) -{ - for (db::Shapes::shape_iterator s = shapes.begin (db::ShapeIterator::All); !s.at_end (); ++s) { - std::auto_ptr value (rdb::ValueBase::create_from_shape (*s, trans)); - if (value.get ()) { - rdb::Item *item = db->create_item (cell_id, cat_id); - item->values ().add (value.release ()); - } - } -} - -void create_item_from_shape (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shape &shape) -{ - std::auto_ptr value (rdb::ValueBase::create_from_shape (shape, trans)); - if (value.get ()) { - rdb::Item *item = db->create_item (cell_id, cat_id); - item->values ().add (value.release ()); - } -} - -void create_items_from_region (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Region &collection) -{ - typedef db::Region::const_iterator iter; - - for (iter o = collection.begin (); ! o.at_end (); ++o) { - rdb::Item *item = db->create_item (cell_id, cat_id); - item->values ().add (new rdb::Value (o->transformed (trans))); - } -} - -void create_items_from_edges (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Edges &collection) -{ - typedef db::Edges::const_iterator iter; - - for (iter o = collection.begin (); ! o.at_end (); ++o) { - rdb::Item *item = db->create_item (cell_id, cat_id); - item->values ().add (new rdb::Value (o->transformed (trans))); - } -} - -void create_items_from_edge_pairs (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::EdgePairs &collection) -{ - typedef db::EdgePairs::const_iterator iter; - - for (iter o = collection.begin (); o != collection.end (); ++o) { - rdb::Item *item = db->create_item (cell_id, cat_id); - item->values ().add (new rdb::Value (o->transformed (trans))); - } -} - void create_items_from_polygon_array (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const std::vector &collection) { - typedef std::vector::const_iterator iter; - - for (iter o = collection.begin (); o != collection.end (); ++o) { - rdb::Item *item = db->create_item (cell_id, cat_id); - item->values ().add (new rdb::Value (o->transformed (trans))); - } + rdb::create_items_from_sequence (db, cell_id, cat_id, trans, collection.begin (), collection.end ()); } void create_items_from_edge_array (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const std::vector &collection) { - typedef std::vector::const_iterator iter; - - for (iter o = collection.begin (); o != collection.end (); ++o) { - rdb::Item *item = db->create_item (cell_id, cat_id); - item->values ().add (new rdb::Value (o->transformed (trans))); - } + rdb::create_items_from_sequence (db, cell_id, cat_id, trans, collection.begin (), collection.end ()); } void create_items_from_edge_pair_array (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const std::vector &collection) { - typedef std::vector::const_iterator iter; - - for (iter o = collection.begin (); o != collection.end (); ++o) { - rdb::Item *item = db->create_item (cell_id, cat_id); - item->values ().add (new rdb::Value (o->transformed (trans))); - } + rdb::create_items_from_sequence (db, cell_id, cat_id, trans, collection.begin (), collection.end ()); } static rdb::Item *create_item (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id) @@ -1081,9 +1048,8 @@ static rdb::Item *create_item_from_objects (rdb::Database *db, rdb::Cell *cell, } Class decl_ReportDatabase ("rdb", "ReportDatabase", - gsi::constructor ("new", &create_rdb, + gsi::constructor ("new", &create_rdb, gsi::arg ("name"), "@brief Creates a report database\n" - "@args name\n" "@param name The name of the database\n" "The name of the database will be used in the user interface to refer to a certain database." ) + @@ -1093,9 +1059,8 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "in a human-readable form.\n" "@return The description string\n" ) + - gsi::method ("description=", &rdb::Database::set_description, + gsi::method ("description=", &rdb::Database::set_description, gsi::arg ("desc"), "@brief Sets the databases description\n" - "@args desc\n" "@param desc The description string\n" ) + gsi::method ("generator", &rdb::Database::generator, @@ -1104,9 +1069,8 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "In a later version this should allow to rerun the tool that created the report.\n" "@return The generator string\n" ) + - gsi::method ("generator=", &rdb::Database::set_generator, + gsi::method ("generator=", &rdb::Database::set_generator, gsi::arg ("generator"), "@brief Sets the generator string\n" - "@args generator\n" "@param generator The generator string\n" ) + gsi::method ("filename", &rdb::Database::filename, @@ -1126,9 +1090,8 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "This property must be set to establish a proper hierarchical context for a hierarchical report database. " "@return The top cell name\n" ) + - gsi::method ("top_cell_name=", &rdb::Database::set_top_cell_name, + gsi::method ("top_cell_name=", &rdb::Database::set_top_cell_name, gsi::arg ("cell_name"), "@brief Sets the top cell name string\n" - "@args cell_name\n" "@param cell_name The top cell name\n" ) + gsi::method ("original_file", &rdb::Database::original_file, @@ -1136,14 +1099,12 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "The original file name is supposed to describe the file from which this report database was generated. " "@return The original file name and path\n" ) + - gsi::method ("original_file=", &rdb::Database::set_original_file, + gsi::method ("original_file=", &rdb::Database::set_original_file, gsi::arg ("path"), "@brief Sets the original file name and path\n" - "@args path\n" "@param path The path\n" ) + - gsi::method_ext ("tag_id", &database_tag_id, + gsi::method_ext ("tag_id", &database_tag_id, gsi::arg ("name"), "@brief Gets the tag ID for a given tag name\n" - "@args name\n" "@param name The tag name\n" "@return The corresponding tag ID\n" "Tags are used to tag items in the database and to specify tagged (named) values. " @@ -1153,9 +1114,8 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "\n" "\\tag_id handles system tags while \\user_tag_id handles user tags.\n" ) + - gsi::method_ext ("user_tag_id", &database_user_tag_id, + gsi::method_ext ("user_tag_id", &database_user_tag_id, gsi::arg ("name"), "@brief Gets the tag ID for a given user tag name\n" - "@args name\n" "@param name The user tag name\n" "@return The corresponding tag ID\n" "This method will always succeed and the tag will be created if it does not exist yet. " @@ -1163,23 +1123,20 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "\n" "This method has been added in version 0.24.\n" ) + - gsi::method_ext ("set_tag_description", &database_set_tag_description, + gsi::method_ext ("set_tag_description", &database_set_tag_description, gsi::arg ("tag_id"), gsi::arg ("description"), "@brief Sets the tag description for the given tag ID\n" - "@args tag_id, description\n" "@param tag_id The ID of the tag\n" "@param description The description string\n" "See \\tag_id for a details about tags.\n" ) + - gsi::method_ext ("tag_description", &database_tag_description, + gsi::method_ext ("tag_description", &database_tag_description, gsi::arg ("tag_id"), "@brief Gets the tag description for the given tag ID\n" - "@args tag_id\n" "@param tag_id The ID of the tag\n" "@return The description string\n" "See \\tag_id for a details about tags.\n" ) + - gsi::method_ext ("tag_name", &database_tag_name, + gsi::method_ext ("tag_name", &database_tag_name, gsi::arg ("tag_id"), "@brief Gets the tag name for the given tag ID\n" - "@args tag_id\n" "@param tag_id The ID of the tag\n" "@return The name of the tag\n" "See \\tag_id for a details about tags.\n\n" @@ -1188,54 +1145,45 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", gsi::iterator_ext ("each_category", &database_begin_categories, &database_end_categories, "@brief Iterates over all top-level categories\n" ) + - gsi::method ("create_category", (rdb::Category *(rdb::Database::*) (const std::string &)) &rdb::Database::create_category, + gsi::method ("create_category", (rdb::Category *(rdb::Database::*) (const std::string &)) &rdb::Database::create_category, gsi::arg ("name"), "@brief Creates a new top level category\n" - "@args name\n" "@param name The name of the category\n" ) + - gsi::method ("create_category", (rdb::Category *(rdb::Database::*) (rdb::Category *, const std::string &)) &rdb::Database::create_category, + gsi::method ("create_category", (rdb::Category *(rdb::Database::*) (rdb::Category *, const std::string &)) &rdb::Database::create_category, gsi::arg ("parent"), gsi::arg ("name"), "@brief Creates a new sub-category\n" - "@args parent,name\n" "@param parent The category under which the category should be created\n" "@param name The name of the category\n" ) + - gsi::method ("category_by_path", &rdb::Database::category_by_name, + gsi::method ("category_by_path", &rdb::Database::category_by_name, gsi::arg ("path"), "@brief Gets a category by path\n" - "@args path\n" "@param path The full path to the category starting from the top level (subcategories separated by dots)\n" "@return The (const) category object or nil if the name is not valid\n" ) + - gsi::method ("category_by_id", &rdb::Database::category_by_id, + gsi::method ("category_by_id", &rdb::Database::category_by_id, gsi::arg ("id"), "@brief Gets a category by ID\n" - "@args id\n" "@return The (const) category object or nil if the ID is not valid\n" ) + - gsi::method ("create_cell", (rdb::Cell *(rdb::Database::*) (const std::string &)) &rdb::Database::create_cell, + gsi::method ("create_cell", (rdb::Cell *(rdb::Database::*) (const std::string &)) &rdb::Database::create_cell, gsi::arg ("name"), "@brief Creates a new cell\n" - "@args name\n" "@param name The name of the cell\n" ) + - gsi::method ("create_cell", (rdb::Cell *(rdb::Database::*) (const std::string &, const std::string &)) &rdb::Database::create_cell, + gsi::method ("create_cell", (rdb::Cell *(rdb::Database::*) (const std::string &, const std::string &)) &rdb::Database::create_cell, gsi::arg ("name"), gsi::arg ("variant"), "@brief Creates a new cell, potentially as a variant for a cell with the same name\n" - "@args name, variant\n" "@param name The name of the cell\n" "@param variant The variant name of the cell\n" ) + - gsi::method ("variants", &rdb::Database::variants, + gsi::method ("variants", &rdb::Database::variants, gsi::arg ("name"), "@brief Gets the variants for a given cell name\n" - "@args name\n" "@param name The basic name of the cell\n" "@return An array of ID's representing cells that are variants for the given base name\n" ) + - gsi::method ("cell_by_qname", &rdb::Database::cell_by_qname, + gsi::method ("cell_by_qname", &rdb::Database::cell_by_qname, gsi::arg ("qname"), "@brief Returns the cell for a given qualified name\n" - "@args qname\n" "@param qname The qualified name of the cell (name plus variant name optionally)\n" "@return The cell object or nil if no such cell exists\n" ) + - gsi::method ("cell_by_id", &rdb::Database::cell_by_id, + gsi::method ("cell_by_id", &rdb::Database::cell_by_id, gsi::arg ("id"), "@brief Returns the cell for a given ID\n" - "@args id\n" "@param id The ID of the cell\n" "@return The cell object or nil if no cell with that ID exists\n" ) + @@ -1281,12 +1229,13 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "\n" "This convenience method has been added in version 0.25.\n" ) + - gsi::method_ext ("create_items", &create_items_from_iterator, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("iter"), + gsi::method_ext ("create_items", &rdb::create_items_from_iterator, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("iter"), "@brief Creates new items from a shape iterator\n" "This method takes the shapes from the given iterator and produces items from them.\n" "It accepts various kind of shapes, such as texts, polygons, boxes and paths and " - "converts them to corresponding items. " - "A similar method, which is intended for production of polygon or edge error layers is \\RdbCategory#scan_shapes.\n" + "converts them to corresponding items. This method will produce a flat version of the shapes iterated by the shape iterator. " + "A similar method, which is intended for production of polygon or edge error layers and also provides hierarchical database " + "construction is \\RdbCategory#scan_shapes.\n" "\n" "This method has been introduced in version 0.25.3.\n" "\n" @@ -1294,7 +1243,7 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "@param category_id The ID of the category to which the item is associated\n" "@param iter The iterator (a \\RecursiveShapeIterator object) from which to take the items\n" ) + - gsi::method_ext ("create_item", &create_item_from_shape, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("shape"), + gsi::method_ext ("create_item", &rdb::create_item_from_shape, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("shape"), "@brief Creates a new item from a single shape\n" "This method produces an item from the given shape.\n" "It accepts various kind of shapes, such as texts, polygons, boxes and paths and " @@ -1308,7 +1257,7 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "@param shape The shape to take the geometrical object from\n" "@param trans The transformation to apply\n" ) + - gsi::method_ext ("create_items", &create_items_from_shapes, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("shapes"), + gsi::method_ext ("create_items", &rdb::create_items_from_shapes, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("shapes"), "@brief Creates new items from a shape container\n" "This method takes the shapes from the given container and produces items from them.\n" "It accepts various kind of shapes, such as texts, polygons, boxes and paths and " @@ -1322,52 +1271,61 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "@param shapes The shape container from which to take the items\n" "@param trans The transformation to apply\n" ) + - gsi::method_ext ("create_items", &create_items_from_region, + gsi::method_ext ("create_items", &rdb::create_items_from_region, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("region"), "@brief Creates new polygon items for the given cell/category combination\n" "For each polygon in the region a single item will be created. The value of the item will be this " "polygon.\n" "A transformation can be supplied which can be used for example to convert the " "object's dimensions to micron units by scaling by the database unit.\n" "\n" + "This method will also produce a flat version of the shapes inside the region. " + "\\RdbCategory#scan_region is a similar method which also supports construction of " + "hierarchical databases from deep regions.\n" + "\n" "This method has been introduced in version 0.23.\n" "\n" - "@args cell_id, category_id, trans, polygons\n" "@param cell_id The ID of the cell to which the item is associated\n" "@param category_id The ID of the category to which the item is associated\n" "@param trans The transformation to apply\n" "@param region The region (a \\Region object) containing the polygons for which to create items\n" ) + - gsi::method_ext ("create_items", &create_items_from_edges, + gsi::method_ext ("create_items", &rdb::create_items_from_edges, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("edges"), "@brief Creates new edge items for the given cell/category combination\n" "For each edge a single item will be created. The value of the item will be this " "edge.\n" "A transformation can be supplied which can be used for example to convert the " "object's dimensions to micron units by scaling by the database unit.\n" "\n" + "This method will also produce a flat version of the edges inside the edge collection. " + "\\RdbCategory#scan_edges is a similar method which also supports construction of " + "hierarchical databases from deep edge collections.\n" + "\n" "This method has been introduced in version 0.23.\n" "\n" - "@args cell_id, category_id, trans, edges\n" "@param cell_id The ID of the cell to which the item is associated\n" "@param category_id The ID of the category to which the item is associated\n" "@param trans The transformation to apply\n" "@param edges The list of edges (an \\Edges object) for which the items are created\n" ) + - gsi::method_ext ("create_items", &create_items_from_edge_pairs, + gsi::method_ext ("create_items", &rdb::create_items_from_edge_pairs, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("edge_pairs"), "@brief Creates new edge pair items for the given cell/category combination\n" "For each edge pair a single item will be created. The value of the item will be this " "edge pair.\n" "A transformation can be supplied which can be used for example to convert the " "object's dimensions to micron units by scaling by the database unit.\n" "\n" + "This method will also produce a flat version of the edge pairs inside the edge pair collection. " + "\\RdbCategory#scan_edge_pairs is a similar method which also supports construction of " + "hierarchical databases from deep edge pair collections.\n" + "\n" "This method has been introduced in version 0.23.\n" "\n" - "@args cell_id, category_id, trans, edge_pairs\n" "@param cell_id The ID of the cell to which the item is associated\n" "@param category_id The ID of the category to which the item is associated\n" "@param trans The transformation to apply\n" "@param edges The list of edge pairs (an \\EdgePairs object) for which the items are created\n" ) + - gsi::method_ext ("create_items", &create_items_from_polygon_array, + gsi::method_ext ("create_items", &create_items_from_polygon_array, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("array"), "@brief Creates new polygon items for the given cell/category combination\n" "For each polygon a single item will be created. The value of the item will be this " "polygon.\n" @@ -1376,13 +1334,12 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "\n" "This method has been introduced in version 0.23.\n" "\n" - "@args cell_id, category_id, trans, polygons\n" "@param cell_id The ID of the cell to which the item is associated\n" "@param category_id The ID of the category to which the item is associated\n" "@param trans The transformation to apply\n" "@param polygons The list of polygons for which the items are created\n" ) + - gsi::method_ext ("create_items", &create_items_from_edge_array, + gsi::method_ext ("create_items", &create_items_from_edge_array, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("array"), "@brief Creates new edge items for the given cell/category combination\n" "For each edge a single item will be created. The value of the item will be this " "edge.\n" @@ -1391,13 +1348,12 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "\n" "This method has been introduced in version 0.23.\n" "\n" - "@args cell_id, category_id, trans, edges\n" "@param cell_id The ID of the cell to which the item is associated\n" "@param category_id The ID of the category to which the item is associated\n" "@param trans The transformation to apply\n" "@param edges The list of edges for which the items are created\n" ) + - gsi::method_ext ("create_items", &create_items_from_edge_pair_array, + gsi::method_ext ("create_items", &create_items_from_edge_pair_array, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("array"), "@brief Creates new edge pair items for the given cell/category combination\n" "For each edge pair a single item will be created. The value of the item will be this " "edge pair.\n" @@ -1406,7 +1362,6 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "\n" "This method has been introduced in version 0.23.\n" "\n" - "@args cell_id, category_id, trans, edge_pairs\n" "@param cell_id The ID of the cell to which the item is associated\n" "@param category_id The ID of the category to which the item is associated\n" "@param trans The transformation to apply\n" diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index 2fe130fa1..3f8082a91 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -73,84 +73,84 @@ template class RDB_PUBLIC Value; // to_string implementations -template <> std::string Value::to_string () const +template <> RDB_PUBLIC std::string Value::to_string () const { return "float: " + tl::to_string (m_value); } -template <> std::string Value::to_string () const +template <> RDB_PUBLIC std::string Value::to_string () const { return "text: " + tl::to_word_or_quoted_string (m_value); } -template <> std::string Value::to_string () const +template <> RDB_PUBLIC std::string Value::to_string () const { return "polygon: " + m_value.to_string (); } -template <> std::string Value::to_string () const +template <> RDB_PUBLIC std::string Value::to_string () const { return "edge: " + m_value.to_string (); } -template <> std::string Value::to_string () const +template <> RDB_PUBLIC std::string Value::to_string () const { return "edge-pair: " + m_value.to_string (); } -template <> std::string Value::to_string () const +template <> RDB_PUBLIC std::string Value::to_string () const { return "box: " + m_value.to_string (); } -template <> std::string Value::to_string () const +template <> RDB_PUBLIC std::string Value::to_string () const { return "path: " + m_value.to_string (); } -template <> std::string Value::to_string () const +template <> RDB_PUBLIC std::string Value::to_string () const { return "label: " + m_value.to_string (); } // to_display_string implementations -template <> std::string Value::to_display_string () const +template <> RDB_PUBLIC std::string Value::to_display_string () const { return tl::to_string (m_value); } -template <> std::string Value::to_display_string () const +template <> RDB_PUBLIC std::string Value::to_display_string () const { return m_value; } -template <> std::string Value::to_display_string () const +template <> RDB_PUBLIC std::string Value::to_display_string () const { return to_string (); } -template <> std::string Value::to_display_string () const +template <> RDB_PUBLIC std::string Value::to_display_string () const { return to_string (); } -template <> std::string Value::to_display_string () const +template <> RDB_PUBLIC std::string Value::to_display_string () const { return to_string (); } -template <> std::string Value::to_display_string () const +template <> RDB_PUBLIC std::string Value::to_display_string () const { return to_string (); } -template <> std::string Value::to_display_string () const +template <> RDB_PUBLIC std::string Value::to_display_string () const { return to_string (); } -template <> std::string Value::to_display_string () const +template <> RDB_PUBLIC std::string Value::to_display_string () const { return to_string (); } @@ -279,6 +279,12 @@ ValueBase::create_from_shape (const db::Shape &shape, const db::CplxTrans &trans shape.edge (edge); return new rdb::Value (edge.transformed (trans)); + } else if (shape.is_edge_pair ()) { + + db::EdgePair edge_pair; + shape.edge_pair (edge_pair); + return new rdb::Value (edge_pair.transformed (trans)); + } else { return 0; } diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index 614ec8f1e..5914d1cb1 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -466,7 +466,7 @@ int type_index_of (); * Using this class, any value can be stored inside the collection of Values. */ template -class RDB_PUBLIC Value +class RDB_PUBLIC_TEMPLATE Value : public ValueBase { public: @@ -520,6 +520,15 @@ private: C m_value; }; +/** + * @brief Type bindings + */ +template +RDB_PUBLIC_TEMPLATE ValueBase *make_value (const T &value) +{ + return new Value (value); +} + /** * @brief A class encapsulating ValueBase pointer */ diff --git a/src/rdb/rdb/rdbUtils.cc b/src/rdb/rdb/rdbUtils.cc index d0f83eadf..9c50609c8 100644 --- a/src/rdb/rdb/rdbUtils.cc +++ b/src/rdb/rdb/rdbUtils.cc @@ -26,11 +26,14 @@ #include "dbLayout.h" #include "dbLayoutUtils.h" #include "dbRecursiveShapeIterator.h" +#include "dbRegion.h" +#include "dbEdges.h" +#include "dbEdgePairs.h" namespace rdb { -RDB_PUBLIC void +void scan_layer (rdb::Category *cat, const db::Layout &layout, unsigned int layer, const db::Cell *from, int levels) { rdb::Database *rdb = cat->database (); @@ -75,63 +78,242 @@ scan_layer (rdb::Category *cat, const db::Layout &layout, unsigned int layer, co } - for (db::ShapeIterator shape = cell.shapes (layer).begin (db::ShapeIterator::All); ! shape.at_end (); ++shape) { - - if (shape->is_polygon () || shape->is_path () || shape->is_box ()) { - - db::Polygon poly; - shape->polygon (poly); - rdb::Item *item = rdb->create_item (rdb_cell->id (), cat->id ()); - item->values ().add (new rdb::Value (poly.transformed (db::CplxTrans (layout.dbu ())))); - - } else if (shape->is_edge ()) { - - db::Edge edge; - shape->edge (edge); - rdb::Item *item = rdb->create_item (rdb_cell->id (), cat->id ()); - item->values ().add (new rdb::Value (edge.transformed (db::CplxTrans (layout.dbu ())))); - - } - - } + create_items_from_shapes (rdb, rdb_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()), cell.shapes (layer)); } } } -RDB_PUBLIC void -scan_layer (rdb::Category *cat, const db::RecursiveShapeIterator &iter) +namespace +{ + +class CreateItemsRecursiveReceiver + : public db::RecursiveShapeReceiver +{ +public: + CreateItemsRecursiveReceiver (rdb::Category *cat, const db::CplxTrans &trans, rdb::Cell *cell) + : mp_cat (cat), mp_rdb (cat->database ()), m_trans (trans), mp_rdb_cell (cell) + { + // just in case the iterator is non-hierarchical + if (cell) { + m_cell_stack.push_back (cell); + } + } + + virtual void begin (const db::RecursiveShapeIterator *iter) + { + if (! iter->top_cell () || ! iter->layout ()) { + return; + } + + db::cell_index_type ci = iter->top_cell ()->cell_index (); + const rdb::Cell *rdb_cell = cell_for_id (iter->layout (), ci); + + if (! m_cell_stack.empty () && rdb_cell != m_cell_stack.front () && (rdb_cell->references ().begin () == rdb_cell->references ().end ())) { + // If the actual top cell is not the one specified, add a dummy reference so we find the real top cell under + // the given one. + // TODO: get rid of the const_cast + (const_cast (rdb_cell))->references ().insert (rdb::Reference (db::DCplxTrans (), m_cell_stack.front ()->id ())); + } + + m_cell_stack.clear (); + m_cell_stack.push_back (rdb_cell); + m_id_to_cell.insert (std::make_pair (ci, rdb_cell)); + } + + virtual void end (const db::RecursiveShapeIterator *) + { + m_cell_stack.pop_back (); + } + + virtual void enter_cell (const db::RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + db::cell_index_type ci = cell->cell_index (); + const rdb::Cell *rdb_cell = cell_for_id (iter->layout (), ci); + m_cell_stack.push_back (rdb_cell); + m_id_to_cell.insert (std::make_pair (ci, rdb_cell)); + + if (rdb_cell->references ().begin () == rdb_cell->references ().end ()) { + db::DCplxTrans t = m_trans * iter->trans () * m_trans.inverted (); + // TODO: get rid of the const_cast + (const_cast (rdb_cell))->references ().insert (rdb::Reference (t, m_cell_stack.front ()->id ())); + } + } + + virtual void leave_cell (const db::RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/) + { + m_cell_stack.pop_back (); + } + + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator * /*iter*/, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) + { + db::cell_index_type ci = inst.object ().cell_index (); + if (m_id_to_cell.find (ci) != m_id_to_cell.end ()) { + return NI_skip; + } else { + return NI_single; + } + } + + virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + tl_assert (! m_cell_stack.empty ()); + create_item_from_shape (mp_rdb, m_cell_stack.back ()->id (), mp_cat->id (), m_trans, shape); + } + +public: + rdb::Category *mp_cat; + rdb::Database *mp_rdb; + std::vector m_cell_stack; + std::map m_id_to_cell; + db::CplxTrans m_trans; + rdb::Cell *mp_rdb_cell; + + const rdb::Cell *cell_for_id (const db::Layout *layout, db::cell_index_type ci) + { + tl_assert (layout != 0); + std::string cn = layout->cell_name (ci); + const rdb::Cell *rdb_cell = mp_rdb->cell_by_qname (cn); + if (! rdb_cell) { + return mp_rdb->create_cell (cn); + } else { + return rdb_cell; + } + } +}; + +class CreateItemsFlatReceiver + : public db::RecursiveShapeReceiver +{ +public: + CreateItemsFlatReceiver (rdb::Category *cat, const db::CplxTrans &trans, rdb::Cell *cell) + : mp_cat (cat), mp_rdb (cat->database ()), m_trans (trans), mp_rdb_cell (cell) + { + // .. nothing yet .. + } + + virtual void begin (const db::RecursiveShapeIterator *iter) + { + if (! mp_rdb_cell) { + + db::cell_index_type ci = iter->top_cell ()->cell_index (); + + tl_assert (iter->layout () != 0); + std::string cn = iter->layout ()->cell_name (ci); + mp_rdb_cell = mp_rdb->cell_by_qname (cn); + if (! mp_rdb_cell) { + mp_rdb_cell = mp_rdb->create_cell (cn); + } + + } + } + + virtual void shape (const db::RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + create_item_from_shape (mp_rdb, mp_rdb_cell->id (), mp_cat->id (), m_trans * iter->trans (), shape); + } + +public: + rdb::Category *mp_cat; + rdb::Database *mp_rdb; + db::CplxTrans m_trans; + const rdb::Cell *mp_rdb_cell; +}; + +} + +void +scan_layer (rdb::Category *cat, const db::RecursiveShapeIterator &iter, bool flat) { if (! iter.top_cell () || ! iter.layout ()) { return; } - rdb::Database *rdb = cat->database (); - if (! rdb) { + db::CplxTrans trans (iter.layout ()->dbu ()); + scan_layer (cat, 0, trans, iter, flat); +} + +void +scan_layer (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::RecursiveShapeIterator &iter, bool flat) +{ + if (! cat->database ()) { return; } - rdb::Cell *rdb_cell = rdb->create_cell (iter.layout ()->cell_name (iter.top_cell ()->cell_index ())); + std::auto_ptr rec; + if (flat) { + rec.reset (new CreateItemsFlatReceiver (cat, trans, cell)); + } else { + rec.reset (new CreateItemsRecursiveReceiver (cat, trans, cell)); + } - for (db::RecursiveShapeIterator i = iter; ! i.at_end (); ++i) { + db::RecursiveShapeIterator (iter).push (rec.get ()); +} - if (i.shape ().is_polygon () || i.shape ().is_path () || i.shape ().is_box ()) { +// ------------------------------------------------------------------------------------------------------------ - db::Polygon poly; - i.shape ().polygon (poly); - rdb::Item *item = rdb->create_item (rdb_cell->id (), cat->id ()); - item->values ().add (new rdb::Value (poly.transformed (db::CplxTrans (iter.layout ()->dbu ()) * i.trans ()))); - - } else if (i.shape ().is_edge ()) { - - db::Edge edge; - i.shape ().edge (edge); - rdb::Item *item = rdb->create_item (rdb_cell->id (), cat->id ()); - item->values ().add (new rdb::Value (edge.transformed (db::CplxTrans (iter.layout ()->dbu ()) * i.trans ()))); +void create_items_from_iterator (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::RecursiveShapeIterator &iter) +{ + tl_assert (iter.layout ()); + double dbu = iter.layout ()->dbu (); + for (db::RecursiveShapeIterator i = iter; !i.at_end (); ++i) { + std::auto_ptr value (rdb::ValueBase::create_from_shape (*i, db::CplxTrans (dbu) * i.trans ())); + if (value.get ()) { + rdb::Item *item = db->create_item (cell_id, cat_id); + item->values ().add (value.release ()); } + } +} +void create_items_from_shapes (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shapes &shapes) +{ + for (db::Shapes::shape_iterator s = shapes.begin (db::ShapeIterator::All); !s.at_end (); ++s) { + std::auto_ptr value (rdb::ValueBase::create_from_shape (*s, trans)); + if (value.get ()) { + rdb::Item *item = db->create_item (cell_id, cat_id); + item->values ().add (value.release ()); + } + } +} + +void create_item_from_shape (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shape &shape) +{ + std::auto_ptr value (rdb::ValueBase::create_from_shape (shape, trans)); + if (value.get ()) { + rdb::Item *item = db->create_item (cell_id, cat_id); + item->values ().add (value.release ()); + } +} + +void create_items_from_region (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Region &collection) +{ + typedef db::Region::const_iterator iter; + + for (iter o = collection.begin (); ! o.at_end (); ++o) { + rdb::Item *item = db->create_item (cell_id, cat_id); + item->values ().add (new rdb::Value (o->transformed (trans))); + } +} + +void create_items_from_edges (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Edges &collection) +{ + typedef db::Edges::const_iterator iter; + + for (iter o = collection.begin (); ! o.at_end (); ++o) { + rdb::Item *item = db->create_item (cell_id, cat_id); + item->values ().add (new rdb::Value (o->transformed (trans))); + } +} + +void create_items_from_edge_pairs (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::EdgePairs &collection) +{ + typedef db::EdgePairs::const_iterator iter; + + for (iter o = collection.begin (); ! o.at_end (); ++o) { + rdb::Item *item = db->create_item (cell_id, cat_id); + item->values ().add (new rdb::Value (o->transformed (trans))); } } diff --git a/src/rdb/rdb/rdbUtils.h b/src/rdb/rdb/rdbUtils.h index 01bade4a8..cfdfb90a2 100644 --- a/src/rdb/rdb/rdbUtils.h +++ b/src/rdb/rdb/rdbUtils.h @@ -26,12 +26,17 @@ #include "rdb.h" #include "dbTypes.h" +#include "dbTrans.h" namespace db { class Layout; class Cell; class RecursiveShapeIterator; + class Shapes; + class Region; + class Edges; + class EdgePairs; } namespace rdb @@ -52,7 +57,84 @@ RDB_PUBLIC void scan_layer (rdb::Category *cat, const db::Layout &layout, unsign /** * @brief Scans a recursive shape iterator into a RDB category */ -RDB_PUBLIC void scan_layer (rdb::Category *cat, const db::RecursiveShapeIterator &iter); +RDB_PUBLIC void scan_layer (rdb::Category *cat, const db::RecursiveShapeIterator &iter, bool flat = false); + +/** + * @brief Scans a recursive shape iterator into a RDB category + * + * This version allows supplying a cell and a transformation. With this information, the function can also handle + * pseudo-iterators which don't deliver the information from a layout from from a plain shape collection. + */ +RDB_PUBLIC void scan_layer (rdb::Category *cat, rdb::Cell *cell, const db::CplxTrans &trans, const db::RecursiveShapeIterator &iter, bool flat = false); + +/** + * @brief Creates RDB items from a recursive shape iterator + * + * This function will produce items from the flattened shape iterator. The items will be stored under + * the given cell. + */ +RDB_PUBLIC void create_items_from_iterator (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::RecursiveShapeIterator &iter); + +/** + * @brief Creates RDB items from a shape collection + * + * An arbitrary transformation can be applied to translate the shapes before turning them to items. + * This transformation is useful for providing the DBU-to-micron conversion. + */ +RDB_PUBLIC void create_items_from_shapes (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shapes &shapes); + +/** + * @brief Creates RDB items from a single shape + * + * An arbitrary transformation can be applied to translate the shapes before turning them to items. + * This transformation is useful for providing the DBU-to-micron conversion. + */ +RDB_PUBLIC void create_item_from_shape (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Shape &shape); + +/** + * @brief Creates RDB items from a region + * + * This function will flatten the region and store the resulting items under the given cell. + * + * An arbitrary transformation can be applied to translate the shapes before turning them to items. + * This transformation is useful for providing the DBU-to-micron conversion. + */ +RDB_PUBLIC void create_items_from_region (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Region &collection); + +/** + * @brief Creates RDB items from an edge collection + * + * This function will flatten the edge collection and store the resulting items under the given cell. + * + * An arbitrary transformation can be applied to translate the shapes before turning them to items. + * This transformation is useful for providing the DBU-to-micron conversion. + */ +RDB_PUBLIC void create_items_from_edges (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::Edges &collection); + +/** + * @brief Creates RDB items from an edge pair collection + * + * This function will flatten the edge pair collection and store the resulting items under the given cell. + * + * An arbitrary transformation can be applied to translate the shapes before turning them to items. + * This transformation is useful for providing the DBU-to-micron conversion. + */ +RDB_PUBLIC void create_items_from_edge_pairs (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const db::EdgePairs &collection); + +/** + * @brief Creates RDB items from a sequence of integer-type objects + * + * An arbitrary transformation can be applied to translate the shapes before turning them to items. + * This transformation is useful for providing the DBU-to-micron conversion. + */ +template +RDB_PUBLIC void create_items_from_sequence (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const Trans &trans, Iter begin, Iter end) +{ + for (Iter o = begin; o != end; ++o) { + rdb::Item *item = db->create_item (cell_id, cat_id); + item->values ().add (rdb::make_value (o->transformed (trans))); + } +} } diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro index d5c002aea..95deceb8c 100644 --- a/src/tl/tl/tl.pro +++ b/src/tl/tl/tl.pro @@ -41,7 +41,10 @@ SOURCES = \ tlThreads.cc \ tlDeferredExecution.cc \ tlUri.cc \ - tlLongInt.cc + tlLongInt.cc \ + tlUniqueId.cc \ + tlList.cc \ + tlEquivalenceClusters.cc HEADERS = \ tlAlgorithm.h \ @@ -92,7 +95,10 @@ HEADERS = \ tlThreads.h \ tlDeferredExecution.h \ tlUri.h \ - tlLongInt.h + tlLongInt.h \ + tlUniqueId.h \ + tlList.h \ + tlEquivalenceClusters.h equals(HAVE_CURL, "1") { diff --git a/src/tl/tl/tlEquivalenceClusters.cc b/src/tl/tl/tlEquivalenceClusters.cc new file mode 100644 index 000000000..519d6aa40 --- /dev/null +++ b/src/tl/tl/tlEquivalenceClusters.cc @@ -0,0 +1,30 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "tlEquivalenceClusters.h" + +namespace tl +{ + + // .. nothing yet .. + +} diff --git a/src/tl/tl/tlEquivalenceClusters.h b/src/tl/tl/tlEquivalenceClusters.h new file mode 100644 index 000000000..5d4b23d4c --- /dev/null +++ b/src/tl/tl/tlEquivalenceClusters.h @@ -0,0 +1,263 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_tlEquivalenceClusters +#define HDR_tlEquivalenceClusters + +#include "tlCommon.h" +#include "tlAssert.h" + +#include +#include +#include +#include + +namespace tl +{ + +/** + * @brief A utility class forming clusters based on equivalence of a certain attribute + * + * To use this class, feed it with equivalences using "is_same", e.g. + * + * @code + * equivalence_clusters eq; + * // forms two clusters: 1,2,5 and 3,4 + * eq.same (1, 2); + * eq.same (3, 4); + * eq.same (1, 5); + * @endcode + * + * Self-equivalence is a way of introduction an attribute without + * an equivalence: + * + * @code + * equivalence_clusters eq; + * // after this, 1 is a known attribute + * eq.same (1, 1); + * eq.has_attribute (1); // ->true + * @endcode + * + * Equivalence clusters can be merged, forming new and bigger clusters. + * + * Eventually, each cluster is represented by a non-zero integer ID. + * The cluster ID can obtained per attribute value using "cluster_id". + * In the above example this will be: + * + * @code + * eq.cluster_id (1); // ->1 + * eq.cluster_id (2); // ->1 + * eq.cluster_id (3); // ->2 + * eq.cluster_id (4); // ->2 + * eq.cluster_id (5); // ->1 + * eq.cluster_id (6); // ->0 (unknown) + * @endcode + * + * "size" will give the maximum cluster ID. + */ +template +class equivalence_clusters +{ +public: + typedef size_t cluster_id_type; + typedef T attribute_type; + typedef typename std::vector::iterator>::const_iterator cluster_iterator; + + /** + * @brief Creates an empty equivalence cluster + */ + equivalence_clusters () + { + // .. nothing yet .. + } + + /** + * @brief Makes attr1 and attr2 equivalent + */ + void same (const T &attr1, const T &attr2) + { + cluster_id_type cl1 = cluster_id (attr1); + + if (attr1 == attr2) { + // special case of "self-identity" + if (! cl1) { + cluster_id_type cl = new_cluster (); + insert (attr1, cl); + } + return; + } + + cluster_id_type cl2 = cluster_id (attr2); + if (! cl1 || ! cl2) { + + if (cl1) { + insert (attr2, cl1); + } else if (cl2) { + insert (attr1, cl2); + } else { + cluster_id_type cl = new_cluster (); + insert (attr1, cl); + insert (attr2, cl); + } + + } else if (cl1 != cl2) { + join (cl1, cl2); + } + } + + /** + * @brief Returns true, if attr is part of the equivalence clusters + */ + bool has_attribute (const T &attr) const + { + return m_cluster_id_by_attr.find (attr) != m_cluster_id_by_attr.end (); + } + + /** + * @brief Returns the cluster ID for the given attribute of 0 if the attribute is not assigned to a cluster + */ + cluster_id_type cluster_id (const T &attr) const + { + typename std::map::const_iterator c = m_cluster_id_by_attr.find (attr); + if (c != m_cluster_id_by_attr.end ()) { + return c->second; + } else { + return 0; + } + } + + /** + * @brief Applies the equivalences of the other clusters + * + * This method will join clusters already within this equivalence cluster collection + * based on the equivalences listed in the other clusters collection. + * + * In contrast to merge, his method will not introduce new attributes. + */ + void apply_equivalences (const equivalence_clusters &other) + { + std::vector attrs; + for (typename std::map::const_iterator a = m_cluster_id_by_attr.begin (); a != m_cluster_id_by_attr.end (); ++a) { + if (other.has_attribute (a->first)) { + attrs.push_back (a->first); + } + } + + for (typename std::vector::const_iterator a = attrs.begin (); a != attrs.end (); ++a) { + cluster_id_type cl = other.cluster_id (*a); + cluster_iterator b = other.begin_cluster (cl); + cluster_iterator e = other.end_cluster (cl); + for (cluster_iterator i = b; i != e; ++i) { + if ((*i)->first != *a && has_attribute ((*i)->first)) { + same ((*i)->first, *a); + } + } + } + } + + /** + * @brief Merges the equivalences of the other clusters into this + * + * This method will add all attributes from the other cluster collection and join + * clusters based on the equivalences there. + */ + void merge (const equivalence_clusters &other) + { + for (cluster_id_type cl = 1; cl <= other.size (); ++cl) { + cluster_iterator b = other.begin_cluster (cl); + cluster_iterator e = other.end_cluster (cl); + for (cluster_iterator i = b; i != e; ++i) { + same ((*b)->first, (*i)->first); + } + } + } + + /** + * @brief Returns the number of clusters kept inside this collection + */ + size_t size () const + { + return m_clusters.size (); + } + + /** + * @brief Begin iterator for the cluster elements for a given cluster ID. + * To access, the attribute, use "(*i)->first". + */ + cluster_iterator begin_cluster (size_t cluster_id) const + { + tl_assert (cluster_id > 0); + return m_clusters [cluster_id - 1].begin (); + } + + /** + * @brief End iterator for the cluster elements for a given cluster ID. + */ + cluster_iterator end_cluster (size_t cluster_id) const + { + tl_assert (cluster_id > 0); + return m_clusters [cluster_id - 1].end (); + } + +private: + void insert (const T &attr, cluster_id_type into) + { + typename std::map::iterator c = m_cluster_id_by_attr.insert (std::make_pair (attr, into)).first; + m_clusters [into - 1].push_back (c); + } + + void join (cluster_id_type id, cluster_id_type with_id) + { + tl_assert (id > 0); + tl_assert (with_id > 0); + std::vector::iterator> &cnew = m_clusters [id - 1]; + std::vector::iterator> &c = m_clusters [with_id - 1]; + for (typename std::vector::iterator>::iterator i = c.begin (); i != c.end (); ++i) { + (*i)->second = id; + cnew.push_back (*i); + } + + c.clear (); + m_free_slots.push_back (with_id); + } + + cluster_id_type new_cluster () + { + if (! m_free_slots.empty ()) { + cluster_id_type cl = m_free_slots.back (); + m_free_slots.pop_back (); + return cl; + } else { + m_clusters.push_back (std::vector::iterator> ()); + return m_clusters.size (); + } + } + + std::map m_cluster_id_by_attr; + std::vector::iterator> > m_clusters; + std::vector m_free_slots; +}; + +} + +#endif diff --git a/src/tl/tl/tlList.cc b/src/tl/tl/tlList.cc new file mode 100644 index 000000000..b7aa685a9 --- /dev/null +++ b/src/tl/tl/tlList.cc @@ -0,0 +1,25 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "tlList.h" + +// .. nothing yet .. diff --git a/src/tl/tl/tlList.h b/src/tl/tl/tlList.h new file mode 100644 index 000000000..0e35ed58b --- /dev/null +++ b/src/tl/tl/tlList.h @@ -0,0 +1,547 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_tlList +#define HDR_tlList + +#include "tlAssert.h" +#include "tlTypeTraits.h" + +#include +#include + +namespace tl +{ + +template class list_impl; + +/** + * @brief A base class for objects that can be kept in the linked list + */ +template +class list_node +{ +public: + list_node () : mp_next (0), mp_prev (0), m_owned (true) { } + list_node (const list_node &) : mp_next (0), mp_prev (0), m_owned (true) { } + list_node &operator= (const list_node &) { return *this; } + + ~list_node () + { + unlink (); + } + + C *next () + { + tl_assert (mp_next); + return static_cast (mp_next->mp_next == 0 ? 0 : mp_next); + } + + const C *next () const + { + tl_assert (mp_next); + return static_cast (mp_next->mp_next == 0 ? 0 : mp_next); + } + + C *prev () + { + tl_assert (mp_prev); + return static_cast (mp_prev->mp_prev == 0 ? 0 : mp_prev); + } + + const C *prev () const + { + tl_assert (mp_prev); + return static_cast (mp_prev->mp_prev == 0 ? 0 : mp_prev); + } + + C *self () + { + return static_cast (this); + } + + const C *self () const + { + return static_cast (this); + } + + void unlink () + { + if (mp_prev) { + tl_assert (mp_prev->mp_next == this); + mp_prev->mp_next = mp_next; + } + if (mp_next) { + tl_assert (mp_next->mp_prev == this); + mp_next->mp_prev = mp_prev; + } + mp_prev = mp_next = 0; + } + +private: + template friend class list_impl; + template friend class list; + template friend class list_iterator; + template friend class reverse_list_iterator; + + list_node *mp_next, *mp_prev; + bool m_owned; +}; + +template +class list_impl +{ +public: + list_impl () : m_head (), m_back () + { + m_head.mp_next = &m_back; + m_back.mp_prev = &m_head; + } + + list_impl (const list_impl &) { tl_assert (false); } + list_impl &operator= (const list_impl &) { tl_assert (false); return *this; } + + ~list_impl () + { + clear (); + } + + void clear () + { + while (! empty ()) { + erase (first ()); + } + } + + void erase (C *c) + { + if (c->m_owned) { + delete c; + } else { + c->unlink (); + } + } + + void swap (list_impl &other) + { + std::swap (m_head.mp_next, other.m_head.mp_next); + if (m_head.mp_next) { + m_head.mp_next->mp_prev = &m_head; + } + if (other.m_head.mp_next) { + other.m_head.mp_next->mp_prev = &other.m_head; + } + std::swap (m_back.mp_prev, other.m_back.mp_prev); + if (m_back.mp_prev) { + m_back.mp_prev->mp_next = &m_back; + } + if (other.m_back.mp_prev) { + other.m_back.mp_prev->mp_next = &other.m_back; + } + } + + bool empty () const + { + return m_head.mp_next == &m_back; + } + + C *first () + { + return ! empty () ? static_cast (m_head.mp_next) : 0; + } + + const C *first () const + { + return ! empty () ? static_cast (m_head.mp_next) : 0; + } + + C *last () + { + return ! empty () ? static_cast (m_back.mp_prev) : 0; + } + + const C *last () const + { + return ! empty () ? static_cast (m_back.mp_prev) : 0; + } + + void pop_back () + { + delete last (); + } + + void pop_front () + { + delete first (); + } + + void insert (C *after, C *new_obj) + { + insert_impl (after, new_obj, true); + } + + void insert_before (C *before, C *new_obj) + { + insert_before_impl (before, new_obj, true); + } + + void push_back (C *new_obj) + { + push_back_impl (new_obj, true); + } + + void push_front (C *new_obj) + { + push_front_impl (new_obj, true); + } + + void insert (C *after, C &new_obj) + { + insert_impl (after, &new_obj, false); + } + + void insert_before (C *before, C &new_obj) + { + insert_before_impl (before, &new_obj, false); + } + + void push_back (C &new_obj) + { + push_back_impl (&new_obj, false); + } + + void push_front (C &new_obj) + { + push_front_impl (&new_obj, false); + } + + size_t size () const + { + size_t n = 0; + for (const C *p = first (); p; p = p->next ()) { + ++n; + } + return n; + } + +protected: + list_node &head () + { + return m_head; + } + + const list_node &head () const + { + return m_head; + } + + list_node &back () + { + return m_back; + } + + const list_node &back () const + { + return m_back; + } + +private: + list_node m_head, m_back; + + void insert_impl (C *after, C *new_obj, bool owned) + { + list_node *after_node = after; + if (! after) { + after_node = &m_head; + } + + new_obj->m_owned = owned; + new_obj->mp_next = after_node->mp_next; + after_node->mp_next = new_obj; + new_obj->mp_prev = after_node; + new_obj->mp_next->mp_prev = new_obj; + } + + void insert_before_impl (C *before, C *new_obj, bool owned) + { + list_node *before_node = before; + if (! before) { + before_node = &m_back; + } + + new_obj->m_owned = owned; + new_obj->mp_prev = before_node->mp_prev; + before_node->mp_prev = new_obj; + new_obj->mp_next = before_node; + new_obj->mp_prev->mp_next = new_obj; + } + + void push_back_impl (C *new_obj, bool owned) + { + insert_before_impl (0, new_obj, owned); + } + + void push_front_impl (C *new_obj, bool owned) + { + insert_impl (0, new_obj, owned); + } +}; + +template +class list_impl + : public list_impl +{ +public: + using list_impl::insert; + using list_impl::push_back; + using list_impl::pop_back; + using list_impl::insert_before; + using list_impl::push_front; + using list_impl::pop_front; + + list_impl () { } + + list_impl (const list_impl &other) + : list_impl () + { + operator= (other); + } + + list_impl &operator= (const list_impl &other) + { + if (this != &other) { + list_impl::clear (); + for (const C *p = other.first (); p; p = p->next ()) { + push_back (*p); + } + } + return *this; + } + + void insert (C *after, const C &obj) + { + insert (after, new C (obj)); + } + + void insert_before (C *before, const C &obj) + { + insert_before (before, new C (obj)); + } + + void push_back (const C &obj) + { + insert_before (0, new C (obj)); + } + + void push_front (const C &obj) + { + insert (0, new C (obj)); + } +}; + +/** + * @brief An iterator for the linked list + */ +template +class list_iterator +{ +public: + typedef std::bidirectional_iterator_tag category; + typedef C value_type; + typedef C &reference; + typedef C *pointer; + + list_iterator (C *p = 0) : mp_p (p) { } + list_iterator operator++ () { mp_p = static_cast (mp_p->mp_next); return *this; } + list_iterator operator-- () { mp_p = static_cast (mp_p->mp_prev); return *this; } + + C *operator-> () const + { + return mp_p; + } + + C &operator* () const + { + return *mp_p; + } + + bool operator== (list_iterator other) const { return mp_p == other.mp_p; } + bool operator!= (list_iterator other) const { return mp_p != other.mp_p; } + +private: + C *mp_p; +}; + +/** + * @brief A reverse iterator for the linked list + */ +template +class reverse_list_iterator +{ +public: + typedef std::bidirectional_iterator_tag category; + typedef C value_type; + typedef C &reference; + typedef C *pointer; + + reverse_list_iterator (C *p = 0) : mp_p (p) { } + reverse_list_iterator operator++ () { mp_p = static_cast (mp_p->mp_prev); return *this; } + reverse_list_iterator operator-- () { mp_p = static_cast (mp_p->mp_next); return *this; } + + C *operator-> () const + { + return mp_p; + } + + C &operator* () const + { + return *mp_p; + } + + bool operator== (reverse_list_iterator other) const { return mp_p == other.mp_p; } + bool operator!= (reverse_list_iterator other) const { return mp_p != other.mp_p; } + +private: + C *mp_p; +}; + +/** + * @brief A linked list + * + * In contrast to std::list this implementation is based on derivation from + * a common base class (list_node where C is the type that needs to be + * put into the list. + * + * The advantage of this approach is that the elements can unregister them + * selves upon delete, there are no iterators involved in insert and delete + * operations and each object knows it's followers and predecessors. + * + * @code + * class MyClass : public tl::list_node { ... }; + * + * tl::list list; + * list.push_back (new MyClass ()); + */ +template +class list + : public list_impl::has_copy_constructor> +{ +public: + typedef list_iterator iterator; + typedef list_iterator const_iterator; + typedef reverse_list_iterator reverse_iterator; + typedef reverse_list_iterator const_reverse_iterator; + + typedef C value_type; + + using list_impl::has_copy_constructor>::first; + using list_impl::has_copy_constructor>::last; + using list_impl::has_copy_constructor>::head; + using list_impl::has_copy_constructor>::back; + + list () { } + list (const list &other) : list_impl::has_copy_constructor> (other) { } + + list &operator= (const list &other) + { + list_impl::has_copy_constructor>::operator= (other); + return *this; + } + + iterator begin () + { + return iterator (static_cast (head ().mp_next)); + } + + iterator end () + { + return iterator (static_cast (&back ())); + } + + const_iterator begin () const + { + return const_iterator (static_cast (head ().mp_next)); + } + + const_iterator end () const + { + return const_iterator (static_cast (&back ())); + } + + reverse_iterator rbegin () + { + return reverse_iterator (static_cast (back ().mp_prev)); + } + + reverse_iterator rend () + { + return reverse_iterator (static_cast (&head ())); + } + + const_reverse_iterator rbegin () const + { + return const_reverse_iterator (static_cast (back ().mp_prev)); + } + + const_reverse_iterator rend () const + { + return const_reverse_iterator (static_cast (&head ())); + } + + bool operator== (const list &other) const + { + const C *i1 = first (); + const C *i2 = other.first (); + while (i1 && i2) { + if (! (*i1 == *i2)) { + return false; + } + i1 = i1->next (); + i2 = i2->next (); + } + return (i1 == 0 && i2 == 0); + } + + bool operator!= (const list &other) const + { + return !operator== (other); + } + + bool operator< (const list &other) const + { + const C *i1 = first (); + const C *i2 = other.first (); + while (i1 && i2) { + if (! (*i1 == *i2)) { + return *i1 < *i2; + } + i1 = i1->next (); + i2 = i2->next (); + } + return ((i1 == 0) > (i2 == 0)); + } +}; + +} + +#endif diff --git a/src/tl/tl/tlObject.cc b/src/tl/tl/tlObject.cc index 537a0df7c..45dbc2bba 100644 --- a/src/tl/tl/tlObject.cc +++ b/src/tl/tl/tlObject.cc @@ -130,12 +130,12 @@ bool Object::has_strong_references () const return false; } -void Object::keep () +void Object::keep_object () { mp_ptrs = (WeakOrSharedPtr *)(size_t (mp_ptrs) | size_t (1)); } -void Object::release () +void Object::release_object () { mp_ptrs = (WeakOrSharedPtr *)(size_t (mp_ptrs) & ~size_t (1)); diff --git a/src/tl/tl/tlObject.h b/src/tl/tl/tlObject.h index b00cc5571..5fabdb595 100644 --- a/src/tl/tl/tlObject.h +++ b/src/tl/tl/tlObject.h @@ -106,14 +106,14 @@ public: * no strong pointer is having a reference to this object and longer, the * object is not deleted. */ - void keep (); + void keep_object (); /** * @brief Releases this object from being kept * This method may delete the object if no strong pointer holds a * reference to it. */ - void release (); + void release_object (); protected: /** diff --git a/src/tl/tl/tlProgress.cc b/src/tl/tl/tlProgress.cc index 32dc1dd38..2acae6f27 100644 --- a/src/tl/tl/tlProgress.cc +++ b/src/tl/tl/tlProgress.cc @@ -72,6 +72,17 @@ Progress::Progress (const std::string &desc, size_t yield_interval) m_last_value (-1.0), m_can_cancel (true), m_cancelled (false) +{ + // .. nothing yet .. +} + +Progress::~Progress () +{ + // .. nothing yet .. +} + +void +Progress::initialize () { ProgressAdaptor *a = adaptor (); if (a) { @@ -79,7 +90,8 @@ Progress::Progress (const std::string &desc, size_t yield_interval) } } -Progress::~Progress () +void +Progress::shutdown () { ProgressAdaptor *a = adaptor (); if (a) { @@ -134,7 +146,6 @@ Progress::set_desc (const std::string &d) } } - } void @@ -181,6 +192,14 @@ RelativeProgress::RelativeProgress (const std::string &desc, size_t max_count, s m_format = "%.0f%%"; m_unit = double (max_count) / 100.0; m_count = 0; + m_last_count = 0; + + initialize (); +} + +RelativeProgress::~RelativeProgress () +{ + shutdown (); } double @@ -203,7 +222,8 @@ RelativeProgress & RelativeProgress::set (size_t count, bool force_yield) { m_count = count; - test (force_yield); + test (force_yield || m_count - m_last_count >= m_unit); + m_last_count = m_count; return *this; } @@ -217,6 +237,13 @@ AbsoluteProgress::AbsoluteProgress (const std::string &desc, size_t yield_interv m_unit = 1.0; m_format_unit = 0.0; m_count = 0; + + initialize (); +} + +AbsoluteProgress::~AbsoluteProgress () +{ + shutdown (); } double diff --git a/src/tl/tl/tlProgress.h b/src/tl/tl/tlProgress.h index 3fab19910..0cab4587d 100644 --- a/src/tl/tl/tlProgress.h +++ b/src/tl/tl/tlProgress.h @@ -29,6 +29,7 @@ #include #include "tlException.h" #include "tlTimer.h" +#include "tlList.h" class QWidget; @@ -105,6 +106,7 @@ class Progress; */ class TL_PUBLIC Progress + : public tl::list_node { public: /** @@ -203,6 +205,16 @@ protected: */ void test (bool force_yield = false); + /** + * @brief This method needs to be called by all derived classes after initialization has happened + */ + void initialize (); + + /** + * @brief This method needs to be called by all derived classes in the destructor + */ + void shutdown (); + private: friend class ProgressAdaptor; @@ -241,6 +253,11 @@ public: */ RelativeProgress (const std::string &desc, size_t max_count = 0, size_t yield_interval = 1000); + /** + * @brief Destructor + */ + ~RelativeProgress (); + /** * @brief Delivers the current progress as a string */ @@ -283,6 +300,7 @@ private: std::string m_format; size_t m_count; + size_t m_last_count; double m_unit; }; @@ -309,6 +327,11 @@ public: */ AbsoluteProgress (const std::string &desc, size_t yield_interval = 1000); + /** + * @brief Destructor + */ + ~AbsoluteProgress (); + /** * @brief Delivers the current progress as a string */ diff --git a/src/tl/tl/tlTimer.cc b/src/tl/tl/tlTimer.cc index 0446c3e78..2ea649f35 100644 --- a/src/tl/tl/tlTimer.cc +++ b/src/tl/tl/tlTimer.cc @@ -186,6 +186,12 @@ Timer::take () m_wall_ms = wall_ms; } +void +SelfTimer::start_report () const +{ + tl::info << m_desc << ": " << tl::to_string (tr ("started")); +} + void SelfTimer::report () const { diff --git a/src/tl/tl/tlTimer.h b/src/tl/tl/tlTimer.h index 326edd342..4ac115abc 100644 --- a/src/tl/tl/tlTimer.h +++ b/src/tl/tl/tlTimer.h @@ -118,6 +118,7 @@ public: { m_enabled = true; start (); + start_report (); } /** @@ -131,6 +132,7 @@ public: m_enabled = enabled; if (enabled) { start (); + start_report (); } } @@ -144,6 +146,7 @@ public: private: void report () const; + void start_report () const; std::string m_desc; bool m_enabled; diff --git a/src/tl/tl/tlUniqueId.cc b/src/tl/tl/tlUniqueId.cc new file mode 100644 index 000000000..81eda206c --- /dev/null +++ b/src/tl/tl/tlUniqueId.cc @@ -0,0 +1,43 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlUniqueId.h" +#include "tlThreads.h" + +namespace tl +{ + +static tl::Mutex s_lock; +static id_type s_next_id = 0; + +UniqueId::UniqueId () +{ + tl::MutexLocker locker (&s_lock); + do { + m_id = ++s_next_id; + } while (m_id == 0); +} + +} // namespace tl + + diff --git a/src/tl/tl/tlUniqueId.h b/src/tl/tl/tlUniqueId.h new file mode 100644 index 000000000..5184e62c8 --- /dev/null +++ b/src/tl/tl/tlUniqueId.h @@ -0,0 +1,81 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_tlUniqueId +#define HDR_tlUniqueId + +#include "tlCommon.h" +#include + +namespace tl +{ + +typedef uint64_t id_type; + +/** + * @brief An object delivering a unique ID per object + * + * This class is supposed to provide a solution for the non-uniqueness + * issue of addresses. Instead of using addresses as keys, use the unique + * ID. The advantage is to be reproducible and guaranteed to be different + * for each allocation of an object (apart from overflow wrapping at 64bit - + * this this is pretty unlikely event). + * + * To supply an ID, derive your class from tl::UniqueId. Use + * "tl::id_of (object *)" the get the ID from that object. + * A null-pointer will give an ID of 0 which is reserved for + * "nothing". + * + * The type of the ID is tl::id_type. + */ +class TL_PUBLIC UniqueId +{ +public: + /** + * @brief @brief Creates a new object with a new ID + */ + UniqueId (); + + UniqueId (const UniqueId &) { } + UniqueId &operator= (const UniqueId &) { return *this; } + +private: + friend id_type id_of (const UniqueId *); + + id_type m_id; +}; + +/** + * @brief Gets the ID of the object pointed to by o + * + * Returns 0 in a null pointer and only then. + */ +inline id_type id_of (const UniqueId *o) +{ + return o ? o->m_id : 0; +} + +} // namespace tl + +#endif + diff --git a/src/tl/tl/tlVariant.h b/src/tl/tl/tlVariant.h index 3821bea7e..b29e3da84 100644 --- a/src/tl/tl/tlVariant.h +++ b/src/tl/tl/tlVariant.h @@ -345,6 +345,15 @@ public: m_var.mp_user.cls = c; } + /** + * @brief Initialize the Variant with an explicit vector or variants + */ + Variant (const std::vector &list) + : m_type (t_list), m_string (0) + { + m_var.m_list = new std::vector (list); + } + /** * @brief Initialize the Variant with a list */ diff --git a/src/tl/unit_tests/tlEquivalenceClustersTests.cc b/src/tl/unit_tests/tlEquivalenceClustersTests.cc new file mode 100644 index 000000000..5d48edd5c --- /dev/null +++ b/src/tl/unit_tests/tlEquivalenceClustersTests.cc @@ -0,0 +1,211 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlEquivalenceClusters.h" +#include "tlUnitTest.h" + +// basics +TEST(1_basics) +{ + tl::equivalence_clusters eq; + + eq.same (1, 5); + eq.same (2, 3); + eq.same (5, 4); + + EXPECT_EQ (eq.cluster_id (1), size_t (1)); + EXPECT_EQ (eq.cluster_id (2), size_t (2)); + EXPECT_EQ (eq.cluster_id (3), size_t (2)); + EXPECT_EQ (eq.cluster_id (4), size_t (1)); + EXPECT_EQ (eq.cluster_id (5), size_t (1)); + EXPECT_EQ (eq.cluster_id (6), size_t (0)); + + EXPECT_EQ (eq.size (), size_t (2)); + + eq.same (2, 6); + EXPECT_EQ (eq.cluster_id (6), size_t (2)); + EXPECT_EQ (eq.cluster_id (7), size_t (0)); + + EXPECT_EQ (eq.size (), size_t (2)); + + eq.same (7, 8); + EXPECT_EQ (eq.size (), size_t (3)); + EXPECT_EQ (eq.cluster_id (6), size_t (2)); + EXPECT_EQ (eq.cluster_id (7), size_t (3)); + EXPECT_EQ (eq.cluster_id (8), size_t (3)); +} + +// joining of clusters +TEST(2_join) +{ + tl::equivalence_clusters eq; + + eq.same (1, 2); + eq.same (3, 4); + eq.same (5, 6); + + EXPECT_EQ (eq.cluster_id (1), size_t (1)); + EXPECT_EQ (eq.cluster_id (2), size_t (1)); + EXPECT_EQ (eq.cluster_id (3), size_t (2)); + EXPECT_EQ (eq.cluster_id (4), size_t (2)); + EXPECT_EQ (eq.cluster_id (5), size_t (3)); + EXPECT_EQ (eq.cluster_id (6), size_t (3)); + + eq.same (3, 2); + + EXPECT_EQ (eq.cluster_id (1), size_t (2)); + EXPECT_EQ (eq.cluster_id (2), size_t (2)); + EXPECT_EQ (eq.cluster_id (3), size_t (2)); + EXPECT_EQ (eq.cluster_id (4), size_t (2)); + EXPECT_EQ (eq.cluster_id (5), size_t (3)); + EXPECT_EQ (eq.cluster_id (6), size_t (3)); + + eq.same (4, 5); + + EXPECT_EQ (eq.cluster_id (1), size_t (2)); + EXPECT_EQ (eq.cluster_id (2), size_t (2)); + EXPECT_EQ (eq.cluster_id (3), size_t (2)); + EXPECT_EQ (eq.cluster_id (4), size_t (2)); + EXPECT_EQ (eq.cluster_id (5), size_t (2)); + EXPECT_EQ (eq.cluster_id (6), size_t (2)); + + eq.same (10, 11); + eq.same (12, 13); + + EXPECT_EQ (eq.cluster_id (10), size_t (3)); + EXPECT_EQ (eq.cluster_id (11), size_t (3)); + + EXPECT_EQ (eq.cluster_id (12), size_t (1)); + EXPECT_EQ (eq.cluster_id (13), size_t (1)); +} + +// size +TEST(3_size) +{ + tl::equivalence_clusters eq; + + eq.same (1, 2); + EXPECT_EQ (eq.size (), size_t (1)); + eq.same (2, 4); + EXPECT_EQ (eq.size (), size_t (1)); + eq.same (5, 6); + EXPECT_EQ (eq.size (), size_t (2)); +} + +// has_attribute +TEST(4_has_attribute) +{ + tl::equivalence_clusters eq; + + eq.same (1, 1); + EXPECT_EQ (eq.has_attribute (1), true); + EXPECT_EQ (eq.has_attribute (2), false); + eq.same (1, 2); + EXPECT_EQ (eq.has_attribute (1), true); + EXPECT_EQ (eq.has_attribute (2), true); + EXPECT_EQ (eq.has_attribute (3), false); +} + +std::string eq2string (const tl::equivalence_clusters &eq) +{ + std::string res; + for (size_t c = 1; c <= eq.size (); ++c) { + for (tl::equivalence_clusters::cluster_iterator i = eq.begin_cluster (c); i != eq.end_cluster (c); ++i) { + if (i != eq.begin_cluster (c)) { + res += ","; + } else if (! res.empty ()) { + res += ";"; + } + res += tl::to_string ((*i)->first); + } + } + return res; +} + +// iterator +TEST(5_iterator) +{ + tl::equivalence_clusters eq; + + eq.same (1, 1); + EXPECT_EQ (eq2string (eq), "1"); + + eq.same (1, 2); + EXPECT_EQ (eq2string (eq), "1,2"); + + eq.same (3, 4); + EXPECT_EQ (eq2string (eq), "1,2;3,4"); + + eq.same (10, 11); + EXPECT_EQ (eq2string (eq), "1,2;3,4;10,11"); + + eq.same (1, 10); + EXPECT_EQ (eq2string (eq), "1,2,10,11;3,4"); +} + +// apply_other_equivalences +TEST(6_apply_equivalences) +{ + tl::equivalence_clusters eq; + + eq.same (1, 1); + eq.same (2, 2); + eq.same (3, 4); + eq.same (5, 6); + EXPECT_EQ (eq2string (eq), "1;2;3,4;5,6"); + + tl::equivalence_clusters eq2; + + eq2.same (2, 2); + eq2.same (4, 5); + eq2.same (4, 10); + eq2.same (11, 11); + EXPECT_EQ (eq2string (eq2), "2;4,5,10;11"); + + eq.apply_equivalences (eq2); + EXPECT_EQ (eq2string (eq), "1;2;5,6,3,4"); +} + +// merge +TEST(7_merge) +{ + tl::equivalence_clusters eq; + + eq.same (1, 1); + eq.same (2, 2); + eq.same (3, 4); + eq.same (5, 6); + EXPECT_EQ (eq2string (eq), "1;2;3,4;5,6"); + + tl::equivalence_clusters eq2; + + eq2.same (2, 2); + eq2.same (4, 5); + eq2.same (4, 10); + eq2.same (11, 11); + EXPECT_EQ (eq2string (eq2), "2;4,5,10;11"); + + eq.merge (eq2); + EXPECT_EQ (eq2string (eq), "1;2;3,4,5,6,10;11"); +} + diff --git a/src/tl/unit_tests/tlListTests.cc b/src/tl/unit_tests/tlListTests.cc new file mode 100644 index 000000000..33e229a84 --- /dev/null +++ b/src/tl/unit_tests/tlListTests.cc @@ -0,0 +1,414 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "tlList.h" +#include "tlUnitTest.h" +#include "tlString.h" + +namespace +{ + +static size_t obj_count = 0; + +struct MyClass1 : public tl::list_node +{ + MyClass1 (int _n) : n (_n) { ++obj_count; } + MyClass1 (const MyClass1 &other) : n (other.n) { ++obj_count; } + ~MyClass1 () { --obj_count; } + int n; + bool operator== (const MyClass1 &other) const { return n == other.n; } + bool operator< (const MyClass1 &other) const { return n < other.n; } +}; + +struct MyClass2 : public tl::list_node +{ + MyClass2 (int _n) : n (_n) { ++obj_count; } + ~MyClass2 () { --obj_count; } + int n; +public: + MyClass2 (const MyClass2 &other); + MyClass2 &operator= (const MyClass2 &other); + bool operator== (const MyClass2 &other) const { return n == other.n; } + bool operator< (const MyClass2 &other) const { return n < other.n; } +}; + +} + +namespace tl +{ + template <> + struct type_traits : public tl::type_traits + { + typedef tl::false_tag has_copy_constructor; + }; +} + +template +static std::string l2s (const tl::list &l) +{ + std::string x; + for (typename tl::list::const_iterator i = l.begin (); i != l.end (); ++i) { + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2sr (const tl::list &l) +{ + std::string x; + for (typename tl::list::const_reverse_iterator i = l.rbegin (); i != l.rend (); ++i) { + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2sm (const tl::list &l) +{ + std::string x; + for (typename tl::list::const_iterator i = l.end (); i != l.begin (); ) { + --i; + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2srm (const tl::list &l) +{ + std::string x; + for (typename tl::list::const_reverse_iterator i = l.rend (); i != l.rbegin (); ) { + --i; + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2s_nc (tl::list &l) +{ + std::string x; + for (typename tl::list::iterator i = l.begin (); i != l.end (); ++i) { + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2sr_nc (tl::list &l) +{ + std::string x; + for (typename tl::list::reverse_iterator i = l.rbegin (); i != l.rend (); ++i) { + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2sm_nc (tl::list &l) +{ + std::string x; + for (typename tl::list::iterator i = l.end (); i != l.begin (); ) { + --i; + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +template +static std::string l2srm_nc (tl::list &l) +{ + std::string x; + for (typename tl::list::reverse_iterator i = l.rend (); i != l.rbegin (); ) { + --i; + if (!x.empty ()) { + x += ","; + } + x += tl::to_string (i->n); + } + return x; +} + +TEST(1_Basic) +{ + obj_count = 0; + + tl::list l1, l2; + + EXPECT_EQ (l1.empty (), true); + EXPECT_EQ (l1.size (), size_t (0)); + EXPECT_EQ (l2s (l1), ""); + EXPECT_EQ (l2sr (l1), ""); + + l1.push_back (new MyClass1 (17)); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (1)); + EXPECT_EQ (l2s (l1), "17"); + EXPECT_EQ (l2sr (l1), "17"); + + l1.push_back (MyClass1 (42)); + l2 = l1; + tl::list l3 (l2); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (2)); + EXPECT_EQ (l2s (l1), "17,42"); + EXPECT_EQ (l2sr (l1), "42,17"); + + delete l1.first (); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (1)); + EXPECT_EQ (l2s (l1), "42"); + EXPECT_EQ (l2sr (l1), "42"); + + l1.clear (); + EXPECT_EQ (l1.empty (), true); + EXPECT_EQ (l1.size (), size_t (0)); + EXPECT_EQ (l2s (l1), ""); + EXPECT_EQ (l2sr (l1), ""); + + EXPECT_EQ (l2s (l2), "17,42"); + EXPECT_EQ (l2sr (l2), "42,17"); + l2.pop_back (); + EXPECT_EQ (l2s (l2), "17"); + EXPECT_EQ (l2sr (l2), "17"); + + l3.push_back (new MyClass1 (2)); + l3.push_front (new MyClass1 (1)); + EXPECT_EQ (l2s (l3), "1,17,42,2"); + EXPECT_EQ (l2srm (l3), "1,17,42,2"); + EXPECT_EQ (l2sm (l3), "2,42,17,1"); + EXPECT_EQ (l2sr (l3), "2,42,17,1"); + EXPECT_EQ (l2s_nc (l3), "1,17,42,2"); + EXPECT_EQ (l2srm_nc (l3), "1,17,42,2"); + EXPECT_EQ (l2sm_nc (l3), "2,42,17,1"); + EXPECT_EQ (l2sr_nc (l3), "2,42,17,1"); + EXPECT_EQ (l3.size (), size_t (4)); + + l3.pop_back (); + EXPECT_EQ (l2s (l3), "1,17,42"); + EXPECT_EQ (l2sr (l3), "42,17,1"); + EXPECT_EQ (l3.size (), size_t (3)); + + MyClass1 *c1; + c1 = l3.first (); + EXPECT_EQ (c1->n, 1); + c1 = c1->next (); + EXPECT_EQ (c1->n, 17); + c1 = c1->next (); + EXPECT_EQ (c1->n, 42); + EXPECT_EQ (c1->next (), 0); + + c1 = l3.last (); + EXPECT_EQ (c1->n, 42); + c1 = c1->prev (); + EXPECT_EQ (c1->n, 17); + c1 = c1->prev (); + EXPECT_EQ (c1->n, 1); + EXPECT_EQ (c1->prev (), 0); + + l3.pop_front (); + EXPECT_EQ (l2s (l3), "17,42"); + EXPECT_EQ (l2sr (l3), "42,17"); + EXPECT_EQ (l3.size (), size_t (2)); + + l3.push_back (new MyClass1 (1)); + EXPECT_EQ (l2s (l3), "17,42,1"); + EXPECT_EQ (l2sr (l3), "1,42,17"); + EXPECT_EQ (l3.size (), size_t (3)); + + c1 = l3.first ()->next (); + delete c1; + EXPECT_EQ (l2s (l3), "17,1"); + EXPECT_EQ (l2sr (l3), "1,17"); + EXPECT_EQ (l3.size (), size_t (2)); + + EXPECT_EQ (l2sr (l2), "17"); + EXPECT_EQ (l2sr (l3), "1,17"); + l3.swap (l2); + EXPECT_EQ (l2sr (l2), "1,17"); + EXPECT_EQ (l2sr (l3), "17"); + + l1.clear (); + l2.swap (l1); + EXPECT_EQ (l2sr (l1), "1,17"); + EXPECT_EQ (l2sr (l2), ""); + + l1.clear (); + l3.clear (); + + l2.swap (l1); + EXPECT_EQ (l2sr (l1), ""); + EXPECT_EQ (l2sr (l2), ""); + + EXPECT_EQ (obj_count, size_t (0)); +} + +TEST(2_BasicNoCopy) +{ + { + obj_count = 0; + + MyClass2 mc2 (42); // will not be owned + tl::list l1, l2, l3; + + EXPECT_EQ (l1.empty (), true); + EXPECT_EQ (l1.size (), size_t (0)); + EXPECT_EQ (l2s (l1), ""); + EXPECT_EQ (l2sr (l1), ""); + + l1.push_back (new MyClass2 (17)); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (1)); + EXPECT_EQ (l2s (l1), "17"); + EXPECT_EQ (l2sr (l1), "17"); + + l1.push_back (mc2); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (2)); + EXPECT_EQ (l2s (l1), "17,42"); + EXPECT_EQ (l2sr (l1), "42,17"); + + delete l1.first (); + EXPECT_EQ (l1.empty (), false); + EXPECT_EQ (l1.size (), size_t (1)); + EXPECT_EQ (l2s (l1), "42"); + EXPECT_EQ (l2sr (l1), "42"); + + l1.clear (); + EXPECT_EQ (l1.empty (), true); + EXPECT_EQ (l1.size (), size_t (0)); + EXPECT_EQ (l2s (l1), ""); + EXPECT_EQ (l2sr (l1), ""); + + l2.push_back (new MyClass2 (17)); + l2.push_back (new MyClass2 (42)); + + EXPECT_EQ (l2s (l2), "17,42"); + EXPECT_EQ (l2sr (l2), "42,17"); + l2.pop_back (); + EXPECT_EQ (l2s (l2), "17"); + EXPECT_EQ (l2sr (l2), "17"); + + EXPECT_EQ (l2 == l3, false); + EXPECT_EQ (l2 != l3, true); + EXPECT_EQ (l2 < l3, false); + + l3.push_back (new MyClass2 (17)); + EXPECT_EQ (l2 == l3, true); + EXPECT_EQ (l2 != l3, false); + EXPECT_EQ (l2 < l3, false); + + l3.push_back (new MyClass2 (42)); + EXPECT_EQ (l2 == l3, false); + EXPECT_EQ (l2 != l3, true); + EXPECT_EQ (l2 < l3, true); + + l3.push_back (new MyClass2 (2)); + l3.push_front (new MyClass2 (1)); + EXPECT_EQ (l2 == l3, false); + EXPECT_EQ (l2 != l3, true); + EXPECT_EQ (l2 < l3, false); + + EXPECT_EQ (l2s (l3), "1,17,42,2"); + EXPECT_EQ (l2srm (l3), "1,17,42,2"); + EXPECT_EQ (l2sm (l3), "2,42,17,1"); + EXPECT_EQ (l2sr (l3), "2,42,17,1"); + EXPECT_EQ (l2s_nc (l3), "1,17,42,2"); + EXPECT_EQ (l2srm_nc (l3), "1,17,42,2"); + EXPECT_EQ (l2sm_nc (l3), "2,42,17,1"); + EXPECT_EQ (l2sr_nc (l3), "2,42,17,1"); + EXPECT_EQ (l3.size (), size_t (4)); + + l3.pop_back (); + EXPECT_EQ (l2s (l3), "1,17,42"); + EXPECT_EQ (l2sr (l3), "42,17,1"); + EXPECT_EQ (l3.size (), size_t (3)); + + MyClass2 *c1; + c1 = l3.first (); + EXPECT_EQ (c1->n, 1); + c1 = c1->next (); + EXPECT_EQ (c1->n, 17); + c1 = c1->next (); + EXPECT_EQ (c1->n, 42); + EXPECT_EQ (c1->next (), 0); + + c1 = l3.last (); + EXPECT_EQ (c1->n, 42); + c1 = c1->prev (); + EXPECT_EQ (c1->n, 17); + c1 = c1->prev (); + EXPECT_EQ (c1->n, 1); + EXPECT_EQ (c1->prev (), 0); + + l3.pop_front (); + EXPECT_EQ (l2s (l3), "17,42"); + EXPECT_EQ (l2sr (l3), "42,17"); + EXPECT_EQ (l3.size (), size_t (2)); + + l3.push_back (new MyClass2 (1)); + EXPECT_EQ (l2s (l3), "17,42,1"); + EXPECT_EQ (l2sr (l3), "1,42,17"); + EXPECT_EQ (l3.size (), size_t (3)); + + c1 = l3.first ()->next (); + delete c1; + EXPECT_EQ (l2s (l3), "17,1"); + EXPECT_EQ (l2sr (l3), "1,17"); + EXPECT_EQ (l3.size (), size_t (2)); + + EXPECT_EQ (l2sr (l2), "17"); + EXPECT_EQ (l2sr (l3), "1,17"); + l3.swap (l2); + EXPECT_EQ (l2sr (l2), "1,17"); + EXPECT_EQ (l2sr (l3), "17"); + + l1.clear (); + l2.clear (); + l3.clear (); + EXPECT_EQ (obj_count, size_t (1)); // one for mc2 + } + + EXPECT_EQ (obj_count, size_t (0)); // mc2 gone as well +} diff --git a/src/tl/unit_tests/tlUniqueIdTests.cc b/src/tl/unit_tests/tlUniqueIdTests.cc new file mode 100644 index 000000000..ef3290436 --- /dev/null +++ b/src/tl/unit_tests/tlUniqueIdTests.cc @@ -0,0 +1,51 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "tlUniqueId.h" +#include "tlUnitTest.h" + +#include + +namespace { + class MyClass : public tl::UniqueId { + public: + MyClass () { } + }; +} + +// basic parsing ability +TEST(1) +{ + tl::id_type id, id0; + + id = tl::id_of (0); + EXPECT_EQ (id, tl::id_type (0)); + + std::auto_ptr ptr; + ptr.reset (new MyClass ()); + id0 = id = tl::id_of (ptr.get ()); + EXPECT_NE (id, tl::id_type (0)); + + ptr.reset (new MyClass ()); + id = tl::id_of (ptr.get ()); + EXPECT_EQ (id, id0 + 1); +} diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index 89d63ec6c..aa8106e22 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -35,6 +35,9 @@ SOURCES = \ tlHttpStream.cc \ tlInt128Support.cc \ tlLongInt.cc \ + tlUniqueIdTests.cc \ + tlListTests.cc \ + tlEquivalenceClustersTests.cc !equals(HAVE_QT, "0") { diff --git a/testdata/algo/angle_check_l1.gds b/testdata/algo/angle_check_l1.gds new file mode 100644 index 000000000..0c539f083 Binary files /dev/null and b/testdata/algo/angle_check_l1.gds differ diff --git a/testdata/algo/antenna_au1.gds b/testdata/algo/antenna_au1.gds new file mode 100644 index 000000000..0367b454c Binary files /dev/null and b/testdata/algo/antenna_au1.gds differ diff --git a/testdata/algo/antenna_l1.gds b/testdata/algo/antenna_l1.gds new file mode 100644 index 000000000..97fcd2569 Binary files /dev/null and b/testdata/algo/antenna_l1.gds differ diff --git a/testdata/algo/cell_variants_au1.gds b/testdata/algo/cell_variants_au1.gds new file mode 100644 index 000000000..97d4877ce Binary files /dev/null and b/testdata/algo/cell_variants_au1.gds differ diff --git a/testdata/algo/cell_variants_au2.gds b/testdata/algo/cell_variants_au2.gds new file mode 100644 index 000000000..c30651bb0 Binary files /dev/null and b/testdata/algo/cell_variants_au2.gds differ diff --git a/testdata/algo/cell_variants_l1.gds b/testdata/algo/cell_variants_l1.gds new file mode 100644 index 000000000..56c817fbc Binary files /dev/null and b/testdata/algo/cell_variants_l1.gds differ diff --git a/testdata/algo/deep_edge_pairs_au1.gds b/testdata/algo/deep_edge_pairs_au1.gds new file mode 100644 index 000000000..c1c697e28 Binary files /dev/null and b/testdata/algo/deep_edge_pairs_au1.gds differ diff --git a/testdata/algo/deep_edges_au1.gds b/testdata/algo/deep_edges_au1.gds new file mode 100644 index 000000000..540080332 Binary files /dev/null and b/testdata/algo/deep_edges_au1.gds differ diff --git a/testdata/algo/deep_edges_au2.gds b/testdata/algo/deep_edges_au2.gds new file mode 100644 index 000000000..022d4132e Binary files /dev/null and b/testdata/algo/deep_edges_au2.gds differ diff --git a/testdata/algo/deep_edges_au3.gds b/testdata/algo/deep_edges_au3.gds new file mode 100644 index 000000000..ae59b5807 Binary files /dev/null and b/testdata/algo/deep_edges_au3.gds differ diff --git a/testdata/algo/deep_edges_au4.gds b/testdata/algo/deep_edges_au4.gds new file mode 100644 index 000000000..f91ff8526 Binary files /dev/null and b/testdata/algo/deep_edges_au4.gds differ diff --git a/testdata/algo/deep_edges_au5a.gds b/testdata/algo/deep_edges_au5a.gds new file mode 100644 index 000000000..28805aa0d Binary files /dev/null and b/testdata/algo/deep_edges_au5a.gds differ diff --git a/testdata/algo/deep_edges_au5b.gds b/testdata/algo/deep_edges_au5b.gds new file mode 100644 index 000000000..538b90698 Binary files /dev/null and b/testdata/algo/deep_edges_au5b.gds differ diff --git a/testdata/algo/deep_edges_au6.gds b/testdata/algo/deep_edges_au6.gds new file mode 100644 index 000000000..1bd817210 Binary files /dev/null and b/testdata/algo/deep_edges_au6.gds differ diff --git a/testdata/algo/deep_edges_au7.gds b/testdata/algo/deep_edges_au7.gds new file mode 100644 index 000000000..ec135e2c2 Binary files /dev/null and b/testdata/algo/deep_edges_au7.gds differ diff --git a/testdata/algo/deep_edges_au8.gds b/testdata/algo/deep_edges_au8.gds new file mode 100644 index 000000000..16aa28b5c Binary files /dev/null and b/testdata/algo/deep_edges_au8.gds differ diff --git a/testdata/algo/deep_edges_au9.gds b/testdata/algo/deep_edges_au9.gds new file mode 100644 index 000000000..2ca62fbee Binary files /dev/null and b/testdata/algo/deep_edges_au9.gds differ diff --git a/testdata/algo/deep_region_area_peri_l1.gds b/testdata/algo/deep_region_area_peri_l1.gds new file mode 100644 index 000000000..a4064f66a Binary files /dev/null and b/testdata/algo/deep_region_area_peri_l1.gds differ diff --git a/testdata/algo/deep_region_area_peri_l1_dbu2.gds b/testdata/algo/deep_region_area_peri_l1_dbu2.gds new file mode 100644 index 000000000..bc2ecc13d Binary files /dev/null and b/testdata/algo/deep_region_area_peri_l1_dbu2.gds differ diff --git a/testdata/algo/deep_region_au1.gds b/testdata/algo/deep_region_au1.gds new file mode 100644 index 000000000..c21eadd76 Binary files /dev/null and b/testdata/algo/deep_region_au1.gds differ diff --git a/testdata/algo/deep_region_au10.gds b/testdata/algo/deep_region_au10.gds new file mode 100644 index 000000000..b06543d13 Binary files /dev/null and b/testdata/algo/deep_region_au10.gds differ diff --git a/testdata/algo/deep_region_au100.gds b/testdata/algo/deep_region_au100.gds new file mode 100644 index 000000000..b88c5140e Binary files /dev/null and b/testdata/algo/deep_region_au100.gds differ diff --git a/testdata/algo/deep_region_au101.gds b/testdata/algo/deep_region_au101.gds new file mode 100644 index 000000000..ea6abc934 Binary files /dev/null and b/testdata/algo/deep_region_au101.gds differ diff --git a/testdata/algo/deep_region_au11.gds b/testdata/algo/deep_region_au11.gds new file mode 100644 index 000000000..764182714 Binary files /dev/null and b/testdata/algo/deep_region_au11.gds differ diff --git a/testdata/algo/deep_region_au12.gds b/testdata/algo/deep_region_au12.gds new file mode 100644 index 000000000..3c98029ba Binary files /dev/null and b/testdata/algo/deep_region_au12.gds differ diff --git a/testdata/algo/deep_region_au13.gds b/testdata/algo/deep_region_au13.gds new file mode 100644 index 000000000..ade409f90 Binary files /dev/null and b/testdata/algo/deep_region_au13.gds differ diff --git a/testdata/algo/deep_region_au14a.gds b/testdata/algo/deep_region_au14a.gds new file mode 100644 index 000000000..7a3109d50 Binary files /dev/null and b/testdata/algo/deep_region_au14a.gds differ diff --git a/testdata/algo/deep_region_au14b.gds b/testdata/algo/deep_region_au14b.gds new file mode 100644 index 000000000..85b83f54e Binary files /dev/null and b/testdata/algo/deep_region_au14b.gds differ diff --git a/testdata/algo/deep_region_au15a.gds b/testdata/algo/deep_region_au15a.gds new file mode 100644 index 000000000..65b5dd96d Binary files /dev/null and b/testdata/algo/deep_region_au15a.gds differ diff --git a/testdata/algo/deep_region_au15b.gds b/testdata/algo/deep_region_au15b.gds new file mode 100644 index 000000000..da172280c Binary files /dev/null and b/testdata/algo/deep_region_au15b.gds differ diff --git a/testdata/algo/deep_region_au16.gds b/testdata/algo/deep_region_au16.gds new file mode 100644 index 000000000..eea9b6161 Binary files /dev/null and b/testdata/algo/deep_region_au16.gds differ diff --git a/testdata/algo/deep_region_au17.gds b/testdata/algo/deep_region_au17.gds new file mode 100644 index 000000000..df3ab0872 Binary files /dev/null and b/testdata/algo/deep_region_au17.gds differ diff --git a/testdata/algo/deep_region_au18.gds b/testdata/algo/deep_region_au18.gds new file mode 100644 index 000000000..35edb6a15 Binary files /dev/null and b/testdata/algo/deep_region_au18.gds differ diff --git a/testdata/algo/deep_region_au19.gds b/testdata/algo/deep_region_au19.gds new file mode 100644 index 000000000..0383ee510 Binary files /dev/null and b/testdata/algo/deep_region_au19.gds differ diff --git a/testdata/algo/deep_region_au2.gds b/testdata/algo/deep_region_au2.gds new file mode 100644 index 000000000..bfa6b5bde Binary files /dev/null and b/testdata/algo/deep_region_au2.gds differ diff --git a/testdata/algo/deep_region_au20.gds b/testdata/algo/deep_region_au20.gds new file mode 100644 index 000000000..e0a4ec139 Binary files /dev/null and b/testdata/algo/deep_region_au20.gds differ diff --git a/testdata/algo/deep_region_au21.gds b/testdata/algo/deep_region_au21.gds new file mode 100644 index 000000000..e166ba578 Binary files /dev/null and b/testdata/algo/deep_region_au21.gds differ diff --git a/testdata/algo/deep_region_au22.gds b/testdata/algo/deep_region_au22.gds new file mode 100644 index 000000000..d6eb6242a Binary files /dev/null and b/testdata/algo/deep_region_au22.gds differ diff --git a/testdata/algo/deep_region_au23.gds b/testdata/algo/deep_region_au23.gds new file mode 100644 index 000000000..2f0ab20d1 Binary files /dev/null and b/testdata/algo/deep_region_au23.gds differ diff --git a/testdata/algo/deep_region_au24.gds b/testdata/algo/deep_region_au24.gds new file mode 100644 index 000000000..751949da0 Binary files /dev/null and b/testdata/algo/deep_region_au24.gds differ diff --git a/testdata/algo/deep_region_au3.gds b/testdata/algo/deep_region_au3.gds new file mode 100644 index 000000000..e2e935ba8 Binary files /dev/null and b/testdata/algo/deep_region_au3.gds differ diff --git a/testdata/algo/deep_region_au4a.gds b/testdata/algo/deep_region_au4a.gds new file mode 100644 index 000000000..d87d595f7 Binary files /dev/null and b/testdata/algo/deep_region_au4a.gds differ diff --git a/testdata/algo/deep_region_au4b.gds b/testdata/algo/deep_region_au4b.gds new file mode 100644 index 000000000..af9145709 Binary files /dev/null and b/testdata/algo/deep_region_au4b.gds differ diff --git a/testdata/algo/deep_region_au5.gds b/testdata/algo/deep_region_au5.gds new file mode 100644 index 000000000..273f7e03e Binary files /dev/null and b/testdata/algo/deep_region_au5.gds differ diff --git a/testdata/algo/deep_region_au6.gds b/testdata/algo/deep_region_au6.gds new file mode 100644 index 000000000..9fecea99f Binary files /dev/null and b/testdata/algo/deep_region_au6.gds differ diff --git a/testdata/algo/deep_region_au7.gds b/testdata/algo/deep_region_au7.gds new file mode 100644 index 000000000..4d0741a36 Binary files /dev/null and b/testdata/algo/deep_region_au7.gds differ diff --git a/testdata/algo/deep_region_au9a.gds b/testdata/algo/deep_region_au9a.gds new file mode 100644 index 000000000..16dd35174 Binary files /dev/null and b/testdata/algo/deep_region_au9a.gds differ diff --git a/testdata/algo/deep_region_au9b.gds b/testdata/algo/deep_region_au9b.gds new file mode 100644 index 000000000..de814b6f6 Binary files /dev/null and b/testdata/algo/deep_region_au9b.gds differ diff --git a/testdata/algo/deep_region_au9c.gds b/testdata/algo/deep_region_au9c.gds new file mode 100644 index 000000000..9e796839e Binary files /dev/null and b/testdata/algo/deep_region_au9c.gds differ diff --git a/testdata/algo/deep_region_au9d.gds b/testdata/algo/deep_region_au9d.gds new file mode 100644 index 000000000..fedebccf0 Binary files /dev/null and b/testdata/algo/deep_region_au9d.gds differ diff --git a/testdata/algo/deep_region_au9e.gds b/testdata/algo/deep_region_au9e.gds new file mode 100644 index 000000000..88453da6d Binary files /dev/null and b/testdata/algo/deep_region_au9e.gds differ diff --git a/testdata/algo/deep_region_l1.gds b/testdata/algo/deep_region_l1.gds new file mode 100644 index 000000000..fa837f4e2 Binary files /dev/null and b/testdata/algo/deep_region_l1.gds differ diff --git a/testdata/algo/device_extract_au1.gds b/testdata/algo/device_extract_au1.gds new file mode 100644 index 000000000..903b6bf0d Binary files /dev/null and b/testdata/algo/device_extract_au1.gds differ diff --git a/testdata/algo/device_extract_au1_flat.gds b/testdata/algo/device_extract_au1_flat.gds new file mode 100644 index 000000000..1dc09360f Binary files /dev/null and b/testdata/algo/device_extract_au1_flat.gds differ diff --git a/testdata/algo/device_extract_au1_implicit_nets.gds b/testdata/algo/device_extract_au1_implicit_nets.gds new file mode 100644 index 000000000..29b65f48e Binary files /dev/null and b/testdata/algo/device_extract_au1_implicit_nets.gds differ diff --git a/testdata/algo/device_extract_au1_rebuild_ff.gds b/testdata/algo/device_extract_au1_rebuild_ff.gds new file mode 100644 index 000000000..c6ec960bd Binary files /dev/null and b/testdata/algo/device_extract_au1_rebuild_ff.gds differ diff --git a/testdata/algo/device_extract_au1_rebuild_fr.gds b/testdata/algo/device_extract_au1_rebuild_fr.gds new file mode 100644 index 000000000..1f2f6ab76 Binary files /dev/null and b/testdata/algo/device_extract_au1_rebuild_fr.gds differ diff --git a/testdata/algo/device_extract_au1_rebuild_nf.gds b/testdata/algo/device_extract_au1_rebuild_nf.gds new file mode 100644 index 000000000..a684cd03a Binary files /dev/null and b/testdata/algo/device_extract_au1_rebuild_nf.gds differ diff --git a/testdata/algo/device_extract_au1_rebuild_nr.gds b/testdata/algo/device_extract_au1_rebuild_nr.gds new file mode 100644 index 000000000..0050d5e7d Binary files /dev/null and b/testdata/algo/device_extract_au1_rebuild_nr.gds differ diff --git a/testdata/algo/device_extract_au1_with_rec_nets.gds b/testdata/algo/device_extract_au1_with_rec_nets.gds new file mode 100644 index 000000000..1bc3662a1 Binary files /dev/null and b/testdata/algo/device_extract_au1_with_rec_nets.gds differ diff --git a/testdata/algo/device_extract_au2_with_rec_nets.gds b/testdata/algo/device_extract_au2_with_rec_nets.gds new file mode 100644 index 000000000..228fa0ef1 Binary files /dev/null and b/testdata/algo/device_extract_au2_with_rec_nets.gds differ diff --git a/testdata/algo/device_extract_au3_with_rec_nets.gds b/testdata/algo/device_extract_au3_with_rec_nets.gds new file mode 100644 index 000000000..bb6696cd4 Binary files /dev/null and b/testdata/algo/device_extract_au3_with_rec_nets.gds differ diff --git a/testdata/algo/device_extract_au4_with_rec_nets.gds b/testdata/algo/device_extract_au4_with_rec_nets.gds new file mode 100644 index 000000000..bd142cbed Binary files /dev/null and b/testdata/algo/device_extract_au4_with_rec_nets.gds differ diff --git a/testdata/algo/device_extract_au5_with_rec_nets.gds b/testdata/algo/device_extract_au5_with_rec_nets.gds new file mode 100644 index 000000000..dbc1be608 Binary files /dev/null and b/testdata/algo/device_extract_au5_with_rec_nets.gds differ diff --git a/testdata/algo/device_extract_l1.gds b/testdata/algo/device_extract_l1.gds new file mode 100644 index 000000000..fa837f4e2 Binary files /dev/null and b/testdata/algo/device_extract_l1.gds differ diff --git a/testdata/algo/device_extract_l1_implicit_nets.gds b/testdata/algo/device_extract_l1_implicit_nets.gds new file mode 100644 index 000000000..0b8e3faf9 Binary files /dev/null and b/testdata/algo/device_extract_l1_implicit_nets.gds differ diff --git a/testdata/algo/device_extract_l2.gds b/testdata/algo/device_extract_l2.gds new file mode 100644 index 000000000..9a9df7ea0 Binary files /dev/null and b/testdata/algo/device_extract_l2.gds differ diff --git a/testdata/algo/device_extract_l3.gds b/testdata/algo/device_extract_l3.gds new file mode 100644 index 000000000..372d04c0e Binary files /dev/null and b/testdata/algo/device_extract_l3.gds differ diff --git a/testdata/algo/device_extract_l5.gds b/testdata/algo/device_extract_l5.gds new file mode 100644 index 000000000..e5a038cdb Binary files /dev/null and b/testdata/algo/device_extract_l5.gds differ diff --git a/testdata/algo/device_extract_l6.gds b/testdata/algo/device_extract_l6.gds new file mode 100644 index 000000000..73ece855b Binary files /dev/null and b/testdata/algo/device_extract_l6.gds differ diff --git a/testdata/algo/hc_test_au1.gds b/testdata/algo/hc_test_au1.gds new file mode 100644 index 000000000..09622935b Binary files /dev/null and b/testdata/algo/hc_test_au1.gds differ diff --git a/testdata/algo/hc_test_au10.gds b/testdata/algo/hc_test_au10.gds new file mode 100644 index 000000000..66499fd67 Binary files /dev/null and b/testdata/algo/hc_test_au10.gds differ diff --git a/testdata/algo/hc_test_au10b.gds b/testdata/algo/hc_test_au10b.gds new file mode 100644 index 000000000..56a41f98c Binary files /dev/null and b/testdata/algo/hc_test_au10b.gds differ diff --git a/testdata/algo/hc_test_au11.gds b/testdata/algo/hc_test_au11.gds new file mode 100644 index 000000000..f24149b23 Binary files /dev/null and b/testdata/algo/hc_test_au11.gds differ diff --git a/testdata/algo/hc_test_au11b.gds b/testdata/algo/hc_test_au11b.gds new file mode 100644 index 000000000..ee96f0842 Binary files /dev/null and b/testdata/algo/hc_test_au11b.gds differ diff --git a/testdata/algo/hc_test_au12.gds b/testdata/algo/hc_test_au12.gds new file mode 100644 index 000000000..72d2f1097 Binary files /dev/null and b/testdata/algo/hc_test_au12.gds differ diff --git a/testdata/algo/hc_test_au12b.gds b/testdata/algo/hc_test_au12b.gds new file mode 100644 index 000000000..e70c96177 Binary files /dev/null and b/testdata/algo/hc_test_au12b.gds differ diff --git a/testdata/algo/hc_test_au13.gds b/testdata/algo/hc_test_au13.gds new file mode 100644 index 000000000..d7fe0cc21 Binary files /dev/null and b/testdata/algo/hc_test_au13.gds differ diff --git a/testdata/algo/hc_test_au13b.gds b/testdata/algo/hc_test_au13b.gds new file mode 100644 index 000000000..f14dfeaa9 Binary files /dev/null and b/testdata/algo/hc_test_au13b.gds differ diff --git a/testdata/algo/hc_test_au14.gds b/testdata/algo/hc_test_au14.gds new file mode 100644 index 000000000..7f5500e60 Binary files /dev/null and b/testdata/algo/hc_test_au14.gds differ diff --git a/testdata/algo/hc_test_au14b.gds b/testdata/algo/hc_test_au14b.gds new file mode 100644 index 000000000..5117445b4 Binary files /dev/null and b/testdata/algo/hc_test_au14b.gds differ diff --git a/testdata/algo/hc_test_au15.gds b/testdata/algo/hc_test_au15.gds new file mode 100644 index 000000000..15cd28050 Binary files /dev/null and b/testdata/algo/hc_test_au15.gds differ diff --git a/testdata/algo/hc_test_au15b.gds b/testdata/algo/hc_test_au15b.gds new file mode 100644 index 000000000..bfde53a73 Binary files /dev/null and b/testdata/algo/hc_test_au15b.gds differ diff --git a/testdata/algo/hc_test_au16.gds b/testdata/algo/hc_test_au16.gds new file mode 100644 index 000000000..96153688c Binary files /dev/null and b/testdata/algo/hc_test_au16.gds differ diff --git a/testdata/algo/hc_test_au16b.gds b/testdata/algo/hc_test_au16b.gds new file mode 100644 index 000000000..a4ac54912 Binary files /dev/null and b/testdata/algo/hc_test_au16b.gds differ diff --git a/testdata/algo/hc_test_au17.gds b/testdata/algo/hc_test_au17.gds new file mode 100644 index 000000000..d4ae2eb93 Binary files /dev/null and b/testdata/algo/hc_test_au17.gds differ diff --git a/testdata/algo/hc_test_au17b.gds b/testdata/algo/hc_test_au17b.gds new file mode 100644 index 000000000..441679c28 Binary files /dev/null and b/testdata/algo/hc_test_au17b.gds differ diff --git a/testdata/algo/hc_test_au1b.gds b/testdata/algo/hc_test_au1b.gds new file mode 100644 index 000000000..65406edd5 Binary files /dev/null and b/testdata/algo/hc_test_au1b.gds differ diff --git a/testdata/algo/hc_test_au2.gds b/testdata/algo/hc_test_au2.gds new file mode 100644 index 000000000..73576e4f4 Binary files /dev/null and b/testdata/algo/hc_test_au2.gds differ diff --git a/testdata/algo/hc_test_au2b.gds b/testdata/algo/hc_test_au2b.gds new file mode 100644 index 000000000..cd520f4de Binary files /dev/null and b/testdata/algo/hc_test_au2b.gds differ diff --git a/testdata/algo/hc_test_au3.gds b/testdata/algo/hc_test_au3.gds new file mode 100644 index 000000000..54b1e4e95 Binary files /dev/null and b/testdata/algo/hc_test_au3.gds differ diff --git a/testdata/algo/hc_test_au3b.gds b/testdata/algo/hc_test_au3b.gds new file mode 100644 index 000000000..9767fc7a0 Binary files /dev/null and b/testdata/algo/hc_test_au3b.gds differ diff --git a/testdata/algo/hc_test_au4.gds b/testdata/algo/hc_test_au4.gds new file mode 100644 index 000000000..37e140f06 Binary files /dev/null and b/testdata/algo/hc_test_au4.gds differ diff --git a/testdata/algo/hc_test_au4b.gds b/testdata/algo/hc_test_au4b.gds new file mode 100644 index 000000000..1855f963d Binary files /dev/null and b/testdata/algo/hc_test_au4b.gds differ diff --git a/testdata/algo/hc_test_au5.gds b/testdata/algo/hc_test_au5.gds new file mode 100644 index 000000000..8d3ea6b82 Binary files /dev/null and b/testdata/algo/hc_test_au5.gds differ diff --git a/testdata/algo/hc_test_au5b.gds b/testdata/algo/hc_test_au5b.gds new file mode 100644 index 000000000..b33c783ae Binary files /dev/null and b/testdata/algo/hc_test_au5b.gds differ diff --git a/testdata/algo/hc_test_au6.gds b/testdata/algo/hc_test_au6.gds new file mode 100644 index 000000000..0828f1104 Binary files /dev/null and b/testdata/algo/hc_test_au6.gds differ diff --git a/testdata/algo/hc_test_au6b.gds b/testdata/algo/hc_test_au6b.gds new file mode 100644 index 000000000..903d59d5f Binary files /dev/null and b/testdata/algo/hc_test_au6b.gds differ diff --git a/testdata/algo/hc_test_au7.gds b/testdata/algo/hc_test_au7.gds new file mode 100644 index 000000000..bbaf147d6 Binary files /dev/null and b/testdata/algo/hc_test_au7.gds differ diff --git a/testdata/algo/hc_test_au7b.gds b/testdata/algo/hc_test_au7b.gds new file mode 100644 index 000000000..457f97e87 Binary files /dev/null and b/testdata/algo/hc_test_au7b.gds differ diff --git a/testdata/algo/hc_test_au8.gds b/testdata/algo/hc_test_au8.gds new file mode 100644 index 000000000..587bff467 Binary files /dev/null and b/testdata/algo/hc_test_au8.gds differ diff --git a/testdata/algo/hc_test_au8b.gds b/testdata/algo/hc_test_au8b.gds new file mode 100644 index 000000000..816bb74d3 Binary files /dev/null and b/testdata/algo/hc_test_au8b.gds differ diff --git a/testdata/algo/hc_test_au9.gds b/testdata/algo/hc_test_au9.gds new file mode 100644 index 000000000..d5306c761 Binary files /dev/null and b/testdata/algo/hc_test_au9.gds differ diff --git a/testdata/algo/hc_test_au9b.gds b/testdata/algo/hc_test_au9b.gds new file mode 100644 index 000000000..fce2779a8 Binary files /dev/null and b/testdata/algo/hc_test_au9b.gds differ diff --git a/testdata/algo/hc_test_l1.gds b/testdata/algo/hc_test_l1.gds new file mode 100644 index 000000000..e428b70b0 Binary files /dev/null and b/testdata/algo/hc_test_l1.gds differ diff --git a/testdata/algo/hc_test_l10.gds b/testdata/algo/hc_test_l10.gds new file mode 100644 index 000000000..b4906fc32 Binary files /dev/null and b/testdata/algo/hc_test_l10.gds differ diff --git a/testdata/algo/hc_test_l11.gds b/testdata/algo/hc_test_l11.gds new file mode 100644 index 000000000..21037004c Binary files /dev/null and b/testdata/algo/hc_test_l11.gds differ diff --git a/testdata/algo/hc_test_l12.gds b/testdata/algo/hc_test_l12.gds new file mode 100644 index 000000000..17c1a90df Binary files /dev/null and b/testdata/algo/hc_test_l12.gds differ diff --git a/testdata/algo/hc_test_l13.gds b/testdata/algo/hc_test_l13.gds new file mode 100644 index 000000000..b3dccbed4 Binary files /dev/null and b/testdata/algo/hc_test_l13.gds differ diff --git a/testdata/algo/hc_test_l14.gds b/testdata/algo/hc_test_l14.gds new file mode 100644 index 000000000..3b1eaa46f Binary files /dev/null and b/testdata/algo/hc_test_l14.gds differ diff --git a/testdata/algo/hc_test_l15.gds b/testdata/algo/hc_test_l15.gds new file mode 100644 index 000000000..ff5f9ece9 Binary files /dev/null and b/testdata/algo/hc_test_l15.gds differ diff --git a/testdata/algo/hc_test_l16.gds b/testdata/algo/hc_test_l16.gds new file mode 100644 index 000000000..f4bb6149e Binary files /dev/null and b/testdata/algo/hc_test_l16.gds differ diff --git a/testdata/algo/hc_test_l17.gds b/testdata/algo/hc_test_l17.gds new file mode 100644 index 000000000..4ddd3a2a7 Binary files /dev/null and b/testdata/algo/hc_test_l17.gds differ diff --git a/testdata/algo/hc_test_l2.gds b/testdata/algo/hc_test_l2.gds new file mode 100644 index 000000000..f876246b0 Binary files /dev/null and b/testdata/algo/hc_test_l2.gds differ diff --git a/testdata/algo/hc_test_l3.gds b/testdata/algo/hc_test_l3.gds new file mode 100644 index 000000000..f0b6251fa Binary files /dev/null and b/testdata/algo/hc_test_l3.gds differ diff --git a/testdata/algo/hc_test_l4.gds b/testdata/algo/hc_test_l4.gds new file mode 100644 index 000000000..933c5ecee Binary files /dev/null and b/testdata/algo/hc_test_l4.gds differ diff --git a/testdata/algo/hc_test_l5.gds b/testdata/algo/hc_test_l5.gds new file mode 100644 index 000000000..1ed519c46 Binary files /dev/null and b/testdata/algo/hc_test_l5.gds differ diff --git a/testdata/algo/hc_test_l6.gds b/testdata/algo/hc_test_l6.gds new file mode 100644 index 000000000..38ff58c9c Binary files /dev/null and b/testdata/algo/hc_test_l6.gds differ diff --git a/testdata/algo/hc_test_l7.gds b/testdata/algo/hc_test_l7.gds new file mode 100644 index 000000000..53c055bbd Binary files /dev/null and b/testdata/algo/hc_test_l7.gds differ diff --git a/testdata/algo/hc_test_l8.gds b/testdata/algo/hc_test_l8.gds new file mode 100644 index 000000000..ce243afd2 Binary files /dev/null and b/testdata/algo/hc_test_l8.gds differ diff --git a/testdata/algo/hc_test_l9.gds b/testdata/algo/hc_test_l9.gds new file mode 100644 index 000000000..2bc98a30e Binary files /dev/null and b/testdata/algo/hc_test_l9.gds differ diff --git a/testdata/algo/hierarchy_builder_au1.gds b/testdata/algo/hierarchy_builder_au1.gds new file mode 100644 index 000000000..fa837f4e2 Binary files /dev/null and b/testdata/algo/hierarchy_builder_au1.gds differ diff --git a/testdata/algo/hierarchy_builder_au2a.gds b/testdata/algo/hierarchy_builder_au2a.gds new file mode 100644 index 000000000..9d556aed6 Binary files /dev/null and b/testdata/algo/hierarchy_builder_au2a.gds differ diff --git a/testdata/algo/hierarchy_builder_au2b.gds b/testdata/algo/hierarchy_builder_au2b.gds new file mode 100644 index 000000000..66a8a6d76 Binary files /dev/null and b/testdata/algo/hierarchy_builder_au2b.gds differ diff --git a/testdata/algo/hierarchy_builder_au2c.gds b/testdata/algo/hierarchy_builder_au2c.gds new file mode 100644 index 000000000..85f5d6082 Binary files /dev/null and b/testdata/algo/hierarchy_builder_au2c.gds differ diff --git a/testdata/algo/hierarchy_builder_au2d.gds b/testdata/algo/hierarchy_builder_au2d.gds new file mode 100644 index 000000000..9589536ac Binary files /dev/null and b/testdata/algo/hierarchy_builder_au2d.gds differ diff --git a/testdata/algo/hierarchy_builder_au2e.gds b/testdata/algo/hierarchy_builder_au2e.gds new file mode 100644 index 000000000..db00d1fc1 Binary files /dev/null and b/testdata/algo/hierarchy_builder_au2e.gds differ diff --git a/testdata/algo/hierarchy_builder_au2f.gds b/testdata/algo/hierarchy_builder_au2f.gds new file mode 100644 index 000000000..7d244fcf7 Binary files /dev/null and b/testdata/algo/hierarchy_builder_au2f.gds differ diff --git a/testdata/algo/hierarchy_builder_au3a.gds b/testdata/algo/hierarchy_builder_au3a.gds new file mode 100644 index 000000000..671fad2a1 Binary files /dev/null and b/testdata/algo/hierarchy_builder_au3a.gds differ diff --git a/testdata/algo/hierarchy_builder_au4a.gds b/testdata/algo/hierarchy_builder_au4a.gds new file mode 100644 index 000000000..504df9920 Binary files /dev/null and b/testdata/algo/hierarchy_builder_au4a.gds differ diff --git a/testdata/algo/hierarchy_builder_au8a.gds b/testdata/algo/hierarchy_builder_au8a.gds new file mode 100644 index 000000000..44fb9379b Binary files /dev/null and b/testdata/algo/hierarchy_builder_au8a.gds differ diff --git a/testdata/algo/hierarchy_builder_au8b.gds b/testdata/algo/hierarchy_builder_au8b.gds new file mode 100644 index 000000000..3e500867d Binary files /dev/null and b/testdata/algo/hierarchy_builder_au8b.gds differ diff --git a/testdata/algo/hierarchy_builder_au_l4.gds b/testdata/algo/hierarchy_builder_au_l4.gds new file mode 100644 index 000000000..ecabac16a Binary files /dev/null and b/testdata/algo/hierarchy_builder_au_l4.gds differ diff --git a/testdata/algo/hierarchy_builder_au_l5.gds b/testdata/algo/hierarchy_builder_au_l5.gds new file mode 100644 index 000000000..ecabac16a Binary files /dev/null and b/testdata/algo/hierarchy_builder_au_l5.gds differ diff --git a/testdata/algo/hierarchy_builder_l1.gds b/testdata/algo/hierarchy_builder_l1.gds new file mode 100644 index 000000000..fa837f4e2 Binary files /dev/null and b/testdata/algo/hierarchy_builder_l1.gds differ diff --git a/testdata/algo/hierarchy_builder_l2.gds b/testdata/algo/hierarchy_builder_l2.gds new file mode 100644 index 000000000..7447367b2 Binary files /dev/null and b/testdata/algo/hierarchy_builder_l2.gds differ diff --git a/testdata/algo/hierarchy_builder_l3.gds b/testdata/algo/hierarchy_builder_l3.gds new file mode 100644 index 000000000..2deb1d3e4 Binary files /dev/null and b/testdata/algo/hierarchy_builder_l3.gds differ diff --git a/testdata/algo/hierarchy_builder_l4.gds b/testdata/algo/hierarchy_builder_l4.gds new file mode 100644 index 000000000..f44e7261f Binary files /dev/null and b/testdata/algo/hierarchy_builder_l4.gds differ diff --git a/testdata/algo/hierarchy_builder_l5.gds b/testdata/algo/hierarchy_builder_l5.gds new file mode 100644 index 000000000..f44e7261f Binary files /dev/null and b/testdata/algo/hierarchy_builder_l5.gds differ diff --git a/testdata/algo/hierarchy_builder_l5.oas.gz b/testdata/algo/hierarchy_builder_l5.oas.gz new file mode 100644 index 000000000..378380678 Binary files /dev/null and b/testdata/algo/hierarchy_builder_l5.oas.gz differ diff --git a/testdata/algo/hlp1.oas b/testdata/algo/hlp1.oas new file mode 100644 index 000000000..27c260c48 Binary files /dev/null and b/testdata/algo/hlp1.oas differ diff --git a/testdata/algo/hlp10.oas b/testdata/algo/hlp10.oas new file mode 100644 index 000000000..ca1e4a913 Binary files /dev/null and b/testdata/algo/hlp10.oas differ diff --git a/testdata/algo/hlp11.oas b/testdata/algo/hlp11.oas new file mode 100644 index 000000000..e509c377f Binary files /dev/null and b/testdata/algo/hlp11.oas differ diff --git a/testdata/algo/hlp12.oas b/testdata/algo/hlp12.oas new file mode 100644 index 000000000..498b14606 Binary files /dev/null and b/testdata/algo/hlp12.oas differ diff --git a/testdata/drc/drcSimpleTests_au3.oas b/testdata/algo/hlp13.oas similarity index 61% rename from testdata/drc/drcSimpleTests_au3.oas rename to testdata/algo/hlp13.oas index 7f496f52f..236c9dc32 100644 Binary files a/testdata/drc/drcSimpleTests_au3.oas and b/testdata/algo/hlp13.oas differ diff --git a/testdata/drc/drcSimpleTests_au1.oas b/testdata/algo/hlp14.oas similarity index 56% rename from testdata/drc/drcSimpleTests_au1.oas rename to testdata/algo/hlp14.oas index 6cd7e0683..6bc579ab6 100644 Binary files a/testdata/drc/drcSimpleTests_au1.oas and b/testdata/algo/hlp14.oas differ diff --git a/testdata/algo/hlp2.oas b/testdata/algo/hlp2.oas new file mode 100644 index 000000000..f23c070e1 Binary files /dev/null and b/testdata/algo/hlp2.oas differ diff --git a/testdata/algo/hlp3.oas b/testdata/algo/hlp3.oas new file mode 100644 index 000000000..e62f42b52 Binary files /dev/null and b/testdata/algo/hlp3.oas differ diff --git a/testdata/algo/hlp4.oas b/testdata/algo/hlp4.oas new file mode 100644 index 000000000..d19c65ad1 Binary files /dev/null and b/testdata/algo/hlp4.oas differ diff --git a/testdata/algo/hlp5.oas b/testdata/algo/hlp5.oas new file mode 100644 index 000000000..0f4c59bc6 Binary files /dev/null and b/testdata/algo/hlp5.oas differ diff --git a/testdata/algo/hlp6.oas b/testdata/algo/hlp6.oas new file mode 100644 index 000000000..7c4fd80e9 Binary files /dev/null and b/testdata/algo/hlp6.oas differ diff --git a/testdata/algo/hlp7.oas b/testdata/algo/hlp7.oas new file mode 100644 index 000000000..275744865 Binary files /dev/null and b/testdata/algo/hlp7.oas differ diff --git a/testdata/algo/hlp8.oas b/testdata/algo/hlp8.oas new file mode 100644 index 000000000..2b6c1947f Binary files /dev/null and b/testdata/algo/hlp8.oas differ diff --git a/testdata/algo/hlp9.oas b/testdata/algo/hlp9.oas new file mode 100644 index 000000000..9b597f891 Binary files /dev/null and b/testdata/algo/hlp9.oas differ diff --git a/testdata/algo/l2n_reader_au.gds b/testdata/algo/l2n_reader_au.gds new file mode 100644 index 000000000..95598a9d6 Binary files /dev/null and b/testdata/algo/l2n_reader_au.gds differ diff --git a/testdata/algo/l2n_reader_au_2.gds b/testdata/algo/l2n_reader_au_2.gds new file mode 100644 index 000000000..eb63e79b3 Binary files /dev/null and b/testdata/algo/l2n_reader_au_2.gds differ diff --git a/testdata/algo/l2n_writer_au.gds b/testdata/algo/l2n_writer_au.gds new file mode 100644 index 000000000..ff8630243 Binary files /dev/null and b/testdata/algo/l2n_writer_au.gds differ diff --git a/testdata/algo/l2n_writer_au.txt b/testdata/algo/l2n_writer_au.txt new file mode 100644 index 000000000..c61190f22 --- /dev/null +++ b/testdata/algo/l2n_writer_au.txt @@ -0,0 +1,482 @@ +#%l2n-klayout + +# General section + +top(RINGO) +unit(0.001) + +# Layer section +# This section lists the mask layers (drawing or derived) and their connections. + +# Mask layers +layer(poly) +layer(poly_lbl) +layer(diff_cont) +layer(poly_cont) +layer(metal1) +layer(metal1_lbl) +layer(via1) +layer(metal2) +layer(metal2_lbl) +layer(psd) +layer(nsd) + +# Mask layer connectivity +connect(poly poly poly_lbl poly_cont) +connect(poly_lbl poly) +connect(diff_cont diff_cont metal1 psd nsd) +connect(poly_cont poly poly_cont metal1) +connect(metal1 diff_cont poly_cont metal1 metal1_lbl via1) +connect(metal1_lbl metal1) +connect(via1 metal1 via1 metal2) +connect(metal2 via1 metal2 metal2_lbl) +connect(metal2_lbl metal2) +connect(psd diff_cont psd) +connect(nsd diff_cont nsd) + +# Device abstracts section +# Device abstracts list the pin shapes of the devices. +device(D$PMOS PMOS + terminal(S + rect(psd -650 -475 -125 475) + ) + terminal(G + rect(poly -125 -475 125 475) + ) + terminal(D + rect(psd 125 -475 675 475) + ) +) +device(D$PMOS$1 PMOS + terminal(S + rect(psd -675 -475 -125 475) + ) + terminal(G + rect(poly -125 -475 125 475) + ) + terminal(D + rect(psd 125 -475 650 475) + ) +) +device(D$NMOS NMOS + terminal(S + rect(nsd -650 -475 -125 475) + ) + terminal(G + rect(poly -125 -475 125 475) + ) + terminal(D + rect(nsd 125 -475 675 475) + ) +) +device(D$NMOS$1 NMOS + terminal(S + rect(nsd -675 -475 -125 475) + ) + terminal(G + rect(poly -125 -475 125 475) + ) + terminal(D + rect(nsd 125 -475 650 475) + ) +) + +# Circuit section +# Circuits are the hierarchical building blocks of the netlist. +circuit(INV2 + + # Nets with their geometries + net(1 name(IN) + rect(poly -525 -250 -275 2250) + rect(poly -1700 1620 -400 1980) + rect(poly -525 -800 -275 800) + rect(poly -525 2000 -275 3600) + rect(poly_lbl -801 1799 -799 1801) + rect(poly_cont -1630 1690 -1410 1910) + ) + net(2 + rect(poly 275 -250 525 2250) + rect(poly 220 820 580 1180) + rect(poly 275 2000 525 3600) + rect(poly 275 -800 525 800) + rect(diff_cont -910 2490 -690 2710) + rect(diff_cont -910 2890 -690 3110) + rect(diff_cont -910 -310 -690 -90) + rect(diff_cont -910 90 -690 310) + rect(poly_cont 290 890 510 1110) + rect(metal1 -800 820 580 1180) + rect(metal1 -980 -420 -620 2420) + rect(metal1 -980 2420 -620 3180) + rect(metal1 -980 -380 -620 380) + rect(psd -1050 2325 -525 3275) + rect(nsd -1050 -475 -525 475) + ) + net(3 name(OUT) + rect(diff_cont 690 2890 910 3110) + rect(diff_cont 690 2490 910 2710) + rect(diff_cont 690 90 910 310) + rect(diff_cont 690 -310 910 -90) + polygon(metal1 800 20 * 380 940 * * 1620 620 * * 2420 980 * * 1980 1300 * * 20) + rect(metal1 620 2420 980 3180) + rect(metal1 620 -380 980 380) + rect(metal1_lbl 799 1799 801 1801) + rect(psd 525 2325 1050 3275) + rect(nsd 525 -475 1050 475) + ) + net(4 + rect(diff_cont -110 -310 110 -90) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 -310 110 -90) + rect(metal1 -180 -380 180 380) + rect(metal1 -180 -380 180 380) + rect(via1 -125 -325 125 -75) + rect(via1 -125 75 125 325) + rect(metal2 -1400 -450 1400 450) + rect(nsd -275 -475 275 475) + ) + net(5 + rect(diff_cont -110 2490 110 2710) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2490 110 2710) + rect(metal1 -180 2420 180 3180) + rect(metal1 -180 2420 180 3180) + rect(via1 -125 2475 125 2725) + rect(via1 -125 2875 125 3125) + rect(metal2 -1400 2350 1400 3250) + rect(psd -275 2325 275 3275) + ) + + # Outgoing pins and their connections to nets + pin(IN 1) + pin($1 2) + pin(OUT 3) + pin($3 4) + pin($4 5) + + # Devices and their connections + device($1 D$PMOS + location(-400 2800) + param(L 0.25) + param(W 0.95) + param(AS 0.49875) + param(AD 0.26125) + param(PS 2.95) + param(PD 1.5) + terminal(S 2) + terminal(G 1) + terminal(D 5) + ) + device($2 D$PMOS$1 + location(400 2800) + param(L 0.25) + param(W 0.95) + param(AS 0.26125) + param(AD 0.49875) + param(PS 1.5) + param(PD 2.95) + terminal(S 5) + terminal(G 2) + terminal(D 3) + ) + device($3 D$NMOS + location(-400 0) + param(L 0.25) + param(W 0.95) + param(AS 0.49875) + param(AD 0.26125) + param(PS 2.95) + param(PD 1.5) + terminal(S 2) + terminal(G 1) + terminal(D 4) + ) + device($4 D$NMOS$1 + location(400 0) + param(L 0.25) + param(W 0.95) + param(AS 0.26125) + param(AD 0.49875) + param(PS 1.5) + param(PD 2.95) + terminal(S 4) + terminal(G 2) + terminal(D 3) + ) + +) +circuit(RINGO + + # Nets with their geometries + net(1 name(FB) + rect(diff_cont 22850 2490 23070 2710) + rect(diff_cont 22850 2890 23070 3110) + rect(diff_cont 22850 -310 23070 -90) + rect(diff_cont 22850 90 23070 310) + rect(metal1 -1700 1620 -1340 1980) + rect(via1 -1645 1675 -1395 1925) + rect(via1 22835 1675 23085 1925) + rect(metal2 -1720 1600 23160 2000) + rect(metal2_lbl -1 1799 1 1801) + ) + net(2 name(OSC) + rect(diff_cont 24450 2890 24670 3110) + rect(diff_cont 24450 2490 24670 2710) + rect(diff_cont 24450 90 24670 310) + rect(diff_cont 24450 -310 24670 -90) + rect(via1 24435 1675 24685 1925) + rect(metal2 24360 1600 24760 2000) + rect(metal2_lbl 24559 1799 24561 1801) + ) + net(3 name(VSS) + rect(diff_cont 2530 -310 2750 -90) + rect(diff_cont 2530 90 2750 310) + rect(diff_cont 2530 90 2750 310) + rect(diff_cont 2530 -310 2750 -90) + rect(diff_cont -110 -310 110 -90) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 -310 110 -90) + rect(diff_cont 5170 -310 5390 -90) + rect(diff_cont 5170 90 5390 310) + rect(diff_cont 5170 90 5390 310) + rect(diff_cont 5170 -310 5390 -90) + rect(diff_cont 7810 -310 8030 -90) + rect(diff_cont 7810 90 8030 310) + rect(diff_cont 7810 90 8030 310) + rect(diff_cont 7810 -310 8030 -90) + rect(diff_cont 10450 -310 10670 -90) + rect(diff_cont 10450 90 10670 310) + rect(diff_cont 10450 90 10670 310) + rect(diff_cont 10450 -310 10670 -90) + rect(diff_cont 13090 -310 13310 -90) + rect(diff_cont 13090 90 13310 310) + rect(diff_cont 13090 90 13310 310) + rect(diff_cont 13090 -310 13310 -90) + rect(diff_cont 15730 -310 15950 -90) + rect(diff_cont 15730 90 15950 310) + rect(diff_cont 15730 90 15950 310) + rect(diff_cont 15730 -310 15950 -90) + rect(diff_cont 18370 -310 18590 -90) + rect(diff_cont 18370 90 18590 310) + rect(diff_cont 18370 90 18590 310) + rect(diff_cont 18370 -310 18590 -90) + rect(diff_cont 21010 -310 21230 -90) + rect(diff_cont 21010 90 21230 310) + rect(diff_cont 21010 90 21230 310) + rect(diff_cont 21010 -310 21230 -90) + rect(diff_cont 23650 -310 23870 -90) + rect(diff_cont 23650 90 23870 310) + rect(diff_cont 23650 90 23870 310) + rect(diff_cont 23650 -310 23870 -90) + rect(metal1 2460 -380 2820 380) + rect(metal1 2460 -380 2820 380) + rect(metal1 -180 -380 180 380) + rect(metal1 -180 -380 180 380) + rect(metal1 5100 -380 5460 380) + rect(metal1 5100 -380 5460 380) + rect(metal1 7740 -380 8100 380) + rect(metal1 7740 -380 8100 380) + rect(metal1 10380 -380 10740 380) + rect(metal1 10380 -380 10740 380) + rect(metal1 13020 -380 13380 380) + rect(metal1 13020 -380 13380 380) + rect(metal1 15660 -380 16020 380) + rect(metal1 15660 -380 16020 380) + rect(metal1 18300 -380 18660 380) + rect(metal1 18300 -380 18660 380) + rect(metal1 20940 -380 21300 380) + rect(metal1 20940 -380 21300 380) + rect(metal1 23580 -380 23940 380) + rect(metal1 23580 -380 23940 380) + rect(metal2_lbl -1 -1 1 1) + ) + net(4 name(VDD) + rect(diff_cont 2530 2490 2750 2710) + rect(diff_cont 2530 2890 2750 3110) + rect(diff_cont 2530 2890 2750 3110) + rect(diff_cont 2530 2490 2750 2710) + rect(diff_cont -110 2490 110 2710) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2490 110 2710) + rect(diff_cont 5170 2490 5390 2710) + rect(diff_cont 5170 2890 5390 3110) + rect(diff_cont 5170 2890 5390 3110) + rect(diff_cont 5170 2490 5390 2710) + rect(diff_cont 7810 2490 8030 2710) + rect(diff_cont 7810 2890 8030 3110) + rect(diff_cont 7810 2890 8030 3110) + rect(diff_cont 7810 2490 8030 2710) + rect(diff_cont 10450 2490 10670 2710) + rect(diff_cont 10450 2890 10670 3110) + rect(diff_cont 10450 2890 10670 3110) + rect(diff_cont 10450 2490 10670 2710) + rect(diff_cont 13090 2490 13310 2710) + rect(diff_cont 13090 2890 13310 3110) + rect(diff_cont 13090 2890 13310 3110) + rect(diff_cont 13090 2490 13310 2710) + rect(diff_cont 15730 2490 15950 2710) + rect(diff_cont 15730 2890 15950 3110) + rect(diff_cont 15730 2890 15950 3110) + rect(diff_cont 15730 2490 15950 2710) + rect(diff_cont 18370 2490 18590 2710) + rect(diff_cont 18370 2890 18590 3110) + rect(diff_cont 18370 2890 18590 3110) + rect(diff_cont 18370 2490 18590 2710) + rect(diff_cont 21010 2490 21230 2710) + rect(diff_cont 21010 2890 21230 3110) + rect(diff_cont 21010 2890 21230 3110) + rect(diff_cont 21010 2490 21230 2710) + rect(diff_cont 23650 2490 23870 2710) + rect(diff_cont 23650 2890 23870 3110) + rect(diff_cont 23650 2890 23870 3110) + rect(diff_cont 23650 2490 23870 2710) + rect(metal1 2460 2420 2820 3180) + rect(metal1 2460 2420 2820 3180) + rect(metal1 -180 2420 180 3180) + rect(metal1 -180 2420 180 3180) + rect(metal1 5100 2420 5460 3180) + rect(metal1 5100 2420 5460 3180) + rect(metal1 7740 2420 8100 3180) + rect(metal1 7740 2420 8100 3180) + rect(metal1 10380 2420 10740 3180) + rect(metal1 10380 2420 10740 3180) + rect(metal1 13020 2420 13380 3180) + rect(metal1 13020 2420 13380 3180) + rect(metal1 15660 2420 16020 3180) + rect(metal1 15660 2420 16020 3180) + rect(metal1 18300 2420 18660 3180) + rect(metal1 18300 2420 18660 3180) + rect(metal1 20940 2420 21300 3180) + rect(metal1 20940 2420 21300 3180) + rect(metal1 23580 2420 23940 3180) + rect(metal1 23580 2420 23940 3180) + rect(metal2_lbl -1 2799 1 2801) + ) + net(5 + rect(diff_cont 690 2890 910 3110) + rect(diff_cont 690 2490 910 2710) + rect(diff_cont 690 90 910 310) + rect(diff_cont 690 -310 910 -90) + ) + net(6 + rect(diff_cont 21810 2890 22030 3110) + rect(diff_cont 21810 2490 22030 2710) + rect(diff_cont 21810 90 22030 310) + rect(diff_cont 21810 -310 22030 -90) + ) + net(7 + rect(diff_cont 19170 2890 19390 3110) + rect(diff_cont 19170 2490 19390 2710) + rect(diff_cont 19170 90 19390 310) + rect(diff_cont 19170 -310 19390 -90) + ) + net(8 + rect(diff_cont 16530 2890 16750 3110) + rect(diff_cont 16530 2490 16750 2710) + rect(diff_cont 16530 90 16750 310) + rect(diff_cont 16530 -310 16750 -90) + ) + net(9 + rect(diff_cont 13890 2890 14110 3110) + rect(diff_cont 13890 2490 14110 2710) + rect(diff_cont 13890 90 14110 310) + rect(diff_cont 13890 -310 14110 -90) + ) + net(10 + rect(diff_cont 11250 2890 11470 3110) + rect(diff_cont 11250 2490 11470 2710) + rect(diff_cont 11250 90 11470 310) + rect(diff_cont 11250 -310 11470 -90) + ) + net(11 + rect(diff_cont 8610 2890 8830 3110) + rect(diff_cont 8610 2490 8830 2710) + rect(diff_cont 8610 90 8830 310) + rect(diff_cont 8610 -310 8830 -90) + ) + net(12 + rect(diff_cont 5970 2890 6190 3110) + rect(diff_cont 5970 2490 6190 2710) + rect(diff_cont 5970 90 6190 310) + rect(diff_cont 5970 -310 6190 -90) + ) + net(13 + rect(diff_cont 3330 2890 3550 3110) + rect(diff_cont 3330 2490 3550 2710) + rect(diff_cont 3330 90 3550 310) + rect(diff_cont 3330 -310 3550 -90) + ) + + # Outgoing pins and their connections to nets + pin(FB 1) + pin(OSC 2) + pin(VSS 3) + pin(VDD 4) + + # Subcircuits and their connections + circuit($1 INV2 location(23760 0) + pin(IN 6) + pin($1 1) + pin(OUT 2) + pin($3 3) + pin($4 4) + ) + circuit($2 INV2 location(0 0) + pin(IN 1) + pin(OUT 5) + pin($3 3) + pin($4 4) + ) + circuit($3 INV2 location(2640 0) + pin(IN 5) + pin(OUT 13) + pin($3 3) + pin($4 4) + ) + circuit($4 INV2 location(5280 0) + pin(IN 13) + pin(OUT 12) + pin($3 3) + pin($4 4) + ) + circuit($5 INV2 location(7920 0) + pin(IN 12) + pin(OUT 11) + pin($3 3) + pin($4 4) + ) + circuit($6 INV2 location(10560 0) + pin(IN 11) + pin(OUT 10) + pin($3 3) + pin($4 4) + ) + circuit($7 INV2 location(13200 0) + pin(IN 10) + pin(OUT 9) + pin($3 3) + pin($4 4) + ) + circuit($8 INV2 location(15840 0) + pin(IN 9) + pin(OUT 8) + pin($3 3) + pin($4 4) + ) + circuit($9 INV2 location(18480 0) + pin(IN 8) + pin(OUT 7) + pin($3 3) + pin($4 4) + ) + circuit($10 INV2 location(21120 0) + pin(IN 7) + pin(OUT 6) + pin($3 3) + pin($4 4) + ) + +) diff --git a/testdata/algo/l2n_writer_au_2.gds b/testdata/algo/l2n_writer_au_2.gds new file mode 100644 index 000000000..0ca9523ff Binary files /dev/null and b/testdata/algo/l2n_writer_au_2.gds differ diff --git a/testdata/algo/l2n_writer_au_2.txt b/testdata/algo/l2n_writer_au_2.txt new file mode 100644 index 000000000..8138e22f3 --- /dev/null +++ b/testdata/algo/l2n_writer_au_2.txt @@ -0,0 +1,574 @@ +#%l2n-klayout + +# General section + +top(RINGO) +unit(0.001) + +# Layer section +# This section lists the mask layers (drawing or derived) and their connections. + +# Mask layers +layer(rbulk) +layer(nwell) +layer(poly) +layer(poly_lbl) +layer(diff_cont) +layer(poly_cont) +layer(metal1) +layer(metal1_lbl) +layer(via1) +layer(metal2) +layer(metal2_lbl) +layer(ntie) +layer(psd) +layer(ptie) +layer(nsd) + +# Mask layer connectivity +connect(nwell nwell ntie) +connect(poly poly poly_lbl poly_cont) +connect(poly_lbl poly) +connect(diff_cont diff_cont metal1 ntie psd ptie nsd) +connect(poly_cont poly poly_cont metal1) +connect(metal1 diff_cont poly_cont metal1 metal1_lbl via1) +connect(metal1_lbl metal1) +connect(via1 metal1 via1 metal2) +connect(metal2 via1 metal2 metal2_lbl) +connect(metal2_lbl metal2) +connect(ntie nwell diff_cont ntie) +connect(psd diff_cont psd) +connect(ptie diff_cont ptie) +connect(nsd diff_cont nsd) + +# Global nets and connectivity +global(rbulk BULK) +global(ptie BULK) + +# Device abstracts section +# Device abstracts list the pin shapes of the devices. +device(D$PMOS PMOS + terminal(S + rect(psd -650 -475 -125 475) + ) + terminal(G + rect(poly -125 -475 125 475) + ) + terminal(D + rect(psd 125 -475 675 475) + ) + terminal(B + rect(nwell -125 -475 125 475) + ) +) +device(D$PMOS$1 PMOS + terminal(S + rect(psd -675 -475 -125 475) + ) + terminal(G + rect(poly -125 -475 125 475) + ) + terminal(D + rect(psd 125 -475 650 475) + ) + terminal(B + rect(nwell -125 -475 125 475) + ) +) +device(D$NMOS NMOS + terminal(S + rect(nsd -650 -475 -125 475) + ) + terminal(G + rect(poly -125 -475 125 475) + ) + terminal(D + rect(nsd 125 -475 675 475) + ) + terminal(B + rect(rbulk -125 -475 125 475) + ) +) +device(D$NMOS$1 NMOS + terminal(S + rect(nsd -675 -475 -125 475) + ) + terminal(G + rect(poly -125 -475 125 475) + ) + terminal(D + rect(nsd 125 -475 650 475) + ) + terminal(B + rect(rbulk -125 -475 125 475) + ) +) + +# Circuit section +# Circuits are the hierarchical building blocks of the netlist. +circuit(INV2 + + # Nets with their geometries + net(1 + rect(nwell -1400 1800 1400 4580) + rect(diff_cont -110 3930 110 4150) + rect(ntie -400 3700 400 4380) + ) + net(2 name(IN) + rect(poly -525 -250 -275 2250) + rect(poly -1700 1620 -400 1980) + rect(poly -525 -800 -275 800) + rect(poly -525 2000 -275 3600) + rect(poly_lbl -801 1799 -799 1801) + rect(poly_cont -1630 1690 -1410 1910) + ) + net(3 + rect(poly 275 -250 525 2250) + rect(poly 220 820 580 1180) + rect(poly 275 2000 525 3600) + rect(poly 275 -800 525 800) + rect(diff_cont -910 2490 -690 2710) + rect(diff_cont -910 2890 -690 3110) + rect(diff_cont -910 -310 -690 -90) + rect(diff_cont -910 90 -690 310) + rect(poly_cont 290 890 510 1110) + rect(metal1 -800 820 580 1180) + rect(metal1 -980 -420 -620 2420) + rect(metal1 -980 2420 -620 3180) + rect(metal1 -980 -380 -620 380) + rect(psd -1050 2325 -525 3275) + rect(nsd -1050 -475 -525 475) + ) + net(4 name(OUT) + rect(diff_cont 690 2890 910 3110) + rect(diff_cont 690 2490 910 2710) + rect(diff_cont 690 90 910 310) + rect(diff_cont 690 -310 910 -90) + polygon(metal1 800 20 * 380 940 * * 1620 620 * * 2420 980 * * 1980 1300 * * 20) + rect(metal1 620 2420 980 3180) + rect(metal1 620 -380 980 380) + rect(metal1_lbl 799 1799 801 1801) + rect(psd 525 2325 1050 3275) + rect(nsd 525 -475 1050 475) + ) + net(5 name(VSS) + rect(diff_cont -110 -310 110 -90) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 -310 110 -90) + rect(metal1 -180 -380 180 380) + rect(metal1 -180 -380 180 380) + rect(via1 -125 -325 125 -75) + rect(via1 -125 75 125 325) + rect(metal2 -1400 -450 1400 450) + rect(metal2_lbl 1239 -91 1241 -89) + rect(nsd -275 -475 275 475) + ) + net(6 name(VDD) + rect(diff_cont -110 2490 110 2710) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2490 110 2710) + rect(metal1 -180 2420 180 3180) + rect(metal1 -180 2420 180 3180) + rect(via1 -125 2475 125 2725) + rect(via1 -125 2875 125 3125) + rect(metal2 -1400 2350 1400 3250) + rect(metal2_lbl 1249 2799 1251 2801) + rect(psd -275 2325 275 3275) + ) + net(7 name(BULK) + rect(diff_cont -110 -1360 110 -1140) + rect(ptie -400 -1590 400 -910) + ) + + # Outgoing pins and their connections to nets + pin($0 1) + pin(IN 2) + pin($2 3) + pin(OUT 4) + pin(VSS 5) + pin(VDD 6) + pin(BULK 7) + + # Devices and their connections + device($1 D$PMOS + location(-400 2800) + param(L 0.25) + param(W 0.95) + param(AS 0.49875) + param(AD 0.26125) + param(PS 2.95) + param(PD 1.5) + terminal(S 3) + terminal(G 2) + terminal(D 6) + terminal(B 1) + ) + device($2 D$PMOS$1 + location(400 2800) + param(L 0.25) + param(W 0.95) + param(AS 0.26125) + param(AD 0.49875) + param(PS 1.5) + param(PD 2.95) + terminal(S 6) + terminal(G 3) + terminal(D 4) + terminal(B 1) + ) + device($3 D$NMOS + location(-400 0) + param(L 0.25) + param(W 0.95) + param(AS 0.49875) + param(AD 0.26125) + param(PS 2.95) + param(PD 1.5) + terminal(S 3) + terminal(G 2) + terminal(D 5) + terminal(B 7) + ) + device($4 D$NMOS$1 + location(400 0) + param(L 0.25) + param(W 0.95) + param(AS 0.26125) + param(AD 0.49875) + param(PS 1.5) + param(PD 2.95) + terminal(S 5) + terminal(G 3) + terminal(D 4) + terminal(B 7) + ) + +) +circuit(INV2PAIR + + # Nets with their geometries + net(1 name(BULK)) + net(2 + rect(diff_cont 3430 3290 3650 3510) + rect(diff_cont 3430 3690 3650 3910) + rect(diff_cont 3430 490 3650 710) + rect(diff_cont 3430 890 3650 1110) + ) + net(3 + rect(diff_cont 4230 3290 4450 3510) + rect(diff_cont 4230 3690 4450 3910) + rect(diff_cont 4230 3690 4450 3910) + rect(diff_cont 4230 3290 4450 3510) + rect(diff_cont 1590 3290 1810 3510) + rect(diff_cont 1590 3690 1810 3910) + rect(diff_cont 1590 3690 1810 3910) + rect(diff_cont 1590 3290 1810 3510) + rect(metal1 4160 3220 4520 3980) + rect(metal1 4160 3220 4520 3980) + rect(metal1 1520 3220 1880 3980) + rect(metal1 1520 3220 1880 3980) + ) + net(4 + rect(diff_cont 4230 490 4450 710) + rect(diff_cont 4230 890 4450 1110) + rect(diff_cont 4230 890 4450 1110) + rect(diff_cont 4230 490 4450 710) + rect(diff_cont 1590 490 1810 710) + rect(diff_cont 1590 890 1810 1110) + rect(diff_cont 1590 890 1810 1110) + rect(diff_cont 1590 490 1810 710) + rect(metal1 4160 420 4520 1180) + rect(metal1 4160 420 4520 1180) + rect(metal1 1520 420 1880 1180) + rect(metal1 1520 420 1880 1180) + ) + net(5 + rect(diff_cont 2390 3690 2610 3910) + rect(diff_cont 2390 3290 2610 3510) + rect(diff_cont 2390 890 2610 1110) + rect(diff_cont 2390 490 2610 710) + ) + net(6) + net(7 + rect(diff_cont 5030 3690 5250 3910) + rect(diff_cont 5030 3290 5250 3510) + rect(diff_cont 5030 890 5250 1110) + rect(diff_cont 5030 490 5250 710) + ) + net(8) + + # Outgoing pins and their connections to nets + pin(BULK 1) + pin($1 2) + pin($2 3) + pin($3 4) + pin($4 6) + pin($5 7) + pin($6 8) + + # Subcircuits and their connections + circuit($1 INV2 location(1700 800) + pin($0 8) + pin(IN 6) + pin(OUT 5) + pin(VSS 4) + pin(VDD 3) + pin(BULK 1) + ) + circuit($2 INV2 location(4340 800) + pin($0 8) + pin(IN 5) + pin($2 2) + pin(OUT 7) + pin(VSS 4) + pin(VDD 3) + pin(BULK 1) + ) + +) +circuit(RINGO + + # Nets with their geometries + net(1 name(FB) + rect(diff_cont 22850 2490 23070 2710) + rect(diff_cont 22850 2890 23070 3110) + rect(diff_cont 22850 -310 23070 -90) + rect(diff_cont 22850 90 23070 310) + rect(metal1 -1700 1620 -1340 1980) + rect(via1 -1645 1675 -1395 1925) + rect(via1 22835 1675 23085 1925) + rect(metal2 -1720 1600 23160 2000) + rect(metal2_lbl -1 1799 1 1801) + ) + net(2 name(OSC) + rect(diff_cont 24450 2890 24670 3110) + rect(diff_cont 24450 2490 24670 2710) + rect(diff_cont 24450 90 24670 310) + rect(diff_cont 24450 -310 24670 -90) + rect(via1 24435 1675 24685 1925) + rect(metal2 24360 1600 24760 2000) + rect(metal2_lbl 24559 1799 24561 1801) + ) + net(3 name(VDD) + rect(diff_cont 7810 2490 8030 2710) + rect(diff_cont 7810 2890 8030 3110) + rect(diff_cont 7810 2890 8030 3110) + rect(diff_cont 7810 2490 8030 2710) + rect(diff_cont 5170 2490 5390 2710) + rect(diff_cont 5170 2890 5390 3110) + rect(diff_cont 5170 2890 5390 3110) + rect(diff_cont 5170 2490 5390 2710) + rect(diff_cont 2530 2490 2750 2710) + rect(diff_cont 2530 2890 2750 3110) + rect(diff_cont 2530 2890 2750 3110) + rect(diff_cont 2530 2490 2750 2710) + rect(diff_cont -110 2490 110 2710) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2890 110 3110) + rect(diff_cont -110 2490 110 2710) + rect(diff_cont 13090 2490 13310 2710) + rect(diff_cont 13090 2890 13310 3110) + rect(diff_cont 13090 2890 13310 3110) + rect(diff_cont 13090 2490 13310 2710) + rect(diff_cont 10450 2490 10670 2710) + rect(diff_cont 10450 2890 10670 3110) + rect(diff_cont 10450 2890 10670 3110) + rect(diff_cont 10450 2490 10670 2710) + rect(diff_cont 18370 2490 18590 2710) + rect(diff_cont 18370 2890 18590 3110) + rect(diff_cont 18370 2890 18590 3110) + rect(diff_cont 18370 2490 18590 2710) + rect(diff_cont 15730 2490 15950 2710) + rect(diff_cont 15730 2890 15950 3110) + rect(diff_cont 15730 2890 15950 3110) + rect(diff_cont 15730 2490 15950 2710) + rect(diff_cont 23650 2490 23870 2710) + rect(diff_cont 23650 2890 23870 3110) + rect(diff_cont 23650 2890 23870 3110) + rect(diff_cont 23650 2490 23870 2710) + rect(diff_cont 21010 2490 21230 2710) + rect(diff_cont 21010 2890 21230 3110) + rect(diff_cont 21010 2890 21230 3110) + rect(diff_cont 21010 2490 21230 2710) + rect(metal1 -180 3100 180 4220) + rect(metal1 2460 3100 2820 4220) + rect(metal1 5100 3100 5460 4220) + rect(metal1 7740 3100 8100 4220) + rect(metal1 10380 3100 10740 4220) + rect(metal1 13020 3100 13380 4220) + rect(metal1 15660 3100 16020 4220) + rect(metal1 18300 3100 18660 4220) + rect(metal1 20940 3100 21300 4220) + rect(metal1 23580 3100 23940 4220) + rect(metal1 7740 2420 8100 3180) + rect(metal1 7740 2420 8100 3180) + rect(metal1 5100 2420 5460 3180) + rect(metal1 5100 2420 5460 3180) + rect(metal1 2460 2420 2820 3180) + rect(metal1 2460 2420 2820 3180) + rect(metal1 -180 2420 180 3180) + rect(metal1 -180 2420 180 3180) + rect(metal1 13020 2420 13380 3180) + rect(metal1 13020 2420 13380 3180) + rect(metal1 10380 2420 10740 3180) + rect(metal1 10380 2420 10740 3180) + rect(metal1 18300 2420 18660 3180) + rect(metal1 18300 2420 18660 3180) + rect(metal1 15660 2420 16020 3180) + rect(metal1 15660 2420 16020 3180) + rect(metal1 23580 2420 23940 3180) + rect(metal1 23580 2420 23940 3180) + rect(metal1 20940 2420 21300 3180) + rect(metal1 20940 2420 21300 3180) + rect(metal2_lbl -1 2799 1 2801) + ) + net(4 name('BULK,VSS') + rect(diff_cont 7810 -310 8030 -90) + rect(diff_cont 7810 90 8030 310) + rect(diff_cont 7810 90 8030 310) + rect(diff_cont 7810 -310 8030 -90) + rect(diff_cont 5170 -310 5390 -90) + rect(diff_cont 5170 90 5390 310) + rect(diff_cont 5170 90 5390 310) + rect(diff_cont 5170 -310 5390 -90) + rect(diff_cont 2530 -310 2750 -90) + rect(diff_cont 2530 90 2750 310) + rect(diff_cont 2530 90 2750 310) + rect(diff_cont 2530 -310 2750 -90) + rect(diff_cont -110 -310 110 -90) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 90 110 310) + rect(diff_cont -110 -310 110 -90) + rect(diff_cont 13090 -310 13310 -90) + rect(diff_cont 13090 90 13310 310) + rect(diff_cont 13090 90 13310 310) + rect(diff_cont 13090 -310 13310 -90) + rect(diff_cont 10450 -310 10670 -90) + rect(diff_cont 10450 90 10670 310) + rect(diff_cont 10450 90 10670 310) + rect(diff_cont 10450 -310 10670 -90) + rect(diff_cont 18370 -310 18590 -90) + rect(diff_cont 18370 90 18590 310) + rect(diff_cont 18370 90 18590 310) + rect(diff_cont 18370 -310 18590 -90) + rect(diff_cont 15730 -310 15950 -90) + rect(diff_cont 15730 90 15950 310) + rect(diff_cont 15730 90 15950 310) + rect(diff_cont 15730 -310 15950 -90) + rect(diff_cont 23650 -310 23870 -90) + rect(diff_cont 23650 90 23870 310) + rect(diff_cont 23650 90 23870 310) + rect(diff_cont 23650 -310 23870 -90) + rect(diff_cont 21010 -310 21230 -90) + rect(diff_cont 21010 90 21230 310) + rect(diff_cont 21010 90 21230 310) + rect(diff_cont 21010 -310 21230 -90) + rect(metal1 -180 -1420 180 -300) + rect(metal1 2460 -1420 2820 -300) + rect(metal1 5100 -1420 5460 -300) + rect(metal1 7740 -1420 8100 -300) + rect(metal1 10380 -1420 10740 -300) + rect(metal1 13020 -1420 13380 -300) + rect(metal1 15660 -1420 16020 -300) + rect(metal1 18300 -1420 18660 -300) + rect(metal1 20940 -1420 21300 -300) + rect(metal1 23580 -1420 23940 -300) + rect(metal1 7740 -380 8100 380) + rect(metal1 7740 -380 8100 380) + rect(metal1 5100 -380 5460 380) + rect(metal1 5100 -380 5460 380) + rect(metal1 2460 -380 2820 380) + rect(metal1 2460 -380 2820 380) + rect(metal1 -180 -380 180 380) + rect(metal1 -180 -380 180 380) + rect(metal1 13020 -380 13380 380) + rect(metal1 13020 -380 13380 380) + rect(metal1 10380 -380 10740 380) + rect(metal1 10380 -380 10740 380) + rect(metal1 18300 -380 18660 380) + rect(metal1 18300 -380 18660 380) + rect(metal1 15660 -380 16020 380) + rect(metal1 15660 -380 16020 380) + rect(metal1 23580 -380 23940 380) + rect(metal1 23580 -380 23940 380) + rect(metal1 20940 -380 21300 380) + rect(metal1 20940 -380 21300 380) + rect(metal2_lbl -1 -1 1 1) + ) + net(5 + rect(diff_cont 3330 2890 3550 3110) + rect(diff_cont 3330 2490 3550 2710) + rect(diff_cont 3330 90 3550 310) + rect(diff_cont 3330 -310 3550 -90) + ) + net(6 + rect(diff_cont 19170 2890 19390 3110) + rect(diff_cont 19170 2490 19390 2710) + rect(diff_cont 19170 90 19390 310) + rect(diff_cont 19170 -310 19390 -90) + ) + net(7 + rect(diff_cont 13890 2890 14110 3110) + rect(diff_cont 13890 2490 14110 2710) + rect(diff_cont 13890 90 14110 310) + rect(diff_cont 13890 -310 14110 -90) + ) + net(8 + rect(diff_cont 8610 2890 8830 3110) + rect(diff_cont 8610 2490 8830 2710) + rect(diff_cont 8610 90 8830 310) + rect(diff_cont 8610 -310 8830 -90) + ) + + # Outgoing pins and their connections to nets + pin(FB 1) + pin(OSC 2) + pin(VDD 3) + pin('BULK,VSS' 4) + + # Subcircuits and their connections + circuit($1 INV2PAIR location(19420 -800) + pin(BULK 4) + pin($1 1) + pin($2 3) + pin($3 4) + pin($4 6) + pin($5 2) + pin($6 3) + ) + circuit($2 INV2PAIR location(-1700 -800) + pin(BULK 4) + pin($2 3) + pin($3 4) + pin($4 1) + pin($5 5) + pin($6 3) + ) + circuit($3 INV2PAIR location(3580 -800) + pin(BULK 4) + pin($2 3) + pin($3 4) + pin($4 5) + pin($5 8) + pin($6 3) + ) + circuit($4 INV2PAIR location(8860 -800) + pin(BULK 4) + pin($2 3) + pin($3 4) + pin($4 8) + pin($5 7) + pin($6 3) + ) + circuit($5 INV2PAIR location(14140 -800) + pin(BULK 4) + pin($2 3) + pin($3 4) + pin($4 7) + pin($5 6) + pin($6 3) + ) + +) diff --git a/testdata/algo/l2n_writer_au_2s.txt b/testdata/algo/l2n_writer_au_2s.txt new file mode 100644 index 000000000..f89b7692e --- /dev/null +++ b/testdata/algo/l2n_writer_au_2s.txt @@ -0,0 +1,535 @@ +#%l2n-klayout +W(RINGO) +U(0.001) +L(rbulk) +L(nwell) +L(poly) +L(poly_lbl) +L(diff_cont) +L(poly_cont) +L(metal1) +L(metal1_lbl) +L(via1) +L(metal2) +L(metal2_lbl) +L(ntie) +L(psd) +L(ptie) +L(nsd) +C(nwell nwell ntie) +C(poly poly poly_lbl poly_cont) +C(poly_lbl poly) +C(diff_cont diff_cont metal1 ntie psd ptie nsd) +C(poly_cont poly poly_cont metal1) +C(metal1 diff_cont poly_cont metal1 metal1_lbl via1) +C(metal1_lbl metal1) +C(via1 metal1 via1 metal2) +C(metal2 via1 metal2 metal2_lbl) +C(metal2_lbl metal2) +C(ntie nwell diff_cont ntie) +C(psd diff_cont psd) +C(ptie diff_cont ptie) +C(nsd diff_cont nsd) +G(rbulk BULK) +G(ptie BULK) +D(D$PMOS PMOS + T(S + R(psd -650 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(psd 125 -475 675 475) + ) + T(B + R(nwell -125 -475 125 475) + ) +) +D(D$PMOS$1 PMOS + T(S + R(psd -675 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(psd 125 -475 650 475) + ) + T(B + R(nwell -125 -475 125 475) + ) +) +D(D$NMOS NMOS + T(S + R(nsd -650 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(nsd 125 -475 675 475) + ) + T(B + R(rbulk -125 -475 125 475) + ) +) +D(D$NMOS$1 NMOS + T(S + R(nsd -675 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(nsd 125 -475 650 475) + ) + T(B + R(rbulk -125 -475 125 475) + ) +) +X(INV2 + N(1 + R(nwell -1400 1800 1400 4580) + R(diff_cont -110 3930 110 4150) + R(ntie -400 3700 400 4380) + ) + N(2 I(IN) + R(poly -525 -250 -275 2250) + R(poly -1700 1620 -400 1980) + R(poly -525 -800 -275 800) + R(poly -525 2000 -275 3600) + R(poly_lbl -801 1799 -799 1801) + R(poly_cont -1630 1690 -1410 1910) + ) + N(3 + R(poly 275 -250 525 2250) + R(poly 220 820 580 1180) + R(poly 275 2000 525 3600) + R(poly 275 -800 525 800) + R(diff_cont -910 2490 -690 2710) + R(diff_cont -910 2890 -690 3110) + R(diff_cont -910 -310 -690 -90) + R(diff_cont -910 90 -690 310) + R(poly_cont 290 890 510 1110) + R(metal1 -800 820 580 1180) + R(metal1 -980 -420 -620 2420) + R(metal1 -980 2420 -620 3180) + R(metal1 -980 -380 -620 380) + R(psd -1050 2325 -525 3275) + R(nsd -1050 -475 -525 475) + ) + N(4 I(OUT) + R(diff_cont 690 2890 910 3110) + R(diff_cont 690 2490 910 2710) + R(diff_cont 690 90 910 310) + R(diff_cont 690 -310 910 -90) + Q(metal1 800 20 * 380 940 * * 1620 620 * * 2420 980 * * 1980 1300 * * 20) + R(metal1 620 2420 980 3180) + R(metal1 620 -380 980 380) + R(metal1_lbl 799 1799 801 1801) + R(psd 525 2325 1050 3275) + R(nsd 525 -475 1050 475) + ) + N(5 I(VSS) + R(diff_cont -110 -310 110 -90) + R(diff_cont -110 90 110 310) + R(diff_cont -110 90 110 310) + R(diff_cont -110 -310 110 -90) + R(metal1 -180 -380 180 380) + R(metal1 -180 -380 180 380) + R(via1 -125 -325 125 -75) + R(via1 -125 75 125 325) + R(metal2 -1400 -450 1400 450) + R(metal2_lbl 1239 -91 1241 -89) + R(nsd -275 -475 275 475) + ) + N(6 I(VDD) + R(diff_cont -110 2490 110 2710) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2490 110 2710) + R(metal1 -180 2420 180 3180) + R(metal1 -180 2420 180 3180) + R(via1 -125 2475 125 2725) + R(via1 -125 2875 125 3125) + R(metal2 -1400 2350 1400 3250) + R(metal2_lbl 1249 2799 1251 2801) + R(psd -275 2325 275 3275) + ) + N(7 I(BULK) + R(diff_cont -110 -1360 110 -1140) + R(ptie -400 -1590 400 -910) + ) + P($0 1) + P(IN 2) + P($2 3) + P(OUT 4) + P(VSS 5) + P(VDD 6) + P(BULK 7) + D($1 D$PMOS + Y(-400 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.49875) + E(AD 0.26125) + E(PS 2.95) + E(PD 1.5) + T(S 3) + T(G 2) + T(D 6) + T(B 1) + ) + D($2 D$PMOS$1 + Y(400 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.26125) + E(AD 0.49875) + E(PS 1.5) + E(PD 2.95) + T(S 6) + T(G 3) + T(D 4) + T(B 1) + ) + D($3 D$NMOS + Y(-400 0) + E(L 0.25) + E(W 0.95) + E(AS 0.49875) + E(AD 0.26125) + E(PS 2.95) + E(PD 1.5) + T(S 3) + T(G 2) + T(D 5) + T(B 7) + ) + D($4 D$NMOS$1 + Y(400 0) + E(L 0.25) + E(W 0.95) + E(AS 0.26125) + E(AD 0.49875) + E(PS 1.5) + E(PD 2.95) + T(S 5) + T(G 3) + T(D 4) + T(B 7) + ) +) +X(INV2PAIR + N(1 I(BULK)) + N(2 + R(diff_cont 3430 3290 3650 3510) + R(diff_cont 3430 3690 3650 3910) + R(diff_cont 3430 490 3650 710) + R(diff_cont 3430 890 3650 1110) + ) + N(3 + R(diff_cont 4230 3290 4450 3510) + R(diff_cont 4230 3690 4450 3910) + R(diff_cont 4230 3690 4450 3910) + R(diff_cont 4230 3290 4450 3510) + R(diff_cont 1590 3290 1810 3510) + R(diff_cont 1590 3690 1810 3910) + R(diff_cont 1590 3690 1810 3910) + R(diff_cont 1590 3290 1810 3510) + R(metal1 4160 3220 4520 3980) + R(metal1 4160 3220 4520 3980) + R(metal1 1520 3220 1880 3980) + R(metal1 1520 3220 1880 3980) + ) + N(4 + R(diff_cont 4230 490 4450 710) + R(diff_cont 4230 890 4450 1110) + R(diff_cont 4230 890 4450 1110) + R(diff_cont 4230 490 4450 710) + R(diff_cont 1590 490 1810 710) + R(diff_cont 1590 890 1810 1110) + R(diff_cont 1590 890 1810 1110) + R(diff_cont 1590 490 1810 710) + R(metal1 4160 420 4520 1180) + R(metal1 4160 420 4520 1180) + R(metal1 1520 420 1880 1180) + R(metal1 1520 420 1880 1180) + ) + N(5 + R(diff_cont 2390 3690 2610 3910) + R(diff_cont 2390 3290 2610 3510) + R(diff_cont 2390 890 2610 1110) + R(diff_cont 2390 490 2610 710) + ) + N(6) + N(7 + R(diff_cont 5030 3690 5250 3910) + R(diff_cont 5030 3290 5250 3510) + R(diff_cont 5030 890 5250 1110) + R(diff_cont 5030 490 5250 710) + ) + N(8) + P(BULK 1) + P($1 2) + P($2 3) + P($3 4) + P($4 6) + P($5 7) + P($6 8) + X($1 INV2 Y(1700 800) + P($0 8) + P(IN 6) + P(OUT 5) + P(VSS 4) + P(VDD 3) + P(BULK 1) + ) + X($2 INV2 Y(4340 800) + P($0 8) + P(IN 5) + P($2 2) + P(OUT 7) + P(VSS 4) + P(VDD 3) + P(BULK 1) + ) +) +X(RINGO + N(1 I(FB) + R(diff_cont 22850 2490 23070 2710) + R(diff_cont 22850 2890 23070 3110) + R(diff_cont 22850 -310 23070 -90) + R(diff_cont 22850 90 23070 310) + R(metal1 -1700 1620 -1340 1980) + R(via1 -1645 1675 -1395 1925) + R(via1 22835 1675 23085 1925) + R(metal2 -1720 1600 23160 2000) + R(metal2_lbl -1 1799 1 1801) + ) + N(2 I(OSC) + R(diff_cont 24450 2890 24670 3110) + R(diff_cont 24450 2490 24670 2710) + R(diff_cont 24450 90 24670 310) + R(diff_cont 24450 -310 24670 -90) + R(via1 24435 1675 24685 1925) + R(metal2 24360 1600 24760 2000) + R(metal2_lbl 24559 1799 24561 1801) + ) + N(3 I(VDD) + R(diff_cont 7810 2490 8030 2710) + R(diff_cont 7810 2890 8030 3110) + R(diff_cont 7810 2890 8030 3110) + R(diff_cont 7810 2490 8030 2710) + R(diff_cont 5170 2490 5390 2710) + R(diff_cont 5170 2890 5390 3110) + R(diff_cont 5170 2890 5390 3110) + R(diff_cont 5170 2490 5390 2710) + R(diff_cont 2530 2490 2750 2710) + R(diff_cont 2530 2890 2750 3110) + R(diff_cont 2530 2890 2750 3110) + R(diff_cont 2530 2490 2750 2710) + R(diff_cont -110 2490 110 2710) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2490 110 2710) + R(diff_cont 13090 2490 13310 2710) + R(diff_cont 13090 2890 13310 3110) + R(diff_cont 13090 2890 13310 3110) + R(diff_cont 13090 2490 13310 2710) + R(diff_cont 10450 2490 10670 2710) + R(diff_cont 10450 2890 10670 3110) + R(diff_cont 10450 2890 10670 3110) + R(diff_cont 10450 2490 10670 2710) + R(diff_cont 18370 2490 18590 2710) + R(diff_cont 18370 2890 18590 3110) + R(diff_cont 18370 2890 18590 3110) + R(diff_cont 18370 2490 18590 2710) + R(diff_cont 15730 2490 15950 2710) + R(diff_cont 15730 2890 15950 3110) + R(diff_cont 15730 2890 15950 3110) + R(diff_cont 15730 2490 15950 2710) + R(diff_cont 23650 2490 23870 2710) + R(diff_cont 23650 2890 23870 3110) + R(diff_cont 23650 2890 23870 3110) + R(diff_cont 23650 2490 23870 2710) + R(diff_cont 21010 2490 21230 2710) + R(diff_cont 21010 2890 21230 3110) + R(diff_cont 21010 2890 21230 3110) + R(diff_cont 21010 2490 21230 2710) + R(metal1 -180 3100 180 4220) + R(metal1 2460 3100 2820 4220) + R(metal1 5100 3100 5460 4220) + R(metal1 7740 3100 8100 4220) + R(metal1 10380 3100 10740 4220) + R(metal1 13020 3100 13380 4220) + R(metal1 15660 3100 16020 4220) + R(metal1 18300 3100 18660 4220) + R(metal1 20940 3100 21300 4220) + R(metal1 23580 3100 23940 4220) + R(metal1 7740 2420 8100 3180) + R(metal1 7740 2420 8100 3180) + R(metal1 5100 2420 5460 3180) + R(metal1 5100 2420 5460 3180) + R(metal1 2460 2420 2820 3180) + R(metal1 2460 2420 2820 3180) + R(metal1 -180 2420 180 3180) + R(metal1 -180 2420 180 3180) + R(metal1 13020 2420 13380 3180) + R(metal1 13020 2420 13380 3180) + R(metal1 10380 2420 10740 3180) + R(metal1 10380 2420 10740 3180) + R(metal1 18300 2420 18660 3180) + R(metal1 18300 2420 18660 3180) + R(metal1 15660 2420 16020 3180) + R(metal1 15660 2420 16020 3180) + R(metal1 23580 2420 23940 3180) + R(metal1 23580 2420 23940 3180) + R(metal1 20940 2420 21300 3180) + R(metal1 20940 2420 21300 3180) + R(metal2_lbl -1 2799 1 2801) + ) + N(4 I('BULK,VSS') + R(diff_cont 7810 -310 8030 -90) + R(diff_cont 7810 90 8030 310) + R(diff_cont 7810 90 8030 310) + R(diff_cont 7810 -310 8030 -90) + R(diff_cont 5170 -310 5390 -90) + R(diff_cont 5170 90 5390 310) + R(diff_cont 5170 90 5390 310) + R(diff_cont 5170 -310 5390 -90) + R(diff_cont 2530 -310 2750 -90) + R(diff_cont 2530 90 2750 310) + R(diff_cont 2530 90 2750 310) + R(diff_cont 2530 -310 2750 -90) + R(diff_cont -110 -310 110 -90) + R(diff_cont -110 90 110 310) + R(diff_cont -110 90 110 310) + R(diff_cont -110 -310 110 -90) + R(diff_cont 13090 -310 13310 -90) + R(diff_cont 13090 90 13310 310) + R(diff_cont 13090 90 13310 310) + R(diff_cont 13090 -310 13310 -90) + R(diff_cont 10450 -310 10670 -90) + R(diff_cont 10450 90 10670 310) + R(diff_cont 10450 90 10670 310) + R(diff_cont 10450 -310 10670 -90) + R(diff_cont 18370 -310 18590 -90) + R(diff_cont 18370 90 18590 310) + R(diff_cont 18370 90 18590 310) + R(diff_cont 18370 -310 18590 -90) + R(diff_cont 15730 -310 15950 -90) + R(diff_cont 15730 90 15950 310) + R(diff_cont 15730 90 15950 310) + R(diff_cont 15730 -310 15950 -90) + R(diff_cont 23650 -310 23870 -90) + R(diff_cont 23650 90 23870 310) + R(diff_cont 23650 90 23870 310) + R(diff_cont 23650 -310 23870 -90) + R(diff_cont 21010 -310 21230 -90) + R(diff_cont 21010 90 21230 310) + R(diff_cont 21010 90 21230 310) + R(diff_cont 21010 -310 21230 -90) + R(metal1 -180 -1420 180 -300) + R(metal1 2460 -1420 2820 -300) + R(metal1 5100 -1420 5460 -300) + R(metal1 7740 -1420 8100 -300) + R(metal1 10380 -1420 10740 -300) + R(metal1 13020 -1420 13380 -300) + R(metal1 15660 -1420 16020 -300) + R(metal1 18300 -1420 18660 -300) + R(metal1 20940 -1420 21300 -300) + R(metal1 23580 -1420 23940 -300) + R(metal1 7740 -380 8100 380) + R(metal1 7740 -380 8100 380) + R(metal1 5100 -380 5460 380) + R(metal1 5100 -380 5460 380) + R(metal1 2460 -380 2820 380) + R(metal1 2460 -380 2820 380) + R(metal1 -180 -380 180 380) + R(metal1 -180 -380 180 380) + R(metal1 13020 -380 13380 380) + R(metal1 13020 -380 13380 380) + R(metal1 10380 -380 10740 380) + R(metal1 10380 -380 10740 380) + R(metal1 18300 -380 18660 380) + R(metal1 18300 -380 18660 380) + R(metal1 15660 -380 16020 380) + R(metal1 15660 -380 16020 380) + R(metal1 23580 -380 23940 380) + R(metal1 23580 -380 23940 380) + R(metal1 20940 -380 21300 380) + R(metal1 20940 -380 21300 380) + R(metal2_lbl -1 -1 1 1) + ) + N(5 + R(diff_cont 3330 2890 3550 3110) + R(diff_cont 3330 2490 3550 2710) + R(diff_cont 3330 90 3550 310) + R(diff_cont 3330 -310 3550 -90) + ) + N(6 + R(diff_cont 19170 2890 19390 3110) + R(diff_cont 19170 2490 19390 2710) + R(diff_cont 19170 90 19390 310) + R(diff_cont 19170 -310 19390 -90) + ) + N(7 + R(diff_cont 13890 2890 14110 3110) + R(diff_cont 13890 2490 14110 2710) + R(diff_cont 13890 90 14110 310) + R(diff_cont 13890 -310 14110 -90) + ) + N(8 + R(diff_cont 8610 2890 8830 3110) + R(diff_cont 8610 2490 8830 2710) + R(diff_cont 8610 90 8830 310) + R(diff_cont 8610 -310 8830 -90) + ) + P(FB 1) + P(OSC 2) + P(VDD 3) + P('BULK,VSS' 4) + X($1 INV2PAIR Y(19420 -800) + P(BULK 4) + P($1 1) + P($2 3) + P($3 4) + P($4 6) + P($5 2) + P($6 3) + ) + X($2 INV2PAIR Y(-1700 -800) + P(BULK 4) + P($2 3) + P($3 4) + P($4 1) + P($5 5) + P($6 3) + ) + X($3 INV2PAIR Y(3580 -800) + P(BULK 4) + P($2 3) + P($3 4) + P($4 5) + P($5 8) + P($6 3) + ) + X($4 INV2PAIR Y(8860 -800) + P(BULK 4) + P($2 3) + P($3 4) + P($4 8) + P($5 7) + P($6 3) + ) + X($5 INV2PAIR Y(14140 -800) + P(BULK 4) + P($2 3) + P($3 4) + P($4 7) + P($5 6) + P($6 3) + ) +) diff --git a/testdata/algo/l2n_writer_au_s.txt b/testdata/algo/l2n_writer_au_s.txt new file mode 100644 index 000000000..e60473552 --- /dev/null +++ b/testdata/algo/l2n_writer_au_s.txt @@ -0,0 +1,452 @@ +#%l2n-klayout +W(RINGO) +U(0.001) +L(poly) +L(poly_lbl) +L(diff_cont) +L(poly_cont) +L(metal1) +L(metal1_lbl) +L(via1) +L(metal2) +L(metal2_lbl) +L(psd) +L(nsd) +C(poly poly poly_lbl poly_cont) +C(poly_lbl poly) +C(diff_cont diff_cont metal1 psd nsd) +C(poly_cont poly poly_cont metal1) +C(metal1 diff_cont poly_cont metal1 metal1_lbl via1) +C(metal1_lbl metal1) +C(via1 metal1 via1 metal2) +C(metal2 via1 metal2 metal2_lbl) +C(metal2_lbl metal2) +C(psd diff_cont psd) +C(nsd diff_cont nsd) +D(D$PMOS PMOS + T(S + R(psd -650 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(psd 125 -475 675 475) + ) +) +D(D$PMOS$1 PMOS + T(S + R(psd -675 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(psd 125 -475 650 475) + ) +) +D(D$NMOS NMOS + T(S + R(nsd -650 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(nsd 125 -475 675 475) + ) +) +D(D$NMOS$1 NMOS + T(S + R(nsd -675 -475 -125 475) + ) + T(G + R(poly -125 -475 125 475) + ) + T(D + R(nsd 125 -475 650 475) + ) +) +X(INV2 + N(1 I(IN) + R(poly -525 -250 -275 2250) + R(poly -1700 1620 -400 1980) + R(poly -525 -800 -275 800) + R(poly -525 2000 -275 3600) + R(poly_lbl -801 1799 -799 1801) + R(poly_cont -1630 1690 -1410 1910) + ) + N(2 + R(poly 275 -250 525 2250) + R(poly 220 820 580 1180) + R(poly 275 2000 525 3600) + R(poly 275 -800 525 800) + R(diff_cont -910 2490 -690 2710) + R(diff_cont -910 2890 -690 3110) + R(diff_cont -910 -310 -690 -90) + R(diff_cont -910 90 -690 310) + R(poly_cont 290 890 510 1110) + R(metal1 -800 820 580 1180) + R(metal1 -980 -420 -620 2420) + R(metal1 -980 2420 -620 3180) + R(metal1 -980 -380 -620 380) + R(psd -1050 2325 -525 3275) + R(nsd -1050 -475 -525 475) + ) + N(3 I(OUT) + R(diff_cont 690 2890 910 3110) + R(diff_cont 690 2490 910 2710) + R(diff_cont 690 90 910 310) + R(diff_cont 690 -310 910 -90) + Q(metal1 800 20 * 380 940 * * 1620 620 * * 2420 980 * * 1980 1300 * * 20) + R(metal1 620 2420 980 3180) + R(metal1 620 -380 980 380) + R(metal1_lbl 799 1799 801 1801) + R(psd 525 2325 1050 3275) + R(nsd 525 -475 1050 475) + ) + N(4 + R(diff_cont -110 -310 110 -90) + R(diff_cont -110 90 110 310) + R(diff_cont -110 90 110 310) + R(diff_cont -110 -310 110 -90) + R(metal1 -180 -380 180 380) + R(metal1 -180 -380 180 380) + R(via1 -125 -325 125 -75) + R(via1 -125 75 125 325) + R(metal2 -1400 -450 1400 450) + R(nsd -275 -475 275 475) + ) + N(5 + R(diff_cont -110 2490 110 2710) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2490 110 2710) + R(metal1 -180 2420 180 3180) + R(metal1 -180 2420 180 3180) + R(via1 -125 2475 125 2725) + R(via1 -125 2875 125 3125) + R(metal2 -1400 2350 1400 3250) + R(psd -275 2325 275 3275) + ) + P(IN 1) + P($1 2) + P(OUT 3) + P($3 4) + P($4 5) + D($1 D$PMOS + Y(-400 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.49875) + E(AD 0.26125) + E(PS 2.95) + E(PD 1.5) + T(S 2) + T(G 1) + T(D 5) + ) + D($2 D$PMOS$1 + Y(400 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.26125) + E(AD 0.49875) + E(PS 1.5) + E(PD 2.95) + T(S 5) + T(G 2) + T(D 3) + ) + D($3 D$NMOS + Y(-400 0) + E(L 0.25) + E(W 0.95) + E(AS 0.49875) + E(AD 0.26125) + E(PS 2.95) + E(PD 1.5) + T(S 2) + T(G 1) + T(D 4) + ) + D($4 D$NMOS$1 + Y(400 0) + E(L 0.25) + E(W 0.95) + E(AS 0.26125) + E(AD 0.49875) + E(PS 1.5) + E(PD 2.95) + T(S 4) + T(G 2) + T(D 3) + ) +) +X(RINGO + N(1 I(FB) + R(diff_cont 22850 2490 23070 2710) + R(diff_cont 22850 2890 23070 3110) + R(diff_cont 22850 -310 23070 -90) + R(diff_cont 22850 90 23070 310) + R(metal1 -1700 1620 -1340 1980) + R(via1 -1645 1675 -1395 1925) + R(via1 22835 1675 23085 1925) + R(metal2 -1720 1600 23160 2000) + R(metal2_lbl -1 1799 1 1801) + ) + N(2 I(OSC) + R(diff_cont 24450 2890 24670 3110) + R(diff_cont 24450 2490 24670 2710) + R(diff_cont 24450 90 24670 310) + R(diff_cont 24450 -310 24670 -90) + R(via1 24435 1675 24685 1925) + R(metal2 24360 1600 24760 2000) + R(metal2_lbl 24559 1799 24561 1801) + ) + N(3 I(VSS) + R(diff_cont 2530 -310 2750 -90) + R(diff_cont 2530 90 2750 310) + R(diff_cont 2530 90 2750 310) + R(diff_cont 2530 -310 2750 -90) + R(diff_cont -110 -310 110 -90) + R(diff_cont -110 90 110 310) + R(diff_cont -110 90 110 310) + R(diff_cont -110 -310 110 -90) + R(diff_cont 5170 -310 5390 -90) + R(diff_cont 5170 90 5390 310) + R(diff_cont 5170 90 5390 310) + R(diff_cont 5170 -310 5390 -90) + R(diff_cont 7810 -310 8030 -90) + R(diff_cont 7810 90 8030 310) + R(diff_cont 7810 90 8030 310) + R(diff_cont 7810 -310 8030 -90) + R(diff_cont 10450 -310 10670 -90) + R(diff_cont 10450 90 10670 310) + R(diff_cont 10450 90 10670 310) + R(diff_cont 10450 -310 10670 -90) + R(diff_cont 13090 -310 13310 -90) + R(diff_cont 13090 90 13310 310) + R(diff_cont 13090 90 13310 310) + R(diff_cont 13090 -310 13310 -90) + R(diff_cont 15730 -310 15950 -90) + R(diff_cont 15730 90 15950 310) + R(diff_cont 15730 90 15950 310) + R(diff_cont 15730 -310 15950 -90) + R(diff_cont 18370 -310 18590 -90) + R(diff_cont 18370 90 18590 310) + R(diff_cont 18370 90 18590 310) + R(diff_cont 18370 -310 18590 -90) + R(diff_cont 21010 -310 21230 -90) + R(diff_cont 21010 90 21230 310) + R(diff_cont 21010 90 21230 310) + R(diff_cont 21010 -310 21230 -90) + R(diff_cont 23650 -310 23870 -90) + R(diff_cont 23650 90 23870 310) + R(diff_cont 23650 90 23870 310) + R(diff_cont 23650 -310 23870 -90) + R(metal1 2460 -380 2820 380) + R(metal1 2460 -380 2820 380) + R(metal1 -180 -380 180 380) + R(metal1 -180 -380 180 380) + R(metal1 5100 -380 5460 380) + R(metal1 5100 -380 5460 380) + R(metal1 7740 -380 8100 380) + R(metal1 7740 -380 8100 380) + R(metal1 10380 -380 10740 380) + R(metal1 10380 -380 10740 380) + R(metal1 13020 -380 13380 380) + R(metal1 13020 -380 13380 380) + R(metal1 15660 -380 16020 380) + R(metal1 15660 -380 16020 380) + R(metal1 18300 -380 18660 380) + R(metal1 18300 -380 18660 380) + R(metal1 20940 -380 21300 380) + R(metal1 20940 -380 21300 380) + R(metal1 23580 -380 23940 380) + R(metal1 23580 -380 23940 380) + R(metal2_lbl -1 -1 1 1) + ) + N(4 I(VDD) + R(diff_cont 2530 2490 2750 2710) + R(diff_cont 2530 2890 2750 3110) + R(diff_cont 2530 2890 2750 3110) + R(diff_cont 2530 2490 2750 2710) + R(diff_cont -110 2490 110 2710) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2890 110 3110) + R(diff_cont -110 2490 110 2710) + R(diff_cont 5170 2490 5390 2710) + R(diff_cont 5170 2890 5390 3110) + R(diff_cont 5170 2890 5390 3110) + R(diff_cont 5170 2490 5390 2710) + R(diff_cont 7810 2490 8030 2710) + R(diff_cont 7810 2890 8030 3110) + R(diff_cont 7810 2890 8030 3110) + R(diff_cont 7810 2490 8030 2710) + R(diff_cont 10450 2490 10670 2710) + R(diff_cont 10450 2890 10670 3110) + R(diff_cont 10450 2890 10670 3110) + R(diff_cont 10450 2490 10670 2710) + R(diff_cont 13090 2490 13310 2710) + R(diff_cont 13090 2890 13310 3110) + R(diff_cont 13090 2890 13310 3110) + R(diff_cont 13090 2490 13310 2710) + R(diff_cont 15730 2490 15950 2710) + R(diff_cont 15730 2890 15950 3110) + R(diff_cont 15730 2890 15950 3110) + R(diff_cont 15730 2490 15950 2710) + R(diff_cont 18370 2490 18590 2710) + R(diff_cont 18370 2890 18590 3110) + R(diff_cont 18370 2890 18590 3110) + R(diff_cont 18370 2490 18590 2710) + R(diff_cont 21010 2490 21230 2710) + R(diff_cont 21010 2890 21230 3110) + R(diff_cont 21010 2890 21230 3110) + R(diff_cont 21010 2490 21230 2710) + R(diff_cont 23650 2490 23870 2710) + R(diff_cont 23650 2890 23870 3110) + R(diff_cont 23650 2890 23870 3110) + R(diff_cont 23650 2490 23870 2710) + R(metal1 2460 2420 2820 3180) + R(metal1 2460 2420 2820 3180) + R(metal1 -180 2420 180 3180) + R(metal1 -180 2420 180 3180) + R(metal1 5100 2420 5460 3180) + R(metal1 5100 2420 5460 3180) + R(metal1 7740 2420 8100 3180) + R(metal1 7740 2420 8100 3180) + R(metal1 10380 2420 10740 3180) + R(metal1 10380 2420 10740 3180) + R(metal1 13020 2420 13380 3180) + R(metal1 13020 2420 13380 3180) + R(metal1 15660 2420 16020 3180) + R(metal1 15660 2420 16020 3180) + R(metal1 18300 2420 18660 3180) + R(metal1 18300 2420 18660 3180) + R(metal1 20940 2420 21300 3180) + R(metal1 20940 2420 21300 3180) + R(metal1 23580 2420 23940 3180) + R(metal1 23580 2420 23940 3180) + R(metal2_lbl -1 2799 1 2801) + ) + N(5 + R(diff_cont 690 2890 910 3110) + R(diff_cont 690 2490 910 2710) + R(diff_cont 690 90 910 310) + R(diff_cont 690 -310 910 -90) + ) + N(6 + R(diff_cont 21810 2890 22030 3110) + R(diff_cont 21810 2490 22030 2710) + R(diff_cont 21810 90 22030 310) + R(diff_cont 21810 -310 22030 -90) + ) + N(7 + R(diff_cont 19170 2890 19390 3110) + R(diff_cont 19170 2490 19390 2710) + R(diff_cont 19170 90 19390 310) + R(diff_cont 19170 -310 19390 -90) + ) + N(8 + R(diff_cont 16530 2890 16750 3110) + R(diff_cont 16530 2490 16750 2710) + R(diff_cont 16530 90 16750 310) + R(diff_cont 16530 -310 16750 -90) + ) + N(9 + R(diff_cont 13890 2890 14110 3110) + R(diff_cont 13890 2490 14110 2710) + R(diff_cont 13890 90 14110 310) + R(diff_cont 13890 -310 14110 -90) + ) + N(10 + R(diff_cont 11250 2890 11470 3110) + R(diff_cont 11250 2490 11470 2710) + R(diff_cont 11250 90 11470 310) + R(diff_cont 11250 -310 11470 -90) + ) + N(11 + R(diff_cont 8610 2890 8830 3110) + R(diff_cont 8610 2490 8830 2710) + R(diff_cont 8610 90 8830 310) + R(diff_cont 8610 -310 8830 -90) + ) + N(12 + R(diff_cont 5970 2890 6190 3110) + R(diff_cont 5970 2490 6190 2710) + R(diff_cont 5970 90 6190 310) + R(diff_cont 5970 -310 6190 -90) + ) + N(13 + R(diff_cont 3330 2890 3550 3110) + R(diff_cont 3330 2490 3550 2710) + R(diff_cont 3330 90 3550 310) + R(diff_cont 3330 -310 3550 -90) + ) + P(FB 1) + P(OSC 2) + P(VSS 3) + P(VDD 4) + X($1 INV2 Y(23760 0) + P(IN 6) + P($1 1) + P(OUT 2) + P($3 3) + P($4 4) + ) + X($2 INV2 Y(0 0) + P(IN 1) + P(OUT 5) + P($3 3) + P($4 4) + ) + X($3 INV2 Y(2640 0) + P(IN 5) + P(OUT 13) + P($3 3) + P($4 4) + ) + X($4 INV2 Y(5280 0) + P(IN 13) + P(OUT 12) + P($3 3) + P($4 4) + ) + X($5 INV2 Y(7920 0) + P(IN 12) + P(OUT 11) + P($3 3) + P($4 4) + ) + X($6 INV2 Y(10560 0) + P(IN 11) + P(OUT 10) + P($3 3) + P($4 4) + ) + X($7 INV2 Y(13200 0) + P(IN 10) + P(OUT 9) + P($3 3) + P($4 4) + ) + X($8 INV2 Y(15840 0) + P(IN 9) + P(OUT 8) + P($3 3) + P($4 4) + ) + X($9 INV2 Y(18480 0) + P(IN 8) + P(OUT 7) + P($3 3) + P($4 4) + ) + X($10 INV2 Y(21120 0) + P(IN 7) + P(OUT 6) + P($3 3) + P($4 4) + ) +) diff --git a/testdata/algo/nwriter10_au.txt b/testdata/algo/nwriter10_au.txt new file mode 100644 index 000000000..4b1c13f9d --- /dev/null +++ b/testdata/algo/nwriter10_au.txt @@ -0,0 +1,413 @@ +* written by unit test + +* cell C1withaverylongextensionthatgoesbeyondmultiplelinesunlessipasteeverythingtogetherwhichmakesithardtoreadbutexactlythatisthereasonwhyiwriteitthisway +* pin p0 +* pin p1 +* pin p2 +* pin p3 +* pin p4 +* pin p5 +* pin p6 +* pin p7 +* pin p8 +* pin p9 +* pin p10 +* pin p11 +* pin p12 +* pin p13 +* pin p14 +* pin p15 +* pin p16 +* pin p17 +* pin p18 +* pin p19 +* pin p20 +* pin p21 +* pin p22 +* pin p23 +* pin p24 +* pin p25 +* pin p26 +* pin p27 +* pin p28 +* pin p29 +* pin p30 +* pin p31 +* pin p32 +* pin p33 +* pin p34 +* pin p35 +* pin p36 +* pin p37 +* pin p38 +* pin p39 +* pin p40 +* pin p41 +* pin p42 +* pin p43 +* pin p44 +* pin p45 +* pin p46 +* pin p47 +* pin p48 +* pin p49 +* pin p50 +* pin p51 +* pin p52 +* pin p53 +* pin p54 +* pin p55 +* pin p56 +* pin p57 +* pin p58 +* pin p59 +* pin p60 +* pin p61 +* pin p62 +* pin p63 +* pin p64 +* pin p65 +* pin p66 +* pin p67 +* pin p68 +* pin p69 +* pin p70 +* pin p71 +* pin p72 +* pin p73 +* pin p74 +* pin p75 +* pin p76 +* pin p77 +* pin p78 +* pin p79 +* pin p80 +* pin p81 +* pin p82 +* pin p83 +* pin p84 +* pin p85 +* pin p86 +* pin p87 +* pin p88 +* pin p89 +* pin p90 +* pin p91 +* pin p92 +* pin p93 +* pin p94 +* pin p95 +* pin p96 +* pin p97 +* pin p98 +* pin p99 +* pin p100 +.SUBCKT ++ C1withaverylongextensionthatgoesbeyondmultiplelinesunlessipasteeverythingtogetherwhichmakesithardtoreadbutexactlythatisthereasonwhyiwriteitthisway ++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ++ 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 ++ 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 ++ 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 +* net 1 n0 +* net 2 n1 +* net 3 n2 +* net 4 n3 +* net 5 n4 +* net 6 n5 +* net 7 n6 +* net 8 n7 +* net 9 n8 +* net 10 n9 +* net 11 n10 +* net 12 n11 +* net 13 n12 +* net 14 n13 +* net 15 n14 +* net 16 n15 +* net 17 n16 +* net 18 n17 +* net 19 n18 +* net 20 n19 +* net 21 n20 +* net 22 n21 +* net 23 n22 +* net 24 n23 +* net 25 n24 +* net 26 n25 +* net 27 n26 +* net 28 n27 +* net 29 n28 +* net 30 n29 +* net 31 n30 +* net 32 n31 +* net 33 n32 +* net 34 n33 +* net 35 n34 +* net 36 n35 +* net 37 n36 +* net 38 n37 +* net 39 n38 +* net 40 n39 +* net 41 n40 +* net 42 n41 +* net 43 n42 +* net 44 n43 +* net 45 n44 +* net 46 n45 +* net 47 n46 +* net 48 n47 +* net 49 n48 +* net 50 n49 +* net 51 n50 +* net 52 n51 +* net 53 n52 +* net 54 n53 +* net 55 n54 +* net 56 n55 +* net 57 n56 +* net 58 n57 +* net 59 n58 +* net 60 n59 +* net 61 n60 +* net 62 n61 +* net 63 n62 +* net 64 n63 +* net 65 n64 +* net 66 n65 +* net 67 n66 +* net 68 n67 +* net 69 n68 +* net 70 n69 +* net 71 n70 +* net 72 n71 +* net 73 n72 +* net 74 n73 +* net 75 n74 +* net 76 n75 +* net 77 n76 +* net 78 n77 +* net 79 n78 +* net 80 n79 +* net 81 n80 +* net 82 n81 +* net 83 n82 +* net 84 n83 +* net 85 n84 +* net 86 n85 +* net 87 n86 +* net 88 n87 +* net 89 n88 +* net 90 n89 +* net 91 n90 +* net 92 n91 +* net 93 n92 +* net 94 n93 +* net 95 n94 +* net 96 n95 +* net 97 n96 +* net 98 n97 +* net 99 n98 +* net 100 n99 +* net 101 n100 +* device instance $1 0,0 RCLS +R$1 1 2 0 +* device instance $2 0,0 RCLS +R$2 1 3 0 +* device instance $3 0,0 RCLS +R$3 1 4 0 +* device instance $4 0,0 RCLS +R$4 1 5 0 +* device instance $5 0,0 RCLS +R$5 1 6 0 +* device instance $6 0,0 RCLS +R$6 1 7 0 +* device instance $7 0,0 RCLS +R$7 1 8 0 +* device instance $8 0,0 RCLS +R$8 1 9 0 +* device instance $9 0,0 RCLS +R$9 1 10 0 +* device instance $10 0,0 RCLS +R$10 1 11 0 +* device instance $11 0,0 RCLS +R$11 1 12 0 +* device instance $12 0,0 RCLS +R$12 1 13 0 +* device instance $13 0,0 RCLS +R$13 1 14 0 +* device instance $14 0,0 RCLS +R$14 1 15 0 +* device instance $15 0,0 RCLS +R$15 1 16 0 +* device instance $16 0,0 RCLS +R$16 1 17 0 +* device instance $17 0,0 RCLS +R$17 1 18 0 +* device instance $18 0,0 RCLS +R$18 1 19 0 +* device instance $19 0,0 RCLS +R$19 1 20 0 +* device instance $20 0,0 RCLS +R$20 1 21 0 +* device instance $21 0,0 RCLS +R$21 1 22 0 +* device instance $22 0,0 RCLS +R$22 1 23 0 +* device instance $23 0,0 RCLS +R$23 1 24 0 +* device instance $24 0,0 RCLS +R$24 1 25 0 +* device instance $25 0,0 RCLS +R$25 1 26 0 +* device instance $26 0,0 RCLS +R$26 1 27 0 +* device instance $27 0,0 RCLS +R$27 1 28 0 +* device instance $28 0,0 RCLS +R$28 1 29 0 +* device instance $29 0,0 RCLS +R$29 1 30 0 +* device instance $30 0,0 RCLS +R$30 1 31 0 +* device instance $31 0,0 RCLS +R$31 1 32 0 +* device instance $32 0,0 RCLS +R$32 1 33 0 +* device instance $33 0,0 RCLS +R$33 1 34 0 +* device instance $34 0,0 RCLS +R$34 1 35 0 +* device instance $35 0,0 RCLS +R$35 1 36 0 +* device instance $36 0,0 RCLS +R$36 1 37 0 +* device instance $37 0,0 RCLS +R$37 1 38 0 +* device instance $38 0,0 RCLS +R$38 1 39 0 +* device instance $39 0,0 RCLS +R$39 1 40 0 +* device instance $40 0,0 RCLS +R$40 1 41 0 +* device instance $41 0,0 RCLS +R$41 1 42 0 +* device instance $42 0,0 RCLS +R$42 1 43 0 +* device instance $43 0,0 RCLS +R$43 1 44 0 +* device instance $44 0,0 RCLS +R$44 1 45 0 +* device instance $45 0,0 RCLS +R$45 1 46 0 +* device instance $46 0,0 RCLS +R$46 1 47 0 +* device instance $47 0,0 RCLS +R$47 1 48 0 +* device instance $48 0,0 RCLS +R$48 1 49 0 +* device instance $49 0,0 RCLS +R$49 1 50 0 +* device instance $50 0,0 RCLS +R$50 1 51 0 +* device instance $51 0,0 RCLS +R$51 1 52 0 +* device instance $52 0,0 RCLS +R$52 1 53 0 +* device instance $53 0,0 RCLS +R$53 1 54 0 +* device instance $54 0,0 RCLS +R$54 1 55 0 +* device instance $55 0,0 RCLS +R$55 1 56 0 +* device instance $56 0,0 RCLS +R$56 1 57 0 +* device instance $57 0,0 RCLS +R$57 1 58 0 +* device instance $58 0,0 RCLS +R$58 1 59 0 +* device instance $59 0,0 RCLS +R$59 1 60 0 +* device instance $60 0,0 RCLS +R$60 1 61 0 +* device instance $61 0,0 RCLS +R$61 1 62 0 +* device instance $62 0,0 RCLS +R$62 1 63 0 +* device instance $63 0,0 RCLS +R$63 1 64 0 +* device instance $64 0,0 RCLS +R$64 1 65 0 +* device instance $65 0,0 RCLS +R$65 1 66 0 +* device instance $66 0,0 RCLS +R$66 1 67 0 +* device instance $67 0,0 RCLS +R$67 1 68 0 +* device instance $68 0,0 RCLS +R$68 1 69 0 +* device instance $69 0,0 RCLS +R$69 1 70 0 +* device instance $70 0,0 RCLS +R$70 1 71 0 +* device instance $71 0,0 RCLS +R$71 1 72 0 +* device instance $72 0,0 RCLS +R$72 1 73 0 +* device instance $73 0,0 RCLS +R$73 1 74 0 +* device instance $74 0,0 RCLS +R$74 1 75 0 +* device instance $75 0,0 RCLS +R$75 1 76 0 +* device instance $76 0,0 RCLS +R$76 1 77 0 +* device instance $77 0,0 RCLS +R$77 1 78 0 +* device instance $78 0,0 RCLS +R$78 1 79 0 +* device instance $79 0,0 RCLS +R$79 1 80 0 +* device instance $80 0,0 RCLS +R$80 1 81 0 +* device instance $81 0,0 RCLS +R$81 1 82 0 +* device instance $82 0,0 RCLS +R$82 1 83 0 +* device instance $83 0,0 RCLS +R$83 1 84 0 +* device instance $84 0,0 RCLS +R$84 1 85 0 +* device instance $85 0,0 RCLS +R$85 1 86 0 +* device instance $86 0,0 RCLS +R$86 1 87 0 +* device instance $87 0,0 RCLS +R$87 1 88 0 +* device instance $88 0,0 RCLS +R$88 1 89 0 +* device instance $89 0,0 RCLS +R$89 1 90 0 +* device instance $90 0,0 RCLS +R$90 1 91 0 +* device instance $91 0,0 RCLS +R$91 1 92 0 +* device instance $92 0,0 RCLS +R$92 1 93 0 +* device instance $93 0,0 RCLS +R$93 1 94 0 +* device instance $94 0,0 RCLS +R$94 1 95 0 +* device instance $95 0,0 RCLS +R$95 1 96 0 +* device instance $96 0,0 RCLS +R$96 1 97 0 +* device instance $97 0,0 RCLS +R$97 1 98 0 +* device instance $98 0,0 RCLS +R$98 1 99 0 +* device instance $99 0,0 RCLS +R$99 1 100 0 +* device instance $100 0,0 RCLS +R$100 1 101 0 +.ENDS ++ C1withaverylongextensionthatgoesbeyondmultiplelinesunlessipasteeverythingtogetherwhichmakesithardtoreadbutexactlythatisthereasonwhyiwriteitthisway diff --git a/testdata/algo/nwriter1_au.txt b/testdata/algo/nwriter1_au.txt new file mode 100644 index 000000000..54205fa66 --- /dev/null +++ b/testdata/algo/nwriter1_au.txt @@ -0,0 +1,14 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 RCLS +R$1 1 3 1.7 +* device instance $2 0,0 RCLS +R$2 3 2 4.2e-05 +.ENDS C1 diff --git a/testdata/algo/nwriter20_au.txt b/testdata/algo/nwriter20_au.txt new file mode 100644 index 000000000..f120641fa --- /dev/null +++ b/testdata/algo/nwriter20_au.txt @@ -0,0 +1,25 @@ +* written by unit test +*** My special header +*** My intro for class RCLS +*** My intro for class LCLS +*** My intro for class CCLS +*** My intro for class DCLS +*** My intro for class M3CLS +*** My intro for class M4CLS + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 RCLS +*** Before device $1 +R$1 1 3 1.7 +*** After device $1 +* device instance $2 0,0 RCLS +*** Before device $2 +R$2 3 2 4.2e-05 +*** After device $2 +.ENDS C1 diff --git a/testdata/algo/nwriter2_au.txt b/testdata/algo/nwriter2_au.txt new file mode 100644 index 000000000..661626646 --- /dev/null +++ b/testdata/algo/nwriter2_au.txt @@ -0,0 +1,14 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 CCLS +C$1 1 3 1.7e-12 +* device instance $2 0,0 CCLS +C$2 3 2 4.2e-14 +.ENDS C1 diff --git a/testdata/algo/nwriter3_au.txt b/testdata/algo/nwriter3_au.txt new file mode 100644 index 000000000..acaf6fb6a --- /dev/null +++ b/testdata/algo/nwriter3_au.txt @@ -0,0 +1,14 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 LCLS +L$1 1 3 1.7e-10 +* device instance $2 0,0 LCLS +L$2 3 2 4.2e-08 +.ENDS C1 diff --git a/testdata/algo/nwriter4_au.txt b/testdata/algo/nwriter4_au.txt new file mode 100644 index 000000000..8622bc7f1 --- /dev/null +++ b/testdata/algo/nwriter4_au.txt @@ -0,0 +1,14 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 DCLS +D$1 1 3 DDCLS +* device instance $2 0,0 DCLS +D$2 3 2 DDCLS +.ENDS C1 diff --git a/testdata/algo/nwriter5_au.txt b/testdata/algo/nwriter5_au.txt new file mode 100644 index 000000000..123eb9eac --- /dev/null +++ b/testdata/algo/nwriter5_au.txt @@ -0,0 +1,16 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +* pin p3 +.SUBCKT C1 1 2 4 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* net 4 n4 +* device instance $1 0,0 M3CLS +M$1 1 4 3 1 MM3CLS L=0.25U W=0.18U AS=1.2P AD=0.75P PS=2.2U PD=1.75U +* device instance $2 0,0 M3CLS +M$2 3 4 2 3 MM3CLS L=1.4U W=0.25U AS=1.3P AD=0.85P PS=2.3U PD=1.85U +.ENDS C1 diff --git a/testdata/algo/nwriter6_au.txt b/testdata/algo/nwriter6_au.txt new file mode 100644 index 000000000..33ef8c225 --- /dev/null +++ b/testdata/algo/nwriter6_au.txt @@ -0,0 +1,18 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +* pin p3 +* pin p4 +.SUBCKT C1 1 2 4 5 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* net 4 n4 +* net 5 n5 +* device instance $1 0,0 M4CLS +M$1 1 4 3 5 MM4CLS L=0.25U W=0.18U AS=1.2P AD=0.75P PS=2.2U PD=1.75U +* device instance $2 0,0 M4CLS +M$2 3 4 2 5 MM4CLS L=1.4U W=0.25U AS=1.3P AD=0.85P PS=2.3U PD=1.85U +.ENDS C1 diff --git a/testdata/algo/nwriter7_au.txt b/testdata/algo/nwriter7_au.txt new file mode 100644 index 000000000..8b4656dca --- /dev/null +++ b/testdata/algo/nwriter7_au.txt @@ -0,0 +1,14 @@ +* written by unit test + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 XCLS +XD_$1 1 3 XCLS PARAMS: U=-17 V=42 +* device instance $2 0,0 XCLS +XD_$2 3 2 XCLS PARAMS: U=17 V=-42 +.ENDS C1 diff --git a/testdata/algo/nwriter8_au.txt b/testdata/algo/nwriter8_au.txt new file mode 100644 index 000000000..7b3e360df --- /dev/null +++ b/testdata/algo/nwriter8_au.txt @@ -0,0 +1,34 @@ +* written by unit test + +* cell C2 +* pin p1 +* pin p2 +* pin p3 +.SUBCKT C2 1 2 4 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* net 4 n4 +* net 5 n5 +* cell instance SC1 r0 *1 0,0 +XSC1 1 3 4 3 C1 +* cell instance SC2 r0 *1 0,0 +XSC2 3 2 4 3 C1 +.ENDS C2 + +* cell C1 +* pin p1 +* pin p2 +* pin p3 +* pin p4 +.SUBCKT C1 1 2 4 5 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* net 4 n4 +* net 5 n5 +* device instance $1 0,0 M4CLS +M$1 1 4 3 5 MM4CLS L=0.25U W=0.18U AS=1.2P AD=0.75P PS=2.2U PD=1.75U +* device instance $2 0,0 M4CLS +M$2 3 4 2 5 MM4CLS L=1.4U W=0.25U AS=1.3P AD=0.85P PS=2.3U PD=1.85U +.ENDS C1 diff --git a/testdata/algo/nwriter_rba1_au.txt b/testdata/algo/nwriter_rba1_au.txt new file mode 100644 index 000000000..17383a7b0 --- /dev/null +++ b/testdata/algo/nwriter_rba1_au.txt @@ -0,0 +1,13 @@ + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 RCLS +R$1 1 3 1.7 +* device instance $2 0,0 RCLS +R$2 3 2 4.2e-05 +.ENDS C1 diff --git a/testdata/algo/nwriter_rba2_au.txt b/testdata/algo/nwriter_rba2_au.txt new file mode 100644 index 000000000..492d0d858 --- /dev/null +++ b/testdata/algo/nwriter_rba2_au.txt @@ -0,0 +1,29 @@ +* A comment +*** My special header +* My intro for class RCLS +* My intro for class LCLS +* My intro for class CCLS +* My intro for class DCLS +* My intro for class M3CLS +* My intro for class M4CLS + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 1 2 +* net 1 n1 +* net 2 n2 +* net 3 n3 +* device instance $1 0,0 RCLS +* Before device $1 +* Terminal #1: 1 +* Terminal #2: 3 +R$1 1 3 1.7 +* After device $1 +* device instance $2 0,0 RCLS +* Before device $2 +* Terminal #1: 3 +* Terminal #2: 2 +R$2 3 2 4.2e-05 +* After device $2 +.ENDS C1 diff --git a/testdata/algo/vexriscv_clocked_r.oas.gz b/testdata/algo/vexriscv_clocked_r.oas.gz new file mode 100644 index 000000000..0ecd4f4f2 Binary files /dev/null and b/testdata/algo/vexriscv_clocked_r.oas.gz differ diff --git a/testdata/drc/antenna_l1.gds b/testdata/drc/antenna_l1.gds new file mode 100644 index 000000000..29c02ea2e Binary files /dev/null and b/testdata/drc/antenna_l1.gds differ diff --git a/testdata/drc/drcBasicTests_au.gds b/testdata/drc/drcBasicTests_au.gds new file mode 100644 index 000000000..594ba23d0 Binary files /dev/null and b/testdata/drc/drcBasicTests_au.gds differ diff --git a/testdata/drc/drcBasicTests_au.oas b/testdata/drc/drcBasicTests_au.oas deleted file mode 100644 index c4c29ffe2..000000000 Binary files a/testdata/drc/drcBasicTests_au.oas and /dev/null differ diff --git a/testdata/drc/drcSimpleTests_1.drc b/testdata/drc/drcSimpleTests_1.drc index 75b575701..28f5bd3a5 100644 --- a/testdata/drc/drcSimpleTests_1.drc +++ b/testdata/drc/drcSimpleTests_1.drc @@ -16,6 +16,6 @@ x.is_box? == false || raise("unexpected value") x.output(10, 0) y = polygon_layer -y.insert(polygon([ p(0, 0), p(0, 1.0), p(2.0, 1.0), p(2.0, 2.0), p(1.0, 2.0), p(1.0, 0) ])) +y.insert(polygon([ p(0, 0), p(0, 1.0), p(2.0, 1.0), p(2.0, 2.0), p(3.0, 2.0), p(3.0, 0) ])) y.output(11, 0) diff --git a/testdata/drc/drcSimpleTests_10.drc b/testdata/drc/drcSimpleTests_10.drc new file mode 100644 index 000000000..743acbd8c --- /dev/null +++ b/testdata/drc/drcSimpleTests_10.drc @@ -0,0 +1,97 @@ +# Flat extraction + +source($drc_test_source) + +# Drawing layers + +nwell = input(1, 0) +diff = input(2, 0) +pplus = input(3, 0) +nplus = input(4, 0) +poly = input(5, 0) +thickox = input(6, 0) +polyres = input(7, 0) +contact = input(8, 0) +metal1 = input(9, 0) +via = input(10, 0) +metal2 = input(11, 0) + +# Special layer for bulk terminals + +bulk = make_layer + +# Computed layers + +diff_in_nwell = diff & nwell +pdiff = diff_in_nwell - nplus +ntie = diff_in_nwell & nplus +pgate = pdiff & poly +psd = pdiff - pgate +hv_pgate = pgate & thickox +lv_pgate = pgate - hv_pgate +hv_psd = psd & thickox +lv_psd = psd - thickox + +diff_outside_nwell = diff - nwell +ndiff = diff_outside_nwell - pplus +ptie = diff_outside_nwell & pplus +ngate = ndiff & poly +nsd = ndiff - ngate +hv_ngate = ngate & thickox +lv_ngate = ngate - hv_ngate +hv_nsd = nsd & thickox +lv_nsd = nsd - thickox + +# PMOS transistor device extraction + +hvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVPMOS") +extract_devices(hvpmos_ex, { "SD" => psd, "G" => hv_pgate, "P" => poly, "W" => nwell }) + +lvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVPMOS") +extract_devices(lvpmos_ex, { "SD" => psd, "G" => lv_pgate, "P" => poly, "W" => nwell }) + +# NMOS transistor device extraction + +lvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVNMOS") +extract_devices(lvnmos_ex, { "SD" => nsd, "G" => lv_ngate, "P" => poly, "W" => bulk }) + +hvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVNMOS") +extract_devices(hvnmos_ex, { "SD" => nsd, "G" => hv_ngate, "P" => poly, "W" => bulk }) + + +# Define connectivity for netlist extraction + +# Inter-layer +connect(contact, ntie) +connect(contact, ptie) +connect(nwell, ntie) +connect(psd, contact) +connect(nsd, contact) +connect(poly, contact) +connect(contact, metal1) +connect(metal1, via) +connect(via, metal2) + +# Global connections +connect_global(ptie, "BULK") +connect_global(bulk, "BULK") + +# Actually performs the extraction + +netlist = l2n_data.netlist + +# Writes the netlist + +writer = RBA::NetlistSpiceWriter::new + +netlist.write($drc_test_target, writer, "RINGO netlist before simplification") + +# Netlist simplification + +netlist.combine_devices +netlist.make_top_level_pins +netlist.purge +netlist.purge_nets + +netlist.write($drc_test_target_simplified, writer, "RINGO netlist after simplification") + diff --git a/testdata/drc/drcSimpleTests_11.drc b/testdata/drc/drcSimpleTests_11.drc new file mode 100644 index 000000000..e9a059603 --- /dev/null +++ b/testdata/drc/drcSimpleTests_11.drc @@ -0,0 +1,204 @@ + +source($drc_test_source) + +# Drawing layers + +nwell = input(1, 0) +diff = input(2, 0) +pplus = input(3, 0) +nplus = input(4, 0) +poly = input(5, 0) +thickox = input(6, 0) +polyres = input(7, 0) +contact = input(8, 0) +metal1 = input(9, 0) +via = input(10, 0) +metal2 = input(11, 0) + +# Special layer for bulk terminals + +bulk = make_layer + +# Computed layers + +poly_not_res = poly - polyres +poly_in_res = poly & polyres + +diff_in_nwell = diff & nwell +pdiff = diff_in_nwell - nplus +ntie = diff_in_nwell & nplus +pgate = pdiff & poly_not_res +psd = pdiff - pgate +hv_pgate = pgate & thickox +lv_pgate = pgate - hv_pgate +hv_psd = psd & thickox +lv_psd = psd - thickox + +diff_outside_nwell = diff - nwell +ndiff = diff_outside_nwell - pplus +ptie = diff_outside_nwell & pplus +ngate = ndiff & poly_not_res +nsd = ndiff - ngate +hv_ngate = ngate & thickox +lv_ngate = ngate - hv_ngate +hv_nsd = nsd & thickox +lv_nsd = nsd - thickox + +# Resistor device extraction + +class CustomResistorExtraction < RBA::GenericDeviceExtractor + + def initialize(name, sheet_rho) + self.name = name + @sheet_rho = sheet_rho + end + + def setup + + define_layer("M", "Marker") + define_layer("C", "Conductor") + define_layer("R", "Resistor") + + register_device_class (RBA::DeviceClassResistor::new); + + end + + def extract_devices(layer_geometry) + + marker = layer_geometry[0] + conductor = layer_geometry[1] + resistor = layer_geometry[2] + + conductor_geometry_index = 1 + + resistor_merged = resistor.merged + + marker_edges = marker.merged.edges & resistor_merged.edges + + resistor_merged.each do |r| + + connection_edges = marker_edges.interacting(RBA::Region::new(r)) + + terminals = connection_edges.extended_out(1) + + if terminals.size != 2 + error("Resistor shape does not touch marker border in exactly two places", r) + else + + # A = L*W + # P = 2*(L+W) + # -> L = p+sqrt(p*p-A) + # -> W = p-sqrt(p*p-A) + # (p=P/4) + + p = 0.25 * r.perimeter + a = r.area + + d = Math.sqrt(p * p - a) + l = p + d + w = p - d + + if w > 1e-3 + + device = create_device + + device.set_parameter(RBA::DeviceClassResistor::PARAM_R, @sheet_rho * l / w); + + define_terminal(device, RBA::DeviceClassResistor::TERMINAL_A, conductor_geometry_index, terminals[0]); + define_terminal(device, RBA::DeviceClassResistor::TERMINAL_B, conductor_geometry_index, terminals[1]); + + end + + end + + end + + end + + def get_connectivity(layout, layers) + + # the layer definition is marker, conductor, resistor + # * resistor is used for extraction + # * conductor is used for producing the terminals + # * marker is included because it's required for the extraction + + marker = layers[0] + conductor = layers[1] + resistor = layers[2] + + conn = RBA::Connectivity::new + + conn.connect(resistor, resistor) + conn.connect(conductor, resistor) + conn.connect(marker, resistor) + + return conn; + + end + +end + + + +# Resistor + +# Assumes a sheet rho of 150 Ohm/Square +res_ex = CustomResistorExtraction::new("RES", 150.0) +extract_devices(res_ex, { "C" => poly_not_res, "R" => poly_in_res, "M" => polyres }) + +# PMOS transistor device extraction + +hvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVPMOS") +extract_devices(hvpmos_ex, { "SD" => psd, "G" => hv_pgate, "P" => poly_not_res, "W" => nwell }) + +lvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVPMOS") +extract_devices(lvpmos_ex, { "SD" => psd, "G" => lv_pgate, "P" => poly_not_res, "W" => nwell }) + +# NMOS transistor device extraction + +lvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVNMOS") +extract_devices(lvnmos_ex, { "SD" => nsd, "G" => lv_ngate, "P" => poly_not_res, "W" => bulk }) + +hvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVNMOS") +extract_devices(hvnmos_ex, { "SD" => nsd, "G" => hv_ngate, "P" => poly_not_res, "W" => bulk }) + + +# Define connectivity for netlist extraction + +# Inter-layer +connect(contact, ntie) +connect(contact, ptie) +connect(nwell, ntie) +connect(psd, contact) +connect(nsd, contact) +connect(poly_not_res, contact) +connect(contact, metal1) +connect(metal1, via) +connect(via, metal2) + +# Global connections +connect_global(ptie, "BULK") +connect_global(bulk, "BULK") + +# Actually performs the extraction + +netlist = l2n_data.netlist + +# Write the netlist + +writer = RBA::NetlistSpiceWriter::new + +writer = RBA::NetlistSpiceWriter::new +netlist.write($drc_test_target, writer, "VDIV netlist before simplification") + +# Netlist simplification + +# NOTE: use make_top_level_pins before combine_devices as the pin +# stops the three resistors to be combined into one +netlist.make_top_level_pins +netlist.combine_devices +netlist.purge +netlist.purge_nets + +netlist.write($drc_test_target_simplified, writer, "VDIV netlist after simplification") + diff --git a/testdata/drc/drcSimpleTests_4.drc b/testdata/drc/drcSimpleTests_4.drc new file mode 100644 index 000000000..b729ee968 --- /dev/null +++ b/testdata/drc/drcSimpleTests_4.drc @@ -0,0 +1,77 @@ + +# Foreign cell test + +source($drc_test_source, "TOPTOP_SMALL") +target($drc_test_target) + +cell("TOPTOP_SMALL") + +l1_flat = input(1) +l1_flat.is_deep? && raise("l1_flat should not be deep") + +is_deep? && raise("is_deep? is true") + +deep + +is_deep? || raise("is_deep? is false") + +l1 = input(1) +l1.is_deep? || raise("l1 should be deep") +l2 = input(2) +l2.is_deep? || raise("l2 should be deep") + +flat + +is_deep? && raise("is_deep? is true") + +l2_flat = input(2) +l2_flat.is_deep? && raise("l2_flat should not be deep") + +l1.output(1, 0) +l2.output(2, 0) + +l1_flat.output(11, 0) +l2_flat.output(12, 0) + +r = l1.and(l2) +r.output(1000, 0) +r.extents.output(1100, 0) + +r = l1_flat.and(l2) +r.output(1001, 0) +r.extents.output(1101, 0) + +r = l1.and(l2_flat) +r.output(1002, 0) +r.extents.output(1102, 0) + +r = l1_flat.and(l2_flat) +r.output(1003, 0) +r.extents.output(1103, 0) + +r = l1.separation(l2, 0.3) +r.output(1020, 0) +r.extents.output(1120, 0) + +r = l1_flat.separation(l2, 0.3) +r.output(1021, 0) +r.extents.output(1121, 0) + +r = l1.separation(l2_flat, 0.3) +r.output(1022, 0) +r.extents.output(1122, 0) + +r = l1_flat.separation(l2_flat, 0.3) +r.output(1023, 0) +r.extents.output(1123, 0) + +r = l1.space(0.5) +r.output(1010, 0) +r.extents.output(1110, 0) + +l1.flatten +r = l1.space(0.5) +r.output(1011, 0) +r.extents.output(1111, 0) + + diff --git a/testdata/drc/drcSimpleTests_5.drc b/testdata/drc/drcSimpleTests_5.drc new file mode 100644 index 000000000..8f46068f5 --- /dev/null +++ b/testdata/drc/drcSimpleTests_5.drc @@ -0,0 +1,26 @@ + +# Hierarchical antenna check + +source($drc_test_source, "RINGO") +target($drc_test_target) + +diff = input(2, 0) +poly = input(3, 0) +contact = input(4, 0) +poly_cont = input(5, 0) +metal1 = input(6, 0) +via1 = input(7, 0) +metal2 = input(8, 0) + +gate = diff & poly + +connect(gate, poly) +connect(poly, poly_cont) +connect(poly_cont, metal1) +connect(metal1, via1) +connect(via1, metal2) + +antenna_check(gate, metal2, 1.0).output(101) +antenna_check(gate, metal2, 5.0).output(105) +antenna_check(gate, metal2, 10.0).output(110) +antenna_check(gate, metal2, 50.0).output(150) diff --git a/testdata/drc/drcSimpleTests_6.drc b/testdata/drc/drcSimpleTests_6.drc new file mode 100644 index 000000000..998f43ce1 --- /dev/null +++ b/testdata/drc/drcSimpleTests_6.drc @@ -0,0 +1,28 @@ + +# Hierarchical antenna check + +source($drc_test_source, "RINGO") +target($drc_test_target) + +deep + +diff = input(2, 0) +poly = input(3, 0) +contact = input(4, 0) +poly_cont = input(5, 0) +metal1 = input(6, 0) +via1 = input(7, 0) +metal2 = input(8, 0) + +gate = diff & poly + +connect(gate, poly) +connect(poly, poly_cont) +connect(poly_cont, metal1) +connect(metal1, via1) +connect(via1, metal2) + +antenna_check(gate, metal2, 1.0).output(101) +antenna_check(gate, metal2, 5.0).output(105) +antenna_check(gate, metal2, 10.0).output(110) +antenna_check(gate, metal2, 50.0).output(150) diff --git a/testdata/drc/drcSimpleTests_7.drc b/testdata/drc/drcSimpleTests_7.drc new file mode 100644 index 000000000..417c0ed44 --- /dev/null +++ b/testdata/drc/drcSimpleTests_7.drc @@ -0,0 +1,37 @@ + +# Hierarchical antenna check + +source($drc_test_source, "RINGO") +target($drc_test_target) + +deep + +diff = input(2, 0) +poly = input(3, 0) +diff_cont = input(4, 0) +poly_cont = input(5, 0) +metal1 = input(6, 0) +via1 = input(7, 0) +metal2 = input(8, 0) +diode = input(2, 10) + +gate = diff & poly + +connect(gate, poly) +connect(poly, poly_cont) +connect(diode, diff_cont) +connect(diff_cont, metal1) +connect(poly_cont, metal1) +connect(metal1, via1) +connect(via1, metal2) + +antenna_check(gate, metal2, 1.0, diode).output(101) +antenna_check(gate, metal2, 5.0, diode).output(105) +antenna_check(gate, metal2, 10.0, diode).output(110) +antenna_check(gate, metal2, 50.0, diode).output(150) + +antenna_check(gate, metal2, 1.0, [ diode, 0.5 ]).output(201) +antenna_check(gate, metal2, 1.0, [ diode, 5.0 ]).output(202) +antenna_check(gate, metal2, 5.0, [ diode, 5.0 ]).output(205) +antenna_check(gate, metal2, 10.0, [ diode, 5.0 ]).output(210) +antenna_check(gate, metal2, 50.0, [ diode, 5.0 ]).output(250) diff --git a/testdata/drc/drcSimpleTests_8.drc b/testdata/drc/drcSimpleTests_8.drc new file mode 100644 index 000000000..faab090a9 --- /dev/null +++ b/testdata/drc/drcSimpleTests_8.drc @@ -0,0 +1,54 @@ + +# Hierarchical antenna check + +source($drc_test_source, "TOP") +target($drc_test_target) + +# Flat mode + +flat_labels = labels(1, 0) +flat_all = input(1, 0) +flat_poly = polygons(1, 0) + +flat_texts_from_labels1 = flat_labels.texts(text("XYZ")) +flat_texts_from_labels2 = flat_labels.texts("*") +flat_texts_from_all1 = flat_all.texts(text("XYZ")) +flat_texts_from_all2 = flat_all.texts("*") +flat_texts_from_poly1 = flat_poly.texts(text("XYZ")) +flat_texts_from_poly2 = flat_poly.texts("*") + +flat_labels.output(100, 0) +flat_all.output(101, 0) +flat_poly.output(102, 0) +flat_texts_from_labels1.output(110, 0) +flat_texts_from_labels2.output(111, 0) +flat_texts_from_all1.output(112, 0) +flat_texts_from_all2.output(113, 0) +flat_texts_from_poly1.output(114, 0) +flat_texts_from_poly2.output(115, 0) + +# Deep mode + +deep + +deep_labels = labels(1, 0) +deep_all = input(1, 0) +deep_poly = polygons(1, 0) + +deep_texts_from_labels1 = deep_labels.texts(text("XYZ")) +deep_texts_from_labels2 = deep_labels.texts("*") +deep_texts_from_all1 = deep_all.texts(text("XYZ")) +deep_texts_from_all2 = deep_all.texts("*") +deep_texts_from_poly1 = deep_poly.texts(text("XYZ")) +deep_texts_from_poly2 = deep_poly.texts("*") + +deep_labels.output(200, 0) +deep_all.output(201, 0) +deep_poly.output(202, 0) +deep_texts_from_labels1.output(210, 0) +deep_texts_from_labels2.output(211, 0) +deep_texts_from_all1.output(212, 0) +deep_texts_from_all2.output(213, 0) +deep_texts_from_poly1.output(214, 0) +deep_texts_from_poly2.output(215, 0) + diff --git a/testdata/drc/drcSimpleTests_9.drc b/testdata/drc/drcSimpleTests_9.drc new file mode 100644 index 000000000..d033481be --- /dev/null +++ b/testdata/drc/drcSimpleTests_9.drc @@ -0,0 +1,99 @@ +# Hierarchical extraction + +source($drc_test_source) + +deep + +# Drawing layers + +nwell = input(1, 0) +diff = input(2, 0) +pplus = input(3, 0) +nplus = input(4, 0) +poly = input(5, 0) +thickox = input(6, 0) +polyres = input(7, 0) +contact = input(8, 0) +metal1 = input(9, 0) +via = input(10, 0) +metal2 = input(11, 0) + +# Special layer for bulk terminals + +bulk = make_layer + +# Computed layers + +diff_in_nwell = diff & nwell +pdiff = diff_in_nwell - nplus +ntie = diff_in_nwell & nplus +pgate = pdiff & poly +psd = pdiff - pgate +hv_pgate = pgate & thickox +lv_pgate = pgate - hv_pgate +hv_psd = psd & thickox +lv_psd = psd - thickox + +diff_outside_nwell = diff - nwell +ndiff = diff_outside_nwell - pplus +ptie = diff_outside_nwell & pplus +ngate = ndiff & poly +nsd = ndiff - ngate +hv_ngate = ngate & thickox +lv_ngate = ngate - hv_ngate +hv_nsd = nsd & thickox +lv_nsd = nsd - thickox + +# PMOS transistor device extraction + +hvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVPMOS") +extract_devices(hvpmos_ex, { "SD" => psd, "G" => hv_pgate, "P" => poly, "W" => nwell }) + +lvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVPMOS") +extract_devices(lvpmos_ex, { "SD" => psd, "G" => lv_pgate, "P" => poly, "W" => nwell }) + +# NMOS transistor device extraction + +lvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVNMOS") +extract_devices(lvnmos_ex, { "SD" => nsd, "G" => lv_ngate, "P" => poly, "W" => bulk }) + +hvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVNMOS") +extract_devices(hvnmos_ex, { "SD" => nsd, "G" => hv_ngate, "P" => poly, "W" => bulk }) + + +# Define connectivity for netlist extraction + +# Inter-layer +connect(contact, ntie) +connect(contact, ptie) +connect(nwell, ntie) +connect(psd, contact) +connect(nsd, contact) +connect(poly, contact) +connect(contact, metal1) +connect(metal1, via) +connect(via, metal2) + +# Global connections +connect_global(ptie, "BULK") +connect_global(bulk, "BULK") + +# Actually performs the extraction + +netlist = l2n_data.netlist + +# Writes the netlist + +writer = RBA::NetlistSpiceWriter::new + +netlist.write($drc_test_target, writer, "RINGO netlist before simplification") + +# Netlist simplification + +netlist.combine_devices +netlist.make_top_level_pins +netlist.purge +netlist.purge_nets + +netlist.write($drc_test_target_simplified, writer, "RINGO netlist after simplification") + diff --git a/testdata/drc/drcSimpleTests_au1.gds b/testdata/drc/drcSimpleTests_au1.gds new file mode 100644 index 000000000..55a1a97e1 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au1.gds differ diff --git a/testdata/drc/drcSimpleTests_au10a.cir b/testdata/drc/drcSimpleTests_au10a.cir new file mode 100644 index 000000000..2e308383c --- /dev/null +++ b/testdata/drc/drcSimpleTests_au10a.cir @@ -0,0 +1,67 @@ +* RINGO netlist before simplification + +* cell RINGO +.SUBCKT RINGO +* net 12 OUT +* net 27 ENABLE +* net 28 VDD +* net 29 FB +* net 43 BULK,VSS +* device instance $1 2.65,5.8 LVPMOS +M$1 2 27 28 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +* device instance $2 3.35,5.8 LVPMOS +M$2 28 29 2 28 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +* device instance $3 5.05,5.8 LVPMOS +M$3 28 2 3 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $4 6.85,5.8 LVPMOS +M$4 28 3 4 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $5 8.65,5.8 LVPMOS +M$5 28 4 5 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $6 10.45,5.8 LVPMOS +M$6 28 5 6 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $7 12.25,5.8 LVPMOS +M$7 28 6 7 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $8 14.05,5.8 LVPMOS +M$8 28 7 8 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $9 15.85,5.8 LVPMOS +M$9 28 8 9 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $10 17.65,5.8 LVPMOS +M$10 28 9 10 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $11 19.45,5.8 LVPMOS +M$11 28 10 11 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $12 21.25,5.8 LVPMOS +M$12 28 11 29 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $13 23.05,5.8 LVPMOS +M$13 28 29 12 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $14 2.65,2.135 LVNMOS +M$14 43 27 14 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U ++ PD=1.4U +* device instance $15 3.35,2.135 LVNMOS +M$15 14 29 2 43 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +* device instance $16 5.05,2.135 LVNMOS +M$16 43 2 3 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $17 6.85,2.135 LVNMOS +M$17 43 3 4 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $18 8.65,2.135 LVNMOS +M$18 43 4 5 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $19 10.45,2.135 LVNMOS +M$19 43 5 6 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $20 12.25,2.135 LVNMOS +M$20 43 6 7 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $21 14.05,2.135 LVNMOS +M$21 43 7 8 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $22 15.85,2.135 LVNMOS +M$22 43 8 9 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $23 17.65,2.135 LVNMOS +M$23 43 9 10 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +* device instance $24 19.45,2.135 LVNMOS +M$24 43 10 11 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +* device instance $25 21.25,2.135 LVNMOS +M$25 43 11 29 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +* device instance $26 23.05,2.135 LVNMOS +M$26 43 29 12 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +.ENDS RINGO diff --git a/testdata/drc/drcSimpleTests_au10b.cir b/testdata/drc/drcSimpleTests_au10b.cir new file mode 100644 index 000000000..dec252d6a --- /dev/null +++ b/testdata/drc/drcSimpleTests_au10b.cir @@ -0,0 +1,71 @@ +* RINGO netlist after simplification + +* cell RINGO +* pin OUT +* pin ENABLE +* pin VDD +* pin FB +* pin BULK,VSS +.SUBCKT RINGO 11 13 14 15 16 +* net 11 OUT +* net 13 ENABLE +* net 14 VDD +* net 15 FB +* net 16 BULK,VSS +* device instance $1 2.65,5.8 LVPMOS +M$1 1 13 14 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +* device instance $2 3.35,5.8 LVPMOS +M$2 14 15 1 14 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +* device instance $3 5.05,5.8 LVPMOS +M$3 14 1 2 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $4 6.85,5.8 LVPMOS +M$4 14 2 3 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $5 8.65,5.8 LVPMOS +M$5 14 3 4 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $6 10.45,5.8 LVPMOS +M$6 14 4 5 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $7 12.25,5.8 LVPMOS +M$7 14 5 6 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $8 14.05,5.8 LVPMOS +M$8 14 6 7 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $9 15.85,5.8 LVPMOS +M$9 14 7 8 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $10 17.65,5.8 LVPMOS +M$10 14 8 9 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $11 19.45,5.8 LVPMOS +M$11 14 9 10 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $12 21.25,5.8 LVPMOS +M$12 14 10 15 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $13 23.05,5.8 LVPMOS +M$13 14 15 11 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $14 2.65,2.135 LVNMOS +M$14 16 13 12 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U ++ PD=1.4U +* device instance $15 3.35,2.135 LVNMOS +M$15 12 15 1 16 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +* device instance $16 5.05,2.135 LVNMOS +M$16 16 1 2 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $17 6.85,2.135 LVNMOS +M$17 16 2 3 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $18 8.65,2.135 LVNMOS +M$18 16 3 4 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $19 10.45,2.135 LVNMOS +M$19 16 4 5 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $20 12.25,2.135 LVNMOS +M$20 16 5 6 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $21 14.05,2.135 LVNMOS +M$21 16 6 7 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $22 15.85,2.135 LVNMOS +M$22 16 7 8 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $23 17.65,2.135 LVNMOS +M$23 16 8 9 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +* device instance $24 19.45,2.135 LVNMOS +M$24 16 9 10 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +* device instance $25 21.25,2.135 LVNMOS +M$25 16 10 15 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +* device instance $26 23.05,2.135 LVNMOS +M$26 16 15 11 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U ++ PD=2.75U +.ENDS RINGO diff --git a/testdata/drc/drcSimpleTests_au11a.cir b/testdata/drc/drcSimpleTests_au11a.cir new file mode 100644 index 000000000..8b06b1937 --- /dev/null +++ b/testdata/drc/drcSimpleTests_au11a.cir @@ -0,0 +1,17 @@ +* VDIV netlist before simplification + +* cell TOP +.SUBCKT TOP +* net 1 OUT +* net 2 GND +* net 4 IN +* net 7 VDD +* device instance $1 1.025,0.335 RES +R$1 6 1 7650 +* device instance $2 2.85,0.335 RES +R$2 3 1 7650 +* device instance $3 4.665,0.335 RES +R$3 3 2 2670 +* device instance $4 1.765,7.485 HVPMOS +M$4 6 4 7 7 MHVPMOS L=0.25U W=1.5U AS=0.63P AD=0.63P PS=3.84U PD=3.84U +.ENDS TOP diff --git a/testdata/drc/drcSimpleTests_au11b.cir b/testdata/drc/drcSimpleTests_au11b.cir new file mode 100644 index 000000000..a98b9510e --- /dev/null +++ b/testdata/drc/drcSimpleTests_au11b.cir @@ -0,0 +1,19 @@ +* VDIV netlist after simplification + +* cell TOP +* pin OUT +* pin GND +* pin IN +* pin VDD +.SUBCKT TOP 1 2 3 5 +* net 1 OUT +* net 2 GND +* net 3 IN +* net 5 VDD +* device instance $1 1.025,0.335 RES +R$1 4 1 7650 +* device instance $2 2.85,0.335 RES +R$2 2 1 10320 +* device instance $4 1.765,7.485 HVPMOS +M$4 4 3 5 5 MHVPMOS L=0.25U W=1.5U AS=0.63P AD=0.63P PS=3.84U PD=3.84U +.ENDS TOP diff --git a/testdata/drc/drcSimpleTests_au2.gds b/testdata/drc/drcSimpleTests_au2.gds new file mode 100644 index 000000000..44b1433ab Binary files /dev/null and b/testdata/drc/drcSimpleTests_au2.gds differ diff --git a/testdata/drc/drcSimpleTests_au2.oas b/testdata/drc/drcSimpleTests_au2.oas deleted file mode 100644 index 7f15c2f3d..000000000 Binary files a/testdata/drc/drcSimpleTests_au2.oas and /dev/null differ diff --git a/testdata/drc/drcSimpleTests_au3.gds b/testdata/drc/drcSimpleTests_au3.gds new file mode 100644 index 000000000..5f919587f Binary files /dev/null and b/testdata/drc/drcSimpleTests_au3.gds differ diff --git a/testdata/drc/drcSimpleTests_au4.gds b/testdata/drc/drcSimpleTests_au4.gds new file mode 100644 index 000000000..1f3124594 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au4.gds differ diff --git a/testdata/drc/drcSimpleTests_au5.gds b/testdata/drc/drcSimpleTests_au5.gds new file mode 100644 index 000000000..3578cc1c0 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au5.gds differ diff --git a/testdata/drc/drcSimpleTests_au6.gds b/testdata/drc/drcSimpleTests_au6.gds new file mode 100644 index 000000000..75a2525f9 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au6.gds differ diff --git a/testdata/drc/drcSimpleTests_au7.gds b/testdata/drc/drcSimpleTests_au7.gds new file mode 100644 index 000000000..3fa5aff17 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au7.gds differ diff --git a/testdata/drc/drcSimpleTests_au8.gds b/testdata/drc/drcSimpleTests_au8.gds new file mode 100644 index 000000000..86825b3c5 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au8.gds differ diff --git a/testdata/drc/drcSimpleTests_au9a.cir b/testdata/drc/drcSimpleTests_au9a.cir new file mode 100644 index 000000000..e62b44d8e --- /dev/null +++ b/testdata/drc/drcSimpleTests_au9a.cir @@ -0,0 +1,151 @@ +* RINGO netlist before simplification + +* cell RINGO +.SUBCKT RINGO +* net 11 FB +* net 12 VDD +* net 15 OUT +* net 16 ENABLE +* net 19 BULK,VSS +* cell instance $1 r0 *1 1.8,0 +X$1 12 1 19 12 11 16 19 ND2X1 +* cell instance $2 r0 *1 4.2,0 +X$2 12 2 19 12 1 19 INVX1 +* cell instance $3 r0 *1 6,0 +X$3 12 3 19 12 2 19 INVX1 +* cell instance $4 r0 *1 7.8,0 +X$4 12 4 19 12 3 19 INVX1 +* cell instance $5 r0 *1 9.6,0 +X$5 12 5 19 12 4 19 INVX1 +* cell instance $6 r0 *1 11.4,0 +X$6 12 6 19 12 5 19 INVX1 +* cell instance $7 r0 *1 13.2,0 +X$7 12 7 19 12 6 19 INVX1 +* cell instance $8 r0 *1 15,0 +X$8 12 8 19 12 7 19 INVX1 +* cell instance $9 r0 *1 16.8,0 +X$9 12 9 19 12 8 19 INVX1 +* cell instance $10 r0 *1 18.6,0 +X$10 12 10 19 12 9 19 INVX1 +* cell instance $11 r0 *1 20.4,0 +X$11 12 11 19 12 10 19 INVX1 +* cell instance $12 r0 *1 22.2,0 +X$12 12 15 19 12 11 19 INVX1 +* cell instance $13 r0 *1 3.28,4 +X$13 11 M1M2 +* cell instance $14 r0 *1 21.42,4 +X$14 11 M1M2 +* cell instance $15 r0 *1 0.6,0 +X$15 12 19 TIE +* cell instance $16 r0 *1 0,0 +X$16 12 19 12 EMPTY +* cell instance $17 r0 *1 24,0 +X$17 12 19 TIE +* cell instance $18 r0 *1 25.2,0 +X$18 12 19 12 EMPTY +* cell instance $19 r0 *1 23.6,4 +X$19 15 M1M2 +* cell instance $20 r0 *1 2.6,3.1 +X$20 16 M1M2 +.ENDS RINGO + +* cell ND2X1 +* pin VDD +* pin OUT +* pin VSS +* pin +* pin B +* pin A +* pin BULK +.SUBCKT ND2X1 1 2 3 4 5 6 9 +* net 1 VDD +* net 2 OUT +* net 3 VSS +* net 5 B +* net 6 A +* net 9 BULK +* cell instance $1 r0 *1 0.3,5.05 +X$1 6 1 2 PMOS3 +* cell instance $2 r0 *1 1,5.05 +X$2 5 2 1 PMOS3 +* cell instance $3 r0 *1 1,1.66 +X$3 5 2 10 NMOS2 +* cell instance $4 r0 *1 0.3,1.66 +X$4 6 10 3 NMOS2 +* cell instance $5 r0 *1 1.48,4 +X$5 5 POLYM1 +* cell instance $6 r0 *1 0.8,3.1 +X$6 6 POLYM1 +* device instance $1 0.85,5.8 LVPMOS +M$1 2 6 1 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +* device instance $2 1.55,5.8 LVPMOS +M$2 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +* device instance $3 0.85,2.135 LVNMOS +M$3 3 6 10 9 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U PD=1.4U +* device instance $4 1.55,2.135 LVNMOS +M$4 10 5 2 9 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +.ENDS ND2X1 + +* cell INVX1 +* pin VDD +* pin OUT +* pin VSS +* pin +* pin IN +* pin BULK +.SUBCKT INVX1 1 2 3 4 5 7 +* net 1 VDD +* net 2 OUT +* net 3 VSS +* net 5 IN +* net 7 BULK +* cell instance $1 r0 *1 0.3,5.05 +X$1 5 2 1 PMOS3 +* cell instance $2 r0 *1 0.3,1.66 +X$2 5 2 3 NMOS2 +* cell instance $3 r0 *1 0.6,3.1 +X$3 5 POLYM1 +* device instance $1 0.85,5.8 LVPMOS +M$1 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $2 0.85,2.135 LVNMOS +M$2 3 5 2 7 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +.ENDS INVX1 + +* cell M1M2 +* pin +.SUBCKT M1M2 1 +.ENDS M1M2 + +* cell TIE +* pin VDD +* pin BULK,VSS +.SUBCKT TIE 2 3 +* net 2 VDD +* net 3 BULK,VSS +.ENDS TIE + +* cell EMPTY +* pin +* pin +* pin +.SUBCKT EMPTY 1 2 3 +.ENDS EMPTY + +* cell POLYM1 +* pin +.SUBCKT POLYM1 1 +.ENDS POLYM1 + +* cell NMOS2 +* pin +* pin +* pin +.SUBCKT NMOS2 1 2 3 +.ENDS NMOS2 + +* cell PMOS3 +* pin +* pin +* pin +.SUBCKT PMOS3 1 2 3 +.ENDS PMOS3 diff --git a/testdata/drc/drcSimpleTests_au9b.cir b/testdata/drc/drcSimpleTests_au9b.cir new file mode 100644 index 000000000..32e7dfc09 --- /dev/null +++ b/testdata/drc/drcSimpleTests_au9b.cir @@ -0,0 +1,83 @@ +* RINGO netlist after simplification + +* cell RINGO +* pin FB +* pin VDD +* pin OUT +* pin ENABLE +* pin BULK,VSS +.SUBCKT RINGO 11 12 13 14 15 +* net 11 FB +* net 12 VDD +* net 13 OUT +* net 14 ENABLE +* net 15 BULK,VSS +* cell instance $1 r0 *1 1.8,0 +X$1 12 1 15 12 11 14 15 ND2X1 +* cell instance $2 r0 *1 4.2,0 +X$2 12 2 15 12 1 15 INVX1 +* cell instance $3 r0 *1 6,0 +X$3 12 3 15 12 2 15 INVX1 +* cell instance $4 r0 *1 7.8,0 +X$4 12 4 15 12 3 15 INVX1 +* cell instance $5 r0 *1 9.6,0 +X$5 12 5 15 12 4 15 INVX1 +* cell instance $6 r0 *1 11.4,0 +X$6 12 6 15 12 5 15 INVX1 +* cell instance $7 r0 *1 13.2,0 +X$7 12 7 15 12 6 15 INVX1 +* cell instance $8 r0 *1 15,0 +X$8 12 8 15 12 7 15 INVX1 +* cell instance $9 r0 *1 16.8,0 +X$9 12 9 15 12 8 15 INVX1 +* cell instance $10 r0 *1 18.6,0 +X$10 12 10 15 12 9 15 INVX1 +* cell instance $11 r0 *1 20.4,0 +X$11 12 11 15 12 10 15 INVX1 +* cell instance $12 r0 *1 22.2,0 +X$12 12 13 15 12 11 15 INVX1 +.ENDS RINGO + +* cell ND2X1 +* pin VDD +* pin OUT +* pin VSS +* pin +* pin B +* pin A +* pin BULK +.SUBCKT ND2X1 1 2 3 4 5 6 7 +* net 1 VDD +* net 2 OUT +* net 3 VSS +* net 5 B +* net 6 A +* net 7 BULK +* device instance $1 0.85,5.8 LVPMOS +M$1 2 6 1 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +* device instance $2 1.55,5.8 LVPMOS +M$2 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +* device instance $3 0.85,2.135 LVNMOS +M$3 3 6 8 7 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U PD=1.4U +* device instance $4 1.55,2.135 LVNMOS +M$4 8 5 2 7 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +.ENDS ND2X1 + +* cell INVX1 +* pin VDD +* pin OUT +* pin VSS +* pin +* pin IN +* pin BULK +.SUBCKT INVX1 1 2 3 4 5 6 +* net 1 VDD +* net 2 OUT +* net 3 VSS +* net 5 IN +* net 6 BULK +* device instance $1 0.85,5.8 LVPMOS +M$1 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $2 0.85,2.135 LVNMOS +M$2 3 5 2 6 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +.ENDS INVX1 diff --git a/testdata/drc/drcSuiteTests.drc b/testdata/drc/drcSuiteTests.drc index 8490b026e..0928bbb0a 100644 --- a/testdata/drc/drcSuiteTests.drc +++ b/testdata/drc/drcSuiteTests.drc @@ -4,33 +4,37 @@ def message(m) $stdout.flush end -def run_testsuite(dm, ic, tiled = false) +def expect_eq(a, b) + a == b || raise("unexpected value #{a.to_s}, should be #{b.to_s}") +end + +def run_testsuite(dm, ic, tiled = false, hier = false) has_float_range = ((0..0.5).max != 0) lb = 100 - - a = input(RBA::LayerInfo::new(1, 0)) - b = input(2) - c = input(3) - x = input(10) - y = input(11) - empty = input(1000) + + a = polygons(RBA::LayerInfo::new(1, 0)) + b = polygons(2) + c = polygons(3) + x = polygons(10) + y = polygons(11) + empty = polygons(1000) h = {} layers.each { |l| h[l] = true } h[RBA::LayerInfo::new(1, 0)] || raise("missing layer 1/0 in layers list") - c.is_merged? == false || raise("unexpected value") + expect_eq(c.is_merged?, false) message "--- general #{lb}" l1 = a&b l1.output(lb, dm) - l1.is_empty? && raise("must not be empty") + expect_eq(l1.is_empty?, false) a.and(b).xor(l1).is_empty? || raise("xor not empty") - tiled || (a.and(b).is_merged? == true || raise("unexpected value")) + tiled || hier || expect_eq(a.and(b).is_merged?, true) a.xor(b).output(RBA::LayerInfo::new(lb + 1, dm)) a.xor(b).xor(a ^ b).is_empty? || raise("xor not empty") @@ -43,28 +47,36 @@ def run_testsuite(dm, ic, tiled = false) a.join(b).xor(a + b).is_empty? || raise("xor not empty") if !tiled - a.join(b).data.size == 16 * ic || raise("unexpected shape count") + expect_eq(a.join(b).data.size, 16 * ic) end - c.raw.is_clean? == false || raise("unexpected value") - # c.raw switched the semantics - c.is_clean? == false || raise("unexpected value") - (c.area / ic).to_s == "10.0" || raise("unexpected value") - (c.edges.length / ic).to_s == "18.0" || raise("unexpected value") - (c.perimeter / ic).to_s == "18.0" || raise("unexpected value") - c.clean - c.is_clean? == true || raise("unexpected value") - (c.area / ic).to_s == "9.0" || raise("unexpected value") - (c.edges.length / ic).to_s == "14.0" || raise("unexpected value") - (c.perimeter / ic).to_s == "14.0" || raise("unexpected value") - (c.dup.area / ic).to_s == "9.0" || raise("unexpected value") - (c.raw.clean.area / ic).to_s == "9.0" || raise("unexpected value") - (c.area / ic).to_s == "9.0" || raise("unexpected value") - (c.raw.area / ic).to_s == "10.0" || raise("unexpected value") - c.clean + # NOTE: there is no clean/raw semantics in deep mode + if ! hier + expect_eq(c.raw.is_clean?, false) + # c.raw switched the semantics + expect_eq(c.is_clean?, false) + expect_eq((c.area / ic).to_s, "10.0") + expect_eq((c.edges.length / ic).to_s, "18.0") + expect_eq((c.perimeter / ic).to_s, "18.0") + c.clean + expect_eq(c.is_clean?, true) + end + + expect_eq((c.area / ic).to_s, "9.0") + expect_eq((c.edges.length / ic).to_s, "14.0") + expect_eq((c.perimeter / ic).to_s, "14.0") + expect_eq((c.dup.area / ic).to_s, "9.0") + + # NOTE: there is no clean/raw semantics in deep mode + if ! hier + expect_eq((c.raw.clean.area / ic).to_s, "9.0") + expect_eq((c.area / ic).to_s, "9.0") + expect_eq((c.raw.area / ic).to_s, "10.0") + c.clean + end if ic == 1 - c.bbox.to_s == "(-4,-5;0,-2)" || raise("unexpected value") + expect_eq(c.bbox.to_s, "(-4,-5;0,-2)") end lb += 10 #110 @@ -80,15 +92,15 @@ def run_testsuite(dm, ic, tiled = false) a.edges.end_segments(0, 0.5).extended_out(0.01).output(lb + 7, dm) a.edges.end_segments(0.5, 0.5).extended_out(0.01).output(lb + 8, dm) - a.polygons? == true || raise("unexpected value") - a.edges? == false || raise("unexpected value") - a.edge_pairs? == false || raise("unexpected value") - a.edges.edge_pairs? == false || raise("unexpected value") - a.edges.polygons? == false || raise("unexpected value") - a.edges.edges? == true || raise("unexpected value") - a.space(0.5).edge_pairs? == true || raise("unexpected value") - a.space(0.5).polygons? == false || raise("unexpected value") - a.space(0.5).edges? == false || raise("unexpected value") + expect_eq(a.polygons?, true) + expect_eq(a.edges?, false) + expect_eq(a.edge_pairs?, false) + expect_eq(a.edges.edge_pairs?, false) + expect_eq(a.edges.polygons?, false) + expect_eq(a.edges.edges?, true) + expect_eq(a.space(0.5).edge_pairs?, true) + expect_eq(a.space(0.5).polygons?, false) + expect_eq(a.space(0.5).edges?, false) lb += 10 #120 message "--- extended #{lb}" @@ -132,7 +144,7 @@ def run_testsuite(dm, ic, tiled = false) b.interacting(a).in(b).output(lb + 1, dm) (b|a).not_in(b).output(lb + 2, dm) x.in(b).output(lb + 3, dm) - b.sized(0.1).in(b).is_empty? == true || raise("unexpected value") + expect_eq(b.sized(0.1).in(b).is_empty?, true) lb += 10 #170 message "--- inside, outside, overlapping, interacting #{lb}" @@ -209,14 +221,18 @@ def run_testsuite(dm, ic, tiled = false) lb += 10 #180 message "--- merge #{lb}" - c.raw + if !hier + c.raw + end if !tiled - c.merged.is_merged? == true || raise("unexpected value") + expect_eq(c.merged.is_merged?, true) end c.merged.output(lb, dm) c.merged(2).output(lb + 1, dm) c.merged(3).output(lb + 2, dm) - c.clean + if !hier + c.clean + end cdup = c.dup cdup.merge.xor(c.merged).output(lb + 3, dm) cdup.xor(c.merged).output(lb + 4, dm) @@ -252,9 +268,11 @@ def run_testsuite(dm, ic, tiled = false) a.non_rectangles.output(lb + 1, dm) a.rectilinear.output(lb + 2, dm) a.non_rectilinear.output(lb + 3, dm) - c.raw - c.non_rectangles.output(lb + 4, dm) - c.clean + if !hier + c.raw + c.non_rectangles.output(lb + 4, dm) + c.clean + end c.rectangles.output(lb + 5, dm) lb += 10 #210 @@ -561,12 +579,18 @@ if $drc_test_mode == 1 source($drc_test_source, "TOP") target($drc_test_target, "TOP") + + flat + run_testsuite(0, 1) elsif $drc_test_mode == 2 target($drc_test_target, "TOPTOP") source($drc_test_source, "TOPTOP") + + flat + run_testsuite(0, 900) elsif $drc_test_mode == 3 @@ -592,5 +616,23 @@ elsif $drc_test_mode == 4 run_testsuite(0, 900, true) +elsif $drc_test_mode == 5 + + source($drc_test_source, "TOP") + target($drc_test_target, "TOP") + + deep + + run_testsuite(0, 1, false, true) + +elsif $drc_test_mode == 6 + + target($drc_test_target, "TOPTOP") + source($drc_test_source, "TOPTOP") + + deep + + run_testsuite(0, 900, false, true) + end diff --git a/testdata/drc/drcSuiteTests_au5.oas b/testdata/drc/drcSuiteTests_au5.oas new file mode 100644 index 000000000..c49c2c616 Binary files /dev/null and b/testdata/drc/drcSuiteTests_au5.oas differ diff --git a/testdata/drc/drcSuiteTests_au6.oas b/testdata/drc/drcSuiteTests_au6.oas new file mode 100644 index 000000000..bf5fabfd9 Binary files /dev/null and b/testdata/drc/drcSuiteTests_au6.oas differ diff --git a/testdata/drc/drctest.gds b/testdata/drc/drctest.gds index daa662289..f1eb87e11 100644 Binary files a/testdata/drc/drctest.gds and b/testdata/drc/drctest.gds differ diff --git a/testdata/drc/ringo.gds b/testdata/drc/ringo.gds new file mode 100644 index 000000000..fa116f14d Binary files /dev/null and b/testdata/drc/ringo.gds differ diff --git a/testdata/drc/texts.gds b/testdata/drc/texts.gds new file mode 100644 index 000000000..241b8f894 Binary files /dev/null and b/testdata/drc/texts.gds differ diff --git a/testdata/drc/vdiv.gds b/testdata/drc/vdiv.gds new file mode 100644 index 000000000..48f2d16f9 Binary files /dev/null and b/testdata/drc/vdiv.gds differ diff --git a/testdata/python/dbRegionTest.py b/testdata/python/dbRegionTest.py index 6efd99829..b0ff7daae 100644 --- a/testdata/python/dbRegionTest.py +++ b/testdata/python/dbRegionTest.py @@ -19,6 +19,7 @@ import pya import unittest import sys +import os class DBRegionTest(unittest.TestCase): @@ -39,6 +40,47 @@ class DBRegionTest(unittest.TestCase): r.merge() self.assertEqual(str(r), "(0,100;0,300;50,300;50,350;250,350;250,150;200,150;200,100)") + def test_deep1(self): + + ut_testsrc = os.getenv("TESTSRC") + + # construction/destruction magic ... + self.assertEqual(pya.DeepShapeStore.instance_count(), 0) + dss = pya.DeepShapeStore() + dss._create() + self.assertEqual(pya.DeepShapeStore.instance_count(), 1) + dss = None + self.assertEqual(pya.DeepShapeStore.instance_count(), 0) + + dss = pya.DeepShapeStore() + ly = pya.Layout() + ly.read(os.path.join(ut_testsrc, "testdata", "algo", "deep_region_l1.gds")) + l1 = ly.layer(1, 0) + r = pya.Region(ly.top_cell().begin_shapes_rec(l1), dss) + rf = pya.Region(ly.top_cell().begin_shapes_rec(l1)) + + self.assertEqual(r.area(), 53120000) + self.assertEqual(rf.area(), 53120000) + + ly_new = pya.Layout() + tc = ly_new.add_cell("TOP") + l1 = ly_new.layer(1, 0) + l2 = ly_new.layer(2, 0) + ly_new.insert(tc, l1, r) + ly_new.insert(tc, l2, rf) + + s1 = { } + s2 = { } + for cell in ly_new.each_cell(): + s1[cell.name] = cell.shapes(l1).size() + s2[cell.name] = cell.shapes(l2).size() + self.assertEqual(s1, {"INV2": 1, "TOP": 0, "TRANS": 0}) + self.assertEqual(s2, {"INV2": 0, "TOP": 10, "TRANS": 0}) + + # force destroy, so the unit tests pass on the next iteration + dss = None + self.assertEqual(pya.DeepShapeStore.instance_count(), 0) + # run unit tests if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(DBRegionTest) diff --git a/testdata/python/dbTransTest.py b/testdata/python/dbTransTest.py index c8a677adc..230605411 100644 --- a/testdata/python/dbTransTest.py +++ b/testdata/python/dbTransTest.py @@ -76,8 +76,8 @@ class DBTransTests(unittest.TestCase): self.assertEqual( str(pya.Trans(pya.Trans.R180, pya.DVector(5,-7))), "r180 5,-7" ) self.assertEqual( str(pya.Trans(pya.Trans.R180)), "r180 0,0" ) - self.assertEqual( str(e.trans( pya.Edge(0, 1, 2, 3) )), "(-1,0;-3,-2)" ) - self.assertEqual( str(( e * pya.Edge(0, 1, 2, 3) )), "(-1,0;-3,-2)" ) + self.assertEqual( str(e.trans( pya.Edge(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) + self.assertEqual( str(( e * pya.Edge(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) self.assertEqual( str(e.trans( pya.Box(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) self.assertEqual( str(( e * pya.Box(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) self.assertEqual( str(e.trans( pya.Text("text", pya.Vector(0, 1)) )), "('text',m135 -1,0)" ) @@ -152,16 +152,16 @@ class DBTransTests(unittest.TestCase): self.assertEqual( str(c.s_trans()), "m135 0,0" ) self.assertAlmostEqual( c.angle, 270 ) - self.assertEqual( str(c.trans( pya.Edge(0, 1, 2, 3) )), "(-1,0;-3,-2)" ) - self.assertEqual( str(( c * pya.Edge(0, 1, 2, 3) )), "(-1,0;-3,-2)" ) - self.assertEqual( str(c.trans( pya.Box(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) - self.assertEqual( str(( c * pya.Box(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) - self.assertEqual( str(c.trans( pya.Text("text", pya.Vector(0, 1)) )), "('text',m135 -1,0)" ) - self.assertEqual( str(( c * pya.Text("text", pya.Vector(0, 1)) )), "('text',m135 -1,0)" ) - self.assertEqual( str(c.trans( pya.Polygon( [ pya.Point(0, 1), pya.Point(2, -3), pya.Point(4, 5) ] ) )), "(-5,-4;-1,0;3,-2)" ) - self.assertEqual( str(( c * pya.Polygon( [ pya.Point(0, 1), pya.Point(2, -3), pya.Point(4, 5) ] ) )), "(-5,-4;-1,0;3,-2)" ) - self.assertEqual( str(c.trans( pya.Path( [ pya.Point(0, 1), pya.Point(2, 3) ], 10 ) )), "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false" ) - self.assertEqual( str(( c * pya.Path( [ pya.Point(0, 1), pya.Point(2, 3) ], 10 ) )), "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false" ) + self.assertEqual( str(c.trans( pya.DEdge(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) + self.assertEqual( str(( c * pya.DEdge(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) + self.assertEqual( str(c.trans( pya.DBox(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) + self.assertEqual( str(( c * pya.DBox(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) + self.assertEqual( str(c.trans( pya.DText("text", pya.DVector(0, 1)) )), "('text',m135 -1,0)" ) + self.assertEqual( str(( c * pya.DText("text", pya.DVector(0, 1)) )), "('text',m135 -1,0)" ) + self.assertEqual( str(c.trans( pya.DPolygon( [ pya.DPoint(0, 1), pya.DPoint(2, -3), pya.DPoint(4, 5) ] ) )), "(-5,-4;-1,0;3,-2)" ) + self.assertEqual( str(( c * pya.DPolygon( [ pya.DPoint(0, 1), pya.DPoint(2, -3), pya.DPoint(4, 5) ] ) )), "(-5,-4;-1,0;3,-2)" ) + self.assertEqual( str(c.trans( pya.DPath( [ pya.DPoint(0, 1), pya.DPoint(2, 3) ], 10 ) )), "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false" ) + self.assertEqual( str(( c * pya.DPath( [ pya.DPoint(0, 1), pya.DPoint(2, 3) ], 10 ) )), "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false" ) c = pya.DCplxTrans.from_itrans( pya.CplxTrans.M135 ) self.assertEqual( str(c), "m135 *1 0,0" ) @@ -279,8 +279,8 @@ class DBTransTests(unittest.TestCase): c.mirror = True self.assertEqual( str(c), "m135 1,7" ) - self.assertEqual( str(e.trans( pya.Edge(0, 1, 2, 3) )), "(-1,0;-3,-2)" ) - self.assertEqual( str(( e * pya.Edge(0, 1, 2, 3) )), "(-1,0;-3,-2)" ) + self.assertEqual( str(e.trans( pya.Edge(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) + self.assertEqual( str(( e * pya.Edge(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) self.assertEqual( str(e.trans( pya.Box(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) self.assertEqual( str(( e * pya.Box(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) self.assertEqual( str(e.trans( pya.Text("text", pya.Vector(0, 1)) )), "('text',m135 -1,0)" ) diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index d12e6f210..5df2cc5f1 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -33,8 +33,10 @@ class DBEdgePairs_TestClass < TestBase assert_equal(r.is_empty?, true) assert_equal(r.size, 0) assert_equal(r.bbox.to_s, "()") + data_id = r.data_id r.insert(RBA::Edge::new(0, 0, 0, 100), RBA::Edge::new(-10, 0, -20, 50)) + assert_equal(data_id != r.data_id, true) assert_equal(r.to_s, "(0,0;0,100)/(-10,0;-20,50)") r.clear @@ -55,6 +57,7 @@ class DBEdgePairs_TestClass < TestBase assert_equal(r.moved(-10, 10).to_s, "(-10,10;-10,110)/(-20,10;-30,60)") assert_equal(r.moved(RBA::Point::new(-10, 10)).to_s, "(-10,10;-10,110)/(-20,10;-30,60)") rr = r.dup + assert_equal(rr.data_id != r.data_id, true) rr.move(-10, 10) assert_equal(rr.to_s, "(-10,10;-10,110)/(-20,10;-30,60)") rr = r.dup @@ -108,6 +111,114 @@ class DBEdgePairs_TestClass < TestBase end + def test_3 + + ep1 = RBA::EdgePair::new(RBA::Edge::new(0, 1, 2, 3), RBA::Edge::new(10, 11, 12, 13)) + ep2 = RBA::EdgePair::new(RBA::Edge::new(20, 21, 22, 23), RBA::Edge::new(30, 31, 32, 33)) + ep3 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 10, 10, 0)) + + r1 = RBA::EdgePairs::new([ ep1, ep2 ]) + assert_equal(r1.to_s, "(0,1;2,3)/(10,11;12,13);(20,21;22,23)/(30,31;32,33)") + + r1 = RBA::EdgePairs::new(ep1) + assert_equal(r1.to_s, "(0,1;2,3)/(10,11;12,13)") + + s = RBA::Shapes::new + s.insert(ep1) + s.insert(ep2) + r1 = RBA::EdgePairs::new(s) + assert_equal(r1.to_s, "(0,1;2,3)/(10,11;12,13);(20,21;22,23)/(30,31;32,33)") + + ly = RBA::Layout::new + l1 = ly.layer("l1") + l2 = ly.layer("l2") + l3 = ly.layer("l3") + c1 = ly.create_cell("C1") + c2 = ly.create_cell("C2") + c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(0, 0))) + c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(0, 100))) + c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(200, 100))) + c2.shapes(l1).insert(ep1) + c2.shapes(l2).insert(ep2) + c2.shapes(l3).insert(ep3) + + r = RBA::EdgePairs::new(ly.begin_shapes(c1.cell_index, l1)) + assert_equal(r.to_s(30), "(0,1;2,3)/(10,11;12,13);(0,101;2,103)/(10,111;12,113);(200,101;202,103)/(210,111;212,113)") + assert_equal(r.to_s(2), "(0,1;2,3)/(10,11;12,13);(0,101;2,103)/(10,111;12,113)...") + assert_equal(r.is_empty?, false) + assert_equal(r.size, 3) + + assert_equal(r.has_valid_edge_pairs?, false) + assert_equal(r.bbox.to_s, "(0,1;212,113)") + + assert_equal(r.is_deep?, false) + + r.flatten + assert_equal(r.has_valid_edge_pairs?, true) + assert_equal(r[1].to_s, "(0,101;2,103)/(10,111;12,113)") + assert_equal(r[100].inspect, "nil") + assert_equal(r.bbox.to_s, "(0,1;212,113)") + + dss = RBA::DeepShapeStore::new + r = RBA::EdgePairs::new(ly.begin_shapes(c1.cell_index, l1), dss) + assert_equal(r.to_s(30), "(0,1;2,3)/(10,11;12,13);(0,101;2,103)/(10,111;12,113);(200,101;202,103)/(210,111;212,113)") + assert_equal(r.to_s(2), "(0,1;2,3)/(10,11;12,13);(0,101;2,103)/(10,111;12,113)...") + assert_equal(r.is_empty?, false) + assert_equal(r.size, 3) + + assert_equal(r.has_valid_edge_pairs?, false) + assert_equal(r.bbox.to_s, "(0,1;212,113)") + + assert_equal(r.is_deep?, true) + + r.flatten + assert_equal(r.has_valid_edge_pairs?, true) + assert_equal(r[1].to_s, "(0,101;2,103)/(10,111;12,113)") + assert_equal(r[100].inspect, "nil") + assert_equal(r.bbox.to_s, "(0,1;212,113)") + + assert_equal(r.is_deep?, false) + + end + + def test_4 + + # insert_into and insert_into_as_polygons + + ep1 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 10, 10, 0)) + + ly = RBA::Layout::new + l1 = ly.layer("l1") + c1 = ly.create_cell("C1") + c2 = ly.create_cell("C2") + c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(0, 0))) + c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(0, 100))) + c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(200, 100))) + c2.shapes(l1).insert(ep1) + + dss = RBA::DeepShapeStore::new + r = RBA::EdgePairs::new(ly.begin_shapes(c1.cell_index, l1), dss) + + target = RBA::Layout::new + target_top = target.add_cell("TOP") + target_li = target.layer + r.insert_into(target, target_top, target_li) + cells = [] + target.each_cell { |c| cells << c.name } + assert_equal(cells.join(","), "TOP,C2") + assert_equal(RBA::EdgePairs::new(target.cell("TOP").shapes(target_li)).to_s, "") + assert_equal(RBA::EdgePairs::new(target.cell("C2").shapes(target_li)).to_s, "(0,0;0,10)/(10,10;10,0)") + + target_li = target.layer + r.insert_into_as_polygons(target, target_top, target_li, 1) + cells = [] + target.each_cell { |c| cells << c.name } + assert_equal(cells.join(","), "TOP,C2") + assert_equal(RBA::Region::new(target.cell("TOP").shapes(target_li)).to_s, "") + assert_equal(RBA::Region::new(target.cell("C2").shapes(target_li)).to_s, "(-1,-1;-1,11;11,11;11,-1)") + + end + end diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 002e97617..9b3435200 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -34,12 +34,21 @@ class DBEdges_TestClass < TestBase assert_equal(r.size, 0) assert_equal(r.bbox.to_s, "()") assert_equal(r.is_merged?, true) + data_id = r.data_id r.assign(RBA::Edges::new([RBA::Edge::new(10, 20, 100, 200)])) + assert_equal(data_id != r.data_id, true) assert_equal(r.to_s, "(10,20;100,200)") assert_equal(r.is_empty?, false) assert_equal(r.size, 1) assert_equal(r.bbox.to_s, "(10,20;100,200)") + assert_equal(r.is_merged?, true) + + r.assign(RBA::Edges::new([RBA::Edge::new(10, 20, 100, 200), RBA::Edge::new(11, 21, 101, 201)])) + assert_equal(r.to_s, "(10,20;100,200);(11,21;101,201)") + assert_equal(r.is_empty?, false) + assert_equal(r.size, 2) + assert_equal(r.bbox.to_s, "(10,20;101,201)") assert_equal(r.is_merged?, false) r.assign(RBA::Edges::new(RBA::Edge::new(10, 20, 100, 200))) @@ -47,7 +56,7 @@ class DBEdges_TestClass < TestBase assert_equal(r.is_empty?, false) assert_equal(r.size, 1) assert_equal(r.bbox.to_s, "(10,20;100,200)") - assert_equal(r.is_merged?, false) + assert_equal(r.is_merged?, true) r.assign(RBA::Edges::new(RBA::Box::new(10, 20, 100, 200))) assert_equal(r.to_s, "(10,20;10,200);(10,200;100,200);(100,200;100,20);(100,20;10,20)") @@ -60,11 +69,12 @@ class DBEdges_TestClass < TestBase assert_equal(r.is_empty?, false) assert_equal(r.size, 4) assert_equal(r.bbox.to_s, "(10,20;100,200)") - assert_equal(r.is_merged?, false) + assert_equal(r.is_merged?, true) assert_equal(r.moved(RBA::Point::new(10, 20)).bbox.to_s, "(20,40;110,220)") assert_equal(r.moved(10, 20).bbox.to_s, "(20,40;110,220)") rr = r.dup + assert_equal(rr.data_id != r.data_id, true) rr.move(RBA::Point::new(10, 20)) assert_equal(rr.bbox.to_s, "(20,40;110,220)") rr = r.dup @@ -163,6 +173,12 @@ class DBEdges_TestClass < TestBase assert_equal(r.to_s(2), "(-10,-20;10,20);(-10,80;10,120)...") assert_equal(r.is_empty?, false) assert_equal(r.size, 3) + assert_equal(r.bbox.to_s, "(-10,-20;210,120)") + assert_equal(r.is_merged?, false) + assert_equal(r.has_valid_edges?, false) + + r.flatten + assert_equal(r.has_valid_edges?, true) assert_equal(r[1].to_s, "(-10,80;10,120)") assert_equal(r[100].to_s, "") assert_equal(r.bbox.to_s, "(-10,-20;210,120)") @@ -546,6 +562,46 @@ class DBEdges_TestClass < TestBase end + # deep edges + def test_9 + + ly = RBA::Layout::new + l1 = ly.layer("l1") + c1 = ly.create_cell("C1") + c2 = ly.create_cell("C2") + c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(0, 0))) + c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(0, 100))) + c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(200, 100))) + c2.shapes(l1).insert(RBA::Box::new(-10, -20, 10, 20)) + + dss = RBA::DeepShapeStore::new + r = RBA::Edges::new(ly.begin_shapes(c1.cell_index, l1), dss, true) + assert_equal(r.to_s(30), "(-10,-20;-10,20);(-10,20;10,20);(10,20;10,-20);(10,-20;-10,-20);(-10,80;-10,120);(-10,120;10,120);(10,120;10,80);(10,80;-10,80);(190,80;190,120);(190,120;210,120);(210,120;210,80);(210,80;190,80)") + assert_equal(r.to_s(2), "(-10,-20;-10,20);(-10,20;10,20)...") + assert_equal(r.is_empty?, false) + assert_equal(r.bbox.to_s, "(-10,-20;210,120)") + assert_equal(r.is_deep?, true) + + target = RBA::Layout::new + target_top = target.add_cell("TOP") + target_li = target.layer + r.insert_into(target, target_top, target_li) + cells = [] + target.each_cell { |c| cells << c.name } + assert_equal(cells.join(","), "TOP,C2") + assert_equal(RBA::Edges::new(target.cell("TOP").shapes(target_li)).to_s, "") + assert_equal(RBA::Edges::new(target.cell("C2").shapes(target_li)).to_s, "(-10,-20;-10,20);(-10,20;10,20);(10,20;10,-20);(10,-20;-10,-20)") + + r.flatten + + assert_equal(r.is_deep?, false) + + assert_equal(r.size, 12) + assert_equal(r[1].to_s, "(-10,20;10,20)") + assert_equal(r[100].to_s, "") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb new file mode 100644 index 000000000..b5e9ac6cc --- /dev/null +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -0,0 +1,647 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2019 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class DBLayoutToNetlist_TestClass < TestBase + + def test_1_Basic + + ly = RBA::Layout::new + ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l1.gds")) + + l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, [])) + + l2n.threads = 17 + l2n.max_vertex_count = 42 + l2n.area_ratio = 7.5 + assert_equal(l2n.threads, 17) + assert_equal(l2n.max_vertex_count, 42) + assert_equal(l2n.area_ratio, 7.5) + + r = l2n.make_layer(ly.layer(6, 0)) + + assert_not_equal(l2n.internal_layout.object_id, ly.object_id) + assert_equal(l2n.internal_layout.top_cell.name, ly.top_cell.name) + assert_equal(l2n.internal_top_cell.name, ly.top_cell.name) + + assert_not_equal(l2n.layer_of(r), ly.layer(6, 0)) # would be a strange coincidence ... + + cm = l2n.const_cell_mapping_into(ly, ly.top_cell) + (0 .. l2n.internal_layout.cells - 1).each do |ci| + assert_equal(l2n.internal_layout.cell(ci).name, ly.cell(cm.cell_mapping(ci)).name) + end + + ly2 = RBA::Layout::new + ly2.create_cell(ly.top_cell.name) + + cm = l2n.cell_mapping_into(ly2, ly2.top_cell) + assert_equal(ly2.cells, ly.cells) + (0 .. l2n.internal_layout.cells - 1).each do |ci| + assert_equal(l2n.internal_layout.cell(ci).name, ly2.cell(cm.cell_mapping(ci)).name) + end + + rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0), "metal1" ) + bulk_id = l2n.connect_global(rmetal1, "BULK") + assert_equal(l2n.global_net_name(bulk_id), "BULK") + + end + + def test_2_ShapesFromNet + + ly = RBA::Layout::new + ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l1.gds")) + + l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, [])) + + # only plain backend connectivity + + rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0), "metal1" ) + rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1), "metal1_lbl" ) + rvia1 = l2n.make_polygon_layer( ly.layer(7, 0), "via1" ) + rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0), "metal2" ) + rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1), "metal2_lbl" ) + + # Intra-layer + l2n.connect(rmetal1) + l2n.connect(rvia1) + l2n.connect(rmetal2) + + # Inter-layer + l2n.connect(rmetal1, rvia1) + l2n.connect(rvia1, rmetal2) + l2n.connect(rmetal1, rmetal1_lbl) # attaches labels + l2n.connect(rmetal2, rmetal2_lbl) # attaches labels + + # Perform netlist extraction + l2n.extract_netlist + + assert_equal(l2n.netlist.to_s, < rpsd, "G" => rpgate, "P" => rpoly }) + + # NMOS transistor device extraction + nmos_ex = RBA::DeviceExtractorMOS3Transistor::new("NMOS") + l2n.extract_devices(nmos_ex, { "SD" => rnsd, "G" => rngate, "P" => rpoly }) + + # Define connectivity for netlist extraction + + l2n.register(rpsd, "psd") + l2n.register(rnsd, "nsd") + + # Intra-layer + l2n.connect(rpsd) + l2n.connect(rnsd) + l2n.connect(rpoly) + l2n.connect(rdiff_cont) + l2n.connect(rpoly_cont) + l2n.connect(rmetal1) + l2n.connect(rvia1) + l2n.connect(rmetal2) + + # Inter-layer + l2n.connect(rpsd, rdiff_cont) + l2n.connect(rnsd, rdiff_cont) + l2n.connect(rpoly, rpoly_cont) + l2n.connect(rpoly_cont, rmetal1) + l2n.connect(rdiff_cont, rmetal1) + l2n.connect(rmetal1, rvia1) + l2n.connect(rvia1, rmetal2) + l2n.connect(rpoly, rpoly_lbl) # attaches labels + l2n.connect(rmetal1, rmetal1_lbl) # attaches labels + l2n.connect(rmetal2, rmetal2_lbl) # attaches labels + + # Perform netlist extraction + l2n.extract_netlist + + assert_equal(l2n.netlist.to_s, < rpsd, "G" => rpgate, "P" => rpoly, "W" => rnwell }) + + # NMOS transistor device extraction + nmos_ex = RBA::DeviceExtractorMOS4Transistor::new("NMOS") + l2n.extract_devices(nmos_ex, { "SD" => rnsd, "G" => rngate, "P" => rpoly, "W" => rbulk }) + + # Define connectivity for netlist extraction + + l2n.register(rpsd, "psd") + l2n.register(rnsd, "nsd") + l2n.register(rptie, "ptie") + l2n.register(rntie, "ntie") + + # Intra-layer + l2n.connect(rpsd) + l2n.connect(rnsd) + l2n.connect(rnwell) + l2n.connect(rpoly) + l2n.connect(rdiff_cont) + l2n.connect(rpoly_cont) + l2n.connect(rmetal1) + l2n.connect(rvia1) + l2n.connect(rmetal2) + l2n.connect(rptie) + l2n.connect(rntie) + + # Inter-layer + l2n.connect(rpsd, rdiff_cont) + l2n.connect(rnsd, rdiff_cont) + l2n.connect(rpoly, rpoly_cont) + l2n.connect(rpoly_cont, rmetal1) + l2n.connect(rdiff_cont, rmetal1) + l2n.connect(rdiff_cont, rntie) + l2n.connect(rdiff_cont, rptie) + l2n.connect(rnwell, rntie) + l2n.connect(rmetal1, rvia1) + l2n.connect(rvia1, rmetal2) + l2n.connect(rpoly, rpoly_lbl) # attaches labels + l2n.connect(rmetal1, rmetal1_lbl) # attaches labels + l2n.connect(rmetal2, rmetal2_lbl) # attaches labels + + # Global connections + l2n.connect_global(rptie, "BULK") + l2n.connect_global(rbulk, "BULK") + + # Perform netlist extraction + l2n.extract_netlist + + assert_equal(l2n.netlist.to_s, < ex + error = true + end + assert_equal(error, true) + + c = RBA::Circuit::new + c.name = "C" + nl.add(c) + d1 = c.create_device(dc) + + assert_equal(d1.circuit.object_id, c.object_id) + + assert_equal(d1.parameter(0), 1.0) + assert_equal(d1.parameter("U"), 1.0) + assert_equal(d1.parameter(1), 2.0) + assert_equal(d1.parameter("V"), 2.0) + + d1.set_parameter(0, 0.5) + assert_equal(d1.parameter(0), 0.5) + assert_equal(d1.parameter(1), 2.0) + d1.set_parameter("U", -0.5) + assert_equal(d1.parameter(0), -0.5) + assert_equal(d1.parameter(1), 2.0) + d1.set_parameter("V", 42) + assert_equal(d1.parameter(0), -0.5) + assert_equal(d1.parameter(1), 42) + + assert_equal(nl.to_s, <1, "TOP"=>0, "TRANS"=>0}) + assert_equal(s2, {"INV2"=>0, "TOP"=>10, "TRANS"=>0}) + + target = RBA::Layout::new + target_top = target.add_cell("TOP") + target_li = target.layer + r.insert_into(target, target_top, target_li) + cells = [] + target.each_cell { |c| cells << c.name } + assert_equal(cells.join(","), "TOP,INV2,TRANS") + assert_equal(RBA::Region::new(target.cell("TOP").shapes(target_li)).to_s, "") + assert_equal(RBA::Region::new(target.cell("INV2").shapes(target_li)).to_s, "(-1400,1800;-1400,3800;1400,3800;1400,1800)") + assert_equal(RBA::Region::new(target.cell("TRANS").shapes(target_li)).to_s, "") + + r.flatten + assert_equal(r.is_deep?, false) + assert_equal(r.area, 53120000) + + # force destroy, so the unit tests pass on the next iteration + dss._destroy + assert_equal(RBA::DeepShapeStore::instance_count, 0) + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbShapesTest.rb b/testdata/ruby/dbShapesTest.rb index 3ae0888c7..f9b8277a8 100644 --- a/testdata/ruby/dbShapesTest.rb +++ b/testdata/ruby/dbShapesTest.rb @@ -155,6 +155,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].polygon.inspect, "(10,-10;10,40;50,40;50,-10)" ) assert_equal( arr[0].simple_polygon.inspect, "(10,-10;10,40;50,40;50,-10)" ) assert_equal( arr[0].edge.inspect, "nil" ) + assert_equal( arr[0].edge_pair.inspect, "nil" ) assert_equal( arr[0].box.inspect, "(10,-10;50,40)" ) assert_equal( arr[0].path.inspect, "nil" ) assert_equal( arr[0].text.inspect, "nil" ) @@ -178,6 +179,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].polygon.inspect, "nil" ) assert_equal( arr[0].simple_polygon.inspect, "nil" ) assert_equal( arr[0].edge.inspect, "(-1,2;5,2)" ) + assert_equal( arr[0].edge_pair.inspect, "nil" ) assert_equal( arr[0].box.inspect, "nil" ) assert_equal( arr[0].path.inspect, "nil" ) assert_equal( arr[0].text.inspect, "nil" ) @@ -188,6 +190,32 @@ class DBShapes_TestClass < TestBase assert_equal( arr.size, 1 ) assert_equal( arr[0].is_box?, true ) + # edge pairs + + a = RBA::EdgePair::new(RBA::Edge::new(RBA::Point::new(-1, 2), RBA::Point::new(5, 2)), RBA::Edge::new(RBA::Point::new(-1, 5), RBA::Point::new(5, 5))) + c1.shapes( lindex ).insert( a ) + arr = [] + shapes.each( RBA::Shapes::SEdgePairs ) { |s| arr.push( s ) } + assert_equal( arr.size, 1 ) + assert_equal( arr[0].prop_id, 0 ) + assert_equal( arr[0].has_prop_id?, false ) + assert_equal( arr[0].is_null?, false ) + assert_equal( arr[0].type, RBA::Shape::t_edge_pair ) + assert_equal( arr[0].is_edge_pair?, true ) + assert_equal( arr[0].polygon.inspect, "nil" ) + assert_equal( arr[0].simple_polygon.inspect, "nil" ) + assert_equal( arr[0].edge_pair.inspect, "(-1,2;5,2)/(-1,5;5,5)" ) + assert_equal( arr[0].edge.inspect, "nil" ) + assert_equal( arr[0].box.inspect, "nil" ) + assert_equal( arr[0].path.inspect, "nil" ) + assert_equal( arr[0].text.inspect, "nil" ) + assert_equal( arr[0].edge_pair == a, true ) + assert_equal( arr[0].bbox == a.bbox, true ) + arr = [] + shapes.each( RBA::Shapes::SBoxes ) { |s| arr.push( s ) } + assert_equal( arr.size, 1 ) + assert_equal( arr[0].is_box?, true ) + # paths a = RBA::Path::new( [ RBA::Point::new( 0, 10 ), RBA::Point::new( 10, 50 ) ], 25 ) @@ -203,6 +231,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].polygon.inspect, "(12,7;-12,13;-2,53;22,47)" ) assert_equal( arr[0].simple_polygon.inspect, "(12,7;-12,13;-2,53;22,47)" ) assert_equal( arr[0].edge.inspect, "nil" ) + assert_equal( arr[0].edge_pair.inspect, "nil" ) assert_equal( arr[0].box.inspect, "nil" ) assert_equal( arr[0].path.inspect, "(0,10;10,50) w=25 bx=0 ex=0 r=false" ) assert_equal( arr[0].text.inspect, "nil" ) @@ -238,6 +267,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].polygon.inspect, "(0,1;1,5;5,5)" ) assert_equal( arr[0].simple_polygon.inspect, "(0,1;1,5;5,5)" ) assert_equal( arr[0].edge.inspect, "nil" ) + assert_equal( arr[0].edge_pair.inspect, "nil" ) assert_equal( arr[0].box.inspect, "nil" ) assert_equal( arr[0].path.inspect, "nil" ) assert_equal( arr[0].text.inspect, "nil" ) @@ -419,6 +449,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dpolygon.inspect, "(0.01,-0.01;0.01,0.04;0.05,0.04;0.05,-0.01)" ) assert_equal( arr[0].dsimple_polygon.inspect, "(0.01,-0.01;0.01,0.04;0.05,0.04;0.05,-0.01)" ) assert_equal( arr[0].dedge.inspect, "nil" ) + assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.inspect, "(0.01,-0.01;0.05,0.04)" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) @@ -442,6 +473,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dpolygon.inspect, "nil" ) assert_equal( arr[0].dsimple_polygon.inspect, "nil" ) assert_equal( arr[0].dedge.inspect, "(-0.001,0.002;0.005,0.002)" ) + assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.inspect, "nil" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) @@ -451,6 +483,31 @@ class DBShapes_TestClass < TestBase assert_equal( arr.size, 1 ) assert_equal( arr[0].is_box?, true ) + # edge pairs + + a = RBA::DEdgePair::new(RBA::DEdge::new(RBA::DPoint::new(-0.001, 0.002), RBA::DPoint::new(0.005, 0.002)), RBA::DEdge::new(RBA::DPoint::new(-0.001, 0.005), RBA::DPoint::new(0.005, 0.005))) + c1.shapes( lindex ).insert( a ) + arr = [] + shapes.each( RBA::Shapes::SEdgePairs ) { |s| arr.push( s ) } + assert_equal( arr.size, 1 ) + assert_equal( arr[0].prop_id, 0 ) + assert_equal( arr[0].has_prop_id?, false ) + assert_equal( arr[0].is_null?, false ) + assert_equal( arr[0].type, RBA::Shape::t_edge_pair ) + assert_equal( arr[0].is_edge_pair?, true ) + assert_equal( arr[0].dpolygon.inspect, "nil" ) + assert_equal( arr[0].dsimple_polygon.inspect, "nil" ) + assert_equal( arr[0].dedge_pair.inspect, "(-0.001,0.002;0.005,0.002)/(-0.001,0.005;0.005,0.005)" ) + assert_equal( arr[0].dedge.inspect, "nil" ) + assert_equal( arr[0].dbox.inspect, "nil" ) + assert_equal( arr[0].dpath.inspect, "nil" ) + assert_equal( arr[0].dtext.inspect, "nil" ) + assert_equal( arr[0].dbbox.inspect, "(-0.001,0.002;0.005,0.005)" ) + arr = [] + shapes.each( RBA::Shapes::SBoxes ) { |s| arr.push( s ) } + assert_equal( arr.size, 1 ) + assert_equal( arr[0].is_box?, true ) + # paths a = RBA::DPath::new( [ RBA::DPoint::new( 0, 0.010 ), RBA::DPoint::new( 0.010, 0.050 ) ], 0.025 ) @@ -466,6 +523,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dpolygon.inspect, "(0.012,0.007;-0.012,0.013;-0.002,0.053;0.022,0.047)" ) assert_equal( arr[0].dsimple_polygon.inspect, "(0.012,0.007;-0.012,0.013;-0.002,0.053;0.022,0.047)" ) assert_equal( arr[0].dedge.inspect, "nil" ) + assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.inspect, "nil" ) assert_equal( arr[0].dpath.inspect, "(0,0.01;0.01,0.05) w=0.025 bx=0 ex=0 r=false" ) assert_equal( arr[0].dtext.inspect, "nil" ) @@ -499,6 +557,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dpolygon.inspect, "(0,0.001;0.001,0.005;0.005,0.005)" ) assert_equal( arr[0].dsimple_polygon.inspect, "(0,0.001;0.001,0.005;0.005,0.005)" ) assert_equal( arr[0].dedge.inspect, "nil" ) + assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.inspect, "nil" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) @@ -720,6 +779,18 @@ class DBShapes_TestClass < TestBase shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } assert_equal( arr, ["simple_polygon (14,0;-21,35;7,64;42,28)", "box (10,-10;50,40) prop_id=17"] ) + s2 = shapes.replace( s2, RBA::Edge::new( 10, -10, 50, 40 ) ) + + arr = [] + shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } + assert_equal( arr, ["simple_polygon (14,0;-21,35;7,64;42,28)", "edge (10,-10;50,40) prop_id=17"] ) + + s2 = shapes.replace( s2, RBA::EdgePair::new( RBA::Edge::new( 10, -10, 50, 40 ), RBA::Edge::new( 10, 0, 50, 30 ) ) ) + + arr = [] + shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } + assert_equal( arr, ["simple_polygon (14,0;-21,35;7,64;42,28)", "edge_pair (10,-10;50,40)/(10,0;50,30) prop_id=17"] ) + shapes.erase( s2 ) arr = [] @@ -775,6 +846,7 @@ class DBShapes_TestClass < TestBase assert_equal(s2.simple_polygon.inspect, "nil") assert_equal(s2.text.inspect, "('text',r0 100,200)") assert_equal(s2.edge.inspect, "nil") + assert_equal(s2.edge_pair.inspect, "nil") assert_equal(s2.path.inspect, "nil") assert_equal(s2.box.inspect, "nil") @@ -784,6 +856,18 @@ class DBShapes_TestClass < TestBase shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } assert_equal( arr, ["box (11,-11;51,41)", "box (200,-10;250,40)", "text ('text',r0 100,200)"] ) + s3.edge_pair = RBA::EdgePair::new(RBA::Edge::new(RBA::Point::new(1, 2), RBA::Point::new(3, 4)), RBA::Edge::new(RBA::Point::new(1, 12), RBA::Point::new(3, 14))) + + shapes = c1.shapes( lindex ) + + arr = [] + shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } + assert_equal( arr, ["edge_pair (1,2;3,4)/(1,12;3,14)", "box (11,-11;51,41)", "text ('text',r0 100,200)"] ) + + arr = [] + shapes.each( RBA::Shapes::SEdgePairs ) { |s| arr.push( s.to_s ) } + assert_equal( arr, ["edge_pair (1,2;3,4)/(1,12;3,14)"] ) + s3.edge = RBA::Edge::new( RBA::Point::new( 1, 2 ), RBA::Point::new( 3, 4 ) ) shapes = c1.shapes( lindex ) @@ -792,6 +876,10 @@ class DBShapes_TestClass < TestBase shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } assert_equal( arr, ["edge (1,2;3,4)", "box (11,-11;51,41)", "text ('text',r0 100,200)"] ) + arr = [] + shapes.each( RBA::Shapes::SEdges ) { |s| arr.push( s.to_s ) } + assert_equal( arr, ["edge (1,2;3,4)"] ) + pts = [ RBA::Point::new( 100, 200 ), RBA::Point::new( 400, 300 ), RBA::Point::new( 500, 600 ) ] s1.polygon = RBA::Polygon::new( pts ) @@ -990,6 +1078,14 @@ class DBShapes_TestClass < TestBase shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } assert_equal( arr, ["box (11,-11;51,41)", "box (200,-10;250,40)", "text ('text',r0 100,200)"] ) + s3.edge_pair = RBA::DEdgePair::new(RBA::DEdge::new(RBA::DPoint::new(0.001, 0.002), RBA::DPoint::new(0.003, 0.004)), RBA::DEdge::new(RBA::DPoint::new(0.001, 0.012), RBA::DPoint::new(0.003, 0.014))) + + shapes = c1.shapes( lindex ) + + arr = [] + shapes.each( RBA::Shapes::SAll ) { |s| arr.push( s.to_s ) } + assert_equal( arr, ["edge_pair (1,2;3,4)/(1,12;3,14)", "box (11,-11;51,41)", "text ('text',r0 100,200)"] ) + s3.edge = RBA::DEdge::new( RBA::DPoint::new( 0.001, 0.002 ), RBA::DPoint::new( 0.003, 0.004 ) ) shapes = c1.shapes( lindex ) diff --git a/testdata/ruby/dbTransTest.rb b/testdata/ruby/dbTransTest.rb index 3bf82499c..b26198d5f 100644 --- a/testdata/ruby/dbTransTest.rb +++ b/testdata/ruby/dbTransTest.rb @@ -80,8 +80,8 @@ class DBTrans_TestClass < TestBase assert_equal( RBA::Trans::new(RBA::Trans::R180, RBA::DVector::new(5,-7)).to_s, "r180 5,-7" ) assert_equal( RBA::Trans::new(RBA::Trans::R180).to_s, "r180 0,0" ) - assert_equal( e.trans( RBA::Edge::new(0, 1, 2, 3) ).to_s, "(-1,0;-3,-2)" ) - assert_equal( ( e * RBA::Edge::new(0, 1, 2, 3) ).to_s, "(-1,0;-3,-2)" ) + assert_equal( e.trans( RBA::Edge::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) + assert_equal( ( e * RBA::Edge::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) assert_equal( e.trans( RBA::Box::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) assert_equal( ( e * RBA::Box::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) assert_equal( e.trans( RBA::Text::new("text", RBA::Vector::new(0, 1)) ).to_s, "('text',m135 -1,0)" ) @@ -160,16 +160,16 @@ class DBTrans_TestClass < TestBase assert_equal( c.s_trans.to_s, "m135 0,0" ) assert_equal( c.angle, 270 ) - assert_equal( c.trans( RBA::Edge::new(0, 1, 2, 3) ).to_s, "(-1,0;-3,-2)" ) - assert_equal( ( c * RBA::Edge::new(0, 1, 2, 3) ).to_s, "(-1,0;-3,-2)" ) - assert_equal( c.trans( RBA::Box::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) - assert_equal( ( c * RBA::Box::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) - assert_equal( c.trans( RBA::Text::new("text", RBA::Vector::new(0, 1)) ).to_s, "('text',m135 -1,0)" ) - assert_equal( ( c * RBA::Text::new("text", RBA::Vector::new(0, 1)) ).to_s, "('text',m135 -1,0)" ) - assert_equal( c.trans( RBA::Polygon::new( [ RBA::Point::new(0, 1), RBA::Point::new(2, -3), RBA::Point::new(4, 5) ] ) ).to_s, "(-5,-4;-1,0;3,-2)" ) - assert_equal( ( c * RBA::Polygon::new( [ RBA::Point::new(0, 1), RBA::Point::new(2, -3), RBA::Point::new(4, 5) ] ) ).to_s, "(-5,-4;-1,0;3,-2)" ) - assert_equal( c.trans( RBA::Path::new( [ RBA::Point::new(0, 1), RBA::Point::new(2, 3) ], 10 ) ).to_s, "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false" ) - assert_equal( ( c * RBA::Path::new( [ RBA::Point::new(0, 1), RBA::Point::new(2, 3) ], 10 ) ).to_s, "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false" ) + assert_equal( c.trans( RBA::DEdge::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) + assert_equal( ( c * RBA::DEdge::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) + assert_equal( c.trans( RBA::DBox::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) + assert_equal( ( c * RBA::DBox::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) + assert_equal( c.trans( RBA::DText::new("text", RBA::DVector::new(0, 1)) ).to_s, "('text',m135 -1,0)" ) + assert_equal( ( c * RBA::DText::new("text", RBA::DVector::new(0, 1)) ).to_s, "('text',m135 -1,0)" ) + assert_equal( c.trans( RBA::DPolygon::new( [ RBA::DPoint::new(0, 1), RBA::DPoint::new(2, -3), RBA::DPoint::new(4, 5) ] ) ).to_s, "(-5,-4;-1,0;3,-2)" ) + assert_equal( ( c * RBA::DPolygon::new( [ RBA::DPoint::new(0, 1), RBA::DPoint::new(2, -3), RBA::DPoint::new(4, 5) ] ) ).to_s, "(-5,-4;-1,0;3,-2)" ) + assert_equal( c.trans( RBA::DPath::new( [ RBA::DPoint::new(0, 1), RBA::DPoint::new(2, 3) ], 10 ) ).to_s, "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false" ) + assert_equal( ( c * RBA::DPath::new( [ RBA::DPoint::new(0, 1), RBA::DPoint::new(2, 3) ], 10 ) ).to_s, "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false" ) c = RBA::DCplxTrans::from_itrans( RBA::CplxTrans::M135 ) assert_equal( c.to_s, "m135 *1 0,0" ) @@ -262,8 +262,8 @@ class DBTrans_TestClass < TestBase c.mirror = true assert_equal( c.to_s, "m135 1,7" ) - assert_equal( e.trans( RBA::Edge::new(0, 1, 2, 3) ).to_s, "(-1,0;-3,-2)" ) - assert_equal( ( e * RBA::Edge::new(0, 1, 2, 3) ).to_s, "(-1,0;-3,-2)" ) + assert_equal( e.trans( RBA::Edge::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) + assert_equal( ( e * RBA::Edge::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) assert_equal( e.trans( RBA::Box::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) assert_equal( ( e * RBA::Box::new(0, 1, 2, 3) ).to_s, "(-3,-2;-1,0)" ) assert_equal( e.trans( RBA::Text::new("text", RBA::Vector::new(0, 1)) ).to_s, "('text',m135 -1,0)" ) diff --git a/testdata/ruby/rdbTest.rb b/testdata/ruby/rdbTest.rb index 2aa57f370..975f1155f 100644 --- a/testdata/ruby/rdbTest.rb +++ b/testdata/ruby/rdbTest.rb @@ -697,6 +697,7 @@ class RDB_TestClass < TestBase end + # scan_... methods def test_11 ly = RBA::Layout::new @@ -769,7 +770,18 @@ class RDB_TestClass < TestBase rdb = RBA::ReportDatabase.new("neu") cat = rdb.create_category("l1") - cat.scan_shapes(c1.begin_shapes_rec(l1)) + cat.scan_shapes(c1.begin_shapes_rec(l1)) # hierarchical scan + assert_equal(cat.num_items, 3) + cn = [] + rdb.each_cell { |c| cn << c.to_s_test } + assert_equal(cn.join(";"), "c1[];c2[c1->r0 *1 0.01,0.02];c3[c1->r0 *1 0.021,0.041]") + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]") + + rdb = RBA::ReportDatabase.new("neu") + cat = rdb.create_category("l1") + cat.scan_shapes(c1.begin_shapes_rec(l1), true) # flat scan assert_equal(cat.num_items, 3) cn = [] rdb.each_cell { |c| cn << c.to_s_test } @@ -780,14 +792,39 @@ class RDB_TestClass < TestBase rdb = RBA::ReportDatabase.new("neu") cat = rdb.create_category("l1") - cat.scan_shapes(c1.begin_shapes_rec(l1)) + r = RBA::Region::new(c1.begin_shapes_rec(l1)) + cat.scan_collection(rdb.create_cell("TOP"), RBA::CplxTrans::new(0.001), r) # hierarchical scan assert_equal(cat.num_items, 3) cn = [] rdb.each_cell { |c| cn << c.to_s_test } - assert_equal(cn.join(";"), "c1[]") + assert_equal(cn.join(";"), "TOP[];c1[TOP->r0 *1 0,0];c2[c1->r0 *1 0.01,0.02];c3[c1->r0 *1 0.021,0.041]") cn = [] rdb.each_cell { |c| cn << c.to_s_items } - assert_equal(cn.join(";"), "c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001),polygon: (0.01,0.021;0.01,0.051;0.031,0.051;0.031,0.021),polygon: (0.021,0.042;0.021,0.073;0.043,0.073;0.043,0.042)]") + assert_equal(cn.join(";"), "TOP[];c1[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001)];c2[polygon: (0,0.001;0,0.031;0.021,0.031;0.021,0.001)];c3[polygon: (0,0.001;0,0.032;0.022,0.032;0.022,0.001)]") + + rdb = RBA::ReportDatabase.new("neu") + cat = rdb.create_category("l1") + r = RBA::Region::new(c1.begin_shapes_rec(l1)) + cat.scan_collection(rdb.create_cell("TOP"), RBA::CplxTrans::new(0.001), r, true) # flat scan + assert_equal(cat.num_items, 3) + cn = [] + rdb.each_cell { |c| cn << c.to_s_test } + assert_equal(cn.join(";"), "TOP[]") + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "TOP[polygon: (0,0.001;0,0.03;0.02,0.03;0.02,0.001),polygon: (0.01,0.021;0.01,0.051;0.031,0.051;0.031,0.021),polygon: (0.021,0.042;0.021,0.073;0.043,0.073;0.043,0.042)]") + + rdb = RBA::ReportDatabase.new("neu") + cat = rdb.create_category("l1") + r = RBA::Region::new(c1.begin_shapes_rec(l1)).merged + cat.scan_collection(rdb.create_cell("TOP"), RBA::CplxTrans::new(0.001), r, true) # flat scan + assert_equal(cat.num_items, 1) + cn = [] + rdb.each_cell { |c| cn << c.to_s_test } + assert_equal(cn.join(";"), "TOP[]") + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "TOP[polygon: (0,0.001;0,0.03;0.01,0.03;0.01,0.051;0.021,0.051;0.021,0.073;0.043,0.073;0.043,0.042;0.031,0.042;0.031,0.021;0.02,0.021;0.02,0.001)]") end